unit uMain; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, uMQTTComps, StdCtrls, Grids, BaseGrid, AdvGrid, uMQTT , ExtCtrls, DateUtils, ComCtrls, Menus, ScktComp, inifiles, Buttons, ZConnection, MQTT, ZAbstractDataset, ZDataset, DB, ZAbstractRODataset, ZAbstractConnection, System.JSON, 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.Stan.Param, FireDAC.DatS, FireDAC.DApt.Intf, FireDAC.DApt, FireDAC.Phys.PGDef, FireDAC.VCLUI.Wait, FireDAC.Comp.UI, FireDAC.Phys.PG, FireDAC.Comp.DataSet, FireDAC.Comp.Client, System.Generics.Collections, System.StrUtils, cxGraphics, cxControls, cxLookAndFeels, cxLookAndFeelPainters, cxStyles, cxCustomData, cxFilter, cxData, cxDataStorage, cxEdit, cxNavigator, dxDateRanges, dxScrollbarAnnotations, cxGridLevel, cxGridCustomTableView, cxGridTableView, cxClasses, cxGridCustomView, cxGrid; type TfrmMain = class(TForm) MQTTClient1: TMQTTClient; initTimer: TTimer; StatusBar1: TStatusBar; MainMenu1: TMainMenu; File1: TMenuItem; Config1: TMenuItem; N2: TMenuItem; Connect1: TMenuItem; Disconnect1: TMenuItem; insertTimer: TTimer; File2: TMenuItem; DebugMode1: TMenuItem; N8: TMenuItem; ProgramExit1: TMenuItem; ZConnection1: TZConnection; ZReadOnlyQuery1: TZReadOnlyQuery; ZQuery1: TZQuery; NILM1: TMenuItem; Edit1: TMenuItem; N1: TMenuItem; Panel2: TPanel; Label5: TLabel; Label6: TLabel; Timer_DateTime: TTimer; TrayIcon1: TTrayIcon; tmrExit: TTimer; FDConnection1: TFDConnection; FDQuery1: TFDQuery; FDPhysPgDriverLink1: TFDPhysPgDriverLink; FDGUIxWaitCursor1: TFDGUIxWaitCursor; MQTTClient1_old: TMQTTClient; PageControl1: TPageControl; TabSheet1: TTabSheet; TabSheet2: TTabSheet; ListBox1: TListBox; Panel1: TPanel; Label2: TLabel; Label3: TLabel; Label4: TLabel; cxGrid_NILM: TcxGrid; cxGrid_NILM_Tv: TcxGridTableView; cxGrid_NILM_Lv: TcxGridLevel; cxGrid_NILM_TvColumn1: TcxGridColumn; cxGrid_NILM_TvColumn2: TcxGridColumn; cxGrid_NILM_TvColumn3: TcxGridColumn; cxGrid_NILM_TvColumn4: TcxGridColumn; cxGrid_NILM_TvColumn5: TcxGridColumn; cxGrid_NILM_TvColumn6: TcxGridColumn; cxGrid_NILM_TvColumn7: TcxGridColumn; cxGrid_NILM_TvColumn8: TcxGridColumn; cxGrid_NILM_TvColumn9: TcxGridColumn; cxGrid_NILM_TvColumn10: TcxGridColumn; cxGrid_NILM_TvColumn11: TcxGridColumn; cxGrid_NILM_TvColumn12: TcxGridColumn; cxGrid_NILM_TvColumn13: TcxGridColumn; cxGrid_NILM_TvColumn14: TcxGridColumn; cxGrid_NILM_TvColumn15: TcxGridColumn; cxGrid_NILM_TvColumn16: TcxGridColumn; cxGrid_NILM_TvColumn17: TcxGridColumn; cxGrid_NILM_TvColumn18: TcxGridColumn; cxGrid_NILM_TvColumn19: TcxGridColumn; cxGrid_NILM_TvColumn20: TcxGridColumn; cxGrid_NILM_TvColumn21: TcxGridColumn; cxGrid_NILM_TvColumn22: TcxGridColumn; cxGrid_NILM_TvColumn23: TcxGridColumn; cxGrid_NILM_TvColumn24: TcxGridColumn; cxGrid_NILM_TvColumn25: TcxGridColumn; cxGrid_NILM_TvColumn26: TcxGridColumn; cxGrid_NILM_TvColumn0: TcxGridColumn; procedure initTimerTimer(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure Connect1Click(Sender: TObject); procedure Disconnect1Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure insertTimerTimer(Sender: TObject); procedure ListBox1DblClick(Sender: TObject); procedure ProgramExit1Click(Sender: TObject); procedure DebugMode1Click(Sender: TObject); procedure Config1Click(Sender: TObject); procedure Edit1Click(Sender: TObject); procedure N1Click(Sender: TObject); procedure MQTTClient1ClientID(Sender: TObject; var aClientID: UTF8String); procedure MQTTClient1EnableChange(Sender: TObject); procedure MQTTClient1Failure(Sender: TObject; aReason: Integer; var CloseClient: Boolean); procedure MQTTClient1Mon(Sender: TObject; aStr: string); procedure MQTTClient1Msg(Sender: TObject; aTopic: UTF8String; aMessage: AnsiString; aQos: TMQTTQOSType; aRetained: Boolean); procedure MQTTClient1Offline(Sender: TObject; Graceful: Boolean); procedure MQTTClient1Online(Sender: TObject); procedure Timer_DateTimeTimer(Sender: TObject); procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); procedure AppEvtMinimize(Sender: TObject); procedure tmrExitTimer(Sender: TObject); procedure TrayIcon1DblClick(Sender: TObject); private { Private declarations } debug_mode : Boolean; msg_flaw_stop : boolean; dumyTopic : string; debug_mode_count, hubPort : integer; ExitOk : boolean; procedure InsertData(topic : string; msg : AnsiString); procedure NILM_Grid_CLear; public //read_gwid : integer; fIOTGW : array [1..max_gw] of TIOTGW; fNILM : array [1..max_gw] of TNILM; appPath : string; dbIP, dbPW, dbUser, dbSchema : string; dbIP_PG, dbUser_PG, dbPW_PG, dbSchema_PG : string; dbPort, dbPort_PG, brokerPort : integer; mqtt_topic, brokerIP, ClientID, mqtt_user, mqtt_pw : string; aRetain: Boolean; old_NilmID : integer; aQos: TMQTTQOSType; NILM_ID_List : TList; // NILM_Map: TDictionary; // NILM_Key, NILM_Value: Integer; function DB_Connect:Boolean; function DB_Close:Boolean; function xAddSqlMark(velStr: String): String; function xAddSqlDoubleMark(velStr: String): string; procedure PrintLog(msg, msgType : String); procedure MQTTParsing(data : AnsiString; aTopic : String); procedure ClearIOTGW(); procedure SendMQTT_CtrlCommand(sTopic, sMsg : string); procedure insertSQL(sql : string); end; var frmMain: TfrmMain; implementation {$R *.dfm} uses System.Math, global, uConfigMQTT, uConfigNILM, uDevConfig; procedure TfrmMain.ClearIOTGW(); var p, i : integer; begin for p:=1 to max_gw do begin fIOTGW[p]._TOPIC := ''; for i:=0 to 15 do fIOTGW[p]._DI[i] := false; for i:=0 to 7 do fIOTGW[p]._DO[i] := false; for i:=0 to 3 do fIOTGW[p]._AI[i] := 0; for i:=0 to 9 do fIOTGW[p]._TEMP[i] := 0.0; for i:=0 to 9 do fIOTGW[p]._HUMID[i] := 0.0; fIOTGW[p].run_dd := 0; fIOTGW[p].run_hh := 0; fIOTGW[p].run_mm := 0; fIOTGW[p].run_ss := 0; fNILM[p].idxNo := 0; fNILM[p]._TOPIC := ''; fNILM[p]._NAME := ''; for i:=1 to 3 do begin fNILM[p]._VALUE.PF[i] := 0.0; fNILM[p]._VALUE.Vrms[i] := 0.0; fNILM[p]._VALUE.Irms[i] := 0.0; fNILM[p]._VALUE._Var[i] := 0.0; fNILM[p]._VALUE.Va[i] := 0.0; fNILM[p]._VALUE.Pwr[i] := 0.0; fNILM[p]._VALUE.Gyro[i] := 0.0; fNILM[p]._VALUE.Accel[i] := 0.0; end; fNILM[p]._VALUE.Total_P := 0.0; fNILM[p]._VALUE.Total_Q := 0.0; fNILM[p]._VALUE.Total_S := 0.0; fNILM[p]._VALUE.Total_PF := 0.0; fNILM[p]._VALUE.wh := 0; fNILM[p]._VALUE.freq := 0.0; fNILM[p]._VALUE.rssi := 0; fNILM[p]._VALUE.seq := 0; fNILM[p]._VALUE.COMM := 0; fNILM[p]._VALUE.wire := ''; fNILM[p]._VALUE.TempC := 0; fNILM[p]._VALUE.TempExt := 0.0; fNILM[p]._VALUE.Vibration := 0.0; fNILM[p]._VALUE.TiltAngle := 0.0; fNILM[p].rcvCount := 0; fNILM[p].oldRcvCount := 0; end; end; procedure TfrmMain.InsertData(topic : string; msg : AnsiString); begin end; //MQTT 데이터 파싱 procedure TfrmMain.MQTTClient1ClientID(Sender: TObject; var aClientID: UTF8String); begin PrintLog('ClientID :: ' + aClientID, 'SYS'); end; procedure TfrmMain.MQTTClient1EnableChange(Sender: TObject); begin if TMQTTClient(Sender).Enabled then printlog('Client is enabled.', 'SYS') else printlog('Client is disabled.', 'SYS'); end; procedure TfrmMain.MQTTClient1Failure(Sender: TObject; aReason: Integer; var CloseClient: Boolean); begin printlog('---- Failure Reported ' + FailureNames(aReason), 'SYS'); end; procedure TfrmMain.MQTTClient1Mon(Sender: TObject; aStr: string); begin printlog(aStr, 'SYS'); end; procedure TfrmMain.MQTTClient1Msg(Sender: TObject; aTopic: UTF8String; aMessage: AnsiString; aQos: TMQTTQOSType; aRetained: Boolean); begin try if (LeftStr(aMessage,1)='{') and (RightStr(aMessage,1)='}') then begin PrintLog('Topic : '+Utf8ToAnsi(aTopic)+', Msg='+aMessage, 'DEBUG'); MQTTParsing(aMessage,Utf8ToAnsi(aTopic)); end else begin PrintLog('[Not Entry]Topic : '+Utf8ToAnsi(aTopic), 'DEBUG'); end; except on e : exception do begin printlog('MQTTClient1Msg Error :: '+e.Message, 'SYS'); end; end; end; procedure TfrmMain.MQTTClient1Offline(Sender: TObject; Graceful: Boolean); begin if Graceful then begin printlog('Client Gracefully Disconnected.', 'SYS'); end else begin printlog('Client Terminated Unexpectedly.', 'SYS'); end; end; procedure TfrmMain.MQTTClient1Online(Sender: TObject); var s: TStringList; begin printlog('Client is online.', 'SYS'); s := TStringList.Create; s.AddObject(mqtt_topic, TObject(aQos)); MQTTClient1.Subscribe(s); s.Free; end; procedure TfrmMain.MQTTParsing(data : AnsiString; aTopic : String); var tempTopic_list : TStringList; pf, Vrms, Irms, Var_, Va, Pwr: Single; nilmID, ch, k, pIdx : integer; grid_rowck : integer; devStr, numStr: string; ch_c : char; JSONObject, ChObj, TotObj, SlaveObj : TJSONObject; ChArr, SlaveArr : TJSONArray; jvCh, jvTot: TJSONValue; hasRealVoltage, hasRealPower, hasCurrent : Boolean; sumP, sumQ, sumS, maxI, nomV, assPF : Double; function GetValNum(const AObj: TJSONObject; const AKey: string; ADefault: Double = 0.0): Double; var LVal: TJSONValue; begin Result := ADefault; if AObj = nil then Exit; LVal := AObj.GetValue(AKey); if Assigned(LVal) and (not (LVal is TJSONNull)) then Result := StrToFloatDef(LVal.Value, ADefault); end; function GetValInt(const AObj: TJSONObject; const AKey: string; ADefault: Integer = 0): Integer; var LVal: TJSONValue; begin Result := ADefault; if AObj = nil then Exit; LVal := AObj.GetValue(AKey); if Assigned(LVal) and (not (LVal is TJSONNull)) then Result := StrToIntDef(LVal.Value, ADefault); end; function GetValInt64(const AObj: TJSONObject; const AKey: string; ADefault: Int64 = 0): Int64; var LVal: TJSONValue; begin Result := ADefault; if AObj = nil then Exit; LVal := AObj.GetValue(AKey); if Assigned(LVal) and (not (LVal is TJSONNull)) then Result := StrToInt64Def(LVal.Value, ADefault); end; function GetValStr(const AObj: TJSONObject; const AKey, ADefault: string): string; var LVal: TJSONValue; begin Result := ADefault; if AObj = nil then Exit; LVal := AObj.GetValue(AKey); if Assigned(LVal) and (not (LVal is TJSONNull)) then Result := LVal.Value; end; begin try if Trim(data) = '' then Exit; InsertData(aTopic, data); JSONObject := TJSONObject.ParseJSONValue(data) as TJSONObject; if JSONObject = nil then Exit; tempTopic_list := TStringList.Create; try nilmID := 0; // 1순위: JSON의 dev 필드 확인 (예: "NILM_001", "NILM_032") devStr := GetValStr(JSONObject, 'dev', ''); if devStr <> '' then begin numStr := ''; for ch_c in devStr do if ch_c in ['0'..'9'] then numStr := numStr + ch_c; nilmID := StrToIntDef(numStr, 0); end; // 2순위: 토픽 경로에서 추출 if nilmID <= 0 then begin Strsplit('/', aTopic, tempTopic_list); if tempTopic_list.Count > 0 then begin if (tempTopic_list.Strings[tempTopic_list.Count - 1] = 'status') or (tempTopic_list.Strings[tempTopic_list.Count - 1] = 'event') then begin if tempTopic_list.Count > 1 then nilmID := StrToIntDef(tempTopic_list.Strings[tempTopic_list.Count - 2], 0); end else begin nilmID := StrToIntDef(tempTopic_list.Strings[tempTopic_list.Count - 1], 0); end; end; end; // 특수 토픽 (status, gateway, event) 처리 if aTopic.EndsWith('/status') then begin printlog(Format('[노드상태] 장비 %d: online=%s, loss_rate=%s', [nilmID, GetValStr(JSONObject, 'online', ''), GetValStr(JSONObject, 'loss_rate', '0')]), 'SYS'); Exit; end else if aTopic.EndsWith('/gateway') then begin printlog(Format('[게이트웨이] %s: online=%s, nodes=%d/%d', [GetValStr(JSONObject, 'gw', ''), GetValStr(JSONObject, 'online', ''), GetValInt(JSONObject, 'nodes_online', 0), GetValInt(JSONObject, 'nodes_total', 0)]), 'SYS'); Exit; end else if aTopic.EndsWith('/event') then begin printlog(Format('[ALARM] 장비 %d: %s (코드:%d, 값:%.2f)', [nilmID, GetValStr(JSONObject, 'name', ''), GetValInt(JSONObject, 'code', 0), GetValNum(JSONObject, 'value', 0.0)]), 'SYS'); Exit; end; if (nilmID <= 0) or (nilmID > max_gw) or (fNILM[nilmID].idxNo <= 0) then Exit; grid_rowck := NILM_ID_List.IndexOf(nilmID); // ========================================================================= // CASE 1: 최신 표준 단일 규격 (ch 배열 및 total 객체) // ========================================================================= jvCh := JSONObject.GetValue('ch'); if (jvCh <> nil) and (jvCh is TJSONArray) then begin ChArr := jvCh as TJSONArray; fNILM[nilmID]._VALUE.seq := GetValInt(JSONObject, 'seq', 0); fNILM[nilmID]._VALUE.COMM := GetValInt(JSONObject, 'COMM', 0); fNILM[nilmID]._VALUE.rssi := GetValInt(JSONObject, 'rssi', 0); fNILM[nilmID]._VALUE.wire := GetValStr(JSONObject, 'wire', '3P4W'); fNILM[nilmID]._VALUE.freq := GetValNum(JSONObject, 'freq', 60.0); fNILM[nilmID]._VALUE.wh := GetValInt64(JSONObject, 'wh', 0); for k := 0 to Min(2, ChArr.Count - 1) do begin if (ChArr.Items[k] <> nil) and (ChArr.Items[k] is TJSONObject) then begin ChObj := ChArr.Items[k] as TJSONObject; pIdx := k + 1; // 1..3 fNILM[nilmID]._VALUE.Vrms[pIdx] := GetValNum(ChObj, 'v', 0.0); fNILM[nilmID]._VALUE.Irms[pIdx] := GetValNum(ChObj, 'i', 0.0); fNILM[nilmID]._VALUE.Pwr[pIdx] := GetValNum(ChObj, 'p', 0.0); fNILM[nilmID]._VALUE._Var[pIdx] := GetValNum(ChObj, 'q', 0.0); fNILM[nilmID]._VALUE.Va[pIdx] := GetValNum(ChObj, 's', 0.0); fNILM[nilmID]._VALUE.PF[pIdx] := GetValNum(ChObj, 'pf', 0.0); end; end; jvTot := JSONObject.GetValue('total'); if (jvTot <> nil) and (jvTot is TJSONObject) then begin TotObj := jvTot as TJSONObject; fNILM[nilmID]._VALUE.Total_P := GetValNum(TotObj, 'p', 0.0); fNILM[nilmID]._VALUE.Total_Q := GetValNum(TotObj, 'q', 0.0); fNILM[nilmID]._VALUE.Total_S := GetValNum(TotObj, 's', 0.0); fNILM[nilmID]._VALUE.Total_PF := GetValNum(TotObj, 'pf', 0.0); end; // 환경/센서 데이터 fNILM[nilmID]._VALUE.TempC := GetValNum(JSONObject, 'temperature', GetValNum(JSONObject, 'TempC', fNILM[nilmID]._VALUE.TempC)); fNILM[nilmID]._VALUE.TempExt := GetValNum(JSONObject, 'tempExt', 0.0); fNILM[nilmID]._VALUE.Accel[1] := GetValNum(JSONObject, 'accX', GetValNum(JSONObject, 'aX', fNILM[nilmID]._VALUE.Accel[1])); fNILM[nilmID]._VALUE.Accel[2] := GetValNum(JSONObject, 'accY', GetValNum(JSONObject, 'aY', fNILM[nilmID]._VALUE.Accel[2])); fNILM[nilmID]._VALUE.Accel[3] := GetValNum(JSONObject, 'accZ', GetValNum(JSONObject, 'aZ', fNILM[nilmID]._VALUE.Accel[3])); fNILM[nilmID]._VALUE.Vibration := GetValNum(JSONObject, 'vibration', 0.0); fNILM[nilmID]._VALUE.Gyro[1] := GetValNum(JSONObject, 'gyroX', GetValNum(JSONObject, 'gX', fNILM[nilmID]._VALUE.Gyro[1])); fNILM[nilmID]._VALUE.Gyro[2] := GetValNum(JSONObject, 'gyroY', GetValNum(JSONObject, 'gY', fNILM[nilmID]._VALUE.Gyro[2])); fNILM[nilmID]._VALUE.Gyro[3] := GetValNum(JSONObject, 'gyroZ', GetValNum(JSONObject, 'gZ', fNILM[nilmID]._VALUE.Gyro[3])); fNILM[nilmID]._VALUE.TiltAngle := GetValNum(JSONObject, 'tiltAngle', 0.0); end // ========================================================================= // CASE 2: 하위 호환 slave_data 구조 // ========================================================================= else if JSONObject.GetValue('slave_data') <> nil then begin SlaveArr := JSONObject.GetValue('slave_data'); if (SlaveArr <> nil) and (SlaveArr.Count > 0) and (SlaveArr.Items[0] is TJSONObject) then begin SlaveObj := SlaveArr.Items[0] as TJSONObject; fNILM[nilmID]._VALUE.seq := GetValInt(SlaveObj, 'seq', 0); fNILM[nilmID]._VALUE.COMM := GetValInt(SlaveObj, 'COMM', 0); fNILM[nilmID]._VALUE.rssi := GetValInt(SlaveObj, 'rssi', 0); fNILM[nilmID]._VALUE.wire := GetValStr(SlaveObj, 'wire', '3P4W'); fNILM[nilmID]._VALUE.freq := GetValNum(SlaveObj, 'frequency', 60.0); fNILM[nilmID]._VALUE.wh := GetValInt64(SlaveObj, 'wh', 0); // A상 (CH 1) fNILM[nilmID]._VALUE.PF[1] := GetValNum(SlaveObj, 'power_factor_a', GetValNum(SlaveObj, 'pfA', 0.0)); fNILM[nilmID]._VALUE.Vrms[1] := GetValNum(SlaveObj, 'vrms_a', GetValNum(SlaveObj, 'voltageA', 0.0)); fNILM[nilmID]._VALUE.Irms[1] := GetValNum(SlaveObj, 'irms_a', GetValNum(SlaveObj, 'currentA', 0.0)); fNILM[nilmID]._VALUE.Pwr[1] := GetValNum(SlaveObj, 'active_power_a', GetValNum(SlaveObj, 'wA', 0.0)); fNILM[nilmID]._VALUE._Var[1] := GetValNum(SlaveObj, 'reactive_power_a', GetValNum(SlaveObj, 'Var_A', 0.0)); fNILM[nilmID]._VALUE.Va[1] := GetValNum(SlaveObj, 'apparent_power_a', GetValNum(SlaveObj, 'Va_A', 0.0)); // B상 (CH 2) if fNILM[nilmID]._VALUE.wire <> '3P3W' then begin fNILM[nilmID]._VALUE.PF[2] := GetValNum(SlaveObj, 'power_factor_b', GetValNum(SlaveObj, 'pfB', 0.0)); fNILM[nilmID]._VALUE.Vrms[2] := GetValNum(SlaveObj, 'vrms_b', GetValNum(SlaveObj, 'voltageB', 0.0)); fNILM[nilmID]._VALUE.Irms[2] := GetValNum(SlaveObj, 'irms_b', GetValNum(SlaveObj, 'currentB', 0.0)); fNILM[nilmID]._VALUE.Pwr[2] := GetValNum(SlaveObj, 'active_power_b', GetValNum(SlaveObj, 'wB', 0.0)); fNILM[nilmID]._VALUE._Var[2] := GetValNum(SlaveObj, 'reactive_power_b', GetValNum(SlaveObj, 'Var_B', 0.0)); fNILM[nilmID]._VALUE.Va[2] := GetValNum(SlaveObj, 'apparent_power_b', GetValNum(SlaveObj, 'Va_B', 0.0)); end; // C상 (CH 3) fNILM[nilmID]._VALUE.PF[3] := GetValNum(SlaveObj, 'power_factor_c', GetValNum(SlaveObj, 'pfC', 0.0)); fNILM[nilmID]._VALUE.Vrms[3] := GetValNum(SlaveObj, 'vrms_c', GetValNum(SlaveObj, 'voltageC', 0.0)); fNILM[nilmID]._VALUE.Irms[3] := GetValNum(SlaveObj, 'irms_c', GetValNum(SlaveObj, 'currentC', 0.0)); fNILM[nilmID]._VALUE.Pwr[3] := GetValNum(SlaveObj, 'active_power_c', GetValNum(SlaveObj, 'wC', 0.0)); fNILM[nilmID]._VALUE._Var[3] := GetValNum(SlaveObj, 'reactive_power_c', GetValNum(SlaveObj, 'Var_C', 0.0)); fNILM[nilmID]._VALUE.Va[3] := GetValNum(SlaveObj, 'apparent_power_c', GetValNum(SlaveObj, 'Va_C', 0.0)); // Total fNILM[nilmID]._VALUE.Total_P := GetValNum(SlaveObj, 'total_p', 0.0); fNILM[nilmID]._VALUE.Total_Q := GetValNum(SlaveObj, 'total_q', 0.0); fNILM[nilmID]._VALUE.Total_S := GetValNum(SlaveObj, 'total_s', 0.0); fNILM[nilmID]._VALUE.Total_PF := GetValNum(SlaveObj, 'total_pf', 0.0); // 환경/센서 fNILM[nilmID]._VALUE.TempC := GetValNum(SlaveObj, 'temperature', GetValNum(SlaveObj, 'TempC', fNILM[nilmID]._VALUE.TempC)); fNILM[nilmID]._VALUE.Accel[1] := GetValNum(SlaveObj, 'accX', GetValNum(SlaveObj, 'aX', fNILM[nilmID]._VALUE.Accel[1])); fNILM[nilmID]._VALUE.Accel[2] := GetValNum(SlaveObj, 'accY', GetValNum(SlaveObj, 'aY', fNILM[nilmID]._VALUE.Accel[2])); fNILM[nilmID]._VALUE.Accel[3] := GetValNum(SlaveObj, 'accZ', GetValNum(SlaveObj, 'aZ', fNILM[nilmID]._VALUE.Accel[3])); fNILM[nilmID]._VALUE.Gyro[1] := GetValNum(SlaveObj, 'gyroX', GetValNum(SlaveObj, 'gX', fNILM[nilmID]._VALUE.Gyro[1])); fNILM[nilmID]._VALUE.Gyro[2] := GetValNum(SlaveObj, 'gyroY', GetValNum(SlaveObj, 'gY', fNILM[nilmID]._VALUE.Gyro[2])); fNILM[nilmID]._VALUE.Gyro[3] := GetValNum(SlaveObj, 'gyroZ', GetValNum(SlaveObj, 'gZ', fNILM[nilmID]._VALUE.Gyro[3])); end; end // ========================================================================= // CASE 3: 구형 CH 분할 포맷 ("CH": "01", "02", "03") // ========================================================================= else if JSONObject.GetValue('CH') <> nil then begin ch := StrToIntDef(JSONObject.GetValue('CH').Value, 0); case ch of 1, 2, 3: begin pf := GetValNum(JSONObject, 'PF', 0.0); Vrms := GetValNum(JSONObject, 'Vrms', 0.0); Irms := GetValNum(JSONObject, 'Irms', 0.0); Var_ := GetValNum(JSONObject, 'Var_', 0.0); Va := GetValNum(JSONObject, 'Va', 0.0); Pwr := GetValNum(JSONObject, 'Pwr', 0.0); fNILM[nilmID]._VALUE.PF[ch] := pf; fNILM[nilmID]._VALUE.Vrms[ch] := Vrms; fNILM[nilmID]._VALUE.Irms[ch] := Irms; fNILM[nilmID]._VALUE._Var[ch] := Var_; fNILM[nilmID]._VALUE.Va[ch] := Va; fNILM[nilmID]._VALUE.Pwr[ch] := Pwr; end; 4: // MPU 센서 또는 03 채널 begin fNILM[nilmID]._VALUE.Gyro[1] := GetValNum(JSONObject, 'gX', fNILM[nilmID]._VALUE.Gyro[1]); fNILM[nilmID]._VALUE.Gyro[2] := GetValNum(JSONObject, 'gY', fNILM[nilmID]._VALUE.Gyro[2]); fNILM[nilmID]._VALUE.Gyro[3] := GetValNum(JSONObject, 'gZ', fNILM[nilmID]._VALUE.Gyro[3]); fNILM[nilmID]._VALUE.Accel[1] := GetValNum(JSONObject, 'aX', fNILM[nilmID]._VALUE.Accel[1]); fNILM[nilmID]._VALUE.Accel[2] := GetValNum(JSONObject, 'aY', fNILM[nilmID]._VALUE.Accel[2]); fNILM[nilmID]._VALUE.Accel[3] := GetValNum(JSONObject, 'aZ', fNILM[nilmID]._VALUE.Accel[3]); fNILM[nilmID]._VALUE.TempC := GetValNum(JSONObject, 'TempC', fNILM[nilmID]._VALUE.TempC); end; end; end; // ========================================================================= // 가상 전력 추정 계산 (1-CT 전류 전용 환경 대응) // ========================================================================= hasRealVoltage := (fNILM[nilmID]._VALUE.Vrms[1] > 0) or (fNILM[nilmID]._VALUE.Vrms[2] > 0) or (fNILM[nilmID]._VALUE.Vrms[3] > 0); hasRealPower := (fNILM[nilmID]._VALUE.Pwr[1] > 0) or (fNILM[nilmID]._VALUE.Pwr[2] > 0) or (fNILM[nilmID]._VALUE.Pwr[3] > 0) or (fNILM[nilmID]._VALUE.Total_P > 0); hasCurrent := (fNILM[nilmID]._VALUE.Irms[1] > 0) or (fNILM[nilmID]._VALUE.Irms[2] > 0) or (fNILM[nilmID]._VALUE.Irms[3] > 0); if hasCurrent and (not hasRealVoltage) and (not hasRealPower) then begin nomV := 220.0; assPF := 0.90; sumP := 0.0; sumQ := 0.0; sumS := 0.0; maxI := 0.0; for k := 1 to 3 do begin if fNILM[nilmID]._VALUE.Irms[k] > 0 then begin fNILM[nilmID]._VALUE.Vrms[k] := nomV; fNILM[nilmID]._VALUE.PF[k] := assPF; fNILM[nilmID]._VALUE.Va[k] := nomV * fNILM[nilmID]._VALUE.Irms[k]; fNILM[nilmID]._VALUE.Pwr[k] := fNILM[nilmID]._VALUE.Va[k] * assPF; fNILM[nilmID]._VALUE._Var[k] := Sqrt(Max(Sqr(fNILM[nilmID]._VALUE.Va[k]) - Sqr(fNILM[nilmID]._VALUE.Pwr[k]), 0.0)); sumP := sumP + fNILM[nilmID]._VALUE.Pwr[k]; sumQ := sumQ + fNILM[nilmID]._VALUE._Var[k]; sumS := sumS + fNILM[nilmID]._VALUE.Va[k]; if fNILM[nilmID]._VALUE.Irms[k] > maxI then maxI := fNILM[nilmID]._VALUE.Irms[k]; end; end; fNILM[nilmID]._VALUE.Total_P := sumP; fNILM[nilmID]._VALUE.Total_Q := sumQ; fNILM[nilmID]._VALUE.Total_S := sumS; fNILM[nilmID]._VALUE.Total_PF := assPF; end; // ========================================================================= // 3P3W / 3P4W Total 전력 자동 합산 보정 // ========================================================================= if (fNILM[nilmID]._VALUE.Total_P = 0) and (fNILM[nilmID]._VALUE.Pwr[1] > 0) then begin if SameText(fNILM[nilmID]._VALUE.wire, '3P3W') then begin fNILM[nilmID]._VALUE.Total_P := fNILM[nilmID]._VALUE.Pwr[1] + fNILM[nilmID]._VALUE.Pwr[3]; fNILM[nilmID]._VALUE.Total_Q := fNILM[nilmID]._VALUE._Var[1] + fNILM[nilmID]._VALUE._Var[3]; end else begin fNILM[nilmID]._VALUE.Total_P := fNILM[nilmID]._VALUE.Pwr[1] + fNILM[nilmID]._VALUE.Pwr[2] + fNILM[nilmID]._VALUE.Pwr[3]; fNILM[nilmID]._VALUE.Total_Q := fNILM[nilmID]._VALUE._Var[1] + fNILM[nilmID]._VALUE._Var[2] + fNILM[nilmID]._VALUE._Var[3]; end; fNILM[nilmID]._VALUE.Total_S := Sqrt(Sqr(fNILM[nilmID]._VALUE.Total_P) + Sqr(fNILM[nilmID]._VALUE.Total_Q)); if fNILM[nilmID]._VALUE.Total_S > 0 then fNILM[nilmID]._VALUE.Total_PF := fNILM[nilmID]._VALUE.Total_P / fNILM[nilmID]._VALUE.Total_S; end; // 수신 카운트 증가 fNILM[nilmID].rcvCount := fNILM[nilmID].rcvCount + 1; if fNILM[nilmID].rcvCount > 65535 then fNILM[nilmID].rcvCount := 1; // 라벨 캡션 갱신 Label2.Caption := Format('[ID : %d / CH : 01] PF : %.3f, Vrms : %.3f, Irms : %.3f, Var : %.3f, Va : %.3f, Pwr : %.3f', [nilmID, fNILM[nilmID]._VALUE.PF[1], fNILM[nilmID]._VALUE.Vrms[1], fNILM[nilmID]._VALUE.Irms[1], fNILM[nilmID]._VALUE._Var[1], fNILM[nilmID]._VALUE.Va[1], fNILM[nilmID]._VALUE.Pwr[1]]); Label3.Caption := Format('[ID : %d / CH : 02] PF : %.3f, Vrms : %.3f, Irms : %.3f, Var : %.3f, Va : %.3f, Pwr : %.3f', [nilmID, fNILM[nilmID]._VALUE.PF[2], fNILM[nilmID]._VALUE.Vrms[2], fNILM[nilmID]._VALUE.Irms[2], fNILM[nilmID]._VALUE._Var[2], fNILM[nilmID]._VALUE.Va[2], fNILM[nilmID]._VALUE.Pwr[2]]); Label4.Caption := Format('[ID : %d / MPU] gX : %.3f, gY : %.3f, gZ : %.3f, aX : %.3f, aY : %.3f, aZ : %.3f, Temp : %.2f', [nilmID, fNILM[nilmID]._VALUE.Gyro[1], fNILM[nilmID]._VALUE.Gyro[2], fNILM[nilmID]._VALUE.Gyro[3], fNILM[nilmID]._VALUE.Accel[1], fNILM[nilmID]._VALUE.Accel[2], fNILM[nilmID]._VALUE.Accel[3], fNILM[nilmID]._VALUE.TempC]); // cxGrid 실시간 반영 if (PageControl1.TabIndex = 0) and (grid_rowck >= 0) then with cxGrid_NILM_Tv.DataController do begin try BeginUpdate; // CH 1 Values[grid_rowck, cxGrid_NILM_TvColumn1.Index] := Format('%.3f', [fNILM[nilmID]._VALUE.PF[1]]); Values[grid_rowck, cxGrid_NILM_TvColumn2.Index] := Format('%.3f', [fNILM[nilmID]._VALUE.Vrms[1]]); Values[grid_rowck, cxGrid_NILM_TvColumn3.Index] := Format('%.3f', [fNILM[nilmID]._VALUE.Irms[1]]); Values[grid_rowck, cxGrid_NILM_TvColumn4.Index] := Format('%.3f', [fNILM[nilmID]._VALUE._Var[1]]); Values[grid_rowck, cxGrid_NILM_TvColumn5.Index] := Format('%.3f', [fNILM[nilmID]._VALUE.Va[1]]); Values[grid_rowck, cxGrid_NILM_TvColumn6.Index] := Format('%.3f', [fNILM[nilmID]._VALUE.Pwr[1]]); // CH 2 Values[grid_rowck, cxGrid_NILM_TvColumn7.Index] := Format('%.3f', [fNILM[nilmID]._VALUE.PF[2]]); Values[grid_rowck, cxGrid_NILM_TvColumn8.Index] := Format('%.3f', [fNILM[nilmID]._VALUE.Vrms[2]]); Values[grid_rowck, cxGrid_NILM_TvColumn9.Index] := Format('%.3f', [fNILM[nilmID]._VALUE.Irms[2]]); Values[grid_rowck, cxGrid_NILM_TvColumn10.Index] := Format('%.3f', [fNILM[nilmID]._VALUE._Var[2]]); Values[grid_rowck, cxGrid_NILM_TvColumn11.Index] := Format('%.3f', [fNILM[nilmID]._VALUE.Va[2]]); Values[grid_rowck, cxGrid_NILM_TvColumn12.Index] := Format('%.3f', [fNILM[nilmID]._VALUE.Pwr[2]]); // CH 3 Values[grid_rowck, cxGrid_NILM_TvColumn13.Index] := Format('%.3f', [fNILM[nilmID]._VALUE.PF[3]]); Values[grid_rowck, cxGrid_NILM_TvColumn14.Index] := Format('%.3f', [fNILM[nilmID]._VALUE.Vrms[3]]); Values[grid_rowck, cxGrid_NILM_TvColumn15.Index] := Format('%.3f', [fNILM[nilmID]._VALUE.Irms[3]]); Values[grid_rowck, cxGrid_NILM_TvColumn16.Index] := Format('%.3f', [fNILM[nilmID]._VALUE._Var[3]]); Values[grid_rowck, cxGrid_NILM_TvColumn17.Index] := Format('%.3f', [fNILM[nilmID]._VALUE.Va[3]]); Values[grid_rowck, cxGrid_NILM_TvColumn18.Index] := Format('%.3f', [fNILM[nilmID]._VALUE.Pwr[3]]); // MPU 및 센서 Values[grid_rowck, cxGrid_NILM_TvColumn19.Index] := Format('%.3f', [fNILM[nilmID]._VALUE.Gyro[1]]); Values[grid_rowck, cxGrid_NILM_TvColumn20.Index] := Format('%.3f', [fNILM[nilmID]._VALUE.Gyro[2]]); Values[grid_rowck, cxGrid_NILM_TvColumn21.Index] := Format('%.3f', [fNILM[nilmID]._VALUE.Gyro[3]]); Values[grid_rowck, cxGrid_NILM_TvColumn22.Index] := Format('%.3f', [fNILM[nilmID]._VALUE.Accel[1]]); Values[grid_rowck, cxGrid_NILM_TvColumn23.Index] := Format('%.3f', [fNILM[nilmID]._VALUE.Accel[2]]); Values[grid_rowck, cxGrid_NILM_TvColumn24.Index] := Format('%.3f', [fNILM[nilmID]._VALUE.Accel[3]]); Values[grid_rowck, cxGrid_NILM_TvColumn25.Index] := Format('%.3f', [fNILM[nilmID]._VALUE.TempC]); // 시각 Values[grid_rowck, cxGrid_NILM_TvColumn26.Index] := FormatDateTime('yymmdd hhnnss', Now); finally EndUpdate; end; end; finally tempTopic_list.Free; JSONObject.Free; end; except on e: Exception do begin printlog('MQTTParsing Error :: ' + e.Message, 'SYS'); end; end; end; procedure TfrmMain.PrintLog(msg, msgType : string); var f : TextFile; log: TStringList; i : integer; logfile, buf : string; begin if ListBox1.Items.Count >= 300 then ListBox1.Items.Clear; if msgType = 'DEBUG' then begin if debug_mode then begin if not msg_flaw_stop then ListBox1.Items.Insert(0, FormatDateTime('hh:nn:ss :: ',now)+msg); end; end else if msgType = 'SYS' then begin ListBox1.Items.Insert(0, FormatDateTime('hh:nn:ss :: ',now)+msg); logfile := ''; logfile := appPath+'log\'+formatdatetime('yyyymmddhh',now())+'.log'; if FileExists(logfile) then begin log := TStringList.Create; AssignFile(f, logfile); reset(f); while not eof(f) do begin ReadLn(f, buf); log.Add(buf); end; log.add(FormatDateTime('hh:nn:ss :: ',now)+msg); CloseFile(F); AssignFile(f, logfile); rewrite(f); for i := 0 to log.Count - 1 do begin WriteLn(f, log.strings[i]); end; CloseFile(F); log.Free; end else begin log := TStringList.Create; log.add(FormatDateTime('hh:nn:ss :: ',now)+msg); AssignFile(f, logfile); rewrite(f); for i := 0 to log.Count - 1 do begin WriteLn(f, log.strings[i]); end; CloseFile(F); log.Free; end; end; end; procedure TfrmMain.initTimerTimer(Sender: TObject); var ini : TInifile; pwd : string; begin old_NilmID := 0; debug_mode := false; debug_mode_count := 0; msg_flaw_stop := false; ListBox1.Clear; mqtt_topic := ''; mqtt_topic := 'QST/NILM/LVDT/#'; pwd := ''; pwd := appPath+'NILM_Manager.ini'; if FileExists(pwd) then begin ini := TInifile.Create(pwd); with ini do begin mqtt_topic := ReadString('MQTT','TOPIC',mqtt_topic); brokerIP := ReadString('MQTT','BrokerIP','183.111.125.203'); brokerPort := ReadInteger('MQTT','BrokerPort',1883); ClientID := ReadString('MQTT','ClientID','NILM_1st'); mqtt_user := ReadString('MQTT','User','qsentech'); mqtt_pw := ReadString('MQTT','Password','qsentech!1233'); dbIP := ReadString('DATABASE','IP','192.168.200.199'); dbUser := ReadString('DATABASE','User','root'); dbPW := ReadString('DATABASE','PW','1233'); dbSchema := ReadString('DATABASE','Schema','nilm'); dbPort := ReadInteger('DATABASE','Port',3306); dbIP_PG := ReadString('DATABASE_PG','IP','192.168.200.199'); dbUser_PG := ReadString('DATABASE_PG','User','root'); dbPW_PG := ReadString('DATABASE_PG','PW','54323'); dbSchema_PG := ReadString('DATABASE_PG','Schema','neol'); dbPort_PG := ReadInteger('DATABASE_PG','Port',3306); hubPort := ReadInteger('MQTT_HUB','Port',6969); end; ini.Free; end; Statusbar1.Panels.Items[2].Text := format('%d',[MQTTClient1.Port]); Statusbar1.Panels.Items[3].Text := mqtt_topic; MQTTClient1.Activate(false); Sleep(300); randomize(); MQTTClient1.ClientID := ClientID+'_'+inttostr(random(999999)); MQTTClient1.Host := brokerIP; MQTTClient1.Port := brokerPort; MQTTClient1.Username := mqtt_user; MQTTClient1.Password := mqtt_pw; MQTTClient1.Activate(true); // QSENTECH_NILM 서버 if ZConnection1.Connected then ZConnection1.disConnect(); Sleep(300); ZConnection1.HostName := dbIP; ZConnection1.Port := dbPort; ZConnection1.User := dbUser; ZConnection1.Password := dbPW; ZConnection1.Database := dbSchema; try if not ZConnection1.Connected then ZConnection1.Connect(); except on e : exception do begin printlog('Database Connect Error :: '+e.Message, 'DB'); end; end; { // NEOL_NILM 서버 if not DB_Connect then begin printlog('Database Connect Error :: PG', 'DB'); end; } frmNILM.ShowNILM(); if NILM_ID_List.Count > 0 then begin NILM_Grid_CLear; end; frmDevConfig.ShowDev(); insertTimer.Enabled := True; initTimer.Enabled := false; AppEvtMinimize(self); end; procedure TfrmMain.NILM_Grid_CLear; var row_ck, i : Integer; begin with cxGrid_NILM_Tv.DataController do begin try BeginUpdate; RecordCount := 0; finally EndUpdate; end; try BeginUpdate; for I := 0 to NILM_ID_List.Count-1 do begin row_ck := AppendRecord; Values[row_ck,cxGrid_NILM_TvColumn0.Index] := NILM_ID_List[i]; end; finally EndUpdate; end; end; end; procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction); begin if insertTimer.Enabled then insertTimer.Enabled := False; if Timer_DateTime.Enabled then Timer_DateTime.Enabled := False; MQTTClient1.Activate(false); if ZConnection1.Connected then ZConnection1.disConnect(); NILM_ID_List.Free; // NILM_Map.Free; end; procedure TfrmMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean); begin if ExitOk then begin CanClose := true; end else begin CanClose := false; case MessageBox(handle, '"예(Y)"를 클릭하면, MQTT Client가 트레이로 최소화 됩니다.'+#$0D#$0A#$0D#$0A+'"아니오(N)"를 클릭하면 프로그램이 종료됩니다. ', 'MQTT Client :: 트레이로 최소화 할까요?', MB_YESNOCANCEL) of ID_YES : begin TrayIcon1.BalloonTitle := 'MQTT Client 화면을 다시 보려면...'; TrayIcon1.BalloonHint := '트레이 아이콘을 더블클릭하세요.'; //TrayIcon1.BalloonFlags := bfInfo; AppEvtMinimize(self); end; ID_NO : begin //종료 타이머 시작하고 끝나면 ExitOk~ tmrExit.Tag := 0; tmrExit.Enabled := true; end; end;//case.. end;//if~ end; procedure TfrmMain.AppEvtMinimize(Sender: TObject); begin Hide; WindowState := wsMinimized; TrayIcon1.Visible := True; TrayIcon1.Animate := True; TrayIcon1.ShowBalloonHint; end; procedure TfrmMain.Connect1Click(Sender: TObject); begin MQTTClient1.Activate(false); sleep(300); MQTTClient1.Activate(true); end; procedure TfrmMain.Disconnect1Click(Sender: TObject); begin MQTTClient1.Activate(false); end; procedure TfrmMain.FormCreate(Sender: TObject); begin NILM_ID_List := TList.Create; // NILM_Map := TDictionary.Create; Label2.Caption := ''; Label3.Caption := ''; Label4.Caption := ''; PageControl1.TabIndex := 1; appPath := ''; appPath := ExtractFilePath(Application.ExeName); if not DirectoryExists(appPath + 'log') then if not CreateDir(appPath + 'log') then raise Exception.Create('Cannot create ' + appPath + 'log'); ClearIOTGW(); Label6.Caption := format('시작 시간 : %s', [FormatDateTime('YYYY-MM-DD HH:mm:ss', now)]); end; procedure TfrmMain.SendMQTT_CtrlCommand(sTopic, sMsg : string); var pTopic : UTF8String; begin pTopic := ''; pTopic := sTopic; MQTTClient1.Publish(pTopic, sMsg, aQos, false); end; procedure TfrmMain.Timer_DateTimeTimer(Sender: TObject); begin Label5.Caption := FormatDateTime('YYYY-MM-DD HH:mm:ss', now); end; procedure TfrmMain.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;//case.. end; procedure TfrmMain.TrayIcon1DblClick(Sender: TObject); begin TrayIcon1.Visible := False; Show; WindowState := wsNormal; Application.BringToFront; end; procedure TfrmMain.insertSQL(sql : string); begin try { 인서트마다 재접속 제거 20250717 try if not ZConnection1.Connected then ZConnection1.Connect(); if ZConnection1.Connected then begin ZQuery1.Active := false; ZQuery1.SQL.Clear; ZQuery1.SQL.Add(sql); ZQuery1.ExecSQL; end; finally if ZConnection1.Connected then ZConnection1.Disconnect; end; } if not ZConnection1.Connected then ZConnection1.Connect(); if ZConnection1.Connected then begin ZQuery1.Active := false; ZQuery1.SQL.Clear; ZQuery1.SQL.Add(sql); ZQuery1.ExecSQL; end; except on e : exception do begin printlog('[insertSQL()]Database Connect Error :: '+e.Message, 'DB'); end; end; end; procedure TfrmMain.insertTimerTimer(Sender: TObject); var pMsg, S, sql : string; pTopic : UTF8String; i, p : integer; begin if debug_mode then begin Statusbar1.Panels.Items[0].Text := 'DEBUG'; Inc(debug_mode_count, 1); if debug_mode_count > 1800 then // Debug Mode는 30분만 동작한다. CPU 점유률 때문에, 24시간 돌리지 않는다. begin debug_mode_count := 0; debug_mode := false; DebugMode1.Checked := debug_mode; end; end else begin Statusbar1.Panels.Items[0].Text := 'RUN'; end; if (MQTTClient1.Online) then begin Statusbar1.Panels.Items[1].Text := 'CONN'; // dumyTopic := ''; // dumyTopic := 'SMART-FARM/QST/MQTT_HUB/PUB/0000'; // pTopic := ''; // pTopic := dumyTopic; // pMsg := ''; // S := ''; // S := formatdatetime('yyyymmddhhnnss',now()); // pMsg := format('{DATE_TIME=%s}', [S]);; // MQTTClient1.Publish(pTopic, pMsg, aQos, false); end else begin Statusbar1.Panels.Items[1].Text := 'DisCON'; end; // for p:=1 to max_gw do NILM_ID_List for i:=0 to NILM_ID_List.Count-1 do begin p := NILM_ID_List[i]; if (fNILM[p].rcvCount>0) and (fNILM[p].rcvCount <> fNILM[p].oldRcvCount) then begin fNILM[p].oldRcvCount := fNILM[p].RcvCount; sql := ''; sql := 'UPDATE nilm_value SET '+ 'pf_1='+floattostr(fNILM[p]._VALUE.PF[1] )+', '+ 'pf_2='+floattostr(fNILM[p]._VALUE.PF[2] )+', '+ 'pf_3='+floattostr(fNILM[p]._VALUE.PF[3] )+', '+ 'Vrms_1='+floattostr(fNILM[p]._VALUE.Vrms[1] )+', '+ 'Vrms_2='+floattostr(fNILM[p]._VALUE.Vrms[2] )+', '+ 'Vrms_3='+floattostr(fNILM[p]._VALUE.Vrms[3] )+', '+ 'Irms_1='+floattostr(fNILM[p]._VALUE.Irms[1] )+', '+ 'Irms_2='+floattostr(fNILM[p]._VALUE.Irms[2] )+', '+ 'Irms_3='+floattostr(fNILM[p]._VALUE.Irms[3] )+', '+ 'Var_1='+floattostr(fNILM[p]._VALUE._Var[1] )+', '+ 'Var_2='+floattostr(fNILM[p]._VALUE._Var[2] )+', '+ 'Var_3='+floattostr(fNILM[p]._VALUE._Var[3] )+', '+ 'Va_1='+floattostr(fNILM[p]._VALUE.Va[1] )+', '+ 'Va_2='+floattostr(fNILM[p]._VALUE.Va[2] )+', '+ 'Va_3='+floattostr(fNILM[p]._VALUE.Va[3] )+', '+ 'Pwr_1='+floattostr(fNILM[p]._VALUE.Pwr[1] )+', '+ 'Pwr_2='+floattostr(fNILM[p]._VALUE.Pwr[2] )+', '+ 'Pwr_3='+floattostr(fNILM[p]._VALUE.Pwr[3] )+', '+ 'gX='+floattostr(fNILM[p]._VALUE.Gyro[1])+', '+ 'gY='+floattostr(fNILM[p]._VALUE.Gyro[2])+', '+ 'gZ='+floattostr(fNILM[p]._VALUE.Gyro[3])+', '+ 'aX='+floattostr(fNILM[p]._VALUE.Accel[1])+', '+ 'aY='+floattostr(fNILM[p]._VALUE.Accel[2])+', '+ 'aZ='+floattostr(fNILM[p]._VALUE.Accel[3])+', '+ 'TempC='+floattostr(fNILM[p]._VALUE.TempC)+', '+ 'lastUpdate=now()'+ ' WHERE NilmID='+inttostr(p); if fNILM[p].idxNo > 0 then insertSQL(sql); sql := ''; sql := 'INSERT INTO '+format('nilm_%.5d',[fNILM[p].idxNo])+' VALUES(null, '+ ''+inttostr(p)+', '+ // Nilm ID ''+floattostr(fNILM[p]._VALUE.PF[1] )+', 0, '+ ''+floattostr(fNILM[p]._VALUE.Vrms[1] )+', '+ ''+floattostr(fNILM[p]._VALUE.Irms[1] )+', '+ ''+floattostr(fNILM[p]._VALUE._Var[1] )+', '+ ''+floattostr(fNILM[p]._VALUE.Va[1] )+', '+ ''+floattostr(fNILM[p]._VALUE.Pwr[1] )+', '+ ''+floattostr(fNILM[p]._VALUE.PF[2] )+', 0, '+ ''+floattostr(fNILM[p]._VALUE.Vrms[2] )+', '+ ''+floattostr(fNILM[p]._VALUE.Irms[2] )+', '+ ''+floattostr(fNILM[p]._VALUE._Var[2] )+', '+ ''+floattostr(fNILM[p]._VALUE.Va[2] )+', '+ ''+floattostr(fNILM[p]._VALUE.Pwr[2] )+', '+ ''+floattostr(fNILM[p]._VALUE.PF[3] )+', 0, '+ ''+floattostr(fNILM[p]._VALUE.Vrms[3] )+', '+ ''+floattostr(fNILM[p]._VALUE.Irms[3] )+', '+ ''+floattostr(fNILM[p]._VALUE._Var[3] )+', '+ ''+floattostr(fNILM[p]._VALUE.Va[3] )+', '+ ''+floattostr(fNILM[p]._VALUE.Pwr[3] )+', '+ ''+floattostr(fNILM[p]._VALUE.Gyro[1])+', '+ ''+floattostr(fNILM[p]._VALUE.Gyro[2])+', '+ ''+floattostr(fNILM[p]._VALUE.Gyro[3])+', '+ ''+floattostr(fNILM[p]._VALUE.Accel[1])+', '+ ''+floattostr(fNILM[p]._VALUE.Accel[2])+', '+ ''+floattostr(fNILM[p]._VALUE.Accel[3])+', '+ ''+floattostr(fNILM[p]._VALUE.TempC)+', '+ 'now())'; if fNILM[p].idxNo > 0 then insertSQL(sql); // PrintLog(IntToStr(i)+' / '+inttostr(p),'SYS'); end; end; end; procedure TfrmMain.ListBox1DblClick(Sender: TObject); begin msg_flaw_stop := not msg_flaw_stop; end; procedure TfrmMain.ProgramExit1Click(Sender: TObject); begin frmMain.Close; end; procedure TfrmMain.DebugMode1Click(Sender: TObject); begin DebugMode1.Checked := not DebugMode1.Checked; debug_mode := DebugMode1.Checked; debug_mode_count := 0; end; procedure TfrmMain.Config1Click(Sender: TObject); begin frmConfigMQTT.Show; end; procedure TfrmMain.Edit1Click(Sender: TObject); begin frmNILM.Show; end; procedure TfrmMain.N1Click(Sender: TObject); begin frmDevConfig.Show; end; function TfrmMain.DB_Connect:Boolean; begin FDPhysPgDriverLink1.VendorLib := '.\libpq-10.dll'; FDConnection1.Connected := False; FDConnection1.Params.Clear; FDConnection1.Params.DriverID := 'PG'; FDConnection1.Params.Database := Trim(dbSchema_PG); FDConnection1.Params.UserName := Trim(dbUser_PG); FDConnection1.Params.Password := Trim(dbPW_PG); FDConnection1.Params.Add('Server=' + Trim(dbIP_PG)); FDConnection1.Params.Add('Port=' + Trim(IntToStr(dbPort_PG))); FDConnection1.Params.Add('CharacterSet=UTF8'); try FDConnection1.Connected := True; Result := True; // Memo1.Lines.Add('연결 성공'); // Memo1.Lines.Add('"Q1" 버튼을 누른 후 "쿼리" 버튼을 누르세요!!'); except on E: Exception do begin Result := False; // Memo1.Lines.Add('연결 실패: ' + E.Message); end; end; end; function TfrmMain.DB_Close:Boolean; begin try if FDConnection1.Connected then FDConnection1.Connected := False; Result := True; except Result := False; end; end; function TfrmMain.xAddSqlDoubleMark(velStr: String): string; begin Result := ''; Result := '"' + velStr + '"'; end; function TfrmMain.xAddSqlMark(velStr: String): String; begin Result := ''; Result := '''' + velStr + ''''; end; end.