MMCL/SOURCE/agents/delphi_nilm_agent/uNILMDeviceForm.pas
2026-09-04 11:27:31 +09:00

310 lines
8.4 KiB
ObjectPascal

unit uNILMDeviceForm;
{
NILM 장비 추가 / 수정 다이얼로그 폼
- 기본 정보 입력 (device_id, 명칭, 위치, 상수타입, 활성여부, 메모)
- 센서 그룹별 CheckBox 설정 (탭 페이지)
- 상수 타입 변경 시 L2/L3 탭 표시/숨김 처리
}
interface
uses
Winapi.Windows, Winapi.Messages,
System.SysUtils, System.Variants, System.Classes,
System.Generics.Collections, System.UITypes, System.Math,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.ComCtrls, Vcl.Grids,
uNILMTypes;
type
TfNILMDevice = class(TForm)
pnlTop : TPanel;
lblTitle : TLabel;
pnlBottom : TPanel;
btnSave : TButton;
btnCancel : TButton;
pnlBasic : TPanel;
lblBasicInfo : TLabel;
lblDeviceID : TLabel;
edtDeviceID : TEdit;
lblDeviceName: TLabel;
edtDeviceName: TEdit;
lblLocation : TLabel;
edtLocation : TEdit;
lblPhaseType : TLabel;
rgPhaseType : TRadioGroup;
chkIsActive : TCheckBox;
lblMqttTopic : TLabel;
edtMqttTopic : TEdit;
lblMemo : TLabel;
memMemo : TMemo;
lblOpTargetPhase: TLabel;
cmbOpTargetPhase: TComboBox;
lblOpThresholdOffCurrent: TLabel;
edtOpThresholdOffCurrent: TEdit;
lblOpThresholdRunCurrent: TLabel;
edtOpThresholdRunCurrent: TEdit;
lblOpThresholdPF: TLabel;
edtOpThresholdPF: TEdit;
pnlSensor : TPanel;
lblSensorCfg : TLabel;
grdSensor : TStringGrid;
procedure btnSaveClick(Sender: TObject);
procedure btnCancelClick(Sender: TObject);
procedure rgPhaseTypeClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
FIsNew : Boolean;
FDevice : TNILMDevice;
procedure BuildSensorUI;
function ValidateInput: Boolean;
procedure PopulateFromDevice;
procedure ApplyToDevice;
public
constructor Create(AOwner: TComponent; const ADevice: TNILMDevice;
AIsNew: Boolean); reintroduce;
/// 다이얼로그 실행 후 결과 장비 정보 반환, OK 여부 반환
function Execute(out AResult: TNILMDevice): Boolean;
end;
implementation
uses
uNILMManager;
{$R *.dfm}
{ TfNILMDevice }
constructor TfNILMDevice.Create(AOwner: TComponent; const ADevice: TNILMDevice;
AIsNew: Boolean);
begin
inherited Create(AOwner);
FIsNew := AIsNew;
FDevice := ADevice;
if not Assigned(FDevice.SensorList) then
FDevice.SensorList := TNILMSensorConfigList.Create;
end;
procedure TfNILMDevice.FormCreate(Sender: TObject);
begin
if FIsNew then
Caption := 'NILM 장비 추가'
else
Caption := 'NILM 장비 수정';
lblTitle.Caption := Caption;
end;
procedure TfNILMDevice.PopulateFromDevice;
begin
edtDeviceID.Text := IntToStr(FDevice.DeviceID);
edtDeviceName.Text := FDevice.DeviceName;
edtLocation.Text := FDevice.Location;
chkIsActive.Checked:= FDevice.IsActive;
edtMqttTopic.Text := FDevice.MqttTopic;
memMemo.Text := FDevice.Memo;
cmbOpTargetPhase.ItemIndex := cmbOpTargetPhase.Items.IndexOf(FDevice.OpTargetPhase);
if cmbOpTargetPhase.ItemIndex < 0 then
cmbOpTargetPhase.ItemIndex := cmbOpTargetPhase.Items.IndexOf('AVG');
edtOpThresholdOffCurrent.Text := FloatToStr(FDevice.OpThresholdOffCurrent);
edtOpThresholdRunCurrent.Text := FloatToStr(FDevice.OpThresholdRunCurrent);
edtOpThresholdPF.Text := FloatToStr(FDevice.OpThresholdPF);
if FDevice.PhaseType = PHASE_3PHASE then
rgPhaseType.ItemIndex := 0
else
rgPhaseType.ItemIndex := 1;
BuildSensorUI;
end;
procedure TfNILMDevice.BuildSensorUI;
var
Is3Phase: Boolean;
SC : TNILMSensorConfig;
i : Integer;
begin
Is3Phase := (rgPhaseType.ItemIndex = 0);
// 센서 목록이 없으면 기본 템플릿 생성
if FDevice.SensorList.Count = 0 then
begin
FDevice.SensorList.Clear;
var TempList: TNILMSensorConfigList;
if Is3Phase then
TempList := TNILMSensorTemplate.BuildDefaultSensors(FDevice.DeviceID, PHASE_3PHASE)
else
TempList := TNILMSensorTemplate.BuildDefaultSensors(FDevice.DeviceID, PHASE_SINGLE);
FDevice.SensorList.AddRange(TempList);
TempList.Free;
end;
grdSensor.RowCount := Max(2, FDevice.SensorList.Count + 1);
grdSensor.Cells[0, 0] := '그룹';
grdSensor.Cells[1, 0] := '센서 항목';
grdSensor.Cells[2, 0] := '사용(O/X)';
grdSensor.Cells[3, 0] := 'JSON 키';
grdSensor.Cells[4, 0] := 'FieldKey';
grdSensor.ColWidths[0] := 150;
grdSensor.ColWidths[1] := 120;
grdSensor.ColWidths[2] := 90;
grdSensor.ColWidths[3] := 140;
grdSensor.ColWidths[4] := 0;
for i := 0 to FDevice.SensorList.Count - 1 do
begin
SC := FDevice.SensorList[i];
grdSensor.Cells[0, i+1] := SC.SensorGroup;
grdSensor.Cells[1, i+1] := SC.FieldLabel;
if SC.IsEnabled then
grdSensor.Cells[2, i+1] := 'O'
else
grdSensor.Cells[2, i+1] := 'X';
grdSensor.Cells[3, i+1] := SC.JsonKey;
grdSensor.Cells[4, i+1] := SC.FieldKey;
end;
end;
procedure TfNILMDevice.rgPhaseTypeClick(Sender: TObject);
begin
FDevice.SensorList.Clear;
BuildSensorUI;
end;
function TfNILMDevice.ValidateInput: Boolean;
var
ID : Integer;
D : Double;
Mgr: TNILMManager;
begin
Result := False;
if not TryStrToInt(edtDeviceID.Text, ID) or (ID <= 0) then
begin
MessageDlg('장비 번호는 1 이상의 숫자여야 합니다.', mtError, [mbOK], 0);
edtDeviceID.SetFocus;
Exit;
end;
if Trim(edtDeviceName.Text) = '' then
begin
MessageDlg('장비 명칭을 입력해주세요.', mtError, [mbOK], 0);
edtDeviceName.SetFocus;
Exit;
end;
if not TryStrToFloat(edtOpThresholdOffCurrent.Text, D) then
begin
MessageDlg('전원꺼짐 기준 전류는 숫자여야 합니다.', mtError, [mbOK], 0);
edtOpThresholdOffCurrent.SetFocus;
Exit;
end;
if not TryStrToFloat(edtOpThresholdRunCurrent.Text, D) then
begin
MessageDlg('가동 기준 전류는 숫자여야 합니다.', mtError, [mbOK], 0);
edtOpThresholdRunCurrent.SetFocus;
Exit;
end;
if not TryStrToFloat(edtOpThresholdPF.Text, D) then
begin
MessageDlg('가동 기준 파워팩터는 숫자여야 합니다.', mtError, [mbOK], 0);
edtOpThresholdPF.SetFocus;
Exit;
end;
Mgr := TNILMManager.Create;
try
try
if Mgr.DeviceIDExists(ID, FDevice.ID) then
begin
MessageDlg(Format('장비 번호 %d 는 이미 등록되어 있습니다.', [ID]), mtError, [mbOK], 0);
edtDeviceID.SetFocus;
Exit;
end;
except
on E: Exception do
begin
MessageDlg('DB 연결이 없어 중복 확인을 건너뜁니다.' + sLineBreak + E.Message,
mtWarning, [mbOK], 0);
end;
end;
finally
Mgr.Free;
end;
Result := True;
end;
procedure TfNILMDevice.ApplyToDevice;
var
SC : TNILMSensorConfig;
i : Integer;
begin
FDevice.DeviceID := StrToIntDef(edtDeviceID.Text, 0);
FDevice.DeviceName := Trim(edtDeviceName.Text);
FDevice.Location := Trim(edtLocation.Text);
FDevice.IsActive := chkIsActive.Checked;
FDevice.Memo := memMemo.Text;
// FDevice.MqttTopic := FDevice.BuildTopic; // 토픽 자동생성 금지
FDevice.OpTargetPhase := cmbOpTargetPhase.Text;
FDevice.OpThresholdOffCurrent := StrToFloatDef(edtOpThresholdOffCurrent.Text, 0.0);
FDevice.OpThresholdRunCurrent := StrToFloatDef(edtOpThresholdRunCurrent.Text, 0.0);
FDevice.OpThresholdPF := StrToFloatDef(edtOpThresholdPF.Text, 0.0);
if rgPhaseType.ItemIndex = 0 then
FDevice.PhaseType := PHASE_3PHASE
else
FDevice.PhaseType := PHASE_SINGLE;
FDevice.SensorList.Clear;
for i := 1 to grdSensor.RowCount - 1 do
begin
if Trim(grdSensor.Cells[1, i]) = '' then Continue;
SC.ID := 0;
SC.DeviceID := FDevice.DeviceID;
SC.SensorGroup := grdSensor.Cells[0, i];
SC.FieldLabel := grdSensor.Cells[1, i];
SC.IsEnabled := (Trim(grdSensor.Cells[2, i]) = 'O');
SC.JsonKey := Trim(grdSensor.Cells[3, i]);
SC.FieldKey := Trim(grdSensor.Cells[4, i]);
SC.DisplayOrder := i;
FDevice.SensorList.Add(SC);
end;
end;
procedure TfNILMDevice.btnSaveClick(Sender: TObject);
begin
if ValidateInput then
begin
ApplyToDevice;
ModalResult := mrOk;
end;
end;
procedure TfNILMDevice.btnCancelClick(Sender: TObject);
begin
ModalResult := mrCancel;
end;
function TfNILMDevice.Execute(out AResult: TNILMDevice): Boolean;
begin
PopulateFromDevice;
Result := (ShowModal = mrOk);
if Result then
AResult := FDevice;
end;
end.