Accessing PLC variables in synchronous/asynchronous/connected modes

System requirements:

Task

The sample program shows how AdsOcx methods and events can be used in a Delphi application. The various access types (synchronous/asynchronous/connected) are applied to the PLC variables. The PLC program defines an integer variable at address 100 in the process data flags area. The PLC variable is to be accessed for reading or writing from the Delphi application, using the various access modes.

Description

Synchronous, asynchronous or connected access to the PLC variables is possible by means of the AdsOcx. In a synchronous access the application is stopped until the requested data has arrived. In an asynchronous access, a request is sent to the PLC, after which execution of the Windows application continues. A callback function is then activated in the Windows application when the requested data has arrived. Under the connected access mode, an event function is called in the Windows application whenever the value of the PLC variable has changed.

Delphi 5 program

In the event function OnFormCreate, the AdsCreateVarHandle method requests a handle for the symbol name of the PLC variable. The handle is then used in the sample application for read or write access to the PLC variable. The OnDestroy event function releases the handle once more using the AdsDeleteVarHandle method when the application is closed.

 

var
  Form1         : TForm1;
  varName       :WideString;    {PLC variable symbol name}
  varValue      :Smallint;      {PLC variable value}
  varHandle     :integer;       {PLC variable handle}
  hConnect      :integer;       {PLC variable connection handle}
  adsResult     :integer;       {Ads result}

implementation

{$R *.DFM}

procedure TForm1.OnFormCreate(Sender: TObject);
begin
     AdsOcx1.AdsAmsServerNetId := AdsOcx1.AdsAmsClientNetId;    {Sets PLC server network adress}
     AdsOcx1.AdsAmsServerPort := 801;               {Sets the PLC run time system}
     varName := 'MAIN.VARINT16';
     varValue := 0;
     varHandle := 0;
     hConnect := 0;
     adsResult := AdsOcx1.AdsCreateVarHandle( varName, varHandle ); {creates variable handle}

     if adsResult = 0 then
    LabelVarHandle.Font.Color := clBlue
     else
    LabelVarHandle.Font.Color := clRed;

     LabelVarHandle.Caption := Format( 'AdsCreateVarHandle adsResult:%d   varName:%s   Handle:%d',[adsResult, varName, varHandle] );
end;

procedure TForm1.OnFormDestroy(Sender: TObject);
begin
     adsResult := AdsOcx1.AdsDeleteVarHandle( varHandle );
end; 

Synchronous access

A mouse click on one of the buttons in the SYNCHRONOUS group will cause the value of the PLC variable to be read or written synchronously, and to be displayed as text on the form. The PLC variable can be accessed in two ways: via the variable name or via the variable address.

Access by means of the variable address

procedure TForm1.OnSyncReadByAddrClick(Sender: TObject);
begin
     adsResult := AdsOcx1.AdsSyncReadIntegerReq( $00004020, 100, 2, varValue );
     LabelSyncRetData.Caption:=Format( 'adsResult:%d   Value:%d',[adsResult, varValue] );
end;

procedure TForm1.OnSyncWriteByAddrClick(Sender: TObject);
begin
     varValue := 100;
     adsResult := AdsOcx1.AdsSyncWriteIntegerReq( $00004020, 100, 2, varValue );
     LabelSyncRetData.Caption:=Format( 'adsResult:%d', [adsResult] );
end;

Access by means of the variable name

In the case of access using the variable name, the corresponding handle of the PLC variable is used as a parameter in the AdsSyncReadIntegerVarReq or AdsSyncWriteIntegerVarReq methods. The handle of the PLC variable is requested in the OnCreate event function when the application starts.

procedure TForm1.OnSyncReadByNameClick(Sender: TObject);
begin
     adsResult := AdsOcx1.AdsSyncReadIntegerVarReq( varHandle, 2, varValue );
     LabelSyncRetData.Caption:=Format( 'adsResult:%d   Value:%d', [adsResult, varValue] );
end;

procedure TForm1.OnSyncWriteByNameClick(Sender: TObject);
begin
     varValue := 200;
     adsResult := AdsOcx1.AdsSyncWriteIntegerVarReq( varHandle, 2, varValue );
     LabelSyncRetData.Caption:=Format( 'adsResult:%d', [adsResult] );
end; 

Asynchronous access

The PLC variable can be accessed asynchronously by means of the AdsReadIntegerReq and AdsWriteIntegerReq methods. 

procedure TForm1.OnAsyncReadByAddrClick(Sender: TObject);
var  varInvokeId       :integer;
begin
     varInvokeId := 33;
     adsResult := AdsOcx1.AdsReadIntegerReq( varInvokeId, $00004020, 100, 2 );
     LabelAsyncRetData.Caption:=Format( 'adsResult:%d', [adsResult] );
end;

procedure TForm1.OnAsyncWriteByAddrClick(Sender: TObject);
var  varInvokeId       :integer;
begin
     varInvokeId := 44;
     varValue := 300;
     adsResult := AdsOcx1.AdsWriteIntegerReq( varInvokeId, $00004020, 100, 2, varValue );
     LabelAsyncRetData.Caption:=Format( 'adsResult:%d', [adsResult] );
end; 

After an asynchronous access the execution of the Delphi application is continued, and an event function is called in the Windows application once the return parameter is available. In our sample, the event function AdsReadIntegerConf is called when reading the PLC variable, while for writing the PLC variable the event function called is AdsWriteConf.

procedure TForm1.AdsOcx1AdsReadIntegerConf(Sender: TObject; nInvokeId,
  nResult, cbLength: Integer; var pData: Smallint);
begin
     LabelAsyncEventData.Caption :=Format('nInvokeId:%d   nResult:%d   cbLength:%d  pData:%d',
              [nInvokeId, nResult, cbLength, pData]);
end;

procedure TForm1.AdsOcx1AdsWriteConf(Sender: TObject; nInvokeId,
  nResult: Integer);
begin
     LabelAsyncEventData.Caption :=Format('nInvokeId:%d   nResult:%d', [nInvokeId, nResult]);
end;

Connected access

In the connected access mode, a "connection" to the PLC variable is established. Depending on the parameters (ADSTRANS_SERVERCYCLE or ADSTRANS_SERVERONCHA), the event functions are called either cyclically or when the PLC variable changes.

In the sample application, clicking on the Connected read by address button calls the AdsReadIntegerConnect method, while a click on the Connected read by variable name button calls the AdsReadIntegerVarConnect method.  

procedure TForm1.OnConReadByAddrClick(Sender: TObject);
begin
     adsResult := AdsOcx1.AdsReadIntegerConnect( $00004020, 100, 2, ADSTRANS_SERVERCYCLE, 220, varValue );
     LabelConRetData.Caption:=Format( 'adsResult:%d', [adsResult] );
end;

procedure TForm1.OnConReadByNameClick(Sender: TObject);
begin
     adsResult := AdsOcx1.AdsReadIntegerVarConnect( varName, 2, ADSTRANS_SERVERCYCLE, 220, varValue );
     LabelConRetData.Caption:=Format( 'adsResult:%d', [adsResult] );
end; 

When successful, the AdsReadConnectUpdate event function is called in the Delphi application, regardless of which of the two methods is used to establish the connection. 

procedure TForm1.AdsOcx1AdsReadConnectUpdate(Sender: TObject; nIndexGroup,
  nIndexOffset: Integer);
begin
     LabelConEventData.Caption := Format('nIndexGroup:%d  nIndexOffset:%d   Value:%d',
              [nIndexGroup, nIndexOffset, varValue]);
end;

The AdsReadIntegerDisconnect method can be used to remove the connection to the PLC variable.

procedure TForm1.OnDisconnectReadClick(Sender: TObject);
begin
     adsResult := AdsOcx1.AdsReadIntegerDisconnect( varValue );
     LabelConRetData.Caption:=Format( 'adsResult:%d', [adsResult] );
end; 

ConnectEx methods (connected access with a user handle)

The ConnectEx methods can be used, in a manner similar to that of the Connect methods, to establish connected access to the PLC variables. The ConnectEx methods have the advantage that a user-defined handle can be passed as a parameter in the connect method when the connection is established. This handle can then be evaluated in the event function, and used to identify the PLC variable for which the event function has been called. 

Clicking on the ConnectEx button will call the AdsReadVarConnectEx2 method in the OnConnectExClick routine.

procedure TForm1.OnConectExClick(Sender: TObject);
var  hUser :integer;
begin
     {disconnect old connection}
     if hConnect <> 0 then
     begin
    adsResult := AdsOcx1.AdsDisconnectEx( hConnect );
    if adsResult = 0 then
        hConnect := 0;
     end;

     hUser := 7;    {create user handle}

     adsResult := AdsOcx1.AdsReadVarConnectEx2( varName, ADSTRANS_SERVERCYCLE, 220, hConnect, hUser );
     LabelConExRetData.Caption:=Format( 'adsResult:%d   hConnect:%d', [adsResult, hConnect] );
end;

 

If the connection is successfully established, then the parameters will be displayed as text on the form in the event function AdsReadConnectUpdateEx2.

procedure TForm1.AdsOcx1AdsReadConnectUpdateEx2(Sender: TObject;
  dateTime: TDateTime; nMs, hConnect: Integer; var data,
  hUser: OleVariant);
begin
     LabelConExEventData.Caption :=Format('Date/Time:%s   nMs:%d   hConnect:%d   data:%d   hUser:%d',
                [ TimeToStr(dateTime), nMs, hConnect, integer(data), integer(hUser)]);
end;

 

Clicking with the mouse on the DisconnectEx button will call the AdsDisconnectEx method, and the connection to the PLC variable will be removed.

procedure TForm1.OnDisconnectExClick(Sender: TObject);
begin
     adsResult := AdsOcx1.AdsDisconnectEx( hConnect );
     if adsResult = 0 then
     hConnect := 0;

     LabelConExRetData.Caption:=Format( 'adsResult:%d', [adsResult] );
end;

Comment

In the course of linking the ADS-OCX into Delphi applications it has been found that the Delphi development environment generates faulty prototypes (more precisely: faulty parameter passing of OleVariant types) for the AdsReadConnectUpdateEx event function.  For this reason, the ADS-OCX has been supplemented with a new AdsReadVarConnectEx2 method and associated AdsReadConnectUpdateEx2 event function. In the new event function the OleVariant parameter is passed by reference instead of by value.

Other

procedure TForm1.Exit1Click(Sender: TObject);
begin
    Close();
end;

procedure TForm1.Properties1Click(Sender: TObject);
begin
    AdsOcx1.BrowseProperties();
end;

procedure TForm1.About1Click(Sender: TObject);
begin
    AdsOcx1.AboutBox();
end;

Initialization
    IsMultiThread := True;// Setting this system variable makes Delphi's memory manager thread-safe 

PLC program

PROGRAM MAIN
VAR
    VARINT16    AT%MB100:INT;
END_VAR

Language / IDE

Unpack sample program

Delphi XE2

Sample01.exe

Delphi 5 or higher (classic)