finish nodeContext pointer for every node

This commit is contained in:
Julius Pfrommer 2017-08-07 12:32:15 +02:00 committed by Julius Pfrommer
parent 2113ec1c9b
commit 394df3be8d
29 changed files with 1499 additions and 1389 deletions

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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 */

View File

@ -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. */

View File

@ -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. */

View File

@ -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);

View File

@ -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;

View File

@ -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,

View File

@ -46,7 +46,7 @@ struct UA_ServerConfig {
/* Custom DataTypes */
size_t customDataTypesSize;
const UA_DataType *customDataTypes;
UA_DataType *customDataTypes;
/* Networking */
size_t networkLayersSize;

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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"

View File

@ -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;
}

View File

@ -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

View File

@ -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) {

View File

@ -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
/**********************/

View File

@ -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, &currentDS,
&UA_TYPES[UA_TYPES_DATETIME].typeId, &currentDS, 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 */

View File

@ -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;

View File

@ -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

View File

@ -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(&current->session.sessionId, sessionId))
continue;
/* Session has timed out */
if(UA_DateTime_nowMonotonic() > current->session.validTill) {
UA_LOG_INFO_SESSION(sm->server->config.logger, &current->session,
"Client tries to use a session that has timed out");
return NULL;
}
/* Ok, return */
return &current->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,

View File

@ -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"

View File

@ -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);
}

View File

@ -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;

View File

@ -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

View File

@ -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),

View File

@ -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" {