High Performance OPC UA Server SDK  1.1.1.177
custom_provider_nodes.c
/*****************************************************************************
* *
* Copyright (c) 2006-2016 Unified Automation GmbH. All rights reserved. *
* *
* Software License Agreement ("SLA") Version 2.6 *
* *
* Unless explicitly acquired and licensed from Licensor under another *
* license, the contents of this file are subject to the Software License *
* Agreement ("SLA") Version 2.6, or subsequent versions as allowed by the *
* SLA, and You may not copy or use this file in either source code or *
* executable form, except in compliance with the terms and conditions of *
* the SLA. *
* *
* All software distributed under the SLA is provided strictly on an "AS *
* IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, *
* AND LICENSOR HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT *
* LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR *
* PURPOSE, QUIET ENJOYMENT, OR NON-INFRINGEMENT. See the SLA for specific *
* language governing rights and limitations under the SLA. *
* *
* The complete license agreement can be found here: *
* http://unifiedautomation.com/License/SLA/2.6/ *
* *
*****************************************************************************/
#include <memory/memory.h>
#include <uabase/statuscodes.h>
#include <uabase/nodeid.h>
#include <uabase/variant.h>
#include <uabase/accesslevel.h>
#include <uabase/valuerank.h>
#include <uabase/structure/nodeclass.h>
#include <uabase/structure/range.h>
#include <uabase/identifiers.h>
#include <uabase/qualifiedname.h>
#include <uabase/structure/argument.h>
#include <uaserver/addressspace/addressspace.h>
#include <uaserver/addressspace/object.h>
#include <uaserver/addressspace/variable.h>
#include "custom_provider.h"
#include "custom_provider_nodes.h"
#include "custom_provider_identifiers.h"
#include "custom_provider_store.h"
/*
* @brief Create a property node EURange.
* @param parent Node to add the property to.
* @param identifier Numeric identifier for the property.
* @param low Initial minimum value of the range.
* @param high Initial maximum value of the range.
* @return 0 on success or errorcode on failure.
*/
static int custom_provider_create_eurange(ua_node_t parent, uint32_t identifier, double low, double high)
{
struct ua_nodeid datatype, nodeid;
struct ua_range *eurange = NULL;
struct ua_variant property;
ua_node_t node;
int ret;
unsigned int valueidx;
ua_nodeid_set_numeric(&nodeid, g_custom_provider_nsidx, identifier);
/* create a node from class variable with mandatory attributes */
&nodeid, /* nodeid for the new node */
UA_NODECLASS_VARIABLE, /* nodeclass of the new node */
0, /* ns index for browsename is namespace zero */
"EURange", /* browsename */
NULL, /* displayname, NULL for same as browsename */
UA_NODE_PROPERTYTYPE, /* typedefinition is propertytype */
parent, /* parent node of the new node */
UA_NODE_HASPROPERTY); /* new node is referenced with hasproperty by parent */
if (node == UA_NODE_INVALID) goto out;
ua_nodeid_set_numeric(&datatype, 0, UA_ID_RANGE);
/* set mandatory attributes for nodeclass variable */
if (ret != 0) goto out;
/* allocate memory for ua_range struct */
eurange = IPC_ALLOC(eurange);
if (eurange == NULL) return UA_EBADNOMEM;
/* set minimum/maximum of the range */
eurange->low = low;
eurange->high = high;
/* attach range to an ua_variant */
ret = ua_variant_attach_extensionobject(&property, eurange, &datatype);
if (ret != 0) goto out;
/* attach the ua_variant to the memorystore */
ret = ua_memorystore_attach_new_value(&g_memorystore, &property, node);
if (ret != 0) goto out;
/* update value store with range info*/
ret = ua_variable_get_value_index(parent, &valueidx);
if (ret != 0) goto out;
ret = custom_store_set_range(valueidx, (uint32_t)low, (uint32_t)high);
if (ret != 0) goto out;
return 0;
out:
ipc_free(eurange);
return -1;
}
/*
* @brief Create a new variable node representing a value of the device.
* @param identifier Numeric identifier for the node.
* @param parent Node to add the new node to.
* @param name Name of the new node, used as browsename and displayname.
* @param valueidx Index in the value array of the device.
* @param initial_value Initial value.
* @return New node on success or UA_NODE_INVALID on failure.
*/
static ua_node_t custom_provider_create_variable(uint32_t identifier, ua_node_t parent, const char *name, unsigned int valueidx, uint32_t initial_value)
{
struct ua_nodeid datatype, nodeid;
ua_node_t node;
int ret;
ua_nodeid_set_numeric(&nodeid, g_custom_provider_nsidx, identifier);
/* create a node from class variable with mandatory attributes */
&nodeid, /* nodeid for the new node */
UA_NODECLASS_VARIABLE, /* nodeclass of the new node */
nodeid.nsindex, /* ns index for browsename is same as for nodeid */
name, /* browsename */
NULL, /* displayname, NULL for same as browsename */
UA_NODE_BASEDATAVARIABLETYPE, /* typedefinition is basedatavariabletype */
parent, /* parent node of the new node */
UA_NODE_ORGANIZES); /* new node is referenced with organizes by parent */
if (node == UA_NODE_INVALID) return UA_NODE_INVALID;
ua_nodeid_set_numeric(&datatype, 0, UA_ID_UINT32);
/* set mandatory attributes for nodeclass variable */
node, /* newly created node to set attributes to */
&datatype, /* datatype is uint32 */
UA_VALUERANK_SCALAR, /* valuerank: scalar */
UA_ACCESSLEVEL_CURRENTREAD | UA_ACCESSLEVEL_CURRENTWRITE, /* allow read and write for the value */
false); /* historizing is not supported for this node */
if (ret != 0) return UA_NODE_INVALID;
/* set initial value */
ret = custom_store_set_initial_value(valueidx, initial_value);
if (ret != 0) return UA_NODE_INVALID;
/* write the storeindex to the node */
ret = ua_variable_set_store_index(node, g_custom_store_idx);
if (ret != 0) return UA_NODE_INVALID;
/* write the valueindex to the node */
ret = ua_variable_set_value_index(node, valueidx);
if (ret != 0) return UA_NODE_INVALID;
return node;
}
#ifdef ENABLE_SERVICE_CALL
static int custom_provider_init_method_arguments(void)
{
int ret;
struct ua_argument args[2];
struct ua_nodeid type_id;
struct ua_variant v;
ua_node_t input_argument_node, output_argument_node;
/* find input arguments node */
input_argument_node = ua_node_find_numeric(g_custom_provider_nsidx, MULTIPLY_INPUT_ARGUMENTS_ID);
if (input_argument_node == UA_NODE_INVALID) return -1;
/* create value of input arguments */
ua_nodeid_set_numeric(&type_id, 0, UA_ID_ARGUMENT);
ua_argument_init(&args[0]);
ua_string_attach_const(&args[0].name, "a");
ua_string_attach_const(&args[0].description.text, "factor a");
ua_string_attach_const(&args[0].description.locale, "en-US");
ua_nodeid_set_numeric(&args[0].data_type, 0, UA_VT_FLOAT);
args[0].value_rank = UA_VALUERANK_SCALAR;
ua_argument_init(&args[1]);
ua_string_attach_const(&args[1].name, "b");
ua_string_attach_const(&args[1].description.text, "factor b");
ua_string_attach_const(&args[1].description.locale, "en-US");
ua_nodeid_set_numeric(&args[1].data_type, 0, UA_VT_FLOAT);
args[1].value_rank = UA_VALUERANK_SCALAR;
ret = ua_variant_set_extensionobject_array(&v, args, 2, &type_id);
if (ret != 0)
return -1;
/* attach the value to the node */
ret = ua_memorystore_attach_new_value(&g_memorystore, &v, input_argument_node);
if (ret != 0) {
return -1;
}
/* find output arguments node */
output_argument_node = ua_node_find_numeric(g_custom_provider_nsidx, MULTIPLY_OUTPUT_ARGUMENTS_ID);
if (output_argument_node == UA_NODE_INVALID) return -1;
/* create value of output arguments */
ua_argument_init(&args[0]);
ua_string_attach_const(&args[0].name, "product");
ua_string_attach_const(&args[0].description.text, "product = a * b");
ua_string_attach_const(&args[0].description.locale, "en-US");
ua_nodeid_set_numeric(&args[0].data_type, 0, UA_VT_FLOAT);
args[0].value_rank = UA_VALUERANK_SCALAR;
ret = ua_variant_set_extensionobject_array(&v, args, 1, &type_id);
if (ret != 0)
return -1;
/* attach the value to the node */
ret = ua_memorystore_attach_new_value(&g_memorystore, &v, output_argument_node);
if (ret != 0) {
return -1;
}
return 0;
}
static int custom_provider_create_multiply_method(uint32_t identifier, const char *method_name, ua_node_t parent)
{
struct ua_nodeid nodeid, datatype_id;
ua_node_t method_node, input_argument_node, output_argument_node;
int ret;
ua_nodeid_set_numeric(&nodeid, g_custom_provider_nsidx, identifier);
&nodeid, /* nodeid for the new node */
UA_NODECLASS_METHOD, /* nodeclass of the new node */
nodeid.nsindex, /* ns index for browsename is same as nodeid */
method_name, /* browsename */
NULL, /* displayname, NULL for same as browsename */
UA_NODE_INVALID, /* the node has no typedefinition */
parent, /* parent node of the new node */
UA_NODE_HASCOMPONENT); /* new node is referenced with hascomponent by parent */
if (method_node == UA_NODE_INVALID) return -1;
/* input nodes */
ua_nodeid_set_numeric(&nodeid, g_custom_provider_nsidx, MULTIPLY_INPUT_ARGUMENTS_ID);
input_argument_node = ua_node_create_with_attributes(
&nodeid, /* nodeid for the new node */
UA_NODECLASS_VARIABLE, /* nodeclass of properties is variable */
0, /* ns index for browsename is namespace zero */
"InputArguments", /* browsename */
NULL, /* displayname, NULL is same as browsename */
UA_NODE_PROPERTYTYPE, /* the node has the typedefinition propertytype */
method_node, /* parent of the new node is the method node */
UA_NODE_HASPROPERTY); /* new node is referenced with hasproperty by parent */
if (input_argument_node == UA_NODE_INVALID) return -1;
ua_nodeid_set_numeric(&datatype_id, 0, UA_ID_ARGUMENT);
ret = ua_variable_set_attributes(input_argument_node, &datatype_id, UA_VALUERANK_ONEDIMENSION, UA_ACCESSLEVEL_CURRENTREAD, false);
if (ret != 0) return ret;
/* output node */
ua_nodeid_set_numeric(&nodeid, g_custom_provider_nsidx, MULTIPLY_OUTPUT_ARGUMENTS_ID);
output_argument_node = ua_node_create_with_attributes(
&nodeid, /* nodeid for the new node */
UA_NODECLASS_VARIABLE, /* nodeclass of properties is variable */
0, /* ns index for browsename is namespace zero */
"OutputArguments", /* browsename */
NULL, /* displayname, NULL is same as browsename */
UA_NODE_PROPERTYTYPE, /* the node has the typedefinition propertytype */
method_node, /* parent of the new node is the method node */
UA_NODE_HASPROPERTY); /* new node is referenced with hasproperty by parent */
if (output_argument_node == UA_NODE_INVALID) return -1;
ua_nodeid_set_numeric(&datatype_id, 0, UA_ID_ARGUMENT);
ret = ua_variable_set_attributes(output_argument_node, &datatype_id, UA_VALUERANK_ONEDIMENSION, UA_ACCESSLEVEL_CURRENTREAD, false);
if (ret != 0) return ret;
return ret;
}
#endif /* ENABLE_SERVICE_CALL */
static int custom_provider_create_object1(ua_node_t parent, uint32_t identifier, const char *nodename)
{
struct ua_nodeid nodeid;
ua_nodeid_set_numeric(&nodeid, g_custom_provider_nsidx, identifier);
/* create a node from class variable with mandatory attributes */
&nodeid, /* nodeid for the new node */
UA_NODECLASS_OBJECT, /* nodeclass of the new node */
nodeid.nsindex, /* ns index for browsename is same as for nodeid */
nodename, /* browsename */
NULL, /* displayname, NULL for same as browsename */
UA_NODE_BASEOBJECTTYPE, /* typedefinition is basedatavariabletype */
parent, /* parent node of the new node */
UA_NODE_ORGANIZES); /* new node is referenced with organizes by parent */
}
/* create nodes in the custom provider namespace */
int custom_provider_create_nodes(void)
{
ua_node_t folder, node;
struct ua_nodeid nodeid;
int ret;
/* create a folder for the new nodes */
ua_nodeid_set_numeric(&nodeid, g_custom_provider_nsidx, CUSTOM_NODES_ID);
folder = ua_object_create_folder(&nodeid, UA_NODE_OBJECTSFOLDER, "CustomNodes");
if (folder == UA_NODE_INVALID) return UA_EBAD;
/* create new variable node */
node = custom_provider_create_variable(CUSTOM_VARIABLE1_ID, folder, "Variable1", 0, 0);
if (node == UA_NODE_INVALID) return UA_EBAD;
/* create property eurange and add it to the new node */
ret = custom_provider_create_eurange(node, CUSTOM_EURANGE_VAR1_ID, 0.0, 50.0);
if (ret != 0) return ret;
node = custom_provider_create_variable(CUSTOM_VARIABLE2_ID, folder, "Variable2", 1, 5);
if (node == UA_NODE_INVALID) return UA_EBAD;
ret = custom_provider_create_eurange(node, CUSTOM_EURANG_VAR2_ID, 0.0, 100.0);
if (ret != 0) return ret;
node = custom_provider_create_variable(CUSTOM_VARIABLE3_ID, folder, "Variable3", 2, 93738);
if (node == UA_NODE_INVALID) return UA_EBAD;
ret = custom_provider_create_eurange(node, CUSTOM_EURANGE_VAR3_ID, 0.0, 100000.0);
if (ret != 0) return ret;
node = custom_provider_create_object1(folder, DEMO_OBJECT_ID, "Object1");
if (node == UA_NODE_INVALID) return UA_EBAD;
#ifdef ENABLE_SERVICE_CALL
ret = custom_provider_create_multiply_method(MULTIPLY_METHOD_ID, "MultiplyMethod", node);
if (ret != 0) return ret;
ret = custom_provider_init_method_arguments();
if (ret != 0) return ret;
#endif
return 0;
}