Client code examples


Simplex OPC UA Client

Simplex OPC UA Client as an example of using the Simplex OPC UA SDK (time-limited evaluation version).
Comes with the source code (Delphi XE 10.2, VCL and FireMonkey).
Supported OPC UA Services:
  • CreateSession, ActivateSession, CloseSession
  • Browse
  • Read, HistoryRead
  • Write
  • Call
  • CreateSubscription, DeleteSubscription
  • CreateMonitoredItems, DeleteMonitoredItems
  • Publish

Download

Simple client code example

..
uses SysUtils, Simplex.Client, Simplex.Types;
..

var OpcUaClient: TOpcUaClient;
  ClientConfig: SpxClientConfig;
begin
  try
   
ClientConfig.EndpointUrl := 'opc.tcp://localhost:4848';
   
ClientConfig.SessionTimeout := 300;
   
ClientConfig.TraceLevel := tlNone;
   
ClientConfig.Callback := nil;
   
OpcUaClient:= TOpcUaClient.Create(ClientConfig);

    if (not
OpcUaClient.Connect()) then
    begin
     
OpcUaClient.Free();
      Writeln(Format('Error connect to OPC UA server, EndpointUrl=%s',
        [
ClientConfig.EndpointUrl]));
      Exit;
    end;
    Writeln(Format('Connect to OPC UA server - OK, EndpointUrl=%s',
      [
ClientConfig.EndpointUrl]));

   
OpcUaClient.Free();
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.


Advanced client code example

..
uses SysUtils, Simplex.Client, Simplex.Types;
..

var Client: TSimplexClientTest;
// see details below
  EndpointUrl: string;
begin
  try
    EndpointUrl := 'opc.tcp://localhost:4848';
    Client := TSimplexClientTest.Create(EndpointUrl);

    if (Client.Connect() = False) then
    begin
      Client.Free();
      Writeln(Format('Error connect to OPC UA server, EndpointUrl=%s',
        [EndpointUrl]));
      Exit;
    end;
    Writeln(Format('Connect to OPC UA server - OK, EndpointUrl=%s',
      [EndpointUrl]));

    if (Client.ReadData() = False) then
    begin
      Client.Free();
      Writeln('Error read data from OPC UA server');
      Exit;
    end;
    Writeln('Read data from OPC UA server - OK');

    Client.Free();
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.


SimplexClientTest.pas

unit SimplexClientTest;

{.$.DEFINE OPCUA_SECURITY}
{.$.DEFINE OPCUA_AUTHENTICATION}

interface

uses SysUtils, DateUtils, Simplex.Client, Simplex.Types;

type
  TWriteln = procedure(AInfo: string) of object;
  TProcessMessages = procedure() of object;

  TSimplexClientTest = class(SpxClientCallback)
  private
    FWriteln: TWriteln;
    FProcessMessages: TProcessMessages;
    FClient: TOpcUaClient;
    FClientConfig: SpxClientConfig;
    function ExistServer(AServers: SpxApplicationDescriptionArray): boolean;
    function ExistSecurityNone(AEndpoints: SpxEndpointDescriptionArray): boolean;
    function GetNodeId(ANamespaceIndex: Word; AIdent: Cardinal): SpxNodeId; overload;
    function GetNodeId(ANamespaceIndex: Word; AIdent: string): SpxNodeId; overload;
    function GetReadValueId(ANodeId: SpxNodeId): SpxReadValueId;
    function GetWriteValue(ANodeId: SpxNodeId; AValue: string): SpxWriteValue;
    function GetValue(AValue: string): SpxVariant;
    function GetMonitoredItem(ANodeId: SpxNodeId; AClientHandle: Cardinal;
      ASamplingInterval: double): SpxMonitoredItem;
    function GetModifyItem(AMonitoredItemId: Cardinal; AClientHandle: Cardinal;
      ASamplingInterval: double): SpxModifyMonitoredItem;
    procedure ConsoleWriteln(AInfo: string);
    procedure ConsoleProcessMessages();
  private // callbacks
    procedure OnChannelEvent(AEvent: SpxChannelEvent; AStatus: SpxStatusCode); override;
    procedure OnMonitoredItemChange(ASubscriptionId: SpxUInt32;
      AValues : SpxMonitoredItemNotifyArray); override;
    procedure OnLog(AMessage: string); override;
    procedure OnLogWarning(AMessage: string); override;
    procedure OnLogError(AMessage: string); override;
    procedure OnLogException(AMessage: string; AException: Exception); override;
  public
    constructor Create(AEndpointUrl: string); overload;
    constructor Create(AEndpointUrl: string; AWriteln: TWriteln;
      AProcessMessages: TProcessMessages); overload;
    destructor Destroy; override;
    function Connect(): boolean;
    procedure Disconnect();
    function ReadData(): boolean;
  end;

implementation

const
  FormatDT = 'yy.mm.dd hh:nn:ss.zzz';
  Localhost = 'localhost';
  VariableClientHandle = 1;

/// <summary>
/// Constructor
/// </summary>
constructor TSimplexClientTest.Create(AEndpointUrl: string);
begin
  Create(AEndpointUrl, ConsoleWriteln, ConsoleProcessMessages);
end;

/// <summary>
/// Constructor
/// </summary>
constructor TSimplexClientTest.Create(AEndpointUrl: string; AWriteln: TWriteln;
  AProcessMessages: TProcessMessages);
begin
  inherited Create();
  FWriteln := AWriteln;
  FProcessMessages := AProcessMessages;
  FClientConfig.EndpointUrl := AEndpointUrl;
  FClientConfig.ApplicationInfo.ApplicationName := 'Simplex OPC UA Cllient SDK';
  // application uri from ClientCertificate.der
  FClientConfig.ApplicationInfo.ApplicationUri := 'urn:localhost:Simplex OPC UA:Client SDK';
  FClientConfig.ApplicationInfo.ProductUri := 'urn:Simplex OPC UA:Client SDK';
  FClientConfig.ApplicationInfo.ManufacturerName := 'Simplex OPC UA';
  FClientConfig.ApplicationInfo.SoftwareVersion := '1.0';
  FClientConfig.ApplicationInfo.BuildNumber := '123';
  FClientConfig.ApplicationInfo.BuildDate := Now;
  FClientConfig.CertificateFileName := '';
  FClientConfig.PrivateKeyFileName := '';
  FClientConfig.TrustedCertificatesFolder := '';
  FClientConfig.ServerCertificateFileName := '';
  FClientConfig.Authentication.AuthenticationType := SpxAuthenticationType_Anonymous;
  FClientConfig.Authentication.UserName := '';
  FClientConfig.Authentication.Password := '';
{$IFDEF OPCUA_SECURITY}
  FClientConfig.CertificateFileName := 'ClientCertificate.der';
  FClientConfig.PrivateKeyFileName := 'ClientPrivateKey.pem';
  FClientConfig.TrustedCertificatesFolder := 'TrustedCertificates';
  FClientConfig.ServerCertificateFileName := 'TrustedCertificates\ServerCertificate.der';
{$IFDEF OPCUA_AUTHENTICATION}
  FClientConfig.Authentication.AuthenticationType := SpxAuthenticationType_UserName;
  FClientConfig.Authentication.UserName := 'root';
  FClientConfig.Authentication.Password := 'secret';
{$ENDIF}
{$ENDIF}
  FClientConfig.SessionTimeout := 300;
  FClientConfig.TraceLevel := tlDebug;
  FClientConfig.Callback := Self;
  FClient := TOpcUaClient.Create(FClientConfig);
end;

/// <summary>
/// Destructor
/// </summary>
destructor TSimplexClientTest.Destroy();
begin
  FClient.Free();
  inherited;
end;

/// <summary>
/// Connect to OPC UA server with security mode - none
/// </summary>
function TSimplexClientTest.Connect(): boolean;
var Servers: SpxApplicationDescriptionArray;
  Endpoints: SpxEndpointDescriptionArray;
begin
  Result := False;

  // connect
{$IFDEF OPCUA_SECURITY}
  if (not FClient.Connect(SpxMessageSecurityMode_SignAndEncrypt,
    SpxSecurityPolicy_Basic128Rsa15)) then Exit;
{$ELSE}
  if (not FClient.Connect()) then Exit;
{$ENDIF}

  // exist OPC UA server
  if (not FClient.FindServers(Servers)) then Exit;
  if (not ExistServer(Servers)) then Exit;

  // exist endpoint with security mode - none
  if (not FClient.GetEndpoints(Endpoints)) then Exit;
  if (not ExistSecurityNone(Endpoints)) then Exit;

  // init session
  if (not FClient.InitSession()) then Exit;

  Result := True;
end;

/// <summary>
/// Disconnect from OPC UA server
/// </summary>
procedure TSimplexClientTest.Disconnect();
begin
  FClient.Disconnect();
end;

/// <summary>
/// Read data
/// </summary>
function TSimplexClientTest.ReadData(): boolean;
var NodeId: SpxNodeId;
  ReadValues: SpxReadValueIdArray;
  Values: SpxDataValueArray;
  NodesToRead: SpxHistoryReadValueIdArray;
  HistoryValues: SpxHistoryReadResultArray;
  WriteValues: SpxWriteValueArray;
  StatusCodes: SpxStatusCodeArray;
  MethodsToCall: SpxCallMethodRequestArray;
  MethodResults: SpxCallMethodResultArray;
  BrowseDescriptions: SpxBrowseDescriptionArray;
  BrowseResults: SpxBrowseResultArray;
  Subscription: SpxSubscription;
  SubscriptionResult: SpxSubscriptionResult;
  SubcriptionId, MonitoredItemId: SpxUInt32;
  SubcriptionIds, MonitoredItemIds: SpxUInt32Array;
  MonitoredItems: SpxMonitoredItemArray;
  MonitoredItemResult: SpxMonitoredItemResultArray;
  ModifyItems: SpxModifyMonitoredItemArray;
  ModifyItemResult: SpxModifyMonitoredItemResultArray;
  ContinuationPoints: SpxByteArrayArray;
  i: integer;
  EndTime: TDateTime;
begin
  Result := False;

  // Read current time
  NodeId := GetNodeId(0, SpxNodeId_Server_ServerStatus_CurrentTime);
  SetLength(ReadValues, 1);
  ReadValues[0] := GetReadValueId(NodeId);
  if (not FClient.ReadValue(0, SpxTimestampsToReturn_Both, ReadValues, Values)) then Exit;
  if (Length(Values) = 0) then Exit;
  FWriteln(Format('ReadValue - OK, StatusCode = %u',
    [Ord(Values[0].StatusCode)]));
  FWriteln(Format('CurrentUtcTime = %s',
    [FormatDateTime(FormatDT, Values[0].Value.AsDateTime)]));

  // Read history value from Simplex OPC UA Server SDK - see "Advanced server code example"
  NodeId := GetNodeId(1, 'MyFolder.MyVarStr');
  SetLength(NodesToRead, 1);
  NodesToRead[0].NodeId := NodeId;
  NodesToRead[0].IndexRange := '';
  NodesToRead[0].DataEncoding.NamespaceIndex := 0;
  NodesToRead[0].DataEncoding.Name := '';
  NodesToRead[0].ContinuationPoint := nil;
  if (not FClient.ReadHistory(False, IncHour(Now, -3), Now, 1000, True,
    SpxTimestampsToReturn_Both, False, NodesToRead, HistoryValues)) then Exit;
  if (Length(HistoryValues) = 0) then Exit;
  FWriteln(Format('ReadHistory - OK, StatusCode = %u',
    [Ord(HistoryValues[0].StatusCode)]));
  for i := Low(HistoryValues[0].HistoryData) to High(HistoryValues[0].HistoryData) do
    FWriteln(Format('HistoryValue[%d] = %s',
      [i, HistoryValues[0].HistoryData[i].Value.AsString]));

  // Write value to Simplex OPC UA Server SDK - see "Advanced server code example"
  NodeId := GetNodeId(1, 'MyFolder.MyVarStr');
  SetLength(WriteValues, 1);
  WriteValues[0] := GetWriteValue(NodeId, 'value123');
  if (not FClient.WriteValue(WriteValues, StatusCodes)) then Exit;
  if (Length(StatusCodes) = 0) then Exit;
  FWriteln(Format('WriteValue - OK, StatusCode = %u',
    [Ord(StatusCodes[0])]));

  // Call method from Simplex OPC UA Server SDK - see "Advanced server code example"
  NodeId := GetNodeId(1, 'MyFolder.MyMethod');
  SetLength(MethodsToCall, 1);
  MethodsToCall[0].MethodId :=  NodeId;
  MethodsToCall[0].ObjectId := GetNodeId(1, 0);
  SetLength(MethodsToCall[0].InputArguments, 2);
  MethodsToCall[0].InputArguments[0] := GetValue('One');
  MethodsToCall[0].InputArguments[1] := GetValue('Two');
  if (not FClient.CallMethod(MethodsToCall, MethodResults)) then Exit;
  if (Length(MethodResults) = 0) then Exit;
  FWriteln(Format('CallMethod - OK, StatusCode = %u',
    [Ord(MethodResults[0].StatusCode)]));
  for i := Low(MethodResults[0].OutputArguments) to High(MethodResults[0].OutputArguments) do
    FWriteln(Format('MethodOutputArgument[%d] = %s',
      [i, MethodResults[0].OutputArguments[i].AsString]));

  // Browse from Root
  ContinuationPoints := nil;
  SetLength(BrowseDescriptions, 1);
  BrowseDescriptions[0].NodeId := GetNodeId(0, SpxNodeId_RootFolder);
  BrowseDescriptions[0].BrowseDirection := SpxBrowseDirection_Forward;
  BrowseDescriptions[0].ReferenceTypeId := GetNodeId(0, SpxNodeId_HierarchicalReferences);
  BrowseDescriptions[0].IncludeSubtypes := True;
  BrowseDescriptions[0].NodeClassMask := SpxUInt32(SpxNodeClass_Unspecified);
  BrowseDescriptions[0].ResultMask := SpxUInt32(SpxBrowseResultMask_All);
  while True do
  begin
    if (Length(ContinuationPoints) = 0) then
    begin
      if (not FClient.Browse(1000, BrowseDescriptions, BrowseResults)) then Exit;
    end
    else begin
      if (not FClient.BrowseNext(False, ContinuationPoints, BrowseResults)) then Exit;
    end;
    if (Length(BrowseResults) = 0) then Exit;
    FWriteln(Format('Browse - OK, StatusCode = %u',
      [Ord(BrowseResults[0].StatusCode)]));
    for i := Low(BrowseResults[0].References) to High(BrowseResults[0].References) do
      FWriteln(Format('BrowseDisplayName[%d] = %s',
        [i, BrowseResults[0].References[i].DisplayName.Text]));

    // continue browse (if more than 1000 references)
    if (Length(BrowseResults[0].ContinuationPoint) > 0) then
    begin
      SetLength(ContinuationPoints, 1);
      ContinuationPoints[0] := BrowseResults[0].ContinuationPoint;
    end
    else Break;
  end;

  // Create subscription
  Subscription.RequestedPublishingInterval := 2000;
  Subscription.RequestedLifetimeCount := 200;
  Subscription.RequestedMaxKeepAliveCount := 20;
  Subscription.MaxNotificationsPerPublish := 20000;
  Subscription.Priority := 0;
  if (not FClient.CreateSubscription(True, Subscription, SubcriptionId,
    SubscriptionResult)) then Exit;
  FWriteln(Format('CreateSubscription - OK, SubscriptionId = %d, '+
    'RevisedPublishingInterval=%f, RevisedLifetimeCount=%d, RevisedMaxKeepAliveCount=%d',
    [SubcriptionId, SubscriptionResult.RevisedPublishingInterval,
     SubscriptionResult.RevisedLifetimeCount, SubscriptionResult.RevisedMaxKeepAliveCount]));

  // Modify subscription
  Subscription.RequestedPublishingInterval := 3000;
  Subscription.RequestedLifetimeCount := 300;
  Subscription.RequestedMaxKeepAliveCount := 30;
  Subscription.MaxNotificationsPerPublish := 30000;
  Subscription.Priority := 1;
  if (not FClient.ModifySubscriptiont(SubcriptionId, Subscription,
    SubscriptionResult)) then Exit;
  FWriteln(Format('ModifySubscriptiont - OK, RevisedPublishingInterval=%f, '+
    'RevisedLifetimeCount=%d, RevisedMaxKeepAliveCount=%d',
    [SubscriptionResult.RevisedPublishingInterval, SubscriptionResult.RevisedLifetimeCount,
     SubscriptionResult.RevisedMaxKeepAliveCount]));

  // Create monitored item
  NodeId := GetNodeId(0, SpxNodeId_Server_ServerStatus_CurrentTime);
  SetLength(MonitoredItems, 1);
  MonitoredItems[0]  := GetMonitoredItem(NodeId, VariableClientHandle, 500);
  if (not FClient.CreateMonitoredItems(SubcriptionId, SpxTimestampsToReturn_Both,
    MonitoredItems, MonitoredItemResult)) then Exit;
  if (Length(MonitoredItemResult) = 0) then Exit;
  FWriteln(Format('CreateMonitoredItems - OK, StatusCode=%u, MonitoredItemId=%d, '+
    'SamplingInterval=%f, QueueSize=%d',
    [MonitoredItemResult[0].StatusCode, MonitoredItemResult[0].MonitoredItemId,
     MonitoredItemResult[0].SamplingInterval, MonitoredItemResult[0].QueueSize]));
  MonitoredItemId := MonitoredItemResult[0].MonitoredItemId;

  // Modify monitored item
  SetLength(ModifyItems, 1);
  ModifyItems[0] := GetModifyItem(MonitoredItemId, VariableClientHandle, 600);
  if (not FClient.ModifyMonitoredItems(SubcriptionId, SpxTimestampsToReturn_Both,
    ModifyItems, ModifyItemResult)) then Exit;
  if (Length(ModifyItemResult) = 0) then Exit;
  FWriteln(Format('ModifyMonitoredItems - OK, StatusCode=%u, SamplingInterval=%f, '+
    'QueueSize=%d',
    [ModifyItemResult[0].StatusCode, ModifyItemResult[0].SamplingInterval,
     ModifyItemResult[0].QueueSize]));

  // Set monitoring mode
  SetLength(MonitoredItemIds, 1);
  MonitoredItemIds[0] := MonitoredItemId;
  if (not FClient.SetMonitoringMode(SubcriptionId, SpxMonitoringMode_Reporting,
    MonitoredItemIds, StatusCodes)) then Exit;
  if (Length(StatusCodes) = 0) then Exit;
  FWriteln(Format('SetMonitoringMode - OK, StatusCode=%u',
    [StatusCodes[0]]));

  // Wait for receive data
  EndTime :=  IncSecond(Now, 30);
  while (Now < EndTime) do
    FProcessMessages();

  // Delete monitored item
  if (not FClient.DeleteMonitoredItems(SubcriptionId, MonitoredItemIds,
    StatusCodes)) then Exit;
  if (Length(StatusCodes) = 0) then Exit;
  FWriteln(Format('DeleteMonitoredItems - OK, StatusCode=%u',
    [StatusCodes[0]]));

  // Delete subscription
  SetLength(SubcriptionIds, 1);
  SubcriptionIds[0] := SubcriptionId;
  if (not FClient.DeleteSubscriptions(SubcriptionIds, StatusCodes)) then Exit;
  if (Length(StatusCodes) = 0) then Exit;
  FWriteln(Format('DeleteSubscriptions- OK, StatusCode = %u',
    [Ord(StatusCodes[0])]));

  Result := True;
end;

/// <summary>
/// Callback - change channel state
/// </summary>
procedure TSimplexClientTest.OnChannelEvent(AEvent: SpxChannelEvent; AStatus: SpxStatusCode);
begin
  if (AEvent = SpxChannelEvent_Disconnected) then
    FWriteln('OnChannelEvent: Disconnected');
end;

/// <summary>
/// Callback - change monitored item
/// </summary>
procedure TSimplexClientTest.OnMonitoredItemChange(ASubscriptionId: SpxUInt32;
  AValues : SpxMonitoredItemNotifyArray);
var i: integer;
begin
  FWriteln(Format('OnMonitoredItemChange: Values.Count=%d',
    [Length(AValues)]));
  for i := Low(AValues) to High(AValues) do
    if (AValues[i].ClientHandle = VariableClientHandle) then
      FWriteln(Format('CurrentUtcTime = %s',
        [FormatDateTime(FormatDT, AValues[i].Value.Value.AsDateTime)]));
end;

/// <summary>
/// Callback - output trace message
/// </summary>
procedure TSimplexClientTest.OnLog(AMessage: SpxString);
begin
  FWriteln(AMessage);
end;

/// <summary>
/// Callback - output warning message
/// </summary>
procedure TSimplexClientTest.OnLogWarning(AMessage: SpxString);
begin
  FWriteln(Format('[WARNING] %s',
    [AMessage]));
end;

/// <summary>
/// Callback - output error message
/// </summary>
procedure TSimplexClientTest.OnLogError(AMessage: SpxString);
begin
  FWriteln(Format('[ERROR] %s',
    [AMessage]));
end;

/// <summary>
/// Callback - output exception
/// </summary>
procedure TSimplexClientTest.OnLogException(AMessage: SpxString; AException: Exception);
begin
  FWriteln(Format('[EXCEPTION] %s, E.Message=%s',
    [AMessage, AException.Message]));
end;

/// <summary>
/// Exist OPC UA server
/// </summary>
function TSimplexClientTest.ExistServer(AServers: SpxApplicationDescriptionArray): boolean;
var i: integer;
begin
  Result := False;
  for i := Low(AServers) to High(AServers) do
    if (AServers[i].ApplicationType = SpxApplicationType_Server) or
      (AServers[i].ApplicationType = SpxApplicationType_ClientAndServer) then
    begin
      Result := True;
      Exit;
    end;
end;

/// <summary>
/// Exist endpoint with security mode - none
/// </summary>
function TSimplexClientTest.ExistSecurityNone(AEndpoints: SpxEndpointDescriptionArray): boolean;
var i: integer;
begin
  Result := False;
  for i := Low(AEndpoints) to High(AEndpoints) do
    if (AEndpoints[i].SecurityMode = SpxMessageSecurityMode_None) then
    begin
      Result := True;
      Exit;
    end;
end;

/// <summary>
/// Get numeric SpxNodeId
/// </summary>
function TSimplexClientTest.GetNodeId(ANamespaceIndex: Word; AIdent: Cardinal): SpxNodeId;
begin
  Result.NamespaceIndex := ANamespaceIndex;
  Result.IdentifierType := SpxIdentifierType_Numeric;
  Result.IdentifierNumeric := AIdent;
end;

/// <summary>
/// Get string SpxNodeId
/// </summary>
function TSimplexClientTest.GetNodeId(ANamespaceIndex: Word; AIdent: string): SpxNodeId;
begin
  Result.NamespaceIndex := ANamespaceIndex;
  Result.IdentifierType := SpxIdentifierType_String;
  Result.IdentifierString := AIdent;
end;

/// <summary>
/// Get parameters for read value
/// </summary>
function TSimplexClientTest.GetReadValueId(ANodeId: SpxNodeId): SpxReadValueId;
begin
  Result.NodeId := ANodeId;
  Result.AttributeId := SpxAttributes_Value;
  Result.IndexRange := '';
  Result.DataEncoding.NamespaceIndex := 0;
  Result.DataEncoding.Name := '';
end;

/// <summary>
/// Get parameters for write value
/// </summary>
function TSimplexClientTest.GetWriteValue(ANodeId: SpxNodeId; AValue: string): SpxWriteValue;
begin
  Result.NodeId := ANodeId;
  Result.AttributeId := SpxAttributes_Value;
  Result.IndexRange := '';
  Result.Value.Value := GetValue(AValue);
  Result.Value.StatusCode := SpxStatusCode_Good;
  Result.Value.SourceTimestamp := 0;
  Result.Value.SourcePicoseconds := 0;
  Result.Value.ServerTimestamp := 0;
  Result.Value.ServerPicoseconds := 0;
end;

/// <summary>
/// Get value
/// </summary>
function TSimplexClientTest.GetValue(AValue: string): SpxVariant;
begin
  Result.ValueRank := SpxValueRanks_Scalar;
  Result.BuiltInType := SpxType_String;
  Result.AsString := AValue;
end;

/// <summary>
/// Get monitored item
/// </summary>
function TSimplexClientTest.GetMonitoredItem(ANodeId: SpxNodeId;
  AClientHandle: Cardinal; ASamplingInterval: double): SpxMonitoredItem;
begin
  Result.NodeId := ANodeId;
  Result.AttributeId := SpxAttributes_Value;
  Result.IndexRange := '';
  Result.DataEncoding.NamespaceIndex := 0;
  Result.DataEncoding.Name := '';
  Result.MonitoringMode := SpxMonitoringMode_Reporting;
  Result.ClientHandle := AClientHandle;
  Result.SamplingInterval := ASamplingInterval;
  Result.QueueSize := 1000;
  Result.DiscardOldest := True;
end;

/// <summary>
/// Get modify monitored item
/// </summary>
function TSimplexClientTest.GetModifyItem(AMonitoredItemId: Cardinal;
  AClientHandle: Cardinal; ASamplingInterval: double): SpxModifyMonitoredItem;
begin
  Result.MonitoredItemId := AMonitoredItemId;
  Result.ClientHandle := AClientHandle;
  Result.SamplingInterval := ASamplingInterval;
  Result.DiscardOldest := True;
end;

/// <summary>
/// Console output
/// </summary>
procedure TSimplexClientTest.ConsoleWriteln(AInfo: string);
begin
  Writeln(AInfo);
end;

/// <summary>
/// Console process messages
/// </summary>
procedure TSimplexClientTest.ConsoleProcessMessages();
begin
  Sleep(100);
end;

end.


Add license code example

1) Copy the license file (license.lic) to the Delphi project directory.

2)
Create an text file SimplexLecense.RC, then add line like:
    SimplexLicense RCDATA license.lic

3) Add file SimplexLecense.RC to you Delphi project.

4)
Use the following code to add licenses:

...
uses Classes, Windows, Simplex.License;
...
function AddLicense(): boolean;
var Stream: TResourceStream;
  License: TBytes;
begin
  Result := False;
  try
    Stream := TResourceStream.Create(hInstance, 'SimplexLicense', RT_RCDATA);
    SetLength(License, Stream.Size);
    Stream.Read(License[0], Stream.Size);
    Stream.Free();

    if (AddLicence(License) = False) then Exit;

    Result := True;
  except
  end;
end;