unit SimplexServerTest;
{$DEFINE SIMPLEX_SECURITY} {$DEFINE SIMPLEX_AUTHENTICATION}
interface
uses SysUtils, Simplex.Server, Simplex.Types, System.Classes;
type TSimplexServerTest = class(SpxServerCallback) private FServer: TOpcUaServer; FStream: TStream; function GetNodeId(ANamespaceIndex: Word; AIdent: SpxUInt32): SpxNodeId; overload; function GetNodeId(ANamespaceIndex: Word; AIdent: SpxString): SpxNodeId; overload; function GetLocalizedText(ALocale, AText: SpxString): SpxLocalizedText; function GetDataValue(AValue: SpxInt32): SpxDataValue; overload; function GetDataValue(AValue: SpxString): SpxDataValue; overload; function GetFileStream(): TStream; private // callbacks function OnAuthorizationUserName(AUserName, APassword: SpxString): SpxBoolean; override; function OnWriteValue(ANodeId: SpxNodeId; AValue: SpxVariant): SpxStatusCode; override; function OnCallMethod(ANodeId, AParentNodeId: SpxNodeId; AInputArguments: SpxVariantArray; out AInputArgumentResults: SpxStatusCodeArray; out AOutputArguments: SpxVariantArray): SpxStatusCode; override; function OnReadHistory(ATimestampsToReturn: SpxTimestampsToReturn; AReleaseContinuationPoints, AIsReadModified: SpxBoolean; AStartTime, AEndTime: SpxDateTime; ANumValuesPerNode: SpxUInt32; AReturnBounds: SpxBoolean; ANodeId: SpxNodeId; AIndexRange: SpxString; ADataEncoding: SpxQualifiedName; AContinuationPoint: SpxByteArray; out AAnswerContinuationPoint: SpxByteArray; out AHistoryData: SpxDataValueArray) : SpxStatusCode; 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); destructor Destroy; override; procedure AddNodes(); function Open(): boolean; procedure Close(); end;
implementation
const MethodIdent = 'MyFolder.MyMethod'; VariableIdent = 'MyFolder.MyVarStr'; TestFileName = 'MyFile.dat';
/// <summary> /// Constructor /// </summary> constructor TSimplexServerTest.Create(AEndpointUrl: string); var ServerConfig: SpxServerConfig; begin inherited Create(); ServerConfig.EndpointUrl := AEndpointUrl; ServerConfig.ApplicationInfo.ApplicationName := 'Simplex OPC UA Server SDK'; ServerConfig.ApplicationInfo.ApplicationUri := 'urn:localhost:Simplex OPC UA:Server SDK'; ServerConfig.ApplicationInfo.ProductUri := 'urn:Simplex OPC UA:Server SDK'; ServerConfig.ApplicationInfo.ManufacturerName := 'Simplex OPC UA'; ServerConfig.ApplicationInfo.SoftwareVersion := '1.0'; ServerConfig.ApplicationInfo.BuildNumber := '123'; ServerConfig.ApplicationInfo.BuildDate := Now; ServerConfig.AllowSecurityNone := True; ServerConfig.AllowSecuritySignAndCrypt := False; ServerConfig.AllowAuthorizationAnonymous := True; ServerConfig.AllowAuthorizationUserName := False; ServerConfig.CertificateFileName := ''; ServerConfig.TrustedCertificatesFolder := ''; {$IFDEF SIMPLEX_SECURITY} ServerConfig.AllowSecuritySignAndCrypt := True; ServerConfig.CertificateFileName := 'ServerCertificate.pfx'; ServerConfig.TrustedCertificatesFolder := 'TrustedCertificates'; {$IFDEF SIMPLEX_AUTHENTICATION} ServerConfig.AllowAuthorizationUserName := True; {$ENDIF} {$ENDIF} ServerConfig.TraceLevel := tlWarning; ServerConfig.Callback := Self; FServer := TOpcUaServer.Create(ServerConfig); FStream := nil; end;
/// <summary> /// Destructor /// </summary> destructor TSimplexServerTest.Destroy(); begin FServer.Free(); if (FStream <> nil) then FStream.Free(); inherited; end;
/// <summary> /// Add nodes for testing /// </summary> procedure TSimplexServerTest.AddNodes(); const Namespace = 'http://www.simplexopcua.com/SimplexOpcUaServer'; var NamespaceIndex: Word; RootNodeId, VarNodeId, TypeNodeId: SpxNodeId; DisplayName, Description: SpxLocalizedText; EnumStrings: SpxLocalizedTextArray; EnumValues: SpxEnumValues; DataType: SpxDataType; InputArguments, OutputArguments: SpxArguments; i: integer; ChangeNodeParams: SpxChangeNodeParams; DataValue: SpxDataValue; begin // Create namespace NamespaceIndex := FServer.CreateNamespace(Namespace);
// Create root folder RootNodeId := GetNodeId(NamespaceIndex, 0); DisplayName := GetLocalizedText('en', 'MyFolder'); Description := GetLocalizedText('en', 'Folder for testing'); FServer.CreateFolder(RootNodeId, 'MyFolder', DisplayName, Description, GetNodeId(0, SpxNodeId_ObjectsFolder));
// Create variable integer type VarNodeId := GetNodeId(NamespaceIndex, 1); DisplayName := GetLocalizedText('en', 'MyVarInt'); Description := GetLocalizedText('en', 'Integer variable for testing'); DataType.ValueRank := SpxValueRanks_Scalar; DataType.BuiltInType := SpxType_Int32; FServer.CreateVariable(VarNodeId, 'MyVarInt', DisplayName, Description, RootNodeId, DataType, False, False);
// Set value of variable DataValue := GetDataValue(12345); FServer.SetVariableValue(VarNodeId, DataValue);
// Create variable string type VarNodeId := GetNodeId(NamespaceIndex, VariableIdent); DisplayName := GetLocalizedText('en', 'MyVarStr'); Description := GetLocalizedText('en', 'String variable for testing'); DataType.ValueRank := SpxValueRanks_Scalar; DataType.BuiltInType := SpxType_String; FServer.CreateVariable(VarNodeId, 'MyVarStr', DisplayName, Description, RootNodeId, DataType, True, True);
// Set value of variable DataValue := GetDataValue('value12345'); FServer.SetVariableValue(VarNodeId, DataValue);
// Create enum type TypeNodeId := GetNodeId(NamespaceIndex, 2); SetLength(EnumStrings, 3); for i := Low(EnumStrings) to High(EnumStrings) do EnumStrings[i] := GetLocalizedText('en', Format('Value%d', [i])); DisplayName := GetLocalizedText('en', 'MyEnumStringsType'); Description := GetLocalizedText('en', 'Enum strings type for testing'); FServer.CreateEnumVariableType(TypeNodeId, 'MyEnumStringsType', DisplayName, Description, EnumStrings);
// Create variable of enum type VarNodeId := GetNodeId(NamespaceIndex, 3); DisplayName := GetLocalizedText('en', 'MyEnumStrings'); Description := GetLocalizedText('en', 'Enumerated variable for testing'); DataType.ValueRank := SpxValueRanks_Scalar; DataType.BuiltInType := SpxType_Enumeration; DataType.EnumTypeNodeId := TypeNodeId; FServer.CreateVariable(VarNodeId, 'MyEnumStrings', DisplayName, Description, RootNodeId, DataType, True, False);
// Create enum type with values TypeNodeId := GetNodeId(NamespaceIndex, 4); SetLength(EnumValues, 3); for i := Low(EnumValues) to High(EnumValues) do begin EnumValues[i].Value := i * 2; EnumValues[i].Name := GetLocalizedText('en', Format('Value%d', [i])); EnumValues[i].Description := GetLocalizedText('en', Format('Value%d', [i])); end; DisplayName := GetLocalizedText('en', 'MyEnumValuesType'); Description := GetLocalizedText('en', 'Enum values type for testing'); FServer.CreateEnumVariableType(TypeNodeId, 'MyEnumValuesType', DisplayName, Description, EnumValues);
// Create variable of enum type with values VarNodeId := GetNodeId(NamespaceIndex, 5); DisplayName := GetLocalizedText('en', 'MyEnumValues'); Description := GetLocalizedText('en', 'Enumerated variable with values for testing'); DataType.ValueRank := SpxValueRanks_Scalar; DataType.BuiltInType := SpxType_Enumeration; DataType.EnumTypeNodeId := TypeNodeId; FServer.CreateVariable(VarNodeId, 'MyEnumValues', DisplayName, Description, RootNodeId, DataType, True, False);
// Create method VarNodeId := GetNodeId(NamespaceIndex, MethodIdent); DisplayName := GetLocalizedText('en', 'MyMethod'); Description := GetLocalizedText('en', 'Method for testing'); SetLength(InputArguments, 2); for i := 0 to Length(InputArguments)-1 do begin InputArguments[i].Name := Format('Argument%d', [i]); InputArguments[i].Description := GetLocalizedText('en', Format('Argument %d for testing', [i])); InputArguments[i].DataType.ValueRank := SpxValueRanks_Scalar; InputArguments[i].DataType.BuiltInType := SpxType_String; end; SetLength(OutputArguments, 1); OutputArguments[0].Name := 'OutArgument1'; OutputArguments[0].Description := GetLocalizedText('en', 'Output argument for testing'); OutputArguments[0].DataType.ValueRank := SpxValueRanks_Scalar; OutputArguments[0].DataType.BuiltInType := SpxType_String; FServer.CreateMethod(VarNodeId, 'MyMethod', DisplayName, Description, RootNodeId, InputArguments, OutputArguments);
// Create file FStream := GetFileStream(); VarNodeId := GetNodeId(NamespaceIndex, 'MyFolder.MyFile'); DisplayName := GetLocalizedText('en', 'MyFile'); Description := GetLocalizedText('en', 'File for testing'); FServer.CreateFile(VarNodeId, 'MyFile', DisplayName, Description, RootNodeId, FStream, True);
// Change node ChangeNodeParams.ChangeType := spxChange_DisplayName; ChangeNodeParams.DisplayName := GetLocalizedText('en', 'MyFile123'); FServer.ChangeNode(VarNodeId, ChangeNodeParams);
// Delete node //FServer.DeleteNode(VarNodeId); end;
/// <summary> /// Open OPC UA server /// </summary> function TSimplexServerTest.Open(): boolean; begin Result := FServer.Open(); end;
/// <summary> /// Close OPC UA server /// </summary> procedure TSimplexServerTest.Close(); begin FServer.Close(); end;
/// <summary> /// Callback - UserName authorization /// </summary> function TSimplexServerTest.OnAuthorizationUserName(AUserName, APassword: SpxString): SpxBoolean; begin Result := (AUserName = 'admin') and (APassword = '123'); end;
/// <summary> /// Callback - write value /// </summary> function TSimplexServerTest.OnWriteValue(ANodeId: SpxNodeId; AValue: SpxVariant): SpxStatusCode; var DataValue: SpxDataValue; begin Result := SpxStatusCode_BadNodeIdUnknown; if (FServer.GetVariableValue(ANodeId, DataValue) = False) then Exit;
DataValue.Value := AValue; DataValue.SourceTimestamp := Now; DataValue.ServerTimestamp := Now;
FServer.SetVariableValue(ANodeId, DataValue); Result := SpxStatusCode_Good; end;
/// <summary> /// Callback - call method /// </summary> function TSimplexServerTest.OnCallMethod(ANodeId, AParentNodeId: SpxNodeId; AInputArguments: SpxVariantArray; out AInputArgumentResults: SpxStatusCodeArray; out AOutputArguments: SpxVariantArray): SpxStatusCode; var i: integer; begin Result := SpxStatusCode_BadNodeIdUnknown; AInputArgumentResults := nil; AOutputArguments := nil; if (ANodeId.IdentifierType <> SpxIdentifierType_String) or (ANodeId.IdentifierString <> MethodIdent) then Exit; Result := SpxStatusCode_Good; SetLength(AOutputArguments, 0);
// must have two string arguments SetLength(AInputArgumentResults, 2); for i := Low(AInputArgumentResults) to High(AInputArgumentResults) do begin if (i >= Length(AInputArguments)) then AInputArgumentResults[i] := SpxStatusCode_BadArgumentsMissing else if (AInputArguments[i].ValueRank <> SpxValueRanks_Scalar) or (AInputArguments[i].BuiltInType <> SpxType_String) then AInputArgumentResults[i] := SpxStatusCode_BadInvalidArgument else AInputArgumentResults[i] := SpxStatusCode_Good; end; for i := Low(AInputArgumentResults) to High(AInputArgumentResults) do if (AInputArgumentResults[i] <> SpxStatusCode_Good) then Exit;
// result is string concatenation SetLength(AOutputArguments, 1); AOutputArguments[0].ValueRank := SpxValueRanks_Scalar; AOutputArguments[0].BuiltInType := SpxType_String; AOutputArguments[0].AsString := AInputArguments[0].AsString + AInputArguments[1].AsString; end;
/// <summary> /// Callback - read history /// </summary> function TSimplexServerTest.OnReadHistory(ATimestampsToReturn: SpxTimestampsToReturn; AReleaseContinuationPoints, AIsReadModified: SpxBoolean; AStartTime, AEndTime: SpxDateTime; ANumValuesPerNode: SpxUInt32; AReturnBounds: SpxBoolean; ANodeId: SpxNodeId; AIndexRange: SpxString; ADataEncoding: SpxQualifiedName; AContinuationPoint: SpxByteArray; out AAnswerContinuationPoint: SpxByteArray; out AHistoryData: SpxDataValueArray): SpxStatusCode; var i: integer; begin Result := SpxStatusCode_BadNodeIdUnknown; AAnswerContinuationPoint := nil; AHistoryData := nil; if (ANodeId.IdentifierType <> SpxIdentifierType_String) or (ANodeId.IdentifierString <> VariableIdent) then Exit; Result := SpxStatusCode_Good;
SetLength(AHistoryData, 10); for i := Low(AHistoryData) to High(AHistoryData) do begin AHistoryData[i].Value.ValueRank := SpxValueRanks_Scalar; AHistoryData[i].Value.BuiltInType := SpxType_String; AHistoryData[i].Value.AsString := Format('History_%d', [i + 1]); AHistoryData[i].StatusCode := SpxStatusCode_Good; AHistoryData[i].SourceTimestamp := Now; AHistoryData[i].SourcePicoseconds := 0; AHistoryData[i].ServerTimestamp := Now; AHistoryData[i].ServerPicoseconds := 0; end; end;
/// <summary> /// Callback - output trace message /// </summary> procedure TSimplexServerTest.OnLog(AMessage: SpxString); begin Writeln(AMessage); end;
/// <summary> /// Callback - output warning message /// </summary> procedure TSimplexServerTest.OnLogWarning(AMessage: SpxString); begin Writeln(Format('[WARNING] %s', [AMessage])); end;
/// <summary> /// Callback - output error message /// </summary> procedure TSimplexServerTest.OnLogError(AMessage: SpxString); begin Writeln(Format('[ERROR] %s', [AMessage])); end;
/// <summary> /// Callback - output exception /// </summary> procedure TSimplexServerTest.OnLogException(AMessage: SpxString; AException: Exception); begin Writeln(Format('[EXCEPTION] %s, E.Message=%s', [AMessage, AException.Message])); end;
/// <summary> /// Get numeric SpxNodeId /// </summary> function TSimplexServerTest.GetNodeId(ANamespaceIndex: Word; AIdent: SpxUInt32): SpxNodeId; begin Result.NamespaceIndex := ANamespaceIndex; Result.IdentifierType := SpxIdentifierType_Numeric; Result.IdentifierNumeric := AIdent; end;
/// <summary> /// Get string SpxNodeId /// </summary> function TSimplexServerTest.GetNodeId(ANamespaceIndex: Word; AIdent: SpxString): SpxNodeId; begin Result.NamespaceIndex := ANamespaceIndex; Result.IdentifierType := SpxIdentifierType_String; Result.IdentifierString := AIdent; end;
/// <summary> /// Get SpxLocalizedText /// </summary> function TSimplexServerTest.GetLocalizedText(ALocale, AText: SpxString): SpxLocalizedText; begin Result.Locale := ALocale; Result.Text := AText; end;
/// <summary> /// Get Int32 data value /// </summary> function TSimplexServerTest.GetDataValue(AValue: SpxInt32): SpxDataValue; begin Result.Value.ValueRank := SpxValueRanks_Scalar; Result.Value.BuiltInType := SpxType_Int32; Result.Value.AsInt32 := AValue; Result.StatusCode := SpxStatusCode_Good; Result.SourceTimestamp := Now; Result.SourcePicoseconds := 0; Result.ServerTimestamp := Now; Result.ServerPicoseconds := 0; end;
/// <summary> /// Get String data value /// </summary> function TSimplexServerTest.GetDataValue(AValue: SpxString): SpxDataValue; begin Result.Value.ValueRank := SpxValueRanks_Scalar; Result.Value.BuiltInType := SpxType_String; Result.Value.AsString := AValue; Result.StatusCode := SpxStatusCode_Good; Result.SourceTimestamp := Now; Result.SourcePicoseconds := 0; Result.ServerTimestamp := Now; Result.ServerPicoseconds := 0; end;
/// <summary> /// Get File stream /// </summary> function TSimplexServerTest.GetFileStream(): TStream; var Buffer: TBytes; i: Integer; begin if FileExists(TestFileName) then Result := TFileStream.Create(TestFileName, fmOpenReadWrite) else begin Result := TMemoryStream.Create(); SetLength(Buffer, 10); for i := Low(Buffer) to High(Buffer) do Buffer[i] := $30 + i; Result.WriteBuffer(Buffer, Length(Buffer)); end; end;
end.
|