893 lines
28 KiB
Plaintext
893 lines
28 KiB
Plaintext
unit uMain;
|
||
|
||
interface
|
||
|
||
uses
|
||
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
|
||
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, CPort, Vcl.Menus, Vcl.ExtCtrls,
|
||
Vcl.ComCtrls, Vcl.Grids, Vcl.StdCtrls, System.IniFiles,
|
||
System.JSON, IdTCPClient, IdGlobal, IdComponent, UMQTTClient;
|
||
|
||
const
|
||
slvID = $01;
|
||
cmdRCR = $01;
|
||
cmdRHR = $03;
|
||
cmdFSC = $05;
|
||
cmdPSR = $06;
|
||
|
||
const
|
||
comm_shm_addr = 0;
|
||
commOK = 1;
|
||
commDisconnect = 0;
|
||
commError = 2;
|
||
manual_shm_addr = 10;
|
||
auto_shm_addr = 11;
|
||
rsON = 1;
|
||
rsOFF = 0;
|
||
valve01_ctrl_shm_addr = 35;
|
||
valve01_ctrlSts_shm_addr = 24;
|
||
vsOpen = 1;
|
||
vsClose = 0;
|
||
pulse_reset_shm_addr = 40;
|
||
SHM_SCHEDULE_OPERATING_AUTO = 50;
|
||
SHM_CTRL_SCHEDULE_OPERATING_AUTO = 51;
|
||
|
||
// Simplified shared memory class
|
||
type
|
||
TSharedMemory = class
|
||
private
|
||
FData: array[0..255] of Byte;
|
||
public
|
||
procedure Open;
|
||
procedure Close;
|
||
procedure SetB(address: Integer; value: Byte);
|
||
procedure SetR(address: Integer; value: Word);
|
||
function GetB(address: Integer): Byte;
|
||
function GetR(address: Integer): Word;
|
||
end;
|
||
|
||
type
|
||
TfMain = class(TForm)
|
||
Label1: TLabel;
|
||
Label2: TLabel;
|
||
Label3: TLabel;
|
||
Label4: TLabel;
|
||
Label5: TLabel;
|
||
Label6: TLabel;
|
||
Label7: TLabel;
|
||
Memo1: TMemo;
|
||
grid_di01: TStringGrid;
|
||
grid_di02: TStringGrid;
|
||
grid_do01: TStringGrid;
|
||
grid_do02: TStringGrid;
|
||
grid_pulse: TStringGrid;
|
||
grid_doCmd: TStringGrid;
|
||
StatusBar1: TStatusBar;
|
||
ReadTimer: TTimer;
|
||
initTimer: TTimer;
|
||
CtrlTimer: TTimer;
|
||
MainMenu1: TMainMenu;
|
||
File1: TMenuItem;
|
||
Exit1: TMenuItem;
|
||
Comm1: TMenuItem;
|
||
Open1: TMenuItem;
|
||
Close1: TMenuItem;
|
||
N1: TMenuItem;
|
||
AutoRun1: TMenuItem;
|
||
N3: TMenuItem;
|
||
AutoHide: TMenuItem;
|
||
PopupMenu1: TPopupMenu;
|
||
SHOW1: TMenuItem;
|
||
HIDE1: TMenuItem;
|
||
N2: TMenuItem;
|
||
EXIT2: TMenuItem;
|
||
ComPort1: TComPort;
|
||
Edit1: TEdit;
|
||
Edit2: TEdit;
|
||
Label8: TLabel;
|
||
Label9: TLabel;
|
||
procedure ComPort1AfterClose(Sender: TObject);
|
||
procedure ComPort1AfterOpen(Sender: TObject);
|
||
procedure ComPort1RxChar(Sender: TObject; Count: Integer);
|
||
procedure FormClose(Sender: TObject; var Action: TCloseAction);
|
||
procedure FormCreate(Sender: TObject);
|
||
procedure FormDestroy(Sender: TObject);
|
||
procedure grid_di01DrawCell(Sender: TObject; ACol, ARow: Integer;
|
||
Rect: TRect; State: TGridDrawState);
|
||
procedure grid_di02DrawCell(Sender: TObject; ACol, ARow: Integer;
|
||
Rect: TRect; State: TGridDrawState);
|
||
procedure grid_do01DrawCell(Sender: TObject; ACol, ARow: Integer;
|
||
Rect: TRect; State: TGridDrawState);
|
||
procedure grid_do02DrawCell(Sender: TObject; ACol, ARow: Integer;
|
||
Rect: TRect; State: TGridDrawState);
|
||
procedure grid_doCmdDrawCell(Sender: TObject; ACol, ARow: Integer;
|
||
Rect: TRect; State: TGridDrawState);
|
||
procedure Exit1Click(Sender: TObject);
|
||
procedure Open1Click(Sender: TObject);
|
||
procedure Close1Click(Sender: TObject);
|
||
procedure initTimerTimer(Sender: TObject);
|
||
procedure AutoRun1Click(Sender: TObject);
|
||
procedure CtrlTimerTimer(Sender: TObject);
|
||
procedure AutoHideClick(Sender: TObject);
|
||
procedure EXIT2Click(Sender: TObject);
|
||
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
|
||
procedure ReadTimerTimer(Sender: TObject);
|
||
private
|
||
{ Private declarations }
|
||
appPath, c_Port, db_ip, db_user, db_pw, ctrlID: string;
|
||
db_port: integer;
|
||
wrtList: TStrings;
|
||
req_flag, exit_flag: Boolean;
|
||
dPacket: array [1..255] of Byte;
|
||
com_step, packet_count, read_No, timeout_count: integer;
|
||
// MQTT
|
||
FMQTTClient: TMQTTClient;
|
||
// MQTT_Client: TIdTCPClient;
|
||
MQTT_Timer: TTimer;
|
||
mqtt_ip, mqtt_topicH, mqtt_topicT, mqtt_GtwyID: string;
|
||
mqtt_port: Integer;
|
||
prev_pulse: array[1..4] of Integer;
|
||
procedure MQTT_TimerTimer(Sender: TObject);
|
||
procedure SendMQTTData;
|
||
function CalculateCRC(data: array of Byte; length: Integer): Word;
|
||
procedure SendModbusRequest(slaveID, functionCode: Byte; startAddr, count: Word);
|
||
procedure SendModbusRTUCommand(DeviceID, FunctionCode: Byte; RegisterAddress, Value: Word);
|
||
procedure SendModbusRTUCoilCommand(DeviceID: Byte; CoilAddress: Word; CoilOn: Boolean);
|
||
procedure SendModbus(DeviceID, cmd: Byte; Address, Value: Word);
|
||
function GetBit(value: Byte; bitIndex: Integer): Byte;
|
||
procedure OnMQTTMessage(const ATopic, APayload: string);
|
||
procedure OnMQTTStatus(AConnected: Boolean);
|
||
public
|
||
{ Public declarations }
|
||
procedure printlog(_msg: string);
|
||
end;
|
||
|
||
var
|
||
fMain: TfMain;
|
||
SharedMem: TSharedMemory;
|
||
|
||
implementation
|
||
|
||
{$R *.dfm}
|
||
|
||
procedure TSharedMemory.Open;
|
||
begin
|
||
FillChar(FData, SizeOf(FData), 0);
|
||
end;
|
||
|
||
procedure TSharedMemory.Close;
|
||
begin
|
||
// Cleanup
|
||
end;
|
||
|
||
procedure TSharedMemory.SetB(address: Integer; value: Byte);
|
||
begin
|
||
if (address >= 0) and (address < 256) then
|
||
FData[address] := value;
|
||
end;
|
||
|
||
function TSharedMemory.GetB(address: Integer): Byte;
|
||
begin
|
||
if (address >= 0) and (address < 256) then
|
||
Result := FData[address]
|
||
else
|
||
Result := 0;
|
||
end;
|
||
|
||
procedure TSharedMemory.SetR(address: Integer; value: Word);
|
||
begin
|
||
if (address >= 0) and (address < 255) then
|
||
begin
|
||
FData[address] := Byte(value shr 8);
|
||
FData[address + 1] := Byte(value and $FF);
|
||
end;
|
||
end;
|
||
|
||
function TSharedMemory.GetR(address: Integer): Word;
|
||
begin
|
||
if (address >= 0) and (address < 255) then
|
||
Result := (FData[address] shl 8) or FData[address + 1]
|
||
else
|
||
Result := 0;
|
||
end;
|
||
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> MQTT <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
procedure TfMain.OnMQTTStatus(AConnected: Boolean);
|
||
begin
|
||
{
|
||
if AConnected then begin
|
||
lblConnectionStatus.Caption := 'Connected';
|
||
lblConnectionStatus.Font.Color := CLR_GREEN;
|
||
lblConnectionStatus.Tag := 1;
|
||
end else begin
|
||
lblConnectionStatus.Caption := 'Disconnected';
|
||
lblConnectionStatus.Font.Color := CLR_RED;
|
||
lblConnectionStatus.Tag := 2;
|
||
mmMQTTmsg.Lines.Add('Connection Error - Connection to server was lost.');
|
||
end;
|
||
}
|
||
end;
|
||
|
||
procedure TfMain.OnMQTTMessage(const ATopic, APayload: string);
|
||
var
|
||
JsonObj: TJSONObject;
|
||
i, DONum: Integer;
|
||
Pair: TJSONPair;
|
||
KeyStr, ValStr: string;
|
||
begin
|
||
// mmMQTTmsg.Lines.Add(ATopic+' '+APayload);
|
||
Edit2.Text := ATopic+' '+APayload;
|
||
|
||
try
|
||
JsonObj := TJSONObject.ParseJSONValue(APayload) as TJSONObject;
|
||
if Assigned(JsonObj) then
|
||
begin
|
||
try
|
||
for i := 0 to JsonObj.Count - 1 do
|
||
begin
|
||
Pair := JsonObj.Pairs[i];
|
||
KeyStr := Pair.JsonString.Value;
|
||
ValStr := Pair.JsonValue.Value;
|
||
|
||
if (Length(KeyStr) >= 5) and (Copy(KeyStr, 1, 3) = 'DO_') then
|
||
begin
|
||
DONum := StrToIntDef(Copy(KeyStr, 4, Length(KeyStr) - 3), -1);
|
||
if DONum >= 0 then
|
||
begin
|
||
if ValStr = '1' then
|
||
SendModbus(slvID, cmdFSC, 35 + DONum, 1)
|
||
else if ValStr = '0' then
|
||
SendModbus(slvID, cmdFSC, 35 + DONum, 0);
|
||
end;
|
||
end;
|
||
end;
|
||
finally
|
||
JsonObj.Free;
|
||
end;
|
||
end;
|
||
except
|
||
on E: Exception do printlog('MQTT Msg Error: ' + E.Message);
|
||
end;
|
||
end;
|
||
|
||
procedure TfMain.ComPort1AfterClose(Sender: TObject);
|
||
begin
|
||
printlog('Comm Close');
|
||
ReadTimer.Enabled := false;
|
||
Open1.Enabled := True;
|
||
Close1.Enabled := False;
|
||
end;
|
||
|
||
procedure TfMain.ComPort1AfterOpen(Sender: TObject);
|
||
begin
|
||
printlog('Comm Open');
|
||
packet_count := 0;
|
||
read_No := 0;
|
||
timeout_count := 0;
|
||
req_flag := true;
|
||
ReadTimer.Enabled := True;
|
||
Open1.Enabled := False;
|
||
Close1.Enabled := True;
|
||
end;
|
||
|
||
procedure TfMain.ComPort1RxChar(Sender: TObject; Count: Integer);
|
||
var
|
||
S: string;
|
||
i, dVal, p, shm_addr: integer;
|
||
noofdata, mVal: word;
|
||
tmp_mVal: array [0..1] of byte;
|
||
b: Byte;
|
||
Buffer: array of Byte;
|
||
begin
|
||
SetLength(Buffer, Count);
|
||
ComPort1.Read(Buffer[0], Count);
|
||
S := '';
|
||
for i := 0 to Count - 1 do S := S + Char(Buffer[i]);
|
||
|
||
case read_No of
|
||
0:
|
||
begin
|
||
for i := 0 to Count - 1 do
|
||
begin
|
||
Inc(packet_count, 1);
|
||
if packet_count >= 25 then
|
||
begin
|
||
if (dPacket[1] = slvID) and (dPacket[2] = cmdRHR) and (dPacket[3] = $14) then
|
||
begin
|
||
noofdata := dPacket[3] div 2;
|
||
for p := 1 to 4 do
|
||
begin
|
||
FillChar(tmp_mVal, sizeof(tmp_mVal), 0);
|
||
tmp_mVal[1] := dPacket[4 + (p - 1) * 2];
|
||
tmp_mVal[0] := dPacket[5 + (p - 1) * 2];
|
||
Move(tmp_mVal[0], mVal, 2);
|
||
SharedMem.SetR(p - 1, mVal);
|
||
grid_pulse.Cells[1, p] := inttostr(mVal);
|
||
end;
|
||
printlog('*************************** Read Word Complete');
|
||
FillChar(dPacket[1], Sizeof(dPacket), 0);
|
||
read_No := 1; packet_count := 0; timeout_count := 0; req_flag := true;
|
||
end;
|
||
end
|
||
else dPacket[packet_count] := Buffer[i];
|
||
end;
|
||
end;
|
||
1:
|
||
begin
|
||
for i := 0 to Count - 1 do
|
||
begin
|
||
Inc(packet_count, 1);
|
||
if packet_count >= 12 then
|
||
begin
|
||
if (dPacket[1] = slvID) and (dPacket[2] = cmdRCR) and (dPacket[3] = $07) then
|
||
begin
|
||
shm_addr := 0;
|
||
dVal := dPacket[4];
|
||
for p := 0 to 7 do begin
|
||
b := GetBit(dVal, p); grid_di01.Cells[1, p + 1] := IntToStr(b);
|
||
SharedMem.SetB(shm_addr, b); Inc(shm_addr, 1);
|
||
end;
|
||
dVal := dPacket[5];
|
||
for p := 0 to 7 do begin
|
||
b := GetBit(dVal, p); grid_di02.Cells[1, p + 1] := IntToStr(b);
|
||
SharedMem.SetB(shm_addr, b); Inc(shm_addr, 1);
|
||
end;
|
||
dVal := dPacket[6];
|
||
for p := 0 to 7 do begin
|
||
b := GetBit(dVal, p); grid_do01.Cells[1, p + 1] := IntToStr(b);
|
||
SharedMem.SetB(shm_addr, b); Inc(shm_addr, 1);
|
||
end;
|
||
dVal := dPacket[7];
|
||
for p := 0 to 7 do begin
|
||
b := GetBit(dVal, p); grid_do02.Cells[1, p + 1] := IntToStr(b);
|
||
SharedMem.SetB(shm_addr, b); Inc(shm_addr, 1);
|
||
end;
|
||
dVal := dPacket[8];
|
||
for p := 0 to 7 do begin
|
||
b := GetBit(dVal, p); grid_doCmd.Cells[p + 1, 1] := IntToStr(b); Inc(shm_addr, 1);
|
||
end;
|
||
dVal := dPacket[9];
|
||
for p := 0 to 7 do begin
|
||
b := GetBit(dVal, p); grid_doCmd.Cells[p + 9, 1] := IntToStr(b); Inc(shm_addr, 1);
|
||
end;
|
||
printlog('*************************** Read Coil Complete');
|
||
FillChar(dPacket[1], Sizeof(dPacket), 0);
|
||
read_No := 0; packet_count := 0; timeout_count := 0; req_flag := true;
|
||
end;
|
||
end
|
||
else dPacket[packet_count] := Buffer[i];
|
||
end;
|
||
end;
|
||
2:
|
||
begin
|
||
for i := 0 to Count - 1 do
|
||
begin
|
||
Inc(packet_count, 1);
|
||
if packet_count >= 8 then
|
||
begin
|
||
if (dPacket[1] = slvID) and ((dPacket[2] = cmdFSC) or (dPacket[2] = cmdPSR)) then
|
||
begin
|
||
FillChar(dPacket[1], Sizeof(dPacket), 0);
|
||
read_No := 0; packet_count := 0; timeout_count := 0; req_flag := true;
|
||
end;
|
||
end
|
||
else dPacket[packet_count] := Buffer[i];
|
||
end;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TfMain.FormClose(Sender: TObject; var Action: TCloseAction);
|
||
begin
|
||
ReadTimer.Enabled := False;
|
||
if Comport1.Connected then Comport1.Close;
|
||
SharedMem.Close;
|
||
end;
|
||
|
||
procedure TfMain.FormCreate(Sender: TObject);
|
||
begin
|
||
grid_doCmd.Cells[ 0, 0] := 'No';
|
||
grid_doCmd.Cells[ 0, 1] := 'val';
|
||
grid_doCmd.Cells[ 1, 0] := '32';
|
||
grid_doCmd.Cells[ 2, 0] := '33';
|
||
grid_doCmd.Cells[ 3, 0] := '34';
|
||
grid_doCmd.Cells[ 4, 0] := '35';
|
||
grid_doCmd.Cells[ 5, 0] := '36';
|
||
grid_doCmd.Cells[ 6, 0] := '37';
|
||
grid_doCmd.Cells[ 7, 0] := '38';
|
||
grid_doCmd.Cells[ 8, 0] := '39';
|
||
grid_doCmd.Cells[ 9, 0] := '40';
|
||
grid_doCmd.Cells[10, 0] := '41';
|
||
grid_doCmd.Cells[11, 0] := '42';
|
||
grid_doCmd.Cells[12, 0] := '43';
|
||
grid_doCmd.Cells[13, 0] := '44';
|
||
grid_doCmd.Cells[14, 0] := '45';
|
||
grid_doCmd.Cells[15, 0] := '46';
|
||
grid_doCmd.Cells[16, 0] := '47';
|
||
|
||
grid_di01.Cells[ 0, 0] := 'No';
|
||
grid_di01.Cells[ 1, 0] := 'val';
|
||
grid_di01.Cells[ 0, 1] := '0';
|
||
grid_di01.Cells[ 0, 2] := '1';
|
||
grid_di01.Cells[ 0, 3] := '2';
|
||
grid_di01.Cells[ 0, 4] := '3';
|
||
grid_di01.Cells[ 0, 5] := '4';
|
||
grid_di01.Cells[ 0, 6] := '5';
|
||
grid_di01.Cells[ 0, 7] := '6';
|
||
grid_di01.Cells[ 0, 8] := '7';
|
||
|
||
grid_di02.Cells[ 0, 0] := 'No';
|
||
grid_di02.Cells[ 1, 0] := 'val';
|
||
grid_di02.Cells[ 0, 1] := '0';
|
||
grid_di02.Cells[ 0, 2] := '1';
|
||
grid_di02.Cells[ 0, 3] := '2';
|
||
grid_di02.Cells[ 0, 4] := '3';
|
||
grid_di02.Cells[ 0, 5] := '4';
|
||
grid_di02.Cells[ 0, 6] := '5';
|
||
grid_di02.Cells[ 0, 7] := '6';
|
||
grid_di02.Cells[ 0, 8] := '7';
|
||
|
||
grid_do01.Cells[ 0, 0] := 'No';
|
||
grid_do01.Cells[ 1, 0] := 'val';
|
||
grid_do01.Cells[ 0, 1] := '0';
|
||
grid_do01.Cells[ 0, 2] := '1';
|
||
grid_do01.Cells[ 0, 3] := '2';
|
||
grid_do01.Cells[ 0, 4] := '3';
|
||
grid_do01.Cells[ 0, 5] := '4';
|
||
grid_do01.Cells[ 0, 6] := '5';
|
||
grid_do01.Cells[ 0, 7] := '6';
|
||
grid_do01.Cells[ 0, 8] := '7';
|
||
|
||
grid_do02.Cells[ 0, 0] := 'No';
|
||
grid_do02.Cells[ 1, 0] := 'val';
|
||
grid_do02.Cells[ 0, 1] := '0';
|
||
grid_do02.Cells[ 0, 2] := '1';
|
||
grid_do02.Cells[ 0, 3] := '2';
|
||
grid_do02.Cells[ 0, 4] := '3';
|
||
grid_do02.Cells[ 0, 5] := '4';
|
||
grid_do02.Cells[ 0, 6] := '5';
|
||
grid_do02.Cells[ 0, 7] := '6';
|
||
grid_do02.Cells[ 0, 8] := '7';
|
||
|
||
grid_pulse.Cells[ 0, 0] := 'No';
|
||
grid_pulse.Cells[ 1, 0] := 'val';
|
||
grid_pulse.Cells[ 0, 1] := '0';
|
||
grid_pulse.Cells[ 0, 2] := '1';
|
||
grid_pulse.Cells[ 0, 3] := '2';
|
||
grid_pulse.Cells[ 0, 4] := '3';
|
||
|
||
appPath := ExtractFilepath(Application.ExeName);
|
||
wrtList := TStringList.Create;
|
||
SharedMem := TSharedMemory.Create;
|
||
// MQTT_Client := TIdTCPClient.Create(nil);
|
||
MQTT_Timer := TTimer.Create(nil);
|
||
MQTT_Timer.Enabled := False;
|
||
MQTT_Timer.Interval := 1000;
|
||
MQTT_Timer.OnTimer := MQTT_TimerTimer;
|
||
prev_pulse[1] := -1;
|
||
prev_pulse[2] := -1;
|
||
prev_pulse[3] := -1;
|
||
prev_pulse[4] := -1;
|
||
end;
|
||
|
||
procedure TfMain.FormDestroy(Sender: TObject);
|
||
begin
|
||
wrtList.Free;
|
||
SharedMem.Free;
|
||
// MQTT_Client.Free;
|
||
if FMQTTClient <> nil then begin
|
||
FMQTTClient.OnMessage := nil;
|
||
FMQTTClient.OnStatus := nil;
|
||
FMQTTClient.Disconnect;
|
||
FreeAndNil(FMQTTClient);
|
||
end;
|
||
MQTT_Timer.Free;
|
||
end;
|
||
|
||
procedure TfMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
|
||
begin
|
||
CanClose := exit_flag;
|
||
end;
|
||
|
||
procedure TfMain.Exit1Click(Sender: TObject);
|
||
begin
|
||
exit_flag := true;
|
||
fMain.close;
|
||
end;
|
||
|
||
procedure TfMain.EXIT2Click(Sender: TObject);
|
||
begin
|
||
exit_flag := true;
|
||
fMain.Close;
|
||
end;
|
||
|
||
procedure TfMain.CtrlTimerTimer(Sender: TObject);
|
||
var i: integer;
|
||
begin
|
||
if (ComPort1.Connected) then begin
|
||
if SharedMem.GetB(auto_shm_addr) = rsON then begin
|
||
for i := 0 to 7 do begin
|
||
if (SharedMem.GetB(valve01_ctrl_shm_addr + i) = vsOpen) and (SharedMem.GetB(valve01_ctrlSts_shm_addr + i) = vsClose) then
|
||
SendModbus(slvID, cmdFSC, valve01_ctrl_shm_addr + i, vsOpen)
|
||
else if (SharedMem.GetB(valve01_ctrl_shm_addr + i) = vsClose) and (SharedMem.GetB(valve01_ctrlSts_shm_addr + i) = vsOpen) then
|
||
SendModbus(slvID, cmdFSC, valve01_ctrl_shm_addr + i, vsClose);
|
||
end;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TfMain.AutoRun1Click(Sender: TObject);
|
||
var ini: Tinifile;
|
||
begin
|
||
AutoRun1.Checked := not AutoRun1.Checked;
|
||
ini := TInifile.Create(appPath + 'xFarm.ini');
|
||
if AutoRun1.Checked then ini.WriteString('COMM', 'AUTO_RUN', 'YES')
|
||
else ini.WriteString('COMM', 'AUTO_RUN', 'NO');
|
||
ini.Free;
|
||
end;
|
||
|
||
procedure TfMain.grid_di01DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
|
||
begin
|
||
if (ARow > 0) and (ACol > 0) then begin
|
||
if StrToIntDef(grid_di01.Cells[ACol, ARow], 0) = 0 then grid_di01.Canvas.Brush.Color := clWhite
|
||
else grid_di01.Canvas.Brush.Color := clRed;
|
||
grid_di01.Canvas.FillRect(Rect);
|
||
grid_di01.Canvas.TextOut(Rect.Left + 2, Rect.Top + 2, grid_di01.Cells[ACol, ARow]);
|
||
end;
|
||
end;
|
||
|
||
procedure TfMain.grid_di02DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
|
||
begin
|
||
if (ARow > 0) and (ACol > 0) then begin
|
||
if StrToIntDef(grid_di02.Cells[ACol, ARow], 0) = 0 then grid_di02.Canvas.Brush.Color := clWhite
|
||
else grid_di02.Canvas.Brush.Color := clRed;
|
||
grid_di02.Canvas.FillRect(Rect);
|
||
grid_di02.Canvas.TextOut(Rect.Left + 2, Rect.Top + 2, grid_di02.Cells[ACol, ARow]);
|
||
end;
|
||
end;
|
||
|
||
procedure TfMain.grid_do01DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
|
||
begin
|
||
if (ARow > 0) and (ACol > 0) then begin
|
||
if StrToIntDef(grid_do01.Cells[ACol, ARow], 0) = 0 then grid_do01.Canvas.Brush.Color := clWhite
|
||
else grid_do01.Canvas.Brush.Color := clRed;
|
||
grid_do01.Canvas.FillRect(Rect);
|
||
grid_do01.Canvas.TextOut(Rect.Left + 2, Rect.Top + 2, grid_do01.Cells[ACol, ARow]);
|
||
end;
|
||
end;
|
||
|
||
procedure TfMain.grid_do02DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
|
||
begin
|
||
if (ARow > 0) and (ACol > 0) then begin
|
||
if StrToIntDef(grid_do02.Cells[ACol, ARow], 0) = 0 then grid_do02.Canvas.Brush.Color := clWhite
|
||
else grid_do02.Canvas.Brush.Color := clRed;
|
||
grid_do02.Canvas.FillRect(Rect);
|
||
grid_do02.Canvas.TextOut(Rect.Left + 2, Rect.Top + 2, grid_do02.Cells[ACol, ARow]);
|
||
end;
|
||
end;
|
||
|
||
procedure TfMain.grid_doCmdDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
|
||
begin
|
||
if (ARow > 0) and (ACol > 0) then begin
|
||
if StrToIntDef(grid_doCmd.Cells[ACol, ARow], 0) = 0 then grid_doCmd.Canvas.Brush.Color := clWhite
|
||
else grid_doCmd.Canvas.Brush.Color := clRed;
|
||
grid_doCmd.Canvas.FillRect(Rect);
|
||
grid_doCmd.Canvas.TextOut(Rect.Left + 2, Rect.Top + 2, grid_doCmd.Cells[ACol, ARow]);
|
||
end;
|
||
end;
|
||
|
||
procedure TfMain.AutoHideClick(Sender: TObject);
|
||
var
|
||
ini: Tinifile;
|
||
begin
|
||
AutoHide.Checked := not AutoHide.Checked;
|
||
ini := TInifile.Create(appPath + 'xFarm.ini');
|
||
if AutoHide.Checked then ini.WriteString('COMM', 'AUTO_HIDE', 'YES')
|
||
else ini.WriteString('COMM', 'AUTO_HIDE', 'NO');
|
||
ini.Free;
|
||
end;
|
||
|
||
procedure TfMain.initTimerTimer(Sender: TObject);
|
||
var
|
||
ini: Tinifile;
|
||
auto_run, auto_hide: string;
|
||
begin
|
||
exit_flag := false;
|
||
SharedMem.Open;
|
||
Memo1.Clear;
|
||
ini := TInifile.Create(appPath + 'xFarm.ini');
|
||
with ini do begin
|
||
ctrlID := ReadString('CONTROLER', 'CODE', '');
|
||
c_Port := ReadString('COMM', 'PORT', 'COM7');
|
||
auto_run := ReadString('COMM', 'AUTO_RUN', 'NO');
|
||
auto_hide := ReadString('COMM', 'AUTO_HIDE', 'NO');
|
||
db_ip := ReadString('DB', 'IP', '127.0.0.1');
|
||
db_user := ReadString('DB', 'USER', 'root');
|
||
db_pw := ReadString('DB', 'PW', '1233');
|
||
db_port := ReadInteger('DB', 'PORT', 3306);
|
||
mqtt_ip := ReadString('MQTT', 'IP', '192.168.200.240');
|
||
mqtt_port := ReadInteger('MQTT', 'PORT', 1883);
|
||
// mqtt_topic := ReadString('MQTT', 'TOPIC', 'SMART-FARM/2/3/2/PUB/1/1/0/GTW/1001');
|
||
mqtt_topicH := ReadString('MQTT', 'TOPICH', 'SMART-FARM/2/3/2/');
|
||
mqtt_topicT := ReadString('MQTT', 'TOPICT', '/1/1/0/GTW/');
|
||
mqtt_GtwyID := ReadString('MQTT', 'GtwyID', '1001');
|
||
end;
|
||
ini.Free;
|
||
|
||
FMQTTClient := TMQTTClient.Create(mqtt_ip, mqtt_port, 'SmartFarmHMI');
|
||
FMQTTClient.OnMessage := OnMQTTMessage;
|
||
FMQTTClient.OnStatus := OnMQTTStatus;
|
||
// FMQTTClient.Subscribe(mqtt_topic);
|
||
// FMQTTClient.Subscribe(ReplaceAll(mqtt_topic,'PUB','SUB'));
|
||
FMQTTClient.Subscribe(mqtt_topicH+'SUB'+mqtt_topicT+mqtt_GtwyID);
|
||
FMQTTClient.Connect;
|
||
|
||
MQTT_Timer.Enabled := True;
|
||
|
||
StatusBar1.Panels.Items[3].Text := c_Port;
|
||
StatusBar1.Panels.Items[4].Text := ctrlID;
|
||
Comport1.Port := c_Port;
|
||
if upperCase(Trim(auto_run)) = 'YES' then begin
|
||
AutoRun1.Checked := True;
|
||
if not Comport1.Connected then Comport1.Open;
|
||
end else AutoRun1.Checked := False;
|
||
if upperCase(Trim(auto_hide)) = 'YES' then AutoHide.Checked := True
|
||
else AutoHide.Checked := False;
|
||
initTimer.Enabled := False;
|
||
end;
|
||
|
||
procedure TfMain.Close1Click(Sender: TObject);
|
||
begin
|
||
if ComPort1.Connected then ComPort1.Close;
|
||
end;
|
||
|
||
procedure TfMain.Open1Click(Sender: TObject);
|
||
begin
|
||
if not Comport1.Connected then ComPort1.Open;
|
||
end;
|
||
|
||
function TfMain.GetBit(value: Byte; bitIndex: Integer): Byte;
|
||
begin
|
||
if (bitIndex >= 0) and (bitIndex <= 7) then Result := (value shr bitIndex) and 1
|
||
else Result := 0;
|
||
end;
|
||
|
||
function TfMain.CalculateCRC(data: array of Byte; length: Integer): Word;
|
||
var i, j: Integer; crc: Word;
|
||
begin
|
||
crc := $FFFF;
|
||
for i := 0 to length - 1 do begin
|
||
crc := crc xor data[i];
|
||
for j := 0 to 7 do begin
|
||
if (crc and 1) <> 0 then crc := (crc shr 1) xor $A001
|
||
else crc := crc shr 1;
|
||
end;
|
||
end;
|
||
Result := crc;
|
||
end;
|
||
|
||
procedure TfMain.SendModbusRequest(slaveID, functionCode: Byte; startAddr, count: Word);
|
||
var request: array[0..7] of Byte; crc: Word;
|
||
begin
|
||
request[0] := slaveID; request[1] := functionCode;
|
||
request[2] := Hi(startAddr); request[3] := Lo(startAddr);
|
||
request[4] := Hi(count); request[5] := Lo(count);
|
||
crc := CalculateCRC(request, 6);
|
||
request[6] := Lo(crc); request[7] := Hi(crc);
|
||
ComPort1.Write(request, 8);
|
||
end;
|
||
|
||
procedure TfMain.SendModbus(DeviceID, cmd: Byte; Address, Value: Word);
|
||
var snddata: string;
|
||
begin
|
||
if not ComPort1.Connected then exit;
|
||
snddata := format('%d,%d,%d,%d', [DeviceID, cmd, Address, Value]);
|
||
wrtList.Add(snddata);
|
||
end;
|
||
|
||
procedure TfMain.SendModbusRTUCoilCommand(DeviceID: Byte; CoilAddress: Word; CoilOn: Boolean);
|
||
var Frame: array[0..7] of Byte; crc, CoilValue: Word;
|
||
begin
|
||
if CoilOn then CoilValue := $FF00 else CoilValue := $0000;
|
||
Frame[0] := DeviceID; Frame[1] := cmdFSC;
|
||
Frame[2] := Hi(CoilAddress); Frame[3] := Lo(CoilAddress);
|
||
Frame[4] := Hi(CoilValue); Frame[5] := Lo(CoilValue);
|
||
crc := CalculateCRC(Frame, 6);
|
||
Frame[6] := Lo(crc); Frame[7] := Hi(crc);
|
||
if ComPort1.Connected then begin
|
||
ComPort1.Write(Frame, SizeOf(Frame));
|
||
printlog('[Coil Write]' + format('[%d]=[%d]', [CoilAddress, CoilValue]));
|
||
end;
|
||
end;
|
||
|
||
procedure TfMain.SendModbusRTUCommand(DeviceID, FunctionCode: Byte; RegisterAddress, Value: Word);
|
||
var Frame: array[0..7] of Byte; crc: Word;
|
||
begin
|
||
Frame[0] := DeviceID; Frame[1] := FunctionCode;
|
||
Frame[2] := Hi(RegisterAddress); Frame[3] := Lo(RegisterAddress);
|
||
Frame[4] := Hi(Value); Frame[5] := Lo(Value);
|
||
crc := CalculateCRC(Frame, 6);
|
||
Frame[6] := Lo(crc); Frame[7] := Hi(crc);
|
||
if ComPort1.Connected then begin
|
||
ComPort1.Write(Frame, SizeOf(Frame));
|
||
printlog('[Register Write]' + format('[%d]=[%d]', [RegisterAddress, Value]));
|
||
end;
|
||
end;
|
||
|
||
procedure TfMain.printlog(_msg: string);
|
||
var S: string;
|
||
begin
|
||
if Memo1.Lines.Count > 1000 then Memo1.Clear;
|
||
S := formatdatetime('dd hh:nn:ss', now()) + '->' + _msg;
|
||
Memo1.Lines.Add(S);
|
||
end;
|
||
|
||
procedure TfMain.ReadTimerTimer(Sender: TObject);
|
||
var
|
||
wrtCmd, devID: Byte;
|
||
wrtAddress, wrtValue: word;
|
||
S: string;
|
||
begin
|
||
if (ComPort1.Connected) then SharedMem.SetB(comm_shm_addr, commOK)
|
||
else SharedMem.SetB(comm_shm_addr, commDisconnect);
|
||
|
||
if (ComPort1.Connected) and (req_flag) then
|
||
begin
|
||
req_flag := false;
|
||
packet_count := 0;
|
||
FillChar(dPacket[1], Sizeof(dPacket), 0);
|
||
|
||
if wrtList.Count > 0 then
|
||
begin
|
||
read_No := 2;
|
||
S := wrtList[0];
|
||
wrtList.Delete(0);
|
||
devID := StrToIntDef(copy(S, 1, Pos(',', S) - 1), $01);
|
||
delete(S, 1, Pos(',', S));
|
||
wrtCmd := StrToIntDef(copy(S, 1, Pos(',', S) - 1), $06);
|
||
delete(S, 1, Pos(',', S));
|
||
wrtAddress := StrToIntDef(copy(S, 1, Pos(',', S) - 1), $0000);
|
||
delete(S, 1, Pos(',', S));
|
||
wrtValue := StrToIntDef(S, 0);
|
||
|
||
case wrtCmd of
|
||
cmdFSC:
|
||
begin
|
||
if wrtValue = $01 then SendModbusRTUCoilCommand(devID, wrtAddress, True)
|
||
else SendModbusRTUCoilCommand(devID, wrtAddress, False);
|
||
StatusBar1.Panels.Items[1].Text := 'Write Coil';
|
||
end;
|
||
cmdPSR:
|
||
begin
|
||
SendModbusRTUCommand(devID, wrtCmd, wrtAddress, wrtValue);
|
||
StatusBar1.Panels.Items[1].Text := 'Write Reg';
|
||
end;
|
||
end;
|
||
end
|
||
else
|
||
begin
|
||
case read_No of
|
||
0:
|
||
begin
|
||
SendModbusRequest(slvID, cmdRHR, $0000, $0A);
|
||
StatusBar1.Panels.Items[1].Text := 'Read Reg';
|
||
end;
|
||
1:
|
||
begin
|
||
SendModbusRequest(slvID, cmdRCR, $0000, $38);
|
||
StatusBar1.Panels.Items[1].Text := 'Read Coil';
|
||
end;
|
||
end;
|
||
end;
|
||
end
|
||
else
|
||
begin
|
||
Inc(timeout_count, 1);
|
||
if timeout_count > 30 then
|
||
begin
|
||
packet_count := 0;
|
||
read_No := 0;
|
||
timeout_count := 0;
|
||
FillChar(dPacket[1], Sizeof(dPacket), 0);
|
||
req_flag := true;
|
||
if Comport1.Connected then SharedMem.SetB(comm_shm_addr, commError);
|
||
printlog('Time Out ---------------');
|
||
end;
|
||
StatusBar1.Panels.Items[2].Text := inttostr(timeout_count);
|
||
end;
|
||
end;
|
||
|
||
procedure TfMain.MQTT_TimerTimer(Sender: TObject);
|
||
begin
|
||
SendMQTTData;
|
||
end;
|
||
|
||
procedure TfMain.SendMQTTData;
|
||
var
|
||
JsonObj: TJSONObject;
|
||
DI, DOs, PS: string;
|
||
p: integer;
|
||
curr_pulse, pulse_diff: Integer;
|
||
Payload: string;
|
||
Buffer: TIdBytes;
|
||
TopicBytes, PayloadBytes: TIdBytes;
|
||
RemLen: Integer;
|
||
begin
|
||
// if not Assigned(MQTT_Client) then Exit;
|
||
if not Assigned(FMQTTClient) then Exit;
|
||
DI := '';
|
||
for p := 1 to 8 do DI := DI + IntToStr(StrToIntDef(grid_di01.Cells[1, p], 0)) + '|';
|
||
for p := 1 to 8 do DI := DI + IntToStr(StrToIntDef(grid_di02.Cells[1, p], 0)) + '|';
|
||
if Length(DI) > 0 then Delete(DI, Length(DI), 1);
|
||
DOs := '';
|
||
for p := 1 to 8 do DOs := DOs + IntToStr(StrToIntDef(grid_do02.Cells[1, p], 0)) + '|';
|
||
for p := 1 to 8 do DOs := DOs + IntToStr(StrToIntDef(grid_do01.Cells[1, p], 0)) + '|';
|
||
if Length(DOs) > 0 then Delete(DOs, Length(DOs), 1);
|
||
PS := '';
|
||
for p := 1 to 4 do begin
|
||
curr_pulse := StrToIntDef(grid_pulse.Cells[1, p], 0);
|
||
if p <= 2 then begin
|
||
if prev_pulse[p] = -1 then pulse_diff := 0
|
||
else begin
|
||
pulse_diff := curr_pulse - prev_pulse[p];
|
||
if pulse_diff < 0 then pulse_diff := curr_pulse;
|
||
end;
|
||
PS := PS + IntToStr(pulse_diff) + '|';
|
||
end
|
||
else begin
|
||
PS := PS + IntToStr(curr_pulse) + '|';
|
||
end;
|
||
prev_pulse[p] := curr_pulse;
|
||
end;
|
||
if Length(PS) > 0 then Delete(PS, Length(PS), 1);
|
||
JsonObj := TJSONObject.Create;
|
||
try
|
||
JsonObj.AddPair('DI', DI); JsonObj.AddPair('DO', DOs); JsonObj.AddPair('AI', '0|0|0|0');
|
||
JsonObj.AddPair('HM', '0|0|0|0|0'); JsonObj.AddPair('TM', '0|0|0|0|0');
|
||
JsonObj.AddPair('CD', '0|0|0|0|0'); JsonObj.AddPair('NT', '0|0|0|0|0');
|
||
JsonObj.AddPair('PH', '0|0|0|0|0'); JsonObj.AddPair('GT', '0|0|0|0|0');
|
||
JsonObj.AddPair('GM', '0|0|0|0|0'); JsonObj.AddPair('GNT', '0|0|0|0|0');
|
||
JsonObj.AddPair('WP', '0|0|0|0|0'); JsonObj.AddPair('GT2', '0|0|0|0|0');
|
||
JsonObj.AddPair('PS', PS);
|
||
Payload := JsonObj.ToString;
|
||
finally JsonObj.Free; end;
|
||
try
|
||
// if not MQTT_Client.Connected then begin
|
||
if not FMQTTClient.Connected then begin
|
||
FMQTTClient.Host := mqtt_ip;
|
||
FMQTTClient.Port := mqtt_port;
|
||
FMQTTClient.Connect;
|
||
{
|
||
SetLength(Buffer, 0); AppendByte(Buffer, $10);
|
||
AppendByte(Buffer, 12 + 4); AppendByte(Buffer, 0); AppendByte(Buffer, 4);
|
||
AppendBytes(Buffer, ToBytes('MQTT', enUTF8)); AppendByte(Buffer, 4);
|
||
AppendByte(Buffer, 2); AppendByte(Buffer, 0); AppendByte(Buffer, 60);
|
||
AppendByte(Buffer, 0); AppendByte(Buffer, 4); AppendBytes(Buffer, ToBytes('PLC1', enUTF8));
|
||
FMQTTClient.IOHandler.Write(Buffer); Sleep(100);
|
||
}
|
||
end;
|
||
if FMQTTClient.Connected then begin
|
||
{
|
||
TopicBytes := ToBytes(mqtt_topic, enUTF8); PayloadBytes := ToBytes(Payload, enUTF8);
|
||
RemLen := 2 + Length(TopicBytes) + Length(PayloadBytes);
|
||
SetLength(Buffer, 0); AppendByte(Buffer, $30);
|
||
if (RemLen < 128) then AppendByte(Buffer, Byte(RemLen))
|
||
else begin
|
||
AppendByte(Buffer, Byte((RemLen mod 128) or $80));
|
||
AppendByte(Buffer, Byte(RemLen div 128));
|
||
end;
|
||
AppendByte(Buffer, Hi(Word(Length(TopicBytes)))); AppendByte(Buffer, Lo(Word(Length(TopicBytes))));
|
||
AppendBytes(Buffer, TopicBytes); AppendBytes(Buffer, PayloadBytes);
|
||
MQTT_Client.IOHandler.Write(Buffer);
|
||
}
|
||
|
||
FMQTTClient.Publish(mqtt_topicH+'PUB'+mqtt_topicT+mqtt_GtwyID, Payload);
|
||
|
||
Edit1.Text := Payload;
|
||
end;
|
||
except on E: Exception do printlog('MQTT Error: ' + E.Message); end;
|
||
end;
|
||
|
||
end.
|
||
|