ADS Summenkommando: Lesen oder schreiben von mehreren Variablen mit einem ADS-Befehl.

Voraussetzungen:

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.

ADS Summenkommando: Lesen oder schreiben von mehreren Variablen mit einem ADS-Befehl. 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, 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

delphixe2_api_ADS-DLL Sample07.exe

Delphi 6 oder höher (classic)

Sample07.exe