Event-driven reading (by symbol name)

Requirements:

Event-driven reading (by symbol name) 1:
Event-driven reading (by symbol name) 2:

Declaration files

The declaration files TcAdsAPI.pas and TcAdsDEF.pas are included in the archive delphi_adsdll_api_units.zip.

Delphi 5 program

unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, TcAdsDef, TcAdsAPI,
  StdCtrls, Math, ComCtrls;
const WM_ADSDEVICENOTIFICATION = WM_APP + 400;
type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    Button2: TButton;
    Memo1: TMemo;
    Label1: TLabel;
    Label2: TLabel;
    editDelayTime: TEdit;
    Label3: TLabel;
    editCycleTime: TEdit;
    StatusBar1: TStatusBar;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure WMAdsDeviceNotification( var Message: TMessage ); message WM_ADSDEVICENOTIFICATION;
    procedure FormDestroy(Sender: TObject);
  private
    server : TAmsAddr;
    hVar : Longword;
    hNotification : Longword;
    nCnt      : Longword;
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;
{$ALIGN OFF}
type
    TNotifyData = record
        netid        : TAmsNetId;
        port         : Word;
        hNotification    : Longword;
        dateTime         : TDateTime;
        cbSampleSize     : Longword;
        hUser        : Longword;
        data         : Byte;
    end;
PNotifyData = ^TNotifyData;
{$ALIGN ON}
var
  Form1: TForm1;
  DevThreadList : TThreadList;
implementation
{$R *.DFM}


Implementation of the notification callback function

////////////////////////////////////////////////////////////////////////////////////////////////
Procedure NotificationCallback( pAddr:PAmsAddr; pNotification:PAdsNotificationHeader;   hUser:Longword ); stdcall;
var pItem           : PNotifyData;
    FileTime        : _FILETIME;
    SystemTime, LocalTime   : _SYSTEMTIME;
    TimeZoneInformation     : _TIME_ZONE_INFORMATION;
begin
     System.GetMem( pItem, sizeof(TNotifyData) - 1 + pNotification^.cbSampleSize );
     pItem^.netId := pAddr^.netId;
     pItem^.port := pAddr^.port;
     pItem^.hNotification := pNotification^.hNotification;
     pItem^.cbSampleSize := pNotification^.cbSampleSize;
     System.Move( pNotification^.data, pItem^.data, pNotification^.cbSampleSize);
     FileTime:=pNotification^.nTimeStamp;
     GetTimeZoneInformation(TimeZoneInformation);
     FileTimeToSystemTime(FileTime, SystemTime);
     SystemTimeToTzSpecificLocalTime(@TimeZoneInformation, SystemTime, LocalTime);
     pItem^.dateTime:=SystemTimeToDateTime(LocalTime);
     with DevThreadList.LockList do
     try
      Add( pItem );
     finally
      DevThreadList.UnlockList;
      PostMessage( HWND(hUser), WM_ADSDEVICENOTIFICATION, 0, 0);
     end;
end;


Synchronization of main thread and callback thread using Windows messages

////////////////////////////////////////////////////////////////////////////////////////////////
procedure TForm1.WMAdsDeviceNotification( var Message: TMessage );
var pItem           : PNotifyData;
    X               : integer;
    sHex            : String;
    i               : Longword;
begin
    with DevThreadList.LockList do
    try
       Memo1.Lines.BeginUpdate();
       for X := 0 to Count-1 do
       begin
       nCnt := nCnt + 1;
       pItem := Items[X];
       {convert data to hex string}
       sHex := '';
       if pItem^.cbSampleSize > 0 then
       begin
        for i:= 0 to math.Min(pItem^.cbSampleSize-1, 20) do
            sHex := sHex + IntToHex( PBYTE(PAnsiChar(@pItem^.data) + i)^, 2 ) + ' ' ;
       end;
       {}
       Memo1.Lines.Insert(0,Format( 'idx:%-4d stamp:%s, %d.%d.%d.%d.%d.%d[%d], hNot:0x%x, size:%d, data:%s',
               [nCnt,TimeToStr(pItem^.dateTime), pItem^.netId.b[0], pItem^.netId.b[1], pItem^.netId.b[2],
               pItem^.netId.b[3], pItem^.netId.b[4], pItem^.netId.b[5], pItem^.port,
               pItem^.hNotification, pItem^.cbSampleSize, sHex]));
       FreeMem( pItem, sizeof(TNotifyData) -1 + pItem^.cbSampleSize );
       end;
       Clear();
   finally
      if memo1.Lines.Count > 1000 then
    memo1.Lines.Clear();
      Memo1.Lines.EndUpdate();
      DevThreadList.UnlockList;
   end;
   inherited;
end;


Establish connection to the PLC

////////////////////////////////////////////////////////////////////////////////////////////////
procedure TForm1.FormCreate(Sender: TObject);
begin
     AdsPortOpen();
     AdsGetLocalAddress(@server);
     server.port := AMSPORT_R0_PLC_RTS1;
     DevThreadList := TThreadList.Create();
     StatusBar1.SimpleText := Format('Server netId: %d.%d.%d.%d.%d.%d port:%d', [server.netId.b[0], server.netId.b[1],server.netId.b[2],
               server.netId.b[3], server.netId.b[4],server.netId.b[5], server.port]);
end;


Release resources, disconnect

////////////////////////////////////////////////////////////////////////////////////////////////
procedure TForm1.FormDestroy(Sender: TObject);
var X : integer;
begin
    if hNotification <> 0 then
    Button2Click(Sender);
     with DevThreadList.LockList do
     try
      for X := 0 to Count -1 do
          FreeMem( Items[X], sizeof(TNotifyData) + PNotifyData(Items[X])^.cbSampleSize - 1 );
      Clear;
     finally
      DevThreadList.UnlockList;
      DevThreadList.Destroy;
     end;
     AdsPortClose();
end;

 

Register notification

////////////////////////////////////////////////////////////////////////////////////////////////
procedure TForm1.Button1Click(Sender: TObject);
var result : Longint;
    adsNotificationAttrib : TAdsNotificationAttrib;
    szVarName : AnsiString;
begin
     Memo1.Lines.Clear;
     nCnt := 0;
     { get symbol handle by symbol name }
     szVarName := AnsiString(Edit1.Text);
     result := AdsSyncReadWriteReq( @server, ADSIGRP_SYM_HNDBYNAME, 0,
                    sizeof(hVar), @hVar, Length(szVarName)+1, @szVarName[1] );
     Memo1.Lines.Insert(0, Format('AdsSyncReadWriteReq(..ADSIGRP_SYM_HNDBYNAME..) result: %d [0x%x], symbol handle: 0x%x', [result, result, hVar]));
     { add notification (connect to variable)}
     adsNotificationAttrib.cbLength := 20;{Should be right}
     adsNotificationAttrib.nTransMode := ADSTRANS_SERVERONCHA;
     adsNotificationAttrib.nMaxDelay := StrToInt(editDelayTime.Text) * 10000; //ms
     adsNotificationAttrib.nCycleTime := StrToInt(editCycleTime.Text) * 10000;//ms
     result := AdsSyncAddDeviceNotificationReq( @server, ADSIGRP_SYM_VALBYHND,
                        hVar,
                        @adsNotificationAttrib,
                        @NotificationCallback,
                        Handle, @hNotification );
     Memo1.Lines.Insert(0, Format( 'AdsSyncAddDeviceNotificationReq(..ADSIGRP_SYM_VALBYHND..) result: %d [0x%x], notification handle: 0x%x', [result, result, hNotification] ));
     Button1.Enabled := not( result = ADSERR_NOERR);
     Button2.Enabled := ( result = ADSERR_NOERR);
end;

 

Unregister notification

////////////////////////////////////////////////////////////////////////////////////////////////
procedure TForm1.Button2Click(Sender: TObject);
var result : Longint;
begin
     { delete notification }
     result := AdsSyncDelDeviceNotificationReq( @server, hNotification );
     hNotification := 0;
     Memo1.Lines.Insert(0, Format( 'AdsSyncDelDeviceNotificationReq() result: %d [0x%x]', [result,result] ));
     {release symbol handle}
     result := AdsSyncWriteReq( @server, ADSIGRP_RELEASE_SYMHND, 0,sizeof(hVar), @hVar);
     Memo1.Lines.Insert(0, Format('AdsSyncReadWriteReq(..ADSIGRP_RELEASE_SYMHND..) result: %d [0x%x]', [result, result]));
     Button1.Enabled := true;
     Button2.Enabled := false;
end;
initialization
 
end.

Language / IDE

Unpack sample program

Delphi XE2

delphixe2_api_ADS-DLLSample02.exe

Delphi 5 or higher (classic)

Sample02.exe