|
@@ -205,7 +205,9 @@ static void getBulidInfo(const UA_Server* server, UA_BuildInfo *buildInfo) {
|
|
|
buildInfo->buildDate = server->buildDate;
|
|
|
}
|
|
|
|
|
|
-static UA_StatusCode readStatus(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimeStamp, const UA_NumericRange *range, UA_DataValue *value) {
|
|
|
+static UA_StatusCode
|
|
|
+readStatus(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimeStamp,
|
|
|
+ const UA_NumericRange *range, UA_DataValue *value) {
|
|
|
if(range) {
|
|
|
value->hasStatus = UA_TRUE;
|
|
|
value->status = UA_STATUSCODE_BADINDEXRANGEINVALID;
|
|
@@ -232,14 +234,17 @@ static UA_StatusCode readStatus(void *handle, const UA_NodeId nodeid, UA_Boolean
|
|
|
return UA_STATUSCODE_GOOD;
|
|
|
}
|
|
|
|
|
|
-static UA_StatusCode readNamespaces(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimestamp, const UA_NumericRange *range, UA_DataValue *value) {
|
|
|
+static UA_StatusCode
|
|
|
+readNamespaces(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimestamp,
|
|
|
+ const UA_NumericRange *range, UA_DataValue *value) {
|
|
|
if(range) {
|
|
|
value->hasStatus = UA_TRUE;
|
|
|
value->status = UA_STATUSCODE_BADINDEXRANGEINVALID;
|
|
|
return UA_STATUSCODE_GOOD;
|
|
|
}
|
|
|
UA_Server *server = (UA_Server*)handle;
|
|
|
- UA_StatusCode retval = UA_Variant_setArrayCopy(&value->value, server->namespaces, server->namespacesSize, &UA_TYPES[UA_TYPES_STRING]);
|
|
|
+ UA_StatusCode retval = UA_Variant_setArrayCopy(&value->value, server->namespaces, server->namespacesSize,
|
|
|
+ &UA_TYPES[UA_TYPES_STRING]);
|
|
|
if(retval != UA_STATUSCODE_GOOD)
|
|
|
return retval;
|
|
|
value->hasValue = UA_TRUE;
|
|
@@ -831,7 +836,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
|
|
|
copyNames((UA_Node*)namespaceArray, "NamespaceArray");
|
|
|
namespaceArray->nodeId.identifier.numeric = UA_NS0ID_SERVER_NAMESPACEARRAY;
|
|
|
namespaceArray->valueSource = UA_VALUESOURCE_DATASOURCE;
|
|
|
- namespaceArray->value.dataSource = (UA_DataSource) {.handle = server, .read = readNamespaces, .write = UA_NULL};
|
|
|
+ namespaceArray->value.dataSource = (UA_DataSource) {.handle = server, .read = readNamespaces,
|
|
|
+ .write = UA_NULL};
|
|
|
namespaceArray->valueRank = 1;
|
|
|
namespaceArray->minimumSamplingInterval = 1.0;
|
|
|
UA_Server_addNode(server, (UA_Node*)namespaceArray, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER),
|
|
@@ -968,7 +974,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
|
|
|
copyNames((UA_Node*)currenttime, "CurrentTime");
|
|
|
currenttime->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
|
|
|
currenttime->valueSource = UA_VALUESOURCE_DATASOURCE;
|
|
|
- currenttime->value.dataSource = (UA_DataSource) {.handle = NULL, .read = readCurrentTime, .write = UA_NULL};
|
|
|
+ currenttime->value.dataSource = (UA_DataSource) {.handle = NULL, .read = readCurrentTime,
|
|
|
+ .write = UA_NULL};
|
|
|
UA_Server_addNode(server, (UA_Node*)currenttime,
|
|
|
UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
|
|
|
nodeIdHasComponent);
|
|
@@ -1096,3 +1103,195 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
|
|
|
|
|
|
return server;
|
|
|
}
|
|
|
+
|
|
|
+UA_StatusCode
|
|
|
+UA_Server_setNodeAttribute(UA_Server *server, const UA_NodeId nodeId, const UA_AttributeId attributeId,
|
|
|
+ const UA_DataType *type, const void *value) {
|
|
|
+ UA_WriteValue wvalue;
|
|
|
+ UA_WriteValue_init(&wvalue);
|
|
|
+ wvalue.nodeId = nodeId;
|
|
|
+ wvalue.attributeId = attributeId;
|
|
|
+ UA_Variant_setScalarCopy(&wvalue.value.value, type, value);
|
|
|
+ wvalue.value.hasValue = UA_TRUE;
|
|
|
+ UA_StatusCode retval = Service_Write_single(server, &adminSession, &wvalue);
|
|
|
+ UA_NodeId_init(&wvalue.nodeId);
|
|
|
+ UA_WriteValue_deleteMembers(&wvalue);
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+UA_StatusCode
|
|
|
+UA_Server_setNodeAttribute_value(UA_Server *server, const UA_NodeId nodeId, const UA_DataType *type,
|
|
|
+ const UA_Variant *value) {
|
|
|
+ UA_WriteValue wvalue;
|
|
|
+ UA_WriteValue_init(&wvalue);
|
|
|
+ wvalue.nodeId = nodeId;
|
|
|
+ wvalue.attributeId = UA_ATTRIBUTEID_VALUE;
|
|
|
+ UA_StatusCode retval = UA_Variant_copy(value, &wvalue.value.value);
|
|
|
+ if(retval != UA_STATUSCODE_GOOD)
|
|
|
+ return retval;
|
|
|
+ wvalue.value.hasValue = UA_TRUE;
|
|
|
+ retval = Service_Write_single(server, &adminSession, &wvalue);
|
|
|
+ UA_NodeId_init(&wvalue.nodeId);
|
|
|
+ UA_WriteValue_deleteMembers(&wvalue);
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+UA_StatusCode
|
|
|
+UA_Server_setNodeAttribute_value_destructive(UA_Server *server, const UA_NodeId nodeId,
|
|
|
+ const UA_DataType *type, UA_Variant *value) {
|
|
|
+ UA_WriteValue wvalue;
|
|
|
+ UA_WriteValue_init(&wvalue);
|
|
|
+ wvalue.nodeId = nodeId;
|
|
|
+ wvalue.attributeId = UA_ATTRIBUTEID_VALUE;
|
|
|
+ wvalue.value.value = *value;
|
|
|
+ UA_Variant_init(value);
|
|
|
+ wvalue.value.hasValue = UA_TRUE;
|
|
|
+ UA_StatusCode retval = Service_Write_single(server, &adminSession, &wvalue);
|
|
|
+ UA_NodeId_init(&wvalue.nodeId);
|
|
|
+ UA_WriteValue_deleteMembers(&wvalue);
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+#ifdef ENABLE_METHODCALLS
|
|
|
+UA_StatusCode
|
|
|
+UA_Server_setNodeAttribute_method(UA_Server *server, UA_NodeId methodNodeId, UA_MethodCallback method,
|
|
|
+ void *handle) {
|
|
|
+ UA_StatusCode retval;
|
|
|
+ retrySetMethod:
|
|
|
+ retval = UA_STATUSCODE_GOOD;
|
|
|
+
|
|
|
+ const UA_Node *attachToMethod = UA_NodeStore_get(server->nodestore, &methodNodeId);
|
|
|
+ if(!attachToMethod)
|
|
|
+ return UA_STATUSCODE_BADNODEIDINVALID;
|
|
|
+
|
|
|
+ if(attachToMethod->nodeClass != UA_NODECLASS_METHOD) {
|
|
|
+ UA_NodeStore_release(attachToMethod);
|
|
|
+ return UA_STATUSCODE_BADNODEIDINVALID;
|
|
|
+ }
|
|
|
+
|
|
|
+ UA_MethodNode *replacementMethod = UA_MethodNode_new();
|
|
|
+ if(!replacementMethod) {
|
|
|
+ UA_NodeStore_release(attachToMethod);
|
|
|
+ return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
+ }
|
|
|
+
|
|
|
+ retval = UA_MethodNode_copy((const UA_MethodNode *) attachToMethod, replacementMethod);
|
|
|
+ if(retval != UA_STATUSCODE_GOOD) {
|
|
|
+ UA_MethodNode_delete(replacementMethod);
|
|
|
+ UA_NodeStore_release(attachToMethod);
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+
|
|
|
+ replacementMethod->attachedMethod = method;
|
|
|
+ replacementMethod->methodHandle = handle;
|
|
|
+ retval = UA_NodeStore_replace(server->nodestore, attachToMethod, (UA_Node *) replacementMethod, UA_NULL);
|
|
|
+ UA_NodeStore_release(attachToMethod);
|
|
|
+
|
|
|
+
|
|
|
+ if(retval != UA_STATUSCODE_GOOD)
|
|
|
+ goto retrySetMethod;
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+UA_StatusCode
|
|
|
+UA_Server_setNodeAttribute_value_dataSource(UA_Server *server, const UA_NodeId nodeId,
|
|
|
+ UA_DataSource dataSource) {
|
|
|
+ const UA_Node *orig;
|
|
|
+ retrySetDataSource:
|
|
|
+ orig = UA_NodeStore_get(server->nodestore, &nodeId);
|
|
|
+ if(!orig)
|
|
|
+ return UA_STATUSCODE_BADNODEIDUNKNOWN;
|
|
|
+
|
|
|
+ if(orig->nodeClass != UA_NODECLASS_VARIABLE &&
|
|
|
+ orig->nodeClass != UA_NODECLASS_VARIABLETYPE) {
|
|
|
+ UA_NodeStore_release(orig);
|
|
|
+ return UA_STATUSCODE_BADNODECLASSINVALID;
|
|
|
+ }
|
|
|
+
|
|
|
+#ifndef UA_MULTITHREADING
|
|
|
+
|
|
|
+ UA_VariableNode *editable = (UA_VariableNode*)(uintptr_t)orig;
|
|
|
+#else
|
|
|
+ UA_VariableNode *editable = (UA_VariableNode*)UA_Node_copyAnyNodeClass(orig);
|
|
|
+ if(!editable) {
|
|
|
+ UA_NodeStore_release(orig);
|
|
|
+ return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
+ if(editable->valueSource == UA_VALUESOURCE_VARIANT)
|
|
|
+ UA_Variant_deleteMembers(&editable->value.variant);
|
|
|
+ editable->value.dataSource = dataSource;
|
|
|
+ editable->valueSource = UA_VALUESOURCE_DATASOURCE;
|
|
|
+
|
|
|
+#ifdef UA_MULTITHREADING
|
|
|
+ UA_StatusCode retval = UA_NodeStore_replace(server->nodestore, orig, (UA_Node*)editable, UA_NULL);
|
|
|
+ if(retval != UA_STATUSCODE_GOOD) {
|
|
|
+
|
|
|
+ UA_NodeStore_release(orig);
|
|
|
+ goto retrySetDataSource;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ UA_NodeStore_release(orig);
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+}
|
|
|
+
|
|
|
+UA_StatusCode
|
|
|
+UA_Server_getNodeAttribute(UA_Server *server, const UA_NodeId nodeId,
|
|
|
+ const UA_AttributeId attributeId, void *v) {
|
|
|
+ UA_Variant out;
|
|
|
+ UA_Variant_init(&out);
|
|
|
+ UA_StatusCode retval = UA_Server_getNodeAttribute(server, nodeId, attributeId, &out);
|
|
|
+ if(retval != UA_STATUSCODE_GOOD)
|
|
|
+ return retval;
|
|
|
+ if(attributeId == UA_ATTRIBUTEID_VALUE)
|
|
|
+ UA_memcpy(v, &out, sizeof(UA_Variant));
|
|
|
+ else {
|
|
|
+ UA_memcpy(v, out.data, out.type->memSize);
|
|
|
+ out.data = UA_NULL;
|
|
|
+ out.arrayLength = -1;
|
|
|
+ UA_Variant_deleteMembers(&out);
|
|
|
+ }
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+}
|
|
|
+
|
|
|
+#ifdef ENABLE_METHODCALLS
|
|
|
+UA_StatusCode
|
|
|
+UA_Server_getNodeAttribute_method(UA_Server *server, UA_NodeId nodeId, UA_MethodCallback *method) {
|
|
|
+ const UA_Node *node = UA_NodeStore_get(server->nodestore, &nodeId);
|
|
|
+ if(!node)
|
|
|
+ return UA_STATUSCODE_BADNODEIDUNKNOWN;
|
|
|
+
|
|
|
+ if(node->nodeClass != UA_NODECLASS_METHOD) {
|
|
|
+ UA_NodeStore_release(node);
|
|
|
+ return UA_STATUSCODE_BADNODECLASSINVALID;
|
|
|
+ }
|
|
|
+
|
|
|
+ *method = ((const UA_MethodNode*)node)->attachedMethod;
|
|
|
+ UA_NodeStore_release(node);
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+UA_StatusCode
|
|
|
+UA_Server_getNodeAttribute_value_dataSource(UA_Server *server, UA_NodeId nodeId, UA_DataSource *dataSource) {
|
|
|
+ const UA_VariableNode *node = (const UA_VariableNode*)UA_NodeStore_get(server->nodestore, &nodeId);
|
|
|
+ if(!node)
|
|
|
+ return UA_STATUSCODE_BADNODEIDUNKNOWN;
|
|
|
+
|
|
|
+ if(node->nodeClass != UA_NODECLASS_VARIABLE &&
|
|
|
+ node->nodeClass != UA_NODECLASS_VARIABLETYPE) {
|
|
|
+ UA_NodeStore_release((const UA_Node*)node);
|
|
|
+ return UA_STATUSCODE_BADNODECLASSINVALID;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(node->valueSource != UA_VALUESOURCE_DATASOURCE) {
|
|
|
+ UA_NodeStore_release((const UA_Node*)node);
|
|
|
+ return UA_STATUSCODE_BADNODECLASSINVALID;
|
|
|
+ }
|
|
|
+
|
|
|
+ *dataSource = node->value.dataSource;
|
|
|
+ UA_NodeStore_release((const UA_Node*)node);
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+}
|