如何将从客户端上的 opc 输出参数接收的二进制数据转换为 opc 服务器上定义的数据类型



我最近在这里添加了一个问题,帮助我连接到OPC服务器并在服务器上调用方法并检索一些数据。我现在面临的问题是我不知道如何将接收到的数据转换为服务器上定义的数据类型,在我的客户端上以理解该数据。输出参数参数包含数据。列表中的第二项是我正在尝试转换的项目。服务器上输出数据类型的节点 ID 为"ns=2;i=15205"。以下是方法:

public void Connect(string endpointUrl)
{
AppSession.UserIdentity = new UserIdentity();
AppSession.SessionName = "urn:ACGIDT053:UnifiedAutomation:UaExpert";
//AppSession.Application.CertificateGroups[0];            
//string endpointUrl = "opc.tcp://212.43.72.27:51510/UA/WipotecServer/";
AppSession.Connect(endpointUrl, SecuritySelection.None);
ICertificate certificateServer = SecurityUtils.LoadCertificate(AppSession.EndpointDescription.ServerCertificate);
AppSession.Application.TrustedStore.Add(certificateServer, true);
/// [Step 1]
// parse the object id.
NodeId objectId = NodeId.Parse("ns=2;i=15108");
// get the selected method id.            
NodeId methodId = NodeId.Parse("ns=2;i=15057");
List<ExtensionObject> ar = new List<ExtensionObject>();
// get input arguments.
UInt32 a = 1;
List<Variant> inputArguments = new List<Variant>
{
new Variant(string.Empty, TypeInfo.Scalars.String), new Variant(a,TypeInfo.Scalars.UInt32), new Variant(string.Empty, TypeInfo.Scalars.String),
new Variant(ar.ToArray(), TypeInfo.Arrays.ExtensionObject), new Variant(string.Empty, TypeInfo.Scalars.String)
};
/// [Step 2]
List<StatusCode> inputArgumentErrors;
List<Variant> outputArguments = null;            
// call the method on the server.
StatusCode error = AppSession.Call(
objectId,
methodId,
inputArguments,
out inputArgumentErrors,
out outputArguments);

AppSession.Disconnect();
}

更新的代码:

StatusCode error = AppSession.Call(
objectId,
methodId,
inputArguments,
out inputArgumentErrors,
out outputArguments);
List<ReadValueId> readListIds = new List<ReadValueId>();
readListIds.Add(new ReadValueId { NodeId = NodeId.Parse("ns=2;i=15059") });
List<DataValue> dataValues = AppSession.Read(readListIds);

OPC UA 方法节点可能有一些输入和/或输出参数。

这些可以在浏览 OPC UA 方法节点时找到(在您的情况下为"ns=2;i=15057"(

然后,您可以使用 OPC UA 读取服务访问输入或输出参数的值。OpcUa_Argument数组实际上被包装在一个扩展对象数组中!

两个节点的值都是 X 参数数组结构,如下所示:

typedef struct _OpcUa_Argument
{
OpcUa_String        Name;
OpcUa_NodeId        DataType;
OpcUa_Int32         ValueRank;
OpcUa_Int32         NoOfArrayDimensions;
OpcUa_UInt32*       ArrayDimensions;
OpcUa_LocalizedText Description;
}
OpcUa_Argument;

我敢肯定,Camille G.发布的答案对许多人都有效。但是,可悲的是它对我不起作用,因为我可能无法理解它的一些细节。因此,我在UA dot net sdk论坛中投入了一段时间,并提出了一个适合我的解决方案。以下是详细说明:

因此,您从该方法中作为输出获得的复杂对象在 dot net 中属于"对象"类型。可悲的是,返回的复杂数据类型的数据是编码的(我认为它是二进制编码,但我不太确定(。所以基本上要获取解码的值,我们需要使用用于调用opc服务器方法的会话初始化DataTypeManager。然后,您需要将输出参数转换为数据值类型。

以下是对我有用的代码:

StatusCode _errorStatus = _session.Call(_objectId, _methodId, _inputArguments, out _inputArgumentStatus, out _outputArguments);
DataTypeManager _dataTypeManager = new DataTypeManager(_session);
DataValue _dataValue = new DataValue(_outputArguments[1]);
Variant value = _dataValue.WrappedValue;
ExtensionObject _extensionObject = value.ToExtensionObject();
GenericEncodeableObject _genericEncodeable = _dataTypeManager.ParseValue(_extensionObject);
GenericStructureDataType _genericStructuredDataType = _genericEncodeable.TypeDefinition;
object _decodedOutput = RecurssiveDecode(_genericEncodeable, 4); 
/// <summary>
/// Decodes a complex opc ua object
/// </summary>
/// <param name="genericEncodeable">Object to decode.</param>
/// <param name="index">Argument in object to decode and retrieve.</param>
/// <returns></returns>
object RecurssiveDecode(GenericEncodeableObject genericEncodeable, int index = 0)
{
GenericStructureDataType _genericStructuredDataType = genericEncodeable.TypeDefinition;
for (int i = index; i < _genericStructuredDataType.Count; i++)
{
// Get the description of the field (name, data type etc.)
GenericStructureDataTypeField _fieldDescription = _genericStructuredDataType[i];
// Get the value of the field
Variant fieldValue = genericEncodeable[i];
string fieldName = _fieldDescription.Name;
if (_fieldDescription.ValueRank == -1)
{
if (_fieldDescription.TypeDescription.TypeClass == GenericDataTypeClass.Simple)
{
// Print the name and the value
string fieldValueString = fieldValue.ToString();
return fieldValueString;
}
else if (_fieldDescription.TypeDescription.TypeClass == GenericDataTypeClass.Structured)
{
// Print the name and call this method again the child
RecurssiveDecode((GenericEncodeableObject)fieldValue.GetValue<GenericEncodeableObject>(null));
}
}
else if (_fieldDescription.ValueRank == 1)
{
// Print the fields name
if (_fieldDescription.TypeDescription.TypeClass == GenericDataTypeClass.Simple)
{
// Print each element
Array array = fieldValue.Value as Array;
return array;
}
else if (_fieldDescription.TypeDescription.TypeClass == GenericDataTypeClass.Structured)
{
// Call this method again foreach element
ExtensionObjectCollection extensionObjects = fieldValue.ToExtensionObjectArray();                        
foreach (ExtensionObject e in extensionObjects)
{
RecurssiveDecode((GenericEncodeableObject)fieldValue.GetValue<GenericEncodeableObject>(null));
}
}
}
}
return null;
}

RecurssiveDecode(( 方法将获取解码的对象。 这是一个链接,应该详细说明

最新更新