mirror of
https://github.com/open62541/open62541.git
synced 2025-06-03 04:00:21 +00:00
finish nodeContext pointer for every node
This commit is contained in:
parent
2113ec1c9b
commit
394df3be8d
@ -253,11 +253,12 @@ set(exported_headers ${PROJECT_BINARY_DIR}/src_generated/ua_config.h
|
||||
${PROJECT_SOURCE_DIR}/include/ua_types.h
|
||||
${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.h
|
||||
${PROJECT_BINARY_DIR}/src_generated/ua_types_generated_handling.h
|
||||
${PROJECT_SOURCE_DIR}/include/ua_server.h
|
||||
${PROJECT_SOURCE_DIR}/include/ua_plugin_network.h
|
||||
${PROJECT_SOURCE_DIR}/include/ua_plugin_log.h
|
||||
${PROJECT_SOURCE_DIR}/include/ua_plugin_access_control.h
|
||||
${PROJECT_SOURCE_DIR}/include/ua_plugin_securitypolicy.h
|
||||
${PROJECT_SOURCE_DIR}/include/ua_server.h
|
||||
${PROJECT_SOURCE_DIR}/include/ua_server_config.h
|
||||
${PROJECT_SOURCE_DIR}/include/ua_client.h
|
||||
${PROJECT_SOURCE_DIR}/include/ua_client_highlevel.h
|
||||
${PROJECT_SOURCE_DIR}/plugins/ua_network_tcp.h
|
||||
|
@ -21,27 +21,39 @@ static void stopHandler(int sign) {
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
readInteger(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimeStamp,
|
||||
const UA_NumericRange *range, UA_DataValue *dataValue) {
|
||||
dataValue->hasValue = true;
|
||||
UA_Variant_setScalarCopy(&dataValue->value, (UA_UInt32 *) handle, &UA_TYPES[UA_TYPES_INT32]);
|
||||
readInteger(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) {
|
||||
UA_Int32 *myInteger = (UA_Int32*)nodeContext;
|
||||
value->hasValue = true;
|
||||
UA_Variant_setScalarCopy(&value->value, myInteger, &UA_TYPES[UA_TYPES_INT32]);
|
||||
|
||||
// we know the nodeid is a string
|
||||
UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Node read %.*s",
|
||||
(int)nodeid.identifier.string.length, nodeid.identifier.string.data);
|
||||
UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "read value %i", *(UA_UInt32 *) handle);
|
||||
(int)nodeId->identifier.string.length,
|
||||
nodeId->identifier.string.data);
|
||||
UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND,
|
||||
"read value %i", *(UA_UInt32 *)myInteger);
|
||||
return UA_STATUSCODE_GOOD;
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
writeInteger(void *handle, const UA_NodeId nodeid,
|
||||
const UA_Variant *data, const UA_NumericRange *range) {
|
||||
if (UA_Variant_isScalar(data) && data->type == &UA_TYPES[UA_TYPES_INT32] && data->data) {
|
||||
*(UA_UInt32 *) handle = *(UA_UInt32 *) data->data;
|
||||
}
|
||||
writeInteger(UA_Server *server, const UA_NodeId *sessionId,
|
||||
void *sessionContext, const UA_NodeId *nodeId,
|
||||
void *nodeContext, const UA_NumericRange *range,
|
||||
const UA_DataValue *value) {
|
||||
UA_Int32 *myInteger = (UA_Int32*)nodeContext;
|
||||
if(value->hasValue && UA_Variant_isScalar(&value->value) &&
|
||||
value->value.type == &UA_TYPES[UA_TYPES_INT32] && value->value.data)
|
||||
*myInteger = *(UA_Int32 *)value->value.data;
|
||||
|
||||
// we know the nodeid is a string
|
||||
UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Node written %.*s",
|
||||
(int)nodeid.identifier.string.length, nodeid.identifier.string.data);
|
||||
UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "written value %i", *(UA_UInt32 *) handle);
|
||||
(int)nodeId->identifier.string.length,
|
||||
nodeId->identifier.string.data);
|
||||
UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND,
|
||||
"written value %i", *(UA_UInt32 *)myInteger);
|
||||
return UA_STATUSCODE_GOOD;
|
||||
}
|
||||
|
||||
@ -105,7 +117,6 @@ int main(int argc, char **argv) {
|
||||
UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
|
||||
UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
|
||||
UA_DataSource dateDataSource;
|
||||
dateDataSource.handle = &myInteger;
|
||||
dateDataSource.read = readInteger;
|
||||
dateDataSource.write = writeInteger;
|
||||
UA_VariableAttributes attr;
|
||||
@ -116,7 +127,8 @@ int main(int argc, char **argv) {
|
||||
UA_Server_addDataSourceVariableNode(server, myIntegerNodeId,
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
|
||||
myIntegerName, UA_NODEID_NULL, attr, dateDataSource, NULL);
|
||||
myIntegerName, UA_NODEID_NULL, attr, dateDataSource,
|
||||
&myInteger, NULL);
|
||||
|
||||
// callback which is called when a new server is detected through mDNS
|
||||
UA_Server_setServerOnNetworkCallback(server, serverOnNetworkCallback, NULL);
|
||||
|
@ -22,27 +22,39 @@ static void stopHandler(int sign) {
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
readInteger(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimeStamp,
|
||||
const UA_NumericRange *range, UA_DataValue *dataValue) {
|
||||
dataValue->hasValue = true;
|
||||
UA_Variant_setScalarCopy(&dataValue->value, (UA_UInt32 *) handle, &UA_TYPES[UA_TYPES_INT32]);
|
||||
readInteger(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) {
|
||||
UA_Int32 *myInteger = (UA_Int32*)nodeContext;
|
||||
value->hasValue = true;
|
||||
UA_Variant_setScalarCopy(&value->value, myInteger, &UA_TYPES[UA_TYPES_INT32]);
|
||||
|
||||
// we know the nodeid is a string
|
||||
UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Node read %.*s",
|
||||
(int)nodeid.identifier.string.length, nodeid.identifier.string.data);
|
||||
UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "read value %i", *(UA_UInt32 *) handle);
|
||||
(int)nodeId->identifier.string.length,
|
||||
nodeId->identifier.string.data);
|
||||
UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND,
|
||||
"read value %i", *(UA_UInt32 *)myInteger);
|
||||
return UA_STATUSCODE_GOOD;
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
writeInteger(void *handle, const UA_NodeId nodeid,
|
||||
const UA_Variant *data, const UA_NumericRange *range) {
|
||||
if (UA_Variant_isScalar(data) && data->type == &UA_TYPES[UA_TYPES_INT32] && data->data) {
|
||||
*(UA_UInt32 *) handle = *(UA_UInt32 *) data->data;
|
||||
}
|
||||
writeInteger(UA_Server *server, const UA_NodeId *sessionId,
|
||||
void *sessionContext, const UA_NodeId *nodeId,
|
||||
void *nodeContext, const UA_NumericRange *range,
|
||||
const UA_DataValue *value) {
|
||||
UA_Int32 *myInteger = (UA_Int32*)nodeContext;
|
||||
if(value->hasValue && UA_Variant_isScalar(&value->value) &&
|
||||
value->value.type == &UA_TYPES[UA_TYPES_INT32] && value->value.data)
|
||||
*myInteger = *(UA_Int32 *)value->value.data;
|
||||
|
||||
// we know the nodeid is a string
|
||||
UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Node written %.*s",
|
||||
(int)nodeid.identifier.string.length, nodeid.identifier.string.data);
|
||||
UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "written value %i", *(UA_UInt32 *) handle);
|
||||
(int)nodeId->identifier.string.length,
|
||||
nodeId->identifier.string.data);
|
||||
UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND,
|
||||
"written value %i", *(UA_UInt32 *)myInteger);
|
||||
return UA_STATUSCODE_GOOD;
|
||||
}
|
||||
|
||||
@ -67,7 +79,6 @@ int main(int argc, char **argv) {
|
||||
UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
|
||||
UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
|
||||
UA_DataSource dateDataSource;
|
||||
dateDataSource.handle = &myInteger;
|
||||
dateDataSource.read = readInteger;
|
||||
dateDataSource.write = writeInteger;
|
||||
UA_VariableAttributes attr;
|
||||
@ -78,7 +89,8 @@ int main(int argc, char **argv) {
|
||||
UA_Server_addDataSourceVariableNode(server, myIntegerNodeId,
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
|
||||
myIntegerName, UA_NODEID_NULL, attr, dateDataSource, NULL);
|
||||
myIntegerName, UA_NODEID_NULL, attr, dateDataSource,
|
||||
&myInteger, NULL);
|
||||
|
||||
|
||||
// periodic server register after 10 Minutes, delay first register for 500ms
|
||||
|
@ -48,7 +48,10 @@ static void stopHandler(int sign) {
|
||||
|
||||
/* Datasource Example */
|
||||
static UA_StatusCode
|
||||
readTimeData(void *handle, const UA_NodeId nodeId, UA_Boolean sourceTimeStamp,
|
||||
readTimeData(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;
|
||||
@ -68,8 +71,10 @@ readTimeData(void *handle, const UA_NodeId nodeId, UA_Boolean sourceTimeStamp,
|
||||
/* Method Node Example */
|
||||
#ifdef UA_ENABLE_METHODCALLS
|
||||
static UA_StatusCode
|
||||
helloWorld(void *methodHandle, const UA_NodeId *objectId,
|
||||
const UA_NodeId *sessionId, void *sessionHandle,
|
||||
helloWorld(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) {
|
||||
/* input is a scalar string (checked by the server) */
|
||||
@ -86,16 +91,20 @@ helloWorld(void *methodHandle, const UA_NodeId *objectId,
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
noargMethod(void *methodHandle, const UA_NodeId *objectId,
|
||||
const UA_NodeId *sessionId, void *sessionHandle,
|
||||
noargMethod(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) {
|
||||
return UA_STATUSCODE_GOOD;
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
outargMethod(void *methodHandle, const UA_NodeId *objectId,
|
||||
const UA_NodeId *sessionId, void *sessionHandle,
|
||||
outargMethod(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_Int32 out = 42;
|
||||
@ -131,7 +140,6 @@ int main(int argc, char** argv) {
|
||||
|
||||
/* add a variable with the datetime data source */
|
||||
UA_DataSource dateDataSource;
|
||||
dateDataSource.handle = NULL;
|
||||
dateDataSource.read = readTimeData;
|
||||
dateDataSource.write = NULL;
|
||||
UA_VariableAttributes v_attr;
|
||||
@ -143,7 +151,7 @@ int main(int argc, char** argv) {
|
||||
UA_NodeId dataSourceId;
|
||||
UA_Server_addDataSourceVariableNode(server, UA_NODEID_NULL, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), dateName,
|
||||
UA_NODEID_NULL, v_attr, dateDataSource, &dataSourceId);
|
||||
UA_NODEID_NULL, v_attr, dateDataSource, NULL, &dataSourceId);
|
||||
|
||||
/* Add HelloWorld method to the server */
|
||||
#ifdef UA_ENABLE_METHODCALLS
|
||||
@ -174,8 +182,7 @@ int main(int argc, char** argv) {
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
|
||||
UA_QUALIFIEDNAME(1, "hello_world"), addmethodattributes,
|
||||
&helloWorld, /* callback of the method node */
|
||||
NULL, /* handle passed with the callback */
|
||||
1, &inputArguments, 1, &outputArguments, NULL);
|
||||
1, &inputArguments, 1, &outputArguments, NULL, NULL);
|
||||
#endif
|
||||
|
||||
/* Add folders for demo information model */
|
||||
@ -328,8 +335,7 @@ int main(int argc, char** argv) {
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
|
||||
UA_QUALIFIEDNAME(1, "noarg"), addmethodattributes,
|
||||
&noargMethod, /* callback of the method node */
|
||||
NULL, /* handle passed with the callback */
|
||||
0, NULL, 0, NULL, NULL);
|
||||
0, NULL, 0, NULL, NULL, NULL);
|
||||
|
||||
/* Method with in arguments */
|
||||
UA_MethodAttributes_init(&addmethodattributes);
|
||||
@ -348,8 +354,7 @@ int main(int argc, char** argv) {
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
|
||||
UA_QUALIFIEDNAME(1, "noarg"), addmethodattributes,
|
||||
&noargMethod, /* callback of the method node */
|
||||
NULL, /* handle passed with the callback */
|
||||
1, &inputArguments, 0, NULL, NULL);
|
||||
1, &inputArguments, 0, NULL, NULL, NULL);
|
||||
|
||||
/* Method with out arguments */
|
||||
UA_MethodAttributes_init(&addmethodattributes);
|
||||
@ -368,8 +373,7 @@ int main(int argc, char** argv) {
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
|
||||
UA_QUALIFIEDNAME(1, "outarg"), addmethodattributes,
|
||||
&outargMethod, /* callback of the method node */
|
||||
NULL, /* handle passed with the callback */
|
||||
0, NULL, 1, &outputArguments, NULL);
|
||||
0, NULL, 1, &outputArguments, NULL, NULL);
|
||||
|
||||
/* Method with inout arguments */
|
||||
UA_MethodAttributes_init(&addmethodattributes);
|
||||
@ -382,8 +386,7 @@ int main(int argc, char** argv) {
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
|
||||
UA_QUALIFIEDNAME(1, "inoutarg"), addmethodattributes,
|
||||
&outargMethod, /* callback of the method node */
|
||||
NULL, /* handle passed with the callback */
|
||||
1, &inputArguments, 1, &outputArguments, NULL);
|
||||
1, &inputArguments, 1, &outputArguments, NULL, NULL);
|
||||
#endif
|
||||
|
||||
/* run server */
|
||||
|
@ -68,9 +68,10 @@ addCurrentTimeVariable(UA_Server *server) {
|
||||
* write operation. */
|
||||
|
||||
static void
|
||||
beforeReadTime(void *handle, const UA_NodeId nodeid, const UA_Variant *data,
|
||||
const UA_NumericRange *range) {
|
||||
UA_Server *server = (UA_Server*)handle;
|
||||
beforeReadTime(UA_Server *server,
|
||||
const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_NodeId *nodeid, void *nodeContext,
|
||||
const UA_NumericRange *range, const UA_DataValue *data) {
|
||||
UA_DateTime now = UA_DateTime_now();
|
||||
UA_Variant value;
|
||||
UA_Variant_setScalar(&value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
|
||||
@ -79,8 +80,10 @@ beforeReadTime(void *handle, const UA_NodeId nodeid, const UA_Variant *data,
|
||||
}
|
||||
|
||||
static void
|
||||
afterWriteTime(void *handle, const UA_NodeId nodeid, const UA_Variant *data,
|
||||
const UA_NumericRange *range) {
|
||||
afterWriteTime(UA_Server *server,
|
||||
const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_NodeId *nodeId, void *nodeContext,
|
||||
const UA_NumericRange *range, const UA_DataValue *data) {
|
||||
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
|
||||
"The variable was updated");
|
||||
}
|
||||
@ -89,7 +92,6 @@ static void
|
||||
addValueCallbackToCurrentTimeVariable(UA_Server *server) {
|
||||
UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time");
|
||||
UA_ValueCallback callback ;
|
||||
callback.handle = server;
|
||||
callback.onRead = beforeReadTime;
|
||||
callback.onWrite = afterWriteTime;
|
||||
UA_Server_setVariableNode_valueCallback(server, currentNodeId, callback);
|
||||
@ -106,8 +108,11 @@ addValueCallbackToCurrentTimeVariable(UA_Server *server) {
|
||||
* own memory management. */
|
||||
|
||||
static UA_StatusCode
|
||||
readCurrentTime(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimeStamp,
|
||||
const UA_NumericRange *range, UA_DataValue *dataValue) {
|
||||
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 *dataValue) {
|
||||
UA_DateTime now = UA_DateTime_now();
|
||||
UA_Variant_setScalarCopy(&dataValue->value, &now,
|
||||
&UA_TYPES[UA_TYPES_DATETIME]);
|
||||
@ -116,8 +121,10 @@ readCurrentTime(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimeStamp
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
writeCurrentTime(void *handle, const UA_NodeId nodeid, const UA_Variant *data,
|
||||
const UA_NumericRange *range) {
|
||||
writeCurrentTime(UA_Server *server,
|
||||
const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_NodeId *nodeId, void *nodeContext,
|
||||
const UA_NumericRange *range, const UA_DataValue *data) {
|
||||
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
|
||||
"Changing the system time is not implemented");
|
||||
return UA_STATUSCODE_BADINTERNALERROR;
|
||||
@ -136,13 +143,12 @@ addCurrentTimeDataSourceVariable(UA_Server *server) {
|
||||
UA_NodeId variableTypeNodeId = UA_NODEID_NULL;
|
||||
|
||||
UA_DataSource timeDataSource;
|
||||
timeDataSource.handle = NULL;
|
||||
timeDataSource.read = readCurrentTime;
|
||||
timeDataSource.write = writeCurrentTime;
|
||||
UA_Server_addDataSourceVariableNode(server, currentNodeId, parentNodeId,
|
||||
parentReferenceNodeId, currentName,
|
||||
variableTypeNodeId, attr,
|
||||
timeDataSource, NULL);
|
||||
timeDataSource, NULL, NULL);
|
||||
}
|
||||
|
||||
/** It follows the main server code, making use of the above definitions. */
|
||||
|
@ -34,8 +34,10 @@
|
||||
#include "open62541.h"
|
||||
|
||||
static UA_StatusCode
|
||||
helloWorldMethodCallback(void *handle, const UA_NodeId *objectId,
|
||||
helloWorldMethodCallback(UA_Server *server,
|
||||
const UA_NodeId *sessionId, void *sessionHandle,
|
||||
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_String *inputStr = (UA_String*)input->data;
|
||||
@ -76,8 +78,8 @@ addHellWorldMethod(UA_Server *server) {
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASORDEREDCOMPONENT),
|
||||
UA_QUALIFIEDNAME(1, "hello world"),
|
||||
helloAttr, &helloWorldMethodCallback, NULL,
|
||||
1, &inputArgument, 1, &outputArgument, NULL);
|
||||
helloAttr, &helloWorldMethodCallback,
|
||||
1, &inputArgument, 1, &outputArgument, NULL, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -87,8 +89,10 @@ addHellWorldMethod(UA_Server *server) {
|
||||
* copy of the array with every entry increased by the scalar. */
|
||||
|
||||
static UA_StatusCode
|
||||
IncInt32ArrayMethodCallback(void *handle, const UA_NodeId *objectId,
|
||||
const UA_NodeId *sessionId, void *sessionHandle,
|
||||
IncInt32ArrayMethodCallback(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_Int32 *inputArray = (UA_Int32*)input[0].data;
|
||||
@ -148,8 +152,9 @@ addIncInt32ArrayMethod(UA_Server *server) {
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
|
||||
UA_QUALIFIEDNAME(1, "IncInt32ArrayValues"),
|
||||
incAttr, &IncInt32ArrayMethodCallback, NULL,
|
||||
2, inputArguments, 1, &outputArgument, NULL);
|
||||
incAttr, &IncInt32ArrayMethodCallback,
|
||||
2, inputArguments, 1, &outputArgument,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
/** It follows the main server code, making use of the above definitions. */
|
||||
|
@ -256,11 +256,11 @@ addPumpObjectInstance(UA_Server *server, char *name) {
|
||||
* pump status to on.
|
||||
*/
|
||||
|
||||
UA_Server *s = NULL; /* required to get the server pointer into the constructor
|
||||
function (will change for v0.3) */
|
||||
|
||||
static void *
|
||||
pumpTypeConstructor(const UA_NodeId instance) {
|
||||
static UA_StatusCode
|
||||
pumpTypeConstructor(UA_Server *server,
|
||||
const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_NodeId *typeId, void *typeContext,
|
||||
const UA_NodeId *nodeId, void **nodeContext) {
|
||||
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "New pump created");
|
||||
|
||||
/* Find the NodeId of the status child variable */
|
||||
@ -273,33 +273,34 @@ pumpTypeConstructor(const UA_NodeId instance) {
|
||||
|
||||
UA_BrowsePath bp;
|
||||
UA_BrowsePath_init(&bp);
|
||||
bp.startingNode = instance;
|
||||
bp.startingNode = *nodeId;
|
||||
bp.relativePath.elementsSize = 1;
|
||||
bp.relativePath.elements = &rpe;
|
||||
|
||||
UA_BrowsePathResult bpr =
|
||||
UA_Server_translateBrowsePathToNodeIds(s, &bp);
|
||||
UA_Server_translateBrowsePathToNodeIds(server, &bp);
|
||||
if(bpr.statusCode != UA_STATUSCODE_GOOD ||
|
||||
bpr.targetsSize < 1)
|
||||
return NULL;
|
||||
return bpr.statusCode;
|
||||
|
||||
/* Set the status value */
|
||||
UA_Boolean status = true;
|
||||
UA_Variant value;
|
||||
UA_Variant_setScalar(&value, &status, &UA_TYPES[UA_TYPES_BOOLEAN]);
|
||||
UA_Server_writeValue(s, bpr.targets[0].targetId.nodeId, value);
|
||||
UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
|
||||
UA_BrowsePathResult_deleteMembers(&bpr);
|
||||
|
||||
/* The return pointer of the constructor is attached to the ObjectNode */
|
||||
return NULL;
|
||||
/* At this point we could replace the node context .. */
|
||||
|
||||
return UA_STATUSCODE_GOOD;
|
||||
}
|
||||
|
||||
static void
|
||||
addPumpTypeConstructor(UA_Server *server) {
|
||||
UA_ObjectLifecycleManagement olm;
|
||||
olm.constructor = pumpTypeConstructor;
|
||||
olm.destructor = NULL;
|
||||
UA_Server_setObjectTypeNode_lifecycleManagement(server, pumpTypeId, olm);
|
||||
UA_NodeTypeLifecycle lifecycle;
|
||||
lifecycle.constructor = pumpTypeConstructor;
|
||||
lifecycle.destructor = NULL;
|
||||
UA_Server_setNodeTypeLifecycle(server, pumpTypeId, lifecycle);
|
||||
}
|
||||
|
||||
/** It follows the main server code, making use of the above definitions. */
|
||||
@ -316,7 +317,6 @@ int main(void) {
|
||||
|
||||
UA_ServerConfig *config = UA_ServerConfig_new_default();
|
||||
UA_Server *server = UA_Server_new(config);
|
||||
s = server; /* required for the constructor */
|
||||
|
||||
manuallyDefinePump(server);
|
||||
defineObjectTypes(server);
|
||||
|
@ -23,55 +23,47 @@ typedef struct {
|
||||
UA_Boolean enableAnonymousLogin;
|
||||
UA_Boolean enableUsernamePasswordLogin;
|
||||
|
||||
/* Authenticate a session. The session handle is attached to the session and
|
||||
/* Authenticate a session. The session context is attached to the session and
|
||||
* later passed into the node-based access control callbacks. */
|
||||
UA_StatusCode (*activateSession)(const UA_NodeId *sessionId,
|
||||
const UA_ExtensionObject *userIdentityToken,
|
||||
void **sessionHandle);
|
||||
void **sessionContext);
|
||||
|
||||
/* Deauthenticate a session and cleanup */
|
||||
void (*closeSession)(const UA_NodeId *sessionId, void *sessionHandle);
|
||||
void (*closeSession)(const UA_NodeId *sessionId, void *sessionContext);
|
||||
|
||||
/* Access control for all nodes*/
|
||||
UA_UInt32 (*getUserRightsMask)(const UA_NodeId *sessionId,
|
||||
void *sessionHandle,
|
||||
const UA_NodeId *nodeId);
|
||||
UA_UInt32 (*getUserRightsMask)(const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_NodeId *nodeId, void *nodeContext);
|
||||
|
||||
/* Additional access control for variable nodes */
|
||||
UA_Byte (*getUserAccessLevel)(const UA_NodeId *sessionId,
|
||||
void *sessionHandle,
|
||||
const UA_NodeId *nodeId);
|
||||
UA_Byte (*getUserAccessLevel)(const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_NodeId *nodeId, void *nodeContext);
|
||||
|
||||
/* Additional access control for method nodes */
|
||||
UA_Boolean (*getUserExecutable)(const UA_NodeId *sessionId,
|
||||
void *sessionHandle,
|
||||
const UA_NodeId *methodId);
|
||||
UA_Boolean (*getUserExecutable)(const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_NodeId *methodId, void *methodContext);
|
||||
|
||||
/* Additional access control for calling a method node in the context of a
|
||||
* specific object */
|
||||
UA_Boolean (*getUserExecutableOnObject)(const UA_NodeId *sessionId,
|
||||
void *sessionHandle,
|
||||
const UA_NodeId *methodId,
|
||||
const UA_NodeId *objectId);
|
||||
UA_Boolean (*getUserExecutableOnObject)(const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_NodeId *methodId, void *methodContext,
|
||||
const UA_NodeId *objectId, void *objectContext);
|
||||
|
||||
/* Allow adding a node */
|
||||
UA_Boolean (*allowAddNode)(const UA_NodeId *sessionId,
|
||||
void *sessionHandle,
|
||||
UA_Boolean (*allowAddNode)(const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_AddNodesItem *item);
|
||||
|
||||
/* Allow adding a reference */
|
||||
UA_Boolean (*allowAddReference)(const UA_NodeId *sessionId,
|
||||
void *sessionHandle,
|
||||
UA_Boolean (*allowAddReference)(const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_AddReferencesItem *item);
|
||||
|
||||
/* Allow deleting a node */
|
||||
UA_Boolean (*allowDeleteNode)(const UA_NodeId *sessionId,
|
||||
void *sessionHandle,
|
||||
UA_Boolean (*allowDeleteNode)(const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_DeleteNodesItem *item);
|
||||
|
||||
/* Allow deleting a reference */
|
||||
UA_Boolean (*allowDeleteReference)(const UA_NodeId *sessionId,
|
||||
void *sessionHandle,
|
||||
UA_Boolean (*allowDeleteReference)(const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_DeleteReferencesItem *item);
|
||||
} UA_AccessControl;
|
||||
|
||||
|
@ -279,6 +279,7 @@ UA_Server_readExecutable(UA_Server *server, const UA_NodeId nodeId,
|
||||
* - UserExecutable
|
||||
*
|
||||
* Historizing is currently unsupported */
|
||||
|
||||
/* Overwrite an attribute of a node. The specialized functions below provide a
|
||||
* more concise syntax.
|
||||
*
|
||||
@ -532,23 +533,35 @@ UA_Server_setServerOnNetworkCallback(UA_Server *server,
|
||||
#endif /* UA_ENABLE_DISCOVERY */
|
||||
|
||||
/**
|
||||
* Information Model
|
||||
* -----------------
|
||||
* Information Model Callbacks
|
||||
* ---------------------------
|
||||
*
|
||||
* Every node has a context pointer. Initially, the node context is set to
|
||||
* user-defined data (can be NULL). During instantiation, when all mandatory
|
||||
* node children have been created, constructor callbacks are executed on the
|
||||
* node that may replace the context pointer. When the ``AddNodes`` service is
|
||||
* used over the network, the context pointer of the new node is initially set
|
||||
* to NULL.
|
||||
* There are three places where a callback from an information model to
|
||||
* user-defined code can happen.
|
||||
*
|
||||
* The server-wide global constructor and destructor callbacks are executed on
|
||||
* all created and deleted nodes. The node-type constructors and destructors are
|
||||
* only defined for ObjectTypes and VariableTypes. In the hierarchy of
|
||||
* ObjectTypes and VariableTypes, only the lowest type constructor is executed.
|
||||
* Every Object and Variable can have only one type with a ``isTypeOf``
|
||||
* reference. Issues of multiple inheritance need to be solved by the user in
|
||||
* the constructor/destructor.
|
||||
* - Custom node constructors and destructors
|
||||
* - Linking VariableNodes with an external data source
|
||||
* - MethodNode callbacks
|
||||
*
|
||||
* .. _node-lifecycle:
|
||||
*
|
||||
* Node Lifecycle: Constructors, Destructors and Node Contexts
|
||||
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
*
|
||||
* To finalize the instantiation of a node, a (user-defined) constructor
|
||||
* callback is executed. There can be both a global constructor for all nodes
|
||||
* and node-type constructor specific to the TypeDefinition of the new node
|
||||
* (attached to an ObjectTypeNode or VariableTypeNode).
|
||||
*
|
||||
* In the hierarchy of ObjectTypes and VariableTypes, only the constructor of
|
||||
* the (lowest) type defined for the new node is executed. Note that every
|
||||
* Object and Variable can have only one ``isTypeOf`` reference. But type-nodes
|
||||
* can technically have several ``hasSubType`` references to implement multiple
|
||||
* inheritance. Issues of (multiple) inheritance in the constructor need to be
|
||||
* solved by the user.
|
||||
*
|
||||
* When a node is destroyed, the node-type destructor is called before the
|
||||
* global destructor. So the overall node lifecycle is as follows:
|
||||
*
|
||||
* 1. Global Constructor (set in the server config)
|
||||
* 2. Node-Type Constructor (for VariableType or ObjectTypes)
|
||||
@ -556,31 +569,43 @@ UA_Server_setServerOnNetworkCallback(UA_Server *server,
|
||||
* 4. Node-Type Destructor
|
||||
* 5. Global Destructor
|
||||
*
|
||||
* The constructor and destructor callbacks can be set to NULL and are not used
|
||||
* in that case. If the node-type constructor fails, the global destructor will
|
||||
* be called on the node before removing it. The destructors are assumed to
|
||||
* never fail. */
|
||||
* The constructor and destructor callbacks can be set to ``NULL`` and are not
|
||||
* used in that case. If the node-type constructor fails, the global destructor
|
||||
* will be called before removing the node. The destructors are assumed to never
|
||||
* fail.
|
||||
*
|
||||
* Every node carries a user-context and a constructor-context pointer. The
|
||||
* user-context is used to attach custom data to a node. But the (user-defined)
|
||||
* constructors and destructors may replace the user-context pointer if they
|
||||
* wish to do so. The initial value for the constructor-context is ``NULL``.
|
||||
* When the ``AddNodes`` service is used over the network, the user-context
|
||||
* pointer of the new node is also initially set to ``NULL``. */
|
||||
|
||||
/* To be set in the server config. */
|
||||
typedef struct {
|
||||
/* Can be NULL. May replace the nodeContext */
|
||||
UA_StatusCode (*constructor)(UA_Server *server, void *userSessionContext,
|
||||
UA_StatusCode (*constructor)(UA_Server *server,
|
||||
const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_NodeId *nodeId, void **nodeContext);
|
||||
|
||||
/* Can be NULL. */
|
||||
void (*destructor)(UA_Server *server, const UA_NodeId *nodeId,
|
||||
void *nodeContext);
|
||||
/* Can be NULL. The context cannot be replaced since the node is destroyed
|
||||
* immediately afterwards anyway. */
|
||||
void (*destructor)(UA_Server *server,
|
||||
const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_NodeId *nodeId, void *nodeContext);
|
||||
} UA_GlobalNodeLifecycle;
|
||||
|
||||
typedef struct {
|
||||
/* Can be NULL. May replace the nodeContext */
|
||||
UA_StatusCode (*constructor)(UA_Server *server, void *userSessionContext,
|
||||
const UA_NodeId *typeId, void *typeContext,
|
||||
UA_StatusCode (*constructor)(UA_Server *server,
|
||||
const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_NodeId *typeNodeId, void *typeNodeContext,
|
||||
const UA_NodeId *nodeId, void **nodeContext);
|
||||
|
||||
/* Can be NULL. May replace the nodeContext. */
|
||||
void (*destructor)(UA_Server *server,
|
||||
const UA_NodeId *typeId, void *typeContext,
|
||||
const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_NodeId *typeNodeId, void *typeNodeContext,
|
||||
const UA_NodeId *nodeId, void **nodeContext);
|
||||
} UA_NodeTypeLifecycle;
|
||||
|
||||
@ -589,25 +614,19 @@ UA_Server_setNodeTypeLifecycle(UA_Server *server, UA_NodeId nodeId,
|
||||
UA_NodeTypeLifecycle lifecycle);
|
||||
|
||||
UA_StatusCode UA_EXPORT
|
||||
UA_Server_getNodeContext(UA_Server *server, UA_NodeId nodeId, void **context);
|
||||
UA_Server_getNodeContext(UA_Server *server, UA_NodeId nodeId,
|
||||
void **nodeContext);
|
||||
|
||||
/* Careful! The context pointer might have been replaced by a constructor.
|
||||
* The user has to ensure that the destructor callbacks still work. */
|
||||
/* Careful! The user has to ensure that the destructor callbacks still work. */
|
||||
UA_StatusCode UA_EXPORT
|
||||
UA_Server_setNodeContext(UA_Server *server, UA_NodeId nodeId, void *context);
|
||||
UA_Server_setNodeContext(UA_Server *server, UA_NodeId nodeId,
|
||||
void *nodeContext);
|
||||
|
||||
/**
|
||||
* Variable Value Storage
|
||||
* ^^^^^^^^^^^^^^^^^^^^^^
|
||||
* - Datasources for variable nodes, where the variable content is managed
|
||||
* externally
|
||||
* - Value-callbacks for variable nodes, where userspace is notified when a
|
||||
* read/write occurs
|
||||
*
|
||||
* .. _datasource:
|
||||
*
|
||||
* Data Source Callback
|
||||
* ~~~~~~~~~~~~~~~~~~~~
|
||||
* ^^^^^^^^^^^^^^^^^^^^
|
||||
*
|
||||
* The server has a unique way of dealing with the content of variables. Instead
|
||||
* of storing a variant attached to the variable node, the node can point to a
|
||||
@ -635,8 +654,9 @@ typedef struct {
|
||||
* @return Returns a status code for logging. Error codes intended for the
|
||||
* original caller are set in the value. If an error is returned,
|
||||
* then no releasing of the value is done. */
|
||||
UA_StatusCode (*read)(const UA_NodeId *nodeId, void *nodeContext,
|
||||
UA_Boolean includeSourceTimeStamp,
|
||||
UA_StatusCode (*read)(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);
|
||||
|
||||
/* Write into a data source. The write member of UA_DataSource can be empty
|
||||
@ -649,8 +669,10 @@ typedef struct {
|
||||
* @param range An optional data range. If the data source is scalar or does
|
||||
* not support writing of ranges, then an error code is returned.
|
||||
* @return Returns a status code that is returned to the user */
|
||||
UA_StatusCode (*write)(const UA_NodeId *nodeId, void *nodeContext,
|
||||
const UA_Variant *data, const UA_NumericRange *range);
|
||||
UA_StatusCode (*write)(UA_Server *server, const UA_NodeId *sessionId,
|
||||
void *sessionContext, const UA_NodeId *nodeId,
|
||||
void *nodeContext, const UA_NumericRange *range,
|
||||
const UA_DataValue *value);
|
||||
} UA_DataSource;
|
||||
|
||||
UA_StatusCode UA_EXPORT
|
||||
@ -661,9 +683,9 @@ UA_Server_setVariableNode_dataSource(UA_Server *server, const UA_NodeId nodeId,
|
||||
* .. _value-callback:
|
||||
*
|
||||
* Value Callback
|
||||
* ~~~~~~~~~~~~~~
|
||||
* ^^^^^^^^^^^^^^
|
||||
* Value Callbacks can be attached to variable and variable type nodes. If
|
||||
* not-null, they are called before reading and after writing respectively. */
|
||||
* not ``NULL``, they are called before reading and after writing respectively. */
|
||||
typedef struct {
|
||||
/* Called before the value attribute is read. It is possible to write into the
|
||||
* value attribute during onRead (using the write service). The node is
|
||||
@ -675,19 +697,29 @@ typedef struct {
|
||||
* @param data Points to the current node value.
|
||||
* @param range Points to the numeric range the client wants to read from
|
||||
* (or NULL). */
|
||||
void (*onRead)(const UA_NodeId *nodeid, void *nodeContext,
|
||||
const UA_Variant *data, const UA_NumericRange *range);
|
||||
void (*onRead)(UA_Server *server, const UA_NodeId *sessionId,
|
||||
void *sessionContext, const UA_NodeId *nodeid,
|
||||
void *nodeContext, const UA_NumericRange *range,
|
||||
const UA_DataValue *value);
|
||||
|
||||
/* Called after writing the value attribute. The node is re-opened after
|
||||
* writing so that the new value is visible in the callback.
|
||||
*
|
||||
* @param handle Points to user-provided data for the callback.
|
||||
* @param server The server executing the callback
|
||||
* @sessionId The identifier of the session
|
||||
* @sessionContext Additional data attached to the session
|
||||
* in the access control layer
|
||||
* @param nodeid The identifier of the node.
|
||||
* @param data Points to the current node value (after writing).
|
||||
* @param nodeUserContext Additional data attached to the node by
|
||||
* the user.
|
||||
* @param nodeConstructorContext Additional data attached to the node
|
||||
* by the type constructor(s).
|
||||
* @param range Points to the numeric range the client wants to write to (or
|
||||
* NULL). */
|
||||
void (*onWrite)(const UA_NodeId *nodeId, void *nodeContext,
|
||||
const UA_Variant *data, const UA_NumericRange *range);
|
||||
void (*onWrite)(UA_Server *server, const UA_NodeId *sessionId,
|
||||
void *sessionContext, const UA_NodeId *nodeId,
|
||||
void *nodeContext, const UA_NumericRange *range,
|
||||
const UA_DataValue *data);
|
||||
} UA_ValueCallback;
|
||||
|
||||
UA_StatusCode UA_EXPORT
|
||||
@ -697,13 +729,19 @@ UA_Server_setVariableNode_valueCallback(UA_Server *server,
|
||||
|
||||
/**
|
||||
* Method Callbacks
|
||||
* ~~~~~~~~~~~~~~~~ */
|
||||
* ^^^^^^^^^^^^^^^^
|
||||
* Method callbacks are set to `NULL` (not executable) when a method node is added
|
||||
* over the network. In theory, it is possible to add a callback via
|
||||
* ``UA_Server_setMethodNode_callback`` within the global constructor when adding
|
||||
* methods over the network is really wanted. */
|
||||
|
||||
typedef UA_StatusCode
|
||||
(*UA_MethodCallback)(const UA_NodeId *methodId, void *methodContext,
|
||||
const UA_NodeId *objectId, void *objectContext,
|
||||
const UA_NodeId *sessionId, void *sessionContext,
|
||||
size_t inputSize, const UA_Variant *input,
|
||||
size_t outputSize, UA_Variant *output);
|
||||
(*UA_MethodCallback)(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);
|
||||
|
||||
#ifdef UA_ENABLE_METHODCALLS
|
||||
|
||||
@ -720,25 +758,26 @@ UA_Server_call(UA_Server *server, const UA_CallMethodRequest *request);
|
||||
* .. _addnodes:
|
||||
*
|
||||
* Node Addition and Deletion
|
||||
* ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
* --------------------------
|
||||
* When creating dynamic node instances at runtime, chances are that you will
|
||||
* not care about the specific NodeId of the new node, as long as you can
|
||||
* reference it later. When passing numeric NodeIds with a numeric identifier 0,
|
||||
* the stack evaluates this as "select a random unassigned numeric NodeId in
|
||||
* that namespace". To find out which NodeId was actually assigned to the new
|
||||
* node, you may pass a pointer `outNewNodeId`, which will (after a successfull
|
||||
* node insertion) contain the nodeId of the new node. You may also pass NULL
|
||||
* pointer if this result is not relevant. The namespace index for nodes you
|
||||
* create should never be 0, as that index is reserved for OPC UA's
|
||||
* self-description (namespace * 0).
|
||||
* node insertion) contain the nodeId of the new node. You may also pass a
|
||||
* ``NULL`` pointer if this result is not needed.
|
||||
*
|
||||
* See the Section :ref:`node-lifecycle` on constructors and on attaching
|
||||
* user-defined data to nodes.
|
||||
*
|
||||
* The methods for node addition and deletion take mostly const arguments that
|
||||
* are not modified. When creating a node, a deep copy of the node identifier,
|
||||
* node attributes, etc. is created. Therefore, it is possible to call for
|
||||
* example `UA_Server_addVariablenode` with a value attribute (a :ref:`variant`)
|
||||
* pointing to a memory location on the stack. If you need changes to a variable
|
||||
* value to manifest at a specific memory location, please use a
|
||||
* :ref:`datasource` or a :ref:`value-callback`. */
|
||||
* example ``UA_Server_addVariablenode`` with a value attribute (a
|
||||
* :ref:`variant`) pointing to a memory location on the stack. If you need
|
||||
* changes to a variable value to manifest at a specific memory location, please
|
||||
* use a :ref:`datasource` or a :ref:`value-callback`. */
|
||||
|
||||
/* Protect against redundant definitions for server/client */
|
||||
#ifndef UA_DEFAULT_ATTRIBUTES_DEFINED
|
||||
@ -757,15 +796,6 @@ UA_EXPORT extern const UA_DataTypeAttributes UA_DataTypeAttributes_default;
|
||||
UA_EXPORT extern const UA_ViewAttributes UA_ViewAttributes_default;
|
||||
#endif
|
||||
|
||||
/* The instantiation callback is used to track the addition of new nodes. It is
|
||||
* also called for all sub-nodes contained in an object or variable type node
|
||||
* that is instantiated. */
|
||||
typedef struct {
|
||||
UA_StatusCode (*method)(const UA_NodeId objectId,
|
||||
const UA_NodeId typeDefinitionId, void *handle);
|
||||
void *handle;
|
||||
} UA_InstantiationCallback;
|
||||
|
||||
/* Don't use this function. There are typed versions as inline functions. */
|
||||
UA_StatusCode UA_EXPORT
|
||||
__UA_Server_addNode(UA_Server *server, const UA_NodeClass nodeClass,
|
||||
|
@ -46,7 +46,7 @@ struct UA_ServerConfig {
|
||||
|
||||
/* Custom DataTypes */
|
||||
size_t customDataTypesSize;
|
||||
const UA_DataType *customDataTypes;
|
||||
UA_DataType *customDataTypes;
|
||||
|
||||
/* Networking */
|
||||
size_t networkLayersSize;
|
||||
|
@ -28,7 +28,7 @@ UA_UsernamePasswordLogin usernamePasswords[2] = {
|
||||
UA_StatusCode
|
||||
activateSession_default(const UA_NodeId *sessionId,
|
||||
const UA_ExtensionObject *userIdentityToken,
|
||||
void **sessionHandle) {
|
||||
void **sessionContext) {
|
||||
/* Could the token be decoded? */
|
||||
if(userIdentityToken->encoding < UA_EXTENSIONOBJECT_DECODED)
|
||||
return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
|
||||
@ -47,7 +47,7 @@ activateSession_default(const UA_NodeId *sessionId,
|
||||
return UA_STATUSCODE_BADIDENTITYTOKENINVALID;
|
||||
|
||||
/* No userdata atm */
|
||||
*sessionHandle = NULL;
|
||||
*sessionContext = NULL;
|
||||
return UA_STATUSCODE_GOOD;
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ activateSession_default(const UA_NodeId *sessionId,
|
||||
return UA_STATUSCODE_BADUSERACCESSDENIED;
|
||||
|
||||
/* No userdata atm */
|
||||
*sessionHandle = NULL;
|
||||
*sessionContext = NULL;
|
||||
return UA_STATUSCODE_GOOD;
|
||||
}
|
||||
|
||||
@ -87,64 +87,55 @@ activateSession_default(const UA_NodeId *sessionId,
|
||||
}
|
||||
|
||||
void
|
||||
closeSession_default(const UA_NodeId *sessionId,
|
||||
void *sessionHandle) {
|
||||
/* no handle to clean up */
|
||||
closeSession_default(const UA_NodeId *sessionId, void *sessionContext) {
|
||||
/* no context to clean up */
|
||||
}
|
||||
|
||||
UA_UInt32
|
||||
getUserRightsMask_default(const UA_NodeId *sessionId,
|
||||
void *sessionHandle,
|
||||
const UA_NodeId *nodeId) {
|
||||
getUserRightsMask_default(const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_NodeId *nodeId, void *nodeContext) {
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
UA_Byte
|
||||
getUserAccessLevel_default(const UA_NodeId *sessionId,
|
||||
void *sessionHandle,
|
||||
const UA_NodeId *nodeId) {
|
||||
getUserAccessLevel_default(const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_NodeId *nodeId, void *nodeContext) {
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
UA_Boolean
|
||||
getUserExecutable_default(const UA_NodeId *sessionId,
|
||||
void *sessionHandle,
|
||||
const UA_NodeId *nodeId) {
|
||||
getUserExecutable_default(const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_NodeId *methodId, void *methodContext) {
|
||||
return true;
|
||||
}
|
||||
|
||||
UA_Boolean
|
||||
getUserExecutableOnObject_default(const UA_NodeId *sessionId,
|
||||
void *sessionHandle,
|
||||
const UA_NodeId *methodId,
|
||||
const UA_NodeId *objectId) {
|
||||
getUserExecutableOnObject_default(const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_NodeId *methodId, void *methodContext,
|
||||
const UA_NodeId *objectId, void *objectContext) {
|
||||
return true;
|
||||
}
|
||||
|
||||
UA_Boolean
|
||||
allowAddNode_default(const UA_NodeId *sessionId,
|
||||
void *sessionHandle,
|
||||
allowAddNode_default(const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_AddNodesItem *item) {
|
||||
return true;
|
||||
}
|
||||
|
||||
UA_Boolean
|
||||
allowAddReference_default(const UA_NodeId *sessionId,
|
||||
void *sessionHandle,
|
||||
allowAddReference_default(const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_AddReferencesItem *item) {
|
||||
return true;
|
||||
}
|
||||
|
||||
UA_Boolean
|
||||
allowDeleteNode_default(const UA_NodeId *sessionId,
|
||||
void *sessionHandle,
|
||||
allowDeleteNode_default(const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_DeleteNodesItem *item) {
|
||||
return true;
|
||||
}
|
||||
|
||||
UA_Boolean
|
||||
allowDeleteReference_default(const UA_NodeId *sessionId,
|
||||
void *sessionHandle,
|
||||
allowDeleteReference_default(const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_DeleteReferencesItem *item) {
|
||||
return true;
|
||||
}
|
||||
|
@ -13,43 +13,42 @@ extern "C" {
|
||||
UA_StatusCode UA_EXPORT
|
||||
activateSession_default(const UA_NodeId *sessionId,
|
||||
const UA_ExtensionObject *userIdentityToken,
|
||||
void **sessionHandle);
|
||||
void **sessionContext);
|
||||
|
||||
void UA_EXPORT
|
||||
closeSession_default(const UA_NodeId *sessionId, void *sessionHandle);
|
||||
closeSession_default(const UA_NodeId *sessionId, void *sessionContext);
|
||||
|
||||
UA_UInt32 UA_EXPORT
|
||||
getUserRightsMask_default(const UA_NodeId *sessionId, void *sessionHandle,
|
||||
const UA_NodeId *nodeId);
|
||||
getUserRightsMask_default(const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_NodeId *nodeId, void *nodeContext);
|
||||
|
||||
UA_Byte UA_EXPORT
|
||||
getUserAccessLevel_default(const UA_NodeId *sessionId, void *sessionHandle,
|
||||
const UA_NodeId *nodeId);
|
||||
getUserAccessLevel_default(const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_NodeId *nodeId, void *nodeContext);
|
||||
|
||||
UA_Boolean UA_EXPORT
|
||||
getUserExecutable_default(const UA_NodeId *sessionId, void *sessionHandle,
|
||||
const UA_NodeId *nodeId);
|
||||
getUserExecutable_default(const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_NodeId *methodId, void *methodContext);
|
||||
|
||||
UA_Boolean UA_EXPORT
|
||||
getUserExecutableOnObject_default(const UA_NodeId *sessionId,
|
||||
void *sessionHandle,
|
||||
const UA_NodeId *methodId,
|
||||
const UA_NodeId *objectId);
|
||||
getUserExecutableOnObject_default(const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_NodeId *methodId, void *methodContext,
|
||||
const UA_NodeId *objectId, void *objectContext);
|
||||
|
||||
UA_Boolean UA_EXPORT
|
||||
allowAddNode_default(const UA_NodeId *sessionId, void *sessionHandle,
|
||||
allowAddNode_default(const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_AddNodesItem *item);
|
||||
|
||||
UA_Boolean UA_EXPORT
|
||||
allowAddReference_default(const UA_NodeId *sessionId, void *sessionHandle,
|
||||
allowAddReference_default(const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_AddReferencesItem *item);
|
||||
|
||||
UA_Boolean UA_EXPORT
|
||||
allowDeleteNode_default(const UA_NodeId *sessionId, void *sessionHandle,
|
||||
allowDeleteNode_default(const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_DeleteNodesItem *item);
|
||||
|
||||
UA_Boolean UA_EXPORT
|
||||
allowDeleteReference_default(const UA_NodeId *sessionId, void *sessionHandle,
|
||||
allowDeleteReference_default(const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_DeleteReferencesItem *item);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -142,6 +142,10 @@ UA_ServerConfig_new_minimal(UA_UInt16 portNumber,
|
||||
/* Endpoints */
|
||||
/* conf->endpoints = {0, NULL}; */
|
||||
|
||||
/* Global Node Lifecycle */
|
||||
conf->nodeLifecycle.constructor = NULL;
|
||||
conf->nodeLifecycle.destructor = NULL;
|
||||
|
||||
/* Access Control */
|
||||
conf->accessControl.enableAnonymousLogin = true;
|
||||
conf->accessControl.enableUsernamePasswordLogin = true;
|
||||
|
@ -9,6 +9,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ua_server.h"
|
||||
#include "ua_server_config.h"
|
||||
#include "ua_client.h"
|
||||
#include "ua_client_highlevel.h"
|
||||
|
||||
|
@ -5,19 +5,8 @@
|
||||
#include "ua_server_internal.h"
|
||||
#include "ua_nodes.h"
|
||||
|
||||
void UA_Node_deleteReferences(UA_Node *node) {
|
||||
for(size_t i = 0; i < node->referencesSize; ++i) {
|
||||
UA_NodeReferenceKind *refs = &node->references[i];
|
||||
for(size_t j = 0; j < refs->targetIdsSize; ++j)
|
||||
UA_ExpandedNodeId_deleteMembers(&refs->targetIds[j]);
|
||||
UA_free(refs->targetIds);
|
||||
UA_NodeId_deleteMembers(&refs->referenceTypeId);
|
||||
}
|
||||
if(node->references)
|
||||
UA_free(node->references);
|
||||
node->references = NULL;
|
||||
node->referencesSize = 0;
|
||||
}
|
||||
/* There is no UA_Node_new() method here. Creating nodes is part of the
|
||||
* NodeStore layer */
|
||||
|
||||
void UA_Node_deleteMembersAnyNodeClass(UA_Node *node) {
|
||||
/* Delete standard content */
|
||||
@ -157,7 +146,7 @@ UA_StatusCode UA_Node_copyAnyNodeClass(const UA_Node *src, UA_Node *dst) {
|
||||
retval |= UA_LocalizedText_copy(&src->description, &dst->description);
|
||||
dst->writeMask = src->writeMask;
|
||||
dst->context = src->context;
|
||||
dst->lifecycleState = src->lifecycleState;
|
||||
dst->constructed = src->constructed;
|
||||
if(retval != UA_STATUSCODE_GOOD) {
|
||||
UA_Node_deleteMembersAnyNodeClass(dst);
|
||||
return retval;
|
||||
@ -230,3 +219,308 @@ UA_StatusCode UA_Node_copyAnyNodeClass(const UA_Node *src, UA_Node *dst) {
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/******************************/
|
||||
/* Copy Attributes into Nodes */
|
||||
/******************************/
|
||||
|
||||
static UA_StatusCode
|
||||
copyStandardAttributes(UA_Node *node, const UA_AddNodesItem *item,
|
||||
const UA_NodeAttributes *attr) {
|
||||
UA_StatusCode retval;
|
||||
retval = UA_NodeId_copy(&item->requestedNewNodeId.nodeId, &node->nodeId);
|
||||
retval |= UA_QualifiedName_copy(&item->browseName, &node->browseName);
|
||||
retval |= UA_LocalizedText_copy(&attr->displayName, &node->displayName);
|
||||
retval |= UA_LocalizedText_copy(&attr->description, &node->description);
|
||||
node->writeMask = attr->writeMask;
|
||||
return retval;
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
copyCommonVariableAttributes(UA_VariableNode *node,
|
||||
const UA_VariableAttributes *attr) {
|
||||
/* Copy the array dimensions */
|
||||
UA_StatusCode retval =
|
||||
UA_Array_copy(attr->arrayDimensions, attr->arrayDimensionsSize,
|
||||
(void**)&node->arrayDimensions, &UA_TYPES[UA_TYPES_UINT32]);
|
||||
if(retval != UA_STATUSCODE_GOOD)
|
||||
return retval;
|
||||
node->arrayDimensionsSize = attr->arrayDimensionsSize;
|
||||
|
||||
/* Data type and value rank */
|
||||
retval |= UA_NodeId_copy(&attr->dataType, &node->dataType);
|
||||
node->valueRank = attr->valueRank;
|
||||
|
||||
/* Copy the value */
|
||||
node->valueSource = UA_VALUESOURCE_DATA;
|
||||
retval |= UA_Variant_copy(&attr->value, &node->value.data.value.value);
|
||||
node->value.data.value.hasValue = true;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
copyVariableNodeAttributes(UA_VariableNode *vnode,
|
||||
const UA_VariableAttributes *attr) {
|
||||
vnode->accessLevel = attr->accessLevel;
|
||||
vnode->historizing = attr->historizing;
|
||||
vnode->minimumSamplingInterval = attr->minimumSamplingInterval;
|
||||
return copyCommonVariableAttributes(vnode, attr);
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
copyVariableTypeNodeAttributes(UA_VariableTypeNode *vtnode,
|
||||
const UA_VariableTypeAttributes *attr) {
|
||||
vtnode->isAbstract = attr->isAbstract;
|
||||
return copyCommonVariableAttributes((UA_VariableNode*)vtnode,
|
||||
(const UA_VariableAttributes*)attr);
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
copyObjectNodeAttributes(UA_ObjectNode *onode, const UA_ObjectAttributes *attr) {
|
||||
onode->eventNotifier = attr->eventNotifier;
|
||||
return UA_STATUSCODE_GOOD;
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
copyReferenceTypeNodeAttributes(UA_ReferenceTypeNode *rtnode,
|
||||
const UA_ReferenceTypeAttributes *attr) {
|
||||
rtnode->isAbstract = attr->isAbstract;
|
||||
rtnode->symmetric = attr->symmetric;
|
||||
return UA_LocalizedText_copy(&attr->inverseName, &rtnode->inverseName);
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
copyObjectTypeNodeAttributes(UA_ObjectTypeNode *otnode,
|
||||
const UA_ObjectTypeAttributes *attr) {
|
||||
otnode->isAbstract = attr->isAbstract;
|
||||
return UA_STATUSCODE_GOOD;
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
copyViewNodeAttributes(UA_ViewNode *vnode, const UA_ViewAttributes *attr) {
|
||||
vnode->containsNoLoops = attr->containsNoLoops;
|
||||
vnode->eventNotifier = attr->eventNotifier;
|
||||
return UA_STATUSCODE_GOOD;
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
copyDataTypeNodeAttributes(UA_DataTypeNode *dtnode,
|
||||
const UA_DataTypeAttributes *attr) {
|
||||
dtnode->isAbstract = attr->isAbstract;
|
||||
return UA_STATUSCODE_GOOD;
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
copyMethodNodeAttributes(UA_MethodNode *mnode,
|
||||
const UA_MethodAttributes *attr) {
|
||||
mnode->executable = attr->executable;
|
||||
return UA_STATUSCODE_GOOD;
|
||||
}
|
||||
|
||||
#define CHECK_ATTRIBUTES(TYPE) \
|
||||
if(item->nodeAttributes.content.decoded.type != &UA_TYPES[UA_TYPES_##TYPE]) { \
|
||||
retval = UA_STATUSCODE_BADNODEATTRIBUTESINVALID; \
|
||||
break; \
|
||||
}
|
||||
|
||||
/* Copy the attributes into a new node. On success, newNode points to the
|
||||
* created node */
|
||||
UA_StatusCode
|
||||
UA_Node_createFromAttributes(const UA_AddNodesItem *item, UA_Node **newNode) {
|
||||
/* Check that we can read the attributes */
|
||||
if(item->nodeAttributes.encoding < UA_EXTENSIONOBJECT_DECODED ||
|
||||
!item->nodeAttributes.content.decoded.type)
|
||||
return UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
|
||||
|
||||
/* Create the node */
|
||||
// todo: error case where the nodeclass is faulty should return a different
|
||||
// status code
|
||||
UA_Node *node = UA_NodeStore_newNode(item->nodeClass);
|
||||
if(!node)
|
||||
return UA_STATUSCODE_BADOUTOFMEMORY;
|
||||
|
||||
/* Copy the attributes into the node */
|
||||
void *data = item->nodeAttributes.content.decoded.data;
|
||||
UA_StatusCode retval = copyStandardAttributes(node, item,
|
||||
(const UA_NodeAttributes*)data);
|
||||
switch(item->nodeClass) {
|
||||
case UA_NODECLASS_OBJECT:
|
||||
CHECK_ATTRIBUTES(OBJECTATTRIBUTES);
|
||||
retval |= copyObjectNodeAttributes((UA_ObjectNode*)node,
|
||||
(const UA_ObjectAttributes*)data);
|
||||
break;
|
||||
case UA_NODECLASS_VARIABLE:
|
||||
CHECK_ATTRIBUTES(VARIABLEATTRIBUTES);
|
||||
retval |= copyVariableNodeAttributes((UA_VariableNode*)node,
|
||||
(const UA_VariableAttributes*)data);
|
||||
break;
|
||||
case UA_NODECLASS_OBJECTTYPE:
|
||||
CHECK_ATTRIBUTES(OBJECTTYPEATTRIBUTES);
|
||||
retval |= copyObjectTypeNodeAttributes((UA_ObjectTypeNode*)node,
|
||||
(const UA_ObjectTypeAttributes*)data);
|
||||
break;
|
||||
case UA_NODECLASS_VARIABLETYPE:
|
||||
CHECK_ATTRIBUTES(VARIABLETYPEATTRIBUTES);
|
||||
retval |= copyVariableTypeNodeAttributes((UA_VariableTypeNode*)node,
|
||||
(const UA_VariableTypeAttributes*)data);
|
||||
break;
|
||||
case UA_NODECLASS_REFERENCETYPE:
|
||||
CHECK_ATTRIBUTES(REFERENCETYPEATTRIBUTES);
|
||||
retval |= copyReferenceTypeNodeAttributes((UA_ReferenceTypeNode*)node,
|
||||
(const UA_ReferenceTypeAttributes*)data);
|
||||
break;
|
||||
case UA_NODECLASS_DATATYPE:
|
||||
CHECK_ATTRIBUTES(DATATYPEATTRIBUTES);
|
||||
retval |= copyDataTypeNodeAttributes((UA_DataTypeNode*)node,
|
||||
(const UA_DataTypeAttributes*)data);
|
||||
break;
|
||||
case UA_NODECLASS_VIEW:
|
||||
CHECK_ATTRIBUTES(VIEWATTRIBUTES);
|
||||
retval |= copyViewNodeAttributes((UA_ViewNode*)node,
|
||||
(const UA_ViewAttributes*)data);
|
||||
break;
|
||||
case UA_NODECLASS_METHOD:
|
||||
CHECK_ATTRIBUTES(METHODATTRIBUTES);
|
||||
retval |= copyMethodNodeAttributes((UA_MethodNode*)node,
|
||||
(const UA_MethodAttributes*)data);
|
||||
break;
|
||||
case UA_NODECLASS_UNSPECIFIED:
|
||||
default:
|
||||
retval = UA_STATUSCODE_BADNODECLASSINVALID;
|
||||
}
|
||||
|
||||
if(retval == UA_STATUSCODE_GOOD)
|
||||
*newNode = (UA_Node*)node;
|
||||
else
|
||||
UA_NodeStore_deleteNode(node);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*********************/
|
||||
/* Manage References */
|
||||
/*********************/
|
||||
|
||||
static UA_StatusCode
|
||||
addReferenceTarget(UA_NodeReferenceKind *refs, const UA_ExpandedNodeId *target) {
|
||||
UA_ExpandedNodeId *targets =
|
||||
(UA_ExpandedNodeId*) UA_realloc(refs->targetIds,
|
||||
sizeof(UA_ExpandedNodeId) * (refs->targetIdsSize+1));
|
||||
if(!targets)
|
||||
return UA_STATUSCODE_BADOUTOFMEMORY;
|
||||
|
||||
refs->targetIds = targets;
|
||||
UA_StatusCode retval =
|
||||
UA_ExpandedNodeId_copy(target, &refs->targetIds[refs->targetIdsSize]);
|
||||
|
||||
if(retval == UA_STATUSCODE_GOOD) {
|
||||
refs->targetIdsSize++;
|
||||
} else if(refs->targetIdsSize == 0) {
|
||||
/* We had zero references before (realloc was a malloc) */
|
||||
UA_free(refs->targetIds);
|
||||
refs->targetIds = NULL;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
addReferenceKind(UA_Node *node, const UA_AddReferencesItem *item) {
|
||||
UA_NodeReferenceKind *refs =
|
||||
(UA_NodeReferenceKind*)UA_realloc(node->references,
|
||||
sizeof(UA_NodeReferenceKind) * (node->referencesSize+1));
|
||||
if(!refs)
|
||||
return UA_STATUSCODE_BADOUTOFMEMORY;
|
||||
node->references = refs;
|
||||
UA_NodeReferenceKind *newRef = &refs[node->referencesSize];
|
||||
memset(newRef, 0, sizeof(UA_NodeReferenceKind));
|
||||
|
||||
newRef->isInverse = !item->isForward;
|
||||
UA_StatusCode retval = UA_NodeId_copy(&item->referenceTypeId, &newRef->referenceTypeId);
|
||||
retval |= addReferenceTarget(newRef, &item->targetNodeId);
|
||||
|
||||
if(retval == UA_STATUSCODE_GOOD) {
|
||||
node->referencesSize++;
|
||||
} else {
|
||||
UA_NodeId_deleteMembers(&newRef->referenceTypeId);
|
||||
if(node->referencesSize == 0) {
|
||||
UA_free(node->references);
|
||||
node->references = NULL;
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
UA_StatusCode
|
||||
UA_Node_addReference(UA_Node *node, const UA_AddReferencesItem *item) {
|
||||
for(size_t i = 0; i < node->referencesSize; ++i) {
|
||||
UA_NodeReferenceKind *refs = &node->references[i];
|
||||
if(refs->isInverse == item->isForward)
|
||||
continue;
|
||||
if(!UA_NodeId_equal(&refs->referenceTypeId, &item->referenceTypeId))
|
||||
continue;
|
||||
return addReferenceTarget(refs, &item->targetNodeId);
|
||||
}
|
||||
return addReferenceKind(node, item);
|
||||
}
|
||||
|
||||
UA_StatusCode
|
||||
UA_Node_deleteReference(UA_Node *node, const UA_DeleteReferencesItem *item) {
|
||||
for(size_t i = node->referencesSize; i > 0; --i) {
|
||||
UA_NodeReferenceKind *refs = &node->references[i-1];
|
||||
if(item->isForward == refs->isInverse)
|
||||
continue;
|
||||
if(!UA_NodeId_equal(&item->referenceTypeId, &refs->referenceTypeId))
|
||||
continue;
|
||||
|
||||
for(size_t j = refs->targetIdsSize; j > 0; --j) {
|
||||
if(!UA_NodeId_equal(&item->targetNodeId.nodeId, &refs->targetIds[j-1].nodeId))
|
||||
continue;
|
||||
|
||||
/* Ok, delete the reference */
|
||||
UA_ExpandedNodeId_deleteMembers(&refs->targetIds[j-1]);
|
||||
refs->targetIdsSize--;
|
||||
|
||||
/* One matching target remaining */
|
||||
if(refs->targetIdsSize > 0) {
|
||||
if(j-1 != refs->targetIdsSize) // avoid valgrind error: Source
|
||||
// and destination overlap in
|
||||
// memcpy
|
||||
refs->targetIds[j-1] = refs->targetIds[refs->targetIdsSize];
|
||||
return UA_STATUSCODE_GOOD;
|
||||
}
|
||||
|
||||
/* Remove refs */
|
||||
UA_free(refs->targetIds);
|
||||
UA_NodeId_deleteMembers(&refs->referenceTypeId);
|
||||
node->referencesSize--;
|
||||
if(node->referencesSize > 0) {
|
||||
if(i-1 != node->referencesSize) // avoid valgrind error: Source
|
||||
// and destination overlap in
|
||||
// memcpy
|
||||
node->references[i-1] = node->references[node->referencesSize];
|
||||
return UA_STATUSCODE_GOOD;
|
||||
}
|
||||
|
||||
/* Remove the node references */
|
||||
UA_free(node->references);
|
||||
node->references = NULL;
|
||||
return UA_STATUSCODE_GOOD;
|
||||
}
|
||||
}
|
||||
return UA_STATUSCODE_UNCERTAINREFERENCENOTDELETED;
|
||||
}
|
||||
|
||||
void UA_Node_deleteReferences(UA_Node *node) {
|
||||
for(size_t i = 0; i < node->referencesSize; ++i) {
|
||||
UA_NodeReferenceKind *refs = &node->references[i];
|
||||
for(size_t j = 0; j < refs->targetIdsSize; ++j)
|
||||
UA_ExpandedNodeId_deleteMembers(&refs->targetIds[j]);
|
||||
UA_free(refs->targetIds);
|
||||
UA_NodeId_deleteMembers(&refs->referenceTypeId);
|
||||
}
|
||||
if(node->references)
|
||||
UA_free(node->references);
|
||||
node->references = NULL;
|
||||
node->referencesSize = 0;
|
||||
}
|
||||
|
@ -57,13 +57,6 @@ typedef struct {
|
||||
UA_ExpandedNodeId *targetIds;
|
||||
} UA_NodeReferenceKind;
|
||||
|
||||
/* Which constructors were run on the node? */
|
||||
typedef enum {
|
||||
UA_NODELIFECYCLE_FRESH,
|
||||
UA_NODELIFECYCLE_CONSTRUCTOR_GLOBAL,
|
||||
UA_NODELIFECYCLE_CONSTRUCTOR_NODETYPE
|
||||
} UA_NodeLifecycleState;
|
||||
|
||||
#define UA_NODE_BASEATTRIBUTES \
|
||||
UA_NodeId nodeId; \
|
||||
UA_NodeClass nodeClass; \
|
||||
@ -76,7 +69,7 @@ typedef enum {
|
||||
\
|
||||
/* Members specific to open62541 */ \
|
||||
void *context; \
|
||||
UA_NodeLifecycleState lifecycleState;
|
||||
UA_Boolean constructed; /* don't run the constructors twice on a node */
|
||||
|
||||
typedef struct {
|
||||
UA_NODE_BASEATTRIBUTES
|
||||
|
@ -475,8 +475,8 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
|
||||
/* Find the matching session */
|
||||
session = UA_SecureChannel_getSession(channel, &requestHeader->authenticationToken);
|
||||
if(!session)
|
||||
session = UA_SessionManager_getSession(&server->sessionManager,
|
||||
&requestHeader->authenticationToken);
|
||||
session = UA_SessionManager_getSessionByToken(&server->sessionManager,
|
||||
&requestHeader->authenticationToken);
|
||||
|
||||
if(requestType == &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST]) {
|
||||
if(!session) {
|
||||
|
@ -11,6 +11,7 @@ extern "C" {
|
||||
|
||||
#include "ua_util.h"
|
||||
#include "ua_server.h"
|
||||
#include "ua_server_config.h"
|
||||
#include "ua_timer.h"
|
||||
#include "ua_connection_internal.h"
|
||||
#include "ua_session_manager.h"
|
||||
@ -60,11 +61,6 @@ struct UA_Worker;
|
||||
typedef struct UA_Worker UA_Worker;
|
||||
#endif
|
||||
|
||||
#if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
|
||||
/* Internally used context to a session 'context' of the current mehtod call */
|
||||
extern UA_THREAD_LOCAL UA_Session* methodCallSession;
|
||||
#endif
|
||||
|
||||
#ifdef UA_ENABLE_DISCOVERY
|
||||
|
||||
typedef struct registeredServer_list_entry {
|
||||
@ -110,9 +106,6 @@ struct UA_Server {
|
||||
/* Address Space */
|
||||
UA_NodeStore *nodestore;
|
||||
|
||||
/* Global Node Lifecycle */
|
||||
UA_NodeLifecycle nodeLifecycle;
|
||||
|
||||
#ifdef UA_ENABLE_DISCOVERY
|
||||
/* Discovery */
|
||||
LIST_HEAD(registeredServer_list, registeredServer_list_entry) registeredServers; // doubly-linked list of registered servers
|
||||
@ -170,9 +163,15 @@ struct UA_Server {
|
||||
/* Node Handling */
|
||||
/*****************/
|
||||
|
||||
void UA_Node_deleteMembersAnyNodeClass(UA_Node *node);
|
||||
void UA_Node_deleteReferences(UA_Node *node);
|
||||
UA_StatusCode UA_Node_copyAnyNodeClass(const UA_Node *src, UA_Node *dst);
|
||||
void UA_Node_deleteMembersAnyNodeClass(UA_Node *node);
|
||||
|
||||
UA_StatusCode UA_Node_addReference(UA_Node *node, const UA_AddReferencesItem *item);
|
||||
UA_StatusCode UA_Node_deleteReference(UA_Node *node, const UA_DeleteReferencesItem *item);
|
||||
void UA_Node_deleteReferences(UA_Node *node);
|
||||
|
||||
UA_StatusCode
|
||||
UA_Node_createFromAttributes(const UA_AddNodesItem *item, UA_Node **newNode);
|
||||
|
||||
/* Calls callback on the node. In the multithreaded case, the node is copied before and replaced in
|
||||
the nodestore. */
|
||||
@ -219,7 +218,7 @@ isNodeInTree(UA_NodeStore *ns, const UA_NodeId *leafNode,
|
||||
/* Returns an array with the hierarchy of type nodes. The returned array starts
|
||||
* at the leaf and continues "upwards" in the hierarchy based on the
|
||||
* ``hasSubType`` references. Since multiple-inheritance is possible in general,
|
||||
* duplicate entries are avoided. */
|
||||
* duplicate entries are removed. */
|
||||
UA_StatusCode
|
||||
getTypeHierarchy(UA_NodeStore *ns, const UA_NodeId *leafType,
|
||||
UA_NodeId **typeHierarchy, size_t *typeHierarchySize);
|
||||
@ -244,7 +243,8 @@ UA_Server_processServiceOperations(UA_Server *server, UA_Session *session,
|
||||
/***************************************/
|
||||
|
||||
UA_StatusCode
|
||||
readValueAttribute(UA_Server *server, const UA_VariableNode *vn, UA_DataValue *v);
|
||||
readValueAttribute(UA_Server *server, UA_Session *session,
|
||||
const UA_VariableNode *vn, UA_DataValue *v);
|
||||
|
||||
UA_StatusCode
|
||||
typeCheckValue(UA_Server *server, const UA_NodeId *targetDataTypeId,
|
||||
@ -266,8 +266,9 @@ compatibleDataType(UA_Server *server, const UA_NodeId *dataType,
|
||||
const UA_NodeId *constraintDataType);
|
||||
|
||||
UA_StatusCode
|
||||
writeValueRankAttribute(UA_Server *server, UA_VariableNode *node,
|
||||
UA_Int32 valueRank, UA_Int32 constraintValueRank);
|
||||
writeValueRankAttribute(UA_Server *server, UA_Session *session,
|
||||
UA_VariableNode *node, UA_Int32 valueRank,
|
||||
UA_Int32 constraintValueRank);
|
||||
|
||||
UA_StatusCode
|
||||
compatibleValueRanks(UA_Int32 valueRank, UA_Int32 constraintValueRank);
|
||||
@ -341,7 +342,7 @@ __UA_Server_addNode_begin(UA_Server *server, const UA_NodeClass nodeClass,
|
||||
const UA_QualifiedName *browseName,
|
||||
const UA_NodeAttributes *attr,
|
||||
const UA_DataType *attributeType,
|
||||
UA_NodeId *outNewNodeId);
|
||||
void *nodeContext, UA_NodeId *outNewNodeId);
|
||||
|
||||
/* The inline function UA_Server_addNode_finish might be more convenient to
|
||||
* pass NodeIds in-situ (e.g. UA_NODEID_NUMERIC(0, 5)) */
|
||||
@ -349,19 +350,18 @@ UA_StatusCode UA_EXPORT
|
||||
UA_Server_addNode_finish(UA_Server *server, const UA_NodeId nodeId,
|
||||
const UA_NodeId parentNodeId,
|
||||
const UA_NodeId referenceTypeId,
|
||||
const UA_NodeId typeDefinition,
|
||||
void *nodeContext);
|
||||
const UA_NodeId typeDefinition);
|
||||
|
||||
static UA_INLINE UA_StatusCode
|
||||
UA_Server_addReferenceTypeNode_begin(UA_Server *server, const UA_NodeId requestedNewNodeId,
|
||||
const UA_QualifiedName browseName,
|
||||
const UA_ReferenceTypeAttributes attr,
|
||||
UA_NodeId *outNewNodeId) {
|
||||
void *nodeContext, UA_NodeId *outNewNodeId) {
|
||||
return __UA_Server_addNode_begin(server, UA_NODECLASS_REFERENCETYPE,
|
||||
&requestedNewNodeId,
|
||||
&browseName, (const UA_NodeAttributes*)&attr,
|
||||
&UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES],
|
||||
outNewNodeId);
|
||||
nodeContext, outNewNodeId);
|
||||
}
|
||||
|
||||
static UA_INLINE UA_StatusCode
|
||||
@ -369,48 +369,48 @@ UA_Server_addDataTypeNode_begin(UA_Server *server,
|
||||
const UA_NodeId requestedNewNodeId,
|
||||
const UA_QualifiedName browseName,
|
||||
const UA_DataTypeAttributes attr,
|
||||
UA_NodeId *outNewNodeId) {
|
||||
void *nodeContext, UA_NodeId *outNewNodeId) {
|
||||
return __UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE,
|
||||
&requestedNewNodeId,
|
||||
&browseName, (const UA_NodeAttributes*)&attr,
|
||||
&UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],
|
||||
outNewNodeId);
|
||||
nodeContext, outNewNodeId);
|
||||
}
|
||||
|
||||
static UA_INLINE UA_StatusCode
|
||||
UA_Server_addVariableNode_begin(UA_Server *server, const UA_NodeId requestedNewNodeId,
|
||||
const UA_QualifiedName browseName,
|
||||
const UA_VariableAttributes attr,
|
||||
UA_NodeId *outNewNodeId) {
|
||||
void *nodeContext, UA_NodeId *outNewNodeId) {
|
||||
return __UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE,
|
||||
&requestedNewNodeId, &browseName,
|
||||
(const UA_NodeAttributes*)&attr,
|
||||
&UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],
|
||||
outNewNodeId);
|
||||
nodeContext, outNewNodeId);
|
||||
}
|
||||
|
||||
static UA_INLINE UA_StatusCode
|
||||
UA_Server_addVariableTypeNode_begin(UA_Server *server, const UA_NodeId requestedNewNodeId,
|
||||
const UA_QualifiedName browseName,
|
||||
const UA_VariableTypeAttributes attr,
|
||||
UA_NodeId *outNewNodeId) {
|
||||
void *nodeContext, UA_NodeId *outNewNodeId) {
|
||||
return __UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLETYPE,
|
||||
&requestedNewNodeId, &browseName,
|
||||
(const UA_NodeAttributes*)&attr,
|
||||
&UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES],
|
||||
outNewNodeId);
|
||||
nodeContext, outNewNodeId);
|
||||
}
|
||||
|
||||
static UA_INLINE UA_StatusCode
|
||||
UA_Server_addObjectNode_begin(UA_Server *server, const UA_NodeId requestedNewNodeId,
|
||||
const UA_QualifiedName browseName,
|
||||
const UA_ObjectAttributes attr,
|
||||
UA_NodeId *outNewNodeId) {
|
||||
void *nodeContext, UA_NodeId *outNewNodeId) {
|
||||
return __UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT,
|
||||
&requestedNewNodeId,
|
||||
&browseName, (const UA_NodeAttributes*)&attr,
|
||||
&UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],
|
||||
outNewNodeId);
|
||||
nodeContext, outNewNodeId);
|
||||
}
|
||||
|
||||
static UA_INLINE UA_StatusCode
|
||||
@ -418,30 +418,34 @@ UA_Server_addObjectTypeNode_begin(UA_Server *server,
|
||||
const UA_NodeId requestedNewNodeId,
|
||||
const UA_QualifiedName browseName,
|
||||
const UA_ObjectTypeAttributes attr,
|
||||
UA_NodeId *outNewNodeId) {
|
||||
void *nodeContext, UA_NodeId *outNewNodeId) {
|
||||
return __UA_Server_addNode_begin(server, UA_NODECLASS_OBJECTTYPE,
|
||||
&requestedNewNodeId,
|
||||
&browseName, (const UA_NodeAttributes*)&attr,
|
||||
&UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES],
|
||||
outNewNodeId);
|
||||
nodeContext, outNewNodeId);
|
||||
}
|
||||
|
||||
static UA_INLINE UA_StatusCode
|
||||
UA_Server_addMethodNode_begin(UA_Server *server, const UA_NodeId requestedNewNodeId,
|
||||
const UA_QualifiedName browseName,
|
||||
const UA_MethodAttributes attr,
|
||||
void *nodeContext, UA_NodeId *outNewNodeId) {
|
||||
return __UA_Server_addNode_begin(server, UA_NODECLASS_METHOD,
|
||||
&requestedNewNodeId,
|
||||
&browseName, (const UA_NodeAttributes*)&attr,
|
||||
&UA_TYPES[UA_TYPES_METHODATTRIBUTES],
|
||||
nodeContext, outNewNodeId);
|
||||
}
|
||||
|
||||
#ifdef UA_ENABLE_METHODCALLS
|
||||
UA_StatusCode
|
||||
UA_Server_addMethodNode_begin(UA_Server *server, const UA_NodeId requestedNewNodeId,
|
||||
const UA_QualifiedName browseName,
|
||||
const UA_MethodAttributes attr, UA_MethodCallback method,
|
||||
UA_NodeId *outNewNodeId);
|
||||
|
||||
UA_StatusCode
|
||||
UA_Server_addMethodNode_finish(UA_Server *server, const UA_NodeId nodeId,
|
||||
const UA_NodeId parentNodeId,
|
||||
const UA_NodeId referenceTypeId,
|
||||
size_t inputArgumentsSize,
|
||||
const UA_Argument* inputArguments,
|
||||
size_t outputArgumentsSize,
|
||||
const UA_Argument* outputArguments,
|
||||
void *nodeContext);
|
||||
const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
|
||||
UA_MethodCallback method,
|
||||
size_t inputArgumentsSize, const UA_Argument* inputArguments,
|
||||
size_t outputArgumentsSize, const UA_Argument* outputArguments);
|
||||
#endif
|
||||
|
||||
/**********************/
|
||||
|
@ -12,10 +12,6 @@
|
||||
#include "ua_subscription.h"
|
||||
#endif
|
||||
|
||||
#if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
|
||||
UA_THREAD_LOCAL UA_Session* methodCallSession = NULL;
|
||||
#endif
|
||||
|
||||
#ifndef UA_ENABLE_GENERATE_NAMESPACE0
|
||||
|
||||
/****************/
|
||||
@ -23,8 +19,9 @@ UA_THREAD_LOCAL UA_Session* methodCallSession = NULL;
|
||||
/****************/
|
||||
|
||||
static UA_StatusCode
|
||||
readStatus(const UA_NodeId *nodeid, void *nodeContext,
|
||||
UA_Boolean sourceTimestamp,
|
||||
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;
|
||||
@ -32,7 +29,6 @@ readStatus(const UA_NodeId *nodeid, void *nodeContext,
|
||||
return UA_STATUSCODE_GOOD;
|
||||
}
|
||||
|
||||
UA_Server *server = (UA_Server*)nodeContext;
|
||||
UA_ServerStatusDataType *status = UA_ServerStatusDataType_new();
|
||||
status->startTime = server->startTime;
|
||||
status->currentTime = UA_DateTime_now();
|
||||
@ -54,22 +50,23 @@ readStatus(const UA_NodeId *nodeid, void *nodeContext,
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
readNamespaces(const UA_NodeId *nodeid, void *nodeContext,
|
||||
UA_Boolean sourceTimestamp,
|
||||
const UA_NumericRange *range, UA_DataValue *value) {
|
||||
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) {
|
||||
if(range) {
|
||||
value->hasStatus = true;
|
||||
value->status = UA_STATUSCODE_BADINDEXRANGEINVALID;
|
||||
return UA_STATUSCODE_GOOD;
|
||||
}
|
||||
UA_Server *server = (UA_Server*)nodeContext;
|
||||
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(sourceTimestamp) {
|
||||
if(includeSourceTimeStamp) {
|
||||
value->hasSourceTimestamp = true;
|
||||
value->sourceTimestamp = UA_DateTime_now();
|
||||
}
|
||||
@ -77,24 +74,25 @@ readNamespaces(const UA_NodeId *nodeid, void *nodeContext,
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
writeNamespaces(const UA_NodeId *nodeid, void *nodeContext,
|
||||
const UA_Variant *data, const UA_NumericRange *range) {
|
||||
UA_Server *server = (UA_Server*)nodeContext;
|
||||
|
||||
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(data->type != &UA_TYPES[UA_TYPES_STRING])
|
||||
if(!value->hasValue ||
|
||||
value->value.type != &UA_TYPES[UA_TYPES_STRING])
|
||||
return UA_STATUSCODE_BADTYPEMISMATCH;
|
||||
|
||||
/* Check that the variant is not empty */
|
||||
if(!data->data)
|
||||
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*)data->data;
|
||||
size_t newNamespacesSize = data->arrayLength;
|
||||
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)
|
||||
@ -113,8 +111,9 @@ writeNamespaces(const UA_NodeId *nodeid, void *nodeContext,
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
readCurrentTime(const UA_NodeId *nodeid, void *nodeContext,
|
||||
UA_Boolean sourceTimeStamp,
|
||||
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;
|
||||
@ -136,13 +135,16 @@ readCurrentTime(const UA_NodeId *nodeid, void *nodeContext,
|
||||
|
||||
#if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
|
||||
static UA_StatusCode
|
||||
readMonitoredItems(const UA_NodeId *methodId, void *methodContext,
|
||||
const UA_NodeId *objectId, void *objectContext,
|
||||
const UA_NodeId *sessionId, void *sessionHandle,
|
||||
size_t inputSize, const UA_Variant *input,
|
||||
size_t outputSize, UA_Variant *output) {
|
||||
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;
|
||||
UA_UInt32 subscriptionId = *((UA_UInt32*)(input[0].data));
|
||||
UA_Session* session = methodCallSession;
|
||||
UA_Subscription* subscription = UA_Session_getSubscriptionByID(session, subscriptionId);
|
||||
if(!subscription)
|
||||
return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
|
||||
@ -267,15 +269,15 @@ addVariableNode(UA_Server *server, UA_UInt32 nodeid, char* name, UA_Int32 valueR
|
||||
|
||||
static void
|
||||
addDataSourceVariableNode(UA_Server *server, UA_UInt32 nodeid, char* name, UA_Int32 valueRank,
|
||||
const UA_NodeId *dataType, UA_DataSource *dataSource, UA_UInt32 parentid,
|
||||
UA_UInt32 referenceid, UA_UInt32 type_id) {
|
||||
const UA_NodeId *dataType, UA_DataSource *dataSource, void *nodeContext,
|
||||
UA_UInt32 parentid,UA_UInt32 referenceid, UA_UInt32 type_id) {
|
||||
UA_VariableAttributes attr = UA_VariableAttributes_default;
|
||||
attr.displayName = UA_LOCALIZEDTEXT("en_US", name);
|
||||
attr.valueRank = valueRank;
|
||||
attr.dataType = *dataType;
|
||||
UA_Server_addDataSourceVariableNode(server, UA_NODEID_NUMERIC(0, nodeid), UA_NODEID_NUMERIC(0, parentid),
|
||||
UA_NODEID_NUMERIC(0, referenceid), UA_QUALIFIEDNAME(0, name),
|
||||
UA_NODEID_NUMERIC(0, type_id), attr, *dataSource, NULL);
|
||||
UA_NODEID_NUMERIC(0, type_id), attr, *dataSource, nodeContext, NULL);
|
||||
}
|
||||
|
||||
/**********************/
|
||||
@ -295,7 +297,7 @@ void UA_Server_createNS0(UA_Server *server) {
|
||||
references_attr.symmetric = true;
|
||||
references_attr.inverseName = UA_LOCALIZEDTEXT("en_US", "References");
|
||||
UA_Server_addReferenceTypeNode_begin(server, UA_NODEID_NUMERIC(0, UA_NS0ID_REFERENCES),
|
||||
UA_QUALIFIEDNAME(0, "References"), references_attr, NULL);
|
||||
UA_QUALIFIEDNAME(0, "References"), references_attr, NULL, NULL);
|
||||
|
||||
UA_ReferenceTypeAttributes hassubtype_attr = UA_ReferenceTypeAttributes_default;
|
||||
hassubtype_attr.displayName = UA_LOCALIZEDTEXT("en_US", "HasSubtype");
|
||||
@ -303,7 +305,7 @@ void UA_Server_createNS0(UA_Server *server) {
|
||||
hassubtype_attr.symmetric = false;
|
||||
hassubtype_attr.inverseName = UA_LOCALIZEDTEXT("en_US", "SubtypeOf");
|
||||
UA_Server_addReferenceTypeNode_begin(server, UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
|
||||
UA_QUALIFIEDNAME(0, "HasSubtype"), hassubtype_attr, NULL);
|
||||
UA_QUALIFIEDNAME(0, "HasSubtype"), hassubtype_attr, NULL, NULL);
|
||||
|
||||
addReferenceTypeNode(server, "HierarchicalReferences", NULL,
|
||||
UA_NS0ID_HIERARCHICALREFERENCES,
|
||||
@ -365,7 +367,7 @@ void UA_Server_createNS0(UA_Server *server) {
|
||||
basedatatype_attr.displayName = UA_LOCALIZEDTEXT("en_US", "BaseDataType");
|
||||
basedatatype_attr.isAbstract = true;
|
||||
UA_Server_addDataTypeNode_begin(server, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE),
|
||||
UA_QUALIFIEDNAME(0, "BaseDataType"), basedatatype_attr, NULL);
|
||||
UA_QUALIFIEDNAME(0, "BaseDataType"), basedatatype_attr, NULL, NULL);
|
||||
|
||||
addDataTypeNode(server, "Boolean", UA_NS0ID_BOOLEAN, false, UA_NS0ID_BASEDATATYPE);
|
||||
addDataTypeNode(server, "Number", UA_NS0ID_NUMBER, true, UA_NS0ID_BASEDATATYPE);
|
||||
@ -411,7 +413,7 @@ void UA_Server_createNS0(UA_Server *server) {
|
||||
basevar_attr.valueRank = -2;
|
||||
basevar_attr.dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE);
|
||||
UA_Server_addVariableTypeNode_begin(server, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEVARIABLETYPE),
|
||||
UA_QUALIFIEDNAME(0, "BaseVariableType"), basevar_attr, NULL);
|
||||
UA_QUALIFIEDNAME(0, "BaseVariableType"), basevar_attr, NULL, NULL);
|
||||
|
||||
addVariableTypeNode(server, "BaseDataVariableType", UA_NS0ID_BASEDATAVARIABLETYPE,
|
||||
false, -2, UA_NS0ID_BASEDATATYPE, NULL, UA_NS0ID_BASEVARIABLETYPE);
|
||||
@ -435,7 +437,7 @@ void UA_Server_createNS0(UA_Server *server) {
|
||||
UA_ObjectTypeAttributes baseobj_attr = UA_ObjectTypeAttributes_default;
|
||||
baseobj_attr.displayName = UA_LOCALIZEDTEXT("en_US", "BaseObjectType");
|
||||
UA_Server_addObjectTypeNode_begin(server, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
|
||||
UA_QUALIFIEDNAME(0, "BaseObjectType"), baseobj_attr, NULL);
|
||||
UA_QUALIFIEDNAME(0, "BaseObjectType"), baseobj_attr, NULL, NULL);
|
||||
|
||||
addObjectTypeNode(server, "ModellingRuleType", UA_NS0ID_MODELLINGRULETYPE,
|
||||
false, UA_NS0ID_BASEOBJECTTYPE);
|
||||
@ -459,7 +461,7 @@ void UA_Server_createNS0(UA_Server *server) {
|
||||
UA_ObjectAttributes root_attr = UA_ObjectAttributes_default;
|
||||
root_attr.displayName = UA_LOCALIZEDTEXT("en_US", "Root");
|
||||
UA_Server_addObjectNode_begin(server, UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER),
|
||||
UA_QUALIFIEDNAME(0, "Root"), root_attr, NULL);
|
||||
UA_QUALIFIEDNAME(0, "Root"), root_attr, NULL, NULL);
|
||||
addReferenceInternal(server, UA_NS0ID_ROOTFOLDER, UA_NS0ID_HASTYPEDEFINITION,
|
||||
UA_NS0ID_FOLDERTYPE, true);
|
||||
|
||||
@ -516,7 +518,7 @@ void UA_Server_createNS0(UA_Server *server) {
|
||||
UA_ObjectAttributes server_attr = UA_ObjectAttributes_default;
|
||||
server_attr.displayName = UA_LOCALIZEDTEXT("en_US", "Server");
|
||||
UA_Server_addObjectNode_begin(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
|
||||
UA_QUALIFIEDNAME(0, "Server"), server_attr, NULL);
|
||||
UA_QUALIFIEDNAME(0, "Server"), server_attr, NULL, NULL);
|
||||
|
||||
/* ServerArray */
|
||||
UA_Variant_setArray(&var, &server->config.applicationDescription.applicationUri,
|
||||
@ -533,20 +535,20 @@ void UA_Server_createNS0(UA_Server *server) {
|
||||
nsarray_attr.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
|
||||
nsarray_attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
|
||||
UA_Server_addVariableNode_begin(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACEARRAY),
|
||||
UA_QUALIFIEDNAME(0, "NamespaceArray"), nsarray_attr, NULL);
|
||||
UA_QUALIFIEDNAME(0, "NamespaceArray"), nsarray_attr, NULL, NULL);
|
||||
UA_DataSource nsarray_datasource = {readNamespaces, writeNamespaces};
|
||||
UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACEARRAY),
|
||||
nsarray_datasource);
|
||||
UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACEARRAY),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE), server /* context for datasource*/);
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
|
||||
|
||||
/* Begin ServerCapabilities */
|
||||
UA_ObjectAttributes servercap_attr = UA_ObjectAttributes_default;
|
||||
servercap_attr.displayName = UA_LOCALIZEDTEXT("en_US", "ServerCapabilities");
|
||||
UA_Server_addObjectNode_begin(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES),
|
||||
UA_QUALIFIEDNAME(0, "ServerCapabilities"), servercap_attr, NULL);
|
||||
UA_QUALIFIEDNAME(0, "ServerCapabilities"), servercap_attr, NULL, NULL);
|
||||
|
||||
UA_String enLocale = UA_STRING("en");
|
||||
UA_Variant_setArray(&var, &enLocale, 1, &UA_TYPES[UA_TYPES_STRING]);
|
||||
@ -615,13 +617,13 @@ void UA_Server_createNS0(UA_Server *server) {
|
||||
UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCAPABILITIESTYPE), NULL);
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCAPABILITIESTYPE));
|
||||
|
||||
/* Begin ServerDiagnostics */
|
||||
UA_ObjectAttributes serverdiag_attr = UA_ObjectAttributes_default;
|
||||
serverdiag_attr.displayName = UA_LOCALIZEDTEXT("en_US", "ServerDiagnostics");
|
||||
UA_Server_addObjectNode_begin(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS),
|
||||
UA_QUALIFIEDNAME(0, "ServerDiagnostics"), serverdiag_attr, NULL);
|
||||
UA_QUALIFIEDNAME(0, "ServerDiagnostics"), serverdiag_attr, NULL, NULL);
|
||||
|
||||
UA_Boolean enabledFlag = false;
|
||||
UA_Variant_setScalar(&var, &enabledFlag, &UA_TYPES[UA_TYPES_BOOLEAN]);
|
||||
@ -633,11 +635,11 @@ void UA_Server_createNS0(UA_Server *server) {
|
||||
UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERDIAGNOSTICSTYPE), NULL);
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERDIAGNOSTICSTYPE));
|
||||
|
||||
UA_DataSource statusDS = {server, readStatus, NULL};
|
||||
UA_DataSource statusDS = {readStatus, NULL};
|
||||
addDataSourceVariableNode(server, UA_NS0ID_SERVER_SERVERSTATUS, "ServerStatus", -1,
|
||||
&UA_TYPES[UA_TYPES_SERVERSTATUSDATATYPE].typeId, &statusDS,
|
||||
&UA_TYPES[UA_TYPES_SERVERSTATUSDATATYPE].typeId, &statusDS, server,
|
||||
UA_NS0ID_SERVER, UA_NS0ID_HASCOMPONENT, UA_NS0ID_BASEDATAVARIABLETYPE);
|
||||
|
||||
UA_Variant_setScalar(&var, &server->startTime, &UA_TYPES[UA_TYPES_DATETIME]);
|
||||
@ -645,9 +647,9 @@ void UA_Server_createNS0(UA_Server *server) {
|
||||
&UA_TYPES[UA_TYPES_DATETIME].typeId, &var, UA_NS0ID_SERVER_SERVERSTATUS,
|
||||
UA_NS0ID_HASCOMPONENT, UA_NS0ID_BASEDATAVARIABLETYPE);
|
||||
|
||||
UA_DataSource currentDS = {NULL, readCurrentTime, NULL};
|
||||
UA_DataSource currentDS = {readCurrentTime, NULL};
|
||||
addDataSourceVariableNode(server, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME, "CurrentTime", -1,
|
||||
&UA_TYPES[UA_TYPES_DATETIME].typeId, ¤tDS,
|
||||
&UA_TYPES[UA_TYPES_DATETIME].typeId, ¤tDS, NULL,
|
||||
UA_NS0ID_SERVER_SERVERSTATUS, UA_NS0ID_HASCOMPONENT,
|
||||
UA_NS0ID_BASEDATAVARIABLETYPE);
|
||||
|
||||
@ -742,9 +744,7 @@ void UA_Server_createNS0(UA_Server *server) {
|
||||
addmethodattributes.userExecutable = true;
|
||||
UA_Server_addMethodNode_begin(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS),
|
||||
UA_QUALIFIEDNAME(0, "GetMonitoredItems"),
|
||||
addmethodattributes,
|
||||
readMonitoredItems, /* callback of the method node */
|
||||
NULL);
|
||||
addmethodattributes, NULL, NULL);
|
||||
|
||||
/* Add the arguments manually to get the nodeids right */
|
||||
UA_Argument inputArguments;
|
||||
@ -777,14 +777,15 @@ void UA_Server_createNS0(UA_Server *server) {
|
||||
UA_Server_addMethodNode_finish(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
|
||||
0, NULL, 0, NULL, NULL);
|
||||
readMonitoredItems, /* callback of the method node */
|
||||
0, NULL, 0, NULL);
|
||||
#endif /* defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS) */
|
||||
|
||||
/* Finish adding the server object */
|
||||
UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERTYPE), NULL);
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERTYPE));
|
||||
}
|
||||
|
||||
#endif /* UA_ENABLE_GENERATE_NAMESPACE0 */
|
||||
|
@ -24,9 +24,8 @@ getUserWriteMask(UA_Server *server, const UA_Session *session,
|
||||
if(session == &adminSession)
|
||||
return 0xFFFFFFFF; /* the local admin user has all rights */
|
||||
return node->writeMask &
|
||||
server->config.accessControl.getUserRightsMask(&session->sessionId,
|
||||
session->sessionHandle,
|
||||
&node->nodeId);
|
||||
server->config.accessControl.getUserRightsMask(&session->sessionId, session->sessionHandle,
|
||||
&node->nodeId, node->context);
|
||||
}
|
||||
|
||||
static UA_Byte
|
||||
@ -35,9 +34,8 @@ getUserAccessLevel(UA_Server *server, const UA_Session *session,
|
||||
if(session == &adminSession)
|
||||
return 0xFF; /* the local admin user has all rights */
|
||||
return node->accessLevel &
|
||||
server->config.accessControl.getUserAccessLevel(&session->sessionId,
|
||||
session->sessionHandle,
|
||||
&node->nodeId);
|
||||
server->config.accessControl.getUserAccessLevel(&session->sessionId, session->sessionHandle,
|
||||
&node->nodeId, node->context);
|
||||
}
|
||||
|
||||
static UA_Boolean
|
||||
@ -46,9 +44,8 @@ getUserExecutable(UA_Server *server, const UA_Session *session,
|
||||
if(session == &adminSession)
|
||||
return true; /* the local admin user has all rights */
|
||||
return node->executable &
|
||||
server->config.accessControl.getUserExecutable(&session->sessionId,
|
||||
session->sessionHandle,
|
||||
&node->nodeId);
|
||||
server->config.accessControl.getUserExecutable(&session->sessionId, session->sessionHandle,
|
||||
&node->nodeId, node->context);
|
||||
}
|
||||
|
||||
/*****************/
|
||||
@ -297,8 +294,8 @@ readArrayDimensionsAttribute(const UA_VariableNode *vn, UA_DataValue *v) {
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
writeArrayDimensionsAttribute(UA_Server *server, UA_VariableNode *node,
|
||||
size_t arrayDimensionsSize,
|
||||
writeArrayDimensionsAttribute(UA_Server *server, UA_Session *session,
|
||||
UA_VariableNode *node, size_t arrayDimensionsSize,
|
||||
UA_UInt32 *arrayDimensions) {
|
||||
/* If this is a variabletype, there must be no instances or subtypes of it
|
||||
* when we do the change */
|
||||
@ -335,7 +332,7 @@ writeArrayDimensionsAttribute(UA_Server *server, UA_VariableNode *node,
|
||||
/* Check if the current value is compatible with the array dimensions */
|
||||
UA_DataValue value;
|
||||
UA_DataValue_init(&value);
|
||||
retval = readValueAttribute(server, node, &value);
|
||||
retval = readValueAttribute(server, session, node, &value);
|
||||
if(retval != UA_STATUSCODE_GOOD)
|
||||
return retval;
|
||||
if(value.hasValue) {
|
||||
@ -367,17 +364,19 @@ writeArrayDimensionsAttribute(UA_Server *server, UA_VariableNode *node,
|
||||
/***********************/
|
||||
|
||||
static UA_StatusCode
|
||||
writeValueRankAttributeWithVT(UA_Server *server, UA_VariableNode *node,
|
||||
UA_Int32 valueRank) {
|
||||
writeValueRankAttributeWithVT(UA_Server *server, UA_Session *session,
|
||||
UA_VariableNode *node, UA_Int32 valueRank) {
|
||||
const UA_VariableTypeNode *vt = getVariableNodeType(server, node);
|
||||
if(!vt)
|
||||
return UA_STATUSCODE_BADINTERNALERROR;
|
||||
return writeValueRankAttribute(server, node, valueRank, vt->valueRank);
|
||||
return writeValueRankAttribute(server, session, node,
|
||||
valueRank, vt->valueRank);
|
||||
}
|
||||
|
||||
UA_StatusCode
|
||||
writeValueRankAttribute(UA_Server *server, UA_VariableNode *node,
|
||||
UA_Int32 valueRank, UA_Int32 constraintValueRank) {
|
||||
writeValueRankAttribute(UA_Server *server, UA_Session *session,
|
||||
UA_VariableNode *node, UA_Int32 valueRank,
|
||||
UA_Int32 constraintValueRank) {
|
||||
/* If this is a variabletype, there must be no instances or subtypes of it
|
||||
when we do the change */
|
||||
if(node->nodeClass == UA_NODECLASS_VARIABLETYPE &&
|
||||
@ -397,7 +396,7 @@ writeValueRankAttribute(UA_Server *server, UA_VariableNode *node,
|
||||
dimensions zero indicate a scalar for compatibleValueRankArrayDimensions. */
|
||||
UA_DataValue value;
|
||||
UA_DataValue_init(&value);
|
||||
retval = readValueAttribute(server, node, &value);
|
||||
retval = readValueAttribute(server, session, node, &value);
|
||||
if(retval != UA_STATUSCODE_GOOD)
|
||||
return retval;
|
||||
if(!value.hasValue || !value.value.type) {
|
||||
@ -423,8 +422,8 @@ writeValueRankAttribute(UA_Server *server, UA_VariableNode *node,
|
||||
/**********************/
|
||||
|
||||
static UA_StatusCode
|
||||
writeDataTypeAttribute(UA_Server *server, UA_VariableNode *node,
|
||||
const UA_NodeId *dataType,
|
||||
writeDataTypeAttribute(UA_Server *server, UA_Session *session,
|
||||
UA_VariableNode *node, const UA_NodeId *dataType,
|
||||
const UA_NodeId *constraintDataType) {
|
||||
/* If this is a variabletype, there must be no instances or subtypes of it
|
||||
when we do the change */
|
||||
@ -439,7 +438,7 @@ writeDataTypeAttribute(UA_Server *server, UA_VariableNode *node,
|
||||
/* Check if the current value would match the new type */
|
||||
UA_DataValue value;
|
||||
UA_DataValue_init(&value);
|
||||
UA_StatusCode retval = readValueAttribute(server, node, &value);
|
||||
UA_StatusCode retval = readValueAttribute(server, session, node, &value);
|
||||
if(retval != UA_STATUSCODE_GOOD)
|
||||
return retval;
|
||||
if(value.hasValue) {
|
||||
@ -466,12 +465,12 @@ writeDataTypeAttribute(UA_Server *server, UA_VariableNode *node,
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
writeDataTypeAttributeWithVT(UA_Server *server, UA_VariableNode *node,
|
||||
const UA_NodeId *dataType) {
|
||||
writeDataTypeAttributeWithVT(UA_Server *server, UA_Session *session,
|
||||
UA_VariableNode *node, const UA_NodeId *dataType) {
|
||||
const UA_VariableTypeNode *vt = getVariableNodeType(server, node);
|
||||
if(!vt)
|
||||
return UA_STATUSCODE_BADINTERNALERROR;
|
||||
return writeDataTypeAttribute(server, node, dataType, &vt->dataType);
|
||||
return writeDataTypeAttribute(server, session, node, dataType, &vt->dataType);
|
||||
}
|
||||
|
||||
/*******************/
|
||||
@ -479,12 +478,14 @@ writeDataTypeAttributeWithVT(UA_Server *server, UA_VariableNode *node,
|
||||
/*******************/
|
||||
|
||||
static UA_StatusCode
|
||||
readValueAttributeFromNode(UA_Server *server, const UA_VariableNode *vn,
|
||||
UA_DataValue *v, UA_NumericRange *rangeptr) {
|
||||
readValueAttributeFromNode(UA_Server *server, UA_Session *session,
|
||||
const UA_VariableNode *vn, UA_DataValue *v,
|
||||
UA_NumericRange *rangeptr) {
|
||||
if(vn->value.data.callback.onRead) {
|
||||
UA_RCU_UNLOCK();
|
||||
vn->value.data.callback.onRead(&vn->nodeId, vn->context,
|
||||
&vn->value.data.value.value, rangeptr);
|
||||
vn->value.data.callback.onRead(server, &session->sessionId,
|
||||
session->sessionHandle, &vn->nodeId,
|
||||
vn->context, rangeptr, &vn->value.data.value);
|
||||
UA_RCU_LOCK();
|
||||
#ifdef UA_ENABLE_MULTITHREADING
|
||||
/* Reopen the node to see the changes (multithreading only) */
|
||||
@ -499,7 +500,8 @@ readValueAttributeFromNode(UA_Server *server, const UA_VariableNode *vn,
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
readValueAttributeFromDataSource(const UA_VariableNode *vn, UA_DataValue *v,
|
||||
readValueAttributeFromDataSource(UA_Server *server, UA_Session *session,
|
||||
const UA_VariableNode *vn, UA_DataValue *v,
|
||||
UA_TimestampsToReturn timestamps,
|
||||
UA_NumericRange *rangeptr) {
|
||||
if(!vn->value.dataSource.read)
|
||||
@ -508,17 +510,16 @@ readValueAttributeFromDataSource(const UA_VariableNode *vn, UA_DataValue *v,
|
||||
timestamps == UA_TIMESTAMPSTORETURN_BOTH);
|
||||
UA_RCU_UNLOCK();
|
||||
UA_StatusCode retval =
|
||||
vn->value.dataSource.read(&vn->nodeId, vn->context,
|
||||
sourceTimeStamp, rangeptr, v);
|
||||
vn->value.dataSource.read(server, &session->sessionId, session->sessionHandle,
|
||||
&vn->nodeId, vn->context, sourceTimeStamp, rangeptr, v);
|
||||
UA_RCU_LOCK();
|
||||
return retval;
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
readValueAttributeComplete(UA_Server *server, const UA_VariableNode *vn,
|
||||
UA_TimestampsToReturn timestamps,
|
||||
const UA_String *indexRange,
|
||||
UA_DataValue *v) {
|
||||
readValueAttributeComplete(UA_Server *server, UA_Session *session,
|
||||
const UA_VariableNode *vn, UA_TimestampsToReturn timestamps,
|
||||
const UA_String *indexRange, UA_DataValue *v) {
|
||||
/* Compute the index range */
|
||||
UA_NumericRange range;
|
||||
UA_NumericRange *rangeptr = NULL;
|
||||
@ -532,9 +533,9 @@ readValueAttributeComplete(UA_Server *server, const UA_VariableNode *vn,
|
||||
|
||||
/* Read the value */
|
||||
if(vn->valueSource == UA_VALUESOURCE_DATA)
|
||||
retval = readValueAttributeFromNode(server, vn, v, rangeptr);
|
||||
retval = readValueAttributeFromNode(server, session, vn, v, rangeptr);
|
||||
else
|
||||
retval = readValueAttributeFromDataSource(vn, v, timestamps, rangeptr);
|
||||
retval = readValueAttributeFromDataSource(server, session, vn, v, timestamps, rangeptr);
|
||||
|
||||
/* Clean up */
|
||||
if(rangeptr)
|
||||
@ -543,10 +544,10 @@ readValueAttributeComplete(UA_Server *server, const UA_VariableNode *vn,
|
||||
}
|
||||
|
||||
UA_StatusCode
|
||||
readValueAttribute(UA_Server *server, const UA_VariableNode *vn,
|
||||
UA_DataValue *v) {
|
||||
return readValueAttributeComplete(server, vn, UA_TIMESTAMPSTORETURN_NEITHER,
|
||||
NULL, v);
|
||||
readValueAttribute(UA_Server *server, UA_Session *session,
|
||||
const UA_VariableNode *vn, UA_DataValue *v) {
|
||||
return readValueAttributeComplete(server, session, vn,
|
||||
UA_TIMESTAMPSTORETURN_NEITHER, NULL, v);
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
@ -594,7 +595,7 @@ writeValueAttributeWithRange(UA_VariableNode *node, const UA_DataValue *value,
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
writeValueAttribute(UA_Server *server, UA_VariableNode *node,
|
||||
writeValueAttribute(UA_Server *server, UA_Session *session, UA_VariableNode *node,
|
||||
const UA_DataValue *value, const UA_String *indexRange) {
|
||||
/* Parse the range */
|
||||
UA_NumericRange range;
|
||||
@ -649,15 +650,18 @@ writeValueAttribute(UA_Server *server, UA_VariableNode *node,
|
||||
change with the nodestore plugin approach) */
|
||||
#endif
|
||||
UA_RCU_UNLOCK();
|
||||
writtenNode->value.data.callback.onWrite(&writtenNode->nodeId, writtenNode->context,
|
||||
&writtenNode->value.data.value.value, rangeptr);
|
||||
writtenNode->value.data.callback.onWrite(server, &session->sessionId,
|
||||
session->sessionHandle, &writtenNode->nodeId,
|
||||
writtenNode->context, rangeptr,
|
||||
&writtenNode->value.data.value);
|
||||
UA_RCU_LOCK();
|
||||
}
|
||||
} else {
|
||||
if(node->value.dataSource.write) {
|
||||
UA_RCU_UNLOCK();
|
||||
retval = node->value.dataSource.write(&node->nodeId, node->context,
|
||||
&editableValue.value, rangeptr);
|
||||
retval = node->value.dataSource.write(server, &session->sessionId,
|
||||
session->sessionHandle, &node->nodeId,
|
||||
node->context, rangeptr, &editableValue);
|
||||
UA_RCU_LOCK();
|
||||
} else {
|
||||
retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
|
||||
@ -819,7 +823,7 @@ Operation_Read(UA_Server *server, UA_Session *session,
|
||||
retval = UA_STATUSCODE_BADUSERACCESSDENIED;
|
||||
break;
|
||||
}
|
||||
retval = readValueAttributeComplete(server, (const UA_VariableNode*)node,
|
||||
retval = readValueAttributeComplete(server, session, (const UA_VariableNode*)node,
|
||||
op_timestampsToReturn, &id->indexRange, v);
|
||||
break;
|
||||
}
|
||||
@ -1153,28 +1157,28 @@ copyAttributeIntoNode(UA_Server *server, UA_Session *session,
|
||||
} else { /* UA_NODECLASS_VARIABLETYPE */
|
||||
CHECK_USERWRITEMASK(UA_WRITEMASK_VALUEFORVARIABLETYPE);
|
||||
}
|
||||
retval = writeValueAttribute(server, (UA_VariableNode*)node,
|
||||
retval = writeValueAttribute(server, session, (UA_VariableNode*)node,
|
||||
&wvalue->value, &wvalue->indexRange);
|
||||
break;
|
||||
case UA_ATTRIBUTEID_DATATYPE:
|
||||
CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
|
||||
CHECK_USERWRITEMASK(UA_WRITEMASK_DATATYPE);
|
||||
CHECK_DATATYPE_SCALAR(NODEID);
|
||||
retval = writeDataTypeAttributeWithVT(server, (UA_VariableNode*)node,
|
||||
retval = writeDataTypeAttributeWithVT(server, session, (UA_VariableNode*)node,
|
||||
(const UA_NodeId*)value);
|
||||
break;
|
||||
case UA_ATTRIBUTEID_VALUERANK:
|
||||
CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
|
||||
CHECK_USERWRITEMASK(UA_WRITEMASK_VALUERANK);
|
||||
CHECK_DATATYPE_SCALAR(INT32);
|
||||
retval = writeValueRankAttributeWithVT(server, (UA_VariableNode*)node,
|
||||
retval = writeValueRankAttributeWithVT(server, session, (UA_VariableNode*)node,
|
||||
*(const UA_Int32*)value);
|
||||
break;
|
||||
case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
|
||||
CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
|
||||
CHECK_USERWRITEMASK(UA_WRITEMASK_ARRRAYDIMENSIONS);
|
||||
CHECK_DATATYPE_ARRAY(UINT32);
|
||||
retval = writeArrayDimensionsAttribute(server, (UA_VariableNode*)node,
|
||||
retval = writeArrayDimensionsAttribute(server, session, (UA_VariableNode*)node,
|
||||
wvalue->value.value.arrayLength,
|
||||
(UA_UInt32 *)wvalue->value.value.data);
|
||||
break;
|
||||
|
@ -91,7 +91,8 @@ Operation_CallMethod(UA_Server *server, UA_Session *session,
|
||||
if(session != &adminSession)
|
||||
executable = executable &&
|
||||
server->config.accessControl.getUserExecutableOnObject(&session->sessionId,
|
||||
session->sessionHandle, &request->objectId, &request->methodId);
|
||||
session->sessionHandle, &request->methodId, methodCalled->context,
|
||||
&request->objectId, object->context);
|
||||
if(!executable) {
|
||||
result->statusCode = UA_STATUSCODE_BADNOTWRITABLE; // There is no NOTEXECUTABLE?
|
||||
return;
|
||||
@ -153,19 +154,12 @@ Operation_CallMethod(UA_Server *server, UA_Session *session,
|
||||
}
|
||||
|
||||
/* Call the method */
|
||||
#if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
|
||||
methodCallSession = session;
|
||||
#endif
|
||||
result->statusCode =
|
||||
methodCalled->method(&methodCalled->nodeId, (void*)(uintptr_t)methodCalled->context,
|
||||
methodCalled->method(server, &session->sessionId, session->sessionHandle,
|
||||
&methodCalled->nodeId, (void*)(uintptr_t)methodCalled->context,
|
||||
&object->nodeId, (void*)(uintptr_t)&object->context,
|
||||
&session->sessionId, session->sessionHandle,
|
||||
request->inputArgumentsSize, request->inputArguments,
|
||||
result->outputArgumentsSize, result->outputArguments);
|
||||
#if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
|
||||
methodCallSession = NULL;
|
||||
#endif
|
||||
|
||||
/* TODO: Verify Output matches the argument definition */
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -68,7 +68,7 @@ UA_SessionManager_cleanupTimedOut(UA_SessionManager *sm,
|
||||
}
|
||||
|
||||
UA_Session *
|
||||
UA_SessionManager_getSession(UA_SessionManager *sm, const UA_NodeId *token) {
|
||||
UA_SessionManager_getSessionByToken(UA_SessionManager *sm, const UA_NodeId *token) {
|
||||
session_list_entry *current = NULL;
|
||||
LIST_FOREACH(current, &sm->sessions, pointers) {
|
||||
/* Token does not match */
|
||||
@ -93,6 +93,32 @@ UA_SessionManager_getSession(UA_SessionManager *sm, const UA_NodeId *token) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
UA_Session *
|
||||
UA_SessionManager_getSessionById(UA_SessionManager *sm, const UA_NodeId *sessionId) {
|
||||
session_list_entry *current = NULL;
|
||||
LIST_FOREACH(current, &sm->sessions, pointers) {
|
||||
/* Token does not match */
|
||||
if(!UA_NodeId_equal(¤t->session.sessionId, sessionId))
|
||||
continue;
|
||||
|
||||
/* Session has timed out */
|
||||
if(UA_DateTime_nowMonotonic() > current->session.validTill) {
|
||||
UA_LOG_INFO_SESSION(sm->server->config.logger, ¤t->session,
|
||||
"Client tries to use a session that has timed out");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Ok, return */
|
||||
return ¤t->session;
|
||||
}
|
||||
|
||||
/* Session not found */
|
||||
UA_LOG_INFO(sm->server->config.logger, UA_LOGCATEGORY_SESSION,
|
||||
"Try to use Session with identifier " UA_PRINTF_GUID_FORMAT " but is not found",
|
||||
UA_PRINTF_GUID_DATA(sessionId->identifier.guid));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Creates and adds a session. But it is not yet attached to a secure channel. */
|
||||
UA_StatusCode
|
||||
UA_SessionManager_createSession(UA_SessionManager *sm, UA_SecureChannel *channel,
|
||||
|
@ -45,7 +45,10 @@ UA_StatusCode
|
||||
UA_SessionManager_removeSession(UA_SessionManager *sm, const UA_NodeId *token);
|
||||
|
||||
UA_Session *
|
||||
UA_SessionManager_getSession(UA_SessionManager *sm, const UA_NodeId *token);
|
||||
UA_SessionManager_getSessionByToken(UA_SessionManager *sm, const UA_NodeId *token);
|
||||
|
||||
UA_Session *
|
||||
UA_SessionManager_getSessionById(UA_SessionManager *sm, const UA_NodeId *sessionId);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
@ -39,7 +39,8 @@ addVariable(size_t size) {
|
||||
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
|
||||
UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
|
||||
parentReferenceNodeId, myIntegerName,
|
||||
UA_NODEID_NULL, attr, NULL, NULL);
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
|
||||
attr, NULL, NULL);
|
||||
|
||||
UA_free(array);
|
||||
}
|
||||
|
@ -91,8 +91,7 @@ START_TEST(Node_Add) {
|
||||
|
||||
// Create custom reference type 'HasSubSubType' as child of HasSubtype
|
||||
{
|
||||
UA_ReferenceTypeAttributes attr;
|
||||
UA_ReferenceTypeAttributes_init(&attr);
|
||||
UA_ReferenceTypeAttributes attr = UA_ReferenceTypeAttributes_default;
|
||||
attr.description = UA_LOCALIZEDTEXT("en_US", "Some HasSubSubType");
|
||||
attr.displayName = UA_LOCALIZEDTEXT("en_US", "HasSubSubType");
|
||||
retval = UA_Client_addReferenceTypeNode(client, UA_NODEID_NULL,
|
||||
@ -105,8 +104,7 @@ START_TEST(Node_Add) {
|
||||
|
||||
// Create TestObjectType SubType within BaseObjectType
|
||||
{
|
||||
UA_ObjectTypeAttributes attr;
|
||||
UA_ObjectTypeAttributes_init(&attr);
|
||||
UA_ObjectTypeAttributes attr = UA_ObjectTypeAttributes_default;
|
||||
attr.description = UA_LOCALIZEDTEXT("en_US", "Some TestObjectType");
|
||||
attr.displayName = UA_LOCALIZEDTEXT("en_US", "TestObjectType");
|
||||
retval = UA_Client_addObjectTypeNode(client, UA_NODEID_NULL,
|
||||
@ -119,8 +117,7 @@ START_TEST(Node_Add) {
|
||||
|
||||
// Create Int128 DataType within Integer Datatype
|
||||
{
|
||||
UA_DataTypeAttributes attr;
|
||||
UA_DataTypeAttributes_init(&attr);
|
||||
UA_DataTypeAttributes attr = UA_DataTypeAttributes_default;
|
||||
attr.description = UA_LOCALIZEDTEXT("en_US", "Some Int128");
|
||||
attr.displayName = UA_LOCALIZEDTEXT("en_US", "Int128");
|
||||
retval = UA_Client_addDataTypeNode(client, UA_NODEID_NULL,
|
||||
@ -133,8 +130,7 @@ START_TEST(Node_Add) {
|
||||
|
||||
// Create PointType VariableType within BaseDataVariableType
|
||||
{
|
||||
UA_VariableTypeAttributes attr;
|
||||
UA_VariableTypeAttributes_init(&attr);
|
||||
UA_VariableTypeAttributes attr = UA_VariableTypeAttributes_default;
|
||||
attr.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
|
||||
attr.valueRank = 1; /* array with one dimension */
|
||||
UA_UInt32 arrayDims[1] = {2};
|
||||
@ -154,8 +150,7 @@ START_TEST(Node_Add) {
|
||||
|
||||
// create Coordinates Object within ObjectsFolder
|
||||
{
|
||||
UA_ObjectAttributes attr;
|
||||
UA_ObjectAttributes_init(&attr);
|
||||
UA_ObjectAttributes attr = UA_ObjectAttributes_default;
|
||||
attr.description = UA_LOCALIZEDTEXT("en_US", "Some Coordinates");
|
||||
attr.displayName = UA_LOCALIZEDTEXT("en_US", "Coordinates");
|
||||
retval = UA_Client_addObjectNode(client, UA_NODEID_NULL,
|
||||
@ -169,8 +164,7 @@ START_TEST(Node_Add) {
|
||||
|
||||
// create Variable 'Top' within Coordinates Object
|
||||
{
|
||||
UA_VariableAttributes attr;
|
||||
UA_VariableAttributes_init(&attr);
|
||||
UA_VariableAttributes attr = UA_VariableAttributes_default;
|
||||
attr.description = UA_LOCALIZEDTEXT("en_US", "Top Coordinate");
|
||||
attr.displayName = UA_LOCALIZEDTEXT("en_US", "Top");
|
||||
UA_Int32 values[2] = {10, 20};
|
||||
@ -188,12 +182,9 @@ START_TEST(Node_Add) {
|
||||
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
|
||||
}
|
||||
|
||||
// create Method 'Dummy' within Coordinates Object. Fails with BADNODECLASSINVALID
|
||||
// create Method 'Dummy' within Coordinates Object.
|
||||
{
|
||||
// creating a method from a client does not yet make much sense since the corresponding
|
||||
// action code can not be set from the client side
|
||||
UA_MethodAttributes attr;
|
||||
UA_MethodAttributes_init(&attr);
|
||||
UA_MethodAttributes attr = UA_MethodAttributes_default;
|
||||
attr.description = UA_LOCALIZEDTEXT("en_US", "Dummy method");
|
||||
attr.displayName = UA_LOCALIZEDTEXT("en_US", "Dummy");
|
||||
attr.executable = true;
|
||||
@ -203,13 +194,12 @@ START_TEST(Node_Add) {
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASORDEREDCOMPONENT),
|
||||
UA_QUALIFIEDNAME(1, "Dummy"),
|
||||
attr, &newMethodId);
|
||||
ck_assert_uint_eq(retval, UA_STATUSCODE_BADNODECLASSINVALID);
|
||||
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
|
||||
}
|
||||
|
||||
// create View 'AllTopCoordinates' whithin Views Folder
|
||||
{
|
||||
UA_ViewAttributes attr;
|
||||
UA_ViewAttributes_init(&attr);
|
||||
UA_ViewAttributes attr = UA_ViewAttributes_default;
|
||||
attr.description = UA_LOCALIZEDTEXT("en_US", "List of all top coordinates");
|
||||
attr.displayName = UA_LOCALIZEDTEXT("en_US", "AllTopCoordinates");
|
||||
retval = UA_Client_addViewNode(client, UA_NODEID_NULL,
|
||||
@ -220,7 +210,6 @@ START_TEST(Node_Add) {
|
||||
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
|
||||
}
|
||||
|
||||
|
||||
// Add 'Top' to view
|
||||
retval = UA_Client_addReference(client, newViewId, UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
|
||||
UA_TRUE, UA_STRING_NULL,
|
||||
@ -268,8 +257,7 @@ START_TEST(Node_ReadWrite) {
|
||||
|
||||
// create Coordinates Object within ObjectsFolder
|
||||
{
|
||||
UA_ObjectAttributes attr;
|
||||
UA_ObjectAttributes_init(&attr);
|
||||
UA_ObjectAttributes attr = UA_ObjectAttributes_default;
|
||||
attr.description = UA_LOCALIZEDTEXT("en_US", "UnitTest");
|
||||
attr.displayName = UA_LOCALIZEDTEXT("en_US", "UnitTest");
|
||||
retval = UA_Client_addObjectNode(client, UA_NODEID_NULL,
|
||||
@ -282,8 +270,7 @@ START_TEST(Node_ReadWrite) {
|
||||
|
||||
// create Variable 'Top' within UnitTest Object
|
||||
{
|
||||
UA_VariableAttributes attr;
|
||||
UA_VariableAttributes_init(&attr);
|
||||
UA_VariableAttributes attr = UA_VariableAttributes_default;
|
||||
attr.description = UA_LOCALIZEDTEXT("en_US", "Array");
|
||||
attr.displayName = UA_LOCALIZEDTEXT("en_US", "Array");
|
||||
attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
|
||||
@ -310,8 +297,7 @@ START_TEST(Node_ReadWrite) {
|
||||
|
||||
// create Variable 'Bottom' within UnitTest Object
|
||||
{
|
||||
UA_VariableAttributes attr;
|
||||
UA_VariableAttributes_init(&attr);
|
||||
UA_VariableAttributes attr = UA_VariableAttributes_default;
|
||||
attr.description = UA_LOCALIZEDTEXT("en_US", "Int");
|
||||
attr.displayName = UA_LOCALIZEDTEXT("en_US", "Int");
|
||||
attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
|
||||
|
@ -24,8 +24,11 @@ static UA_Server *server = NULL;
|
||||
static UA_ServerConfig *config = NULL;
|
||||
|
||||
static UA_StatusCode
|
||||
readCPUTemperature(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimeStamp,
|
||||
const UA_NumericRange *range, UA_DataValue *dataValue) {
|
||||
readCPUTemperature(UA_Server *server_,
|
||||
const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_NodeId *nodeId, void *nodeContext,
|
||||
UA_Boolean sourceTimeStamp, const UA_NumericRange *range,
|
||||
UA_DataValue *dataValue) {
|
||||
UA_Float temp = 20.5f;
|
||||
UA_Variant_setScalarCopy(&dataValue->value, &temp, &UA_TYPES[UA_TYPES_FLOAT]);
|
||||
dataValue->hasValue = true;
|
||||
@ -43,8 +46,7 @@ static void setup(void) {
|
||||
UA_StatusCode retval = UA_STATUSCODE_GOOD;
|
||||
|
||||
/* VariableNode */
|
||||
UA_VariableAttributes vattr;
|
||||
UA_VariableAttributes_init(&vattr);
|
||||
UA_VariableAttributes vattr = UA_VariableAttributes_default;
|
||||
UA_Int32 myInteger = 42;
|
||||
UA_Variant_setScalar(&vattr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
|
||||
vattr.description = UA_LOCALIZEDTEXT("locale","the answer");
|
||||
@ -56,24 +58,28 @@ static void setup(void) {
|
||||
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
|
||||
retval = UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
|
||||
parentReferenceNodeId, myIntegerName,
|
||||
UA_NODEID_NULL, vattr, NULL, NULL);
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
|
||||
vattr, NULL, NULL);
|
||||
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
|
||||
|
||||
/* DataSource VariableNode */
|
||||
UA_VariableAttributes_init(&vattr);
|
||||
UA_DataSource temperatureDataSource =
|
||||
(UA_DataSource) {.handle = NULL, .read = readCPUTemperature, .write = NULL};
|
||||
vattr = UA_VariableAttributes_default;
|
||||
UA_DataSource temperatureDataSource;
|
||||
temperatureDataSource.read = readCPUTemperature;
|
||||
temperatureDataSource.write = NULL;
|
||||
vattr.description = UA_LOCALIZEDTEXT("en_US","temperature");
|
||||
vattr.displayName = UA_LOCALIZEDTEXT("en_US","temperature");
|
||||
retval = UA_Server_addDataSourceVariableNode(server, UA_NODEID_STRING(1, "cpu.temperature"),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
|
||||
UA_QUALIFIEDNAME(1, "cpu temperature"),
|
||||
UA_NODEID_NULL, vattr, temperatureDataSource, NULL);
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
|
||||
vattr, temperatureDataSource,
|
||||
NULL, NULL);
|
||||
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
|
||||
|
||||
/* VariableNode with array */
|
||||
UA_VariableAttributes_init(&vattr);
|
||||
vattr = UA_VariableAttributes_default;
|
||||
UA_Int32 myIntegerArray[9] = {1,2,3,4,5,6,7,8,9};
|
||||
UA_Variant_setArray(&vattr.value, &myIntegerArray, 9, &UA_TYPES[UA_TYPES_INT32]);
|
||||
vattr.valueRank = -2;
|
||||
@ -87,12 +93,12 @@ static void setup(void) {
|
||||
parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
|
||||
retval = UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
|
||||
parentReferenceNodeId, myIntegerName,
|
||||
UA_NODEID_NULL, vattr, NULL, NULL);
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
|
||||
vattr, NULL, NULL);
|
||||
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
|
||||
|
||||
/* ObjectNode */
|
||||
UA_ObjectAttributes obj_attr;
|
||||
UA_ObjectAttributes_init(&obj_attr);
|
||||
UA_ObjectAttributes obj_attr = UA_ObjectAttributes_default;
|
||||
obj_attr.description = UA_LOCALIZEDTEXT("en_US","Demo");
|
||||
obj_attr.displayName = UA_LOCALIZEDTEXT("en_US","Demo");
|
||||
retval = UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, 50),
|
||||
@ -104,8 +110,7 @@ static void setup(void) {
|
||||
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
|
||||
|
||||
/* ViewNode */
|
||||
UA_ViewAttributes view_attr;
|
||||
UA_ViewAttributes_init(&view_attr);
|
||||
UA_ViewAttributes view_attr = UA_ViewAttributes_default;
|
||||
view_attr.description = UA_LOCALIZEDTEXT("en_US", "Viewtest");
|
||||
view_attr.displayName = UA_LOCALIZEDTEXT("en_US", "Viewtest");
|
||||
retval = UA_Server_addViewNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_VIEWNODE),
|
||||
@ -116,15 +121,14 @@ static void setup(void) {
|
||||
|
||||
#ifdef UA_ENABLE_METHODCALLS
|
||||
/* MethodNode */
|
||||
UA_MethodAttributes ma;
|
||||
UA_MethodAttributes_init(&ma);
|
||||
UA_MethodAttributes ma = UA_MethodAttributes_default;
|
||||
ma.description = UA_LOCALIZEDTEXT("en_US", "Methodtest");
|
||||
ma.displayName = UA_LOCALIZEDTEXT("en_US", "Methodtest");
|
||||
retval = UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_METHODNODE),
|
||||
UA_NODEID_NUMERIC(0, 3),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
|
||||
UA_QUALIFIEDNAME(0, "Methodtest"), ma,
|
||||
NULL, NULL, 0, NULL, 0, NULL, NULL);
|
||||
NULL, 0, NULL, 0, NULL, NULL, NULL);
|
||||
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
|
||||
#endif
|
||||
}
|
||||
@ -491,7 +495,7 @@ START_TEST(ReadSingleAttributeExecutableWithoutTimestamp) {
|
||||
ck_assert_int_eq(true, resp.hasValue);
|
||||
ck_assert_int_eq(0, resp.value.arrayLength);
|
||||
ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_BOOLEAN], resp.value.type);
|
||||
ck_assert(*(UA_Boolean*)resp.value.data==false);
|
||||
ck_assert(*(UA_Boolean*)resp.value.data==true);
|
||||
UA_DataValue_deleteMembers(&resp);
|
||||
#endif
|
||||
} END_TEST
|
||||
|
@ -21,9 +21,22 @@
|
||||
|
||||
static UA_Server *server = NULL;
|
||||
static UA_ServerConfig *config = NULL;
|
||||
static UA_Int32 handleCalled = 0;
|
||||
|
||||
static UA_StatusCode
|
||||
globalInstantiationMethod(UA_Server *server_,
|
||||
const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_NodeId *nodeId, void **nodeContext) {
|
||||
handleCalled++;
|
||||
return UA_STATUSCODE_GOOD;
|
||||
}
|
||||
|
||||
static void setup(void) {
|
||||
config = UA_ServerConfig_new_default();
|
||||
UA_GlobalNodeLifecycle lifecycle;
|
||||
lifecycle.constructor = globalInstantiationMethod;
|
||||
lifecycle.destructor = NULL;
|
||||
config->nodeLifecycle = lifecycle;
|
||||
server = UA_Server_new(config);
|
||||
}
|
||||
|
||||
@ -32,15 +45,9 @@ static void teardown(void) {
|
||||
UA_ServerConfig_delete(config);
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
instantiationMethod(UA_NodeId newNodeId, UA_NodeId templateId, void *handle ) {
|
||||
*((UA_Int32 *) handle) += 1;
|
||||
return UA_STATUSCODE_GOOD;
|
||||
}
|
||||
START_TEST(AddVariableNode) {
|
||||
/* add a variable node to the address space */
|
||||
UA_VariableAttributes attr;
|
||||
UA_VariableAttributes_init(&attr);
|
||||
UA_VariableAttributes attr = UA_VariableAttributes_default;
|
||||
UA_Int32 myInteger = 42;
|
||||
UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
|
||||
attr.description = UA_LOCALIZEDTEXT("en_US","the answer");
|
||||
@ -49,36 +56,36 @@ START_TEST(AddVariableNode) {
|
||||
UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
|
||||
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
|
||||
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
|
||||
UA_StatusCode res = UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId, parentReferenceNodeId,
|
||||
myIntegerName, UA_NODEID_NULL, attr, NULL, NULL);
|
||||
UA_StatusCode res =
|
||||
UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
|
||||
parentReferenceNodeId, myIntegerName,
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
|
||||
attr, NULL, NULL);
|
||||
ck_assert_int_eq(UA_STATUSCODE_GOOD, res);
|
||||
} END_TEST
|
||||
|
||||
START_TEST(AddComplexTypeWithInheritance) {
|
||||
/* add a variable node to the address space */
|
||||
UA_ObjectAttributes attr;
|
||||
UA_ObjectAttributes_init(&attr);
|
||||
attr.description = UA_LOCALIZEDTEXT("en_US","fakeServerStruct");
|
||||
attr.displayName = UA_LOCALIZEDTEXT("en_US","fakeServerStruct");
|
||||
/* add a variable node to the address space */
|
||||
UA_ObjectAttributes attr = UA_ObjectAttributes_default;
|
||||
attr.description = UA_LOCALIZEDTEXT("en_US","fakeServerStruct");
|
||||
attr.displayName = UA_LOCALIZEDTEXT("en_US","fakeServerStruct");
|
||||
|
||||
UA_NodeId myObjectNodeId = UA_NODEID_STRING(1, "the.fake.Server.Struct");
|
||||
UA_QualifiedName myObjectName = UA_QUALIFIEDNAME(1, "the.fake.Server.Struct");
|
||||
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
|
||||
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
|
||||
UA_Int32 handleCalled = 0;
|
||||
UA_InstantiationCallback iCallback = {.method=instantiationMethod, .handle = (void *) &handleCalled};
|
||||
|
||||
UA_StatusCode res = UA_Server_addObjectNode(server, myObjectNodeId, parentNodeId, parentReferenceNodeId,
|
||||
myObjectName, UA_NODEID_NUMERIC(0, 2004),
|
||||
attr, &iCallback, NULL);
|
||||
ck_assert_int_eq(UA_STATUSCODE_GOOD, res);
|
||||
ck_assert_int_gt(handleCalled, 0); // Should be 58, but may depend on NS0 XML detail
|
||||
UA_NodeId myObjectNodeId = UA_NODEID_STRING(1, "the.fake.Server.Struct");
|
||||
UA_QualifiedName myObjectName = UA_QUALIFIEDNAME(1, "the.fake.Server.Struct");
|
||||
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
|
||||
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
|
||||
UA_StatusCode res =
|
||||
UA_Server_addObjectNode(server, myObjectNodeId, parentNodeId,
|
||||
parentReferenceNodeId, myObjectName,
|
||||
UA_NODEID_NUMERIC(0, 2004), attr,
|
||||
&handleCalled, NULL);
|
||||
ck_assert_int_eq(UA_STATUSCODE_GOOD, res);
|
||||
ck_assert_int_gt(handleCalled, 0); // Should be 58, but may depend on NS0 XML detail
|
||||
} END_TEST
|
||||
|
||||
START_TEST(AddNodeTwiceGivesError) {
|
||||
/* add a variable node to the address space */
|
||||
UA_VariableAttributes attr;
|
||||
UA_VariableAttributes_init(&attr);
|
||||
UA_VariableAttributes attr = UA_VariableAttributes_default;
|
||||
UA_Int32 myInteger = 42;
|
||||
UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
|
||||
attr.description = UA_LOCALIZEDTEXT("en_US","the answer");
|
||||
@ -87,45 +94,58 @@ START_TEST(AddNodeTwiceGivesError) {
|
||||
UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
|
||||
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
|
||||
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
|
||||
UA_StatusCode res = UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId, parentReferenceNodeId,
|
||||
myIntegerName, UA_NODEID_NULL, attr, NULL, NULL);
|
||||
UA_StatusCode res =
|
||||
UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
|
||||
parentReferenceNodeId, myIntegerName,
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
|
||||
attr, NULL, NULL);
|
||||
ck_assert_int_eq(UA_STATUSCODE_GOOD, res);
|
||||
res = UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId, parentReferenceNodeId,
|
||||
myIntegerName, UA_NODEID_NULL, attr, NULL, NULL);
|
||||
res = UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
|
||||
parentReferenceNodeId, myIntegerName,
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
|
||||
attr, NULL, NULL);
|
||||
ck_assert_int_eq(res, UA_STATUSCODE_BADNODEIDEXISTS);
|
||||
} END_TEST
|
||||
|
||||
static UA_Boolean constructorCalled = false;
|
||||
|
||||
static void * objectConstructor(const UA_NodeId instance) {
|
||||
static UA_StatusCode
|
||||
objectConstructor(UA_Server *server_,
|
||||
const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_NodeId *typeId, void *typeContext,
|
||||
const UA_NodeId *nodeId, void **nodeContext) {
|
||||
constructorCalled = true;
|
||||
return NULL;
|
||||
return UA_STATUSCODE_GOOD;
|
||||
}
|
||||
|
||||
START_TEST(AddObjectWithConstructor) {
|
||||
/* Add an object type */
|
||||
UA_NodeId objecttypeid = UA_NODEID_NUMERIC(0, 13371337);
|
||||
UA_ObjectTypeAttributes attr;
|
||||
UA_ObjectTypeAttributes_init(&attr);
|
||||
UA_ObjectTypeAttributes attr = UA_ObjectTypeAttributes_default;
|
||||
attr.displayName = UA_LOCALIZEDTEXT("en_US","my objecttype");
|
||||
UA_StatusCode res = UA_Server_addObjectTypeNode(server, objecttypeid,
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
|
||||
UA_QUALIFIEDNAME(0, "myobjecttype"), attr, NULL, NULL);
|
||||
UA_StatusCode res =
|
||||
UA_Server_addObjectTypeNode(server, objecttypeid,
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
|
||||
UA_QUALIFIEDNAME(0, "myobjecttype"), attr,
|
||||
NULL, NULL);
|
||||
ck_assert_int_eq(res, UA_STATUSCODE_GOOD);
|
||||
|
||||
/* Add a constructor to the object type */
|
||||
UA_ObjectLifecycleManagement olm = {objectConstructor, NULL};
|
||||
res = UA_Server_setObjectTypeNode_lifecycleManagement(server, objecttypeid, olm);
|
||||
UA_NodeTypeLifecycle lifecycle;
|
||||
lifecycle.constructor = objectConstructor;
|
||||
lifecycle.destructor = NULL;
|
||||
res = UA_Server_setNodeTypeLifecycle(server, objecttypeid, lifecycle);
|
||||
ck_assert_int_eq(res, UA_STATUSCODE_GOOD);
|
||||
|
||||
/* Add an object of the type */
|
||||
UA_ObjectAttributes attr2;
|
||||
UA_ObjectAttributes_init(&attr2);
|
||||
UA_ObjectAttributes attr2 = UA_ObjectAttributes_default;
|
||||
attr2.displayName = UA_LOCALIZEDTEXT("en_US","my object");
|
||||
res = UA_Server_addObjectNode(server, UA_NODEID_NULL, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(0, ""),
|
||||
objecttypeid, attr2, NULL, NULL);
|
||||
res = UA_Server_addObjectNode(server, UA_NODEID_NULL,
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
|
||||
UA_QUALIFIEDNAME(0, ""), objecttypeid,
|
||||
attr2, NULL, NULL);
|
||||
ck_assert_int_eq(res, UA_STATUSCODE_GOOD);
|
||||
|
||||
/* Verify that the constructor was called */
|
||||
@ -134,35 +154,42 @@ START_TEST(AddObjectWithConstructor) {
|
||||
|
||||
static UA_Boolean destructorCalled = false;
|
||||
|
||||
static void objectDestructor(const UA_NodeId instance, void *handle) {
|
||||
static void
|
||||
objectDestructor(UA_Server *server_,
|
||||
const UA_NodeId *sessionId, void *sessionContext,
|
||||
const UA_NodeId *typeId, void *typeContext,
|
||||
const UA_NodeId *nodeId, void **nodeContext) {
|
||||
destructorCalled = true;
|
||||
}
|
||||
|
||||
START_TEST(DeleteObjectWithDestructor) {
|
||||
/* Add an object type */
|
||||
UA_NodeId objecttypeid = UA_NODEID_NUMERIC(0, 13371337);
|
||||
UA_ObjectTypeAttributes attr;
|
||||
UA_ObjectTypeAttributes_init(&attr);
|
||||
UA_ObjectTypeAttributes attr = UA_ObjectTypeAttributes_default;
|
||||
attr.displayName = UA_LOCALIZEDTEXT("en_US","my objecttype");
|
||||
UA_StatusCode res = UA_Server_addObjectTypeNode(server, objecttypeid,
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
|
||||
UA_QUALIFIEDNAME(0, "myobjecttype"), attr, NULL, NULL);
|
||||
UA_StatusCode res =
|
||||
UA_Server_addObjectTypeNode(server, objecttypeid,
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
|
||||
UA_QUALIFIEDNAME(0, "myobjecttype"), attr, NULL, NULL);
|
||||
ck_assert_int_eq(res, UA_STATUSCODE_GOOD);
|
||||
|
||||
/* Add a constructor to the object type */
|
||||
UA_ObjectLifecycleManagement olm = {NULL, objectDestructor};
|
||||
res = UA_Server_setObjectTypeNode_lifecycleManagement(server, objecttypeid, olm);
|
||||
UA_NodeTypeLifecycle lifecycle;
|
||||
lifecycle.constructor = NULL;
|
||||
lifecycle.destructor = objectDestructor;
|
||||
res = UA_Server_setNodeTypeLifecycle(server, objecttypeid, lifecycle);
|
||||
ck_assert_int_eq(res, UA_STATUSCODE_GOOD);
|
||||
|
||||
/* Add an object of the type */
|
||||
UA_NodeId objectid = UA_NODEID_NUMERIC(0, 23372337);
|
||||
UA_ObjectAttributes attr2;
|
||||
UA_ObjectAttributes_init(&attr2);
|
||||
UA_ObjectAttributes attr2 = UA_ObjectAttributes_default;
|
||||
attr2.displayName = UA_LOCALIZEDTEXT("en_US","my object");
|
||||
res = UA_Server_addObjectNode(server, objectid, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(0, ""),
|
||||
objecttypeid, attr2, NULL, NULL);
|
||||
res = UA_Server_addObjectNode(server, objectid,
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
|
||||
UA_QUALIFIEDNAME(0, ""), objecttypeid,
|
||||
attr2, NULL, NULL);
|
||||
ck_assert_int_eq(res, UA_STATUSCODE_GOOD);
|
||||
|
||||
/* Delete the object */
|
||||
@ -174,14 +201,16 @@ START_TEST(DeleteObjectWithDestructor) {
|
||||
|
||||
START_TEST(DeleteObjectAndReferences) {
|
||||
/* Add an object of the type */
|
||||
UA_ObjectAttributes attr;
|
||||
UA_ObjectAttributes_init(&attr);
|
||||
UA_ObjectAttributes attr = UA_ObjectAttributes_default;
|
||||
attr.displayName = UA_LOCALIZEDTEXT("en_US","my object");
|
||||
UA_NodeId objectid = UA_NODEID_NUMERIC(0, 23372337);
|
||||
UA_StatusCode res;
|
||||
res = UA_Server_addObjectNode(server, objectid, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(0, ""),
|
||||
UA_NODEID_NULL, attr, NULL, NULL);
|
||||
res = UA_Server_addObjectNode(server, objectid,
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
|
||||
UA_QUALIFIEDNAME(0, ""),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
|
||||
attr, NULL, NULL);
|
||||
ck_assert_int_eq(res, UA_STATUSCODE_GOOD);
|
||||
|
||||
/* Verify that we have a reference to the node from the objects folder */
|
||||
@ -216,12 +245,15 @@ START_TEST(DeleteObjectAndReferences) {
|
||||
UA_BrowseResult_deleteMembers(&br);
|
||||
|
||||
/* Add an object the second time */
|
||||
UA_ObjectAttributes_init(&attr);
|
||||
attr = UA_ObjectAttributes_default;
|
||||
attr.displayName = UA_LOCALIZEDTEXT("en_US","my object");
|
||||
objectid = UA_NODEID_NUMERIC(0, 23372337);
|
||||
res = UA_Server_addObjectNode(server, objectid, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(0, ""),
|
||||
UA_NODEID_NULL, attr, NULL, NULL);
|
||||
res = UA_Server_addObjectNode(server, objectid,
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
|
||||
UA_QUALIFIEDNAME(0, ""),
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
|
||||
attr, NULL, NULL);
|
||||
ck_assert_int_eq(res, UA_STATUSCODE_GOOD);
|
||||
|
||||
/* Browse again, this time we expect that a single reference to the node is found */
|
||||
@ -246,8 +278,7 @@ START_TEST(InstantiateObjectType) {
|
||||
|
||||
/* Define the object type for "Device" */
|
||||
UA_NodeId deviceTypeId; /* get the nodeid assigned by the server */
|
||||
UA_ObjectTypeAttributes dtAttr;
|
||||
UA_ObjectTypeAttributes_init(&dtAttr);
|
||||
UA_ObjectTypeAttributes dtAttr = UA_ObjectTypeAttributes_default;
|
||||
dtAttr.displayName = UA_LOCALIZEDTEXT("en_US", "DeviceType");
|
||||
retval = UA_Server_addObjectTypeNode(server, UA_NODEID_NULL,
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
|
||||
@ -256,14 +287,14 @@ START_TEST(InstantiateObjectType) {
|
||||
NULL, &deviceTypeId);
|
||||
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
|
||||
|
||||
UA_VariableAttributes mnAttr;
|
||||
UA_VariableAttributes_init(&mnAttr);
|
||||
UA_VariableAttributes mnAttr = UA_VariableAttributes_default;
|
||||
mnAttr.displayName = UA_LOCALIZEDTEXT("en_US", "ManufacturerName");
|
||||
UA_NodeId manufacturerNameId;
|
||||
retval = UA_Server_addVariableNode(server, UA_NODEID_NULL, deviceTypeId,
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
|
||||
UA_QUALIFIEDNAME(1, "ManufacturerName"),
|
||||
UA_NODEID_NULL, mnAttr, NULL, &manufacturerNameId);
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
|
||||
mnAttr, NULL, &manufacturerNameId);
|
||||
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
|
||||
/* Make the manufacturer name mandatory */
|
||||
retval = UA_Server_addReference(server, manufacturerNameId,
|
||||
@ -271,34 +302,33 @@ START_TEST(InstantiateObjectType) {
|
||||
UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY), true);
|
||||
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
|
||||
|
||||
UA_VariableAttributes modelAttr;
|
||||
UA_VariableAttributes_init(&modelAttr);
|
||||
UA_VariableAttributes modelAttr = UA_VariableAttributes_default;
|
||||
modelAttr.displayName = UA_LOCALIZEDTEXT("en_US", "ModelName");
|
||||
retval = UA_Server_addVariableNode(server, UA_NODEID_NULL, deviceTypeId,
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
|
||||
UA_QUALIFIEDNAME(1, "ModelName"),
|
||||
UA_NODEID_NULL, modelAttr, NULL, NULL);
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
|
||||
modelAttr, NULL, NULL);
|
||||
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
|
||||
|
||||
/* Define the object type for "Pump" */
|
||||
UA_ObjectTypeAttributes ptAttr;
|
||||
UA_ObjectTypeAttributes_init(&ptAttr);
|
||||
UA_ObjectTypeAttributes ptAttr = UA_ObjectTypeAttributes_default;
|
||||
ptAttr.displayName = UA_LOCALIZEDTEXT("en_US", "PumpType");
|
||||
retval = UA_Server_addObjectTypeNode(server, pumpTypeId,
|
||||
deviceTypeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
|
||||
retval = UA_Server_addObjectTypeNode(server, pumpTypeId, deviceTypeId,
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
|
||||
UA_QUALIFIEDNAME(1, "PumpType"), ptAttr,
|
||||
NULL, NULL);
|
||||
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
|
||||
|
||||
UA_VariableAttributes statusAttr;
|
||||
UA_VariableAttributes_init(&statusAttr);
|
||||
UA_VariableAttributes statusAttr = UA_VariableAttributes_default;
|
||||
statusAttr.displayName = UA_LOCALIZEDTEXT("en_US", "Status");
|
||||
statusAttr.valueRank = -1;
|
||||
UA_NodeId statusId;
|
||||
retval = UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpTypeId,
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
|
||||
UA_QUALIFIEDNAME(1, "Status"),
|
||||
UA_NODEID_NULL, statusAttr, NULL, &statusId);
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
|
||||
statusAttr, NULL, &statusId);
|
||||
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
|
||||
|
||||
/* Make the status variable mandatory */
|
||||
@ -307,19 +337,18 @@ START_TEST(InstantiateObjectType) {
|
||||
UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY), true);
|
||||
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
|
||||
|
||||
UA_VariableAttributes rpmAttr;
|
||||
UA_VariableAttributes_init(&rpmAttr);
|
||||
UA_VariableAttributes rpmAttr = UA_VariableAttributes_default;
|
||||
rpmAttr.displayName = UA_LOCALIZEDTEXT("en_US", "MotorRPM");
|
||||
rpmAttr.valueRank = -1;
|
||||
retval = UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpTypeId,
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
|
||||
UA_QUALIFIEDNAME(1, "MotorRPMs"),
|
||||
UA_NODEID_NULL, rpmAttr, NULL, NULL);
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
|
||||
rpmAttr, NULL, NULL);
|
||||
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
|
||||
|
||||
/* Instantiate the variable */
|
||||
UA_ObjectAttributes oAttr;
|
||||
UA_ObjectAttributes_init(&oAttr);
|
||||
UA_ObjectAttributes oAttr = UA_ObjectAttributes_default;
|
||||
oAttr.displayName = UA_LOCALIZEDTEXT("en_US", "MyPump");
|
||||
retval = UA_Server_addObjectNode(server, UA_NODEID_NULL,
|
||||
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
||||
|
@ -5,7 +5,7 @@
|
||||
#ifndef TESTING_NETWORKLAYERS_H_
|
||||
#define TESTING_NETWORKLAYERS_H_
|
||||
|
||||
#include "ua_server.h"
|
||||
#include "ua_server_config.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
Loading…
Reference in New Issue
Block a user