Dust/SOURCE/kocom_Homenet_D10.4/kocomHomenet.pas
2026-09-04 10:15:33 +09:00

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.