Professional OPC
Development Tools

logos

Online Forums

Technical support is provided through Support Forums below. Anybody can view them; you need to Register/Login to our site (see links in upper right corner) in order to Post questions. You do not have to be a licensed user of our product.

Please read Rules for forum posts before reporting your issue or asking a question. OPC Labs team is actively monitoring the forums, and replies as soon as possible. Various technical information can also be found in our Knowledge Base. For your convenience, we have also assembled a Frequently Asked Questions page.

Do not use the Contact page for technical issues.

How to create and write a simple complex type

More
29 Apr 2020 14:08 #8429 by RND
Excellent. Thank you for your help.

A colleague showed me that specifying the type was not necessary. It was probably the fact that I specified the wrong type that got me in trouble.

Please Log in or Create an account to join the conversation.

More
29 Apr 2020 06:19 #8422 by support
Thank you.

Some notes:

1. Constructing the DataType (I mean your "myStructuredDataType") and passing it into Write is optional. You can try creating the myStructuredData without myStructuredDataType, and things should work the same. The DataType, when given, allows an initial validation of the GenericData.

2. The fact that it is often needed to specify TypeCode or Type when writing numbers (because a precise built-in type needs to be send to OPC UA server but that mioght not be the same type as in the application) should not confuse you into thinking that such steps are necessary with complex data types. You can simply omit the Type/TypeCode argument to WriteValue.

3. In situations where you need to specify the Type or TypeCode (e.g. you need to specify the types of arguments for CallMethod because you need that UInt16 there), I recommend you use TypeCode.Empty for complex data types.

Best regards
The following user(s) said Thank You: RND

Please Log in or Create an account to join the conversation.

More
28 Apr 2020 16:11 #8417 by RND
It required two changes. Our problem was we had not tried them both at the same time in the right combination.

First, the string that describes the type of the variable had to be correct in both the constructor for myStructured Data and myGenericData (obviously ;)) . As you showed us, the correct type string was confirmed by examining the variable after we read it. We previously tried that correct type string many times, but without the second necessary change.

With the correct string we received an exception saying:
Exception thrown: 'OpcLabs.EasyOpc.UA.OperationModel.UAException' in OpcLabs.EasyOpcUA.dll
*** Failure: Object must implement IConvertible.
+ Attempting to change an object of type "OpcLabs.EasyOpc.UA.UAExtensionObject" to type "OpcLabs.BaseLib.DataTypeModel.StructuredDataType".
+ The client method called was 'WriteMultiple'.
That showed us we had another type mismatch so we changed the type argument in WriteValue. That was our main problem.
from:
client.WriteValue(endpointDescriptor, nodeDescriptor, myGenericData, typeof(StructuredDataType));
to:
client.WriteValue(endpointDescriptor, nodeDescriptor, myGenericData, typeof(UAGenericObject));

It was easy to incorrectly assume that the type problem was a single issue associated just with the node descriptor string because we had tried the correct string so many times unsuccessfully. It was the second problem with the argument in typeof() that finally fixed it. :)

Unfortunately, we are now struggling with passing exactly the same object to a method call immediately after the successful write using the following additional snippet after the WriteValue in succession. The method call fails though using the same data, and endpoint used one line before. We are troubleshooting. We've tried TypeCode.Object and TypeCode.Empty and also not using TypeCodes at all, but get various exceptions. If we do the same method call approach with a double, instead of the complex object it works fine.
object[] inputs =
                {
                    myGenericData,
                    0
                };
 
                TypeCode[] typeCodes =
                {
                    TypeCode.Object,
                    TypeCode.UInt16
                };
 
                object[] outputs;
 
                outputs = client.CallMethod(
                    endpointDescriptor,
                    "ns=4;s=OpcCommunication.OpcEffectLinear[1]",
                    "ns=4;s=OpcCommunication.OpcEffectLinear[1]#MoveAbsolute",
                    inputs,
                    typeCodes);

Strangely, the inner exception seems to say there is no valid endpoint, which seems odd because it was used the moment before in WriteValue()
Exception thrown: 'OpcLabs.EasyOpc.UA.OperationModel.UAException' in OpcLabs.EasyOpcUA.dll
*** Failure: Invalid opc.tcp endpoint URL ("opc.tcp:/"): The Host must not be empty.
+ The client method called was 'CallMultipleMethods'.

Please Log in or Create an account to join the conversation.

More
28 Apr 2020 13:36 #8416 by support
Thank you for update. I am glad it works. It is not clear, to me, however, which change was it that made it working.

Please Log in or Create an account to join the conversation.

More
28 Apr 2020 03:29 #8408 by RND
Success! Thank you for your help!

Although this is not its final form, this code allowed us to construct a variable and successfully write it. In case this is any help to others.
var myStructuredDataType = new StructuredDataType(
        "ST_OpcMoveAbsoluteData",
        "nsu=urn:BeckhoffAutomation:Ua:PLC1 ;ns=4;s=<StructuredDataType>:ST_OpcMoveAbsoluteData")
         {
              new DataField("fTarget", UAOpcBinaryStandardDataTypes.Double),
              new DataField("fVelocity", UAOpcBinaryStandardDataTypes.Double),
              new DataField("fAccel", UAOpcBinaryStandardDataTypes.Double),
              new DataField("fDecel", UAOpcBinaryStandardDataTypes.Double),
              new DataField("fDelay", UAOpcBinaryStandardDataTypes.Double)
          };
 
var myStructuredData = new StructuredData(myStructuredDataType);
 
// previously used myStucturedData.Add(). It worked. But below is more
// appropriate as the type definition above appears to create those fields.
myStructuredData.FieldData["fTarget"] = new PrimitiveData(111d);
myStructuredData.FieldData["fVelocity"] = new PrimitiveData(22d);
myStructuredData.FieldData["fAccel"] = new PrimitiveData(33d);
myStructuredData.FieldData["fDecel"] = new PrimitiveData(33d);
myStructuredData.FieldData["fDelay"] = new PrimitiveData(4d);
 
var myGenericData = new UAGenericObject(
      myStructuredData,
      new UAModelNodeDescriptor(
              endpointDescriptor, 
              "nsu=urn:BeckhoffAutomation:Ua:PLC1 ;ns=4;s=<StructuredDataType>:ST_OpcMoveAbsoluteData"));
 
client.WriteValue(endpointDescriptor, nodeDescriptor, myGenericData, typeof(UAGenericObject));

The successful write was verified in the PLC.

Please Log in or Create an account to join the conversation.

More
28 Apr 2020 02:30 #8407 by RND
Thank you for your quick answer.

Yes, we were able to read the variable and immediately write it back again unchanged without error.

The value of UAGenericObject.DataTypeId.NodeDescriptor.NodeId.ExpandedText after reading the variable was :
"nsu=urn:BeckhoffAutomation:Ua:PLC1 ;ns=4;s=<StructuredDataType>:ST_OpcMoveAbsoluteData"

If I use that value in the constructor for a new structured variable, we still get the type exception. We are continuing to troubleshoot.

Thanks,
Rick

Please Log in or Create an account to join the conversation.

More
25 Apr 2020 09:42 #8399 by support
Hello.

I suspect (the same as you) that the problem can be in the improper DataTypeId in the UAGenericObject you pass to WriteValue.
Some questions:

1. Are you able to write back directly what you have read? Take the whole UAGenericObject you receive from ReadValue, and pass it to WriteValue?
2. What is the contents of UAGenericObject.DataTypeId.NodeDescriptor.NodeId.ExpandedText you get from ReadValue?

Kind regards

Please Log in or Create an account to join the conversation.

More
24 Apr 2020 16:40 - 24 Apr 2020 16:47 #8396 by RND
We hoped to use a structured type as an argument for method calls. We created a variable of that type to read and write for testing. We can browse and read the variable. We can use the OPC UA Complex Data writing example in the documentation to write one of the primitive data members in the variable after first reading it. But, we have been unable two find a way to write the variable as a single write. That would be necessary if using a complex type as a method call argument.

The PLC is Beckhoff with the Beckhoff OPC Server. The simple type is shown below.
{attribute 'OPC.UA.DA.StructuredType' := '1'}
TYPE ST_OpcMoveAbsoluteData :
STRUCT
	fTarget		: LREAL;
	fVelocity	: LREAL;
	fAccel		: LREAL;
	fDecel		: LREAL;
	fDelay		: LREAL;
END_STRUCT
END_TYPE

We've tried several ways to construct an acceptable complex type variable to write with no success. It seems that we are unable to make the library aware of the type of the complex object being written Our common error message has been some form of this:
Exception thrown: 'OpcLabs.EasyOpc.UA.OperationModel.UAException' in OpcLabs.EasyOpcUA.dll
*** Failure: No data type system found for node "NodeId="nsu=urn:BeckhoffAutomation:Ua:PLC1 ;ns=4;s=OpcCommunication.OpcTestData1"" on endpoint "opc.tcp://192.168.78.64:4840". The 0 encoding name(s) were: .

Browsing the nodes in the code, from Connectivity Explorer or UAExpert all seem to identify the type easily. We see 2 different nomenclatures for the type depending on the method for browsing it as:
Connectivity Explorer:
nsu=urn:BeckhoffAutomation:Ua:PLC1;ns=4;s=#Type|ST_OpcMoveAbsoluteData
 
Browsing the variable after being read in code:
Name = "ST_OpcMoveAbsoluteData""
FullName = "urn:BeckhoffAutomation:Ua:PLC1:ST_OpcMoveAbsoluteData"
 
Others:
nsu=urn:BeckhoffAutomation:Ua:PLC1;ns=4;s=<StructuredDataType>:ST_OpcMoveAbsoluteData"
We have tried some different code snippets to construct an object instance to be written with the code below as the latest, but in no case can we seem to be able to properly specify the type of the object being written based on the exceptions we are seeing. For Full Name of the data type, we have tried all of the above and many more.
UANodeDescriptor nodeDescriptor =
                "nsu=urn:BeckhoffAutomation:Ua:PLC1;ns=4;s=OpcCommunication.OpcTestData1";
 
var myStructuredDataType = new StructuredDataType(
    "ST_OpcMoveAbsoluteData",
    "urn:BeckhoffAutomation:Ua:PLC1:ST_OpcMoveAbsoluteData")
    {
        new DataField("fTarget", UAOpcBinaryStandardDataTypes.Double),
        new DataField("fVelocity", UAOpcBinaryStandardDataTypes.Double),
        new DataField("fAccel", UAOpcBinaryStandardDataTypes.Double),
        new DataField("fDecel", UAOpcBinaryStandardDataTypes.Double),
        new DataField("fDelay", UAOpcBinaryStandardDataTypes.Double)
    };
 
var myStructuredData = new StructuredData(myStructuredDataType);
myStructuredData.Add("fTarget", new PrimitiveData(100d));
myStructuredData.Add("fVelocity", new PrimitiveData(10d));
myStructuredData.Add("fAccel", new PrimitiveData(10d));
myStructuredData.Add("fDecel", new PrimitiveData(10d));
myStructuredData.Add("fDelay", new PrimitiveData(0d));
 
var myGenericData = new UAGenericObject(myStructuredData, new UAModelNodeDescriptor(endpointDescriptor, "urn:BeckhoffAutomation:Ua:PLC1:ST_OpcMoveAbsoluteData"));
 
client.WriteValue(endpointDescriptor, nodeDescriptor, myGenericData, typeof(StructuredDataType));

Can you see any fundamental error in understanding we may have in using the library?

Thank you. We tried to include a few details that might show our missteps.
Last edit: 24 Apr 2020 16:47 by RND.

Please Log in or Create an account to join the conversation.

Moderators: support
Time to create page: 0.078 seconds