05/25/2021
Hi, I am new to OPC ua and I am having problems calling a server method with a custom type from an opc ua client.
The server and client are both in house applications using the opc ua foundation sdk.
The client references Opc.Ua.Client, Opc.Ua.Client.ComplexTypes, Opc.Ua.Configuration, Opc.Ua.Core.
The custom type is of this structure:
st_outputConfiguration
Distance int
Duration int
Status enum
The method takes the following parameters:
StrobeId int,
OutputConfiguration array of structure st_outputConfiguration
I can get the type st_outputConfiguration structure from the server and build an array however I get an error when calling the method:
"InvalidCastExecption - Object cannot be stored in an array of this type"
Here is the relevant code snippet:
NodeId node = (NodeId)objectReference.NodeId; //Node id of the object containing the method
NodeId method = (NodeId)methodReference.NodeId; // Node id of the method.
Int16 strobeIndex = 1; //hard coded test of the strobeIndex parameter
List<Object> inputArguments = new List<Object>(); // an object to contain the all the input arguments for the method.
inputArguments.Add(strobeIndex);
// Get the st_outputConfiguration type from the server
Opc.Ua.Client.ComplexTypes.ComplexTypeSystem cts = new Opc.Ua.Client.ComplexTypes.ComplexTypeSystem(m_s);
Task<Type>t = cts.LoadType(ExpandedNodeId.Parse("ns=6;i=100050")); // id for the custom type.
NodeId nodeId = NodeId.Parse("ns=6;i=100050");
// build array of st_outputConfiguration
dynamic[] arOutputs = new dynamic[14];
Type typ = (Type)t.Result;
for (int i = 0; i < arOutputs.Length; i++)
{
dynamic st = Activator.CreateInstance(typ);
st.Distance = 1;
st.Duration = 1;
st.Status = 0;
arOutputs[i] = st;
}
inputArguments.Add(arOutputs); // add to the input arguments
var result = m_s.Call(node, method, inputArguments); // call the method.
End of Code Snippet.
I'm new to this and may be trying something that is difficult to implement, but I've seen examples that seem to work and calling the method from UA Expert works with no problem, so I know it's possible.
Can anyone help point me in the right direction. Thanks.
05/25/2021
Randy Armstrong said
The C# API takes an Object but complex types must be wrapped in an ExtensionObject.Any type that implements the IEncodeable interface can be placed in an ExtensionObject.
Thanks for your feedback. I've taken another attempt but I'm still having problems.
Here is my new code for making the method call:
NodeId node = (NodeId)objectReference.NodeId;
NodeId method = (NodeId)methodReference.NodeId;
EncodeableFactory.GlobalFactory.AddEncodeableType(typeof(ST_ClientTEST02));
ST_ClientTEST02 v1 = new ST_ClientTEST02();
v1.Distance = 1;
v1.Duration = 1;
v1.Status = 1;
var result = m_s.Call(node, method, new ExtensionObject(v1));
I am getting an invalid argument error.
This is the IEncodable class I've created:
public class ST_ClientTEST02 : EncodeableObject
{
public UInt32 Distance { get; set; }
public UInt32 Duration { get; set; }
public UInt32 Status { get; set; }
public override ExpandedNodeId TypeId
{
get { return ExpandedNodeId.Parse("ns=6;i=100170"); }
//protected set { _myProp = value; }
}
public override ExpandedNodeId BinaryEncodingId
{
get { return ExpandedNodeId.Parse("ns=6;i=100175"); }
}
public override ExpandedNodeId XmlEncodingId
{
get { return null; }
}
public override void Encode(IEncoder encoder)
{
encoder.WriteUInt32("Distance", this.Distance);
encoder.WriteUInt32("Duration", this.Duration);
encoder.WriteUInt32("Status", this.Status);
}
public override void Decode(IDecoder decoder)
{
this.Distance = decoder.ReadUInt32("Distance");
this.Duration = decoder.ReadUInt32("Duration");
this.Status = decoder.ReadUInt32("Status");
}
public override string ToString()
{
return "{distance=" + this.Distance.ToString() + "; Duration=" + Duration.ToString() + "; status=" + this.Status.ToString() + "}";
}
}
Any pointers?
05/25/2021
Thanks for you feedback.
I've been trying to get this working with the correct uri/nsu, but I can't get it working. I've only been using the index previously and am not sure I'm formatting the uri correctly. Is there a way of translating from index to nsu/uri to make sure I've got the correct uri/nsu? I can't seem to find a way of getting this.
05/30/2017
When you create a Session the client library reads the value of the NamespaceArray Property and caches it.
When you pass a IEncodeable as a parameter the URI stored in the TypeId is replaced by the NamespaceIndex for the current NamespaceArray.
The URI is an opaque string. A simple case sensitive compare is done with each entry in the NamespaceArray. Omitting trailing slashes or similar typing errors can lead to a failure to locate the URI.
05/25/2021
Thanks for replying Randy.
I'm aware of what the IEncodable passed in will do. Whatever I try I can't get the TypeId or BinaryEncoding Id correct.
I'm taking a step back and trying to verify I can get the URI address correct and read a node through the session.ReadNode
As I say I'm a complete novice at this so may be making simple basic errors, but I hitting brick walls I can't find answers to so I'm completely stuck.
Code snippet of what I'm trying to do.
string id = "";
// i can read this node ok
id = "ns=6;i=100055";
var val1 = m_s.ReadNode(NodeId.Parse(id));
//Contents of server namespace array property
//[0] https://opcfoundation.org/UA/
//[1] urn:br-automation/BR/UA/EmbeddedServer
//[2] https://opcfoundation.org/UA/DI/
//[3] http://PLCopen.org/OpcUa/IEC61131-3/
//[4] http://br-automation.com/OpcUa/PLC/
//[5]
//[6] http://br-automation.com/OpcUa/PLC/PV/
// I'm trying to veryify a URI/NSU method of reading the node, then I can veryify I at least have the address correct.
// This does not work - Bad Id.
id = "http://br-automation.com/OpcUa/PLC/;i=100055";
NodeId nid = NodeId.Parse(id); // I'm assuming this can take a URI? If not how can I read a node using the URI?
var val2 = m_s.ReadNode(nid);
05/30/2017
If you are writing a custom struct to a server you can just pass the ExtensionObject.
If you are reading a custom struct you will get a ExtensionObject with a binary body that you need to manually parse using your Decode method.
If you register your custom type with the stack using the MessageContext.Factory.AddEncodeableType then the stack will decode automatically for you but I would get the manually decoding working first.
05/25/2021
Thank you very much. I have it working now.
I found it tricky to know precisely how to use the Extension Object. I couldn't find any detailed documentation - even a list of the properties and methods on the ExtensionObject class. Can anyone point me to any documentation?
I ended up either looking at the code for the ExtensionObject Class in GitHub or using wireshark to see how UA expert formulated the messages.
Thanks for all the help on this thread. It's been a huge help to a newbie like me.
07/04/2022
Hello,
I read this thread and think that I have the same problem like Shaun: reading complex types from a B&R PLC. 😉
@Shaun: It would be very helpful, if you can explain a little bit more what you have to do to get it working.
Randy Armstrong said
If you are reading a custom struct you will get a ExtensionObject with a binary body that you need to manually parse using your Decode method.
If I tried to use "ReadValue" on such a complex type, I got always a "BadNotSupported" StatusCode. (Same with the free UaExpert app: NodeClass = Variable, DataType = Structure, Value = BadNotSupported.) I can see, that the node has the attribute "HasTypeDefinition = myType" and I can also find the type in the Address Space: Types => VariableTypes => BaseVariableType => BaseDataVariableType => myType
With the input from this thread, I found the "NetCoreComplexClient" sample:
var value = m_session.ReadValue(variableNode.NodeId);
CastInt32ToEnum(variableNode, value);
Console.WriteLine($" -- {variableNode}:{value}");if (value.Value is ExtensionObject extensionObject)
{ ...
or this thread: https://opcfoundation.org/foru.....ionobject/
But for all of them, I need the DataValue. => BadNotSupported
What could be the reason for that? What I'm missing?
Thanks,
Mike
05/30/2017
Sounds like a Server error.
BadNotSupported on a Read suggests that you are asking the Server to return something it can't provide.
Since UAExpert sees the same error is it not likely a issue with parameters.
At this point it seems to be a server bug. You need to contact B&R and ask them to explain why their Server would return BadNotSupported.
07/04/2022
Hi,
I got the answer from the B&R support:
I have to set the version of the "Informationmodel" to 2.0. (In the B&R IDE "Automation Studio" go to Physical View : CPU : Configuration : OPC-UA System : Informationmodels : PV : Version => 2.0.)
And my client has to load the ComplexTypeSystem:
var complexTypeSystem = new ComplexTypeSystem(Session);
await complexTypeSystem.Load().ConfigureAwait(false);
After that and with a little bit of .NET Reflection, I can now read complex types from the OPC UA server. 🙂
Thanks,
Mike
1 Guest(s)