From 671dc7197ee82f671d66c692d2c2da9dda4d6c0d Mon Sep 17 00:00:00 2001 From: Piotr Nikiel Date: Wed, 17 Feb 2021 12:41:55 +0100 Subject: [PATCH 1/2] Initial implementation that seems to be OK for writing values --- include/nodemanagerbase.h | 4 ++ include/opcua_basedatavariabletype.h | 23 +++++++++++ include/uabasenodes.h | 8 ++++ src/nodemanagerbase.cpp | 48 +++++++++++++++++++++- src/opcua_basedatavariabletype.cpp | 59 +++++++++++++++++++++++++--- 5 files changed, 135 insertions(+), 7 deletions(-) diff --git a/include/nodemanagerbase.h b/include/nodemanagerbase.h index be6cd91..8164b80 100644 --- a/include/nodemanagerbase.h +++ b/include/nodemanagerbase.h @@ -86,6 +86,10 @@ class NodeManagerBase: public NodeManagerConfig UaNode* parent, UaNode* to, const UaNodeId& refType); + UaStatus addDataSourceVariableNodeAndReference( + UaNode* parent, + OpcUa::BaseDataVariableType* to, + const UaNodeId& refType); UaStatus addDataVariableNodeAndReference( UaNode* parent, OpcUa::BaseDataVariableType* to, diff --git a/include/opcua_basedatavariabletype.h b/include/opcua_basedatavariabletype.h index fbd7e7d..2eac265 100644 --- a/include/opcua_basedatavariabletype.h +++ b/include/opcua_basedatavariabletype.h @@ -28,6 +28,7 @@ #include #include #include +#include namespace OpcUa { @@ -61,7 +62,11 @@ class BaseDataVariableType: public UaNode virtual UaNodeId nodeId() const override { return m_nodeId; } virtual OpcUa_Int32 valueRank() const { return m_valueRank; } virtual void arrayDimensions(UaUInt32Array & arrayDimensions ) const { arrayDimensions = m_arrayDimensions;} + + const UA_DataValue* valueImpl() const { return m_currentValue.impl(); } + void associateServer( UA_Server* server ); + void setValueHandling( ValueHandling valueHandling ) { m_valueHandling = valueHandling; } private: UaQualifiedName m_browseName; @@ -71,6 +76,24 @@ class BaseDataVariableType: public UaNode OpcUa_Int32 m_valueRank; OpcUa_Byte m_accessLevel; UaUInt32Array m_arrayDimensions; + + /* It seems necessary to comment on ValueHandling policies within open62541-compat; as it is a wrapper layer on top of open62541, + * attempting to look&feel like UASDK is not necessarily easy. + * To me (Piotr) I choose the following mapping: + * None - the m_currentValue is not used at all. setValue() and value() should be overridden by a subclass. Within this class their behaviour is undefined. + * Cache - the m_currentValue only reflects last value passed to setValue(). setValue() will invoke open62541 writeValues() and value() will invoke + * open62541 readValues. This mode seems to match the new implementation of cache-variables in open62541-compat. + * CacheIsSource - the m_currentValue stores the actual "online" value, open62541 will use value() to sample it. This matches the historic way of doing + * cache variables using open62541-compat and remains to be used for simpler DataSource applications in future. + * CacheIsUpdatedOnRequest - not supported at all in open62541 at this very moment. + */ + ValueHandling m_valueHandling; + + UaStatus requestWriteToServer (UA_NodeId variableNodeId, const UaDataValue& dataValue); + UaDataValue requestReadFromServer (); + + UA_Server* m_associatedServer; + }; diff --git a/include/uabasenodes.h b/include/uabasenodes.h index 6189c95..01671bf 100644 --- a/include/uabasenodes.h +++ b/include/uabasenodes.h @@ -24,6 +24,14 @@ #include +enum ValueHandling +{ + UaVariable_Value_None = 0x00, + UaVariable_Value_Cache = 0x01, + UaVariable_Value_CacheIsSource = 0x02 + // not supported: UaVariable_Value_CacheIsUpdatedOnRequest = 0x04 +}; + class UaObject: public UaNode { diff --git a/src/nodemanagerbase.cpp b/src/nodemanagerbase.cpp index bdfc165..1fb5905 100644 --- a/src/nodemanagerbase.cpp +++ b/src/nodemanagerbase.cpp @@ -239,7 +239,11 @@ UaStatus NodeManagerBase::addObjectNodeAndReference( } -UaStatus NodeManagerBase::addDataVariableNodeAndReference( +/** Piotr's comment: this mechanism was used historically to handle cache-variables in open62541-compat. + * However, it is no longer needed for cache-variables as we have better ways of doing it, using proper Variables. + * I'm not deprecating it though because it very well matches what's needed for synchronous source-variables. + */ +UaStatus NodeManagerBase::addDataSourceVariableNodeAndReference( UaNode* parent, OpcUa::BaseDataVariableType* variable, const UaNodeId& refType) @@ -285,6 +289,48 @@ UaStatus NodeManagerBase::addDataVariableNodeAndReference( return s; } +UaStatus NodeManagerBase::addDataVariableNodeAndReference( + UaNode* parent, + OpcUa::BaseDataVariableType* variable, + const UaNodeId& refType) +{ + UaLocalizedText displayName( "en_US", variable->browseName().unqualifiedName().toUtf8().c_str()); + + UA_VariableAttributes attr = UA_VariableAttributes_default; + attr.description = *emptyDescription.impl(); + attr.displayName = *displayName.impl(); + attr.dataType = variable->typeDefinitionId().impl(); + attr.valueRank = variable->valueRank(); + attr.accessLevel = variable->accessLevel(); + UaUInt32Array arrayDimensions; + variable->arrayDimensions(arrayDimensions); + attr.arrayDimensionsSize = arrayDimensions.size(); + if (arrayDimensions.size() > 0) + attr.arrayDimensions = &arrayDimensions[0]; + else + attr.arrayDimensions = nullptr; + variable->value(/*session*/nullptr).value()->copyTo(&attr.value); + UA_StatusCode s = + UA_Server_addVariableNode( m_server, + variable->nodeId().impl(), + parent->nodeId().impl(), + refType.impl(), + variable->browseName().impl(), + UaNodeId(UA_NS0ID_BASEDATAVARIABLETYPE ,0).impl() , + attr, + nullptr, /* nodecontext */ + nullptr /* output nodeid - not using automatic assignment so don't care */ + ); + if (UA_STATUSCODE_GOOD == s) + { + m_listNodes.push_back( variable ); + parent->addReferencedTarget( variable, refType ); + } + variable->associateServer(m_server); + variable->setValueHandling(ValueHandling::UaVariable_Value_Cache); + return s; +} + UaStatus NodeManagerBase::addVariableNodeAndReference( UaNode* parent, UaNode* to, diff --git a/src/opcua_basedatavariabletype.cpp b/src/opcua_basedatavariabletype.cpp index 0824145..92f7ea4 100644 --- a/src/opcua_basedatavariabletype.cpp +++ b/src/opcua_basedatavariabletype.cpp @@ -38,13 +38,14 @@ BaseDataVariableType::BaseDataVariableType( m_nodeId (nodeId), m_typeDefinitionId( OpcUaType_Variant, 0), m_valueRank(-1), // by default: scalar - m_accessLevel(accessLevel) + m_accessLevel(accessLevel), + m_valueHandling(ValueHandling::UaVariable_Value_CacheIsSource), // the behaviour of this class prior to v1.3.8 of open62541-compat matches this ValueHandling mode., + m_associatedServer (nullptr) { } - UaStatus BaseDataVariableType::setValue( Session *session, const UaDataValue& dataValue, @@ -53,17 +54,63 @@ UaStatus BaseDataVariableType::setValue( { if (!checkAccessLevel || (m_accessLevel & UA_ACCESSLEVELMASK_WRITE)) { - m_currentValue = dataValue; - return OpcUa_Good; + // okay, at least we know that it's writable. + switch (m_valueHandling) + { + case ValueHandling::UaVariable_Value_None: OPEN62541_COMPAT_LOG_AND_THROW(std::logic_error, "This mode of ValueHandling requires that setValue(...) is overridden in a subclass."); + case ValueHandling::UaVariable_Value_Cache: + { + UaStatus status = requestWriteToServer(m_nodeId.impl(), dataValue); + if (status.isGood()) + m_currentValue = dataValue; + return status; + } + + case ValueHandling::UaVariable_Value_CacheIsSource: + m_currentValue = dataValue; + return OpcUa_Good; + + default: OPEN62541_COMPAT_LOG_AND_THROW(std::logic_error, "Not-implemented"); + } } else return OpcUa_BadUserAccessDenied; - } UaDataValue BaseDataVariableType::value(Session* session) { - return m_currentValue.clone(); + switch (m_valueHandling) + { + case ValueHandling::UaVariable_Value_None: OPEN62541_COMPAT_LOG_AND_THROW(std::logic_error, "This mode of ValueHandling requires that value(...) is overridden in a subclass."); + case ValueHandling::UaVariable_Value_Cache: + return requestReadFromServer(); + + case ValueHandling::UaVariable_Value_CacheIsSource: + return m_currentValue.clone(); + + default: OPEN62541_COMPAT_LOG_AND_THROW(std::logic_error, "Not-implemented"); + } +} + +UaStatus BaseDataVariableType::requestWriteToServer (UA_NodeId variableNodeId, const UaDataValue& dataValue) +{ + if (!m_associatedServer) + OPEN62541_COMPAT_LOG_AND_THROW(std::logic_error, "Can't perform write request before knowing which server to talk to. Perhaps wrong ValueHandling mode?"); + return UA_Server_writeValue(m_associatedServer, variableNodeId, *dataValue.value()->impl()); +} + +UaDataValue BaseDataVariableType::requestReadFromServer () +{ + // TODO: fake + UaDataValue dv (UaVariant(), OpcUa_Good, UaDateTime::now(), UaDateTime::now()); + return dv; +} + +void BaseDataVariableType::associateServer( UA_Server* server ) +{ + if (m_associatedServer) + OPEN62541_COMPAT_LOG_AND_THROW(std::logic_error, "Changing already associated server is probably a logic error?"); + m_associatedServer = server; } } From 3b3fd838eacd6b7f264a173fa72242c46411882b Mon Sep 17 00:00:00 2001 From: Piotr Nikiel Date: Wed, 17 Feb 2021 16:29:56 +0100 Subject: [PATCH 2/2] Now also implemented for reading --- include/opcua_basedatavariabletype.h | 4 ++-- src/opcua_basedatavariabletype.cpp | 17 ++++++++++------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/include/opcua_basedatavariabletype.h b/include/opcua_basedatavariabletype.h index 2eac265..c2e2ccb 100644 --- a/include/opcua_basedatavariabletype.h +++ b/include/opcua_basedatavariabletype.h @@ -89,8 +89,8 @@ class BaseDataVariableType: public UaNode */ ValueHandling m_valueHandling; - UaStatus requestWriteToServer (UA_NodeId variableNodeId, const UaDataValue& dataValue); - UaDataValue requestReadFromServer (); + UaStatus requestWriteToServer (UaNodeId variableNodeId, const UaDataValue& dataValue); + UaDataValue requestReadFromServer (UaNodeId variableNodeId); UA_Server* m_associatedServer; diff --git a/src/opcua_basedatavariabletype.cpp b/src/opcua_basedatavariabletype.cpp index 92f7ea4..c7835b3 100644 --- a/src/opcua_basedatavariabletype.cpp +++ b/src/opcua_basedatavariabletype.cpp @@ -83,7 +83,7 @@ UaDataValue BaseDataVariableType::value(Session* session) { case ValueHandling::UaVariable_Value_None: OPEN62541_COMPAT_LOG_AND_THROW(std::logic_error, "This mode of ValueHandling requires that value(...) is overridden in a subclass."); case ValueHandling::UaVariable_Value_Cache: - return requestReadFromServer(); + return requestReadFromServer(m_nodeId.impl()); case ValueHandling::UaVariable_Value_CacheIsSource: return m_currentValue.clone(); @@ -92,18 +92,21 @@ UaDataValue BaseDataVariableType::value(Session* session) } } -UaStatus BaseDataVariableType::requestWriteToServer (UA_NodeId variableNodeId, const UaDataValue& dataValue) +UaStatus BaseDataVariableType::requestWriteToServer (UaNodeId variableNodeId, const UaDataValue& dataValue) { if (!m_associatedServer) OPEN62541_COMPAT_LOG_AND_THROW(std::logic_error, "Can't perform write request before knowing which server to talk to. Perhaps wrong ValueHandling mode?"); - return UA_Server_writeValue(m_associatedServer, variableNodeId, *dataValue.value()->impl()); + return UA_Server_writeValue(m_associatedServer, variableNodeId.impl(), *dataValue.value()->impl()); } -UaDataValue BaseDataVariableType::requestReadFromServer () +UaDataValue BaseDataVariableType::requestReadFromServer (UaNodeId variableNodeId) { - // TODO: fake - UaDataValue dv (UaVariant(), OpcUa_Good, UaDateTime::now(), UaDateTime::now()); - return dv; + UA_Variant valueRead; + UA_Variant_init(&valueRead); + UaStatus status = UA_Server_readValue(m_associatedServer, variableNodeId.impl(), &valueRead); + if (!status.isGood()) + OPEN62541_COMPAT_LOG_AND_THROW(std::runtime_error, "UA_Server_readValue for variable: "+variableNodeId.toFullString().toUtf8()+" returned not-good:"+status.toString().toUtf8()); + return UaDataValue (valueRead, OpcUa_Good, UaDateTime::now(), UaDateTime::now()); } void BaseDataVariableType::associateServer( UA_Server* server )