Event driven reading with Delphi
Task
There are 7 global variables in the PLC. Each of these PLC variables is of a different data type. The values of the variables should be read in the most effective manner, and the value with its timestamp is to be displayed on a form.
Description
In the form's MainForm_Load event, a connection to each of the PLC variables is created with the TcAdsClient.AddDeviceNotification() method. The handle for this connection is stored in a array.
The parameter TransMode specifies the type of data exchange. AdsTransMode.OnChange has been selected here. This means that the value of the PLC variable is only transmitted if its value within the PLC has changed (see the AdsTransMode data type). The parameter cycleTime indicates that the PLC is to check whether the corresponding variable has changed every 100 ms. MaxDelay allows to collect notification for a specified interval. If the maxDelay elapse, all notifications will be send at once.
When the PLC variable changes, the TcAdsClient.AdsNotification() event is called. The parameter e of the event handling method is of the type AdsNotificationEventArgs and contains the time stamp, the handle, the value and the control in which the value is to be displayed. The connections are released again in the MainForm_FormClosing event by means of the TcAdsClient.DeleteDeviceNotification() method. It is essential that you do this, since every connection established by TcAdsClient.AddDeviceNotification() uses resources.
You should also choose appropriate values for the cycle time, since too many write / read operations load the system so heavily that the user interface becomes much slower.
Advice: Don't use time intensive executions in callbacks (OnNotification()).
Delphi Prism (Embarcadero Prism XE2, Oxygene for .NET) program
namespace Sample03;
interface
uses
System.Drawing,
System.Collections,
System.Collections.Generic,
System.Windows.Forms,
System.ComponentModel, System.IO, TwinCAT.Ads;
type
/// <summary>
/// Summary description for MainForm.
/// </summary>
MainForm = partial class(System.Windows.Forms.Form)
private
method tcClient_AdsNotification(sender: Object; e: AdsNotificationEventArgs);
method MainForm_Load(sender: System.Object; e: System.EventArgs);
method MainForm_FormClosing(sender: System.Object; e: System.Windows.Forms.FormClosingEventArgs);
tcClient : TcAdsClient;
hConnect : Array[0..6] of Integer;
dataStream : AdsStream;
binRead : BinaryReader;
protected
method Dispose(disposing: Boolean); override;
public
// boolVal : BOOL; 1 byte, unsigned integer
// intVal : INT; 2 byte, signed integer
// dintVal : DINT; 4 byte, signed integer
// sintVal : SINT; 1 byte, signed integer
// lrealVal : LREAL; 8 byte, floating point value
// realVal : REAL; 4 byte, floating point value
// stringVal: STRING(10); 11 byte, string length inclusive terminating zero character
// Length of the stream = 31 byte, Byte length of all PLC variables
const MAX_ADSSTREAM_SIZE = 31;
// max. length of PLC string variable without terminating zero character
const MAX_STRINGVAL_LENGTH = 10;
constructor;
end;
implementation
{$REGION Construction and Disposition}
constructor MainForm;
begin
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
end;
method MainForm.Dispose(disposing: Boolean);
begin
if disposing then begin
if assigned(components) then
components.Dispose();
//
// TODO: Add custom disposition code here
//
end;
inherited Dispose(disposing);
end;
{$ENDREGION}
method MainForm.MainForm_Load(sender: System.Object; e: System.EventArgs);
begin
try
// Creaste instance of class TcAdsClient
tcClient := new TcAdsClient();
// Create connectin with Port 801 on the local PC
tcClient.Connect(801);
// Length of the stream = Byte length of all PLC variables
dataStream := new AdsStream(MAX_ADSSTREAM_SIZE);
// Encoding is set to ASCII, to read strings
binRead := new BinaryReader(dataStream, System.Text.Encoding.ASCII);
// Add notifications
hConnect[0] := tcClient.AddDeviceNotification('MAIN.boolVal', dataStream, 0, 1, AdsTransMode.OnChange, 100, 0, tbBool);
hConnect[1] := tcClient.AddDeviceNotification('MAIN.intVal', dataStream, 1, 2, AdsTransMode.OnChange, 100, 0, tbInt);
hConnect[2] := tcClient.AddDeviceNotification('MAIN.dintVal', dataStream, 3, 4, AdsTransMode.OnChange, 100, 0, tbDint);
hConnect[3] := tcClient.AddDeviceNotification('MAIN.sintVal', dataStream, 7, 1, AdsTransMode.OnChange, 100, 0, tbSint);
hConnect[4] := tcClient.AddDeviceNotification('MAIN.lrealVal', dataStream, 8, 8, AdsTransMode.OnChange, 100, 0, tbLreal);
hConnect[5] := tcClient.AddDeviceNotification('MAIN.realVal', dataStream, 16, 4, AdsTransMode.OnChange, 100, 0, tbReal);
hConnect[6] := tcClient.AddDeviceNotification('MAIN.stringVal', dataStream, 20, MAX_STRINGVAL_LENGTH + 1, AdsTransMode.OnChange, 100, 0, tbString);
// Add notification event handler
tcClient.AdsNotification += tcClient_AdsNotification
except
on err: Exception do
MessageBox.Show(err.Message, err.Source)
end;
end;
method MainForm.MainForm_FormClosing(sender: System.Object; e: System.Windows.Forms.FormClosingEventArgs);
begin
try
// Release notifications
for i : Integer := 0 to 6 do
tcClient.DeleteDeviceNotification(hConnect[i]);
// Remove notification handler
tcClient.AdsNotification -= tcClient_AdsNotification;
except
on err: Exception do
MessageBox.Show(err.Message, err.Source)
end;
// Close connection
tcClient.Dispose();
end;
method MainForm.tcClient_AdsNotification(sender: Object; e: AdsNotificationEventArgs);
begin
var strValue : String := '';
// Convert PLC time stamp to DateTime type
var time := DateTime.FromFileTime(e.TimeStamp);
// Get notification data (PLC value) byte offset
e.DataStream.Position := e.Offset;
// Convert PLC value to string
if ( e.NotificationHandle = hConnect[0]) then
strValue := binRead.ReadBoolean().ToString()
else if( e.NotificationHandle = hConnect[1] ) then
strValue := binRead.ReadInt16().ToString()
else if( e.NotificationHandle = hConnect[2] ) then
strValue := binRead.ReadInt32().ToString()
else if( e.NotificationHandle = hConnect[3] ) then
strValue := binRead.ReadSByte().ToString()
else if( e.NotificationHandle = hConnect[4] ) then
strValue := binRead.ReadDouble().ToString()
else if( e.NotificationHandle = hConnect[5] ) then
strValue := binRead.ReadSingle().ToString()
else if ( e.NotificationHandle = hConnect[6] ) then
begin
strValue := new String(binRead.ReadChars(MAX_STRINGVAL_LENGTH + 1));
// Search for terminating zero character and cut at found position
// Necessary if you want to compare the string to other strings
strValue := strValue.Substring(0, strValue.IndexOf(Char(0)));
end
else
;
TextBox(e.UserData).Text := System.String.Format('DateTime: {0}, {1}ms; {2}', [time, time.Millisecond, strValue]);
end;
end.
PLC program
PROGRAM MAIN
VAR
boolVal : BOOL;
intVal : INT;
dintVal : DINT;
sintVal : SINT;
lrealVal : LREAL;
realVal : REAL;
stringVal : STRING(10);
END_VAR
;
Requirements
Language / IDE | Unpack the sample program |
---|---|
Delphi Prism (Embarcadero Prism XE2, Oxygene for .NET) | |
Delphi for .NET (Borland Developer Studio 2006) |