Files
WCS/Plugins/Driver/Driver.DeviceOpcUa/ExtensionObjectHelper.cs
2026-03-02 09:08:20 +08:00

240 lines
9.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Opc.Ua;
using Opc.Ua.Client;
using System.Reflection;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Plugin.Driver.DeviceOpcUa;
public static class ExtensionObjectHelper
{
public static string ReadStruct(string address, DataValue value, ISession? session)
{
if (session == null) return string.Empty;
if (value == null || value.Value == null) return string.Empty;
NodeId node = new NodeId(address);
//获取Node 信息 验证Node的数据类型
VariableNode? nodeInfo = session.ReadNode(node) as VariableNode;
if (nodeInfo == null) return string.Empty;
var datatypeNode = (session.ReadNode(nodeInfo.DataType)) as DataTypeNode;
if (datatypeNode == null) return string.Empty;
var typeDefine = datatypeNode.DataTypeDefinition.Body as StructureDefinition;
if (typeDefine == null) return string.Empty;
int index = 0;
if (value.Value is ExtensionObject[])//数组
{
var res = new Dictionary<int, object?>();
var values = value.Value as ExtensionObject[];
if (values == null) return string.Empty;
int i = 0;
foreach (var item in values)
{
var obj = GetJsonFromExtensionObject(address, item, typeDefine, session, ref index);
if (obj != null)
{
res[i] = obj;
}
i++;
}
var jsonOptions = new JsonSerializerOptions
{
WriteIndented = true,
ReferenceHandler = ReferenceHandler.IgnoreCycles,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
return JsonSerializer.Serialize(res, jsonOptions);
}
else //非数组
{
var values = value.Value as ExtensionObject;
if (values == null) return string.Empty;
var obj = GetJsonFromExtensionObject(address, values, typeDefine, session, ref index);
if (obj == null) return string.Empty;
var jsonOptions = new JsonSerializerOptions
{
WriteIndented = true,
ReferenceHandler = ReferenceHandler.IgnoreCycles,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
return JsonSerializer.Serialize(obj, jsonOptions);
}
}
private static object? GetJsonFromExtensionObject(string address, ExtensionObject value, StructureDefinition structure, ISession session, ref int index)
{
var res = new Dictionary<string, object?>();
var data = value.Body as byte[];
if (data == null) return null;
foreach (var field in structure.Fields)
{
if (field.ValueRank == 1)
{
//是数组,需要读取数组长度
string fieldNodeId = $"{address}.{field.Name}"; // 例如:"StationStatus1.AlarmA1"
VariableNode? fieldNode = session.ReadNode(new NodeId(fieldNodeId)) as VariableNode;
if (fieldNode == null)
{
return null;
}
else if (fieldNode.ArrayDimensions != null && fieldNode.ArrayDimensions.Count > 0)
{
int count = (int)BitConverter.ToUInt32(data.Skip(index).Take(4).ToArray(), 0);
index += 4;
for (int i = 0; i < count; i++)
{
res[$"{field.Name}[{i}]"] = BitConverter.ToBoolean(data.Skip(index + i).Take(1).ToArray(), 0);
}
index += count;
}
}
else if (field.DataType.NamespaceIndex == 2)
{
//结构体嵌套
var count = BitConverter.ToUInt32(data.Skip(index).Take(4).ToArray(), 0);
index += 4;
var datatypeNode1 = (session.ReadNode(field.DataType)) as DataTypeNode;
if (datatypeNode1 == null) return null;
var typeDefine = datatypeNode1.DataTypeDefinition.Body as StructureDefinition;
if (typeDefine == null) return null;
for (int i = 0; i < count; i++)
{
res[field.Name + i] = GetJsonFromExtensionObject(address, value, typeDefine, session, ref index);
}
}
else
{
string name = field.Name;
if (field.DataType == DataTypeIds.String)
{
int length = (int)BitConverter.ToUInt32(data.Skip(index).Take(4).ToArray(), 0);
index += 4;
string re = Encoding.Default.GetString(data.Skip(index).Take(length).ToArray());
res[name] = re;
index += length;
}
else if (field.DataType == DataTypeIds.UInt32)
{
UInt32 re = BitConverter.ToUInt32(data.Skip(index).Take(4).ToArray(), 0);
index += 4;
res[name] = re;
}
else if (field.DataType == DataTypeIds.Float)
{
float re = BitConverter.ToSingle(data.Skip(index).Take(4).ToArray(), 0);
index += 4;
res[name] = re;
}
else if (field.DataType == DataTypeIds.Boolean)
{
bool re = BitConverter.ToBoolean(data.Skip(index).Take(1).ToArray());
res[name] = re;
index += 1;
}
else if (field.DataType == DataTypeIds.Double)
{
double re = BitConverter.ToDouble(data.Skip(index).Take(8).ToArray());
res[name] = re;
index += 8;
}
else if (field.DataType == DataTypeIds.Int16)
{
Int16 re = BitConverter.ToInt16(data.Skip(index).Take(2).ToArray());
res[name] = re;
index += 2;
}
else if (field.DataType == DataTypeIds.UInt16)
{
UInt16 re = BitConverter.ToUInt16(data.Skip(index).Take(2).ToArray());
res[name] = re;
index += 2;
}
}
}
return res;
}
/// <summary>
/// 解析ExtensionObject为可序列化的字典对象
/// </summary>
/// <param name="extensionObject">OPC UA扩展对象</param>
/// <returns>解析后的键值对字典</returns>
public static object? ResolveExtensionObject(object extensionObject)
{
if (extensionObject == null)
return null;
try
{
// 适配不同OPC UA库的ExtensionObject类型根据实际情况调整
Type extObjType = extensionObject.GetType();
// 方式1如果是已知的结构体类型直接转换
if (extObjType.IsValueType && !extObjType.IsPrimitive && !extObjType.IsEnum)
{
return ConvertStructToDictionary(extensionObject);
}
// 方式2适配OPC UA标准ExtensionObject根据实际库调整属性名
PropertyInfo? bodyProp = extObjType?.GetProperty("Body") ??
extObjType?.GetProperty("WrappedValue") ??
extObjType?.GetProperty("Value");
if (bodyProp != null)
{
object? bodyValue = bodyProp?.GetValue(extensionObject);
if (bodyValue != null && bodyValue.GetType().IsValueType)
{
return ConvertStructToDictionary(bodyValue);
}
return bodyValue;
}
// 方式3无法解析时返回原始对象最终会序列化为JSON
return extensionObject;
}
catch (Exception ex)
{
// 解析失败时记录异常并返回原始对象
System.Diagnostics.Debug.WriteLine($"解析结构体失败: {ex.Message}");
return extensionObject;
}
}
/// <summary>
/// 将结构体转换为字典便于JSON序列化
/// </summary>
/// <param name="structObj">结构体对象</param>
/// <returns>键值对字典</returns>
private static object? ConvertStructToDictionary(object structObj)
{
if (structObj == null)
return null;
Type structType = structObj.GetType();
var dict = new System.Collections.Generic.Dictionary<string, object?>();
// 获取结构体的所有公共字段和属性
foreach (FieldInfo field in structType.GetFields(BindingFlags.Public | BindingFlags.Instance))
{
dict[field.Name] = field?.GetValue(structObj);
}
foreach (PropertyInfo prop in structType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (prop.CanRead)
{
dict[prop.Name] = prop.GetValue(structObj);
}
}
return dict;
}
}