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中就能看到结构体中的所有项,大家可以打个断点看看
NodeId node = new NodeId(ItemAdress);
//获取Node 信息 验证Node的数据类型
VariableNode nodeInfo = m_session.ReadNode(node) as VariableNode;
//DataValue value = m_session.ReadValue(node);
var datatypeNode = (m_session.ReadNode(nodeInfo.DataType)) as DataTypeNode;
var typeDefine = datatypeNode.DataTypeDefinition.Body as StructureDefinition;
然后就是遍历所有项,其中GetJsonFromExtensionObject就是对每项就行解析
if (value.Value is ExtensionObject[])//数组
{
JArray res = new JArray();
foreach (var item in value.Value as ExtensionObject[])
{
res.Add(GetJsonFromExtensionObject(item, typeDefine,ref index));
}
return res.ToString();
}
最后对每项项进行解析时,要和相关人员确定byte[]拼接的规则。比如我这里如果该项是字符串,那么首先需要先读取该字符串所占的长度,然后根据长度读取字符串。当然具体项目中的规则也可能不完全相同,但是思路是一样的。如果是int,float直接读取4个byte,bool读取1个byte,double读取8个byte,根据一个类型占多少位决定。下面代码可供参考
private JObject GetJsonFromExtensionObject(ExtensionObject value, StructureDefinition structure,ref int index)
{
JObject res = new JObject();
var data = value.Body as byte[];
foreach (var field in structure.Fields)
{
if (field.DataType.NamespaceIndex==2)
{
var count = BitConverter.ToUInt32(data.Skip(index).Take(4).ToArray(), 0);
index += 4;
var datatypeNode1 = (m_session.ReadNode(field.DataType)) as DataTypeNode;
var typeDefine = datatypeNode1.DataTypeDefinition.Body as StructureDefinition;
for (int i = 0; i < count; i++)
{
res[field.Name+i] =GetJsonFromExtensionObject(value, typeDefine,ref index);
}
}
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;
}
if (field.DataType == DataTypeIds.UInt32)
{
UInt32 re = BitConverter.ToUInt32(data.Skip(index).Take(4).ToArray(), 0);
index += 4;
res[name] = re;
}
if (field.DataType == DataTypeIds.Float)
{
float re = BitConverter.ToSingle(data.Skip(index).Take(4).ToArray(), 0);
index += 4;
res[name] = re;
}
if (field.DataType == DataTypeIds.Boolean)
{
bool re = BitConverter.ToBoolean(data.Skip(index).Take(1).ToArray());
res[name] = re;
index += 1;
}
if (field.DataType == DataTypeIds.Double)
{
double re = BitConverter.ToDouble(data.Skip(index).Take(8).ToArray());
res[name] = re;
index += 8;
}
}
return res;
}
读取结构体完整代码如下:
public string structItem(string ItemAdress)
{
DataValue item= m_session.ReadValue(new NodeId(ItemAdress));
string aaa = ReadStruct(ItemAdress, item);
return aaa;
}
/// <summary>
/// 读取结构体数据
/// </summary>
public string ReadStruct(string ItemAdress, DataValue value)
{
#region 得到ExtensionObject中的结构体的项
NodeId node = new NodeId(ItemAdress);
//获取Node 信息 验证Node的数据类型
VariableNode nodeInfo = m_session.ReadNode(node) as VariableNode;
//DataValue value = m_session.ReadValue(node);
var datatypeNode = (m_session.ReadNode(nodeInfo.DataType)) as DataTypeNode;
var typeDefine = datatypeNode.DataTypeDefinition.Body as StructureDefinition;
#endregion
int index = 0;
if (value.Value is ExtensionObject[])//数组
{
JArray res = new JArray();
foreach (var item in value.Value as ExtensionObject[])
{
res.Add(GetJsonFromExtensionObject(item, typeDefine,ref index));
}
return res.ToString();
}
else //非数组
{
return GetJsonFromExtensionObject(value.Value as ExtensionObject, typeDefine, ref index).ToString();
}
}
private JObject GetJsonFromExtensionObject(ExtensionObject value, StructureDefinition structure,ref int index)
{
JObject res = new JObject();
var data = value.Body as byte[];
foreach (var field in structure.Fields)
{
if (field.DataType.NamespaceIndex==2)
{
var count = BitConverter.ToUInt32(data.Skip(index).Take(4).ToArray(), 0);
index += 4;
var datatypeNode1 = (m_session.ReadNode(field.DataType)) as DataTypeNode;
var typeDefine = datatypeNode1.DataTypeDefinition.Body as StructureDefinition;
for (int i = 0; i < count; i++)
{
res[field.Name+i] =GetJsonFromExtensionObject(value, typeDefine,ref index);
}
}
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;
}
if (field.DataType == DataTypeIds.UInt32)
{
UInt32 re = BitConverter.ToUInt32(data.Skip(index).Take(4).ToArray(), 0);
index += 4;
res[name] = re;
}
if (field.DataType == DataTypeIds.Float)
{
float re = BitConverter.ToSingle(data.Skip(index).Take(4).ToArray(), 0);
index += 4;
res[name] = re;
}
if (field.DataType == DataTypeIds.Boolean)
{
bool re = BitConverter.ToBoolean(data.Skip(index).Take(1).ToArray());
res[name] = re;
index += 1;
}
if (field.DataType == DataTypeIds.Double)
{
double re = BitConverter.ToDouble(data.Skip(index).Take(8).ToArray());
res[name] = re;
index += 8;
}
}
return res;
}
写入步骤:
1.首先可以先用UAExpert查看结构体
2.把每项的结构体转成把ExtensionObject,加入到ExtensionObject[]
3.把ExtensionObject[]写入即可
写入结构体的难点就是如何按照规则(就是拼接方式)把结构体转成byte[]然后加入到ExtensionObject[]
1.首先可以先用UAExpert查看结构体
同读取,省略
2.把每项的结构体转成把ExtensionObject,加入到ExtensionObject[]
首先定义一个结构体类,同UAExpert查看结构体,注意结构体顺序也不能乱
/// <summary>
/// writevar对象
/// </summary>
public class WriteVar
{
/// <summary>
/// 数组索引
/// </summary>
public int index = 0;
/// <summary>
/// 数据项名称
/// </summary>
public string Name = "";
/// <summary>
/// 数据类型
/// </summary>
public int DataType = 0;
/// <summary>
/// 字符串值
/// </summary>
public string StringValue = "";
/// <summary>
/// 整数值
/// </summary>
public int IntValue = 0;
/// <summary>
/// 浮点数值
/// </summary>
public float FloatValue = 0.0F;
}
然后赋值给结构体,并转成ExtensionObject,这里我定义的规则就是如果是字符串就要把长度也拼接上
WriteVar structs = new WriteVar
{
index = 4,
Name = "重量",
DataType = 0,
StringValue = "",
IntValue = 25,
FloatValue = 0.0F
};
var result = Pro_OPCUA.GetValueByteFromObj(structs);
/// <summary>
/// 从对象中获取对象所有的值,并转化为byte[]
/// </summary>
/// <param name="value">需要写入节点的值组成的byte数组列表</param>
/// <returns></returns>
public static ExtensionObject GetValueByteFromObj(WriteVar value)
{
List<byte> result = new List<byte>();
//前4位为字符串数据长度
var length = BitConverter.GetBytes(value.Name.Length);
result.AddRange(length);
//数据项名称
var Name = Encoding.Default.GetBytes(value.Name);
result.AddRange(Name);
//数据类型
var DataType = BitConverter.GetBytes(value.DataType);
result.AddRange(DataType);
//字符串数据转换
if (value.StringValue.Length==0)
{
result.AddRange(new byte[4] { 0,0,0,0});
}
else
{
//前4位为字符串数据长度
length = BitConverter.GetBytes(value.StringValue.Length);
result.AddRange(length);
//字符串数据
var StringValue = Encoding.Default.GetBytes(value.StringValue);
result.AddRange(StringValue);
}
//整数数据
var IntValue = BitConverter.GetBytes(value.IntValue);
result.AddRange(IntValue);
//浮点数数据
var FloatValue = BitConverter.GetBytes(value.FloatValue);
result.AddRange(FloatValue);
return new ExtensionObject {Body= result.ToArray() } ;
}
3.把ExtensionObject[]写入即可
其实拼接好ExtensionObject[]后按照通用写OPCUA的方法就可以了
}
/// <summary>
/// OPCUATag写入
/// </summary>
/// <param name="value">需要写入节点的值组成的byte数组列表</param>
/// <returns></returns>
public void WriteVarStruct(ExtensionObject[] value,string nodeId)
{
WriteValueCollection nodesToWrite = new WriteValueCollection();
nodesToWrite.Add(new WriteValue()
{
NodeId = new NodeId(nodeId),
AttributeId = Attributes.Value,
Value = new DataValue()
{
Value = value
}
});
WriteNode(nodesToWrite);
}
/// <summary>
/// OPCUATag写入
/// </summary>
/// <param name="adress">需要写入节点的地址</param>
/// <param name="value">需要写入节点的值</param>
/// <returns></returns>
public bool WriteNode(WriteValueCollection value)
{
try
{
//if (isCconnect)
//{
StatusCodeCollection resultsValues = null;
DiagnosticInfoCollection diagnosticInfos = null;
//Console.WriteLine("数据读取开始");
// Call Read Service
var result = m_session.Write(
null,
value,
out resultsValues,
out diagnosticInfos);
//写入返回值为bad,则写入失败
if (StatusCode.IsBad(result.ServiceResult))
{
return false;
}
//验证结果
ClientBase.ValidateResponse(resultsValues, value);
return true;
//}
//return false;
}
catch (Exception ex)
{
throw new Exception("OPUAReadNodes:" + ex.Message + ex.StackTrace);
}
}
写结构体的完整代码:
private void button6_Click(object sender, EventArgs e)
{
ExtensionObject[] AA = new ExtensionObject[100];
for (int i = 0; i < AA.Length; i++)
{
AA[i] = new ExtensionObject { Body = getNewByte() };
}
WriteVar structs = new WriteVar
{
index = 4,
Name = "重量",
DataType = 0,
StringValue = "",
IntValue = 25,
FloatValue = 0.0F
};
var result = Pro_OPCUA.GetValueByteFromObj(structs);
AA[4] = result;
float a = 1.23F;
WriteVar structs1 = new WriteVar
{
index = 5,
Name = "形状",
DataType = 1,
StringValue = "",
IntValue = 0,
FloatValue = a
};
var result1 = Pro_OPCUA.GetValueByteFromObj(structs1);
AA[5] = result1;
WriteVar structs2 = new WriteVar
{
index = 6,
Name = "C",
DataType = 2,
StringValue = "WUXE",
IntValue = 0,
FloatValue = 0.0F
};
var result2= Pro_OPCUA.GetValueByteFromObj(structs2);
AA[6] = result2;
//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};
string aaa= AA.ToString();
pro_OPCUAp.WriteVarStruct(AA, "ns=2;s=writeStrucat");
}
/// <summary>
/// OPCUATag写入
/// </summary>
/// <param name="value">需要写入节点的值组成的byte数组列表</param>
/// <returns></returns>
public void WriteVarStruct(ExtensionObject[] value,string nodeId)
{
WriteValueCollection nodesToWrite = new WriteValueCollection();
nodesToWrite.Add(new WriteValue()
{
NodeId = new NodeId(nodeId),
AttributeId = Attributes.Value,
Value = new DataValue()
{
Value = value
}
});
WriteNode(nodesToWrite);
}
/// <summary>
/// OPCUATag写入
/// </summary>
/// <param name="adress">需要写入节点的地址</param>
/// <param name="value">需要写入节点的值</param>
/// <returns></returns>
public bool WriteNode(WriteValueCollection value)
{
try
{
//if (isCconnect)
//{
StatusCodeCollection resultsValues = null;
DiagnosticInfoCollection diagnosticInfos = null;
//Console.WriteLine("数据读取开始");
// Call Read Service
var result = m_session.Write(
null,
value,
out resultsValues,
out diagnosticInfos);
//写入返回值为bad,则写入失败
if (StatusCode.IsBad(result.ServiceResult))
{
return false;
}
//验证结果
ClientBase.ValidateResponse(resultsValues, value);
return true;
//}
//return false;
}
catch (Exception ex)
{
throw new Exception("OPUAReadNodes:" + ex.Message + ex.StackTrace);
}
}
项目地址: