ADS sum command: fetching and releasing multiple variable handles with one ADS command.
Requirements:
- TwinCAT v2.11 Build >= 1550;
- Delphi 6.0 + + update Pack 2 or higher;
- TcAdsDLL.DLL;
- TcAdsDEF.pas and TcAdsAPI.pas, contained in the file delphi_adsdll_api_units.zip, if you want to compile the source code yourself;
The task
When starting the simple Windows application, handles for 15 PLC variables are to be fetched with a sum command. These handles can be used for read access to the variables, for example. When the application is terminated, the handles are to be released with a sum command.
In Delphi it is possible to pass open array parameters to functions. I.e. the number of array elements in such a function parameter is not fixed. Three new functions have been implemented in the TcAdsApi.pas file: AdsSyncSumReadReq, AdsSyncSumWriteReq and AdsSyncSumReadWriteReq. These functions use the functionality of the open array parameters and simplify the use of the sum command under Delphi. The sample project uses these new wrapper functions.
The PLC program
The PLC variables were declared as global variables. With a started PLC, value changes of the variables are simulated in the simple MAIN program.
VAR_GLOBAL
nBits : BYTE;
wBits : WORD;
dwBits : DWORD;
f32AxPos : REAL;
f64AxPos : LREAL;
bEnable : BOOL;
sValue : STRING := 'INIT';
ui8Num : USINT;
ui16Num : UINT;
ui32Num : UDINT;
i8Num : SINT;
i16Num : INT;
i32Num : DINT;
aui32 : ARRAY[0..9] OF DWORD;
out8Bit AT%QX0.1 : BOOL;
END_VAR
Delphi 6 program
The declarations for the TcAdsDLL functions used are located in the Pascal units TcAdsDEF.pas and TcAdsAPI.pas. They were linked in the project via a Uses clause.
unit Unit1;
interface
uses
TcAdsDEF, TcAdsAPI, Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Math;
The communication port is opened when the program starts and closed when it ends.
type TAdsHandleInfo = packed record // packed = 1 byte alignment
name : AnsiString;// symbol name
handle : Longword; // symbol handle
data : Array Of Byte;// data bytes (resize the dynamic array to size of the plc variables)
end;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure CreateHandleList();
procedure ReleaseHandleList();
procedure ReadListByHandle();
procedure Button1Click(Sender: TObject);
private
infos : Array of TAdsHandleInfo;
client : Longint;
amsAddr : TAmsAddr;
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
Fetch handles
implementation
{$R *.dfm}
////////////////////////////////////////////////////////////////////////////
procedure TForm1.FormCreate(Sender: TObject);
var adsErr : Longint;
begin
client := AdsPortOpen(); // open ads port
if client <> 0 then
begin
adsErr := AdsGetLocalAddress( @amsAddr );
if adsErr = 0 then
begin
amsAddr.port := AMSPORT_R0_PLC_RTS1; // set port to 801 (first PLC run time system)
CreateHandleList();
end
else
ShowMessageFmt('AdsGetLocalAddress() error: %d [0x%X]', [adsErr, adsErr]);
end
else
ShowMessage('AdsPortOpen() failed!');
end;
////////////////////////////////////////////////////////////////////////////
procedure TForm1.CreateHandleList();
var igList, ioList, writeLenList, readLenList, retLenList : Array of Longword;
writePtrList, readPtrList : Array of Pointer;
resList : Array of Longint;
i, adsErr : Longint;
nInfos : Longword;
begin
nInfos := 15;
SetLength(infos, nInfos);
infos[0].name := '.nBits';
SetLength( infos[0].data, 1);//BYTE
infos[1].name := '.wBits';
SetLength( infos[1].data, 2);//WORD
infos[2].name := '.dwBits';
SetLength( infos[2].data, 4);//DWORD
infos[3].name := '.f32AxPos';
SetLength( infos[3].data, 4);//REAL
infos[4].name := '.f64AxPos';
SetLength( infos[4].data, 8);//LREAL
infos[5].name := '.bEnable';
SetLength( infos[5].data, 1);//BOOL
infos[6].name := '.sValue';
SetLength( infos[6].data, 81);//STRING(80) + 1 byte null delimiter
infos[7].name := '.ui8Num';
SetLength( infos[7].data, 1);//USINT
infos[8].name := '.ui16Num';
SetLength( infos[8].data, 2);//UINT
infos[9].name := '.ui32Num';
SetLength( infos[9].data, 4);//UDINT
infos[10].name := '.i8Num';
SetLength( infos[10].data, 1);//SINT
infos[11].name := '.i16Num';
SetLength( infos[11].data, 2);//INT
infos[12].name := '.i32Num';
SetLength( infos[12].data, 4);//DINT
infos[13].name := '.aui32';
SetLength( infos[13].data, 40);//ARRARY[..9] OF DWORD
infos[14].name := '.out8Bit';
SetLength( infos[14].data, 1);// 1 Byte
SetLength(igList, nInfos);
SetLength(ioList, nInfos);
SetLength(readLenList, nInfos);
SetLength(readPtrList, nInfos);
SetLength(writeLenList, nInfos);
SetLength(writePtrList, nInfos);
SetLength(resList, nInfos);
SetLength(retLenList, nInfos);
for i:=Low(infos) to High(infos) do
begin
igList[i] := ADSIGRP_SYM_HNDBYNAME;
ioList[i] := $0000000;
readLenList[i] := SizeOf(infos[i].handle);
readPtrList[i] := @infos[i].handle;
writeLenList[i] := Length(infos[i].name) + 1;
writePtrList[i] := @infos[i].name[1];
resList[i] := 0;
retLenList[i] := 0;
end;
adsErr := AdsSyncSumReadWriteReq( @amsAddr, igList, ioList,
readLenList, readPtrList,
writeLenList, writePtrList,
resList,retLenList );
// parse received data
if adsErr = 0 then
begin
for i:=Low(resList) to High(resList) do
begin
if resList[i] <> 0 then
ShowMessageFmt('AdsSyncSumReadWriteReq(ADSIGRP_SYM_HNDBYNAME) error:%d [0x%X], handle:0x%X', [resList[i], resList[i], infos[i].handle]);
ListBox1.Items.Add(Format('CREATE name: %s, result:%d [0x%X], handle:0x%X', [infos[i].name, resList[i], resList[i], infos[i].handle] )); end;
end
else
ShowMessageFmt('AdsSyncSumReadWriteReq() error:%d [0x%X]', [adsErr, adsErr]);
end;
Release handles
////////////////////////////////////////////////////////////////////////////
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if client <> 0 then
begin
ReleaseHandleList();
AdsPortClose();// close ads port
end;
end;
////////////////////////////////////////////////////////////////////////////
procedure TForm1.ReleaseHandleList();
var igList, ioList, lenList : Array of Longword;
srcList : Array of Pointer;
resList : Array of Longint;
i, adsErr : Longint;
begin
SetLength(igList, Length(infos));
SetLength(ioList, Length(infos));
SetLength(lenList, Length(infos));
SetLength(srcList, Length(infos));
SetLength(resList, Length(infos));
for i:= Low(infos) to High(infos) do
begin
igList[i] := ADSIGRP_RELEASE_SYMHND;
ioList[i] := $00000000;
lenList[i] := SizeOf(infos[i].handle);
srcList[i] := @infos[i].handle;
resList[i] := 0;
end;
adsErr := AdsSyncSumWriteReq( @amsAddr,
igList,
ioList,
lenList,
srcList,
resList );
if adsErr = 0 then
begin
for i:= Low(resList) to High(resList) do
begin
if resList[i] = 0 then // release successfull
infos[i].handle := 0// set to null
else
ShowMessageFmt('AdsSyncSumWriteReq(ADSIGRP_RELEASE_SYMHND) error: %d [0x%X], handle:0x%X', [resList[i], resList[i], infos[i].handle]);
ListBox1.Items.Add(Format('RELEASE name: %s, result:%d [0x%X], handle:0x%X', [infos[i].name, resList[i], resList[i], infos[i].handle] ));
end;
end
else
ShowMessageFmt('AdsSyncSumWriteReq() error: %d [0x%X]', [adsErr, adsErr]);
end;
Read values "by name"
////////////////////////////////////////////////////////////////////////////
procedure TForm1.Button1Click(Sender: TObject);
begin
ListBox1.Items.Clear();
ReadListByHandle();
end;
////////////////////////////////////////////////////////////////////////////
procedure TForm1.ReadListByHandle();
var igList, ioList, lenList : Array of Longword;
destList : Array of Pointer;
resList : Array of Longint;
i, adsErr : Longint;
b : Longint;
sValue : String;
cbValue : Longint;
begin
SetLength(igList, Length(infos));
SetLength(ioList, Length(infos));
SetLength(lenList, Length(infos));
SetLength(destList, Length(infos));
SetLength(resList, Length(infos));
for i:= Low(infos) to High(infos) do
begin
igList[i] := ADSIGRP_SYM_VALBYHND;
ioList[i] := infos[i].handle;
lenList[i] := Length(infos[i].data){ * SizeOf(infos[i].data[0])};
destList[i] := @infos[i].data[0];
resList[i] := 0;
end;
adsErr := AdsSyncSumReadReq( @amsAddr,
igList,
ioList,
lenList,
destList,
resList );
if adsErr = 0 then
begin
for i:= Low(resList) to High(resList) do
begin
if resList[i] = 0 then
begin
//TODO: save read data...
//Convert data to hex string and show it on the form
sValue := '';
cbValue := Length(infos[i].data){ * SizeOf(infos[i].data[0])};
if cbValue > 0 then
for b:= 0 to (cbValue - 1) do
sValue := sValue + IntToHex( infos[i].data[b], 2 ) + ' ' ;
ListBox1.Items.Add(Format('READ name: %s, value:%s', [infos[i].name, sValue] ));
end
else
ShowMessageFmt('AdsSyncSumReadReq(ADSIGRP_SYM_VALBYHND) error: %d [0x%X], handle:0x%X', [resList[i], resList[i], infos[i].handle]);
end;
end
else
ShowMessageFmt('AdsSyncSumReadReq() error: %d [0x%X]', [adsErr, adsErr]);
end;
end.
Language / IDE | Unpack sample program |
---|---|
Delphi XE2 | |
Delphi 6 or higher (classic) |