ADS Summenkommando: Lesen oder schreiben von mehreren Variablen mit einem ADS-Befehl.
Voraussetzungen:
- TwinCAT v2.10 Build >= 1324;
- Delphi 6.0 + Update Pack 2 oder höher;
- TcAdsDLL.DLL;
- TcAdsDEF.pas und TcAdsAPI.pas, enthalten in der Datei delphi_adsdll_api_units.zip, falls Sie den Quelltext selber übersetzen möchten;
Aufgabenstellung
Es sollen 15-SPS-Variablen, unterschiedlichen Typs mit einem ADS-Summenkommando gelesen oder geschrieben werden.
In Delphi ist es möglich offene Arrayparameter an Funktionen zu übergeben. D.h. die Anzahl der Arrayelemente in so einem Funktionsparameter ist nicht festgelegt. In der TcAdsApi.pas-Datei wurden drei neue Funktionen implementiert: AdsSyncSumReadReq, AdsSyncSumWriteReq und AdsSyncSumReadWriteReq. Diese Funktionen benutzen die Funktionalität der offenen Arrayparameter und vereinfachen die Benutzung des Summenkomandos unter Delphi. Das Beispielprojekt nutzt diese neuen Wrapper-Funktionen.
Das SPS-Programm
Die SPS-Variablen wurden als globale Variablen deklariert. Bei einer gestarteten SPS werden Wertänderungen der Variablen im einfachen MAIN-Programm simuliert.
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 Programm
Die Deklarationen der benutzten TcAdsDLL-Funktionen befinden sich in den Pascal-Units TcAdsDEF.pas und TcAdsAPI.pas. Diese wurden über eine Uses-Klausel in das Projekt eingebunden.
unit Unit1;
interface
uses
TcAdsDEF, TcAdsAPI, Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Grids, ValEdit, ComCtrls, StdCtrls, Math;
Beim Programmstart wird der Kommunikationsport geöffnet und beim Beenden geschlossen. Die benötigten Symbolinformationen der SPS-Variablen wie Index-Group, index-Offset usw. werden beim Programmstart ebenfalls mit Hilfe eines Summenkommandos einmalig gelesen.
/////////////////////////////////////////////////////////////////////////
type
TAdsSymbolInfo = packed record
entry : TAdsSymbolEntry; // ADS symbol info
name : AnsiString; // ADS symbol name
arraySize : Longword;
case typeID : ADS_DATATYPE of //variable part value
ADST_VOID:(); //nothing
ADST_INT16:(vINT : Smallint); //2 byte signed number
ADST_INT32:(vDINT: Longint); //4 byte signed number
ADST_REAL32:(vREAL : Single); //4 byte real
ADST_REAL64:(vLREAL : Double); //8 byte real
ADST_INT8:(vSINT : Shortint); //1 byte signed number
ADST_UINT8:(vBYTE : Byte); //1 byte unsigned number
ADST_UINT16:(vWORD : Word); //2 byte unsigned number
ADST_UINT32:(vDWORD : DWORD); //4 byte unsigned number
ADST_STRING:(vSTRING : Array[0..80] OF AnsiChar); //null terminated string
ADST_BIT:(vBOOL : Boolean); //boolean
ADST_BIGTYPE:(data : Array[0..255] OF Byte); //data byte array
end;
/////////////////////////////////////////////////////////////////////////
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
ValueListEditor1: TValueListEditor;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormCreate(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
public
infos : Array of TAdsSymbolInfo;
client : Longint;
amsAddr : TAmsAddr;
{ Public declarations }
procedure ReadListOfSymbolInfo();
procedure ReadListOfVars();
procedure WriteListOfVars();
procedure ReadListByName();
procedure DataToView( var info : TAdsSymbolInfo);
procedure ViewToData( var info : TAdsSymbolInfo);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
/////////////////////////////////////////////////////////////////////////
procedure TForm1.FormCreate(Sender: TObject);
var adsErr : Longint;
begin
Button1.Enabled := True;
Button2.Enabled := False;
Button3.Enabled := False;
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)
ReadListOfSymbolInfo();// read symbol info
end
else
ShowMessageFmt('AdsGetLocalAddress() error: %d [0x%X]', [adsErr, adsErr]);
end
else
ShowMessage('AdsPortOpen() failed!');
end;
/////////////////////////////////////////////////////////////////////////
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if client <> 0 then
AdsPortClose();// close ads port
end;
Symbolinformationen lesen
/////////////////////////////////////////////////////////////////////////
procedure TForm1.ReadListOfSymbolInfo();
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';
infos[1].name := '.wBits';
infos[2].name := '.dwBits';
infos[3].name := '.f32AxPos';
infos[4].name := '.f64AxPos';
infos[5].name := '.bEnable';
infos[6].name := '.sValue';
infos[7].name := '.ui8Num';
infos[8].name := '.ui16Num';
infos[9].name := '.ui32Num';
infos[10].name := '.i8Num';
infos[11].name := '.i16Num';
infos[12].name := '.i32Num';
infos[13].name := '.aui32';
infos[14].name := '.out8Bit';
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_INFOBYNAMEEX;
ioList[i] := $0;
readLenList[i] := SizeOf(infos[i].entry);
readPtrList[i] := @infos[i].entry;
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 );
if adsErr = 0 then
begin
for i:=Low(resList) to High(resList) do
begin
if resList[i] <> 0 then
ShowMessageFmt('AdsSyncSumReadWriteReq(reading info "%s") error: %d [0x%X]', [infos[i].name, resList[i], resList[i]]);
end
else
ShowMessageFmt('AdsSyncSumReadWriteReq() error: %d [0x%X]', [adsErr, adsErr]);
end;
Werte lesen
/////////////////////////////////////////////////////////////////////////
procedure TForm1.Button1Click(Sender: TObject);
begin
ValueListEditor1.Strings.Clear();
ReadListOfVars();
end;
/////////////////////////////////////////////////////////////////////////
procedure TForm1.ReadListOfVars();
var igList, ioList, lenList : Array of Longword;
ptrList : Array of Pointer;
resList : Array of Longint;
i, adsErr : Longint;
begin
SetLength(igList, Length(infos));
SetLength(ioList, Length(infos));
SetLength(lenList, Length(infos));
SetLength(ptrList, Length(infos));
SetLength(resList, Length(infos));
for i:=Low(infos) to High(infos) do
begin
igList[i] := infos[i].entry.iGroup;
ioList[i] := infos[i].entry.iOffs;
lenList[i] := infos[i].entry.size;
ptrList[i] := @infos[i].data[0];
resList[i] := 0;
end;
adsErr := AdsSyncSumReadReq(@amsAddr, igList, ioList, lenList, ptrList, resList );
if adsErr = 0 then
begin
for i:= Low(resList) to High(resList) do
begin
if resList[i] <> 0 then
ShowMessageFmt('AdsSyncSumReadReq(reading value "%s") error: %d [0x%X]', [infos[i].name, resList[i], resList[i]]);
DataToView( infos[i] )// Output read data
end;
end
else
ShowMessageFmt('AdsSyncSumReadReq() error: %d [0x%X]', [adsErr, adsErr]);
Button2.Enabled := adsErr = 0;
Button3.Enabled := adsErr = 0;
end;
Werte schreiben
/////////////////////////////////////////////////////////////////////////
procedure TForm1.Button2Click(Sender: TObject);
begin
WriteListOfVars();
end;
/////////////////////////////////////////////////////////////////////////
procedure TForm1.WriteListOfVars();
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
try
ViewToData( infos[i] ); // refresh value
finally
igList[i] := infos[i].entry.iGroup;
ioList[i] := infos[i].entry.iOffs;
lenList[i] := infos[i].entry.size;
srcList[i] := @infos[i].data[0];
resList[i] := 0;
end;
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
ShowMessageFmt('AdsSyncSumWriteReq(writing value "%s") error: %d [0x%X]', [infos[i].name, resList[i], resList[i]]);
end;
end
else
ShowMessageFmt('AdsSyncSumWriteReq() error: %d [0x%X]', [adsErr, adsErr]);
end;
Werte lesen "by name"
/////////////////////////////////////////////////////////////////////////
procedure TForm1.Button3Click(Sender: TObject);
begin
ValueListEditor1.Strings.Clear();
ReadListByName();
end;
/////////////////////////////////////////////////////////////////////////
procedure TForm1.ReadListByName();
var igList, ioList, writeLenList, readLenList, retLenList : Array of Longword;
writePtrList, readPtrList : Array of Pointer;
resList : Array of Longint;
i, adsErr : Longint;
begin
SetLength(igList, Length(infos));
SetLength(ioList, Length(infos));
SetLength(readLenList, Length(infos));
SetLength(readPtrList, Length(infos));
SetLength(writeLenList, Length(infos));
SetLength(writePtrList, Length(infos));
SetLength(resList, Length(infos));
SetLength(retLenList, Length(infos));
for i:=Low(infos) to High(infos) do
begin
igList[i] := ADSIGRP_SYM_VALBYNAME;
ioList[i] := $0;
readLenList[i] := SizeOf(infos[i].data);
readPtrList[i] := @infos[i].data[0];
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 );
if adsErr = 0 then
begin
for i:=Low(resList) to High(resList) do
begin
if resList[i] <> 0 then
ShowMessageFmt('AdsSyncSumReadWriteReq(reading value "%s) error:%d [0x%X]', [infos[i].name, resList[i], resList[i]]);
DataToView( infos[i] )// Output read data
end;
end
else
ShowMessageFmt('AdsSyncSumReadWriteReq() error: %d [0x%X]', [adsErr, adsErr]);
end;
Einfache Proceduren um gelesenen Werte im Dialog anzeigen zu können oder die zu schreibenden Werte vom Dialog in Daten die übertragen werden müssen zu konvertieren.
/////////////////////////////////////////////////////////////////////////
procedure TForm1.DataToView( var info : TAdsSymbolInfo );
var sValue : String;
index : integer;
list : TStringList;
bPickList : Boolean;
begin
list := TStringList.Create();
bPickList := False;
sValue := '';
info.arraySize := 0;
info.typeID := ADS_DATATYPE(info.entry.dataType);
case info.typeID of
ADST_INT8:
begin
info.arraySize := info.entry.size Div sizeof(info.vSINT);
sValue := Format('%d', [info.vSINT]);
end;
ADST_INT16:
begin
info.arraySize := info.entry.size Div sizeof(info.vINT);
sValue := Format('%d', [info.vINT]);
end;
ADST_INT32:
begin
info.arraySize := info.entry.size Div sizeof(info.vDINT);
sValue := Format('%d', [info.vDINT]);
end;
ADST_UINT8:
begin
info.arraySize := info.entry.size Div sizeof(info.vBYTE);
sValue := Format('%u', [info.vBYTE]);
end;
ADST_UINT16:
begin
info.arraySize := info.entry.size Div sizeof(info.vWORD);
sValue := Format('%u', [info.vWORD]);
end;
ADST_UINT32:
begin
info.arraySize := info.entry.size Div sizeof(info.vDWORD);
sValue := Format('%u', [info.vDWORD]);
end;
ADST_BIT:
begin
info.arraySize := info.entry.size Div sizeof(info.vBOOL);
sValue := Format('%s', [BoolToStr(info.vBOOL, true)]);
list.Add('True');
list.Add('False');
bPickList := True;
end;
ADST_STRING:
begin
info.arraySize := info.entry.size Div sizeof(info.vSTRING);
sValue := Format('%s', [Trim(String(info.vSTRING))]); //Trim = Cut characters behind of string end delimiter
end;
ADST_REAL32:
begin
info.arraySize := info.entry.size Div sizeof(info.vREAL);
sValue := Format('%f', [info.vREAL]);
end;
ADST_REAL64:
begin
info.arraySize := info.entry.size Div sizeof(info.vLREAL);
sValue := Format('%f', [info.vLREAL]);
end;
ADST_BIGTYPE:
sValue := 'BIGTYPE';
else
sValue := 'Unknown type?';
end;
index := ValueListEditor1.Strings.Add(Format('%s=%s', [info.name, sValue]));
if bPickList then
begin
ValueListEditor1.itemProps[index].EditStyle := esPickList;
ValueListEditor1.itemProps[index].PickList.AddStrings(list);
end;
list.Destroy();
end;
//////////////////////////////////////////////////////////////////////////////
procedure TForm1.ViewToData( var info : TAdsSymbolInfo);
var sValue : String;
begin
try
sValue := ValueListEditor1.Values[String(info.name)];
case info.typeID of
ADST_INT8:
begin
info.vSINT := Shortint(StrToInt(sValue));
end;
ADST_INT16:
begin
info.vINT := Smallint(StrToInt(sValue));
end;
ADST_INT32:
begin
info.vDINT := Longint(StrToInt64(sValue));
end;
ADST_UINT8:
begin
info.vBYTE := Byte(StrToInt(sValue));
end;
ADST_UINT16:
begin
info.vWORD := Word(StrToInt(sValue));
end;
ADST_UINT32:
begin
info.vDWORD := Longword(StrToInt64(sValue));
end;
ADST_BIT:
begin
info.vBOOL := StrToBool(sValue);
end;
ADST_STRING:
begin
StrPCopy(@info.vSTRING[0], AnsiString(sValue));
end;
ADST_REAL32:
begin
info.vREAL:= StrToFloat(sValue);
end;
ADST_REAL64:
begin
info.vLREAL:= StrToFloat(sValue);
end;
ADST_BIGTYPE:
;
else
;
end;
except
on E: EConvertError do
ShowMessage(E.ClassName + E.Message);
end;
end;
end.
Sprache / IDE | Beispielprogram auspacken |
---|---|
Delphi XE2 | |
Delphi 6 oder höher (classic) |