Введение

Over The Air Provision (OTAP)  TC65Т

GSM модем SL116 на базе модуля Enfora GSM0308

Пишите автору сайта ganatol2000@mail.ru

Программа сервера обеспечивающая обмен 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 и DataSend
Time.
И здесь необходимо объяснить использование директивы #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, с использованием веб интерфейса.


 

 

 

Hosted by uCoz