ADS Summenkommando: Das Holen und Freigeben von mehreren Variable-Handles mit einem ADS-Kommando.

Voraussetzungen:

Aufgabenstellung

Beim Start der einfachen Windows-Applikation sollen Handles für 15 SPS-Variablen mit einem Summenkommando geholt werden. Mit diesen Handles kann z. B. auf die Variablen lesend zugegriffen werden. Beim Beenden der Applikation sollen die Handles mit einem Summenkommando freigegeben 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.

ADS Summenkommando: Das Holen und Freigeben von mehreren Variable-Handles mit einem ADS-Kommando. 1:

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, StdCtrls, Math;

Beim Programmstart wird der Kommunikationsport geöffnet und beim Beenden geschlossen.

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;

Handles holen

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;

Handles freigeben

////////////////////////////////////////////////////////////////////////////
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;

 

Werte lesen "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. 

Sprache / IDE

Beispielprogram auspacken

Delphi XE2

Sample08.exe

Delphi 6 oder höher (classic)

Sample08.exe