umati / Sample-Server

Sample-Server implementation based on open62541, showcases umati endorsed OPC UA companion specifications. Provides a :whale2: :package: to run locally for development purpose.
https://umati.org
Mozilla Public License 2.0
53 stars 16 forks source link

How to declare HasInterface Reference in TypeDefinitions #304

Closed ChristophRuckstetter closed 3 years ago

ChristophRuckstetter commented 3 years ago

Hi,

we have a case where an Object should be of Type "BaseObjectType" and should have a HasInterface Reference to another type. What needs to be done in the TypeDefinition to achive this?

The concrete example currently looks this this. The State Object should have a HasInterface Reference to another Type

namespace woodworking{
struct WwMachine_t {
  BindableMember<MachineIdentification_t> Identification;
  BindableMember<ns0::BaseObject_t> State;
};
}  // namespace woodworking

REFL_TYPE(
  woodworking::WwMachine_t ,
  UmatiServerLib::attribute::UaObjectType(UmatiServerLib::constexp::NodeId(constants::NsWoodworkingUri, UA_WOODWORKINGID_WWMACHINETYPE)))
REFL_FIELD(
  Identification,
  UmatiServerLib::attribute::UaBrowseName(constants::NsDIUri))
REFL_FIELD(
  State
)
ccvca commented 3 years ago

At the moment, there are no specific attributes for interfaces, as in MachineTools, Interfaces are only used for TypeDefinitions, e.g. here, but it should work the same way. https://github.com/umati/Sample-Server/blob/7cd9a3f9a4ce04a52e930f147f0c84e0d1f9845f/TypeDefinition/MachineTool/MachineToolIdentification.hpp#L16

As for all acessable OPC UA Variable a C++ variable must be available, the State must use another type than ns0::BaseObject_t (as this has no members). E.g. (Unchecked, only as a demonstration)

struct IWWState_t {
  BinableMember<ns0::StateVariable_t> CurrentState;
}
REFL_TYPE(
  woodworking::IWWState_t ,
  UmatiServerLib::attribute::UaObjectType(UmatiServerLib::constexp::NodeId(constants::NsWoodworkingUri, UA_WOODWORKINGID_IWWSTATE)))
REFL_FIELD(CurrentState)

Then this IWWState_t should be fine for using in WwMachine_t for State, if State is a mandatory member.

For optional members which ony use Interfaces, this will not work as expected because the instanciation will use the methods from open62541 to instanciate an object, which will result in an Object of the inteface type (without HasInterface Reference). In this case additional implementations are required.

ChristophRuckstetter commented 3 years ago

In the Woodworking case the IWwStateType has two children of BaseObjectTypes, which a hasInterface Reference to another Type. And each of these BaseObjectTypes have again children of BaseObjectTypes with hasInterface references.

So I couldn't directly adapt your example.

I tried to implement the interface Types, and also dummy structs, that inherit from the interface types to use them. But i couldn't get it to work. Here is the complete code how i tried to implement the type definitions: https://github.com/ChristophRuckstetter/Sample-Server/commit/b695d3756fef241728931b62c58f656c3618389c

ccvca commented 3 years ago

After investigation, I detected two issues:

  1. Overview in IWwBaseState_t is an object, so it must be BindableMember instead of BindableMemberValue and some Fields are missing in the reflection (Fixed here: https://github.com/ChristophRuckstetter/Sample-Server/pull/1)
  2. UA_Server_addObjectNode from open62541 seems to ignore HasInterface references when creating the instance. (see also: https://github.com/open62541/open62541/issues/4363 and https://github.com/open62541/open62541/pull/3813) This must be fixed in open62541 for all mandatory childs, as the server expects that UA_Server_addObjectNode create a valid instance.
GoetzGoerisch commented 3 years ago

https://github.com/open62541/open62541/pull/4479

ChristophRuckstetter commented 3 years ago

I applied this patch (https://github.com/open62541/open62541/pull/4479) to my open62541 build and tried to bring up the State/Machine/Flags Object by defining the Flags as optional in the TypeDefinitions and calling InstantiateOptional() on Instantiation (https://github.com/ChristophRuckstetter/Sample-Server/commit/ab8187971669f5e5cc6f3bb6c1df727a61ff0633) .

This compiles fine, but on runtime it throws the following exception: "terminate called after throwing an instance of 'std::runtime_error' what(): Could not create optional node, Error: BadTypeDefinitionInvalid".

Is there something else that needs to be done to get the optional objects with hasinterface reference instantiated?

GoetzGoerisch commented 3 years ago

With #330 also in develop

ccvca commented 3 years ago

On https://github.com/ccvca/Sample-Server/tree/ww_fix 9f9ac79f752b903421db5acc57a7fbfbc5a9be3a I fixed a bug, which causes the instanciation of abstract types. But I was not able to fully succeed in creating the optional Object, as I don't know a way to add the childs of that interface: https://github.com/ccvca/Sample-Server/blob/9f9ac79f752b903421db5acc57a7fbfbc5a9be3a/UmatiServerLib/Instantiation.cpp#L158

open62541/open62541#4479 only supports HasInterfaces on TypeDefinition, but in this case an Object of type BaseObjectType in created and the interfaces are added afterwards.

basyskom-jvoe commented 3 years ago

Optional children can also be added by implementing the optional child callback. Open62541 will then ask for every optional child if it should be added when an object is added to the server and adds it if the callback function returns true.

My pull request should also support HasInterface references on BaseObjectType objects, but in this case, the object creation must be done via the UA_Server_addNode_begin() and UA_Server_addNode_finish() functions.

ChristophRuckstetter commented 3 years ago

@basyskom-jvoe I tried to create the object with UA_Server_addNode_begin() and UA_Server_addNode_finish() and the object was created properly with the HasInterface reference, but the mandatory children as defined by the interface type weren't created. I'm not sure if it should behave like that.

When I use UA_Server_addObjectNode (and the optional child callback returns true for the relevant object), the object is created properly with the HasInterface reference, and the mandatory children are also there.

ChristophRuckstetter commented 3 years ago

I did an error on my side. With the UA_Server_addNode_begin() and UA_Server_addNode_finish() and the HasInterface reference it works just fine.