| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| * |
| * Copyright 2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) |
| * Copyright 2017 (c) Stefan Profanter, fortiss GmbH |
| * Copyright 2017 (c) Thomas Bender |
| * Copyright 2017 (c) Julian Grothoff |
| * Copyright 2017 (c) Henrik Norrman |
| * Copyright 2018 (c) Fabian Arndt, Root-Core |
| * Copyright 2019 (c) Kalycito Infotech Private Limited |
| */ |
| |
| #include "open62541/namespace0_generated.h" |
| |
| #include "ua_server_internal.h" |
| #include "ua_session.h" |
| #include "ua_subscription.h" |
| |
| static UA_StatusCode |
| addNode_raw(UA_Server *server, UA_NodeClass nodeClass, |
| UA_UInt32 nodeId, char *name, void *attributes, |
| const UA_DataType *attributesType) { |
| UA_AddNodesItem item; |
| UA_AddNodesItem_init(&item); |
| item.nodeClass = nodeClass; |
| item.requestedNewNodeId.nodeId = UA_NODEID_NUMERIC(0, nodeId); |
| item.browseName = UA_QUALIFIEDNAME(0, name); |
| item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE; |
| item.nodeAttributes.content.decoded.data = attributes; |
| item.nodeAttributes.content.decoded.type = attributesType; |
| return AddNode_raw(server, &server->adminSession, NULL, &item, NULL); |
| } |
| |
| static UA_StatusCode |
| addNode_finish(UA_Server *server, UA_UInt32 nodeId, |
| UA_UInt32 parentNodeId, UA_UInt32 referenceTypeId) { |
| const UA_NodeId sourceId = UA_NODEID_NUMERIC(0, nodeId); |
| const UA_NodeId refTypeId = UA_NODEID_NUMERIC(0, referenceTypeId); |
| const UA_ExpandedNodeId targetId = UA_EXPANDEDNODEID_NUMERIC(0, parentNodeId); |
| UA_StatusCode retval = UA_Server_addReference(server, sourceId, refTypeId, targetId, false); |
| if (retval != UA_STATUSCODE_GOOD) |
| return retval; |
| return AddNode_finish(server, &server->adminSession, &sourceId); |
| } |
| |
| static UA_StatusCode |
| addObjectNode(UA_Server *server, char* name, UA_UInt32 objectid, |
| UA_UInt32 parentid, UA_UInt32 referenceid, UA_UInt32 type_id) { |
| UA_ObjectAttributes object_attr = UA_ObjectAttributes_default; |
| object_attr.displayName = UA_LOCALIZEDTEXT("", name); |
| return UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(0, objectid), |
| UA_NODEID_NUMERIC(0, parentid), |
| UA_NODEID_NUMERIC(0, referenceid), |
| UA_QUALIFIEDNAME(0, name), |
| UA_NODEID_NUMERIC(0, type_id), |
| object_attr, NULL, NULL); |
| } |
| |
| static UA_StatusCode |
| addReferenceTypeNode(UA_Server *server, char* name, char *inverseName, UA_UInt32 referencetypeid, |
| UA_Boolean isabstract, UA_Boolean symmetric, UA_UInt32 parentid) { |
| UA_ReferenceTypeAttributes reference_attr = UA_ReferenceTypeAttributes_default; |
| reference_attr.displayName = UA_LOCALIZEDTEXT("", name); |
| reference_attr.isAbstract = isabstract; |
| reference_attr.symmetric = symmetric; |
| if(inverseName) |
| reference_attr.inverseName = UA_LOCALIZEDTEXT("", inverseName); |
| return UA_Server_addReferenceTypeNode(server, UA_NODEID_NUMERIC(0, referencetypeid), |
| UA_NODEID_NUMERIC(0, parentid), UA_NODEID_NULL, |
| UA_QUALIFIEDNAME(0, name), reference_attr, NULL, NULL); |
| } |
| |
| /***************************/ |
| /* Bootstrap NS0 hierarchy */ |
| /***************************/ |
| |
| /* Creates the basic nodes which are expected by the nodeset compiler to be |
| * already created. This is necessary to reduce the dependencies for the nodeset |
| * compiler. */ |
| static UA_StatusCode |
| UA_Server_createNS0_base(UA_Server *server) { |
| /* Bootstrap References and HasSubtype */ |
| UA_StatusCode ret = UA_STATUSCODE_GOOD; |
| UA_ReferenceTypeAttributes references_attr = UA_ReferenceTypeAttributes_default; |
| references_attr.displayName = UA_LOCALIZEDTEXT("", "References"); |
| references_attr.isAbstract = true; |
| references_attr.symmetric = true; |
| references_attr.inverseName = UA_LOCALIZEDTEXT("", "References"); |
| ret |= addNode_raw(server, UA_NODECLASS_REFERENCETYPE, UA_NS0ID_REFERENCES, "References", |
| &references_attr, &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES]); |
| |
| UA_ReferenceTypeAttributes hassubtype_attr = UA_ReferenceTypeAttributes_default; |
| hassubtype_attr.displayName = UA_LOCALIZEDTEXT("", "HasSubtype"); |
| hassubtype_attr.isAbstract = false; |
| hassubtype_attr.symmetric = false; |
| hassubtype_attr.inverseName = UA_LOCALIZEDTEXT("", "HasSupertype"); |
| ret |= addNode_raw(server, UA_NODECLASS_REFERENCETYPE, UA_NS0ID_HASSUBTYPE, "HasSubtype", |
| &hassubtype_attr, &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES]); |
| |
| UA_ReferenceTypeAttributes aggregates_attr = UA_ReferenceTypeAttributes_default; |
| aggregates_attr.displayName = UA_LOCALIZEDTEXT("", "Aggregates"); |
| aggregates_attr.isAbstract = false; |
| aggregates_attr.symmetric = false; |
| aggregates_attr.inverseName = UA_LOCALIZEDTEXT("", "AggregatedBy"); |
| ret |= addNode_raw(server, UA_NODECLASS_REFERENCETYPE, UA_NS0ID_AGGREGATES, "Aggregates", |
| &aggregates_attr, &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES]); |
| |
| ret |= addReferenceTypeNode(server, "HierarchicalReferences", NULL, |
| UA_NS0ID_HIERARCHICALREFERENCES, true, false, UA_NS0ID_REFERENCES); |
| |
| ret |= addReferenceTypeNode(server, "NonHierarchicalReferences", NULL, |
| UA_NS0ID_NONHIERARCHICALREFERENCES, true, false, UA_NS0ID_REFERENCES); |
| |
| ret |= addReferenceTypeNode(server, "HasChild", NULL, UA_NS0ID_HASCHILD, |
| true, false, UA_NS0ID_HIERARCHICALREFERENCES); |
| |
| ret |= addReferenceTypeNode(server, "Organizes", "OrganizedBy", UA_NS0ID_ORGANIZES, |
| false, false, UA_NS0ID_HIERARCHICALREFERENCES); |
| |
| ret |= addReferenceTypeNode(server, "HasEventSource", "EventSourceOf", UA_NS0ID_HASEVENTSOURCE, |
| false, false, UA_NS0ID_HIERARCHICALREFERENCES); |
| |
| ret |= addReferenceTypeNode(server, "HasModellingRule", "ModellingRuleOf", UA_NS0ID_HASMODELLINGRULE, |
| false, false, UA_NS0ID_NONHIERARCHICALREFERENCES); |
| |
| ret |= addReferenceTypeNode(server, "HasEncoding", "EncodingOf", UA_NS0ID_HASENCODING, |
| false, false, UA_NS0ID_NONHIERARCHICALREFERENCES); |
| |
| ret |= addReferenceTypeNode(server, "HasDescription", "DescriptionOf", UA_NS0ID_HASDESCRIPTION, |
| false, false, UA_NS0ID_NONHIERARCHICALREFERENCES); |
| |
| ret |= addReferenceTypeNode(server, "HasTypeDefinition", "TypeDefinitionOf", UA_NS0ID_HASTYPEDEFINITION, |
| false, false, UA_NS0ID_NONHIERARCHICALREFERENCES); |
| |
| ret |= addReferenceTypeNode(server, "GeneratesEvent", "GeneratedBy", UA_NS0ID_GENERATESEVENT, |
| false, false, UA_NS0ID_NONHIERARCHICALREFERENCES); |
| |
| /* Complete bootstrap of Aggregates */ |
| ret |= addNode_finish(server, UA_NS0ID_AGGREGATES, UA_NS0ID_HASCHILD, UA_NS0ID_HASSUBTYPE); |
| |
| /* Complete bootstrap of HasSubtype */ |
| ret |= addNode_finish(server, UA_NS0ID_HASSUBTYPE, UA_NS0ID_HASCHILD, UA_NS0ID_HASSUBTYPE); |
| |
| ret |= addReferenceTypeNode(server, "HasProperty", "PropertyOf", UA_NS0ID_HASPROPERTY, |
| false, false, UA_NS0ID_AGGREGATES); |
| |
| ret |= addReferenceTypeNode(server, "HasComponent", "ComponentOf", UA_NS0ID_HASCOMPONENT, |
| false, false, UA_NS0ID_AGGREGATES); |
| |
| ret |= addReferenceTypeNode(server, "HasNotifier", "NotifierOf", UA_NS0ID_HASNOTIFIER, |
| false, false, UA_NS0ID_HASEVENTSOURCE); |
| |
| ret |= addReferenceTypeNode(server, "HasOrderedComponent", "OrderedComponentOf", |
| UA_NS0ID_HASORDEREDCOMPONENT, false, false, UA_NS0ID_HASCOMPONENT); |
| |
| /**************/ |
| /* Data Types */ |
| /**************/ |
| |
| /* Bootstrap BaseDataType */ |
| UA_DataTypeAttributes basedatatype_attr = UA_DataTypeAttributes_default; |
| basedatatype_attr.displayName = UA_LOCALIZEDTEXT("", "BaseDataType"); |
| basedatatype_attr.isAbstract = true; |
| ret |= addNode_raw(server, UA_NODECLASS_DATATYPE, UA_NS0ID_BASEDATATYPE, "BaseDataType", |
| &basedatatype_attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES]); |
| |
| /*****************/ |
| /* VariableTypes */ |
| /*****************/ |
| |
| UA_VariableTypeAttributes basevar_attr = UA_VariableTypeAttributes_default; |
| basevar_attr.displayName = UA_LOCALIZEDTEXT("", "BaseVariableType"); |
| basevar_attr.isAbstract = true; |
| basevar_attr.valueRank = UA_VALUERANK_ANY; |
| basevar_attr.dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE); |
| ret |= addNode_raw(server, UA_NODECLASS_VARIABLETYPE, UA_NS0ID_BASEVARIABLETYPE, "BaseVariableType", |
| &basevar_attr, &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES]); |
| |
| UA_VariableTypeAttributes bdv_attr = UA_VariableTypeAttributes_default; |
| bdv_attr.displayName = UA_LOCALIZEDTEXT("", "BaseDataVariableType"); |
| bdv_attr.dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE); |
| bdv_attr.valueRank = UA_VALUERANK_ANY; |
| ret |= UA_Server_addVariableTypeNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), |
| UA_NODEID_NUMERIC(0, UA_NS0ID_BASEVARIABLETYPE), |
| UA_NODEID_NULL, UA_QUALIFIEDNAME(0, "BaseDataVariableType"), |
| UA_NODEID_NULL, bdv_attr, NULL, NULL); |
| |
| UA_VariableTypeAttributes prop_attr = UA_VariableTypeAttributes_default; |
| prop_attr.displayName = UA_LOCALIZEDTEXT("", "PropertyType"); |
| prop_attr.dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE); |
| prop_attr.valueRank = UA_VALUERANK_ANY; |
| ret |= UA_Server_addVariableTypeNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE), |
| UA_NODEID_NUMERIC(0, UA_NS0ID_BASEVARIABLETYPE), |
| UA_NODEID_NULL, UA_QUALIFIEDNAME(0, "PropertyType"), |
| UA_NODEID_NULL, prop_attr, NULL, NULL); |
| |
| /***************/ |
| /* ObjectTypes */ |
| /***************/ |
| |
| UA_ObjectTypeAttributes baseobj_attr = UA_ObjectTypeAttributes_default; |
| baseobj_attr.displayName = UA_LOCALIZEDTEXT("", "BaseObjectType"); |
| ret |= addNode_raw(server, UA_NODECLASS_OBJECTTYPE, UA_NS0ID_BASEOBJECTTYPE, "BaseObjectType", |
| &baseobj_attr, &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES]); |
| |
| UA_ObjectTypeAttributes folder_attr = UA_ObjectTypeAttributes_default; |
| folder_attr.displayName = UA_LOCALIZEDTEXT("", "FolderType"); |
| ret |= UA_Server_addObjectTypeNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), |
| UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE), |
| UA_NODEID_NULL, UA_QUALIFIEDNAME(0, "FolderType"), |
| folder_attr, NULL, NULL); |
| |
| /******************/ |
| /* Root and below */ |
| /******************/ |
| |
| ret |= addObjectNode(server, "Root", UA_NS0ID_ROOTFOLDER, 0, 0, UA_NS0ID_FOLDERTYPE); |
| |
| ret |= addObjectNode(server, "Objects", UA_NS0ID_OBJECTSFOLDER, UA_NS0ID_ROOTFOLDER, |
| UA_NS0ID_ORGANIZES, UA_NS0ID_FOLDERTYPE); |
| |
| ret |= addObjectNode(server, "Types", UA_NS0ID_TYPESFOLDER, UA_NS0ID_ROOTFOLDER, |
| UA_NS0ID_ORGANIZES, UA_NS0ID_FOLDERTYPE); |
| |
| ret |= addObjectNode(server, "ReferenceTypes", UA_NS0ID_REFERENCETYPESFOLDER, UA_NS0ID_TYPESFOLDER, |
| UA_NS0ID_ORGANIZES, UA_NS0ID_FOLDERTYPE); |
| ret |= addNode_finish(server, UA_NS0ID_REFERENCES, UA_NS0ID_REFERENCETYPESFOLDER, |
| UA_NS0ID_ORGANIZES); |
| |
| ret |= addObjectNode(server, "DataTypes", UA_NS0ID_DATATYPESFOLDER, UA_NS0ID_TYPESFOLDER, |
| UA_NS0ID_ORGANIZES, UA_NS0ID_FOLDERTYPE); |
| ret |= addNode_finish(server, UA_NS0ID_BASEDATATYPE, UA_NS0ID_DATATYPESFOLDER, |
| UA_NS0ID_ORGANIZES); |
| |
| ret |= addObjectNode(server, "VariableTypes", UA_NS0ID_VARIABLETYPESFOLDER, UA_NS0ID_TYPESFOLDER, |
| UA_NS0ID_ORGANIZES, UA_NS0ID_FOLDERTYPE); |
| ret |= addNode_finish(server, UA_NS0ID_BASEVARIABLETYPE, UA_NS0ID_VARIABLETYPESFOLDER, |
| UA_NS0ID_ORGANIZES); |
| |
| ret |= addObjectNode(server, "ObjectTypes", UA_NS0ID_OBJECTTYPESFOLDER, UA_NS0ID_TYPESFOLDER, |
| UA_NS0ID_ORGANIZES, UA_NS0ID_FOLDERTYPE); |
| ret |= addNode_finish(server, UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_OBJECTTYPESFOLDER, |
| UA_NS0ID_ORGANIZES); |
| |
| ret |= addObjectNode(server, "EventTypes", UA_NS0ID_EVENTTYPESFOLDER, UA_NS0ID_TYPESFOLDER, |
| UA_NS0ID_ORGANIZES, UA_NS0ID_FOLDERTYPE); |
| |
| ret |= addObjectNode(server, "Views", UA_NS0ID_VIEWSFOLDER, UA_NS0ID_ROOTFOLDER, |
| UA_NS0ID_ORGANIZES, UA_NS0ID_FOLDERTYPE); |
| |
| if(ret != UA_STATUSCODE_GOOD) |
| ret = UA_STATUSCODE_BADINTERNALERROR; |
| |
| return ret; |
| } |
| |
| /****************/ |
| /* Data Sources */ |
| /****************/ |
| |
| static UA_StatusCode |
| readStatus(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, |
| const UA_NodeId *nodeId, void *nodeContext, UA_Boolean sourceTimestamp, |
| const UA_NumericRange *range, UA_DataValue *value) { |
| if(range) { |
| value->hasStatus = true; |
| value->status = UA_STATUSCODE_BADINDEXRANGEINVALID; |
| return UA_STATUSCODE_GOOD; |
| } |
| |
| if(sourceTimestamp) { |
| value->hasSourceTimestamp = true; |
| value->sourceTimestamp = UA_DateTime_now(); |
| } |
| |
| void *data = NULL; |
| |
| UA_assert(nodeId->identifierType == UA_NODEIDTYPE_NUMERIC); |
| |
| switch(nodeId->identifier.numeric) { |
| case UA_NS0ID_SERVER_SERVERSTATUS_SECONDSTILLSHUTDOWN: { |
| UA_UInt32 *shutdown = UA_UInt32_new(); |
| if(!shutdown) |
| return UA_STATUSCODE_BADOUTOFMEMORY; |
| if(server->endTime != 0) |
| *shutdown = (UA_UInt32)((server->endTime - UA_DateTime_now()) / UA_DATETIME_SEC); |
| value->value.data = shutdown; |
| value->value.type = &UA_TYPES[UA_TYPES_UINT32]; |
| value->hasValue = true; |
| return UA_STATUSCODE_GOOD; |
| } |
| |
| case UA_NS0ID_SERVER_SERVERSTATUS_STATE: { |
| UA_ServerState *state = UA_ServerState_new(); |
| if(!state) |
| return UA_STATUSCODE_BADOUTOFMEMORY; |
| if(server->endTime != 0) |
| *state = UA_SERVERSTATE_SHUTDOWN; |
| value->value.data = state; |
| value->value.type = &UA_TYPES[UA_TYPES_SERVERSTATE]; |
| value->hasValue = true; |
| return UA_STATUSCODE_GOOD; |
| } |
| |
| case UA_NS0ID_SERVER_SERVERSTATUS: { |
| UA_ServerStatusDataType *statustype = UA_ServerStatusDataType_new(); |
| if(!statustype) |
| return UA_STATUSCODE_BADOUTOFMEMORY; |
| statustype->startTime = server->startTime; |
| statustype->currentTime = UA_DateTime_now(); |
| |
| statustype->state = UA_SERVERSTATE_RUNNING; |
| statustype->secondsTillShutdown = 0; |
| if(server->endTime != 0) { |
| statustype->state = UA_SERVERSTATE_SHUTDOWN; |
| statustype->secondsTillShutdown = (UA_UInt32)((server->endTime - UA_DateTime_now()) / UA_DATETIME_SEC); |
| } |
| |
| value->value.data = statustype; |
| value->value.type = &UA_TYPES[UA_TYPES_SERVERSTATUSDATATYPE]; |
| value->hasValue = true; |
| return UA_BuildInfo_copy(&server->config.buildInfo, &statustype->buildInfo); |
| } |
| |
| case UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO: |
| value->value.type = &UA_TYPES[UA_TYPES_BUILDINFO]; |
| data = &server->config.buildInfo; |
| break; |
| |
| case UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTURI: |
| value->value.type = &UA_TYPES[UA_TYPES_STRING]; |
| data = &server->config.buildInfo.productUri; |
| break; |
| |
| case UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_MANUFACTURERNAME: |
| value->value.type = &UA_TYPES[UA_TYPES_STRING]; |
| data = &server->config.buildInfo.manufacturerName; |
| break; |
| |
| case UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTNAME: |
| value->value.type = &UA_TYPES[UA_TYPES_STRING]; |
| data = &server->config.buildInfo.productName; |
| break; |
| |
| case UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_SOFTWAREVERSION: |
| value->value.type = &UA_TYPES[UA_TYPES_STRING]; |
| data = &server->config.buildInfo.softwareVersion; |
| break; |
| |
| case UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDNUMBER: |
| value->value.type = &UA_TYPES[UA_TYPES_STRING]; |
| data = &server->config.buildInfo.buildNumber; |
| break; |
| |
| case UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDDATE: |
| value->value.type = &UA_TYPES[UA_TYPES_DATETIME]; |
| data = &server->config.buildInfo.buildDate; |
| break; |
| |
| default: |
| value->hasStatus = true; |
| value->status = UA_STATUSCODE_BADINTERNALERROR; |
| return UA_STATUSCODE_GOOD; |
| } |
| |
| value->value.data = UA_new(value->value.type); |
| if(!value->value.data) { |
| value->value.type = NULL; |
| return UA_STATUSCODE_BADOUTOFMEMORY; |
| } |
| |
| value->hasValue = true; |
| return UA_copy(data, value->value.data, value->value.type); |
| } |
| |
| #ifdef UA_GENERATED_NAMESPACE_ZERO |
| static UA_StatusCode |
| readServiceLevel(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, |
| const UA_NodeId *nodeId, void *nodeContext, UA_Boolean includeSourceTimeStamp, |
| const UA_NumericRange *range, UA_DataValue *value) { |
| if(range) { |
| value->hasStatus = true; |
| value->status = UA_STATUSCODE_BADINDEXRANGEINVALID; |
| return UA_STATUSCODE_GOOD; |
| } |
| |
| value->value.type = &UA_TYPES[UA_TYPES_BYTE]; |
| value->value.arrayLength = 0; |
| UA_Byte *byte = UA_Byte_new(); |
| *byte = 255; |
| value->value.data = byte; |
| value->value.arrayDimensionsSize = 0; |
| value->value.arrayDimensions = NULL; |
| value->hasValue = true; |
| if(includeSourceTimeStamp) { |
| value->hasSourceTimestamp = true; |
| value->sourceTimestamp = UA_DateTime_now(); |
| } |
| return UA_STATUSCODE_GOOD; |
| } |
| |
| static UA_StatusCode |
| readAuditing(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, |
| const UA_NodeId *nodeId, void *nodeContext, UA_Boolean includeSourceTimeStamp, |
| const UA_NumericRange *range, UA_DataValue *value) { |
| if(range) { |
| value->hasStatus = true; |
| value->status = UA_STATUSCODE_BADINDEXRANGEINVALID; |
| return UA_STATUSCODE_GOOD; |
| } |
| |
| value->value.type = &UA_TYPES[UA_TYPES_BOOLEAN]; |
| value->value.arrayLength = 0; |
| UA_Boolean *boolean = UA_Boolean_new(); |
| *boolean = false; |
| value->value.data = boolean; |
| value->value.arrayDimensionsSize = 0; |
| value->value.arrayDimensions = NULL; |
| value->hasValue = true; |
| if(includeSourceTimeStamp) { |
| value->hasSourceTimestamp = true; |
| value->sourceTimestamp = UA_DateTime_now(); |
| } |
| return UA_STATUSCODE_GOOD; |
| } |
| #endif |
| |
| static UA_StatusCode |
| readNamespaces(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, |
| const UA_NodeId *nodeid, void *nodeContext, UA_Boolean includeSourceTimeStamp, |
| const UA_NumericRange *range, |
| UA_DataValue *value) { |
| /* ensure that the uri for ns1 is set up from the app description */ |
| setupNs1Uri(server); |
| |
| if(range) { |
| value->hasStatus = true; |
| value->status = UA_STATUSCODE_BADINDEXRANGEINVALID; |
| return UA_STATUSCODE_GOOD; |
| } |
| UA_StatusCode retval; |
| retval = UA_Variant_setArrayCopy(&value->value, server->namespaces, |
| server->namespacesSize, &UA_TYPES[UA_TYPES_STRING]); |
| if(retval != UA_STATUSCODE_GOOD) |
| return retval; |
| value->hasValue = true; |
| if(includeSourceTimeStamp) { |
| value->hasSourceTimestamp = true; |
| value->sourceTimestamp = UA_DateTime_now(); |
| } |
| return UA_STATUSCODE_GOOD; |
| } |
| |
| static UA_StatusCode |
| writeNamespaces(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, |
| const UA_NodeId *nodeid, void *nodeContext, const UA_NumericRange *range, |
| const UA_DataValue *value) { |
| /* Check the data type */ |
| if(!value->hasValue || |
| value->value.type != &UA_TYPES[UA_TYPES_STRING]) |
| return UA_STATUSCODE_BADTYPEMISMATCH; |
| |
| /* Check that the variant is not empty */ |
| if(!value->value.data) |
| return UA_STATUSCODE_BADTYPEMISMATCH; |
| |
| /* TODO: Writing with a range is not implemented */ |
| if(range) |
| return UA_STATUSCODE_BADINTERNALERROR; |
| |
| UA_String *newNamespaces = (UA_String*)value->value.data; |
| size_t newNamespacesSize = value->value.arrayLength; |
| |
| /* Test if we append to the existing namespaces */ |
| if(newNamespacesSize <= server->namespacesSize) |
| return UA_STATUSCODE_BADTYPEMISMATCH; |
| |
| /* ensure that the uri for ns1 is set up from the app description */ |
| setupNs1Uri(server); |
| |
| /* Test if the existing namespaces are unchanged */ |
| for(size_t i = 0; i < server->namespacesSize; ++i) { |
| if(!UA_String_equal(&server->namespaces[i], &newNamespaces[i])) |
| return UA_STATUSCODE_BADINTERNALERROR; |
| } |
| |
| /* Add namespaces */ |
| for(size_t i = server->namespacesSize; i < newNamespacesSize; ++i) |
| addNamespace(server, newNamespaces[i]); |
| return UA_STATUSCODE_GOOD; |
| } |
| |
| static UA_StatusCode |
| readCurrentTime(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, |
| const UA_NodeId *nodeid, void *nodeContext, UA_Boolean sourceTimeStamp, |
| const UA_NumericRange *range, UA_DataValue *value) { |
| if(range) { |
| value->hasStatus = true; |
| value->status = UA_STATUSCODE_BADINDEXRANGEINVALID; |
| return UA_STATUSCODE_GOOD; |
| } |
| UA_DateTime currentTime = UA_DateTime_now(); |
| UA_StatusCode retval = UA_Variant_setScalarCopy(&value->value, ¤tTime, |
| &UA_TYPES[UA_TYPES_DATETIME]); |
| if(retval != UA_STATUSCODE_GOOD) |
| return retval; |
| value->hasValue = true; |
| if(sourceTimeStamp) { |
| value->hasSourceTimestamp = true; |
| value->sourceTimestamp = currentTime; |
| } |
| return UA_STATUSCODE_GOOD; |
| } |
| |
| #ifdef UA_GENERATED_NAMESPACE_ZERO |
| static UA_StatusCode |
| readMinSamplingInterval(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, |
| const UA_NodeId *nodeid, void *nodeContext, UA_Boolean includeSourceTimeStamp, |
| const UA_NumericRange *range, |
| UA_DataValue *value) { |
| if(range) { |
| value->hasStatus = true; |
| value->status = UA_STATUSCODE_BADINDEXRANGEINVALID; |
| return UA_STATUSCODE_GOOD; |
| } |
| |
| UA_StatusCode retval; |
| retval = UA_Variant_setScalarCopy(&value->value, |
| &server->config.samplingIntervalLimits.min, |
| &UA_TYPES[UA_TYPES_DURATION]); |
| if(retval != UA_STATUSCODE_GOOD) |
| return retval; |
| value->hasValue = true; |
| if(includeSourceTimeStamp) { |
| value->hasSourceTimestamp = true; |
| value->sourceTimestamp = UA_DateTime_now(); |
| } |
| return UA_STATUSCODE_GOOD; |
| } |
| #endif |
| |
| #if defined(UA_GENERATED_NAMESPACE_ZERO) && defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS) |
| static UA_StatusCode |
| readMonitoredItems(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, |
| const UA_NodeId *methodId, void *methodContext, const UA_NodeId *objectId, |
| void *objectContext, size_t inputSize, |
| const UA_Variant *input, size_t outputSize, |
| UA_Variant *output) { |
| UA_Session *session = UA_SessionManager_getSessionById(&server->sessionManager, sessionId); |
| if(!session) |
| return UA_STATUSCODE_BADINTERNALERROR; |
| if (inputSize == 0 || !input[0].data) |
| return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID; |
| UA_UInt32 subscriptionId = *((UA_UInt32*)(input[0].data)); |
| UA_Subscription* subscription = UA_Session_getSubscriptionById(session, subscriptionId); |
| if(!subscription) |
| { |
| if(LIST_EMPTY(&session->serverSubscriptions)) |
| { |
| UA_Variant_setArray(&output[0], UA_Array_new(0, &UA_TYPES[UA_TYPES_UINT32]), |
| 0, &UA_TYPES[UA_TYPES_UINT32]); |
| UA_Variant_setArray(&output[1], UA_Array_new(0, &UA_TYPES[UA_TYPES_UINT32]), |
| 0, &UA_TYPES[UA_TYPES_UINT32]); |
| |
| return UA_STATUSCODE_BADNOMATCH; |
| } |
| |
| return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID; |
| } |
| |
| UA_UInt32 sizeOfOutput = 0; |
| UA_MonitoredItem* monitoredItem; |
| LIST_FOREACH(monitoredItem, &subscription->monitoredItems, listEntry) { |
| ++sizeOfOutput; |
| } |
| if(sizeOfOutput==0) |
| return UA_STATUSCODE_GOOD; |
| |
| UA_UInt32* clientHandles = (UA_UInt32 *)UA_Array_new(sizeOfOutput, &UA_TYPES[UA_TYPES_UINT32]); |
| UA_UInt32* serverHandles = (UA_UInt32 *)UA_Array_new(sizeOfOutput, &UA_TYPES[UA_TYPES_UINT32]); |
| UA_UInt32 i = 0; |
| LIST_FOREACH(monitoredItem, &subscription->monitoredItems, listEntry) { |
| clientHandles[i] = monitoredItem->clientHandle; |
| serverHandles[i] = monitoredItem->monitoredItemId; |
| ++i; |
| } |
| UA_Variant_setArray(&output[0], serverHandles, sizeOfOutput, &UA_TYPES[UA_TYPES_UINT32]); |
| UA_Variant_setArray(&output[1], clientHandles, sizeOfOutput, &UA_TYPES[UA_TYPES_UINT32]); |
| return UA_STATUSCODE_GOOD; |
| } |
| #endif /* defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS) */ |
| |
| UA_StatusCode |
| writeNs0VariableArray(UA_Server *server, UA_UInt32 id, void *v, |
| size_t length, const UA_DataType *type) { |
| UA_Variant var; |
| UA_Variant_init(&var); |
| UA_Variant_setArray(&var, v, length, type); |
| return UA_Server_writeValue(server, UA_NODEID_NUMERIC(0, id), var); |
| } |
| |
| #ifndef UA_GENERATED_NAMESPACE_ZERO |
| static UA_StatusCode |
| addVariableNode(UA_Server *server, char* name, UA_UInt32 variableid, |
| UA_UInt32 parentid, UA_UInt32 referenceid, |
| UA_Int32 valueRank, UA_UInt32 dataType) { |
| UA_VariableAttributes attr = UA_VariableAttributes_default; |
| attr.displayName = UA_LOCALIZEDTEXT("", name); |
| attr.dataType = UA_NODEID_NUMERIC(0, dataType); |
| attr.valueRank = valueRank; |
| attr.accessLevel = UA_ACCESSLEVELMASK_READ; |
| return UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(0, variableid), |
| UA_NODEID_NUMERIC(0, parentid), UA_NODEID_NUMERIC(0, referenceid), |
| UA_QUALIFIEDNAME(0, name), |
| UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), |
| attr, NULL, NULL); |
| } |
| |
| /* A minimal server object that is not complete and does not use the mandated |
| * references to a server type. To be used on very constrained devices. */ |
| static UA_StatusCode |
| UA_Server_minimalServerObject(UA_Server *server) { |
| /* Server */ |
| UA_StatusCode retval = addObjectNode(server, "Server", UA_NS0ID_SERVER, UA_NS0ID_OBJECTSFOLDER, |
| UA_NS0ID_ORGANIZES, UA_NS0ID_BASEOBJECTTYPE); |
| |
| /* Use a valuerank of -2 for now. The array is added later on and the valuerank set to 1. */ |
| retval |= addVariableNode(server, "ServerArray", UA_NS0ID_SERVER_SERVERARRAY, |
| UA_NS0ID_SERVER, UA_NS0ID_HASPROPERTY, |
| UA_VALUERANK_ANY, UA_NS0ID_BASEDATATYPE); |
| retval |= addVariableNode(server, "NamespaceArray", UA_NS0ID_SERVER_NAMESPACEARRAY, |
| UA_NS0ID_SERVER, UA_NS0ID_HASPROPERTY, |
| UA_VALUERANK_ANY, UA_NS0ID_BASEDATATYPE); |
| |
| retval |= addVariableNode(server, "ServerStatus", UA_NS0ID_SERVER_SERVERSTATUS, |
| UA_NS0ID_SERVER, UA_NS0ID_HASCOMPONENT, |
| UA_VALUERANK_SCALAR, UA_NS0ID_BASEDATATYPE); |
| |
| retval |= addVariableNode(server, "CurrentTime", UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME, |
| UA_NS0ID_SERVER_SERVERSTATUS, UA_NS0ID_HASCOMPONENT, |
| UA_VALUERANK_SCALAR, UA_NS0ID_BASEDATATYPE); |
| |
| retval |= addVariableNode(server, "State", UA_NS0ID_SERVER_SERVERSTATUS_STATE, |
| UA_NS0ID_SERVER_SERVERSTATUS, UA_NS0ID_HASCOMPONENT, |
| UA_VALUERANK_SCALAR, UA_NS0ID_BASEDATATYPE); |
| |
| retval |= addVariableNode(server, "BuildInfo", UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO, |
| UA_NS0ID_SERVER_SERVERSTATUS, UA_NS0ID_HASCOMPONENT, |
| UA_VALUERANK_SCALAR, UA_NS0ID_BASEDATATYPE); |
| |
| retval |= addVariableNode(server, "ProductUri", UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTURI, |
| UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO, UA_NS0ID_HASCOMPONENT, |
| UA_VALUERANK_SCALAR, UA_NS0ID_BASEDATATYPE); |
| |
| retval |= addVariableNode(server, "ManufacturerName", |
| UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_MANUFACTURERNAME, |
| UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO, UA_NS0ID_HASCOMPONENT, |
| UA_VALUERANK_SCALAR, UA_NS0ID_BASEDATATYPE); |
| |
| retval |= addVariableNode(server, "ProductName", |
| UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTNAME, |
| UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO, UA_NS0ID_HASCOMPONENT, |
| UA_VALUERANK_SCALAR, UA_NS0ID_BASEDATATYPE); |
| |
| retval |= addVariableNode(server, "SoftwareVersion", |
| UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_SOFTWAREVERSION, |
| UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO, UA_NS0ID_HASCOMPONENT, |
| UA_VALUERANK_SCALAR, UA_NS0ID_BASEDATATYPE); |
| |
| retval |= addVariableNode(server, "BuildNumber", |
| UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDNUMBER, |
| UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO, UA_NS0ID_HASCOMPONENT, |
| UA_VALUERANK_SCALAR, UA_NS0ID_BASEDATATYPE); |
| |
| retval |= addVariableNode(server, "BuildDate", |
| UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDDATE, |
| UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO, UA_NS0ID_HASCOMPONENT, |
| UA_VALUERANK_SCALAR, UA_NS0ID_BASEDATATYPE); |
| |
| return retval; |
| } |
| |
| #else |
| |
| static UA_StatusCode |
| writeNs0Variable(UA_Server *server, UA_UInt32 id, void *v, const UA_DataType *type) { |
| UA_Variant var; |
| UA_Variant_init(&var); |
| UA_Variant_setScalar(&var, v, type); |
| return UA_Server_writeValue(server, UA_NODEID_NUMERIC(0, id), var); |
| } |
| |
| static void |
| addModellingRules(UA_Server *server) { |
| /* Test if the ModellingRules folder was added. (Only for the full ns0.) */ |
| UA_NodeClass mrnc = UA_NODECLASS_UNSPECIFIED; |
| UA_StatusCode retval = UA_Server_readNodeClass(server, |
| UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MODELLINGRULES), |
| &mrnc); |
| if(retval != UA_STATUSCODE_GOOD) |
| return; |
| |
| /* Add ExposesItsArray */ |
| UA_Server_addReference(server, |
| UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MODELLINGRULES), |
| UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), |
| UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_EXPOSESITSARRAY), |
| true); |
| |
| /* Add Mandatory */ |
| UA_Server_addReference(server, |
| UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MODELLINGRULES), |
| UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), |
| UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY), |
| true); |
| |
| |
| /* Add MandatoryPlaceholder */ |
| UA_Server_addReference(server, |
| UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MODELLINGRULES), |
| UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), |
| UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORYPLACEHOLDER), |
| true); |
| |
| /* Add Optional */ |
| UA_Server_addReference(server, |
| UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MODELLINGRULES), |
| UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), |
| UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_OPTIONAL), |
| true); |
| |
| /* Add OptionalPlaceholder */ |
| UA_Server_addReference(server, |
| UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MODELLINGRULES), |
| UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), |
| UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_OPTIONALPLACEHOLDER), |
| true); |
| } |
| |
| #endif |
| |
| /* Initialize the nodeset 0 by using the generated code of the nodeset compiler. |
| * This also initialized the data sources for various variables, such as for |
| * example server time. */ |
| UA_StatusCode |
| UA_Server_initNS0(UA_Server *server) { |
| |
| /* Initialize base nodes which are always required an cannot be created |
| * through the NS compiler */ |
| server->bootstrapNS0 = true; |
| UA_StatusCode retVal = UA_Server_createNS0_base(server); |
| server->bootstrapNS0 = false; |
| if(retVal != UA_STATUSCODE_GOOD) |
| return retVal; |
| |
| #ifdef UA_GENERATED_NAMESPACE_ZERO |
| /* Load nodes and references generated from the XML ns0 definition */ |
| retVal = namespace0_generated(server); |
| #else |
| /* Create a minimal server object */ |
| retVal = UA_Server_minimalServerObject(server); |
| #endif |
| |
| if(retVal != UA_STATUSCODE_GOOD) { |
| UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, |
| "Initialization of Namespace 0 (before bootstrapping) " |
| "failed with %s. See previous outputs for any error messages.", |
| UA_StatusCode_name(retVal)); |
| return UA_STATUSCODE_BADINTERNALERROR; |
| } |
| |
| /* NamespaceArray */ |
| UA_DataSource namespaceDataSource = {readNamespaces, writeNamespaces}; |
| retVal |= UA_Server_setVariableNode_dataSource(server, |
| UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACEARRAY), |
| namespaceDataSource); |
| retVal |= UA_Server_writeValueRank(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACEARRAY), 1); |
| |
| /* ServerArray */ |
| retVal |= writeNs0VariableArray(server, UA_NS0ID_SERVER_SERVERARRAY, |
| &server->config.applicationDescription.applicationUri, |
| 1, &UA_TYPES[UA_TYPES_STRING]); |
| retVal |= UA_Server_writeValueRank(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERARRAY), 1); |
| |
| /* ServerStatus */ |
| UA_DataSource serverStatus = {readStatus, NULL}; |
| retVal |= UA_Server_setVariableNode_dataSource(server, |
| UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS), serverStatus); |
| |
| /* StartTime will be sampled in UA_Server_run_startup()*/ |
| |
| /* CurrentTime */ |
| UA_DataSource currentTime = {readCurrentTime, NULL}; |
| retVal |= UA_Server_setVariableNode_dataSource(server, |
| UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME), currentTime); |
| |
| /* State */ |
| retVal |= UA_Server_setVariableNode_dataSource(server, |
| UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STATE), |
| serverStatus); |
| |
| /* BuildInfo */ |
| retVal |= UA_Server_setVariableNode_dataSource(server, |
| UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO), serverStatus); |
| |
| /* BuildInfo - ProductUri */ |
| retVal |= UA_Server_setVariableNode_dataSource(server, |
| UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTURI), |
| serverStatus); |
| |
| /* BuildInfo - ManufacturerName */ |
| retVal |= UA_Server_setVariableNode_dataSource(server, |
| UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_MANUFACTURERNAME), |
| serverStatus); |
| |
| /* BuildInfo - ProductName */ |
| retVal |= UA_Server_setVariableNode_dataSource(server, |
| UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTNAME), |
| serverStatus); |
| |
| /* BuildInfo - SoftwareVersion */ |
| retVal |= UA_Server_setVariableNode_dataSource(server, |
| UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_SOFTWAREVERSION), |
| serverStatus); |
| |
| /* BuildInfo - BuildNumber */ |
| retVal |= UA_Server_setVariableNode_dataSource(server, |
| UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDNUMBER), |
| serverStatus); |
| |
| /* BuildInfo - BuildDate */ |
| retVal |= UA_Server_setVariableNode_dataSource(server, |
| UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDDATE), |
| serverStatus); |
| |
| #ifdef UA_GENERATED_NAMESPACE_ZERO |
| |
| /* SecondsTillShutdown */ |
| retVal |= UA_Server_setVariableNode_dataSource(server, |
| UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SECONDSTILLSHUTDOWN), |
| serverStatus); |
| |
| /* ShutDownReason */ |
| UA_LocalizedText shutdownReason; |
| UA_LocalizedText_init(&shutdownReason); |
| retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERSTATUS_SHUTDOWNREASON, |
| &shutdownReason, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]); |
| |
| /* ServiceLevel */ |
| UA_DataSource serviceLevel = {readServiceLevel, NULL}; |
| retVal |= UA_Server_setVariableNode_dataSource(server, |
| UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVICELEVEL), serviceLevel); |
| |
| /* ServerDiagnostics - ServerDiagnosticsSummary */ |
| UA_ServerDiagnosticsSummaryDataType serverDiagnosticsSummary; |
| UA_ServerDiagnosticsSummaryDataType_init(&serverDiagnosticsSummary); |
| retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY, |
| &serverDiagnosticsSummary, |
| &UA_TYPES[UA_TYPES_SERVERDIAGNOSTICSSUMMARYDATATYPE]); |
| |
| /* ServerDiagnostics - EnabledFlag */ |
| UA_Boolean enabledFlag = false; |
| retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_ENABLEDFLAG, |
| &enabledFlag, &UA_TYPES[UA_TYPES_BOOLEAN]); |
| |
| /* According to Specification part-5 - pg.no-11(PDF pg.no-29), when the ServerDiagnostics is disabled the client |
| * may modify the value of enabledFlag=true in the server. By default, this node have CurrentRead/Write access. |
| * In CTT, Subscription_Minimum_1/002.js test will modify the above flag. This will not be a problem when build |
| * configuration is set at UA_NAMESPACE_ZERO="REDUCED" as NodeIds will not be present. When UA_NAMESPACE_ZERO="FULL", |
| * the test will fail. Hence made the NodeId as read only */ |
| retVal |= UA_Server_writeAccessLevel(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_ENABLEDFLAG), |
| UA_ACCESSLEVELMASK_READ); |
| |
| /* Auditing */ |
| UA_DataSource auditing = {readAuditing, NULL}; |
| retVal |= UA_Server_setVariableNode_dataSource(server, |
| UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_AUDITING), auditing); |
| |
| /* Redundancy Support */ |
| UA_RedundancySupport redundancySupport = UA_REDUNDANCYSUPPORT_NONE; |
| retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERREDUNDANCY_REDUNDANCYSUPPORT, |
| &redundancySupport, &UA_TYPES[UA_TYPES_REDUNDANCYSUPPORT]); |
| |
| /* Remove unused subtypes of ServerRedundancy */ |
| UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERREDUNDANCY_CURRENTSERVERID), true); |
| UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERREDUNDANCY_REDUNDANTSERVERARRAY), true); |
| UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERREDUNDANCY_SERVERURIARRAY), true); |
| UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERREDUNDANCY_SERVERNETWORKGROUPS), true); |
| |
| /* ServerCapabilities - LocaleIdArray */ |
| UA_LocaleId locale_en = UA_STRING("en"); |
| retVal |= writeNs0VariableArray(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_LOCALEIDARRAY, |
| &locale_en, 1, &UA_TYPES[UA_TYPES_LOCALEID]); |
| |
| /* ServerCapabilities - MaxBrowseContinuationPoints */ |
| UA_UInt16 maxBrowseContinuationPoints = UA_MAXCONTINUATIONPOINTS; |
| retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXBROWSECONTINUATIONPOINTS, |
| &maxBrowseContinuationPoints, &UA_TYPES[UA_TYPES_UINT16]); |
| |
| /* ServerProfileArray */ |
| UA_String profileArray[3]; |
| UA_UInt16 profileArraySize = 0; |
| #define ADDPROFILEARRAY(x) profileArray[profileArraySize++] = UA_STRING(x) |
| ADDPROFILEARRAY("http://opcfoundation.org/UA-Profile/Server/MicroEmbeddedDevice"); |
| #ifdef UA_ENABLE_NODEMANAGEMENT |
| ADDPROFILEARRAY("http://opcfoundation.org/UA-Profile/Server/NodeManagement"); |
| #endif |
| #ifdef UA_ENABLE_METHODCALLS |
| ADDPROFILEARRAY("http://opcfoundation.org/UA-Profile/Server/Methods"); |
| #endif |
| retVal |= writeNs0VariableArray(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_SERVERPROFILEARRAY, |
| profileArray, profileArraySize, &UA_TYPES[UA_TYPES_STRING]); |
| |
| /* ServerCapabilities - MaxQueryContinuationPoints */ |
| UA_UInt16 maxQueryContinuationPoints = 0; |
| retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXQUERYCONTINUATIONPOINTS, |
| &maxQueryContinuationPoints, &UA_TYPES[UA_TYPES_UINT16]); |
| |
| /* ServerCapabilities - MaxHistoryContinuationPoints */ |
| UA_UInt16 maxHistoryContinuationPoints = 0; |
| retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXHISTORYCONTINUATIONPOINTS, |
| &maxHistoryContinuationPoints, &UA_TYPES[UA_TYPES_UINT16]); |
| |
| /* ServerCapabilities - MinSupportedSampleRate */ |
| UA_DataSource samplingInterval = {readMinSamplingInterval, NULL}; |
| retVal |= UA_Server_setVariableNode_dataSource(server, |
| UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MINSUPPORTEDSAMPLERATE), |
| samplingInterval); |
| |
| /* ServerCapabilities - OperationLimits - MaxNodesPerRead */ |
| retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERREAD, |
| &server->config.maxNodesPerRead, &UA_TYPES[UA_TYPES_UINT32]); |
| |
| /* ServerCapabilities - OperationLimits - maxNodesPerWrite */ |
| retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERWRITE, |
| &server->config.maxNodesPerWrite, &UA_TYPES[UA_TYPES_UINT32]); |
| |
| /* ServerCapabilities - OperationLimits - MaxNodesPerMethodCall */ |
| retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERMETHODCALL, |
| &server->config.maxNodesPerMethodCall, &UA_TYPES[UA_TYPES_UINT32]); |
| |
| /* ServerCapabilities - OperationLimits - MaxNodesPerBrowse */ |
| retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERBROWSE, |
| &server->config.maxNodesPerBrowse, &UA_TYPES[UA_TYPES_UINT32]); |
| |
| /* ServerCapabilities - OperationLimits - MaxNodesPerRegisterNodes */ |
| retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERREGISTERNODES, |
| &server->config.maxNodesPerRegisterNodes, &UA_TYPES[UA_TYPES_UINT32]); |
| |
| /* ServerCapabilities - OperationLimits - MaxNodesPerTranslateBrowsePathsToNodeIds */ |
| retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERTRANSLATEBROWSEPATHSTONODEIDS, |
| &server->config.maxNodesPerTranslateBrowsePathsToNodeIds, &UA_TYPES[UA_TYPES_UINT32]); |
| |
| /* ServerCapabilities - OperationLimits - MaxNodesPerNodeManagement */ |
| retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERNODEMANAGEMENT, |
| &server->config.maxNodesPerNodeManagement, &UA_TYPES[UA_TYPES_UINT32]); |
| |
| /* ServerCapabilities - OperationLimits - MaxMonitoredItemsPerCall */ |
| retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXMONITOREDITEMSPERCALL, |
| &server->config.maxMonitoredItemsPerCall, &UA_TYPES[UA_TYPES_UINT32]); |
| |
| #ifdef UA_ENABLE_MICRO_EMB_DEV_PROFILE |
| /* Remove unused operation limit components */ |
| UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERHISTORYREADDATA), true); |
| UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERHISTORYREADEVENTS), true); |
| UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERHISTORYUPDATEDATA), true); |
| UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXNODESPERHISTORYUPDATEEVENTS), true); |
| UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_ROLESET), true); |
| UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXSTRINGLENGTH), true); |
| UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXARRAYLENGTH), true); |
| UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXBYTESTRINGLENGTH), true); |
| |
| /* Remove not supported Server Instance */ |
| UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_DICTIONARIES), true); |
| UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_ESTIMATEDRETURNTIME), true); |
| UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_LOCALTIME), true); |
| UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_PUBLISHSUBSCRIBE), true); |
| UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACES), true); |
| UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_REQUESTSERVERSTATECHANGE), true); |
| UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_RESENDDATA), true); |
| UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION), true); |
| UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SETSUBSCRIPTIONDURABLE), true); |
| |
| UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SAMPLINGINTERVALDIAGNOSTICSARRAY), true); |
| UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SESSIONSDIAGNOSTICSSUMMARY), true); |
| |
| /* Removing these NodeIds make Server Object to be non-complaint with UA 1.03 in CTT (Base Inforamtion/Base Info Core Structure/ 001.js) |
| * In the 1.04 specification this has been resolved by allowing to remove these static nodes as well */ |
| UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY), true); |
| UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SUBSCRIPTIONDIAGNOSTICSARRAY), true); |
| #endif |
| |
| #ifndef UA_ENABLE_HISTORIZING |
| UA_Server_deleteNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_HISTORYSERVERCAPABILITIES), true); |
| #else |
| /* ServerCapabilities - HistoryServerCapabilities - AccessHistoryDataCapability */ |
| retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_ACCESSHISTORYDATACAPABILITY, |
| &server->config.accessHistoryDataCapability, &UA_TYPES[UA_TYPES_BOOLEAN]); |
| |
| /* ServerCapabilities - HistoryServerCapabilities - MaxReturnDataValues */ |
| retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_MAXRETURNDATAVALUES, |
| &server->config.maxReturnDataValues, &UA_TYPES[UA_TYPES_UINT32]); |
| |
| /* ServerCapabilities - HistoryServerCapabilities - AccessHistoryEventsCapability */ |
| retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_ACCESSHISTORYEVENTSCAPABILITY, |
| &server->config.accessHistoryEventsCapability, &UA_TYPES[UA_TYPES_BOOLEAN]); |
| |
| /* ServerCapabilities - HistoryServerCapabilities - MaxReturnEventValues */ |
| retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_MAXRETURNEVENTVALUES, |
| &server->config.maxReturnEventValues, &UA_TYPES[UA_TYPES_UINT32]); |
| |
| /* ServerCapabilities - HistoryServerCapabilities - InsertDataCapability */ |
| retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_INSERTDATACAPABILITY, |
| &server->config.insertDataCapability, &UA_TYPES[UA_TYPES_BOOLEAN]); |
| |
| /* ServerCapabilities - HistoryServerCapabilities - InsertEventCapability */ |
| retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_INSERTEVENTCAPABILITY, |
| &server->config.insertEventCapability, &UA_TYPES[UA_TYPES_BOOLEAN]); |
| |
| /* ServerCapabilities - HistoryServerCapabilities - InsertAnnotationsCapability */ |
| retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_INSERTANNOTATIONCAPABILITY, |
| &server->config.insertAnnotationsCapability, &UA_TYPES[UA_TYPES_BOOLEAN]); |
| |
| /* ServerCapabilities - HistoryServerCapabilities - ReplaceDataCapability */ |
| retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_REPLACEDATACAPABILITY, |
| &server->config.replaceDataCapability, &UA_TYPES[UA_TYPES_BOOLEAN]); |
| |
| /* ServerCapabilities - HistoryServerCapabilities - ReplaceEventCapability */ |
| retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_REPLACEEVENTCAPABILITY, |
| &server->config.replaceEventCapability, &UA_TYPES[UA_TYPES_BOOLEAN]); |
| |
| /* ServerCapabilities - HistoryServerCapabilities - UpdateDataCapability */ |
| retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_UPDATEDATACAPABILITY, |
| &server->config.updateDataCapability, &UA_TYPES[UA_TYPES_BOOLEAN]); |
| |
| /* ServerCapabilities - HistoryServerCapabilities - UpdateEventCapability */ |
| retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_UPDATEEVENTCAPABILITY, |
| &server->config.updateEventCapability, &UA_TYPES[UA_TYPES_BOOLEAN]); |
| |
| /* ServerCapabilities - HistoryServerCapabilities - DeleteRawCapability */ |
| retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_DELETERAWCAPABILITY, |
| &server->config.deleteRawCapability, &UA_TYPES[UA_TYPES_BOOLEAN]); |
| |
| /* ServerCapabilities - HistoryServerCapabilities - DeleteEventCapability */ |
| retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_DELETEEVENTCAPABILITY, |
| &server->config.deleteEventCapability, &UA_TYPES[UA_TYPES_BOOLEAN]); |
| |
| /* ServerCapabilities - HistoryServerCapabilities - DeleteAtTimeDataCapability */ |
| retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_DELETEATTIMECAPABILITY, |
| &server->config.deleteAtTimeDataCapability, &UA_TYPES[UA_TYPES_BOOLEAN]); |
| #endif |
| |
| #if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS) |
| retVal |= UA_Server_setMethodNode_callback(server, |
| UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS), readMonitoredItems); |
| #endif |
| |
| /* The HasComponent references to the ModellingRules are not part of the |
| * Nodeset2.xml. So we add the references manually. */ |
| addModellingRules(server); |
| |
| #endif /* UA_GENERATED_NAMESPACE_ZERO */ |
| |
| /* create the OverFlowEventType |
| * The EventQueueOverflowEventType is defined as abstract, therefore we can not create an instance of that type |
| * directly, but need to create a subtype. This is already posted on the OPC Foundation bug tracker under the |
| * following link for clarification: https://opcfoundation-onlineapplications.org/mantis/view.php?id=4206 */ |
| #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS |
| UA_ObjectTypeAttributes overflowAttr = UA_ObjectTypeAttributes_default; |
| overflowAttr.description = UA_LOCALIZEDTEXT("en-US", "A simple event for indicating a queue overflow."); |
| overflowAttr.displayName = UA_LOCALIZEDTEXT("en-US", "SimpleOverflowEventType"); |
| retVal |= UA_Server_addObjectTypeNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SIMPLEOVERFLOWEVENTTYPE), |
| UA_NODEID_NUMERIC(0, UA_NS0ID_EVENTQUEUEOVERFLOWEVENTTYPE), |
| UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE), |
| UA_QUALIFIEDNAME(0, "SimpleOverflowEventType"), |
| overflowAttr, NULL, NULL); |
| #endif |
| |
| if(retVal != UA_STATUSCODE_GOOD) { |
| UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, |
| "Initialization of Namespace 0 (after bootstrapping) " |
| "failed with %s. See previous outputs for any error messages.", |
| UA_StatusCode_name(retVal)); |
| return UA_STATUSCODE_BADINTERNALERROR; |
| } |
| return UA_STATUSCODE_GOOD; |
| } |