I'm trying to read values written from an opc client to the server via the OnSimpleWriteValue event. As regards "basic" values (such as int, float, string,...) there are no problems, but when I have to read "complex" values, therefore with dedicated IEncodable classes, the story changes. When writing, the server encodes the values correctly, while when reading, the value returns to me in the form of an array of bytes
Below the codes I am using
public static void HandleProperty<T, P>(this T parent, ISystemContext systemContext, P property, string propertyStateName)
where T : NodeState
where P : PropertyChanged
{
BaseVariableState variableState = (BaseVariableState)parent.FindChildBySymbolicName(systemContext, propertyStateName);
if (variableState == null) return;
//Handle value changed on service
property.ChangedByService += (obj, pn, v)
=> OnPropertyChangedByService(systemContext, variableState, v);
//Handle value changed on OPC
variableState.OnSimpleWriteValue += (ISystemContext ctx, NodeState n, ref object v)
=> OnPropertyChangedByOpc(ctx, n, property, v);
//Init value
UpdateValue(variableState, systemContext, property.GetValue());
}
public static void UpdateValue<T>(this T variableState, ISystemContext systemContext, object value)
where T : BaseVariableState
{
var type = value.GetType();
//Convert IList to IEncodeable[]
if (value is IList valueList)
{
var valueArray = Array.CreateInstance(type.GenericTypeArguments[0], valueList.Count);
valueList.CopyTo(valueArray, 0);
value = valueArray;
}
variableState.Value = value;
variableState.UpdateChangeMasks(NodeStateChangeMasks.Value);
variableState.ClearChangeMasks(systemContext, false); // notifies any monitored items that the value has changed.
}
private static ServiceResult OnPropertyChangedByOpc<P>(ISystemContext ctx, NodeState n, P property, object value)
where P : PropertyChanged
{
var test = n.FindChild(ctx, property.Name);
if (value is ExtensionObject extObj) value = extObj.DecodeBinaryValue();
if (value is ExtensionObject[] extArr)
{
var type = property.GetType().GenericTypeArguments[0];
IList tempList = Array.CreateInstance(type, extArr.Length);
for (var index = 0; index < extArr.Length; index++)
{
var valueBytes = extArr[index];
var ext = valueBytes.DecodeBinaryValue(); //Here's I am trying to Decode, value is byte[]
tempList[index] = ext;
}
value = tempList;
}
property.SetValue(value);
return ServiceResult.Good;
}
private static object DecodeBinaryValue(this ExtensionObject extensionObject)
{
using var decoder = new BinaryDecoder(extensionObject.Body as byte[], ServiceMessageContext.GlobalContext);
var decodeValue = decoder.ReadExtensionObject(null); //TODO - Fix length array error
return decodeValue;
}
What I tried
What I expect
I think this is a work around, but after some spent time I found a solution.
I modified the DecodeBinaryValue
using the ReadEncodable
method
private static object DecodeBinaryValue(this ExtensionObject extensionObject, Type type)
{
using var decoder = new BinaryDecoder(extensionObject.Body as byte[], ServiceMessageContext.GlobalContext);
var decodeValue = decoder.ReadEncodeable("", type);
return decodeValue;
}
On method handler OnPropertyChangedByOpc
, I have to modified the code too because the object returned it's not cast correctly
private static ServiceResult OnPropertyChangedByOpc<P>(ISystemContext ctx, NodeState n, P property, object value)
where P : PropertyChanged
{
if (value is ExtensionObject extObj)
{
var type = property.GetType().GenericTypeArguments[0];
value = extObj.DecodeBinaryValue(type);
}
if (value is ExtensionObject[] extArr)
{
var listType = property.GetType().GenericTypeArguments[0];
var type = listType.GenericTypeArguments[0];
List<object> tempList = new List<object>();
for (var index = 0; index < extArr.Length; index++)
{
var valueBytes = extArr[index];
var ext = valueBytes.DecodeBinaryValue(type);
//Here, after decode the value, create a temp list object and
//re-cast all single value as expected then save on a new cast
//istance list and save the value as usual.
//If you dont do this, method will return cast conversion error
tempList.Add(Convert.ChangeType(ext, type));
}
var castValue = Activator.CreateInstance(listType) as IList;
foreach (var item in tempList) castValue.Add(item);
value = castValue;
}
property.SetValue(value);
return ServiceResult.Good;
}