Программа сервера обеспечивающая обмен UDP сообщениями с модемом Siemens ТС65 и запись полученной информации в базу данных MySQL.
Согласно разработанному протоколу,
который был реализован в программе модема ProbaTC65new
программа сервера должна функционировать следующим образом:
Сервер предназначен для работы с группой модемов. Идентификаторы модемов заранее
прописаны в
xml
файле:
<?xml version="1.0"?>
<Config>
<Options>
<Ident>1</Ident>
<Ident>255</Ident>
</Options>
</Config>
Сервер принимает от модема
UDP сообщение в виде
short Id - уникальный номер(идентификатор) модема
int AI0 - значение аналогового сигнала подаваемого
на первый аналоговый вход модема
int AI1 - значение
аналогового сигнала подаваемого на второй аналоговый вход модема
int TimeModem - модифицированное время модема в
формате UNIX.
Далее сервер сравнивает идентификатор модема (Id) со
списком идентификаторов (поле Ident в xml
файле). Если идентификатор в списке присутствует
то информация содержащаяся в UDP
сообщении записывается в базу данных MySQL. Затем
происходит сравнение времени переданного модемом и временем сервера, если
разница времен составляет менее некоторой величины, характеризующей задержку
прохождения UDP сообщения от модема к серверу(в программе
принято значение 50 сек, за это время UDP сообщения
обычно доходили до сервера), то сервер отправляет ответное UDP
сообщение вида
unsigned short id
unsigned short sFlag
где id тот
же, что и принят сервером,
sFlag=0x01 подтверждение успешного приема.
Если разница времен более некоторой величины, характеризующей задержку
прохождения UDP сообщения от модема к серверу, то сервер
отправляет ответное UDP сообщение:
unsigned short id
unsigned short sFlag
long dTime
где id тот же,
что и принят сервером, sFlag=0x01+0x02=0x03
подтверждение успешного приема и
флаг передачи времени, dTime время в
UNIX формате.
Полученные данные величин аналоговых сигналов программа сервера вносит в
базу данных MySQL. Приложение сервера должно иметь
возможность изменять настройки (логин, пароль и т. д.) базы данных. В программе
эти настройки хранятся в реестре.
Программа сервера также должна вести лог-файл, куда
записываются принятые и отправленные дейтаграммы.
Для организации UDP соединения в
программе были использованы компоненты Indy.
Для работы с базой данных MySQL была использована
библиотека компонентов
ZEOSDBO-6.6.6:
ZEOSDBO-6.6.6-stable.part1.rar,
ZEOSDBO-6.6.6-stable.part2.rar.
Файл ServerTC65tnew.cpp
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include <stdio.h>
#include "ServerTC65tnew.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "ZAbstractDataset"
#pragma link "ZAbstractRODataset"
#pragma link "ZConnection"
#pragma link "ZDataset"
#pragma link "ZAbstractDataset"
#pragma link "ZAbstractRODataset"
#pragma link "ZConnection"
#pragma link "ZDataset"
#pragma resource "*.dfm"
TForm1 *Form1;
// В переменной int TCPPort
будем хранить номер порта используемого программой сервера в
UDP протоколе.
int TCPPort;
/*
Направление Server > Modem
unsigned short id //Тот же, что и принят сервером
unsigned short sFlag:
sfAck = 0x01 - подтверждение успешного приема
sfTime = 0x02 - текущее время
Если установлен sfTime, то передается текущее время:
long dTime
*/
//Флаг ответа и флаг времени в ответной датаграмме
short sfAck = 0x01, sfTime = 0x02;
//Глобальная константа ModemDelta содержит допустимую
разницу в секундах между временем сервера и временем модема.
int const ModemDelta=50;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
//Для обеспечения приема UDP
сообщений используем Indy компонент
IdUDPServer. Событие OnUDPRead
генерируется всякий раз в момент приема UDP сообщения.
Функция IdUDPServer1UDPRead реализует обработку этого события.
void __fastcall TForm1::IdUDPServer1UDPRead(TObject *Sender,
TStream *AData, TIdSocketHandle *ABinding)
{
char My[200];
AnsiString MyDump;
AnsiString MyReciever;
//Указатель AData - это объект
класса TStream, указывает на поток
данных поступающих в момент приема UDP сообщения, используем
метод ReadBufer класса TStream для
чтения данных в буфер My и для обозначения размера
используем свойство Size класса TStream.
AData->ReadBuffer(My, AData->Size);
//Чтобы буфер данных содержал строчную переменную
последний символ в массиве должен быть 0.
My[AData->Size]=0;
//Функция Dump преобразует
строку данных в шестнадцатеричный дамп.
MyDump=Dump(My, AData->Size, 0, true, "");
//Формируем строку содержащую дату, время, IP-адрес,
порт и дамп данных принятого UDP сообщения для
отображения в поле Memo и записи в лог-файл.
MyReciever=DateToStr(Date())+" "+TimeToStr(Time())+" "+
ABinding->PeerIP+":"+ABinding->PeerPort+" "+"R:"+MyDump;
Memo1->Lines->Add(MyReciever);
LogFileSave(MyReciever);
//Отладка функции Cut,
которая извлекает составные части из UDP сообщения модема
// Memo1->Lines->Add(Cut(My, 0,2, 0, ""));
id
// Memo1->Lines->Add(Cut(My, 2,6, 0, "")); AI0
// Memo1->Lines->Add(Cut(My, 6,10, 0, "")); AI1
// Memo1->Lines->Add(Cut(My, 10,14, 0, "")); TimeModem
long Id_chek=Cut(My, 0,2, 0, "");
//Проверяем Id в принятом
UDP сообщении на наличие в XML
файле.
if(Id_in_XML(XMLDocument1, Id_chek))
{
//Если проверка прошла успешно в лог файл и в базу
данных MySQL записываем раскодированные данные.
LogFileSaveData(My);
MySqlInsertData(My);
//Проверяем разницу между временем модема(поле
TimeModem в датаграмме) и временем сервера.
if(TimeModemChek(Cut(My, 10,14, 0, "")))
{
//Эта функция отправляет UDP
сообщение с сервера на модем, с включением в дейтаграмму поля времени.
IdUDPClient1SendTime(ABinding, AData, (short)Id_chek);
}
else
//Эта функция отправляет UDP
сообщение с сервера на модем, ,без включения в
дейтаграмму
поля времени.
IdUDPClient1SendAsk(ABinding, AData, (short)Id_chek);
}
else
Memo1->Lines->Add(AnsiString("Id=false"));
}
//---------------------------------------------------------------------------
//Функция Dump использует
процедуру IntToHex для преобразования каждого символа данных в шестнадцатеричный
вид, разделяет их пробелами и возвращает шестнадцатеричный дамп данных.
AnsiString __fastcall Dump(void *data, int size, int col, bool hex, const
AnsiString &pref)
{
AnsiString s;
for (int i = 0; i < size; ++i) {
if (!i)
s += pref;
else if (col && !(i % col)) {
s += "\n";
s += pref;
}
if (hex)
s += IntToHex(((unsigned char*)data)[i], 2) + " ";
else {
AnsiString s1 = int(((unsigned char*)data)[i]);
while (s1.Length() < 3)
s1 = " " + s1;
s += s1 + " ";
}
}
return s;
}
//---------------------------------------------------------------------------
//Функция Сат, используя процедуру IntToHex,
вырезает из массива данных указанный участок (между begin
и end) преобразуя его в шестнадцатеричный вид, а затем
возвращает это число в десятичном виде. Функция используется для извлечения из
UDP сообщения модема его составляющих.
long __fastcall Cut(void *data, int begin, int end, int col, const AnsiString
&pref)
{
AnsiString s;
for (int i = begin; i < end; ++i) {
if (!i)
s += pref;
else if (col && !(i % col)) {
s += "\n";
s += pref;
}
s += IntToHex(((unsigned char*)data)[i], 2);
}
long a=(long) AnsiString("0x"+s).Trim().ToInt();
return a;
}
//---------------------------------------------------------------------------
//Номер порта, используемого программой сервера в
UDP протоколе,
храниться в ключе реестра HKEY_LOCAL_MACHINE\Software\tc65Config\Port. Эта
функция считывает номер порта и присваивает его значение переменной
TCPPort. При удачной попытке возвращает true.
bool __fastcall TForm1::ReadRegistryPort(void)
{
TRegistry *reg = new TRegistry(KEY_ALL_ACCESS);
if (reg) // если всё ОК
{
// выбираем нужный корень
reg->RootKey = HKEY_LOCAL_MACHINE;
if ( (reg->KeyExists("Software\\tc65Config\\Port\\"))==false)
{
// ругаемся
ShowMessage("Необходимо ввести TCP port!");
return false;
}
else
{
reg->OpenKey("Software\\tc65Config\\Port\\", false);
if (reg->ValueExists("Port"))
Label1->Caption = reg->ReadString("Port");
TCPPort=reg->ReadString("Port").Trim().ToInt();
reg->CloseKey();
delete reg;
return true;
}
}
return false;
}
//---------------------------------------------------------------------------
//Процедура записи номера порта в реестр
void __fastcall TForm1::SaveRegistryPort(void)
{
TRegistry *reg = new TRegistry(KEY_ALL_ACCESS);
if (reg) // если всё ОК
{
// выбираем нужный корень
reg->RootKey = HKEY_LOCAL_MACHINE;
reg->OpenKey("Software\\tc65Config\\Port\\", true);
reg->WriteString("Port", Edit1->Text);
reg->CloseKey();
delete reg;
}
}
//---------------------------------------------------------------------------
//Функция использует компонент TXMLDocument.
В ней происходит последовательное сравнение идентификатора модема, из
полученного UDP сообщения, с
содержимым элементов Ident. Если совпадения не
найдено функция возвращает false.
bool __fastcall Id_in_XML(TXMLDocument *XMLDocument1, int Id_chek)
{
int Ident;
XMLDocument1->FileName="ServTC65tNew.xml";
XMLDocument1->Active=true;
_di_IXMLNode Config=XMLDocument1->ChildNodes->FindNode(WideString("Config"));
if( Config->GetNodeName() == WideString( "Config" ) )
{
_di_IXMLNode Options = Config->GetChildNodes()->FindNode(WideString("Options"));
if(Options->GetNodeName()==WideString("Options"))
{
for (int i = 0; i < Options->ChildNodes->Count; ++i)
{
Ident=Options->ChildNodes->Nodes[i]->GetNodeValue();
if(Ident==Id_chek){ XMLDocument1->Active=false; return true;}
else{continue;}
}
/* <?xml version="1.0"?>
<Config>
<Options>
<Ident>1</Ident>
<Ident>255</Ident>
</Options>
</Config>
*/
}
}
XMLDocument1->Active=false;
return false;
}
//---------------------------------------------------------------------------
//Функция считывает системное время сервера в UNIX
формате и используя время модема, полученное в UDP
сообщении вычисляет их разницу delta.
Затем эту разницу сравнивает с константой ModemDelta.
Если delta меньше функция возвращает true.
bool __fastcall TForm1::TimeModemChek(int ModemTime)
{
time_t t;
t = time(NULL);
t=t-1000000000L;
int delta;
if(t>ModemTime){ delta=t%ModemTime;}
else{ delta=ModemTime%t; }
if(delta>ModemDelta){ return true; }
else {return false; }
}
//---------------------------------------------------------------------------
//Процедура вызывается в момент запуска приложения.
Используя функцию ReadRegistryPort из реестра.
она считывает номер порта. За установку UDP
соединения отвечает компонент Indy: IDUDPServer
со свойствами DefaulPort и Active.
Свойству DefaultPort присваиваеться считанное значение
порта, а свойству Active значение true.
void __fastcall TForm1::FormCreate(TObject *Sender)
{
if(ReadRegistryPort())
{
IdUDPServer1->DefaultPort=TCPPort;
IdUDPServer1->Active=true;
}
}
//---------------------------------------------------------------------------
//Процедура вызывается в момент нажатия кнопки
"Port" для ввода нового порта.
Для того, чтобы новое значение порта было принято UDP
протоколом без перезагрузки программы, необходимо вызвать процедуру
IdUDPServer1->Bindings->Clear(), очищающую привязки Indy
компонента IdUDPServer.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
SaveRegistryPort();
IdUDPServer1->Active=false;
//чтобы новый номер порта был принят
IdUDPServer1->Bindings->Clear();
if(ReadRegistryPort())
{
IdUDPServer1->DefaultPort=TCPPort;
IdUDPServer1->Active=true;
}
}
//---------------------------------------------------------------------------
// Процедура посылает ответное UDP
сообщение от сервера к модему состоящего из идентификатора модема и флага
ответа. Следует учесть, что при передаче данных по сети
общепринятым является представление чисел в формате big-endian(прямой
порядок следования батов), в котором запись начинается со
старшего и заканчивается младшим байтом т. е. идентификатор равный 1 должен быть
передан в виде 00 01, а компьютеры построенные на архитектуре Intel x86
используют схему представления чисел little-endian, в которой запись начинается
с младшего и заканчивается старшим т. е. значение
short 0x01 в памяти компьютера
выглядит так 01 00. Для преобразования числа из той схемы которая используется
на компьютере к той которая используется в сети применяется функция Swap.
Функция memcpy копирует в буфер памяти содержимое
структуры ответного UDP сообщения и затем Indy-компонент
IdUDPClient функцией SendBuffer отправляет дейтограмму на
адрес и порт взятые из принятого UDP сообщения. В
лог-файле фиксируется факт отправки дейтограммы.
void __fastcall TForm1::IdUDPClient1SendAsk(TIdSocketHandle *ABinding,
TStream *AData, short IdChek)
{
AnsiString SendAskString;
DataSendAsk dataSendAsk;
dataSendAsk.id=Swap(IdChek);
dataSendAsk.sFlag=Swap(sfAck);
char* buf;
buf=new char [sizeof(dataSendAsk)];
memcpy(buf, &dataSendAsk, sizeof(dataSendAsk));
IdUDPClient1->Host=ABinding->PeerIP;
IdUDPClient1->Port=ABinding->PeerPort;
IdUDPClient1->Active=true;
IdUDPClient1->SendBuffer(buf, sizeof(dataSendAsk));
IdUDPClient1->Active=false;
SendAskString=DateToStr(Date())+" "+TimeToStr(Time())+" "+
IdUDPClient1->Host+":"+IdUDPClient1->Port+" "+
"S:"+Dump(&dataSendAsk, sizeof(dataSendAsk), 0, true, "");
delete []buf;
Memo1->Lines->Add(SendAskString);
LogFileSave(SendAskString);
}
//---------------------------------------------------------------------------
//Эта процедура также отправляет ответное UDP
сообщение, но здесь в дейтограмме будет присутствовать еще поле
времени.
void __fastcall TForm1::IdUDPClient1SendTime(TIdSocketHandle *ABinding, TStream
*AData, short IdChek)
{
AnsiString SendTimeString;
time_t t;
DataSendTime dataSendTime;
dataSendTime.id=Swap(IdChek);
dataSendTime.sFlag=Swap(sfAck | sfTime);
t = time(NULL);
t=t-1000000000L;
dataSendTime.TimeServer=Swap(t);
char* buf;
buf=new char [sizeof(dataSendTime)];
memcpy(buf, &dataSendTime, sizeof(dataSendTime));
IdUDPClient1->Host=ABinding->PeerIP;
IdUDPClient1->Port=ABinding->PeerPort;
IdUDPClient1->Active=true;
IdUDPClient1->SendBuffer(buf, sizeof(dataSendTime));
IdUDPClient1->Active=false;
SendTimeString=DateToStr(Date())+" "+TimeToStr(Time())+" "+
IdUDPClient1->Host+":"+IdUDPClient1->Port+" "+
"S:"+Dump(&dataSendTime, sizeof(dataSendTime), 0, true, "");
delete []buf;
Memo1->Lines->Add(SendTimeString);
LogFileSave(SendTimeString);
}
//---------------------------------------------------------------------------
//Функции Swap
использует такой способ организации информации как:
объединения(union), и массив значений char,
как элемент входящий в объединение. Swap переставляет члены массива
значений char таким образом, чтобы старшие байты переместились на место младших и наоборот.
Это позволяет преобразовать число из little-endian формата в big-endian
формат и затем корректно передать в UDP сообщении.
short __fastcall Swap(short v)
{
union {short v; char c[sizeof(short)];} u1, u2;
u1.v = v;
for (int i = 0, j = sizeof(u1); j;)
u2.c[i++] = u1.c[--j];
return u2.v;
}
//---------------------------------------------------------------------------
unsigned short __fastcall Swap(unsigned short v)
{
union {unsigned short v; char c[sizeof(unsigned short)];} u1, u2;
u1.v = v;
for (int i = 0, j = sizeof(u1); j;)
u2.c[i++] = u1.c[--j];
return u2.v;
}
//---------------------------------------------------------------------------
long __fastcall Swap(long v)
{
union {long v; char c[sizeof(long)];} u1, u2;
u1.v = v;
for (int i = 0, j = sizeof(u1); j;)
u2.c[i++] = u1.c[--j];
return u2.v;
}
//---------------------------------------------------------------------------
unsigned long __fastcall Swap(unsigned long v)
{
union {unsigned long v; char c[sizeof(unsigned long)];} u1, u2;
u1.v = v;
for (int i = 0, j = sizeof(u1); j;)
u2.c[i++] = u1.c[--j];
return u2.v;
}
//---------------------------------------------------------------------------
//Процедура сохраняет в лог-файл строку. Используется для
сохранения принятой и переданной дейтограммы.
void __fastcall LogFileSave(AnsiString String)
{
FILE* MyAnalogValue;
MyAnalogValue=fopen("analog_value.log","a");
fprintf(MyAnalogValue,"%s \n", String.c_str());
fclose(MyAnalogValue);
}
//---------------------------------------------------------------------------
//Процедура сохраняет в лог-файл декодированные данные
взятые из принятого UDP сообщения.
void __fastcall LogFileSaveData(char *Data)
{
FILE* MyAnalogValue;
timeMy time;
char* id="id=";
char* ai1="AI1=";
char* ai2="AI2=";
char* tm="time=";
char* Tp="Tp=";
int ID=Cut(Data, 0,2, 0, "");
long AI1=Cut(Data, 2,6, 0, "");
long AI2=Cut(Data, 6,10, 0, "");
long t=Cut(Data, 10,14, 0, "");
time=Time(t);
MyAnalogValue=fopen("analog_value.log","a");
fprintf(MyAnalogValue,"%s%d %s%d %s%d %s%d \n", id, ID, ai1, AI1, ai2, AI2, tm,
t);
fprintf(MyAnalogValue,"%s%s\n", Tp,
(time.mday+"."+time.mon+"."+time.year+" "+time.hour+":"+time.min+":"+time.sec).c_str());
fclose(MyAnalogValue);
}
//---------------------------------------------------------------------------
//Функция получает в качестве аргумента время в формате
UNIX и возвращает структуру состоящую из цифровых
значений года, месяца, дня, часов, минут, секунд соответствующих времени в
UNIX формате.
timeMy __fastcall Time(long t)
{
timeMy time_t;
struct tm *area;
long time=t+1000000000L;
area = localtime(&time);
time_t.year=AnsiString(AnsiString(area->tm_year).ToInt()+1900);
time_t.mon=AnsiString(area->tm_mon+1);
if(time_t.mon.Length()==1) time_t.mon="0"+time_t.mon;
time_t.mday=AnsiString(area->tm_mday);
if(time_t.mday.Length()==1) time_t.mday="0"+time_t.mday;
time_t.hour=AnsiString(area->tm_hour);
if(time_t.hour.Length()==1) time_t.hour="0"+time_t.hour;
time_t.min=AnsiString(area->tm_min);
if(time_t.min.Length()==1) time_t.min="0"+time_t.min;
time_t.sec=AnsiString(area->tm_sec);
if(time_t.sec.Length()==1) time_t.sec="0"+time_t.sec;
return time_t;
}
//---------------------------------------------------------------------------
//Процедура вызывается во время клика по пункту меню
"Настройки MySQL". При этом
появляется форма для ввода настроек. базы данных и записи их в реестр.
void __fastcall TForm1::N1Click(TObject *Sender)
{
Form2->Show();
}
//---------------------------------------------------------------------------
//Процедура записи информации, полученной в UDP
сообщении от модема, в базу данных MySQL.
Для работы с MySQL используются компоненты ZEOSDBO.
Сначала извлекаем из полученной дейтограммы значение идентификатора модема,
величину сигналов на первом и втором аналоговом входе и время в UNIX
формате и составляем из этих данных строку которая будет
использована в SQL запросе. Из реестра извлекаются
настройки базы данных и затем используется компонент ZEOSDBO
ZConnection для подсоединения к базе данных и компонент ZQuery
с методом Add() для составление
SQL запроса записывающего информацию в базу данных
(оператор INSERT) . В заключении вызывается ZQuery с методом ExecSQL(); для
осуществления записи. (Если бы запрос осуществлялся для чтения данных,
то применялся бы оператор SELECT и
с компонентом ZQuery использовался бы метод
Open())
void __fastcall TForm1::MySqlInsertData(char *Data)
{
int id=Cut(Data, 0,2, 0, "");
long AI0=Cut(Data, 2,6, 0, "");
long AI1=Cut(Data, 6,10, 0, "");
long Time=Cut(Data, 10,14, 0, "");
AnsiString DataInMySQL="'"+IntToStr(id)+"'"+","+"'"+AI0+"'"+","+"'"+AI1+"'"+","+"'"+Time+"'";
ZConnection1->Disconnect();
TRegistry *reg = new TRegistry(KEY_ALL_ACCESS);
if (reg) // если всё ОК
{
// выбираем нужный корень
reg->RootKey = HKEY_LOCAL_MACHINE;
if ( (reg->KeyExists("Software\\tc65Config\\tc65MySqlConfig\\"))==false)
{
// ругаемся
ShowMessage("Необходимо ввести данные конфигурации MySQL.");
}
else
{
reg->OpenKey("Software\\tc65Config\\tc65MySqlConfig\\", false);
AnsiString Database= reg->ReadString("Database");
AnsiString HostName=reg->ReadString("HostName");
AnsiString User=reg->ReadString("User");
AnsiString Password=reg->ReadString("Password");
ZConnection1->Database=Database;
ZConnection1->HostName=HostName;
ZConnection1->User=User;
ZConnection1->Password=Password;
ZConnection1->Protocol="mysql";
// ZConnection1->Database="tc65t";
// ZConnection1->HostName="192.168.1.100";
// ZConnection1->User="alex";
// ZConnection1->Password="12345";
reg->CloseKey();
delete reg;
ZConnection1->Connect();
DataInMySQL="INSERT INTO `tc65t`.`tc65tdbnew` (`id`, `AI0`, `AI1`, `Time`)
VALUES ("+DataInMySQL+")";
//Memo1->Lines->Add(DataInMySQL);
ZQuery1->SQL->Clear();
ZQuery1->SQL->Add(DataInMySQL);
//ZQuery1->SQL->Add("INSERT INTO `tc65t`.`tc65tdbnew` (`id`, `AI0`, `AI1`, `Time`)
VALUES ('1', '234', '432', '275300605')");
ZQuery1->ExecSQL();
}
}
}
//---------------------------------------------------------------------------
Заголовочный файл ServerTC65tnew.h
Кроме прототипов функций содержит описание структур DataSendAsk и DataSendTime.
И здесь необходимо объяснить использование директивы #pragma pack(push, 1) и
#pragma pack(pop) . Дело в том, что по умолчанию
компилятор располагая данные структуры в памяти выравнивает их по 8 байт. Но в
структуре DataSendAsk только 4 байта (да и в DataSendTime
8 байт получилось случайно) и чтобы компилятор не заполнил оставшееся место
нулями, которые мы потом отправлял бы в дейтограмме, мы даем команду #pragma pack(push, 1)
т. е. сохраняем текущее выравнивание в стек и назначаем выравнивание по 1 байту.
После описание структур даем команду компилятору
#pragma pack(pop) чтобы восстановить выравнивание по умолчанию, достав его из
стека.
//---------------------------------------------------------------------------
#ifndef ServerTC65tnewH
#define ServerTC65tnewH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <IdBaseComponent.hpp>
#include <IdComponent.hpp>
#include <IdUDPBase.hpp>
#include <IdUDPServer.hpp>
#include <IdUDPClient.hpp>
#include <Menus.hpp>
#include <Registry.hpp>
#include <ExtCtrls.hpp>
#include <time.h>
#include <msxmldom.hpp>
#include <XMLDoc.hpp>
#include <xmldom.hpp>
#include <XMLIntf.hpp>
#include <DB.hpp>
#include "MySQLConfig.h"
#include "ZAbstractDataset.hpp"
#include "ZAbstractRODataset.hpp"
#include "ZConnection.hpp"
#include "ZDataset.hpp"
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TMemo *Memo1;
TMainMenu *MainMenu1;
TMenuItem *N1;
TButton *Button1;
TEdit *Edit1;
TPanel *Panel1;
TLabel *Label1;
TIdUDPServer *IdUDPServer1;
TIdUDPClient *IdUDPClient1;
TXMLDocument *XMLDocument1;
TDataSource *DataSource1;
TZQuery *ZQuery1;
TZConnection *ZConnection1;
void __fastcall IdUDPServer1UDPRead(TObject *Sender,
TStream *AData, TIdSocketHandle *ABinding);
void __fastcall IdUDPClient1SendTime(TIdSocketHandle *ABinding, TStream *AData,
short IdChek);
void __fastcall IdUDPClient1SendAsk(TIdSocketHandle *ABinding, TStream *AData,
short IdChek);
bool __fastcall TimeModemChek(int ModemTime);
bool __fastcall ReadRegistryPort(void);
void __fastcall SaveRegistryPort(void);
void __fastcall FormCreate(TObject *Sender);
void __fastcall Button1Click(TObject *Sender);
void __fastcall N1Click(TObject *Sender);
void __fastcall MySqlInsertData(char *Data);
private: // User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
AnsiString __fastcall Dump(void *data, int size, int col, bool hex, const
AnsiString &pref);
short __fastcall Swap(short v);
unsigned short __fastcall Swap(unsigned short v);
long __fastcall Swap(long v);
unsigned long __fastcall Swap(unsigned long v);
long __fastcall Cut(void *data, int begin, int end, int col, const AnsiString &pref);
bool __fastcall Id_in_XML(TXMLDocument *XMLDocument1, int Id_chek);
void __fastcall LogFileSave(AnsiString String);
void __fastcall LogFileSaveData(char *Data);
//---------------------------------------------------------------------------
#endif
#pragma pack(push, 1)
struct DataSendAsk{
short id;
short sFlag;
};
struct DataSendTime{
short id;
short sFlag;
int TimeServer;
};
#pragma pack(pop)
struct timeMy{
AnsiString sec;
AnsiString min;
AnsiString hour;
AnsiString mday;
AnsiString mon;
AnsiString year;
};
timeMy __fastcall Time(long t);
Форма для ввода настроек базы данных MySqlConfig
состоит из четырех компонентов для ввода
текста - Edit и компонента кнопки - Button.
Файл MySqlConfig.cpp
При запуске формы в поля Edit заноситься
информация о настройках базы данных из соответствующих ключей реестра, если
такая есть в реестре, если нет то поля Edit остаются
пустыми. После редактирования и нажатия кнопки Save Config MySql
данные соответствующих ключей реестра перезаписываться.
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "MySqlConfig.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm2 *Form2;
//---------------------------------------------------------------------------
__fastcall TForm2::TForm2(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
//Процедура чтения данных из реестра
void __fastcall TForm2::ReadRegistry(void)
{
TRegistry *reg = new TRegistry(KEY_ALL_ACCESS);
if (reg) // если всё ОК
{
// выбираем нужный корень
reg->RootKey = HKEY_LOCAL_MACHINE;
if ( (reg->KeyExists("Software\\tc65Config\\tc65MySqlConfig\\"))==false)
{
// ругаемся
ShowMessage("Необходимо ввести данные конфигурации MySQL.");
}
else
{
reg->OpenKey("Software\\tc65Config\\tc65MySqlConfig\\", false);
if (reg->ValueExists("Database"))
Edit1->Text = reg->ReadString("Database");
if (reg->ValueExists("HostName"))
Edit2->Text = reg->ReadString("HostName");
if (reg->ValueExists("User"))
Edit3->Text = reg->ReadString("User");
if (reg->ValueExists("Password"))
Edit4->Text = reg->ReadString("Password");
reg->CloseKey();
}
delete reg;
}
}
//---------------------------------------------------------------------------
//Процедура записи данных в реестр
void __fastcall TForm2::SaveRegistry(void)
{
TRegistry *reg = new TRegistry(KEY_ALL_ACCESS);
if (reg) // если всё ОК
{
// выбираем нужный корень
reg->RootKey = HKEY_LOCAL_MACHINE;
reg->OpenKey("Software\\tc65Config\\tc65MySqlConfig\\", true);
reg->WriteString("Database", Edit1->Text);
reg->WriteString("HostName", Edit2->Text);
reg->WriteString("User", Edit3->Text);
reg->WriteString("Password", Edit4->Text);
reg->CloseKey();
delete reg;
}
}
//---------------------------------------------------------------------------
//При создании формы вызывается процедура чтения
данных из реестра
void __fastcall TForm2::FormCreate(TObject *Sender)
{
ReadRegistry();
}
//---------------------------------------------------------------------------
//При нажатии кнопки Save Config MySql
вызывается процедура записи данных в реестр
void __fastcall TForm2::Button1Click(TObject *Sender)
{
SaveRegistry();
}
//---------------------------------------------------------------------------
Заголовочный файл MySqlConfig.h
/---------------------------------------------------------------------------
#ifndef MySqlConfigH
#define MySqlConfigH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Registry.hpp>
#include <Forms.hpp>
//---------------------------------------------------------------------------
class TForm2 : public TForm
{
__published: // IDE-managed Components
TEdit *Edit1;
TEdit *Edit2;
TEdit *Edit3;
TEdit *Edit4;
TLabel *Label1;
TLabel *Label2;
TLabel *Label3;
TLabel *Label4;
TButton *Button1;
void __fastcall FormCreate(TObject *Sender);
void __fastcall Button1Click(TObject *Sender);
private: // User declarations
public: // User declarations
__fastcall TForm2(TComponent* Owner);
void __fastcall TForm2::ReadRegistry(void);
void __fastcall TForm2::SaveRegistry(void);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm2 *Form2;
//---------------------------------------------------------------------------
#endif
Программа ServerTC65new в работе выглядит так.
На экран выводятся принятые и отправленные дейтограммы.
Для работы программы необходим файл libmySQL.dll (взять из директории где
установлен MySQL в каталоге bin).
Его следует скопировать в туже папку, где находиться файл ServerTC65new.exe.
Это лог - файл который ведется
программой ServerTC65new. Кроме дейтограмм сюда
записывается информация содержащаяся в принятом UDP
сообщении от модема.
Исходники рассмотренной программы сервера:
ServerTC65tNew.rar
Далее рассмотрим программу просмотра данных, переданных
модемом TC65,
с использованием веб интерфейса.