Accessing PLC variables in synchronous/asynchronous/connected modes
System requirements:
- Delphi 5.0 or higher;
- TwinCAT v2.9 or higher
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 | |
Delphi 5 or higher (classic) |