Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OPCUA-1389 cache variables from datasource to variable #82

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/nodemanagerbase.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
23 changes: 23 additions & 0 deletions include/opcua_basedatavariabletype.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <nodemanagerbase.h>
#include <open62541_compat.h>
#include <statuscode.h>
#include <uabasenodes.h>

namespace OpcUa
{
Expand Down Expand Up @@ -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;
Expand All @@ -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 (UaNodeId variableNodeId, const UaDataValue& dataValue);
UaDataValue requestReadFromServer (UaNodeId variableNodeId);

UA_Server* m_associatedServer;

};


Expand Down
8 changes: 8 additions & 0 deletions include/uabasenodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@

#include <uanode.h>

enum ValueHandling
{
UaVariable_Value_None = 0x00,
UaVariable_Value_Cache = 0x01,
UaVariable_Value_CacheIsSource = 0x02
// not supported: UaVariable_Value_CacheIsUpdatedOnRequest = 0x04
};

class UaObject: public UaNode
{

Expand Down
48 changes: 47 additions & 1 deletion src/nodemanagerbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down
62 changes: 56 additions & 6 deletions src/opcua_basedatavariabletype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -53,17 +54,66 @@ 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(m_nodeId.impl());

case ValueHandling::UaVariable_Value_CacheIsSource:
return m_currentValue.clone();

default: OPEN62541_COMPAT_LOG_AND_THROW(std::logic_error, "Not-implemented");
}
}

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.impl(), *dataValue.value()->impl());
}

UaDataValue BaseDataVariableType::requestReadFromServer (UaNodeId variableNodeId)
{
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 )
{
if (m_associatedServer)
OPEN62541_COMPAT_LOG_AND_THROW(std::logic_error, "Changing already associated server is probably a logic error?");
m_associatedServer = server;
}

}
Expand Down