unit uMain; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl., Vcl.StdCtrlsStdCtrls, Data.DB, FireDAC.Comp.Client, FireDAC.Stan.Def, FireDAC.Phys.MySQL, FireDAC.DApt, System.JSON, Vcl.StdCtrls; // JSON 파싱용 // 사용자가 추가한 MQTT 및 매니저 라이브러리가 있을 경우 이곳에 uses 처리 // uses UMQTTClient, uNILMTypes; type TfMain = class(TForm) btnStart: TButton; btnStop: TButton; memLog: TMemo; procedure btnStartClick(Sender: TObject); procedure btnStopClick(Sender: TObject); private FDConn: TFDConnection; FDPhysMySQLDriverLink: TFDPhysMySQLDriverLink; // FClient: TMQTTClient; // 실제 사용하실 컴포넌트 객체 선언 procedure SetupDatabase; procedure LogMsg(const AMsg: string); procedure InsertNilmData(SensorNo: Integer; PayloadJSON: string); // MQTT 수신 이벤트 콜백 시그니처 (컴포넌트에 맞게 수정하세요) procedure OnMqttMessageReceived(const Topic, Payload: string); public { Public declarations } end; var fMain: TfMain; implementation {$R *.dfm} uses System.IniFiles; procedure TfMain.LogMsg(const AMsg: string); begin // 멀티스레딩 환경(MQTT 콜백 등)에서 VCL 메모 컨트롤 접근 시 에러 방지 TThread.Queue(nil, procedure begin memLog.Lines.Add(FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + ' - ' + AMsg); end); end; procedure TfMain.SetupDatabase; var Ini: TIniFile; DBHost, DBUser, DBPass, DBName: string; DBPort: Integer; begin if Assigned(FDConn) then Exit; Ini := TIniFile.Create(ExtractFilePath(ParamStr(0)) + 'settings.ini'); try DBHost := Ini.ReadString('DB', 'Host', '127.0.0.1'); DBPort := Ini.ReadInteger('DB', 'Port', 3306); DBUser := Ini.ReadString('DB', 'User', 'root'); DBPass := Ini.ReadString('DB', 'Password', 'password'); DBName := Ini.ReadString('DB', 'Database', 'mmcl_db'); finally Ini.Free; end; FDConn := TFDConnection.Create(Self); FDPhysMySQLDriverLink := TFDPhysMySQLDriverLink.Create(Self); FDConn.Params.DriverID := 'MySQL'; FDConn.Params.Database := DBName; FDConn.Params.UserName := DBUser; FDConn.Params.Password := DBPass; FDConn.Params.Add('Server=' + DBHost); FDConn.Params.Add('Port=' + IntToStr(DBPort)); try FDConn.Connected := True; LogMsg('MariaDB 연결 성공 (Host: ' + DBHost + ')'); except on E: Exception do LogMsg('DB 연결 오류: ' + E.Message); end; end; procedure TfMain.InsertNilmData(SensorNo: Integer; PayloadJSON: string); var Qry: TFDQuery; JSONObj: TJSONObject; v1_pwr, v1_cur, v1_volt: Double; v2_pwr, v2_cur, v2_volt: Double; v3_pwr, v3_cur, v3_volt: Double; begin if not Assigned(FDConn) or not FDConn.Connected then Exit; // 기본값 초기화 v1_pwr:=0; v1_cur:=0; v1_volt:=0; v2_pwr:=0; v2_cur:=0; v2_volt:=0; v3_pwr:=0; v3_cur:=0; v3_volt:=0; // JSON 파싱 (System.JSON 활용) // uNILMTypes.pas의 "wA", "currentA", "voltageA" 형식 파싱 대응 JSONObj := TJSONObject.ParseJSONValue(PayloadJSON) as TJSONObject; if Assigned(JSONObj) then begin try // CH1 (L1 / A) JSONObj.TryGetValue('wA', v1_pwr); JSONObj.TryGetValue('currentA', v1_cur); JSONObj.TryGetValue('voltageA', v1_volt); // CH2 (L2 / B) JSONObj.TryGetValue('wB', v2_pwr); JSONObj.TryGetValue('currentB', v2_cur); JSONObj.TryGetValue('voltageB', v2_volt); // CH3 (L3 / C) JSONObj.TryGetValue('wC', v3_pwr); JSONObj.TryGetValue('currentC', v3_cur); JSONObj.TryGetValue('voltageC', v3_volt); finally JSONObj.Free; end; end; Qry := TFDQuery.Create(nil); try Qry.Connection := FDConn; // 1. sensor_history_log (시계열 이력) 적재 - CH1, CH2, CH3 모두 저장 Qry.SQL.Text := 'INSERT INTO sensor_history_log (sensor_no, log_time, ' + ' value_ch1_pwr, value_ch1_current, value_ch1_volt, ' + ' value_ch2_pwr, value_ch2_current, value_ch2_volt, ' + ' value_ch3_pwr, value_ch3_current, value_ch3_volt) ' + 'VALUES (:SNo, NOW(), :P1, :C1, :V1, :P2, :C2, :V2, :P3, :C3, :V3)'; Qry.ParamByName('SNo').AsInteger := SensorNo; Qry.ParamByName('P1').AsFloat := v1_pwr; Qry.ParamByName('C1').AsFloat := v1_cur; Qry.ParamByName('V1').AsFloat := v1_volt; Qry.ParamByName('P2').AsFloat := v2_pwr; Qry.ParamByName('C2').AsFloat := v2_cur; Qry.ParamByName('V2').AsFloat := v2_volt; Qry.ParamByName('P3').AsFloat := v3_pwr; Qry.ParamByName('C3').AsFloat := v3_cur; Qry.ParamByName('V3').AsFloat := v3_volt; Qry.ExecSQL; // 2. sensor_info (현재 상태) 업데이트 Qry.SQL.Text := 'UPDATE sensor_info SET update_time = NOW(), ' + ' value_ch1_pwr = :P1, value_ch1_current = :C1, value_ch1_volt = :V1, ' + ' value_ch2_pwr = :P2, value_ch2_current = :C2, value_ch2_volt = :V2, ' + ' value_ch3_pwr = :P3, value_ch3_current = :C3, value_ch3_volt = :V3 ' + 'WHERE sensor_no = :SNo AND sensor_typeid = 1'; Qry.ParamByName('SNo').AsInteger := SensorNo; Qry.ParamByName('P1').AsFloat := v1_pwr; Qry.ParamByName('C1').AsFloat := v1_cur; Qry.ParamByName('V1').AsFloat := v1_volt; Qry.ParamByName('P2').AsFloat := v2_pwr; Qry.ParamByName('C2').AsFloat := v2_cur; Qry.ParamByName('V2').AsFloat := v2_volt; Qry.ParamByName('P3').AsFloat := v3_pwr; Qry.ParamByName('C3').AsFloat := v3_cur; Qry.ParamByName('V3').AsFloat := v3_volt; Qry.ExecSQL; LogMsg(Format('DB Insert/Update 완료 [Sensor: %d]', [SensorNo])); except on E: Exception do LogMsg('DB 처리 오류: ' + E.Message); end; Qry.Free; end; procedure TfMain.OnMqttMessageReceived(const Topic, Payload: string); begin LogMsg('MQTT 수신 Topic: ' + Topic); LogMsg('MQTT 페이로드: ' + Payload); // 하드코딩된 센서 번호 101 사용 (실제로는 Topic이나 JSON에서 추출) // 예: Topic = '/nilm/cnc/101' InsertNilmData(101, Payload); end; procedure TfMain.btnStartClick(Sender: TObject); var Ini: TIniFile; MqttHost: string; MqttPort: Integer; begin LogMsg('NILM 에이전트 구동을 시작합니다...'); SetupDatabase; Ini := TIniFile.Create(ExtractFilePath(ParamStr(0)) + 'settings.ini'); try MqttHost := Ini.ReadString('MQTT', 'Host', '127.0.0.1'); MqttPort := Ini.ReadInteger('MQTT', 'Port', 1883); finally Ini.Free; end; LogMsg(Format('MQTT 연결 정보 로드 (Host: %s, Port: %d)', [MqttHost, MqttPort])); // TODO: 추가해주신 UMQTTClient.pas 클래스 인스턴스화 및 연결 // FClient := TMQTTClient.Create(...); // FClient.Host := MqttHost; // FClient.Port := MqttPort; // FClient.OnMessage := OnMqttMessageReceived; // FClient.Connect; LogMsg('MQTT 브로커 리스닝 대기중...'); end; procedure TfMain.btnStopClick(Sender: TObject); begin LogMsg('에이전트 중지 요청...'); // if Assigned(FClient) then FClient.Disconnect; if Assigned(FDConn) and FDConn.Connected then begin FDConn.Connected := False; LogMsg('DB 연결이 안전하게 해제되었습니다.'); end; end; end.