MMCL/SOURCE/agents/delphi_led_agent/__history/uMain.pas.~8~
2026-09-04 11:27:31 +09:00

397 lines
12 KiB
Plaintext

unit uMain;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls,
System.IniFiles, Data.DB, U_DM, uLogManagerThread;
type
TfrmMain = class(TForm)
GroupBox1: TGroupBox;
btnRedOn: TButton;
btnRedBlink: TButton;
btnRedOff: TButton;
btnYellowOn: TButton;
btnYellowBlink: TButton;
btnYellowOff: TButton;
btnGreenOn: TButton;
btnGreenBlink: TButton;
btnGreenOff: TButton;
btnBlueOn: TButton;
btnBlueBlink: TButton;
btnBlueOff: TButton;
btnWhiteOn: TButton;
btnWhiteBlink: TButton;
btnWhiteOff: TButton;
GroupBox2: TGroupBox;
btnSoundOff: TButton;
btnSound1: TButton;
btnSound2: TButton;
btnSound3: TButton;
btnSound4: TButton;
btnSound5: TButton;
Label1: TLabel;
edtIP1: TEdit;
edtIP2: TEdit;
edtIP3: TEdit;
edtIP4: TEdit;
GroupBox3: TGroupBox;
edtPort: TEdit;
rgModel: TRadioGroup;
btnStatRead: TButton;
btnReset: TButton;
btnExit: TButton;
GroupBox4: TGroupBox;
lbStatus: TListBox;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure btnLampClick(Sender: TObject);
procedure btnSoundClick(Sender: TObject);
procedure btnStatReadClick(Sender: TObject);
procedure btnResetClick(Sender: TObject);
procedure btnExitClick(Sender: TObject);
private
{ Private declarations }
c_pIdata: array[0..14] of Byte;
c_pIpadd: array[0..3] of Byte;
// 환경 설정 변수
FDBHost, FDBUser, FDBPass, FDBName: string;
FDBPort: Integer;
// 폴링 타이머
FPollingTimer: TTimer;
function SendCommand: Boolean;
procedure LogMessage(const Msg: string);
procedure LoadSettings;
procedure InitDB;
procedure OnPollingTimer(Sender: TObject);
public
{ Public declarations }
end;
var
frmMain: TfrmMain;
function Tcp_Qu_RW(iPort: Integer; var pbIp: Byte; var pbData: Byte): Boolean; stdcall; external 'Qtvc_dll.dll';
implementation
{$R *.dfm}
const
C_lampoff = 0;
C_lampon = 1;
C_lampblink = 2;
D_not = 100;
procedure TfrmMain.LoadSettings;
var
Ini: TIniFile;
begin
Ini := TIniFile.Create(ExtractFilePath(Application.ExeName) + 'settings.ini');
try
FDBHost := Ini.ReadString('DB', 'Host', '0.0.0.0');
FDBPort := Ini.ReadInteger('DB', 'Port', 33063);
FDBUser := Ini.ReadString('DB', 'User', 'mmcl_user');
FDBPass := Ini.ReadString('DB', 'Password', '');
FDBName := Ini.ReadString('DB', 'Database', 'mmcl_db');
finally
Ini.Free;
end;
end;
procedure TfrmMain.InitDB;
begin
DM.fdConnEtc.Close;
DM.fdConnEtc.Params.Clear;
DM.fdConnEtc.Params.Add('DriverID=MySQL');
DM.fdConnEtc.Params.Add('Server=' + FDBHost);
DM.fdConnEtc.Params.Add('Port=' + IntToStr(FDBPort));
DM.fdConnEtc.Params.Add('Database=' + FDBName);
DM.fdConnEtc.Params.Add('User_Name=' + FDBUser);
if FDBPass <> '' then
DM.fdConnEtc.Params.Add('Password=' + FDBPass);
DM.fdConnEtc.Params.Add('CharacterSet=utf8mb4');
try
DM.fdConnEtc.Connected := True;
LogMessage('DB 연결 성공 (' + FDBHost + ')');
AddLog_Thread('DB 연결 성공 (' + FDBHost + ')');
except
on E: Exception do
begin
LogMessage('DB 연결 실패: ' + E.Message);
AddLog_Thread('DB 연결 실패: ' + E.Message);
end;
end;
end;
procedure TfrmMain.FormCreate(Sender: TObject);
var
i: Integer;
begin
// Initialize LED data
for i := 0 to 14 do c_pIdata[i] := D_not;
c_pIdata[0] := 1; // 1-write, 0-read
c_pIdata[1] := 0; // type default
// 1. 로그 시스템 초기화
InitLogger(ExtractFilePath(Application.ExeName) + 'Logs', 'LEDAgent', rtDaily);
AddLog_Thread('=== LEDAgent 시작 ===');
// 2. INI 설정 로드
LoadSettings;
// 3. DB 연결
InitDB;
// 4. 폴링 타이머 가동 (1초=1000ms)
FPollingTimer := TTimer.Create(Self);
FPollingTimer.Interval := 1000;
FPollingTimer.OnTimer := OnPollingTimer;
FPollingTimer.Enabled := True;
end;
procedure TfrmMain.FormDestroy(Sender: TObject);
begin
if Assigned(FPollingTimer) then
begin
FPollingTimer.Enabled := False;
FPollingTimer.Free;
end;
if Assigned(DM) and DM.fdConnEtc.Connected then
DM.fdConnEtc.Close;
AddLog_Thread('=== LEDAgent 종료 ===');
StopLogger;
end;
procedure TfrmMain.OnPollingTimer(Sender: TObject);
var
SensorNo, DbLedPort: Integer;
DbLedIp: string;
T1, T2, T3: Integer;
IpParts: TArray<string>;
SendSuccess: Boolean;
i: Integer;
begin
FPollingTimer.Enabled := False; // 겹침 방지
try
if not DM.fdConnEtc.Connected then Exit;
DM.fdQryEtc.Close;
DM.fdQryEtc.SQL.Text :=
'SELECT sensor_no, target_ch1_statusID, target_ch2_statusID, target_ch3_statusID, led_ip, led_port ' +
'FROM sensor_info ' +
'WHERE sensor_typeid = 2 ' +
' AND (target_ch1_statusID != value_ch1_statusID ' +
' OR target_ch2_statusID != value_ch2_statusID ' +
' OR target_ch3_statusID != value_ch3_statusID) ' +
'LIMIT 1';
try
DM.fdQryEtc.Open;
except
on E: Exception do
begin
AddLog_Thread('DB 폴링 에러: ' + E.Message);
Exit;
end;
end;
if not DM.fdQryEtc.IsEmpty then
begin
SensorNo := DM.fdQryEtc.FieldByName('sensor_no').AsInteger;
T1 := DM.fdQryEtc.FieldByName('target_ch1_statusID').AsInteger; // Green
T2 := DM.fdQryEtc.FieldByName('target_ch2_statusID').AsInteger; // Yellow
T3 := DM.fdQryEtc.FieldByName('target_ch3_statusID').AsInteger; // Red
DbLedIp := DM.fdQryEtc.FieldByName('led_ip').AsString;
DbLedPort := DM.fdQryEtc.FieldByName('led_port').AsInteger;
if DbLedPort = 0 then DbLedPort := 20000;
LogMessage(Format('DB 명령 감지 - Sensor:%d, IP:%s (Target: G:%d Y:%d R:%d)', [SensorNo, DbLedIp, T1, T2, T3]));
AddLog_Thread(Format('DB 명령 감지 - Sensor:%d, IP:%s (Target: G:%d Y:%d R:%d)', [SensorNo, DbLedIp, T1, T2, T3]));
// 1. LED 전송용 데이터 배열 구성
for i := 0 to 14 do c_pIdata[i] := D_not;
c_pIdata[0] := 1; // write 모드
c_pIdata[1] := 0; // 모델 기본값
c_pIdata[7] := 0; // 사운드 끄기 기본
c_pIdata[4] := T1; // Green (2: Red, 3: Yellow, 4: Green)
c_pIdata[3] := T2; // Yellow
c_pIdata[2] := T3; // Red
// 2. IP 및 Port 준비 (DB 기준)
IpParts := DbLedIp.Split(['.']);
if Length(IpParts) = 4 then
begin
c_pIpadd[0] := StrToIntDef(IpParts[0], 192);
c_pIpadd[1] := StrToIntDef(IpParts[1], 168);
c_pIpadd[2] := StrToIntDef(IpParts[2], 200);
c_pIpadd[3] := StrToIntDef(IpParts[3], 114);
end;
// 3. DLL 호출하여 하드웨어 제어
SendSuccess := Tcp_Qu_RW(DbLedPort, c_pIpadd[0], c_pIdata[0]);
if SendSuccess then
begin
AddLog_Thread('하드웨어 제어 성공');
LogMessage('하드웨어 제어 성공');
// 4. DB 상태 갱신 (Handshake 완료)
DM.fdQryEtc.Close;
DM.fdQryEtc.SQL.Text :=
'UPDATE sensor_info ' +
'SET value_ch1_statusID = :v1, value_ch2_statusID = :v2, value_ch3_statusID = :v3 ' +
'WHERE sensor_no = :sno';
DM.fdQryEtc.ParamByName('v1').AsInteger := T1;
DM.fdQryEtc.ParamByName('v2').AsInteger := T2;
DM.fdQryEtc.ParamByName('v3').AsInteger := T3;
DM.fdQryEtc.ParamByName('sno').AsInteger := SensorNo;
DM.fdQryEtc.ExecSQL;
AddLog_Thread('DB 완료 갱신 (Handshake 종료)');
LogMessage('DB 반영 완료');
end
else
begin
AddLog_Thread('TCP/IP 제어 실패 (하드웨어 연결 확인)');
LogMessage('LED 통신 실패');
end;
end;
finally
FPollingTimer.Enabled := True;
end;
end;
//---------------------------------------------------------
// 기존 UI 매뉴얼 테스트 로직 (수동 테스트용 유지)
//---------------------------------------------------------
procedure TfrmMain.LogMessage(const Msg: string);
begin
lbStatus.Items.Insert(0, FormatDateTime('hh:nn:ss', Now) + ' ' + Msg);
end;
function TfrmMain.SendCommand: Boolean;
var
iPort: Integer;
begin
Result := False;
try
c_pIpadd[0] := StrToIntDef(edtIP1.Text, 192);
c_pIpadd[1] := StrToIntDef(edtIP2.Text, 168);
c_pIpadd[2] := StrToIntDef(edtIP3.Text, 200);
c_pIpadd[3] := StrToIntDef(edtIP4.Text, 114);
iPort := StrToIntDef(edtPort.Text, 20000);
c_pIdata[1] := rgModel.ItemIndex;
Result := Tcp_Qu_RW(iPort, c_pIpadd[0], c_pIdata[0]);
if Result then
LogMessage('[Success send]')
else
LogMessage('[Send Error]');
except
on E: Exception do
LogMessage('[Error] ' + E.Message);
end;
end;
procedure TfrmMain.btnLampClick(Sender: TObject);
var
Btn: TButton;
ColorIdx: Integer;
Action: Integer;
i: Integer;
begin
for i := 2 to 6 do c_pIdata[i] := D_not;
c_pIdata[7] := D_not;
c_pIdata[0] := 1;
Btn := Sender as TButton;
if (Btn = btnRedOn) or (Btn = btnRedBlink) or (Btn = btnRedOff) then ColorIdx := 2
else if (Btn = btnYellowOn) or (Btn = btnYellowBlink) or (Btn = btnYellowOff) then ColorIdx := 3
else if (Btn = btnGreenOn) or (Btn = btnGreenBlink) or (Btn = btnGreenOff) then ColorIdx := 4
else if (Btn = btnBlueOn) or (Btn = btnBlueBlink) or (Btn = btnBlueOff) then ColorIdx := 5
else if (Btn = btnWhiteOn) or (Btn = btnWhiteBlink) or (Btn = btnWhiteOff) then ColorIdx := 6
else Exit;
if Btn.Caption = 'ON' then Action := C_lampon
else if Btn.Caption = 'ON/OFF' then Action := C_lampblink
else Action := C_lampoff;
c_pIdata[ColorIdx] := Action;
SendCommand;
end;
procedure TfrmMain.btnSoundClick(Sender: TObject);
var
Btn: TButton;
i: Integer;
begin
for i := 2 to 6 do c_pIdata[i] := D_not;
c_pIdata[0] := 1;
Btn := Sender as TButton;
if Btn = btnSoundOff then c_pIdata[7] := 0
else if Btn = btnSound1 then c_pIdata[7] := 1
else if Btn = btnSound2 then c_pIdata[7] := 2
else if Btn = btnSound3 then c_pIdata[7] := 3
else if Btn = btnSound4 then c_pIdata[7] := 4
else if Btn = btnSound5 then c_pIdata[7] := 5
else c_pIdata[7] := D_not;
SendCommand;
end;
procedure TfrmMain.btnStatReadClick(Sender: TObject);
var
iPort: Integer;
Success: Boolean;
StatusStr: string;
begin
try
c_pIpadd[0] := StrToIntDef(edtIP1.Text, 192);
c_pIpadd[1] := StrToIntDef(edtIP2.Text, 168);
c_pIpadd[2] := StrToIntDef(edtIP3.Text, 200);
c_pIpadd[3] := StrToIntDef(edtIP4.Text, 114);
iPort := StrToIntDef(edtPort.Text, 20000);
c_pIdata[0] := 0;
Success := Tcp_Qu_RW(iPort, c_pIpadd[0], c_pIdata[0]);
if Success then
begin
StatusStr := '[Read Success] ';
if c_pIdata[2] = 0 then StatusStr := StatusStr + 'R-OFF ' else if c_pIdata[2] = 1 then StatusStr := StatusStr + 'R-ON ' else if c_pIdata[2] = 2 then StatusStr := StatusStr + 'R-BLINK ';
if c_pIdata[3] = 0 then StatusStr := StatusStr + 'Y-OFF ' else if c_pIdata[3] = 1 then StatusStr := StatusStr + 'Y-ON ' else if c_pIdata[3] = 2 then StatusStr := StatusStr + 'Y-BLINK ';
if c_pIdata[4] = 0 then StatusStr := StatusStr + 'G-OFF ' else if c_pIdata[4] = 1 then StatusStr := StatusStr + 'G-ON ' else if c_pIdata[4] = 2 then StatusStr := StatusStr + 'G-BLINK ';
LogMessage(StatusStr);
end
else
LogMessage('[Read Error]');
except
on E: Exception do
LogMessage('[Error] ' + E.Message);
end;
end;
procedure TfrmMain.btnResetClick(Sender: TObject);
begin
lbStatus.Clear;
end;
procedure TfrmMain.btnExitClick(Sender: TObject);
begin
Close;
end;
end.