unit U_LSPLCAgent; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, System.Win.ScktComp, Vcl.ExtCtrls, cxGraphics, cxControls, cxLookAndFeels, cxLookAndFeelPainters, cxStyles, cxCustomData, cxFilter, cxData, cxDataStorage, cxEdit, cxNavigator, dxDateRanges, dxScrollbarAnnotations, cxGridCustomTableView, cxGridTableView, cxGridLevel, cxClasses, cxGridCustomView, cxGrid, Data.DB, Datasnap.DBClient, math, IniFiles, 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.StrUtils; type TF_LSPLCAgent = class(TForm) GroupBox1: TGroupBox; Label14: TLabel; Edit5: TEdit; Label15: TLabel; Edit6: TEdit; Button11: TButton; ClientSocket1: TClientSocket; Button1: TButton; ListBox1: TListBox; ListBox2: TListBox; Splitter1: TSplitter; GridAddressList: TcxGrid; GridAddressListTV: TcxGridTableView; GridAddressListTVColumn1: TcxGridColumn; GridAddressListTVColumn2: TcxGridColumn; GridAddressListTVColumn3: TcxGridColumn; GridAddressListTVColumn4: TcxGridColumn; GridAddressListTVColumn5: TcxGridColumn; GridAddressListLv: TcxGridLevel; Label1: TLabel; Edit1: TEdit; Panel1: TPanel; Memo1: TMemo; Splitter2: TSplitter; Memo2: TMemo; Button2: TButton; Button3: TButton; Button4: TButton; Button5: TButton; Tmr_PLCRead: TTimer; Label2: TLabel; Edit2: TEdit; Button6: TButton; Button7: TButton; TrayIcon1: TTrayIcon; tmrExit: TTimer; CheckBox1: TCheckBox; FDConnection1: TFDConnection; FDQuery1: TFDQuery; FDPhysPgDriverLink1: TFDPhysPgDriverLink; FDGUIxWaitCursor1: TFDGUIxWaitCursor; RadioButton1: TRadioButton; RadioButton2: TRadioButton; Tmr_DBInsert: TTimer; FDQuery2: TFDQuery; Panel2: TPanel; Label17: TLabel; Label16: TLabel; ComboBox5: TComboBox; Button13: TButton; Edit10: TEdit; Label3: TLabel; cb_logview: TCheckBox; procedure Button11Click(Sender: TObject); procedure ClientSocket1Connecting(Sender: TObject; Socket: TCustomWinSocket); procedure ClientSocket1Disconnect(Sender: TObject; Socket: TCustomWinSocket); procedure ClientSocket1Error(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer); procedure ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket); procedure Button13Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); procedure ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); procedure Button4Click(Sender: TObject); procedure Button5Click(Sender: TObject); procedure Button6Click(Sender: TObject); procedure Button7Click(Sender: TObject); procedure Tmr_PLCReadTimer(Sender: TObject); procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); procedure TrayIcon1Click(Sender: TObject); procedure tmrExitTimer(Sender: TObject); procedure CheckBox1Click(Sender: TObject); procedure Tmr_DBInsertTimer(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); private { Private declarations } inifilename : string; ExitOk : boolean; close_ck : Boolean; TxBuf, RxBuf : array [0..1024-1] of byte; procedure BuildReadPacket(str:string); procedure BuildReadPacketMulti(const AddrList: TStrings); function PLCDataLoad: boolean; function PLCDataLoadMulti: boolean; procedure AppEvtMinimize(Sender: TObject); procedure ParseMultiReadResponse(var RxBuf: array of Byte; AddrCount: Integer; out Values: TArray); procedure insertSQL(sql: string); function DB_MainTableCreate: Boolean; function DB_MainTableDescriptCreate: Boolean; function DB_LogTableCreate: Boolean; procedure Memo1_ADD(msg: string; ck:Integer = 0); procedure Memo2_ADD(msg: string); procedure PLC_connect; public { Public declarations } dbIP_PG, dbUser_PG, dbPW_PG, dbSchema_PG : string; dbPort_PG : integer; function DB_Connect:Boolean; function DB_Close:Boolean; procedure DB_Insert; end; var F_LSPLCAgent: TF_LSPLCAgent; LoadCK : Boolean; PLCconnectCK : Integer; const PLC_NO = 1; // ¼Ò°á·Î 1È£ implementation uses WinSock; {$R *.dfm} procedure TF_LSPLCAgent.Memo1_ADD(msg:string; ck:Integer = 0); begin while Memo1.Lines.Count > 50 do begin Memo1.Lines.Delete(0); end; Memo1.Lines.Add(msg); if ck = 1 then begin // ·Î±× ÀúÀå end; end; procedure TF_LSPLCAgent.Memo2_ADD(msg:string); begin while Memo2.Lines.Count > 50 do begin Memo2.Lines.Delete(0); end; Memo2.Lines.ADD(msg); end; procedure TF_LSPLCAgent.BuildReadPacket(str:string); var i, varcnt, sum: Integer; addrStr: string; begin FillChar(TxBuf, SizeOf(TxBuf), 0); // Çì´õ ±¸¼º TxBuf[0] := $4C; // L TxBuf[1] := $53; // S TxBuf[2] := $49; // I TxBuf[3] := $53; // S TxBuf[4] := $2d; // - TxBuf[5] := $58; // X TxBuf[6] := $47; // G TxBuf[7] := $54; // T TxBuf[8] := $00; // NULL TxBuf[9] := $00; // NULL TxBuf[10] := $00; // Ŭ¶óÀÌ¾ðÆ® -> ¼­¹ö (0x00) TxBuf[11] := $00; TxBuf[12] := $A0; // XGK 0xA0 , XGB(MK) 0xB0 , XGI 0xA4 , XGB(IEC) 0x11 , XGR 0xA8 TxBuf[13] := $33; // Ŭ¶óÀ̾ðÆ®(HMI) -> ¼­¹ö(PLC) (0x33) / ¼­¹ö(PLC) -> Ŭ¶óÀ̾ðÆ®(HMI) (0x11) TxBuf[14] := $00; // ÇÁ·¹ÀÓ°£ÀÇ ¼ø¼­¸¦ ±¸º°Çϱâ À§ÇÑ ID TxBuf[15] := $00; TxBuf[16] := 16; // Length (¸í·É¿µ¿ª 16¹ÙÀÌÆ®) - ¸í·É¾î ±¸Á¶ÀÇ ¹ÙÀÌÆ® Å©±â TxBuf[17] := $00; // ÀÌ´õ³ÝÀ§Ä¡ TxBuf[18] := $00; // ¿¹¾à ¿µ¿ª (0x00) // CheckSum °è»ê sum := 0; for i := 0 to 18 do begin sum := sum + TxBuf[i]; if sum > 255 then sum := sum - 256; end; TxBuf[19] := sum; // ¸í·É ¿µ¿ª TxBuf[20] := $54; // READ TxBuf[21] := 0; // ¿¹¾à ¿µ¿ª ¶Ç´Â º¸Á¶ ¸í·É TxBuf[22] := $02; // DataType = WORD °³º° / $0E - ¿¬¼Ó TxBuf[23] := 0; // $30; DataType (À̾îÁü) TxBuf[24] := 0; // $30; LinkID (2) TxBuf[25] := 0; // $30; (À̾îÁü) TxBuf[26] := $01; // Variable count TxBuf[27] := 0; // $30; (À̾îÁü) addrStr := Trim(str); varcnt := Length(addrStr); TxBuf[28] := varcnt; TxBuf[29] := 0; for i := 1 to varcnt do TxBuf[29 + i] := Ord(addrStr[i]); end; procedure TF_LSPLCAgent.BuildReadPacketMulti(const AddrList: TStrings); var i, j, sum, pos: Integer; addrStr: string; addrLen: Integer; totalLen: Integer; begin FillChar(TxBuf, SizeOf(TxBuf), 0); // 1. Header ('LSIS-XGT') TxBuf[0] := $4C; // L TxBuf[1] := $53; // S TxBuf[2] := $49; // I TxBuf[3] := $53; // S TxBuf[4] := $2d; // - TxBuf[5] := $58; // X TxBuf[6] := $47; // G TxBuf[7] := $54; // T TxBuf[8] := $00; // NULL TxBuf[9] := $00; // NULL // 2. Fixed header data TxBuf[10] := $00; // Ŭ¶óÀÌ¾ðÆ® -> ¼­¹ö (0x00) TxBuf[11] := $00; TxBuf[12] := $A0; // XGK 0xA0 , XGB(MK) 0xB0 , XGI 0xA4 , XGB(IEC) 0x11 , XGR 0xA8 TxBuf[13] := $33; // Ŭ¶óÀ̾ðÆ®(HMI) -> ¼­¹ö(PLC) (0x33) / ¼­¹ö(PLC) -> Ŭ¶óÀ̾ðÆ®(HMI) (0x11) TxBuf[14] := $00; // ÇÁ·¹ÀÓ°£ÀÇ ¼ø¼­¸¦ ±¸º°Çϱâ À§ÇÑ ID TxBuf[15] := $00; // ÀÏ´Ü °íÁ¤À¸·Î ³Ö°í ³ªÁß¿¡ ¼öÁ¤ÇÒ ¿µ¿ª ±æÀÌ TxBuf[16] := $00; // Length (¸í·É¿µ¿ª 16¹ÙÀÌÆ®) - ¸í·É¾î ±¸Á¶ÀÇ ¹ÙÀÌÆ® Å©±â TxBuf[17] := $00; // ÀÌ´õ³ÝÀ§Ä¡ TxBuf[18] := $00; // ¿¹¾à ¿µ¿ª (0x00) ///////////////////////// 3. Checksum °è»êÀº ³ªÁß¿¡ ó¸®ÇÒ ¿¹Á¤ ¡æ À§Ä¡´Â [19] // 4. ¸í·É ¿µ¿ª TxBuf[20] := $54; // READ TxBuf[21] := 0; // ¿¹¾à ¿µ¿ª ¶Ç´Â º¸Á¶ ¸í·É TxBuf[22] := $02; // DataType = WORD °³º° / $0E - ¿¬¼Ó TxBuf[23] := 0; // $30; DataType (À̾îÁü) TxBuf[24] := 0; // $30; LinkID (2) TxBuf[25] := 0; // $30; (À̾îÁü) TxBuf[26] := AddrList.Count; // Variable Count TxBuf[27] := $00; // $30; (À̾îÁü) pos := 28; // ¸í·É ÀÌÈÄ À§Ä¡ for i := 0 to AddrList.Count - 1 do begin addrStr := Trim(AddrList[i]); // ¿¹: %MW301 addrLen := Length(addrStr); TxBuf[pos] := addrLen; // ¹®ÀÚ¿­ ±æÀÌ TxBuf[pos + 1] := 0; // Reserved for j := 1 to addrLen do TxBuf[pos + 1 + j] := Ord(addrStr[j]); Inc(pos, addrLen + 2); // ´ÙÀ½ À§Ä¡ À̵¿ end; // 5. Àüü Length °è»ê totalLen := pos - 20; // ¸í·É ¿µ¿ªºÎÅÍ ³¡±îÁö TxBuf[16] := totalLen; TxBuf[17] := 0; // 6. Checksum °è»ê (0~18±îÁö ÇÕ»ê) sum := 0; for i := 0 to 18 do begin sum := sum + TxBuf[i]; if sum > 255 then sum := sum - 256; end; TxBuf[19] := sum; // °á°úÀûÀ¸·Î TxBuf ÀÇ Å©±â´Â pos ¹ÙÀÌÆ®±îÁö À¯È¿ÇÔ end; procedure TF_LSPLCAgent.Button11Click(Sender: TObject); begin { TThread.Synchronize(nil, procedure begin try LoadCK := False; if Button11.Caption = 'Connect' then begin ClientSocket1.Host := Edit5.Text; ClientSocket1.Port := StrToIntDef(Edit6.Text,2004); ClientSocket1.Active := True; LoadCK := True; end else begin ClientSocket1.Active := False; end; except on E: Exception do begin Memo1_ADD('PLC Connect Except : ' + E.Message); end; end; end ); } { 20251013 if PLCconnectCK = 0 then begin // ÃʱⰪ : 0 - ¹ÌÁ¢¼Ó »óÅ PLCconnectCK := 1; // Á¢¼ÓÁß try LoadCK := False; if Button11.Caption = 'Connect' then begin ClientSocket1.Host := Edit5.Text; ClientSocket1.Port := StrToIntDef(Edit6.Text,2004); ClientSocket1.Active := True; LoadCK := True; end else begin ClientSocket1.Active := False; end; except on E: Exception do begin Memo1_ADD('Except : ' + E.Message); PLCconnectCK := 0; // ÃʱⰪ : 0 - ¹ÌÁ¢¼Ó »óÅ end; end; end; if PLCconnectCK = 1 then begin if Pos('Á¢¼Ó ½Ãµµ',Memo1.Lines[Memo1.Lines.Count-1]) > 0 then begin Memo1.Lines[Memo1.Lines.Count-1] := Memo1.Lines[Memo1.Lines.Count-1]+'.'; end; end; } if Button11.Caption = 'Connect' then begin PLC_connect; end else begin ClientSocket1.Active := False; end; end; procedure TF_LSPLCAgent.PLC_connect; begin try if PLCconnectCK = 0 then begin // ÃʱⰪ : 0 - ¹ÌÁ¢¼Ó »óÅ PLCconnectCK := 1; // Á¢¼ÓÁß try LoadCK := False; if Button11.Caption = 'Connect' then begin ClientSocket1.Host := Edit5.Text; ClientSocket1.Port := StrToIntDef(Edit6.Text,2004); ClientSocket1.Active := True; LoadCK := True; end else begin ClientSocket1.Active := False; end; except on E: Exception do begin Memo1_ADD('Except : ' + E.Message); PLCconnectCK := 0; // ÃʱⰪ : 0 - ¹ÌÁ¢¼Ó »óÅ end; end; end; if PLCconnectCK = 1 then begin if Pos('Á¢¼Ó ½Ãµµ',Memo1.Lines[Memo1.Lines.Count-1]) > 0 then begin Memo1.Lines[Memo1.Lines.Count-1] := Memo1.Lines[Memo1.Lines.Count-1]+'.'; end; end; except on E: Exception do begin Memo1_ADD('PLC_connect Error : ' + E.Message); end; end; end; procedure TF_LSPLCAgent.Button13Click(Sender: TObject); var Sent, Recv: Integer; begin if not ClientSocket1.Active then PLC_connect; if ClientSocket1.Active then begin try BuildReadPacket(ComboBox5.Text+Edit10.Text); Sent := ClientSocket1.Socket.SendBuf(TxBuf, 36); if Sent <> 36 then begin Memo1_ADD('ÆÐŶ Àü¼Û ½ÇÆÐ : [ '+ComboBox5.Text+Edit10.Text+' ]'); Exit; end; Recv := ClientSocket1.Socket.ReceiveBuf(RxBuf, 34); if Recv < 34 then begin Memo1_ADD('ÀÀ´ä ¼ö½Å ½ÇÆÐ : [ '+ComboBox5.Text+Edit10.Text+' ]'); Exit; end; if cb_logview.Checked then begin // 1. Memo1: ¿øº» HEX var hexStr := ''; for var i := 0 to Recv - 1 do hexStr := hexStr + IntToHex(RxBuf[i], 2) + ' '; Memo2_ADD(hexStr); // 2. Memo2: ÇØ¼®µÈ °á°ú var value := RxBuf[32] + (RxBuf[33] shl 8); Memo2_ADD('DEC: ' + IntToStr(value)); // StringGrid1.Cells[1,1] := Trim(Edit2.Text) + '=' + IntToStr(value); Memo2_ADD('HEX: ' + IntToHex(value, 4)); end; except on E: ESocketError do begin Memo1_ADD('Ŭ¶óÀÌ¾ðÆ® ¼ÒÄÏ ¿¡·¯: ' + E.Message); // ¿¡·¯ ¹ß»ý ½Ã ¿¬°áÀ» Á¾·á ClientSocket1.Active := False; end; on E: Exception do begin Memo1_ADD('¿¹»óÄ¡ ¸øÇÑ ¿¡·¯: ' + E.Message); ClientSocket1.Active := False; end; end; end; end; function TF_LSPLCAgent.PLCDataLoad:boolean; var i : integer; Sent, Recv: Integer; LastError: Integer; ErrorMsg: string; begin if PLCconnectCK <> 2 then exit; Button1.Enabled := False; try try GridAddressListTV.DataController.BeginUpdate; for i := 0 to GridAddressListTV.DataController.RecordCount - 1 do begin with GridAddressListTV.DataController do begin Values[i, GridAddressListTVColumn5.Index] := ''; if ClientSocket1.Active then begin BuildReadPacket(Values[i, GridAddressListTVColumn2.Index]); Sent := ClientSocket1.Socket.SendBuf(TxBuf, 36); if Sent <> 36 then begin if cb_logview.Checked then Memo1_ADD('ÆÐŶ Àü¼Û ½ÇÆÐ PLCDataLoad: ['+IntToStr(Sent)+']-[ '+Values[i, GridAddressListTVColumn2.Index]+' ]'); LoadCK := False; // Exit; end else begin Sleep(StrToIntDef(Edit1.Text,0)); Recv := ClientSocket1.Socket.ReceiveBuf(RxBuf, 34); if Recv < 34 then begin if cb_logview.Checked then begin Memo1_ADD('ÀÀ´ä ¼ö½Å ½ÇÆÐ PLCDataLoad: [Sent:'+IntToStr(Sent)+']-[Recv:'+IntToStr(Recv)+']-[ '+Values[i, GridAddressListTVColumn2.Index]+' ]'); if Recv = -1 then begin // -1: ¼ÒÄÏ ¿À·ù ¹ß»ý (WSAGetLastError È£Ãâ) LastError := WSAGetLastError; // ¿À·ù Äڵ忡 µû¸¥ ¸Þ½ÃÁö ¼³Á¤ (¿¹½Ã, Çʿ信 µû¶ó È®Àå °¡´É) case LastError of 10054: ErrorMsg := 'Connection reset by peer (PLC¿¡¼­ ¿¬°á °­Á¦ Á¾·á)'; 10060: ErrorMsg := 'Connection timed out (¼ö½Å ŸÀӾƿô)'; 10004: ErrorMsg := 'Interrupted function call (ºí·ÎÅ· µ¿ÀÛ Áß Ãë¼Ò)'; // ±× ¿Ü Äڵ忡 ´ëÇÑ ¼³¸íÀº Winsock ¹®¼­ ÂüÁ¶ else ErrorMsg := 'Unknown socket error'; end; if cb_logview.Checked then Memo1_ADD('¼ÒÄÏ ¿À·ù Recv: ['+IntToStr(Recv)+'] - WSAGetLastError: ['+IntToStr(LastError)+'] - Message: ['+ErrorMsg+']'); // LoadCK := False; // ¿À·ù ¹ß»ý ½Ã µ¥ÀÌÅÍ ·Îµå ½ÇÆÐ ó¸® end else if Recv = 0 then begin // 0: Á¤»óÀûÀÎ ¿¬°á Á¾·á (PLC Ãø¿¡¼­ ¼ÒÄÏÀ» ´Ý¾ÒÀ½) if cb_logview.Checked then Memo1_ADD('°æ°í PLC¿¡¼­ ¿¬°áÀ» Á¤»ó Á¾·áÇÔ (Recv: 0) - ÁÖ¼Ò: ['+Values[i, GridAddressListTVColumn2.Index]+']'); end else begin // 0 < Recv < 34: ÆÐŶÀ» ÀϺθ¸ ¼ö½ÅÇ߰ųª, PLC¿¡¼­ ¿À·ù ÀÀ´ä ÆÐŶ(ÀÛÀº Å©±â)À» º¸³ÂÀ» °¡´É¼º if cb_logview.Checked then Memo1_ADD('ÀÀ´ä ¼ö½Å ºÒ¿ÏÀü (Recv < 34) PLCDataLoad: [Sent:'+IntToStr(Sent)+']-[Recv:'+IntToStr(Recv)+']-[ '+Values[i, GridAddressListTVColumn2.Index]+' ]'); // ÀÌ °æ¿ì, ¼ö½ÅµÈ RxBuf[0] ~ RxBuf[Recv-1]±îÁö¸¦ ºÐ¼®ÇÏ¿© // LSIS XGT ÇÁ·ÎÅäÄÝ ¿À·ù Äڵ带 È®ÀÎÇÏ´Â ·ÎÁ÷ÀÌ ÇÊ¿äÇÕ´Ï´Ù. end; // LoadCK := False; // Exit; // Çʿ信 µû¶ó Åë½ÅÀ» Áß´ÜÇÏ°í ³ª°¥ ¼ö ÀÖ½À´Ï´Ù. end; //LoadCK := False; // Exit; end else begin if cb_logview.Checked then begin // 1. Memo1: ¿øº» HEX var hexStr := ''; for var x := 0 to Recv - 1 do hexStr := hexStr + IntToHex(RxBuf[x], 2) + ' '; Memo2_ADD(hexStr); end; // 2. Memo2: ÇØ¼®µÈ °á°ú var value := RxBuf[32] + (RxBuf[33] shl 8); if cb_logview.Checked then begin Memo2_ADD('DEC: ' + IntToStr(value)); // StringGrid1.Cells[1,1] := Trim(Edit2.Text) + '=' + IntToStr(value); Memo2_ADD('HEX: ' + IntToHex(value, 4)); end; Values[i, GridAddressListTVColumn3.Index] := IntToStr(value); Values[i, GridAddressListTVColumn4.Index] := IntToHex(value, 4); Values[i, GridAddressListTVColumn5.Index] := FormatDateTime('hhnnss.zzz',Now); LoadCK := True; end; end; end; end; // Sleep(StrToIntDef(Edit1.Text,0)); // Refresh; end; except on E: Exception do begin Memo1_ADD('PLCDataLoad Error : ' + E.Message); LoadCK := False; end; end; finally Result := True; GridAddressListTV.DataController.EndUpdate; Button1.Enabled := True; Refresh; end; end; function TF_LSPLCAgent.PLCDataLoadMulti:boolean; var t, i, j, t_count : integer; Sent, Recv: Integer; addrList: TStringList; addr_Values: TArray; Addr_Count: Integer; begin Button1.Enabled := False; addrList := TStringList.Create; try try GridAddressListTV.DataController.BeginUpdate; t_count := GridAddressListTV.DataController.RecordCount; if (t_count mod 10) > 0 then begin t := (t_count div 10) +1; end else begin if t_count < 10 then t := 1 else t := (t_count div 10); end; for i := 0 to t - 1 do begin Addr_Count := 0; with GridAddressListTV.DataController do begin addrList.Clear; for j := 0 to 9 do begin if ((i*10) + j) <= t_count then begin // Values[(i*10) + j, GridAddressListTVColumn5.Index] := ''; addrList.Add(Values[(i*10) + j, GridAddressListTVColumn2.Index]); end else begin Break; end; end; if ClientSocket1.Active then begin BuildReadPacketMulti(addrList); Sent := ClientSocket1.Socket.SendBuf(TxBuf, 36); if Sent <> 36 then begin Memo1_ADD('ÆÐŶ Àü¼Û ½ÇÆÐ : [ '+Values[i, GridAddressListTVColumn2.Index]+' ]'); // Exit; end else begin Recv := ClientSocket1.Socket.ReceiveBuf(RxBuf, 34); if Recv < 34 then begin Memo1_ADD('ÀÀ´ä ¼ö½Å ½ÇÆÐ : [ '+Values[i, GridAddressListTVColumn2.Index]+' ]'); // Exit; end else begin ParseMultiReadResponse(RxBuf, addrList.Count, addr_Values); // addr_Values ¹è¿­¿¡ °¢°¢ÀÇ ¿öµå °ªÀÌ ÀúÀåµÊ // ShowMessage('ù¹øÂ° ÁÖ¼Ò°ª: ' + IntToStr(Values[0])); for j := 0 to 9 do begin if ((i*10) + j) <= t_count then begin Values[(i*10) + j, GridAddressListTVColumn3.Index] := addr_Values[j].ToString; Values[(i*10) + j, GridAddressListTVColumn4.Index] := addr_Values[j].ToHexString; Values[(i*10) + j, GridAddressListTVColumn5.Index] := FormatDateTime('hhnnss.zzz',Now); end else begin Break; end; end; end; end; end; end; Sleep(StrToIntDef(Edit1.Text,0)); Refresh; end; except on E: Exception do begin Memo1_ADD('PLCDataLoadMulti Error : ' + E.Message); end; end; finally Result := True; GridAddressListTV.DataController.EndUpdate; Button1.Enabled := True; addrList.Free; end; end; procedure TF_LSPLCAgent.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 Memo2_ADD('[insertSQL()]Database Connect Error :: '+e.Message); end; end; end; procedure TF_LSPLCAgent.ParseMultiReadResponse(var RxBuf: array of Byte; AddrCount: Integer; out Values: TArray); var i, pos: Integer; begin SetLength(Values, AddrCount); // ±âº» Çì´õ 20¹ÙÀÌÆ®´Â °Ç³Ê¶Ü, ¸í·É ¿µ¿ª ½ÃÀÛ À§Ä¡ pos := 20; // ¸í·ÉÄÚµå üũ if RxBuf[pos] <> $54 then raise Exception.Create('Invalid response command code.'); pos := pos + 7; // pos°¡ 27ÀÌ µÊ (¿äû ½Ã Variable count À§Ä¡) // Variable count È®ÀÎ (°ËÁõ °¡´É) if RxBuf[pos] <> AddrCount then raise Exception.Create('Response variable count mismatch.'); pos := pos + 2; // º¯¼ö µ¥ÀÌÅÍ ½ÃÀÛ À§Ä¡ // µ¥ÀÌÅÍ Àбâ (WORD ŸÀÔ, 2¹ÙÀÌÆ®¾¿) for i := 0 to AddrCount - 1 do begin if pos + 1 > High(RxBuf) then raise Exception.Create('Response packet too short.'); Values[i] := (RxBuf[pos] shl 8) or RxBuf[pos + 1]; Inc(pos, 2); end; end; procedure TF_LSPLCAgent.Tmr_DBInsertTimer(Sender: TObject); begin Tmr_DBInsert.Enabled := False; if LoadCK and (not close_ck) then begin DB_Insert; Tmr_DBInsert.Enabled := True; end; end; procedure TF_LSPLCAgent.Tmr_PLCReadTimer(Sender: TObject); begin Tmr_PLCRead.Enabled := False; if not close_ck then begin if RadioButton1.Checked then Button1.Click else Button5.Click; Tmr_PLCRead.Enabled := True; end; end; procedure TF_LSPLCAgent.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 TF_LSPLCAgent.TrayIcon1Click(Sender: TObject); begin TrayIcon1.Visible := False; Show; WindowState := wsNormal; Application.BringToFront; end; procedure TF_LSPLCAgent.Button1Click(Sender: TObject); begin if (not ClientSocket1.Active) or (PLCconnectCK <> 2) then begin PLC_connect; end else begin // if PLCconnectCK = 2 then begin if PLCDataLoad then begin // LoadCK := True; Memo1_ADD('['+FormatDateTime('yymmdd hhnnss.zzz',now)+'] PLCDataLoad - ok'); end else begin LoadCK := False; end; // end; end; end; procedure TF_LSPLCAgent.Button2Click(Sender: TObject); var i : integer; Ini: TIniFile; begin // ini ÆÄÀÏ °»½Å.. Ini := TIniFile.Create(inifilename); try Ini.WriteString('PLC', 'IP', Edit5.Text); Ini.WriteString('PLC', 'PORT', Edit6.Text); finally Ini.Free; end; if FileExists(StringReplace(Application.ExeName,'.exe','_list1.ini',[rfReplaceAll])) then RenameFile(StringReplace(Application.ExeName,'.exe','_list1.ini',[rfReplaceAll]), StringReplace(Application.ExeName,'.exe','_list1_'+FormatDateTime('yymmdd_hhnnss.zzz',now)+'.ini.bak',[rfReplaceAll])); if FileExists(StringReplace(Application.ExeName,'.exe','_list2.ini',[rfReplaceAll])) then RenameFile(StringReplace(Application.ExeName,'.exe','_list2.ini',[rfReplaceAll]), StringReplace(Application.ExeName,'.exe','_list2_'+FormatDateTime('yymmdd_hhnnss.zzz',now)+'.ini.bak',[rfReplaceAll])); ListBox2.Clear; ListBox1.Clear; for i := 0 to GridAddressListTV.DataController.RecordCount - 1 do begin with GridAddressListTV.DataController do begin ListBox2.Items.Add(Values[i, GridAddressListTVColumn1.Index]); ListBox1.Items.Add(Values[i, GridAddressListTVColumn2.Index]); end; end; ListBox1.Items.SaveToFile(StringReplace(Application.ExeName,'.exe','_list1.ini',[rfReplaceAll])); ListBox2.Items.SaveToFile(StringReplace(Application.ExeName,'.exe','_list2.ini',[rfReplaceAll])); end; procedure TF_LSPLCAgent.Button3Click(Sender: TObject); var SL: TStringList; i, j: Integer; Row: string; begin SL := TStringList.Create; try for i := 0 to GridAddressListTV.DataController.RecordCount - 1 do begin Row := ''; for j := 0 to GridAddressListTV.ColumnCount - 1 do Row := Row + GridAddressListTV.DataController.DisplayTexts[i, j] + ','; SL.Add(Copy(Row, 1, Length(Row) - 1)); // ¸¶Áö¸· , Á¦°Å end; SL.SaveToFile('.\grid.dat'); finally SL.Free; end; end; procedure TF_LSPLCAgent.Button4Click(Sender: TObject); var SL, Items: TStringList; i, j: Integer; begin SL := TStringList.Create; Items := TStringList.Create; try SL.LoadFromFile('.\grid.dat'); GridAddressListTV.BeginUpdate; GridAddressListTV.DataController.RecordCount := SL.Count; for i := 0 to SL.Count - 1 do begin Items.Delimiter := ','; // ÄÞ¸¶·Î ±¸ºÐ Items.StrictDelimiter := True; // °ø¹éÀº ¹«½Ã Items.CommaText := SL[i]; for j := 0 to Min(Items.Count - 1, GridAddressListTV.ColumnCount - 1) do begin GridAddressListTV.DataController.Values[i, j] := Items[j]; end; end; GridAddressListTV.EndUpdate; finally Items.Free; SL.Free; end; end; procedure TF_LSPLCAgent.Button5Click(Sender: TObject); // Àϰý begin if not ClientSocket1.Active then PLC_connect; if PLCDataLoadMulti then Memo1_ADD('['+FormatDateTime('yymmdd hhnnss.zzz',now)+'] PLCDataLoadMulti - ok'); end; procedure TF_LSPLCAgent.Button6Click(Sender: TObject); begin if Button11.Caption = 'Connect' then begin PLC_connect; end; Button1.Enabled := False; Tmr_PLCRead.Enabled := False; Tmr_PLCRead.Interval := StrToIntDef(Edit2.Text,1000); Tmr_PLCRead.Enabled := True; Tmr_DBInsert.Enabled := False; Tmr_DBInsert.Interval := StrToIntDef(Edit2.Text,1000); Tmr_DBInsert.Enabled := True; Button6.Enabled := False; Button7.Enabled := True; end; procedure TF_LSPLCAgent.Button7Click(Sender: TObject); begin Button1.Enabled := True; Tmr_PLCRead.Enabled := False; Button6.Enabled := True; Button7.Enabled := False; end; procedure TF_LSPLCAgent.CheckBox1Click(Sender: TObject); var Ini: TIniFile; begin // ini ÆÄÀÏ °»½Å.. Ini := TIniFile.Create(inifilename); try Ini.WriteString('START', 'AUTO', BoolToStr(CheckBox1.Checked)); finally Ini.Free; end; end; procedure TF_LSPLCAgent.ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket); begin Button11.Caption := 'DisConnect'; Memo1_ADD('['+FormatDateTime('yymmdd hhnnss.zzz',now)+'] PLC ¿¬°á ¼º°ø!'); PLCconnectCK := 2; // ¿¬°áµÊ end; procedure TF_LSPLCAgent.ClientSocket1Connecting(Sender: TObject; Socket: TCustomWinSocket); begin Memo1_ADD('['+FormatDateTime('yymmdd hhnnss.zzz',now)+'] Á¢¼Ó ½Ãµµ.'); end; procedure TF_LSPLCAgent.ClientSocket1Disconnect(Sender: TObject; Socket: TCustomWinSocket); begin Memo1_ADD('['+FormatDateTime('yymmdd hhnnss.zzz',now)+'] ¿¬°á ÇØÁ¦µÊ!'); Button11.Caption := 'Connect'; PLCconnectCK := 0; // ÃʱⰪ : 0 - ¹ÌÁ¢¼Ó »óÅ LoadCK := False; end; procedure TF_LSPLCAgent.ClientSocket1Error(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer); begin Memo1_ADD('['+FormatDateTime('yymmdd hhnnss.zzz',now)+'] PLC ¿¬°á ½ÇÆÐ: ' + IntToStr(ErrorCode)); PLCconnectCK := 0; // ÃʱⰪ : 0 - ¹ÌÁ¢¼Ó »óÅ ErrorCode := 0; // ¿¹¿Ü ¹ß»ý ¸·±â LoadCK := False; end; procedure TF_LSPLCAgent.ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket); var Buf: array[0..1023] of Byte; i, Len: Integer; RawData: TBytes; BufCopy: TBytes; S: string; Recv: Integer; begin try Len := Socket.ReceiveBuf(Buf, SizeOf(Buf)); if Len > 0 then begin SetLength(RawData, Len); Move(Buf, RawData[0], Len); // 1. Memo1¿¡ Çí»ç Ç¥½Ã S := ''; for i := 0 to Len - 1 do S := S + IntToHex(RawData[i], 2) + ' '; if cb_logview.Checked then begin Memo2_ADD('>> ¼ö½Å ¿øº» (HEX)'); Memo2_ADD(S); end; end; except on E: Exception do begin Memo1_ADD('Read error: ' + E.Message); LoadCK := False; end; end; end; procedure TF_LSPLCAgent.AppEvtMinimize(Sender: TObject); begin Hide; WindowState := wsMinimized; TrayIcon1.Visible := True; TrayIcon1.Animate := True; TrayIcon1.ShowBalloonHint; end; procedure TF_LSPLCAgent.FormClose(Sender: TObject; var Action: TCloseAction); begin close_ck := true; Tmr_DBInsert.Enabled := False; Tmr_PLCRead.Enabled := False; end; procedure TF_LSPLCAgent.FormCloseQuery(Sender: TObject; var CanClose: Boolean); begin if ExitOk then begin CanClose := true; end else begin CanClose := false; case MessageBox(handle, '"¿¹(Y)"¸¦ Ŭ¸¯Çϸé, LSPLC Agent01°¡ Æ®·¹ÀÌ·Î ÃÖ¼ÒÈ­ µË´Ï´Ù.'+#$0D#$0A#$0D#$0A+'"¾Æ´Ï¿À(N)"¸¦ Ŭ¸¯Çϸé ÇÁ·Î±×·¥ÀÌ Á¾·áµË´Ï´Ù. ', 'LSPLC Agent01 :: Æ®·¹ÀÌ·Î ÃÖ¼ÒÈ­ ÇÒ±î¿ä?', MB_YESNOCANCEL) of ID_YES : begin TrayIcon1.BalloonTitle := 'LSPLC Agent È­¸éÀ» ´Ù½Ã º¸·Á¸é...'; 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 TF_LSPLCAgent.FormCreate(Sender: TObject); var i, j : Integer; Sections: TStringList; CK_ROW : integer; Ini: TIniFile; begin close_ck := False; LoadCK := False; PLCconnectCK := 0; inifilename := StringReplace(Application.ExeName,'.exe','.ini',[rfReplaceAll]); Ini := TIniFile.Create(inifilename); try Edit5.Text := Ini.ReadString('PLC', 'IP', '192.168.100.11'); Edit6.Text := Ini.ReadString('PLC', 'PORT', '2004'); CheckBox1.Checked := Ini.ReadBool('START', 'AUTO', False); dbIP_PG := Ini.ReadString('DATABASE_PG','IP','192.168.200.199'); dbUser_PG := Ini.ReadString('DATABASE_PG','User','root'); dbPW_PG := Ini.ReadString('DATABASE_PG','PW','54323'); dbSchema_PG := Ini.ReadString('DATABASE_PG','Schema','neol'); dbPort_PG := Ini.ReadInteger('DATABASE_PG','Port',3306); finally Ini.Free; end; if FileExists(StringReplace(Application.ExeName,'.exe','_list1.ini',[rfReplaceAll])) then ListBox1.Items.LoadFromFile(StringReplace(Application.ExeName,'.exe','_list1.ini',[rfReplaceAll])); if FileExists(StringReplace(Application.ExeName,'.exe','_list2.ini',[rfReplaceAll])) then ListBox2.Items.LoadFromFile(StringReplace(Application.ExeName,'.exe','_list2.ini',[rfReplaceAll])); try GridAddressListTV.DataController.BeginUpdate; GridAddressListTV.DataController.RecordCount:=0; finally GridAddressListTV.DataController.EndUpdate; end; try GridAddressListTV.DataController.BeginUpdate; for i := 0 to ListBox1.Items.Count - 1 do begin CK_ROW := 0; with GridAddressListTV.DataController do begin CK_ROW := AppendRecord; Values[CK_ROW, GridAddressListTVColumn1.Index] := ListBox2.Items[i]; Values[CK_ROW, GridAddressListTVColumn2.Index] := ListBox1.Items[i]; Values[CK_ROW, GridAddressListTVColumn3.Index] := ''; Values[CK_ROW, GridAddressListTVColumn4.Index] := ''; Values[CK_ROW, GridAddressListTVColumn5.Index] := ''; end; end; finally GridAddressListTV.DataController.EndUpdate; end; // PG ¼­¹öÁ¢¼Ó if not DB_Connect then begin Memo1_ADD('['+FormatDateTime('yymmdd hhnnss.zzz',now)+'] Database Connect Error :: PG'); end; // ÀÚµ¿ ·Î±× ½ÃÀÛ if CheckBox1.Checked then Button6.Click; end; function TF_LSPLCAgent.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; DB_MainTableCreate; DB_MainTableDescriptCreate; DB_LogTableCreate; Result := True; // Memo1_ADD('¿¬°á ¼º°ø'); // Memo1_ADD('"Q1" ¹öưÀ» ´©¸¥ ÈÄ "Äõ¸®" ¹öưÀ» ´©¸£¼¼¿ä!!'); except on E: Exception do begin Result := False; // Memo1_ADD('¿¬°á ½ÇÆÐ: ' + E.Message); end; end; end; procedure TF_LSPLCAgent.DB_Insert; var sql, update_sql : string; i : Integer; ck_data : Float64; begin try sql := ''; sql := 'INSERT INTO "plc_'+format('%.3d',[PLC_NO])+'" '+ ' ( "Idx","CreateDT","plcID"' ; for i := 0 to GridAddressListTV.DataController.RecordCount - 1 do begin sql := sql +', "data'+format('%.2d',[i+1])+'"'; end; sql := sql + ' ) '; sql := sql + ' VALUES (DEFAULT, '+ 'now(),'+ inttostr(PLC_NO) ; update_sql := ''; update_sql := 'UPDATE "plc_Data" SET '; for i := 0 to GridAddressListTV.DataController.RecordCount - 1 do begin if Trim(GridAddressListTV.DataController.Values[i, GridAddressListTVColumn3.Index]) <> '' then begin sql := sql +','+ floattostr(GridAddressListTV.DataController.Values[i, GridAddressListTVColumn3.Index]); update_sql := update_sql + '"data'+format('%.2d',[i+1])+'" = '+floattostr(GridAddressListTV.DataController.Values[i, GridAddressListTVColumn3.Index])+' , '; end else begin sql := sql +','+ '0.0'; update_sql := update_sql + '"data'+format('%.2d',[i+1])+'" = 0.0 , '; end; end; sql := sql + ' ) '; update_sql := update_sql + '"UpdateDT"=now()'+ ' WHERE "plcID"='+inttostr(PLC_NO); insertSQL(update_sql); insertSQL(sql); except on E: Exception do begin Memo1_ADD('DB_Insert Error : ' + E.Message); end; end; end; function TF_LSPLCAgent.DB_Close:Boolean; begin try if FDConnection1.Connected then FDConnection1.Connected := False; Result := True; except Result := False; end; end; function TF_LSPLCAgent.DB_MainTableCreate:Boolean; var sql : string; i : integer; begin try sql := 'CREATE TABLE IF NOT EXISTS "plc_Data" ('+ '"Idx" SERIAL NOT NULL,'+ '"UpdateDT" TIMESTAMP ,'+ '"plcID" INTEGER ,'+ '"data01" REAL ,'+ '"data02" REAL ,'+ '"data03" REAL ,'+ '"data04" REAL ,'+ '"data05" REAL ,'+ '"data06" REAL ,'+ '"data07" REAL ,'+ '"data08" REAL ,'+ '"data09" REAL ,'+ '"data10" REAL ,'+ '"data11" REAL ,'+ '"data12" REAL ,'+ '"data13" REAL ,'+ '"data14" REAL ,'+ '"data15" REAL ,'+ '"data16" REAL ,'+ '"data17" REAL ,'+ '"data18" REAL ,'+ '"data19" REAL ,'+ '"data20" REAL ,'+ '"data21" REAL ,'+ '"data22" REAL ,'+ '"data23" REAL ,'+ '"data24" REAL ,'+ '"data25" REAL ,'+ '"data26" REAL ,'+ '"data27" REAL ,'+ '"data28" REAL ,'+ '"data29" REAL ,'+ '"data30" REAL ,'+ 'CONSTRAINT plc_Data_pkey PRIMARY KEY ("Idx"));'; insertSQL(sql); if not FDConnection1.Connected then FDConnection1.Connected := True; if FDConnection1.Connected then begin FDQuery2.Close; FDQuery2.SQL.Clear; FDQuery2.SQL.Text := 'SELECT * FROM "plc_Data" WHERE "plcID"='+IntToStr(PLC_NO); FDQuery2.Open; if FDQuery2.RecordCount <= 0 then begin sql := ''; sql := 'INSERT INTO "plc_Data" ' + 'VALUES (DEFAULT, '+ 'now(), '+ inttostr(PLC_NO)+ ' ' ; for i := 0 to 29 do begin sql := sql +','+ '0.0'; end; sql := sql + ' ) '; insertSQL(sql); end; end; except on E: Exception do begin Memo1_ADD('DB_MainTableCreate Error : ' + E.Message); end; end; end; function TF_LSPLCAgent.DB_MainTableDescriptCreate:Boolean; var sql : string; i : integer; begin try sql := 'CREATE TABLE IF NOT EXISTS "plc_Data_Desc" ('+ '"Idx" SERIAL NOT NULL,'+ '"UpdateDT" TIMESTAMP ,'+ '"plcID" INTEGER ,'+ '"fieldname" VARCHAR(50) ,'+ '"address" VARCHAR(50) ,'+ '"addressDescription" VARCHAR(200) ,'+ 'CONSTRAINT plc_Data_Desc_pkey PRIMARY KEY ("Idx"));'; insertSQL(sql); { if not FDConnection1.Connected then FDConnection1.Connected := True; if FDConnection1.Connected then begin FDQuery2.Close; FDQuery2.SQL.Clear; FDQuery2.SQL.Text := ' SELECT * FROM "plc_Data_Desc" '+ ' WHERE "plcID" = '+IntToStr(PLC_NO)+ ' and "address" = '''+'*****'+''''; FDQuery2.Open; if FDQuery2.RecordCount <= 0 then begin sql := ''; sql := 'INSERT INTO "plc_Data_Desc" ' + 'VALUES (DEFAULT, '+ 'now(), '+ inttostr(PLC_NO)+ ' '''+'*****'+''', '+ ' '''+'*****'+''', '+ ' '''+'*****'+'''); '; insertSQL(sql); end; end; } except on E: Exception do begin Memo1_ADD('DB_MainTableDescriptCreate Error : ' + E.Message); end; end; end; function TF_LSPLCAgent.DB_LogTableCreate:Boolean; var sql : string; begin try sql := 'CREATE TABLE IF NOT EXISTS "plc_'+format('%.3d',[PLC_NO])+'" ('+ '"Idx" SERIAL NOT NULL,'+ '"CreateDT" TIMESTAMP ,'+ '"plcID" INTEGER ,'+ '"data01" REAL ,'+ '"data02" REAL ,'+ '"data03" REAL ,'+ '"data04" REAL ,'+ '"data05" REAL ,'+ '"data06" REAL ,'+ '"data07" REAL ,'+ '"data08" REAL ,'+ '"data09" REAL ,'+ '"data10" REAL ,'+ '"data11" REAL ,'+ '"data12" REAL ,'+ '"data13" REAL ,'+ '"data14" REAL ,'+ '"data15" REAL ,'+ '"data16" REAL ,'+ '"data17" REAL ,'+ '"data18" REAL ,'+ '"data19" REAL ,'+ '"data20" REAL ,'+ '"data21" REAL ,'+ '"data22" REAL ,'+ '"data23" REAL ,'+ '"data24" REAL ,'+ '"data25" REAL ,'+ '"data26" REAL ,'+ '"data27" REAL ,'+ '"data28" REAL ,'+ '"data29" REAL ,'+ '"data30" REAL ,'+ 'CONSTRAINT plc_'+format('%.3d',[PLC_NO])+'_pkey PRIMARY KEY ("Idx"));'; insertSQL(sql); except on E: Exception do begin Memo1_ADD('DB_LogTableCreate Error : ' + E.Message); end; end; end; end.