Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class TmsClientDeviceImpl : public TmsClientComponentBaseImpl<MirroredDeviceBase
void removed() override;
bool isAddedToLocalComponentTree() override;
StringPtr onGetRemoteId() const override;
ErrCode setOperationModeImpl(OperationModeType modeType, bool recursiveCall);

private:
void fetchTimeDomain();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ class TmsClientObjectImpl
uint32_t tryReadChildNumberInList(const std::string& nodeName);
uint32_t tryReadChildNumberInList(const opcua::OpcUaNodeId& nodeId);
CachedReferences getChildReferencesOfType(const opcua::OpcUaNodeId& nodeId, const opcua::OpcUaNodeId& typeId);
bool getAttributeWritePermission(const opcua::OpcUaNodeId& nodeId);
bool getExecutePermission(const opcua::OpcUaNodeId& nodeId);

opcua::MonitoredItem* monitoredItemsCreateEvent(
const opcua::EventMonitoredItemCreateRequest& item,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ class TmsClientPropertyObjectBaseImpl : public TmsClientObjectImpl, public Impl
std::unordered_map<std::string, BaseObjectPtr>& functionPropValues);
PropertyPtr addVariableBlockProperty(const StringPtr& propName, const OpcUaNodeId& propNodeId);
void browseRawProperties();
void setLocksForAttributes();
bool isIgnoredMethodProperty(const std::string& browseName);
PropertyObjectPtr cloneChildPropertyObject(const PropertyPtr& prop) override;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,30 +156,40 @@ ErrCode TmsClientDeviceImpl::getAvailableOperationModes(IList** availableOpModes

ErrCode TmsClientDeviceImpl::setOperationMode(OperationModeType modeType)
{
if (!this->hasReference("OperationMode"))
return DAQ_MAKE_ERROR_INFO(OPENDAQ_ERR_NOT_SUPPORTED, "OperationModes are not supported by the server");

const auto nodeId = getNodeId("OperationMode");
const auto modeTypeStr = OperationModeTypeToString(modeType);

const auto variant = VariantConverter<IString>::ToVariant(String(modeTypeStr), nullptr, daqContext);
client->writeValue(nodeId, variant);

return OPENDAQ_SUCCESS;
return setOperationModeImpl(modeType, false);
}

ErrCode TmsClientDeviceImpl::setOperationModeRecursive(OperationModeType modeType)
{
if (!this->hasReference("OperationMode"))
return DAQ_MAKE_ERROR_INFO(OPENDAQ_ERR_NOT_SUPPORTED, "OperationModes are not supported by the server");

const auto nodeId = getNodeId("OperationMode");
const auto modeTypeStr = "Recursive" + OperationModeTypeToString(modeType);
return setOperationModeImpl(modeType, true);
}

const auto variant = VariantConverter<IString>::ToVariant(String(modeTypeStr), nullptr, daqContext);
client->writeValue(nodeId, variant);
ErrCode TmsClientDeviceImpl::setOperationModeImpl(OperationModeType modeType, bool recursiveCall)
{
ErrCode errCode = OPENDAQ_SUCCESS;
if (this->hasReference("OperationMode"))
{
const auto nodeId = getNodeId("OperationMode");
const auto modeTypeStr = ((recursiveCall) ? "Recursive" : "") + OperationModeTypeToString(modeType);

return OPENDAQ_SUCCESS;
const auto variant = VariantConverter<IString>::ToVariant(String(modeTypeStr), nullptr, daqContext);
try
{
client->writeValue(nodeId, variant);
}
catch (OpcUaException& e)
{
if (e.getStatusCode() == UA_STATUSCODE_BADUSERACCESSDENIED)
errCode = DAQ_MAKE_ERROR_INFO(OPENDAQ_ERR_ACCESSDENIED, "Access denied when setting OperationModes");
else
errCode = DAQ_MAKE_ERROR_INFO(OPENDAQ_ERR_OPCUA_GENERAL, "Failed to set OperationMode on server");
}
}
else
{
errCode = DAQ_MAKE_ERROR_INFO(OPENDAQ_ERR_NOT_SUPPORTED, "OperationModes are not supported by the server");
}
return errCode;
}

ErrCode TmsClientDeviceImpl::getOperationMode(OperationModeType* modeType)
Expand Down Expand Up @@ -735,6 +745,9 @@ DictPtr<IString, IFunctionBlockType> TmsClientDeviceImpl::onGetAvailableFunction
auto browser = clientContext->getReferenceBrowser();
auto types = Dict<IString, IFunctionBlockType>();

if (getAttributeWritePermission(this->nodeId) == false)
return types;

const auto fbFolderNodeId = browser->getChildNodeId(nodeId, "FB");

if (!browser->hasReference(fbFolderNodeId, "AvailableTypes"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,18 @@ ErrCode TmsClientFunctionImpl::call(IBaseObject* args, IBaseObject** result)
lastProccessDescription = "Calling function";
OpcUaObject<UA_CallMethodResult> callResult = ctx->getClient()->callMethod(callRequest);
if (OPCUA_STATUSCODE_FAILED(callResult->statusCode) || (callResult->outputArgumentsSize != 1))
return DAQ_MAKE_ERROR_INFO(OPENDAQ_ERR_CALLFAILED);
{
if (callResult->statusCode == UA_STATUSCODE_BADUSERACCESSDENIED)
return DAQ_MAKE_ERROR_INFO(OPENDAQ_ERR_ACCESSDENIED);
else
return DAQ_MAKE_ERROR_INFO(OPENDAQ_ERR_CALLFAILED);
}

lastProccessDescription = "Getting call result";
*result = VariantConverter<IBaseObject>::ToDaqObject(OpcUaVariant(callResult->outputArguments[0]), daqContext).detach();
return OPENDAQ_SUCCESS;
});
if (OPENDAQ_FAILED(errCode))
{
daqClearErrorInfo();
if (this->daqContext.getLogger().assigned())
{
auto loggerComponent = this->daqContext.getLogger().getOrAddComponent("OpcUaClientProcedure");
LOG_W("Failed to call function on OpcUA client. Error in \"{}\"", lastProccessDescription);
}
}
OPENDAQ_RETURN_IF_FAILED(errCode, fmt::format("Failed to call function on OpcUA client. Error in \"{}\"", lastProccessDescription));
return OPENDAQ_SUCCESS;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ ErrCode TmsClientInputPortImpl::setRequiresSignal(Bool value)

ErrCode TmsClientInputPortImpl::acceptsSignal(ISignal* signal, Bool* accepts)
{
return DAQ_MAKE_ERROR_INFO(OPENDAQ_ERR_OPCUA_CLIENT_CALL_NOT_AVAILABLE);

return getAttributeWritePermission(nodeId);
//const ErrCode errCode = daqTry([&]()
//{
// OpcUaNodeId methodId(NAMESPACE_DAQBSP, UA_DAQBSPID_INPUTPORTTYPE_ACCEPTSSIGNAL);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <opcuatms_client/objects/tms_client_object_impl.h>
#include <opcuaclient/browse_request.h>
#include <opcuaclient/browser/opcuabrowser.h>
#include <opcuatms_client/objects/tms_client_object_impl.h>
#include "opendaq/custom_log.h"

BEGIN_NAMESPACE_OPENDAQ_OPCUA_TMS

Expand Down Expand Up @@ -105,4 +106,51 @@ CachedReferences TmsClientObjectImpl::getChildReferencesOfType(const opcua::OpcU
return clientContext->getReferenceBrowser()->browseFiltered(nodeId, filter);
}

bool TmsClientObjectImpl::getAttributeWritePermission(const opcua::OpcUaNodeId& nodeId)
{
// Note: This method is used to determine if a node attributes is changeable.
// For variable nodes you should check UA_ATTRIBUTEID_ACCESSLEVEL and UA_ATTRIBUTEID_USERACCESSLEVEL!
// In common case a user can have write permission to a node but not to its attributes.
bool commonWritePerm = true;
try
{
const auto reader = clientContext->getAttributeReader();
const int64_t userWriteMask = reader->getValue(nodeId, UA_ATTRIBUTEID_USERWRITEMASK).toInteger();
const int64_t writeMask = reader->getValue(nodeId, UA_ATTRIBUTEID_WRITEMASK).toInteger();
commonWritePerm = ((userWriteMask & writeMask) != 0);
}
catch (...)
{
if (this->daqContext.getLogger().assigned())
{
auto loggerComponent = this->daqContext.getLogger().getOrAddComponent("OpcUaClientObject");
LOG_W("Cannot read write mask attributes for OpcUA node");
}
}
return commonWritePerm;
}

bool TmsClientObjectImpl::getExecutePermission(const opcua::OpcUaNodeId& nodeId)
{
// Note: This method is used to determine if a method node is executable.
// Other nodes don't have executable attributes and this method will return true for them.
bool commonExecutable = true;
try
{
const auto reader = clientContext->getAttributeReader();
const bool userExecutable = reader->getValue(nodeId, UA_ATTRIBUTEID_USEREXECUTABLE).toBool();
const bool executable = reader->getValue(nodeId, UA_ATTRIBUTEID_EXECUTABLE).toBool();
commonExecutable = userExecutable & executable;
}
catch (...)
{
if (this->daqContext.getLogger().assigned())
{
auto loggerComponent = this->daqContext.getLogger().getOrAddComponent("OpcUaClientObject");
LOG_W("Cannot read executable mask attributes for OpcUA node");
}
}
return commonExecutable;
}

END_NAMESPACE_OPENDAQ_OPCUA_TMS
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,16 @@ ErrCode TmsClientProcedureImpl::dispatch(IBaseObject* args)
lastProccessDescription = "Calling procedure";
OpcUaObject<UA_CallMethodResult> callResult = ctx->getClient()->callMethod(callRequest);
if (OPCUA_STATUSCODE_FAILED(callResult->statusCode) || (callResult->outputArgumentsSize != 0))
return DAQ_MAKE_ERROR_INFO(OPENDAQ_ERR_CALLFAILED);
{
if (callResult->statusCode == UA_STATUSCODE_BADUSERACCESSDENIED)
return DAQ_MAKE_ERROR_INFO(OPENDAQ_ERR_ACCESSDENIED);
else
return DAQ_MAKE_ERROR_INFO(OPENDAQ_ERR_CALLFAILED);
}

return OPENDAQ_SUCCESS;
});
if (OPENDAQ_FAILED(errCode))
{
daqClearErrorInfo();
if (this->daqContext.getLogger().assigned())
{
auto loggerComponent = this->daqContext.getLogger().getOrAddComponent("OpcUaClientProcudure");
LOG_W("Failed to call procedure on OpcUA client. Error: \"{}\"", lastProccessDescription);
}
}
OPENDAQ_RETURN_IF_FAILED(errCode, fmt::format("Failed to call procedure on OpcUA client. Error: \"{}\"", lastProccessDescription));
return OPENDAQ_SUCCESS;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,21 @@ void TmsClientPropertyImpl::configurePropertyFields()
const auto& references = clientContext->getReferenceBrowser()->browse(nodeId);
const auto reader = clientContext->getAttributeReader();

for (auto [browseName, ref] : references.byBrowseName)
int64_t userAccessLevel = reader->getValue(nodeId, UA_ATTRIBUTEID_USERACCESSLEVEL).toInteger();
int64_t accessLevel = reader->getValue(nodeId, UA_ATTRIBUTEID_ACCESSLEVEL).toInteger();
int64_t commonAccessLevel = userAccessLevel & accessLevel;

this->readOnly = ((commonAccessLevel & UA_ACCESSLEVELMASK_WRITE) == 0);

bool isExecutableProperty = (valueType == CoreType::ctFunc || valueType == CoreType::ctProc);
bool commonExecutable = true;
if (isExecutableProperty)
{
commonExecutable = getExecutePermission(nodeId);
this->visible = commonExecutable;
}

for (const auto& [browseName, ref] : references.byBrowseName)
{
const auto childNodeId = OpcUaNodeId(ref->nodeId.nodeId);

Expand Down Expand Up @@ -120,11 +134,17 @@ void TmsClientPropertyImpl::configurePropertyFields()
break;

case details::PropertyField::IsReadOnly:
this->readOnly = EvalValue(evalStr).asPtr<IBoolean>();
if ((commonAccessLevel & UA_ACCESSLEVELMASK_WRITE) != 0)
this->readOnly = EvalValue(evalStr).asPtr<IBoolean>();
else
this->readOnly = true;
break;

case details::PropertyField::IsVisible:
this->visible = EvalValue(evalStr).asPtr<IBoolean>();
if (!isExecutableProperty || commonExecutable)
this->visible = EvalValue(evalStr).asPtr<IBoolean>();
else
this->visible = false;
break;

case details::PropertyField::Unit:
Expand Down Expand Up @@ -207,10 +227,16 @@ void TmsClientPropertyImpl::configurePropertyFields()
break;
}
case details::PropertyField::IsReadOnly:
this->readOnly = VariantConverter<IBoolean>::ToDaqObject(reader->getValue(childNodeId, UA_ATTRIBUTEID_VALUE));
if ((commonAccessLevel & UA_ACCESSLEVELMASK_WRITE) != 0)
this->readOnly = VariantConverter<IBoolean>::ToDaqObject(reader->getValue(childNodeId, UA_ATTRIBUTEID_VALUE));
else
this->readOnly = true;
break;
case details::PropertyField::IsVisible:
this->visible = VariantConverter<IBoolean>::ToDaqObject(reader->getValue(childNodeId, UA_ATTRIBUTEID_VALUE));
if (!isExecutableProperty || commonExecutable)
this->visible = VariantConverter<IBoolean>::ToDaqObject(reader->getValue(childNodeId, UA_ATTRIBUTEID_VALUE));
else
this->visible = false;
break;
case details::PropertyField::Unit:
this->unit = VariantConverter<IUnit>::ToDaqObject(reader->getValue(childNodeId, UA_ATTRIBUTEID_VALUE));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ void TmsClientPropertyObjectBaseImpl<Impl>::init()
}
clientContext->readObjectAttributes(nodeId);
browseRawProperties();
setLocksForAttributes();
}

template <typename Impl>
Expand Down Expand Up @@ -287,7 +288,14 @@ ErrCode INTERFACE_FUNC TmsClientPropertyObjectBaseImpl<Impl>::beginUpdate()
request->inputArgumentsSize = 0;
request->objectId = nodeId.copyAndGetDetachedValue();
request->methodId = beginUpdateId.copyAndGetDetachedValue();
client->callMethod(request);
OpcUaObject<UA_CallMethodResult> callResult = client->callMethod(request);
if (callResult->statusCode != UA_STATUSCODE_GOOD)
{
if (callResult->statusCode == UA_STATUSCODE_BADUSERACCESSDENIED)
return DAQ_MAKE_ERROR_INFO(OPENDAQ_ERR_ACCESSDENIED);
else
return DAQ_MAKE_ERROR_INFO(OPENDAQ_ERR_CALLFAILED);
}
return OPENDAQ_SUCCESS;
}

Expand All @@ -302,7 +310,14 @@ ErrCode INTERFACE_FUNC TmsClientPropertyObjectBaseImpl<Impl>::endUpdate()
request->inputArgumentsSize = 0;
request->objectId = nodeId.copyAndGetDetachedValue();
request->methodId = endUpdateId.copyAndGetDetachedValue();
client->callMethod(request);
OpcUaObject<UA_CallMethodResult> callResult = client->callMethod(request);
if (callResult->statusCode != UA_STATUSCODE_GOOD)
{
if (callResult->statusCode == UA_STATUSCODE_BADUSERACCESSDENIED)
return DAQ_MAKE_ERROR_INFO(OPENDAQ_ERR_ACCESSDENIED);
else
return DAQ_MAKE_ERROR_INFO(OPENDAQ_ERR_CALLFAILED);
}
return OPENDAQ_SUCCESS;
}

Expand Down Expand Up @@ -430,6 +445,7 @@ void TmsClientPropertyObjectBaseImpl<Impl>::addMethodProperties(const OpcUaNodeI
ListPtr<IArgumentInfo> inputArgs;
ListPtr<IArgumentInfo> outputArgs;
uint32_t numberInList = std::numeric_limits<uint32_t>::max();
bool commonExecutable = true;

try
{
Expand All @@ -450,6 +466,7 @@ void TmsClientPropertyObjectBaseImpl<Impl>::addMethodProperties(const OpcUaNodeI
const auto numberInListId = browser->getChildNodeId(childNodeId, "NumberInList");
numberInList = VariantConverter<IInteger>::ToDaqObject(reader->getValue(numberInListId, UA_ATTRIBUTEID_VALUE));
}
commonExecutable = getExecutePermission(childNodeId);
}
catch(const std::exception& e)
{
Expand All @@ -462,13 +479,13 @@ void TmsClientPropertyObjectBaseImpl<Impl>::addMethodProperties(const OpcUaNodeI
if (outputArgs.assigned() && outputArgs.getCount() == 1)
{
auto callableInfo = FunctionInfo(outputArgs[0].getType(), inputArgs);
prop = FunctionPropertyBuilder(propName, callableInfo).setReadOnly(true).build();
prop = FunctionPropertyBuilder(propName, callableInfo).setReadOnly(true).setVisible(commonExecutable).build();
func = TmsClientFunction(clientContext, daqContext, parentNodeId, childNodeId);
}
else
{
auto callableInfo = ProcedureInfo(inputArgs);
prop = FunctionPropertyBuilder(propName, callableInfo).setReadOnly(true).build();
prop = FunctionPropertyBuilder(propName, callableInfo).setReadOnly(true).setVisible(commonExecutable).build();
func = TmsClientProcedure(clientContext, daqContext, parentNodeId, childNodeId);
}

Expand Down Expand Up @@ -564,6 +581,22 @@ void TmsClientPropertyObjectBaseImpl<Impl>::browseRawProperties()

}

template <typename Impl>
void TmsClientPropertyObjectBaseImpl<Impl>::setLocksForAttributes()
{
if (!getAttributeWritePermission(nodeId))
{
if (this->objPtr.template supportsInterface<IComponentPrivate>())
{
this->objPtr.template asPtrOrNull<IComponentPrivate>(true).lockAllAttributes();
}
else
{
LOG_W("Object does not support IComponentPrivate, cannot lock attributes for write protection");
}
}
}

template <class Impl>
bool TmsClientPropertyObjectBaseImpl<Impl>::isIgnoredMethodProperty(const std::string& browseName)
{
Expand Down
Loading
Loading