• C# OPCUA 读写结构体


    OPCUA结构体的读写说白了就是对ExtensionObject中按规则对byte的转换

    读取步骤:

    1.首先可以先用UAExpert查看结构体

    2.读取出结构体DataValue的值

    3.把读取出来的值转换成ExtensionObject[]

    4.把ExtensionObject中每项进行解析。

    具体步骤解析:

    1.首先可以先用UAExpert查看

            如图1我们能看到有一个结构体的数据类型ExtensionObject。这个结构体的有5项,且每个字段都是什么。(这里主要验证我们后期自己读取的数据对不对)

     图1

    2.读取出结构体DataValue的值

            这里我们通过session中的ReadValue()方法读取出结构体DataValue的值,这里和正常的已知nodeID读值方法一样(怎么建立连接,大家可以搜随便搜一下就能找到,或者也可以看我前面OPCUA的文章)。

               DataValue item= m_session.ReadValue(new NodeId(ItemAdress));
    

    3.把读取出来的值转换成ExtensionObject[]       

            读取后我们可以看到value的值,如图2,它是一个ExtensionObject类型长为100的数据。而且每项都是byte[],我们要做的就是如何把byte按照规则转换成字符

     4.把ExtensionObject中每项进行解析。

            首先我们先得到ExtensionObject中的结构体的项,下面代码中typeDefine中就能看到结构体中的所有项,大家可以打个断点看看

    1. NodeId node = new NodeId(ItemAdress);
    2. //获取Node 信息 验证Node的数据类型
    3. VariableNode nodeInfo = m_session.ReadNode(node) as VariableNode;
    4. //DataValue value = m_session.ReadValue(node);
    5. var datatypeNode = (m_session.ReadNode(nodeInfo.DataType)) as DataTypeNode;
    6. var typeDefine = datatypeNode.DataTypeDefinition.Body as StructureDefinition;

            然后就是遍历所有项,其中GetJsonFromExtensionObject就是对每项就行解析

    1. if (value.Value is ExtensionObject[])//数组
    2. {
    3. JArray res = new JArray();
    4. foreach (var item in value.Value as ExtensionObject[])
    5. {
    6. res.Add(GetJsonFromExtensionObject(item, typeDefine,ref index));
    7. }
    8. return res.ToString();
    9. }

            最后对每项项进行解析时,要和相关人员确定byte[]拼接的规则。比如我这里如果该项是字符串,那么首先需要先读取该字符串所占的长度,然后根据长度读取字符串。当然具体项目中的规则也可能不完全相同,但是思路是一样的。如果是int,float直接读取4个byte,bool读取1个byte,double读取8个byte,根据一个类型占多少位决定。下面代码可供参考

    1. private JObject GetJsonFromExtensionObject(ExtensionObject value, StructureDefinition structure,ref int index)
    2. {
    3. JObject res = new JObject();
    4. var data = value.Body as byte[];
    5. foreach (var field in structure.Fields)
    6. {
    7. if (field.DataType.NamespaceIndex==2)
    8. {
    9. var count = BitConverter.ToUInt32(data.Skip(index).Take(4).ToArray(), 0);
    10. index += 4;
    11. var datatypeNode1 = (m_session.ReadNode(field.DataType)) as DataTypeNode;
    12. var typeDefine = datatypeNode1.DataTypeDefinition.Body as StructureDefinition;
    13. for (int i = 0; i < count; i++)
    14. {
    15. res[field.Name+i] =GetJsonFromExtensionObject(value, typeDefine,ref index);
    16. }
    17. }
    18. string name = field.Name;
    19. if (field.DataType == DataTypeIds.String)
    20. {
    21. int length = (int)BitConverter.ToUInt32(data.Skip(index).Take(4).ToArray(), 0);//读取字符串所占长度
    22. index += 4;
    23. string re = Encoding.Default.GetString(data.Skip(index).Take(length).ToArray());//根据字符串长度读取字符串
    24. res[name] = re;
    25. index += length;
    26. }
    27. if (field.DataType == DataTypeIds.UInt32)
    28. {
    29. UInt32 re = BitConverter.ToUInt32(data.Skip(index).Take(4).ToArray(), 0);
    30. index += 4;
    31. res[name] = re;
    32. }
    33. if (field.DataType == DataTypeIds.Float)
    34. {
    35. float re = BitConverter.ToSingle(data.Skip(index).Take(4).ToArray(), 0);
    36. index += 4;
    37. res[name] = re;
    38. }
    39. if (field.DataType == DataTypeIds.Boolean)
    40. {
    41. bool re = BitConverter.ToBoolean(data.Skip(index).Take(1).ToArray());
    42. res[name] = re;
    43. index += 1;
    44. }
    45. if (field.DataType == DataTypeIds.Double)
    46. {
    47. double re = BitConverter.ToDouble(data.Skip(index).Take(8).ToArray());
    48. res[name] = re;
    49. index += 8;
    50. }
    51. }
    52. return res;
    53. }

    读取结构体完整代码如下:

    1. public string structItem(string ItemAdress)
    2. {
    3. DataValue item= m_session.ReadValue(new NodeId(ItemAdress));
    4. string aaa = ReadStruct(ItemAdress, item);
    5. return aaa;
    6. }
    7. ///
    8. /// 读取结构体数据
    9. ///
    10. public string ReadStruct(string ItemAdress, DataValue value)
    11. {
    12. #region 得到ExtensionObject中的结构体的项
    13. NodeId node = new NodeId(ItemAdress);
    14. //获取Node 信息 验证Node的数据类型
    15. VariableNode nodeInfo = m_session.ReadNode(node) as VariableNode;
    16. //DataValue value = m_session.ReadValue(node);
    17. var datatypeNode = (m_session.ReadNode(nodeInfo.DataType)) as DataTypeNode;
    18. var typeDefine = datatypeNode.DataTypeDefinition.Body as StructureDefinition;
    19. #endregion
    20. int index = 0;
    21. if (value.Value is ExtensionObject[])//数组
    22. {
    23. JArray res = new JArray();
    24. foreach (var item in value.Value as ExtensionObject[])
    25. {
    26. res.Add(GetJsonFromExtensionObject(item, typeDefine,ref index));
    27. }
    28. return res.ToString();
    29. }
    30. else //非数组
    31. {
    32. return GetJsonFromExtensionObject(value.Value as ExtensionObject, typeDefine, ref index).ToString();
    33. }
    34. }
    35. private JObject GetJsonFromExtensionObject(ExtensionObject value, StructureDefinition structure,ref int index)
    36. {
    37. JObject res = new JObject();
    38. var data = value.Body as byte[];
    39. foreach (var field in structure.Fields)
    40. {
    41. if (field.DataType.NamespaceIndex==2)
    42. {
    43. var count = BitConverter.ToUInt32(data.Skip(index).Take(4).ToArray(), 0);
    44. index += 4;
    45. var datatypeNode1 = (m_session.ReadNode(field.DataType)) as DataTypeNode;
    46. var typeDefine = datatypeNode1.DataTypeDefinition.Body as StructureDefinition;
    47. for (int i = 0; i < count; i++)
    48. {
    49. res[field.Name+i] =GetJsonFromExtensionObject(value, typeDefine,ref index);
    50. }
    51. }
    52. string name = field.Name;
    53. if (field.DataType == DataTypeIds.String)
    54. {
    55. int length = (int)BitConverter.ToUInt32(data.Skip(index).Take(4).ToArray(), 0);//读取字符串所占长度
    56. index += 4;
    57. string re = Encoding.Default.GetString(data.Skip(index).Take(length).ToArray());//根据字符串长度读取字符串
    58. res[name] = re;
    59. index += length;
    60. }
    61. if (field.DataType == DataTypeIds.UInt32)
    62. {
    63. UInt32 re = BitConverter.ToUInt32(data.Skip(index).Take(4).ToArray(), 0);
    64. index += 4;
    65. res[name] = re;
    66. }
    67. if (field.DataType == DataTypeIds.Float)
    68. {
    69. float re = BitConverter.ToSingle(data.Skip(index).Take(4).ToArray(), 0);
    70. index += 4;
    71. res[name] = re;
    72. }
    73. if (field.DataType == DataTypeIds.Boolean)
    74. {
    75. bool re = BitConverter.ToBoolean(data.Skip(index).Take(1).ToArray());
    76. res[name] = re;
    77. index += 1;
    78. }
    79. if (field.DataType == DataTypeIds.Double)
    80. {
    81. double re = BitConverter.ToDouble(data.Skip(index).Take(8).ToArray());
    82. res[name] = re;
    83. index += 8;
    84. }
    85. }
    86. return res;
    87. }

    写入步骤:

    1.首先可以先用UAExpert查看结构体

    2.把每项的结构体转成把ExtensionObject,加入到ExtensionObject[]

    3.把ExtensionObject[]写入即可

    写入结构体的难点就是如何按照规则(就是拼接方式)把结构体转成byte[]然后加入到ExtensionObject[]

    1.首先可以先用UAExpert查看结构体

            同读取,省略

    2.把每项的结构体转成把ExtensionObject,加入到ExtensionObject[]

            首先定义一个结构体类,同UAExpert查看结构体,注意结构体顺序也不能乱

            

    1. ///
    2. /// writevar对象
    3. ///
    4. public class WriteVar
    5. {
    6. ///
    7. /// 数组索引
    8. ///
    9. public int index = 0;
    10. ///
    11. /// 数据项名称
    12. ///
    13. public string Name = "";
    14. ///
    15. /// 数据类型
    16. ///
    17. public int DataType = 0;
    18. ///
    19. /// 字符串值
    20. ///
    21. public string StringValue = "";
    22. ///
    23. /// 整数值
    24. ///
    25. public int IntValue = 0;
    26. ///
    27. /// 浮点数值
    28. ///
    29. public float FloatValue = 0.0F;
    30. }

            然后赋值给结构体,并转成ExtensionObject,这里我定义的规则就是如果是字符串就要把长度也拼接上

    1. WriteVar structs = new WriteVar
    2. {
    3. index = 4,
    4. Name = "重量",
    5. DataType = 0,
    6. StringValue = "",
    7. IntValue = 25,
    8. FloatValue = 0.0F
    9. };
    10. var result = Pro_OPCUA.GetValueByteFromObj(structs);
    11. ///
    12. /// 从对象中获取对象所有的值,并转化为byte[]
    13. ///
    14. /// 需要写入节点的值组成的byte数组列表
    15. ///
    16. public static ExtensionObject GetValueByteFromObj(WriteVar value)
    17. {
    18. List<byte> result = new List<byte>();
    19. //前4位为字符串数据长度
    20. var length = BitConverter.GetBytes(value.Name.Length);
    21. result.AddRange(length);
    22. //数据项名称
    23. var Name = Encoding.Default.GetBytes(value.Name);
    24. result.AddRange(Name);
    25. //数据类型
    26. var DataType = BitConverter.GetBytes(value.DataType);
    27. result.AddRange(DataType);
    28. //字符串数据转换
    29. if (value.StringValue.Length==0)
    30. {
    31. result.AddRange(new byte[4] { 0,0,0,0});
    32. }
    33. else
    34. {
    35. //前4位为字符串数据长度
    36. length = BitConverter.GetBytes(value.StringValue.Length);
    37. result.AddRange(length);
    38. //字符串数据
    39. var StringValue = Encoding.Default.GetBytes(value.StringValue);
    40. result.AddRange(StringValue);
    41. }
    42. //整数数据
    43. var IntValue = BitConverter.GetBytes(value.IntValue);
    44. result.AddRange(IntValue);
    45. //浮点数数据
    46. var FloatValue = BitConverter.GetBytes(value.FloatValue);
    47. result.AddRange(FloatValue);
    48. return new ExtensionObject {Body= result.ToArray() } ;
    49. }

    3.把ExtensionObject[]写入即可

            其实拼接好ExtensionObject[]后按照通用写OPCUA的方法就可以了

            

    1. }
    2. ///
    3. /// OPCUATag写入
    4. ///
    5. /// 需要写入节点的值组成的byte数组列表
    6. ///
    7. public void WriteVarStruct(ExtensionObject[] value,string nodeId)
    8. {
    9. WriteValueCollection nodesToWrite = new WriteValueCollection();
    10. nodesToWrite.Add(new WriteValue()
    11. {
    12. NodeId = new NodeId(nodeId),
    13. AttributeId = Attributes.Value,
    14. Value = new DataValue()
    15. {
    16. Value = value
    17. }
    18. });
    19. WriteNode(nodesToWrite);
    20. }
    21. ///
    22. /// OPCUATag写入
    23. ///
    24. /// 需要写入节点的地址
    25. /// 需要写入节点的值
    26. ///
    27. public bool WriteNode(WriteValueCollection value)
    28. {
    29. try
    30. {
    31. //if (isCconnect)
    32. //{
    33. StatusCodeCollection resultsValues = null;
    34. DiagnosticInfoCollection diagnosticInfos = null;
    35. //Console.WriteLine("数据读取开始");
    36. // Call Read Service
    37. var result = m_session.Write(
    38. null,
    39. value,
    40. out resultsValues,
    41. out diagnosticInfos);
    42. //写入返回值为bad,则写入失败
    43. if (StatusCode.IsBad(result.ServiceResult))
    44. {
    45. return false;
    46. }
    47. //验证结果
    48. ClientBase.ValidateResponse(resultsValues, value);
    49. return true;
    50. //}
    51. //return false;
    52. }
    53. catch (Exception ex)
    54. {
    55. throw new Exception("OPUAReadNodes:" + ex.Message + ex.StackTrace);
    56. }
    57. }

    写结构体的完整代码:

    1. private void button6_Click(object sender, EventArgs e)
    2. {
    3. ExtensionObject[] AA = new ExtensionObject[100];
    4. for (int i = 0; i < AA.Length; i++)
    5. {
    6. AA[i] = new ExtensionObject { Body = getNewByte() };
    7. }
    8. WriteVar structs = new WriteVar
    9. {
    10. index = 4,
    11. Name = "重量",
    12. DataType = 0,
    13. StringValue = "",
    14. IntValue = 25,
    15. FloatValue = 0.0F
    16. };
    17. var result = Pro_OPCUA.GetValueByteFromObj(structs);
    18. AA[4] = result;
    19. float a = 1.23F;
    20. WriteVar structs1 = new WriteVar
    21. {
    22. index = 5,
    23. Name = "形状",
    24. DataType = 1,
    25. StringValue = "",
    26. IntValue = 0,
    27. FloatValue = a
    28. };
    29. var result1 = Pro_OPCUA.GetValueByteFromObj(structs1);
    30. AA[5] = result1;
    31. WriteVar structs2 = new WriteVar
    32. {
    33. index = 6,
    34. Name = "C",
    35. DataType = 2,
    36. StringValue = "WUXE",
    37. IntValue = 0,
    38. FloatValue = 0.0F
    39. };
    40. var result2= Pro_OPCUA.GetValueByteFromObj(structs2);
    41. AA[6] = result2;
    42. //byte[] re = new byte[23] { 3,0,0,0,56,56,56,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0};
    43. string aaa= AA.ToString();
    44. pro_OPCUAp.WriteVarStruct(AA, "ns=2;s=writeStrucat");
    45. }
    46. ///
    47. /// OPCUATag写入
    48. ///
    49. /// 需要写入节点的值组成的byte数组列表
    50. ///
    51. public void WriteVarStruct(ExtensionObject[] value,string nodeId)
    52. {
    53. WriteValueCollection nodesToWrite = new WriteValueCollection();
    54. nodesToWrite.Add(new WriteValue()
    55. {
    56. NodeId = new NodeId(nodeId),
    57. AttributeId = Attributes.Value,
    58. Value = new DataValue()
    59. {
    60. Value = value
    61. }
    62. });
    63. WriteNode(nodesToWrite);
    64. }
    65. ///
    66. /// OPCUATag写入
    67. ///
    68. /// 需要写入节点的地址
    69. /// 需要写入节点的值
    70. ///
    71. public bool WriteNode(WriteValueCollection value)
    72. {
    73. try
    74. {
    75. //if (isCconnect)
    76. //{
    77. StatusCodeCollection resultsValues = null;
    78. DiagnosticInfoCollection diagnosticInfos = null;
    79. //Console.WriteLine("数据读取开始");
    80. // Call Read Service
    81. var result = m_session.Write(
    82. null,
    83. value,
    84. out resultsValues,
    85. out diagnosticInfos);
    86. //写入返回值为bad,则写入失败
    87. if (StatusCode.IsBad(result.ServiceResult))
    88. {
    89. return false;
    90. }
    91. //验证结果
    92. ClientBase.ValidateResponse(resultsValues, value);
    93. return true;
    94. //}
    95. //return false;
    96. }
    97. catch (Exception ex)
    98. {
    99. throw new Exception("OPUAReadNodes:" + ex.Message + ex.StackTrace);
    100. }
    101. }

    项目地址:

    OPCUA读写结构体示例-C#文档类资源-CSDN文库

  • 相关阅读:
    机器学习:银行贷款违约预测模型
    CRM,下一程在哪?
    域名系统DNS
    2020牛客暑期多校训练营(第十场)I.Tournament(构造/贪心)
    Linux C简单服务器模型解析及完整代码
    简单认识时间复杂度和空间复杂度
    我总结了3个做好事情的万能动作,简单高效!
    linux的常见命令
    Android 打印16进制颜色值
    WEB前端网页设计 HTML CSS 网页设计参数 - 列表、鼠标、块级元素
  • 原文地址:https://blog.csdn.net/weixin_42415843/article/details/126403953