Serialize/dezerialize types created with UaModeler?

Questions regarding the use of UaModeler

Moderator: uamodeler

Post Reply
bveldhoen
Sr. Member
Sr. Member
Posts: 11
Joined: 07 May 2015, 14:38

Serialize/dezerialize types created with UaModeler?

Post by bveldhoen »

We use UaModeler to generate C++ code, which contains the types that we can use to implement our services. We want to have a way to serialize (and deserialize) those objects, for instance for logging and/or persistence purposes.

I mean the classes that derive from OpcUa::BaseObjectType:

Code: Select all

class mynamespace_dll_EXPORT MyTypeBase:
    public OpcUa::BaseObjectType
{
...
    virtual void setName(const UaString& Name);
    virtual UaString getName() const;
    // More properties that need serialization here
...
}

class mynamespace_dll_EXPORT MyType:
    public MyTypeBase
{
...
}
How do I serialize and deserialize an instance of MyType?

Is there an existing example how to serialize and deserialize instances of classes generated by UaModeler, preferably by using the opc ua sdk?
If not, could you provide such an example?

Many thanks!

User avatar
Support Team
Hero Member
Hero Member
Posts: 3072
Joined: 18 Mar 2011, 15:09

Re: Serialize/dezerialize types created with UaModeler?

Post by Support Team »

Hello,

There is no direct way to export the namespace of NodeManager - neither be methods provided by the SDK nor by the auto-generted code.

We would prefer not to export a namespace but to store the infomation about how the addressspace was built. E.g. if the addressspace was created by using a configuation file you can just rerun the configuration step.
If you have good reasons to export a namespace, you can use the methods read and browse at ServerManager to get all needed information about the addressspace.

Best regards
Support Team

bveldhoen
Sr. Member
Sr. Member
Posts: 11
Joined: 07 May 2015, 14:38

Re: Serialize/dezerialize types created with UaModeler?

Post by bveldhoen »

Dear Support Team,

Thanks for your reply, but it didn't really answer my question.

I'm not looking for a way to rerun the configuration. I'm looking for a way to persist data at runtime, and to be able to retrieve that data and add instances to the address space. We're looking into using a data store such as redis for this.

For instance, we would have an object (or folder) called MyTypeCollection, and we need to populate that folder with instances of MyType that were read from a data store. We need to keep the datastore and the service address space in sync.

So something like this:
Instance of MyType (i.e. "my1") <--> serialized string or binary blob <--> data store

How can we use the Opc Ua Sdk to store the data (properties, variables, etc.) contained by MyType?
(And subsequently, to recreate an instance of MyType based on that data?)

I have tried the following approaches (pls. answer the questions below):
1) Use UaBinaryEncoder:

Code: Select all

    // Create MyType instance
    auto nodeId = 1;
    MyType my1(UaNodeId(nodeId, nodeManager.getNameSpaceIndex()),
        UaString(std::to_string(nodeId).c_str()),
        nodeManager.getNameSpaceIndex(),
        nodeManager.getNodeManagerConfig());

    my1->setName("Bla");
    // Set other properties of MyType here...

    // Create encoder
    UaByteString byteString;
    UaBinaryEncoder encoder;
    encoder.create(byteString);

    // Encode
    auto structureDefinition = nm->getNodeManagerUaNode()->structureDefinition(g1->typeDefinitionId());
    UaGenericValue genericValue1(structureDefinition);
    genericValue1.setField("Name", my1->getName());
    genericValue1.encode(&encoder);

    // Create decoder
    UaBinaryDecoder decoder;
    decoder.create(byteString);

    // Decode
    UaGenericValue genericValue2(structureDefinition);
    genericValue2.decode(&decoder);
But that's highly undesirable because of the manual calls to SetField etc.

2) Use boost::serialization:

Code: Select all

#include "boost/serialization/split_free.hpp"
template <typename Archive>
void save(Archive& ar, const UaString& uaString, const unsigned int /*version*/)
{
  // TODO: save uaString
}

template <typename Archive>
void load(Archive& ar, UaString& uaString, const unsigned int /*version*/)
{
  // TODO: load uaString
}

template <typename Archive>
void save(Archive& ar, const UaDateTime& uaDateTime, const unsigned int /*version*/)
{
    const auto& uaString = uaDateTime.toString();
    ar & uaString;
}

template <typename Archive>
void load(Archive& ar, UaDateTime& uaDateTime, const unsigned int /*version*/)
{
    UaString uaString;
    ar & uaString;
    uaDateTime.fromString(uaString);
}

template <typename Archive>
void save(Archive& ar, const UaLocalizedText& uaText, const unsigned int /*version*/)
{
    const UaString& locale = uaText.locale();
    ar & locale;

    const UaString& text = uaText.text();
    ar & text;
}

template <typename Archive>
void load(Archive& ar, UaLocalizedText& uaText, const unsigned int /*version*/)
{
    UaString locale;
    ar & locale;

    UaString text;
    ar & text;

    uaText.setLocalizedText(locale, text);
}

template <typename Archive>
void save(Archive& ar, const UaByteString& uaByteString, const unsigned int /*version*/)
{
  // TODO: save uaByteString
}

template <typename Archive>
void load(Archive& ar, UaByteString& uaByteString, const unsigned int /*version*/)
{
  // TODO: load uaByteString
}

template <typename Archive>
void save(Archive& ar, const MyType& growerType, const unsigned int /*version*/)
{
    const auto& name = myType.getName();
    ar & name;

    // Save other properties here...
}

template <typename Archive>
void load(Archive& ar, MyType& growerType, const unsigned int /*version*/)
{
    UaString name;
    ar & name;
    myType.setName(name);

    // Load other properties here...
}

template <class Archive>
void serialize(Archive& ar, UaDateTime& uaDateTime, const unsigned int version)
{
    boost::serialization::split_free(ar, uaDateTime, version);
}

template <class Archive>
void serialize(Archive& ar, UaString& uaString, const unsigned int version)
{
    boost::serialization::split_free(ar, uaString, version);
}

template <class Archive>
void serialize(Archive& ar, UaLocalizedText& uaText, const unsigned int version)
{
    boost::serialization::split_free(ar, uaText, version);
}

template <class Archive>
void serialize(Archive& ar, UaByteString& uaByteString, const unsigned int version)
{
    boost::serialization::split_free(ar, uaByteString, version);
}

template <class Archive>
void serialize(Archive& ar, MyType& myType, const unsigned int version)
{
    boost::serialization::split_free(ar, myType, version);
}

void myTest()
{
    // Create MyType instance
    auto nodeId = 1;
    MyType my1(UaNodeId(nodeId, nodeManager.getNameSpaceIndex()),
        UaString(std::to_string(nodeId).c_str()),
        nodeManager.getNameSpaceIndex(),
        nodeManager.getNodeManagerConfig());

    my1->setName("Bla");
    // Set other properties of MyType here...

    std::stringstream ss;
    boost::archive::text_oarchive oa(ss);

    // Unfortunately won't work, because of compiler error:
    //   ‘virtual mynamespace::MyType::~MyType()’ is protected
    oa << my1;
}
The TODO's in the code above are not the point and can be ignored for my questions.

As the comment says, this doesn't compile because of the protected constructor on MyType.
(As a workaround for this, we could consider deriving from MyType and make the destructor public, but that could interfere with the node reference counting)

3) We've looked at UaNodeSetXmlParser, but this seems to only be able to read from an xml file, and does not provide a way to serialize to xml. Can you confirm this?

4) Would it be possible to use the uastack for this? (_OpcUa_EncodableType and friends)?

5) In case there is no out of the box way for serialization in the Opc Ua Sdk, would you advise to implement a generic approach using the metadata that is built up during the call to MyTypeBase::createTypes()? (everything below pObjectType)
Or would it be better to implement a custom pod type (i.e. MyPod), which can be (de)serialized easily, and then also provide the conversion functions necessary (MyType <--> MyPod)?

6) Is there a roadmap to add (de)serialization functions to the generated types?

Thanks!

User avatar
Support Team
Hero Member
Hero Member
Posts: 3072
Joined: 18 Mar 2011, 15:09

Re: Serialize/dezerialize types created with UaModeler?

Post by Support Team »

Hello,

There is no built in functionality to save and load instances.
1. GenericValue, BinaryEncoder and BinaryDecoder are used for serializing structured DataTypes that are not registered at the stack. So these classes cannot be directly used for serializing OPC UA Objects.
3. UaNodeSetXmlParser can read XML files only. Writing XML files is not supported.
4. Also uastack does not have built in / out of the box functions for saving and loading addressspace. OpcUa_EncodableType is used for structured DataTypes.
5. This depends on your addressspace. For large addressspaces with a lot of types a generic solution would be smart since you have to make the implementation only once. For small addressspaces with few types we would prefer to implement save and load methods in the generated implementation classes of the ObjectTypes. Such methods can be more efficient and less complex compared to the generic solution.
6. There are no plans at the moment to add (de)serialization functions to the generated types.

Best regards
Support Team

Post Reply