1564 lines
44 KiB
ObjectPascal
1564 lines
44 KiB
ObjectPascal
unit kocomHomenet;
|
|
|
|
interface
|
|
|
|
uses
|
|
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
|
|
Dialogs, ExtCtrls, StdCtrls, Math, StrUtils,
|
|
IdHashMessageDigest, IdHash, Vcl.Menus, System.Win.ScktComp, IdTCPClient, IdGlobal,
|
|
IdBaseComponent, IdComponent, IdTCPConnection, IdIOHandler, IdIOHandlerSocket,
|
|
IdIOHandlerStack, Vcl.AppEvnts, IniFiles,
|
|
FireDAC.Stan.Intf, FireDAC.Stan.Option, FireDAC.Stan.Error, FireDAC.UI.Intf,
|
|
FireDAC.Phys.Intf, FireDAC.Stan.Def, FireDAC.Stan.Pool, FireDAC.Stan.Async,
|
|
FireDAC.Phys, FireDAC.Phys.MySQL, FireDAC.Phys.MySQLDef, FireDAC.VCLUI.Wait,
|
|
FireDAC.Stan.Param, FireDAC.DatS, FireDAC.DApt.Intf, FireDAC.DApt, Data.DB,
|
|
FireDAC.Comp.DataSet, FireDAC.Comp.Client;
|
|
|
|
type
|
|
TStationName = record
|
|
id : integer;
|
|
name : string;
|
|
end;
|
|
|
|
type
|
|
TForm1 = class(TForm)
|
|
LogMemo: TMemo;
|
|
aliveTimer: TTimer;
|
|
addBtn: TButton;
|
|
reqBtn: TButton;
|
|
Panel1: TPanel;
|
|
addLabel2: TLabel;
|
|
szDataTime: TEdit;
|
|
addLabel1: TLabel;
|
|
nArea: TEdit;
|
|
addLabel3: TLabel;
|
|
addLabel4: TLabel;
|
|
szMangName: TEdit;
|
|
addLabel5: TLabel;
|
|
szDataTerm: TEdit;
|
|
addLabel6: TLabel;
|
|
szStationName: TEdit;
|
|
addLabel7: TLabel;
|
|
nPm10Value: TEdit;
|
|
addLabel8: TLabel;
|
|
fCtValue: TEdit;
|
|
addLabel9: TLabel;
|
|
nPm25Value: TEdit;
|
|
addLabel10: TLabel;
|
|
fRhValue: TEdit;
|
|
addLabel11: TLabel;
|
|
fO3Value: TEdit;
|
|
addLabel12: TLabel;
|
|
fNo2Value: TEdit;
|
|
addLabel13: TLabel;
|
|
fUvValue: TEdit;
|
|
szReserved: TEdit;
|
|
redLabel2: TLabel;
|
|
reqMultiBtn: TButton;
|
|
nAreaReq: TEdit;
|
|
reqLabel1: TLabel;
|
|
readBtn: TButton;
|
|
cs: TClientSocket;
|
|
csHome2: TClientSocket;
|
|
miseAddress: TEdit;
|
|
miseAddLabel: TLabel;
|
|
DebugCheck: TCheckBox;
|
|
TrayIcon1: TTrayIcon;
|
|
AppEvt: TApplicationEvents;
|
|
tmrExit: TTimer;
|
|
LogCheck: TCheckBox;
|
|
Panel2: TPanel;
|
|
TotalTimer: TTimer;
|
|
AutoCheck: TCheckBox;
|
|
SetIntervalLabel: TLabel;
|
|
SetInterval: TEdit;
|
|
SetIntervalBtn: TButton;
|
|
cs2: TIdTCPClient;
|
|
Panel3: TPanel;
|
|
loginLabel: TLabel;
|
|
loginBtn: TButton;
|
|
csConnectBtn: TButton;
|
|
LogoutBtn: TButton;
|
|
AliveBtn: TButton;
|
|
Label1: TLabel;
|
|
ExitBtn: TButton;
|
|
ClrBtn: TButton;
|
|
Label2: TLabel;
|
|
tmrUpInterval: TTimer;
|
|
tmrInit: TTimer;
|
|
Label3: TLabel;
|
|
lblCollectMode: TLabel;
|
|
cboCollectMode: TComboBox;
|
|
FDCon: TFDConnection;
|
|
FDQuery1: TFDQuery;
|
|
FDPhysMySQLDriverLink1: TFDPhysMySQLDriverLink;
|
|
procedure loginBtnClick(Sender: TObject);
|
|
procedure addBtnClick(Sender: TObject);
|
|
procedure FormCreate(Sender: TObject);
|
|
procedure FormDestroy(Sender: TObject);
|
|
procedure aliveTimerTimer(Sender: TObject);
|
|
procedure reqBtnClick(Sender: TObject);
|
|
procedure reqMultiBtnClick(Sender: TObject);
|
|
procedure csRead(Sender: TObject; Socket: TCustomWinSocket);
|
|
procedure readBtnClick(Sender: TObject);
|
|
procedure LogoutBtnClick(Sender: TObject);
|
|
procedure AliveBtnClick(Sender: TObject);
|
|
procedure csConnectBtnClick(Sender: TObject);
|
|
procedure csConnect(Sender: TObject; Socket: TCustomWinSocket);
|
|
procedure csDisconnect(Sender: TObject; Socket: TCustomWinSocket);
|
|
procedure csError(Sender: TObject; Socket: TCustomWinSocket;
|
|
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
|
|
procedure csHome2Connect(Sender: TObject; Socket: TCustomWinSocket);
|
|
procedure csHome2Disconnect(Sender: TObject; Socket: TCustomWinSocket);
|
|
procedure csHome2Error(Sender: TObject; Socket: TCustomWinSocket;
|
|
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
|
|
procedure csHome2Read(Sender: TObject; Socket: TCustomWinSocket);
|
|
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
|
|
procedure AppEvtMinimize(Sender: TObject);
|
|
procedure tmrExitTimer(Sender: TObject);
|
|
procedure TrayIcon1DblClick(Sender: TObject);
|
|
procedure LoadINI;
|
|
procedure DebugCheckClick(Sender: TObject);
|
|
procedure LogCheckClick(Sender: TObject);
|
|
procedure TotalTimerTimer(Sender: TObject);
|
|
procedure AutoCheckClick(Sender: TObject);
|
|
procedure SetIntervalBtnClick(Sender: TObject);
|
|
procedure ClrBtnClick(Sender: TObject);
|
|
procedure ExitBtnClick(Sender: TObject);
|
|
procedure FormClose(Sender: TObject; var Action: TCloseAction);
|
|
procedure tmrUpIntervalTimer(Sender: TObject);
|
|
procedure tmrInitTimer(Sender: TObject);
|
|
procedure cboCollectModeChange(Sender: TObject);
|
|
private
|
|
{ Private declarations }
|
|
ExitOk : boolean;
|
|
FIsBound : boolean;
|
|
FIsBound2 : boolean;
|
|
FIsConnecting : boolean;
|
|
FIsConnecting2 : boolean;
|
|
FUseServer2 : boolean;
|
|
FCollectMode : integer; // 0: MODBUS TCP, 1: MariaDB
|
|
FRestoreMsgID : Cardinal;
|
|
StName : array of TStationName;
|
|
procedure AddLog(txt: string);
|
|
function TruncateUTF8ToMaxBytes(const S: string; MaxBytes: Integer): TBytes;
|
|
procedure RestoreAppWindow;
|
|
function AppHook(var Msg: TMessage): Boolean;
|
|
procedure ConnectServer1;
|
|
procedure ConnectServer2;
|
|
procedure SendLoginPacket(ASocket: TCustomWinSocket; const AServerName: string; const ALoginId: string; const ALoginPw: string);
|
|
procedure SendAlivePacket(ASocket: TCustomWinSocket; const AServerName: string);
|
|
procedure ProcessServerRead(Socket: TCustomWinSocket; const AServerName: string; var AIsBound: Boolean);
|
|
protected
|
|
procedure WndProc(var Msg: TMessage); override;
|
|
public
|
|
SEND_TYPE : integer;
|
|
LogFile : TextFile;
|
|
FileName : String;
|
|
CurrentDate : TDateTime;
|
|
fn,sn : string;
|
|
ini : TIniFile;
|
|
AutoChk, SendDustChk : boolean;
|
|
LogPath : string;
|
|
|
|
ErrChk : array[0..3] of byte;
|
|
ErrChkInt : integer;
|
|
ErrChkText : string;
|
|
|
|
ConnectChk: integer;
|
|
AddCount : integer;
|
|
|
|
LoginId, LoginPw : string;
|
|
LoginId2, LoginPw2 : string;
|
|
ServerIP, ServerIP2 : string;
|
|
|
|
DB_IP, DB_ID, DB_PW, DB_DB : string;
|
|
DB_Port : integer;
|
|
|
|
{ Public declarations }
|
|
function MD5Str(const S: String): String;
|
|
function MD5File(const FilePath: String): String;
|
|
function CRC16(Data: AnsiString): AnsiString;
|
|
function ConvertUTF8ToANSI(const UTF8Text: string): AnsiString;
|
|
|
|
procedure InitMariaDB;
|
|
procedure LoadStationFromDB;
|
|
procedure LoadStationFromINI;
|
|
end;
|
|
|
|
var
|
|
Form1: TForm1;
|
|
|
|
implementation
|
|
|
|
{$R *.dfm}
|
|
const
|
|
NONE_TYPE = $00000000;
|
|
ENVIRONMENT_SENSOR_TYPE = $39000000;
|
|
BIND = 0;
|
|
BIND_ACK = 1;
|
|
ALIVE = 4;
|
|
ALIVE_ACK = 5;
|
|
ENVIRONMENT_SENSOR_REQ = 180;
|
|
ENVIRONMENT_SENSOR_REP = 181;
|
|
ENVIRONMENT_SENSOR_MULTI_REQ = 182;
|
|
ENVIRONMENT_SENSOR_MULTI_REP = 183;
|
|
ENVIRONMENT_SENSOR_ADD = 188;
|
|
ENVIRONMENT_SENSOR_ADD_ACK = 189;
|
|
ERROR_ACK = 10000;
|
|
|
|
procedure TForm1.RestoreAppWindow;
|
|
begin
|
|
TrayIcon1.Visible := False;
|
|
Show;
|
|
WindowState := wsNormal;
|
|
Application.Restore;
|
|
Application.BringToFront;
|
|
ShowWindow(Handle, SW_RESTORE);
|
|
ShowWindow(Handle, SW_SHOW);
|
|
SetForegroundWindow(Handle);
|
|
BringWindowToTop(Handle);
|
|
ShowWindow(Application.Handle, SW_RESTORE);
|
|
SetForegroundWindow(Application.Handle);
|
|
end;
|
|
|
|
function TForm1.AppHook(var Msg: TMessage): Boolean;
|
|
begin
|
|
Result := False;
|
|
if (FRestoreMsgID <> 0) and (Msg.Msg = FRestoreMsgID) then
|
|
begin
|
|
RestoreAppWindow;
|
|
Result := True;
|
|
end;
|
|
end;
|
|
|
|
procedure TForm1.WndProc(var Msg: TMessage);
|
|
begin
|
|
if (FRestoreMsgID <> 0) and (Msg.Msg = FRestoreMsgID) then
|
|
begin
|
|
RestoreAppWindow;
|
|
Msg.Result := 1;
|
|
Exit;
|
|
end;
|
|
inherited WndProc(Msg);
|
|
end;
|
|
|
|
procedure TForm1.FormCreate(Sender: TObject);
|
|
var
|
|
ErrChkInt2 :integer;
|
|
begin
|
|
FRestoreMsgID := RegisterWindowMessage('KOCOM_HOMENET_RESTORE_MSG');
|
|
Application.HookMainWindow(AppHook);
|
|
ExitOk := false;
|
|
FIsBound := false;
|
|
FIsBound2 := false;
|
|
FIsConnecting := false;
|
|
FIsConnecting2 := false;
|
|
FUseServer2 := false;
|
|
SendDustChk := false;
|
|
AddCount := 0;
|
|
|
|
LoadINI;
|
|
AppEvtMinimize(self);
|
|
|
|
ErrChkInt := ENVIRONMENT_SENSOR_TYPE or ERROR_ACK;
|
|
fillmemory(@ErrChk, length(ErrChk), 0);
|
|
copymemory(@ErrChk, @ErrChkInt, 4);
|
|
|
|
for ErrChkInt2 := 0 to 3 do ErrChkText := ErrChkText + format('%0.2X,',[ord(ErrChk[ErrChkInt2])]);
|
|
|
|
try
|
|
LogPath := ExtractFilePath(Application.ExeName) + 'Log' + FormatDateTime('YYYYMMDD', Now);
|
|
ForceDirectories(LogPath);
|
|
|
|
FileName := LogPath + '\Log(' + FormatDateTime('YYYYMMDDHH', Now) + ').LOG';
|
|
AssignFile(LogFile, FileName);
|
|
if not FileExists(FileName) then
|
|
begin
|
|
Rewrite(LogFile);
|
|
Writeln(LogFile, '[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] ' + 'Make New File');
|
|
end
|
|
else
|
|
begin
|
|
Append(LogFile);
|
|
end;
|
|
|
|
Writeln(LogFile, 'Program START : ' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now));
|
|
|
|
finally
|
|
CloseFile(LogFile);
|
|
end;
|
|
|
|
tmrInit.Enabled := true;
|
|
end;
|
|
|
|
procedure TForm1.AddLog(txt : string);
|
|
begin
|
|
try
|
|
LogPath := ExtractFilePath(Application.ExeName) + 'Log' + FormatDateTime('YYYYMMDD', Now);
|
|
ForceDirectories(LogPath);
|
|
|
|
FileName := LogPath + '\Log(' + FormatDateTime('YYYYMMDDHH', Now) + ').LOG';
|
|
AssignFile(LogFile, FileName);
|
|
if not FileExists(FileName) then
|
|
begin
|
|
Rewrite(LogFile);
|
|
Writeln(LogFile, '[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] ' + 'Make New File');
|
|
end
|
|
else
|
|
begin
|
|
Append(LogFile);
|
|
end;
|
|
|
|
if (LogMemo.Lines.Count > 1000) and (LogCheck.Checked) then begin
|
|
WriteLn(LogFile, LogMemo.Lines.Text);
|
|
LogMemo.Clear;
|
|
end;
|
|
LogMemo.Lines.Add(txt);
|
|
|
|
finally
|
|
CloseFile(LogFile);
|
|
end;
|
|
end;
|
|
|
|
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
|
|
begin
|
|
try
|
|
LogPath := ExtractFilePath(Application.ExeName) + 'Log' + FormatDateTime('YYYYMMDD', Now);
|
|
ForceDirectories(LogPath);
|
|
|
|
FileName := LogPath + '\Log(' + FormatDateTime('YYYYMMDDHH', Now) + ').LOG';
|
|
AssignFile(LogFile, FileName);
|
|
if not FileExists(FileName) then
|
|
begin
|
|
Rewrite(LogFile);
|
|
Writeln(LogFile, '[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] ' + 'Make New File');
|
|
end
|
|
else
|
|
begin
|
|
Append(LogFile);
|
|
end;
|
|
|
|
WriteLn(LogFile, 'Program END : ' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now));
|
|
|
|
finally
|
|
CloseFile(LogFile);
|
|
end;
|
|
end;
|
|
|
|
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
|
|
begin
|
|
if ExitOk then begin
|
|
CanClose := true;
|
|
end else begin
|
|
CanClose := false;
|
|
case MessageBox(handle, '"예(Y)"를 클릭하면, 프로그램이 트레이로 최소화 됩니다.'+#$0D#$0A#$0D#$0A+'"아니오(N)"를 클릭하면 프로그램이 종료됩니다. ', '트레이로 최소화 할까요?', MB_YESNOCANCEL) of
|
|
ID_YES : begin
|
|
TrayIcon1.BalloonTitle := '프로그램 화면을 다시 보려면...';
|
|
TrayIcon1.BalloonHint := '트레이 아이콘을 더블클릭하세요.';
|
|
AppEvtMinimize(self);
|
|
end;
|
|
ID_NO : begin
|
|
tmrExit.Tag := 0;
|
|
tmrExit.Enabled := true;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TForm1.FormDestroy(Sender: TObject);
|
|
begin
|
|
Application.UnhookMainWindow(AppHook);
|
|
if cs.Active then
|
|
cs.Close;
|
|
if csHome2.Active then
|
|
csHome2.Close;
|
|
if FDCon.Connected then
|
|
FDCon.Close;
|
|
FreeAndNil(ini);
|
|
end;
|
|
|
|
procedure TForm1.LoadStationFromINI;
|
|
var
|
|
i, nCount : integer;
|
|
Addst : TStationName;
|
|
begin
|
|
SetLength(StName, 0);
|
|
for i := 1 to 10 do begin
|
|
Sn := 'SYSTEM';
|
|
Addst.id := ini.ReadInteger(Sn, format('ID%d', [i]), -1);
|
|
Addst.name := ini.ReadString(Sn, format('StationName%d', [i]), '');
|
|
if (Addst.id <> -1) and (Trim(Addst.name) <> '') then begin
|
|
nCount := Length(StName);
|
|
SetLength(StName, nCount + 1);
|
|
StName[nCount] := Addst;
|
|
end;
|
|
end;
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] INI 관측소 로드 완료: ' + IntToStr(Length(StName)) + '개');
|
|
end;
|
|
|
|
procedure TForm1.InitMariaDB;
|
|
begin
|
|
try
|
|
if FDCon.Connected then
|
|
FDCon.Close;
|
|
|
|
FDCon.Params.Clear;
|
|
FDCon.Params.Add('DriverID=MySQL');
|
|
FDCon.Params.Add('Server=' + DB_IP);
|
|
FDCon.Params.Add('Port=' + DB_Port.ToString);
|
|
FDCon.Params.Add('User_Name=' + DB_ID);
|
|
FDCon.Params.Add('Password=' + DB_PW);
|
|
FDCon.Params.Add('Database=' + DB_DB);
|
|
FDCon.Params.Add('CharacterSet=utf8');
|
|
FDCon.Connected := true;
|
|
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] MariaDB 연결 성공 (' + DB_IP + ':' + IntToStr(DB_Port) + '/' + DB_DB + ')');
|
|
LoadStationFromDB;
|
|
except
|
|
on E: Exception do begin
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] MariaDB 연결 실패: ' + E.Message);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TForm1.LoadStationFromDB;
|
|
var
|
|
i : integer;
|
|
AddSt: TStationName;
|
|
begin
|
|
if not FDCon.Connected then Exit;
|
|
try
|
|
FDQuery1.SQL.Clear;
|
|
FDQuery1.SQL.Text := 'SELECT * FROM real_time WHERE LENGTH(sensordata) - LENGTH(REPLACE(sensordata, "/", "")) = 5';
|
|
FDQuery1.Open;
|
|
|
|
SetLength(StName, FDQuery1.RecordCount);
|
|
i := 0;
|
|
FDQuery1.First;
|
|
while not FDQuery1.Eof do begin
|
|
i := i + 1;
|
|
Addst.id := i;
|
|
Addst.name := FDQuery1.FieldByName('BlinkerName').AsString;
|
|
StName[i-1] := Addst;
|
|
FDQuery1.Next;
|
|
end;
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] MariaDB 실시간 관측소 로드 완료: ' + IntToStr(Length(StName)) + '개');
|
|
except
|
|
on E: Exception do begin
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] DB 관측소 로드 실패: ' + E.Message);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TForm1.cboCollectModeChange(Sender: TObject);
|
|
begin
|
|
case cboCollectMode.ItemIndex of
|
|
0: // 1. MariaDB (실시간 DB)
|
|
begin
|
|
FCollectMode := 1;
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] 수집 방식 전환: MariaDB (실시간 DB)');
|
|
InitMariaDB;
|
|
end;
|
|
1: // 2. MODBUS TCP (포트 502)
|
|
begin
|
|
FCollectMode := 0;
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] 수집 방식 전환: MODBUS TCP (포트 502)');
|
|
LoadStationFromINI;
|
|
end;
|
|
end;
|
|
|
|
if Assigned(ini) then begin
|
|
ini.WriteInteger('CollectMode', 'Mode', FCollectMode);
|
|
ini.UpdateFile;
|
|
end;
|
|
end;
|
|
|
|
procedure TForm1.LoadINI;
|
|
begin
|
|
fn := ChangeFileExt(Application.ExeName,'.INI');
|
|
ini := TIniFile.Create(fn);
|
|
if not FileExists(Fn) then begin
|
|
Sn := 'Check';
|
|
ini.WriteString(Sn, 'DebugCheck', '0');
|
|
ini.WriteString(Sn, 'SaveCheck', '0');
|
|
ini.WriteString(Sn, 'AutoCheck', '1');
|
|
|
|
Sn := 'Interval';
|
|
ini.WriteInteger(Sn, 'Interval', 10);
|
|
|
|
Sn := 'Login';
|
|
ini.WriteString(Sn,'ID','qstech');
|
|
ini.WriteString(Sn,'PW','71737465636831323334');
|
|
ini.WriteString(Sn,'ID2','');
|
|
ini.WriteString(Sn,'PW2','');
|
|
|
|
Sn := 'IP';
|
|
ini.WriteString(Sn, 'IP', '10.254.254.1');
|
|
ini.WriteString(Sn, 'IP2', '');
|
|
|
|
Sn := 'CollectMode';
|
|
ini.WriteInteger(Sn, 'Mode', 1);
|
|
|
|
Sn := 'SYSTEM';
|
|
ini.WriteInteger(Sn, 'ID1', 1);
|
|
ini.WriteString(Sn, 'StationName1', '202동');
|
|
ini.WriteInteger(Sn, 'ID2', 2);
|
|
ini.WriteString(Sn, 'StationName2', '205동');
|
|
|
|
Sn := 'DB';
|
|
ini.WriteString(Sn, 'IP', '127.0.0.1');
|
|
ini.WriteInteger(Sn, 'Port', 3306);
|
|
ini.WriteString(Sn, 'ID', 'root');
|
|
ini.WriteString(Sn, 'PW', 'qsentech!1233');
|
|
ini.WriteString(Sn, 'DataBase', 'dust');
|
|
|
|
ini.UpdateFile;
|
|
end;
|
|
|
|
Sn := 'Check';
|
|
DebugCheck.Checked := ini.ReadString(Sn,'DebugCheck','0') = '1';
|
|
LogCheck.Checked := ini.ReadString(Sn,'SaveCheck','0') = '1';
|
|
AutoCheck.Checked := ini.ReadString(Sn,'AutoCheck','1') = '1';
|
|
if AutoCheck.Checked then AutoChk := True else AutoChk := False;
|
|
|
|
Sn := 'Interval';
|
|
SetInterval.Text := IntToStr(ini.ReadInteger(Sn,'Interval',10));
|
|
tmrUpInterval.Interval := StrToIntDef(SetInterval.Text, 10) * 1000;
|
|
|
|
Sn := 'Login';
|
|
LoginId := Trim(ini.ReadString(Sn, 'ID', ''));
|
|
if LoginId = '' then
|
|
LoginId := Trim(ini.ReadString(Sn, 'ID1', 'qstech'));
|
|
if LoginId = '' then LoginId := 'qstech';
|
|
|
|
LoginPw := Trim(ini.ReadString(Sn, 'PW', ''));
|
|
if LoginPw = '' then
|
|
LoginPw := Trim(ini.ReadString(Sn, 'PASS', ''));
|
|
if LoginPw = '' then
|
|
LoginPw := Trim(ini.ReadString(Sn, 'PW1', ''));
|
|
if LoginPw = '' then
|
|
LoginPw := Trim(ini.ReadString(Sn, 'PASS1', '71737465636831323334'));
|
|
if LoginPw = '' then LoginPw := '71737465636831323334';
|
|
|
|
LoginId2 := Trim(ini.ReadString(Sn, 'ID2', ''));
|
|
if LoginId2 = '' then
|
|
LoginId2 := LoginId;
|
|
|
|
LoginPw2 := Trim(ini.ReadString(Sn, 'PW2', ''));
|
|
if LoginPw2 = '' then
|
|
LoginPw2 := Trim(ini.ReadString(Sn, 'PASS2', ''));
|
|
if LoginPw2 = '' then
|
|
LoginPw2 := LoginPw;
|
|
|
|
Sn := 'IP';
|
|
ServerIP := Trim(ini.ReadString(Sn, 'IP', '10.254.254.1'));
|
|
if ServerIP = '' then
|
|
ServerIP := Trim(ini.ReadString(Sn, 'IP1', '10.254.254.1'));
|
|
if ServerIP = '' then ServerIP := '10.254.254.1';
|
|
|
|
ServerIP2 := Trim(ini.ReadString(Sn, 'IP2', ''));
|
|
FUseServer2 := (ServerIP2 <> '') and (ServerIP2 <> '0.0.0.0');
|
|
|
|
Sn := 'DB';
|
|
DB_IP := ini.ReadString(Sn, 'IP', '127.0.0.1');
|
|
DB_Port := ini.ReadInteger(Sn, 'Port', 3306);
|
|
DB_ID := ini.ReadString(Sn, 'ID', 'root');
|
|
DB_PW := ini.ReadString(Sn, 'PW', 'qsentech!1233');
|
|
DB_DB := ini.ReadString(Sn, 'DataBase', 'dust');
|
|
|
|
Sn := 'CollectMode';
|
|
FCollectMode := ini.ReadInteger(Sn, 'Mode', 1);
|
|
if FCollectMode = 1 then
|
|
cboCollectMode.ItemIndex := 0
|
|
else
|
|
cboCollectMode.ItemIndex := 1;
|
|
|
|
if FUseServer2 then
|
|
LogMemo.Lines.Add('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] 환경설정 로드 완료 (서버1: ' + ServerIP + ' [' + LoginId + '], 서버2: ' + ServerIP2 + ' [' + LoginId2 + '])')
|
|
else
|
|
LogMemo.Lines.Add('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] 환경설정 로드 완료 (서버1: ' + ServerIP + ' [' + LoginId + '], 단일서버)');
|
|
|
|
if FCollectMode = 1 then
|
|
InitMariaDB
|
|
else
|
|
LoadStationFromINI;
|
|
end;
|
|
|
|
procedure TForm1.DebugCheckClick(Sender: TObject);
|
|
begin
|
|
end;
|
|
|
|
procedure TForm1.LogCheckClick(Sender: TObject);
|
|
begin
|
|
end;
|
|
|
|
procedure TForm1.AutoCheckClick(Sender: TObject);
|
|
begin
|
|
if AutoCheck.Checked then begin
|
|
AutoChk := True;
|
|
if not cs.Active and not FIsConnecting then
|
|
ConnectServer1;
|
|
if FUseServer2 and not csHome2.Active and not FIsConnecting2 then
|
|
ConnectServer2;
|
|
end
|
|
else begin
|
|
AutoChk := False;
|
|
aliveTimer.Enabled := False;
|
|
if cs.Active then
|
|
cs.Close;
|
|
if csHome2.Active then
|
|
csHome2.Close;
|
|
if cs2.Connected then
|
|
cs2.Disconnect;
|
|
FIsBound := False;
|
|
FIsBound2 := False;
|
|
FIsConnecting := False;
|
|
FIsConnecting2 := False;
|
|
csConnectBtn.Enabled := True;
|
|
end;
|
|
|
|
Sn := 'Check';
|
|
ini.WriteString(Sn, 'AutoCheck', IfThen(AutoChk, '1', '0'));
|
|
ini.UpdateFile;
|
|
end;
|
|
|
|
procedure TForm1.SetIntervalBtnClick(Sender: TObject);
|
|
begin
|
|
tmrUpInterval.Interval := StrToIntDef(SetInterval.Text, 10) * 1000;
|
|
|
|
Sn := 'Interval';
|
|
ini.WriteInteger(Sn, 'Interval', StrToIntDef(SetInterval.Text, 10));
|
|
end;
|
|
|
|
procedure TForm1.AppEvtMinimize(Sender: TObject);
|
|
begin
|
|
Hide;
|
|
WindowState := wsMinimized;
|
|
|
|
TrayIcon1.Visible := True;
|
|
TrayIcon1.Animate := True;
|
|
TrayIcon1.ShowBalloonHint;
|
|
end;
|
|
|
|
procedure TForm1.TrayIcon1DblClick(Sender: TObject);
|
|
begin
|
|
TrayIcon1.Visible := False;
|
|
Show;
|
|
WindowState := wsNormal;
|
|
Application.BringToFront;
|
|
end;
|
|
|
|
procedure TForm1.tmrExitTimer(Sender: TObject);
|
|
begin
|
|
case tmrExit.Tag of
|
|
0 : begin
|
|
tmrExit.Tag := tmrExit.Tag+1;
|
|
end;
|
|
1..4 : tmrExit.Tag := tmrExit.Tag+1;
|
|
5 : begin
|
|
tmrExit.Tag := tmrExit.Tag+1;
|
|
tmrExit.Enabled := false;
|
|
ExitOk := true;
|
|
Close;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TForm1.ConnectServer1;
|
|
begin
|
|
if cs.Active or FIsConnecting then Exit;
|
|
FIsConnecting := True;
|
|
cs.Host := ServerIP;
|
|
cs.Port := 15010;
|
|
try
|
|
cs.Open;
|
|
except
|
|
on E: Exception do begin
|
|
FIsConnecting := False;
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] 서버1 소켓 접속 예외: ' + E.Message);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TForm1.ConnectServer2;
|
|
begin
|
|
if not FUseServer2 then Exit;
|
|
if csHome2.Active or FIsConnecting2 then Exit;
|
|
FIsConnecting2 := True;
|
|
csHome2.Host := ServerIP2;
|
|
csHome2.Port := 15010;
|
|
try
|
|
csHome2.Open;
|
|
except
|
|
on E: Exception do begin
|
|
FIsConnecting2 := False;
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] 서버2 소켓 접속 예외: ' + E.Message);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TForm1.csConnectBtnClick(Sender: TObject);
|
|
begin
|
|
ConnectServer1;
|
|
if FUseServer2 then
|
|
ConnectServer2;
|
|
csConnectBtn.Enabled := not (cs.Active and (not FUseServer2 or csHome2.Active));
|
|
end;
|
|
|
|
procedure TForm1.csConnect(Sender: TObject; Socket: TCustomWinSocket);
|
|
begin
|
|
FIsConnecting := False;
|
|
FIsBound := False;
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] Sys>> cs.Connected - 홈넷 서버1 접속 성공');
|
|
SendLoginPacket(Socket, '서버1', LoginId, LoginPw);
|
|
end;
|
|
|
|
procedure TForm1.csDisconnect(Sender: TObject; Socket: TCustomWinSocket);
|
|
begin
|
|
FIsConnecting := False;
|
|
FIsBound := False;
|
|
if not (FUseServer2 and FIsBound2) then
|
|
aliveTimer.Enabled := False;
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] Sys>> cs.DisConnected - 홈넷 서버1 연결 해제');
|
|
csConnectBtn.Enabled := True;
|
|
end;
|
|
|
|
procedure TForm1.csError(Sender: TObject; Socket: TCustomWinSocket;
|
|
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
|
|
begin
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] 서버1 Socket Error>> ' + IntToStr(ErrorCode));
|
|
ErrorCode := 0;
|
|
FIsConnecting := False;
|
|
FIsBound := False;
|
|
if not (FUseServer2 and FIsBound2) then
|
|
aliveTimer.Enabled := False;
|
|
try
|
|
cs.Close;
|
|
except
|
|
end;
|
|
csConnectBtn.Enabled := True;
|
|
end;
|
|
|
|
procedure TForm1.csHome2Connect(Sender: TObject; Socket: TCustomWinSocket);
|
|
begin
|
|
FIsConnecting2 := False;
|
|
FIsBound2 := False;
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] Sys>> csHome2.Connected - 홈넷 서버2 접속 성공');
|
|
SendLoginPacket(Socket, '서버2', LoginId2, LoginPw2);
|
|
end;
|
|
|
|
procedure TForm1.csHome2Disconnect(Sender: TObject; Socket: TCustomWinSocket);
|
|
begin
|
|
FIsConnecting2 := False;
|
|
FIsBound2 := False;
|
|
if not FIsBound then
|
|
aliveTimer.Enabled := False;
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] Sys>> csHome2.DisConnected - 홈넷 서버2 연결 해제');
|
|
csConnectBtn.Enabled := True;
|
|
end;
|
|
|
|
procedure TForm1.csHome2Error(Sender: TObject; Socket: TCustomWinSocket;
|
|
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
|
|
begin
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] 서버2 Socket Error>> ' + IntToStr(ErrorCode));
|
|
ErrorCode := 0;
|
|
FIsConnecting2 := False;
|
|
FIsBound2 := False;
|
|
if not FIsBound then
|
|
aliveTimer.Enabled := False;
|
|
try
|
|
csHome2.Close;
|
|
except
|
|
end;
|
|
csConnectBtn.Enabled := True;
|
|
end;
|
|
|
|
procedure TForm1.ProcessServerRead(Socket: TCustomWinSocket; const AServerName: string; var AIsBound: Boolean);
|
|
var
|
|
pkText : ansistring;
|
|
i, n : integer;
|
|
buf : array[0..1024] of byte;
|
|
ErrChkPk, ErrLogTxt, ErrViewTxt : string;
|
|
msgType : integer;
|
|
begin
|
|
FillMemory(@buf, length(buf), 0);
|
|
|
|
pkText := '';
|
|
ErrChkPk := '';
|
|
ErrLogTxt := '';
|
|
ErrViewTxt := '';
|
|
|
|
n := socket.ReceiveLength;
|
|
if n <= 0 then Exit;
|
|
if n > SizeOf(buf) then n := SizeOf(buf);
|
|
socket.ReceiveBuf(buf, n);
|
|
|
|
for i := 0 to n-1 do pkText := pkText + format('%0.2X,',[ord(buf[i])]);
|
|
|
|
if n >= 8 then begin
|
|
for i := 4 to 7 do ErrChkPk := ErrChkPk + format('%0.2X,',[ord(buf[i])]);
|
|
end;
|
|
|
|
if CompareStr(ErrChkPk, ErrChkText) = 0 then begin
|
|
ErrLogTxt := '[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [' + AServerName + '] 오류 패킷 발생 >> ';
|
|
|
|
if n >= 4 then begin
|
|
for i := n-4 to n-1 do ErrViewTxt := ErrViewTxt + format('%0.2X,',[ord(buf[i])]);
|
|
end;
|
|
|
|
if CompareStr(ErrViewTxt, '01,00,00,00,') = 0 then
|
|
ErrLogTxt := ErrLogTxt + '아이디 불일치'
|
|
else if CompareStr(ErrViewTxt, '02,00,00,00,') = 0 then
|
|
ErrLogTxt := ErrLogTxt + '비밀번호 불일치'
|
|
else if CompareStr(ErrViewTxt, '03,00,00,00,') = 0 then
|
|
ErrLogTxt := ErrLogTxt + '해당 필드 존재'
|
|
else
|
|
ErrLogTxt := ErrLogTxt + '로그인 미수행 상태 또는 기타 오류';
|
|
|
|
AddLog(ErrLogTxt);
|
|
AIsBound := False;
|
|
Exit;
|
|
end;
|
|
|
|
msgType := 0;
|
|
if n >= 8 then
|
|
CopyMemory(@msgType, @buf[4], 4);
|
|
|
|
if (msgType = (ENVIRONMENT_SENSOR_TYPE or BIND_ACK)) or ((msgType = 0) and (n = 32) and not AIsBound) then begin
|
|
if DebugCheck.Checked then
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [' + AServerName + '] RCV Login : Rx<< ' + pkText);
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [' + AServerName + '] 로그인(BIND) 성공');
|
|
AIsBound := True;
|
|
aliveTimer.Interval := 30000;
|
|
aliveTimer.Enabled := True;
|
|
end
|
|
else if (msgType = (ENVIRONMENT_SENSOR_TYPE or ALIVE_ACK)) or ((msgType = 0) and (n = 32) and AIsBound) then begin
|
|
if DebugCheck.Checked then
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [' + AServerName + '] RCV Alive : Rx<< ' + pkText);
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [' + AServerName + '] 생존 신호 수신 완료');
|
|
end
|
|
else if (msgType = (ENVIRONMENT_SENSOR_TYPE or ENVIRONMENT_SENSOR_ADD_ACK)) or ((n > 0) and (n mod 32 = 0)) then begin
|
|
if DebugCheck.Checked then
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [' + AServerName + '] RCV Add : Rx<< ' + pkText);
|
|
if n = 32 then
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [' + AServerName + '] 정보 등록(ADD) 완료')
|
|
else
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [' + AServerName + '] 정보 등록(ADD) ' + IntToStr(n div 32) + '건 일괄 완료 (길이: ' + IntToStr(n) + ')');
|
|
end
|
|
else if msgType = (ENVIRONMENT_SENSOR_TYPE or ENVIRONMENT_SENSOR_REP) then begin
|
|
if DebugCheck.Checked then
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [' + AServerName + '] RCV Req : Rx<< ' + pkText);
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [' + AServerName + '] 정보 요청 응답 완료');
|
|
end
|
|
else if msgType = (ENVIRONMENT_SENSOR_TYPE or ENVIRONMENT_SENSOR_MULTI_REP) then begin
|
|
if DebugCheck.Checked then
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [' + AServerName + '] RCV ReqMulti : Rx<< ' + pkText);
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [' + AServerName + '] 정보 요청(멀티) 응답 완료');
|
|
end
|
|
else begin
|
|
if DebugCheck.Checked then
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [' + AServerName + '] RCV Unknown (' + IntToStr(n) + ' bytes): Rx<< ' + pkText);
|
|
end;
|
|
end;
|
|
|
|
procedure TForm1.csRead(Sender: TObject; Socket: TCustomWinSocket);
|
|
begin
|
|
ProcessServerRead(Socket, '서버1', FIsBound);
|
|
end;
|
|
|
|
procedure TForm1.csHome2Read(Sender: TObject; Socket: TCustomWinSocket);
|
|
begin
|
|
ProcessServerRead(Socket, '서버2', FIsBound2);
|
|
end;
|
|
|
|
procedure TForm1.SendLoginPacket(ASocket: TCustomWinSocket; const AServerName: string; const ALoginId: string; const ALoginPw: string);
|
|
var
|
|
pk : array[0..27+103+1] of byte;
|
|
i, j : integer;
|
|
str : string;
|
|
pkText : ansistring;
|
|
begin
|
|
if (ASocket = nil) or (not ASocket.Connected) then Exit;
|
|
|
|
FillMemory(@pk, length(pk), 0);
|
|
pkText := '';
|
|
|
|
i := $12345678;
|
|
CopyMemory(@pk[0],@i,4);
|
|
|
|
i := ENVIRONMENT_SENSOR_TYPE or BIND;
|
|
CopyMemory(@pk[4],@i,4);
|
|
|
|
i := 104;
|
|
CopyMemory(@pk[8],@i,4);
|
|
|
|
i := 10;
|
|
CopyMemory(@pk[12],@i,4);
|
|
|
|
i := 100;
|
|
CopyMemory(@pk[16],@i,4);
|
|
|
|
i := 1000;
|
|
CopyMemory(@pk[20],@i,4);
|
|
|
|
i := 1234;
|
|
CopyMemory(@pk[24],@i,4);
|
|
|
|
i := 123;
|
|
CopyMemory(@pk[28],@i,4);
|
|
|
|
i := 456;
|
|
CopyMemory(@pk[32],@i,4);
|
|
|
|
i := 789;
|
|
CopyMemory(@pk[36],@i,4);
|
|
CopyMemory(@pk[40],@i,4);
|
|
CopyMemory(@pk[44],@i,4);
|
|
CopyMemory(@pk[48],@i,4);
|
|
|
|
str := ALoginId;
|
|
for i := 1 to length(str) do begin
|
|
if 52 + i - 1 < 92 then
|
|
pk[52+i-1] := ord(str[i]);
|
|
end;
|
|
|
|
str := '';
|
|
if (Length(ALoginPw) >= 2) and (Length(ALoginPw) mod 2 = 0) then begin
|
|
j := 1;
|
|
while (j <= Length(ALoginPw)) and (CharInSet(ALoginPw[j], ['0'..'9', 'a'..'f', 'A'..'F'])) do
|
|
Inc(j);
|
|
|
|
if j > Length(ALoginPw) then begin
|
|
j := 1;
|
|
while j < Length(ALoginPw) do begin
|
|
str := str + chr(StrToIntDef('$' + Copy(ALoginPw, j, 2), 0));
|
|
Inc(j, 2);
|
|
end;
|
|
end
|
|
else
|
|
str := ALoginPw;
|
|
end
|
|
else
|
|
str := ALoginPw;
|
|
|
|
for i := 1 to length(str) do begin
|
|
if 92 + i - 1 < 132 then
|
|
pk[92+i-1] := ord(str[i]);
|
|
end;
|
|
|
|
for i := 0 to 131 do pkText := pkText + format('%0.2X,',[ord(pk[i])]);
|
|
|
|
ASocket.SendBuf(pk, 132);
|
|
|
|
if DebugCheck.Checked then
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [' + AServerName + '] Send Login : Tx>> ' + pkText);
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [' + AServerName + '] 로그인 패킷 전송 완료 (ID: ' + ALoginId + ')');
|
|
end;
|
|
|
|
procedure TForm1.loginBtnClick(Sender: TObject);
|
|
begin
|
|
if cs.Active then
|
|
SendLoginPacket(cs.Socket, '서버1', LoginId, LoginPw);
|
|
if FUseServer2 and csHome2.Active then
|
|
SendLoginPacket(csHome2.Socket, '서버2', LoginId2, LoginPw2);
|
|
end;
|
|
|
|
procedure TForm1.LogoutBtnClick(Sender: TObject);
|
|
begin
|
|
FIsBound := False;
|
|
FIsBound2 := False;
|
|
aliveTimer.Enabled := False;
|
|
if cs.Active then
|
|
cs.Close;
|
|
if csHome2.Active then
|
|
csHome2.Close;
|
|
csConnectBtn.Enabled := True;
|
|
end;
|
|
|
|
procedure TForm1.SendAlivePacket(ASocket: TCustomWinSocket; const AServerName: string);
|
|
var
|
|
pk : array[0..27+35+1] of byte;
|
|
i : integer;
|
|
pkText : ansistring;
|
|
begin
|
|
if (ASocket = nil) or (not ASocket.Connected) then Exit;
|
|
|
|
FillMemory(@pk, length(pk), 0);
|
|
pkText := '';
|
|
|
|
i := $12345678;
|
|
CopyMemory(@pk[0],@i,4);
|
|
|
|
i := ENVIRONMENT_SENSOR_TYPE or ALIVE;
|
|
CopyMemory(@pk[4],@i,4);
|
|
|
|
i := 36;
|
|
CopyMemory(@pk[8],@i,4);
|
|
|
|
i := 10;
|
|
CopyMemory(@pk[12],@i,4);
|
|
|
|
i := 100;
|
|
CopyMemory(@pk[16],@i,4);
|
|
|
|
i := 1000;
|
|
CopyMemory(@pk[20],@i,4);
|
|
|
|
i := 1234;
|
|
CopyMemory(@pk[24],@i,4);
|
|
|
|
i := 123;
|
|
CopyMemory(@pk[28],@i,4);
|
|
|
|
i := 456;
|
|
CopyMemory(@pk[32],@i,4);
|
|
|
|
i := 789;
|
|
CopyMemory(@pk[36],@i,4);
|
|
CopyMemory(@pk[40],@i,4);
|
|
CopyMemory(@pk[44],@i,4);
|
|
CopyMemory(@pk[48],@i,4);
|
|
|
|
i := 12;
|
|
CopyMemory(@pk[52],@i,4);
|
|
|
|
i := 34;
|
|
CopyMemory(@pk[56],@i,4);
|
|
|
|
i := 56;
|
|
CopyMemory(@pk[60],@i,4);
|
|
|
|
for i := 0 to 63 do pkText := pkText + format('%0.2X,',[ord(pk[i])]);
|
|
|
|
ASocket.SendBuf(pk, 64);
|
|
|
|
if DebugCheck.Checked then
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [' + AServerName + '] Send Alive : Tx>> ' + pkText);
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [' + AServerName + '] 생존 신호 패킷 전송 완료');
|
|
end;
|
|
|
|
procedure TForm1.AliveBtnClick(Sender: TObject);
|
|
begin
|
|
if cs.Active and FIsBound then
|
|
SendAlivePacket(cs.Socket, '서버1');
|
|
if FUseServer2 and csHome2.Active and FIsBound2 then
|
|
SendAlivePacket(csHome2.Socket, '서버2');
|
|
end;
|
|
|
|
procedure TForm1.aliveTimerTimer(Sender: TObject);
|
|
begin
|
|
if cs.Active and FIsBound then
|
|
SendAlivePacket(cs.Socket, '서버1');
|
|
if FUseServer2 and csHome2.Active and FIsBound2 then
|
|
SendAlivePacket(csHome2.Socket, '서버2');
|
|
end;
|
|
|
|
function TForm1.TruncateUTF8ToMaxBytes(const S: string; MaxBytes: Integer): TBytes;
|
|
var
|
|
i, ByteCount: Integer;
|
|
CharLen: Integer;
|
|
CharBytes: TBytes;
|
|
begin
|
|
Result := nil;
|
|
ByteCount := 0;
|
|
i := 1;
|
|
while i <= Length(S) do
|
|
begin
|
|
if (Ord(S[i]) >= $D800) and (Ord(S[i]) <= $DBFF) and (i < Length(S)) then
|
|
CharLen := 2
|
|
else
|
|
CharLen := 1;
|
|
|
|
try
|
|
CharBytes := TEncoding.UTF8.GetBytes(Copy(S, i, CharLen));
|
|
except
|
|
CharBytes := nil;
|
|
end;
|
|
|
|
if (Length(CharBytes) = 0) or (ByteCount + Length(CharBytes) > MaxBytes) then
|
|
Break;
|
|
|
|
Result := Result + CharBytes;
|
|
Inc(ByteCount, Length(CharBytes));
|
|
Inc(i, CharLen);
|
|
end;
|
|
end;
|
|
|
|
procedure TForm1.addBtnClick(Sender: TObject);
|
|
var
|
|
pk : array[0..27+111+1] of byte;
|
|
i : integer;
|
|
f : single;
|
|
str : string;
|
|
utf8Bytes: TBytes;
|
|
pkText : ansistring;
|
|
handle : HWND;
|
|
sendSuccess : boolean;
|
|
begin
|
|
try
|
|
FillMemory(@pk, length(pk), 0);
|
|
|
|
pkText := '';
|
|
|
|
i := $12345678;
|
|
CopyMemory(@pk[0],@i,4);
|
|
|
|
i := ENVIRONMENT_SENSOR_TYPE or ENVIRONMENT_SENSOR_ADD;
|
|
CopyMemory(@pk[4],@i,4);
|
|
|
|
i := 112;
|
|
CopyMemory(@pk[8],@i,4);
|
|
|
|
i := 10;
|
|
CopyMemory(@pk[12],@i,4);
|
|
|
|
i := 100;
|
|
CopyMemory(@pk[16],@i,4);
|
|
|
|
i := 1000;
|
|
CopyMemory(@pk[20],@i,4);
|
|
|
|
i := 1234;
|
|
CopyMemory(@pk[24],@i,4);
|
|
|
|
i := StrToIntDef(nArea.Text,0);
|
|
CopyMemory(@pk[28],@i,4);
|
|
|
|
str := szDataTime.Text;
|
|
for i := 1 to length(str) do begin
|
|
pk[32+i-1] := ord(str[i]);
|
|
end;
|
|
|
|
str := szMangName.Text;
|
|
for i := 1 to length(str) do begin
|
|
pk[52+i-1] := ord(str[i]);
|
|
end;
|
|
|
|
str := szDataTerm.Text;
|
|
for i := 1 to length(str) do begin
|
|
pk[72+i-1] := ord(str[i]);
|
|
end;
|
|
|
|
str := szStationName.Text;
|
|
utf8Bytes := TruncateUTF8ToMaxBytes(str, 20);
|
|
for i := 0 to Length(utf8Bytes) - 1 do
|
|
pk[92 + i] := utf8Bytes[i];
|
|
|
|
i := StrToIntDef(nPm10Value.Text,0);
|
|
CopyMemory(@pk[112],@i,4);
|
|
|
|
i := StrToIntDef(nPm25Value.Text,0);
|
|
CopyMemory(@pk[116],@i,4);
|
|
|
|
f := StrToFloatDef(fCtValue.Text,0);
|
|
CopyMemory(@pk[120],@f,4);
|
|
|
|
f := StrToFloatDef(fRhValue.Text,0);
|
|
CopyMemory(@pk[124],@f,4);
|
|
|
|
f := StrToFloatDef(fO3Value.Text,0);
|
|
CopyMemory(@pk[128],@f,4);
|
|
|
|
f := StrToFloatDef(fNo2Value.Text,0);
|
|
CopyMemory(@pk[132],@f,4);
|
|
|
|
f := StrToFloatDef(fUvValue.Text,0);
|
|
CopyMemory(@pk[136],@f,4);
|
|
|
|
for i := 0 to 139 do pkText := pkText + format('%0.2X,',[ord(pk[i])]);
|
|
|
|
sendSuccess := false;
|
|
|
|
if cs.Active and FIsBound then begin
|
|
cs.Socket.SendBuf(pk, 140);
|
|
sendSuccess := true;
|
|
if DebugCheck.Checked then
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [서버1] Send Add : Tx>> ' + pkText);
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [서버1] 정보 등록 패킷 전송 완료 : ' + StName[AddCount].name);
|
|
end;
|
|
|
|
if FUseServer2 and csHome2.Active and FIsBound2 then begin
|
|
csHome2.Socket.SendBuf(pk, 140);
|
|
sendSuccess := true;
|
|
if DebugCheck.Checked then
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [서버2] Send Add : Tx>> ' + pkText);
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [서버2] 정보 등록 패킷 전송 완료 : ' + StName[AddCount].name);
|
|
end;
|
|
|
|
handle := FindWindow(nil, '미세먼지 모니터링 시스템');
|
|
if handle <> 0 then begin
|
|
if sendSuccess then
|
|
PostMessage(handle, WM_USER + 1, 1, 1)
|
|
else
|
|
PostMessage(handle, WM_USER + 2, 0, 0);
|
|
if DebugCheck.Checked then
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] 윈도우 메시지 전송.');
|
|
end;
|
|
|
|
except
|
|
on E: Exception do begin
|
|
handle := FindWindow(nil, '미세먼지 모니터링 시스템');
|
|
if handle <> 0 then begin
|
|
PostMessage(handle, WM_USER + 2, 0, 0);
|
|
if DebugCheck.Checked then
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] 윈도우 메시지(실패) 전송.');
|
|
end;
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] 정보 등록 패킷 전송 오류: ' + E.Message);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TForm1.reqBtnClick(Sender: TObject);
|
|
var
|
|
pk : array[0..27+3+1] of byte;
|
|
i : integer;
|
|
pkText : ansistring;
|
|
begin
|
|
FillMemory(@pk, length(pk), 0);
|
|
|
|
pkText := '';
|
|
|
|
i := $12345678;
|
|
CopyMemory(@pk[0],@i,4);
|
|
|
|
i := ENVIRONMENT_SENSOR_TYPE or ENVIRONMENT_SENSOR_REQ;
|
|
CopyMemory(@pk[4],@i,4);
|
|
|
|
i := 4;
|
|
CopyMemory(@pk[8],@i,4);
|
|
|
|
i := 10;
|
|
CopyMemory(@pk[12],@i,4);
|
|
|
|
i := 100;
|
|
CopyMemory(@pk[16],@i,4);
|
|
|
|
i := 1000;
|
|
CopyMemory(@pk[20],@i,4);
|
|
|
|
i := 1234;
|
|
CopyMemory(@pk[24],@i,4);
|
|
|
|
i := StrToIntDef(nAreaReq.Text, 0);
|
|
CopyMemory(@pk[28],@i,4);
|
|
|
|
for i := 0 to 31 do pkText := pkText + format('%0.2X,',[ord(pk[i])]);
|
|
|
|
SEND_TYPE := 4;
|
|
|
|
if cs.Active and FIsBound then begin
|
|
cs.Socket.SendBuf(pk, 32);
|
|
if DebugCheck.Checked then
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [서버1] Send Req : Tx>> ' + pkText);
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [서버1] 정보 요청 패킷 전송 완료');
|
|
end;
|
|
|
|
if FUseServer2 and csHome2.Active and FIsBound2 then begin
|
|
csHome2.Socket.SendBuf(pk, 32);
|
|
if DebugCheck.Checked then
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [서버2] Send Req : Tx>> ' + pkText);
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [서버2] 정보 요청 패킷 전송 완료');
|
|
end;
|
|
end;
|
|
|
|
procedure TForm1.reqMultiBtnClick(Sender: TObject);
|
|
var
|
|
pk : array[0..27+7+1] of byte;
|
|
i : integer;
|
|
pkText : ansistring;
|
|
begin
|
|
FillMemory(@pk, length(pk), 0);
|
|
|
|
pkText := '';
|
|
|
|
i := $12345678;
|
|
CopyMemory(@pk[0],@i,4);
|
|
|
|
i := ENVIRONMENT_SENSOR_TYPE or ENVIRONMENT_SENSOR_MULTI_REQ;
|
|
CopyMemory(@pk[4],@i,4);
|
|
|
|
i := 8;
|
|
CopyMemory(@pk[8],@i,4);
|
|
|
|
i := 10;
|
|
CopyMemory(@pk[12],@i,4);
|
|
|
|
i := 100;
|
|
CopyMemory(@pk[16],@i,4);
|
|
|
|
i := 1000;
|
|
CopyMemory(@pk[20],@i,4);
|
|
|
|
i := 1234;
|
|
CopyMemory(@pk[24],@i,4);
|
|
|
|
i := StrToIntDef(nAreaReq.Text, 0);
|
|
CopyMemory(@pk[28],@i,4);
|
|
|
|
i := 0;
|
|
CopyMemory(@pk[32],@i,4);
|
|
|
|
for i := 0 to 35 do pkText := pkText + format('%0.2X,',[ord(pk[i])]);
|
|
|
|
SEND_TYPE := 5;
|
|
|
|
if cs.Active and FIsBound then begin
|
|
cs.Socket.SendBuf(pk, 36);
|
|
if DebugCheck.Checked then
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [서버1] Send ReqMulti : Tx>> ' + pkText);
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [서버1] 정보 요청(멀티) 패킷 전송 완료');
|
|
end;
|
|
|
|
if FUseServer2 and csHome2.Active and FIsBound2 then begin
|
|
csHome2.Socket.SendBuf(pk, 36);
|
|
if DebugCheck.Checked then
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [서버2] Send ReqMulti : Tx>> ' + pkText);
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] [서버2] 정보 요청(멀티) 패킷 전송 완료');
|
|
end;
|
|
end;
|
|
|
|
procedure TForm1.readBtnClick(Sender: TObject);
|
|
var
|
|
response, response2: TIdBytes;
|
|
i : integer;
|
|
text : ansistring;
|
|
sensorData : string;
|
|
splitData : TArray<string>;
|
|
begin
|
|
if (AddCount < 0) or (AddCount >= Length(StName)) then Exit;
|
|
if StName[AddCount].id <= 0 then Exit;
|
|
|
|
try
|
|
try
|
|
if FCollectMode = 1 then begin
|
|
// 1. MariaDB 수집 모드
|
|
if not FDCon.Connected then begin
|
|
InitMariaDB;
|
|
if not FDCon.Connected then Exit;
|
|
end;
|
|
|
|
FDQuery1.SQL.Clear;
|
|
FDQuery1.SQL.Text := format('SELECT * FROM real_time WHERE BlinkerName = "%s"', [StName[AddCount].name]);
|
|
FDQuery1.Open;
|
|
|
|
if FDQuery1.RecordCount > 0 then begin
|
|
sensorData := FDQuery1.FieldByName('SensorData').AsString;
|
|
splitData := sensorData.Split(['/']);
|
|
|
|
if Length(splitData) >= 4 then begin
|
|
nPm10Value.Text := splitData[0];
|
|
nPm25Value.Text := splitData[1];
|
|
fCtValue.Text := splitData[2];
|
|
fRhValue.Text := splitData[3];
|
|
end;
|
|
|
|
nArea.Text := IntToStr(StName[AddCount].id);
|
|
szStationName.Text := StName[AddCount].name;
|
|
szDataTime.Text := FormatDateTime('yyyy-mm-dd hh:nn:ss', Now);
|
|
|
|
if DebugCheck.Checked then
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] RCV DB Dust [' + StName[AddCount].name + ']: ' + sensorData);
|
|
|
|
addBtn.OnClick(nil);
|
|
end
|
|
else begin
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] DB 데이터 없음 [' + StName[AddCount].name + ']');
|
|
end;
|
|
end
|
|
else begin
|
|
// 0. MODBUS TCP 수집 모드
|
|
cs2.Host := '127.0.0.1';
|
|
cs2.Port := 502;
|
|
cs2.ConnectTimeout := 2000;
|
|
cs2.ReadTimeout := 2000;
|
|
cs2.Connect;
|
|
|
|
if DebugCheck.Checked then
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] 미세먼지 수집기 접속 완료');
|
|
|
|
SetLength(response, 12);
|
|
response[0] := 0;
|
|
response[1] := 1;
|
|
response[2] := 0;
|
|
response[3] := 0;
|
|
response[4] := 0;
|
|
response[5] := 6;
|
|
response[6] := 1;
|
|
response[7] := $03;
|
|
response[8] := 0;
|
|
response[9] := IfThen(AutoChk, (StName[AddCount].id - 1) * 3, StrToIntDef(miseAddress.Text, 1));
|
|
response[10] := 0;
|
|
response[11] := 3;
|
|
|
|
cs2.IOHandler.Write(response);
|
|
cs2.IOHandler.WriteBufferFlush;
|
|
cs2.IOHandler.ReadBytes(response2, 15);
|
|
|
|
nPm10Value.Text := IntToStr(ord(response2[14]));
|
|
nPm25Value.Text := IntToStr(ord(response2[12]));
|
|
nArea.Text := IntToStr(StName[AddCount].id);
|
|
szStationName.Text := StName[AddCount].name;
|
|
szDataTime.Text := FormatDateTime('yyyy-mm-dd hh:nn:ss', Now);
|
|
|
|
text := '';
|
|
for i := 0 to 14 do text := text + format('%0.2X,',[ord(response2[i])]);
|
|
|
|
if DebugCheck.Checked then
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] RCV MODBUS Dust : ' + text);
|
|
|
|
addBtn.OnClick(nil);
|
|
end;
|
|
except
|
|
on E: Exception do begin
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] 미세먼지 수집 실패 [' + StName[AddCount].name + ']: ' + E.Message);
|
|
end;
|
|
end;
|
|
finally
|
|
if (FCollectMode = 0) and cs2.Connected then begin
|
|
try
|
|
cs2.Disconnect;
|
|
except
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TForm1.tmrInitTimer(Sender: TObject);
|
|
begin
|
|
TotalTimer.Enabled := false;
|
|
aliveTimer.Interval := 30000;
|
|
aliveTimer.Enabled := false;
|
|
tmrUpInterval.Enabled := true;
|
|
tmrInit.Enabled := false;
|
|
tmrInit.OnTimer := nil;
|
|
|
|
if AutoChk then begin
|
|
csConnectBtnClick(nil);
|
|
end;
|
|
end;
|
|
|
|
procedure TForm1.tmrUpIntervalTimer(Sender: TObject);
|
|
var
|
|
i : integer;
|
|
anyBound : boolean;
|
|
begin
|
|
if not AutoChk then Exit;
|
|
|
|
// 1. 서버1 재연결 체크
|
|
if not cs.Active then begin
|
|
if not FIsConnecting then begin
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] 서버1 미연결 - 재연결 시도');
|
|
ConnectServer1;
|
|
end;
|
|
end;
|
|
|
|
// 2. 서버2 재연결 체크
|
|
if FUseServer2 and not csHome2.Active then begin
|
|
if not FIsConnecting2 then begin
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] 서버2 미연결 - 재연결 시도');
|
|
ConnectServer2;
|
|
end;
|
|
end;
|
|
|
|
// 3. 인증 완료(BIND)된 서버가 최소 1개 이상 있는지 확인
|
|
anyBound := FIsBound or (FUseServer2 and FIsBound2);
|
|
if not anyBound then begin
|
|
AddLog('[' + FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + '] BIND 대기 중 (인증 완료된 서버 없음)');
|
|
Exit;
|
|
end;
|
|
|
|
// 4. MariaDB 모드일 때 최신 관측소 목록 갱신
|
|
if FCollectMode = 1 then
|
|
LoadStationFromDB;
|
|
|
|
// 5. 각 관측소별 데이터 수집 및 전송
|
|
for i := 0 to Length(StName) - 1 do begin
|
|
if StName[i].id > 0 then begin
|
|
AddCount := i;
|
|
readBtnClick(nil);
|
|
Sleep(50);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TForm1.TotalTimerTimer(Sender: TObject);
|
|
begin
|
|
end;
|
|
|
|
function TForm1.MD5Str(const S: String): String;
|
|
var IdMD5: TIdHashMessageDigest5;
|
|
begin
|
|
IdMD5:=TIdHashMessageDigest5.Create;
|
|
try
|
|
Result:=IdMD5.HashStringAsHex(S);
|
|
finally
|
|
FreeAndNil(IdMD5);
|
|
end;
|
|
end;
|
|
|
|
function TForm1.MD5File(const FilePath: String): String;
|
|
var
|
|
IdMD5: TIdHashMessageDigest5;
|
|
fStream: TFileStream;
|
|
begin
|
|
Result:='';
|
|
if not FileExists(FilePath) then Exit;
|
|
|
|
IdMD5:=TIdHashMessageDigest5.Create;
|
|
fStream:=TFileStream.Create(FilePath, fmOpenRead or fmShareDenyWrite);
|
|
try
|
|
Result:=IdMD5.HashStreamAsHex(fStream);
|
|
finally
|
|
FreeAndNil(fStream);
|
|
FreeAndNil(IdMD5);
|
|
end;
|
|
end;
|
|
|
|
function TForm1.CRC16(Data: AnsiString): AnsiString;
|
|
var
|
|
i,j,iSum,f : Integer;
|
|
begin
|
|
iSum := $FFFF;
|
|
for i := 1 to Length(Data) do
|
|
begin
|
|
iSum := iSum xor Ord(Data[i]);
|
|
for j := 1 to 8 do
|
|
begin
|
|
f := iSum and $0001;
|
|
iSum := iSum shr 1;
|
|
if f = 1 then iSum := iSum xor $A001;
|
|
end;
|
|
end;
|
|
Result := AnsiChar(Lo(iSum)) + AnsiChar(Hi(iSum));
|
|
end;
|
|
|
|
procedure TForm1.ClrBtnClick(Sender: TObject);
|
|
begin
|
|
LogMemo.Clear;
|
|
end;
|
|
|
|
procedure TForm1.ExitBtnClick(Sender: TObject);
|
|
begin
|
|
ExitOk := true;
|
|
Form1.Close;
|
|
end;
|
|
|
|
function TForm1.ConvertUTF8ToANSI(const UTF8Text: string): AnsiString;
|
|
var
|
|
UTF8Bytes: TBytes;
|
|
UTF8Stream: TBytesStream;
|
|
ANSIText: AnsiString;
|
|
begin
|
|
UTF8Bytes := TEncoding.UTF8.GetBytes(UTF8Text);
|
|
UTF8Stream := TBytesStream.Create(UTF8Bytes);
|
|
try
|
|
ANSIText := TEncoding.ANSI.GetString(UTF8Stream.Bytes, 0, UTF8Stream.Size);
|
|
Result := ANSIText;
|
|
finally
|
|
UTF8Stream.Free;
|
|
end;
|
|
end;
|
|
|
|
end.
|