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!