1680 lines
60 KiB
ObjectPascal
1680 lines
60 KiB
ObjectPascal
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, MQTT, DB, 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, VclTee.TeeGDIPlus, VCLTee.TeEngine,
|
|
VCLTee.TeeProcs, VCLTee.Chart, VCLTee.Series, system.Math, Vcl.WinXPickers;
|
|
|
|
|
|
|
|
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;
|
|
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;
|
|
TabSheet3: TTabSheet;
|
|
Panel3: TPanel;
|
|
Panel4: TPanel;
|
|
ComboBox1: TComboBox;
|
|
Label1: TLabel;
|
|
Label7: TLabel;
|
|
ComboBox2: TComboBox;
|
|
Button1: TButton;
|
|
Edit2: TEdit;
|
|
rb_RealTimeChart: TRadioButton;
|
|
rb_LogloadChart: TRadioButton;
|
|
Panel5: TPanel;
|
|
DatePicker2: TDatePicker;
|
|
TimePicker1: TTimePicker;
|
|
Label8: TLabel;
|
|
FDQuery2: TFDQuery;
|
|
cb_PF: TCheckBox;
|
|
cb_Vrms: TCheckBox;
|
|
cb_Irms: TCheckBox;
|
|
cb_Var: TCheckBox;
|
|
cb_Va: TCheckBox;
|
|
cb_Pwr: TCheckBox;
|
|
CheckBox1: TCheckBox;
|
|
Panel6: TPanel;
|
|
Chart_main: TChart;
|
|
Panel7: TPanel;
|
|
Memo1: TMemo;
|
|
Splitter1: TSplitter;
|
|
Button2: TButton;
|
|
Button3: TButton;
|
|
|
|
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);
|
|
procedure Button1Click(Sender: TObject);
|
|
procedure ComboBox1Change(Sender: TObject);
|
|
procedure rb_LogloadChartClick(Sender: TObject);
|
|
procedure TimePicker1Change(Sender: TObject);
|
|
procedure DatePicker2Change(Sender: TObject);
|
|
procedure cb_PFClick(Sender: TObject);
|
|
procedure Chart_mainClickSeries(Sender: TCustomChart; Series: TChartSeries;
|
|
ValueIndex: Integer; Button: TMouseButton; Shift: TShiftState; X,
|
|
Y: Integer);
|
|
procedure Button2Click(Sender: TObject);
|
|
procedure Button3Click(Sender: TObject);
|
|
private
|
|
{ Private declarations }
|
|
Series1: TLineSeries; // 전력량 데이터를 저장할 라인 시리즈
|
|
Series2: TLineSeries; // 전력량 데이터를 저장할 라인 시리즈
|
|
Series3: TLineSeries; // 전력량 데이터를 저장할 라인 시리즈
|
|
Series4: TLineSeries; // 전력량 데이터를 저장할 라인 시리즈
|
|
Series5: TLineSeries; // 전력량 데이터를 저장할 라인 시리즈
|
|
Series6: TLineSeries; // 전력량 데이터를 저장할 라인 시리즈
|
|
FMaxPoints: Integer; // 차트에 표시할 최대 데이터 포인트 수
|
|
|
|
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;
|
|
procedure InitializeChart(ck:integer);
|
|
procedure LoadLogChart;
|
|
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<Integer>;
|
|
NILM_TOPIC_List : TList<string>;
|
|
|
|
// NILM_Map: TDictionary<Integer, Integer>;
|
|
// 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;
|
|
no : Double;
|
|
|
|
implementation
|
|
|
|
{$R *.dfm}
|
|
|
|
uses global, uConfigMQTT, uConfigNILM, uDevConfig;
|
|
|
|
procedure TfrmMain.Button1Click(Sender: TObject);
|
|
begin
|
|
FMaxPoints := StrToInt(edit2.Text); // 차트에 100개의 데이터 포인트만 표시
|
|
// 차트 초기화 함수 호출
|
|
if rb_RealTimeChart.Checked then
|
|
InitializeChart(0)
|
|
else
|
|
InitializeChart(1);
|
|
end;
|
|
|
|
procedure TfrmMain.Button2Click(Sender: TObject);
|
|
begin
|
|
memo1.Clear;
|
|
end;
|
|
|
|
procedure TfrmMain.Button3Click(Sender: TObject);
|
|
var
|
|
log_path : string;
|
|
begin
|
|
// save text
|
|
|
|
// log_path := StringReplace(Application.ExeName,'.exe','_NILM_'+ComboBox1.Text+'_'+ComboBox2.Text+'_'+FormatDateTime('yyyymmdd_hhnnss.zzz',Now)+'.txt',[rfReplaceAll]);
|
|
|
|
log_path := '.\log\NILM_CK'+'_'+FormatDateTime('yyyymmdd_hhnnss.zzz',Now)+'.txt';
|
|
|
|
Memo1.Lines.SaveToFile(log_path);
|
|
end;
|
|
|
|
procedure TfrmMain.cb_PFClick(Sender: TObject);
|
|
begin
|
|
Button1.Click;
|
|
if rb_LogloadChart.Checked then
|
|
LoadLogChart;
|
|
end;
|
|
|
|
procedure TfrmMain.Chart_mainClickSeries(Sender: TCustomChart;
|
|
Series: TChartSeries; ValueIndex: Integer; Button: TMouseButton;
|
|
Shift: TShiftState; X, Y: Integer);
|
|
begin
|
|
Memo1.Lines.Add(
|
|
'[ '+
|
|
'NILM_'+RightStr('00000'+ComboBox1.Text,5)+' , '+
|
|
'CH_'+ComboBox2.Text+' , '+
|
|
|
|
// 'No:'+ AdvColumnGrid1.Cells[0,ValueIndex]+','+
|
|
// 'currtime:'+AdvColumnGrid1.Cells[1,ValueIndex]+','+
|
|
'PF:'+ Series1.YValueToText( Series1.YValue[ValueIndex] ) +' , '+
|
|
'Vrms:'+ Series2.YValueToText( Series2.YValue[ValueIndex] ) +' , '+
|
|
'Irms:'+ Series3.YValueToText( Series3.YValue[ValueIndex] ) +' , '+
|
|
'Var:'+ Series4.YValueToText( Series4.YValue[ValueIndex] ) +' , '+
|
|
'Va:'+ Series5.YValueToText( Series5.YValue[ValueIndex] ) +' , '+
|
|
'Pwr:'+ Series6.YValueToText( Series6.YValue[ValueIndex] ) +
|
|
' ]'+#13
|
|
);
|
|
Memo1.SetFocus;
|
|
end;
|
|
|
|
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;
|
|
fNILM[p].oper := 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;
|
|
x : Integer;
|
|
begin
|
|
printlog('Client is online.', 'SYS');
|
|
s := TStringList.Create;
|
|
|
|
//NILM_TOPIC_List : TList<string>;
|
|
|
|
if NILM_TOPIC_List.Count > 0 then begin
|
|
for x := 0 to NILM_TOPIC_List.Count-1 do
|
|
s.AddObject(NILM_TOPIC_List[x], TObject(aQos));
|
|
end;
|
|
|
|
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;
|
|
|
|
procedure UpdateRealtimeChart(ANilmID, ACh: Integer; APF, AVrms, AIrms, AVar, AVa, APwr: Double);
|
|
begin
|
|
if not rb_RealTimeChart.Checked then Exit;
|
|
if CheckBox1.Checked then Exit;
|
|
if Trim(ComboBox1.Text) = '' then Exit;
|
|
if (ANilmID = StrToIntDef(ComboBox1.Text, -1)) and (ACh = ComboBox2.ItemIndex + 1) then
|
|
begin
|
|
if cb_PF.Checked and cb_Vrms.Checked and cb_Irms.Checked and cb_Var.Checked and cb_Va.Checked and cb_Pwr.Checked then
|
|
begin
|
|
Series1.AddXY(no, APF * 1000000, '', clRed);
|
|
Series2.AddXY(no, AVrms * 1000, '', $000080FF);
|
|
Series3.AddXY(no, AIrms * 100000, '', clYellow);
|
|
Series4.AddXY(no, AVar * 1000, '', clGreen);
|
|
Series5.AddXY(no, AVa * 1000, '', clBlue);
|
|
Series6.AddXY(no, APwr * 100, '', clPurple);
|
|
end
|
|
else
|
|
begin
|
|
Series1.AddXY(no, APF, '', clRed);
|
|
Series2.AddXY(no, AVrms, '', $000080FF);
|
|
Series3.AddXY(no, AIrms, '', clYellow);
|
|
Series4.AddXY(no, AVar, '', clGreen);
|
|
Series5.AddXY(no, AVa, '', clBlue);
|
|
Series6.AddXY(no, APwr, '', clPurple);
|
|
end;
|
|
|
|
if Series1.Count > FMaxPoints then Series1.Delete(0);
|
|
if Series2.Count > FMaxPoints then Series2.Delete(0);
|
|
if Series3.Count > FMaxPoints then Series3.Delete(0);
|
|
if Series4.Count > FMaxPoints then Series4.Delete(0);
|
|
if Series5.Count > FMaxPoints then Series5.Delete(0);
|
|
if Series6.Count > FMaxPoints then Series6.Delete(0);
|
|
|
|
Series1.Visible := cb_PF.Checked;
|
|
Series2.Visible := cb_Vrms.Checked;
|
|
Series3.Visible := cb_Irms.Checked;
|
|
Series4.Visible := cb_Var.Checked;
|
|
Series5.Visible := cb_Va.Checked;
|
|
Series6.Visible := cb_Pwr.Checked;
|
|
|
|
if Series1.Count > 0 then
|
|
Chart_Main.Axes.Bottom.SetMinMax(
|
|
Series1.XValues.Value[Max(0, Series1.Count - FMaxPoints)],
|
|
Series1.XValues.Value[Series1.Count - 1]
|
|
);
|
|
|
|
no := no + 1;
|
|
end;
|
|
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<TJSONArray>('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]]);
|
|
if debug_mode then
|
|
begin
|
|
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]);
|
|
end;
|
|
|
|
// 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;
|
|
|
|
// 실시간 차트 업데이트 (선택된 채널 1..3에 대해 갱신)
|
|
for k := 1 to 3 do
|
|
begin
|
|
UpdateRealtimeChart(nilmID, k,
|
|
fNILM[nilmID]._VALUE.PF[k],
|
|
fNILM[nilmID]._VALUE.Vrms[k],
|
|
fNILM[nilmID]._VALUE.Irms[k],
|
|
fNILM[nilmID]._VALUE._Var[k],
|
|
fNILM[nilmID]._VALUE.Va[k],
|
|
fNILM[nilmID]._VALUE.Pwr[k]);
|
|
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);
|
|
|
|
// PG 서버접속
|
|
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;
|
|
ComboBox1.Clear;
|
|
|
|
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];
|
|
|
|
ComboBox1.AddItem(NILM_ID_List[i].ToString,nil);
|
|
end;
|
|
finally
|
|
EndUpdate;
|
|
if ComboBox1.Items.Count > 0 then
|
|
ComboBox1.ItemIndex := 0;
|
|
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);
|
|
|
|
NILM_ID_List.Free;
|
|
NILM_TOPIC_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<Integer>.Create;
|
|
|
|
NILM_TOPIC_List := TList<string>.Create;
|
|
|
|
// NILM_Map := TDictionary<Integer, Integer>.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)]);
|
|
|
|
Button1.Click;
|
|
|
|
DatePicker2.Date := Now;
|
|
TimePicker1.Time := 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.TimePicker1Change(Sender: TObject);
|
|
begin
|
|
LoadLogChart;
|
|
end;
|
|
|
|
procedure TfrmMain.LoadLogChart;
|
|
var
|
|
ck_date : TDateTime;
|
|
pf, Vrms, Irms, Var_, Va, Pwr : Single;
|
|
begin
|
|
if FDConnection1.Connected then
|
|
begin
|
|
ck_date := StrToDateTime(FormatDateTime('yyyy-mm-dd',DatePicker2.Date) +' '+FormatDateTime('hh',TimePicker1.Time)+':00:00');
|
|
|
|
FDQuery2.Close;
|
|
FDQuery2.SQL.Clear;
|
|
FDQuery2.SQL.Text := ' SELECT * FROM '+format('nilm_%.5d',[fNILM[StrToInt(combobox1.Text)].idxNo])+' '+
|
|
' WHERE "lastUpdate" >= ''' +FormatDateTime('yyyy-mm-dd hh:nn:ss',ck_date) +''' '+
|
|
' AND "lastUpdate" < ''' +FormatDateTime('yyyy-mm-dd hh:nn:ss',IncHour(ck_date,1)) +''' '+
|
|
' order by "lastUpdate"; ';
|
|
|
|
// ShowMessage(FDQuery2.SQL.Text);
|
|
|
|
FDQuery2.Open;
|
|
|
|
FMaxPoints := FDQuery2.RecordCount;
|
|
// 차트 초기화 함수 호출
|
|
InitializeChart(1);
|
|
|
|
no := 0;
|
|
if FDQuery2.RecordCount> 0 then
|
|
begin
|
|
|
|
FDQuery2.First;
|
|
|
|
While not FDQuery2.Eof do
|
|
begin
|
|
if ComboBox2.ItemIndex = 0 then begin
|
|
pf := FDQuery2.FieldByName('pf_1').AsSingle;
|
|
Vrms := FDQuery2.FieldByName('Vrms_1').AsSingle;
|
|
Irms := FDQuery2.FieldByName('Irms_1').AsSingle;
|
|
Var_ := FDQuery2.FieldByName('Var_1').AsSingle;
|
|
Va := FDQuery2.FieldByName('Va_1').AsSingle;
|
|
Pwr := FDQuery2.FieldByName('Pwr_1').AsSingle;
|
|
end
|
|
else
|
|
if ComboBox2.ItemIndex = 1 then begin
|
|
pf := FDQuery2.FieldByName('pf_2').AsSingle;
|
|
Vrms := FDQuery2.FieldByName('Vrms_2').AsSingle;
|
|
Irms := FDQuery2.FieldByName('Irms_2').AsSingle;
|
|
Var_ := FDQuery2.FieldByName('Var_2').AsSingle;
|
|
Va := FDQuery2.FieldByName('Va_2').AsSingle;
|
|
Pwr := FDQuery2.FieldByName('Pwr_2').AsSingle;
|
|
end;
|
|
|
|
{
|
|
pf if CheckBox1.Checked then Series1.Add(ck*1000000, AdvColumnGrid1.Cells[0,i], $000080FF);
|
|
Volt if CheckBox2.Checked then Series2.Add(ck1*1000, AdvColumnGrid1.Cells[0,i], clRed);
|
|
CUR if CheckBox3.Checked then Series3.Add(ck2*100000, AdvColumnGrid1.Cells[0,i], clGreen);
|
|
Var_ if CheckBox4.Checked then Series4.Add(ck3*1000, AdvColumnGrid1.Cells[0,i], $00FF0080);
|
|
Va if CheckBox5.Checked then Series5.Add(ck4*1000, AdvColumnGrid1.Cells[0,i], $00FF8000);
|
|
}
|
|
|
|
if cb_PF.Checked and
|
|
cb_Vrms.Checked and
|
|
cb_Irms.Checked and
|
|
cb_Var.Checked and
|
|
cb_Va.Checked and
|
|
cb_Pwr.Checked then begin
|
|
Series1.AddXY(no, pf*1000000, '', clRed); // x: 시간, y: PF
|
|
Series2.AddXY(no, Vrms*1000, '', $000080FF); // x: 시간, y: Vrms
|
|
Series3.AddXY(no, Irms*100000, '', clYellow); // x: 시간, y: Irms
|
|
Series4.AddXY(no, Var_*1000, '', clGreen); // x: 시간, y: Pwr
|
|
Series5.AddXY(no, Va*1000, '', clBlue); // x: 시간, y: Pwr
|
|
Series6.AddXY(no, Pwr*100, '', clPurple); // x: 시간, y: Pwr
|
|
end
|
|
else begin
|
|
Series1.AddXY(no, pf, '', clRed); // x: 시간, y: PF
|
|
Series2.AddXY(no, Vrms, '', $000080FF); // x: 시간, y: Vrms
|
|
Series3.AddXY(no, Irms, '', clYellow); // x: 시간, y: Irms
|
|
Series4.AddXY(no, Var_, '', clGreen); // x: 시간, y: Pwr
|
|
Series5.AddXY(no, Va, '', clBlue); // x: 시간, y: Pwr
|
|
Series6.AddXY(no, Pwr, '', clPurple); // x: 시간, y: Pwr
|
|
end;
|
|
|
|
Series1.Visible := cb_PF.Checked;
|
|
Series2.Visible := cb_Vrms.Checked;
|
|
Series3.Visible := cb_Irms.Checked;
|
|
Series4.Visible := cb_Var.Checked;
|
|
Series5.Visible := cb_Va.Checked;
|
|
Series6.Visible := cb_Pwr.Checked;
|
|
|
|
no := no + 1;
|
|
|
|
FDQuery2.Next;
|
|
end;
|
|
|
|
Chart_Main.Axes.Bottom.SetMinMax(
|
|
0,
|
|
FDQuery2.RecordCount-1
|
|
);
|
|
end;
|
|
end;
|
|
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
|
|
if not FDConnection1.Connected then FDConnection1.Connected := True;
|
|
if FDConnection1.Connected then
|
|
begin
|
|
FDQuery1.Active := false;
|
|
FDQuery1.SQL.Clear;
|
|
FDQuery1.SQL.Add(sql);
|
|
FDQuery1.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;
|
|
x : integer;
|
|
begin
|
|
insertTimer.Enabled := false;
|
|
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;
|
|
Panel1.Visible := False;
|
|
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(DEFAULT, '+
|
|
''+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);
|
|
|
|
|
|
for x := 1 to 2 do begin
|
|
sql := '';
|
|
sql := ' SELECT dev_status, range FROM "dev_info" '+
|
|
' WHERE "nilm_id"='+inttostr(p)+
|
|
' AND "nilm_chNo"='+inttostr(x);
|
|
|
|
FDQuery2.Close;
|
|
FDQuery2.SQL.Clear;
|
|
FDQuery2.SQL.Text := sql;
|
|
|
|
FDQuery2.Open;
|
|
|
|
if FDQuery2.RecordCount > 0 then begin
|
|
if (FDQuery2.FieldByName('range').AsInteger > fNILM[p]._VALUE.Va[x]) and
|
|
(FDQuery2.FieldByName('dev_status').AsString <> '대기') then begin //대기
|
|
sql := '';
|
|
sql := ' UPDATE "dev_info" SET '+
|
|
' "dev_status"= ''대기'', '+
|
|
' "lastUpdate"=now()'+
|
|
' WHERE "nilm_id"='+inttostr(p)+
|
|
' AND "nilm_chNo"='+inttostr(x);
|
|
|
|
if fNILM[p].idxNo > 0 then insertSQL(sql);
|
|
end
|
|
else
|
|
if (FDQuery2.FieldByName('range').AsInteger <= fNILM[p]._VALUE.Va[x]) and
|
|
(FDQuery2.FieldByName('dev_status').AsString <> '가동') then begin //가동
|
|
sql := '';
|
|
sql := ' UPDATE "dev_info" SET '+
|
|
' "dev_status"= ''가동'', '+
|
|
' "lastUpdate"=now()'+
|
|
' WHERE "nilm_id"='+inttostr(p)+
|
|
' AND "nilm_chNo"='+inttostr(x);
|
|
|
|
if fNILM[p].idxNo > 0 then insertSQL(sql);
|
|
end;
|
|
end;
|
|
end;
|
|
end
|
|
else begin
|
|
for x := 1 to 2 do begin
|
|
sql := '';
|
|
sql := ' SELECT dev_status, range FROM "dev_info" '+
|
|
' WHERE "nilm_id"='+inttostr(p)+
|
|
' AND "nilm_chNo"='+inttostr(x);
|
|
|
|
FDQuery2.Close;
|
|
FDQuery2.SQL.Clear;
|
|
FDQuery2.SQL.Text := sql;
|
|
|
|
FDQuery2.Open;
|
|
|
|
if FDQuery2.RecordCount > 0 then begin
|
|
if (FDQuery2.FieldByName('dev_status').AsString <> '대기') then begin //대기
|
|
sql := '';
|
|
sql := ' UPDATE "dev_info" SET '+
|
|
' "dev_status"= ''대기'', '+
|
|
' "lastUpdate"=now()'+
|
|
' WHERE "nilm_id"='+inttostr(p)+
|
|
' AND "nilm_chNo"='+inttostr(x);
|
|
|
|
if fNILM[p].idxNo > 0 then insertSQL(sql);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
insertTimer.Enabled := True;
|
|
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.rb_LogloadChartClick(Sender: TObject);
|
|
begin
|
|
Panel5.Visible := rb_LogloadChart.Checked;
|
|
|
|
if rb_RealTimeChart.Checked then begin
|
|
FMaxPoints := StrToInt(edit2.Text); // 차트에 100개의 데이터 포인트만 표시
|
|
// 차트 초기화 함수 호출
|
|
InitializeChart(0);
|
|
CheckBox1.Visible := True;
|
|
end
|
|
else begin
|
|
LoadLogChart;
|
|
CheckBox1.Visible := False;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmMain.DebugMode1Click(Sender: TObject);
|
|
begin
|
|
DebugMode1.Checked := not DebugMode1.Checked;
|
|
|
|
debug_mode := DebugMode1.Checked;
|
|
debug_mode_count := 0;
|
|
|
|
Panel1.Visible := debug_mode;
|
|
end;
|
|
|
|
procedure TfrmMain.ComboBox1Change(Sender: TObject);
|
|
begin
|
|
Button1.Click;
|
|
if rb_LogloadChart.Checked then
|
|
LoadLogChart;
|
|
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;
|
|
|
|
procedure TfrmMain.DatePicker2Change(Sender: TObject);
|
|
begin
|
|
LoadLogChart;
|
|
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;
|
|
|
|
procedure TfrmMain.InitializeChart(ck:integer);
|
|
var
|
|
x : Integer;
|
|
begin
|
|
// 기존 시리즈가 있다면 모두 제거
|
|
Chart_Main.SeriesList.Clear;
|
|
Series1 := TLineSeries.Create(Self);
|
|
Series2 := TLineSeries.Create(Self);
|
|
Series3 := TLineSeries.Create(Self);
|
|
Series4 := TLineSeries.Create(Self);
|
|
Series5 := TLineSeries.Create(Self);
|
|
Series6 := TLineSeries.Create(Self);
|
|
// 라인 시리즈 추가
|
|
Chart_Main.AddSeries(Series1);
|
|
Chart_Main.AddSeries(Series2);
|
|
Chart_Main.AddSeries(Series3);
|
|
Chart_Main.AddSeries(Series4);
|
|
Chart_Main.AddSeries(Series5);
|
|
Chart_Main.AddSeries(Series6);
|
|
// 시리즈 설정
|
|
Series1.Title := 'PF';
|
|
Series1.Color := clRed; // 선 색상
|
|
Series1.LinePen.Width := 1; // 선 두께
|
|
Series1.Pointer.Visible := False; // 포인트 표시 여부 (선만 보려면 False)
|
|
// 시리즈 설정
|
|
Series2.Title := 'Vrms';
|
|
Series2.Color := $000080FF; // 선 색상
|
|
Series2.LinePen.Width := 1; // 선 두께
|
|
Series2.Pointer.Visible := False; // 포인트 표시 여부 (선만 보려면 False)
|
|
// 시리즈 설정
|
|
Series3.Title := 'Irms';
|
|
Series3.Color := clYellow; // 선 색상
|
|
Series3.LinePen.Width := 1; // 선 두께
|
|
Series3.Pointer.Visible := False; // 포인트 표시 여부 (선만 보려면 False)
|
|
// 시리즈 설정
|
|
Series4.Title := 'Var';
|
|
Series4.Color := clGreen; // 선 색상
|
|
Series4.LinePen.Width := 1; // 선 두께
|
|
Series4.Pointer.Visible := False; // 포인트 표시 여부 (선만 보려면 False)
|
|
// 시리즈 설정
|
|
Series5.Title := 'Va';
|
|
Series5.Color := clBlue; // 선 색상
|
|
Series5.LinePen.Width := 1; // 선 두께
|
|
Series5.Pointer.Visible := False; // 포인트 표시 여부 (선만 보려면 False)
|
|
// 시리즈 설정
|
|
Series6.Title := 'Pwr';
|
|
Series6.Color := clPurple; // 선 색상
|
|
Series6.LinePen.Width := 1; // 선 두께
|
|
Series6.Pointer.Visible := False; // 포인트 표시 여부 (선만 보려면 False)
|
|
// 차트 축 설정
|
|
Chart_Main.Axes.Bottom.Title.Caption := '시간';
|
|
Chart_Main.Axes.Left.Title.Caption := 'PF / Vrms / Irms / Var / Va / Pwr';
|
|
Chart_Main.Axes.Left.Minimum := 0; // 최소값 설정 (필요에 따라 조절)
|
|
Chart_Main.Axes.Left.Maximum := 100; // 최대값 설정 (필요에 따라 조절)
|
|
Chart_Main.Axes.Bottom.DateTimeFormat := 'mm:ss'; // 시간 포맷 설정
|
|
// 차트 제목
|
|
Chart_Main.Title.Text.Text := '실시간 (PF / Vrms / Irms / Var / Va / Pwr) 모니터링';
|
|
Chart_Main.Title.Visible := True;
|
|
// 범례 표시
|
|
Chart_Main.Legend.Visible := True;
|
|
// 애니메이션 효과 (선택 사항)
|
|
// Chart1.Options.ClipSeries := False; // 스크롤링 시 부드러운 움직임
|
|
if ck = 0 then begin
|
|
for x := 0 to FMaxPoints-1 do begin
|
|
Series1.AddXY(x, 0, '', clRed); // x: 시간, y: PF
|
|
Series2.AddXY(x, 0, '', $000080FF); // x: 시간, y: Vrms
|
|
Series3.AddXY(x, 0, '', clYellow); // x: 시간, y: Irms
|
|
Series4.AddXY(x, 0, '', clGreen); // x: 시간, y: Var
|
|
Series5.AddXY(x, 0, '', clBlue); // x: 시간, y: Va
|
|
Series6.AddXY(x, 0, '', clPurple); // x: 시간, y: Pwr
|
|
end;
|
|
no := FMaxPoints;
|
|
end;
|
|
end;
|
|
|
|
end.
|