Event-driven reading of PLC variables

Requirements:

In the FormCreate event function a connection to the server in the local system is established via CoTcClient.Create. The Connect method is then used to establish a connection to the first PLC runtime system (port 801) on the local computer (netid = '0.0.0.0.0.0'). When the Start button is pressed, a connection to the PLC variables is established via the AddDeviceNotification method.  If a PLC variable changes, the server calls the DeviceNotification function of the _ITcAdsSyncEvents interface. In order to be able to send events to the client, this interface must be implemented in the client. In addition, the server has to be informed about their existence. This is achieved by calling the InterfaceConnect procedure. The connections are canceled via the DelDeviceNotification method by pressing the Stop button.

Event-driven reading of PLC variables 1:

The Delphi program

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls, TcAdsDll_TLB, OleServer, ComObj,activex, Math;

type
  TForm1 = class( TForm, IUnknown, _ITcAdsSyncEvents )
    Label1: TLabel;
    Label2: TLabel;
    Button1: TButton;
    Button2: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    TcClient      : ITcClient;
    TcAdsSync     : ITcAdsSync;
    dwCookie      : integer;
    hInteger, hDouble   : integer;
    serverAddr      : AmsNetId;
    function  DeviceNotification(var pTime: TimeStamp; hNotification: Integer; cbLen: Integer; var pData: Byte): HResult; stdcall;
  end;
type
    PINT = ^Smallint;
    PLREAL= ^Double;
var
  Form1: TForm1;

implementation
{$R *.DFM}
/////////////////////////////////////////////////////////////////////////
procedure TForm1.FormCreate(Sender: TObject);
begin
     serverAddr.b[0] := 0;
     serverAddr.b[1] := 0;
     serverAddr.b[2] := 0;
     serverAddr.b[3] := 0;
     serverAddr.b[4] := 0;
     serverAddr.b[5] := 0;
     Button1.Enabled := True;
     Button2.Enabled := False;

     try
    TcClient := CoTcClient.Create();
    try
        OleCheck(TcClient.Connect( serverAddr, 801,TcAdsSync));
        InterfaceConnect(TcAdsSync ,IID__ITcAdsSyncEvents,self as IUnknown, dwCookie);
    except
        ShowMessage('TcClient.Connect() failed!');
    end;
     except
    ShowMessage('CoTcClient.Create() failed!');
     end;
end;
/////////////////////////////////////////////////////////////////////////
procedure TForm1.FormDestroy(Sender: TObject);
begin
    if hInteger <> 0 then
    OleCheck(TcAdsSync.DelDeviceNotification(hInteger));

    if hDouble <> 0 then
    OleCheck(TcAdsSync.DelDeviceNotification(hDouble));

     InterfaceDisconnect(TcAdsSync,IID__ITcAdsSyncEvents, dwCookie);
end;
/////////////////////////////////////////////////////////////////////////
procedure TForm1.Button1Click(Sender: TObject);
begin
    hInteger := 0;
    hDouble := 0;
    try
    OleCheck(TcAdsSync.AddDeviceNotification($4020, 2, sizeof(Smallint), ADSTRANS_SERVERONCHA, 0, 1000000, hInteger ));
    OleCheck(TcAdsSync.AddDeviceNotification($4020, 12, sizeof(Double), ADSTRANS_SERVERONCHA, 0, 1000000, hDouble ));
    Button1.Enabled := False;
    Button2.Enabled := True;
    except
    ShowMessage('TcAdsSync.AddDeviceNotification() failed!');
    end;
end;
/////////////////////////////////////////////////////////////////////////
procedure TForm1.Button2Click(Sender: TObject);
begin
    try
    OleCheck(TcAdsSync.DelDeviceNotification(hInteger));
    OleCheck(TcAdsSync.DelDeviceNotification(hDouble));
    Button1.Enabled := True;
    Button2.Enabled := False;
    except
    ShowMessage('TcAdsSync.DelDeviceNotification() failed!');
    end;
    hInteger := 0;
    hDouble := 0;
end;
/////////////////////////////////////////////////////////////////////////
function  TForm1.DeviceNotification(var pTime: TimeStamp; hNotification: Integer; cbLen: Integer; var pData: Byte): HResult; stdcall;
var
    SystemTime, LocalTime   : _SYSTEMTIME;
    TimeZoneInformation     : _TIME_ZONE_INFORMATION;
    stamp           : TDateTime;
begin
    FileTimeToSystemTime(_FILETIME(pTime), SystemTime);
    GetTimeZoneInformation(TimeZoneInformation);
    SystemTimeToTzSpecificLocalTime(@TimeZoneInformation, SystemTime, LocalTime);
    stamp := SystemTimeToDateTime(LocalTime);

    if hNotification = hInteger then
       Label1.Caption := Format( 'PLCIntVar, %s %s, hNot:%d, cbLen:%d, data:%d',[DateToStr(stamp), TimeToStr(stamp), hNotification, cbLen, PINT(Addr(pData))^] )
    else
       Label2.Caption := Format( 'PLCLRealVar, %s %s, hNot:%d, cbLen:%d, data:%f',[DateToStr(stamp), TimeToStr(stamp), hNotification, cbLen, PLREAL(Addr(pData))^] );

    Result := S_OK;
end;

initialization
    IsMultiThread := True;


end.

PLC program

PROGRAM MAIN
VAR
  PLCBoolVar AT %MX0.0 : BOOL := TRUE;
  PLCIntVar AT %MB2 : INT := 75;
  PLCDIntVar AT %MB4 : DINT := 43234532;
  PLCRealVar AT %MB8 : REAL := 3.1415;
  PLCLRealVar AT %MB12 : LREAL := 3.141592654;
END_VAR
PLCLRealVar := PLCLRealVar +1;
PLCIntVar :=PLCIntVar +1;

Language / IDE

Unpack sample program

Delphi XE2

Sample02.zip

Delphi 5 or higher (classic)

Sample02.exe