addDataTypeNode(server, "Enumeration", UA_NS0ID_ENUMERATION, true, UA_NS0ID_BASEDATATYPE);
addDataTypeNode(server, "ServerState", UA_NS0ID_SERVERSTATE, false, UA_NS0ID_ENUMERATION);UA_LocalizedText localized_name = UA_LOCALIZEDTEXT_ALLOC ("en_US", enum_name);
UA_NodeId object_type_id = UA_NODEID_NUMERIC (0, m_serial_id++);
UA_DataTypeAttributes eAttr;
UA_DataTypeAttributes_init (&eAttr);
eAttr.description = localized_name;
eAttr.displayName = localized_name;
auto ret = UA_Server_addDataTypeNode(m_server,
object_type_id,
UA_NODEID_NUMERIC (0, UA_NS0ID_ENUMERATION),
UA_NODEID_NUMERIC (0, UA_NS0ID_ORGANIZES),
UA_QUALIFIEDNAME_ALLOC (1, enum_name),
eAttr, nullptr, nullptr);
#include <signal.h>
#ifdef UA_NO_AMALGAMATION
#include "ua_types.h"
#include "ua_server.h"
#include "ua_config_standard.h"
#include "ua_network_tcp.h"
#include "ua_log_stdout.h"
#else
#include "open62541.h"
#endif
UA_Boolean running = true;
UA_Logger logger = UA_Log_Stdout;
static void stopHandler(int sign) {
UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "received ctrl-c");
running = false;
}
static UA_StatusCode read_builtin (void * handle, const UA_NodeId id, UA_Boolean source_timestamp,
const UA_NumericRange * range, UA_DataValue * data_value) {
if (range) {
data_value->hasStatus = true;
data_value->status = UA_STATUSCODE_BADINDEXRANGEINVALID;
return UA_STATUSCODE_GOOD;
}
data_value->hasValue = true;
UA_Variant_setScalarCopy (&data_value->value, handle,
&UA_TYPES[UA_TYPES_INT32]);
if (source_timestamp) {
data_value->hasSourceTimestamp = true;
data_value->sourceTimestamp = UA_DateTime_now ();
}
return UA_STATUSCODE_GOOD;
}
static UA_StatusCode write_builtin (void * handle, const UA_NodeId id, const UA_Variant * data,
const UA_NumericRange * r) {
if (UA_Variant_isScalar (data) && data->type == &UA_TYPES[UA_TYPES_INT32] && data->data != NULL) {
* (int32_t*) (handle) = * (int32_t *) (data->data);
}
return UA_STATUSCODE_GOOD;
}
int main(int argc, char** argv) {
signal(SIGINT, stopHandler); /* catches ctrl-c */
UA_ServerConfig config = UA_ServerConfig_standard;
UA_ServerNetworkLayer nl;
nl = UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 16664);
config.networkLayers = &nl;
config.networkLayersSize = 1;
UA_Server *server = UA_Server_new(config);
uint32_t node_id_serial = 2000;
// Register enum Datatype with 'base type' Enumeration
UA_DataTypeAttributes attr;
UA_DataTypeAttributes_init (&attr);
attr.displayName = UA_LOCALIZEDTEXT("en_US", "MyEnum");
attr.description = UA_LOCALIZEDTEXT("en_US", "A custom enum type");
UA_NodeId nodeid = UA_NODEID_NUMERIC (1, node_id_serial++);
UA_NodeId parentNodeId = UA_NODEID_NUMERIC (0, UA_NS0ID_ENUMERATION);
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC (0, UA_NS0ID_HASSUBTYPE);
UA_QualifiedName nodeName = UA_QUALIFIEDNAME (1, "MyEnum");
UA_Server_addDataTypeNode (server, nodeid, parentNodeId, parentReferenceNodeId,
nodeName, attr, NULL, NULL);
// Register enum strings property
UA_VariableAttributes property_arr_string;
UA_VariableAttributes_init (&property_arr_string);
property_arr_string.displayName = UA_LOCALIZEDTEXT ("en_US", "StringValues");
property_arr_string.description = UA_LOCALIZEDTEXT ("en_US", "string value array");
property_arr_string.accessLevel = 3;
property_arr_string.userAccessLevel = 3;
property_arr_string.valueRank = 1;
property_arr_string.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
property_arr_string.arrayDimensions = UA_Array_new (2u, &UA_TYPES[UA_TYPES_STRING]);
UA_String * strings = UA_Array_new (2u, &UA_TYPES[UA_TYPES_STRING]);
strings[0] = UA_STRING("ONE");
strings[1] = UA_STRING("TWO");
// Enum string array
UA_Variant_setArray (&property_arr_string.value, strings, 2u, &UA_TYPES[UA_TYPES_STRING]);
UA_NodeId typeDefinition = UA_NODEID_NUMERIC (0, UA_NS0ID_PROPERTYTYPE);
parentReferenceNodeId = UA_NODEID_NUMERIC (0, UA_NS0ID_HASPROPERTY);
UA_NodeId str_nodeid = UA_NODEID_NUMERIC (1, node_id_serial++);
UA_QualifiedName qualified_node_name = UA_QUALIFIEDNAME (1, "EnumStrings");
uint32_t ret = UA_Server_addVariableNode (server, str_nodeid, nodeid, parentReferenceNodeId,
qualified_node_name, typeDefinition, property_arr_string,
NULL, NULL);
if (ret != UA_STATUSCODE_GOOD) {
printf("ERROR: %s", UA_StatusCode_description (ret)->explanation);
return -1;
}
int32_t myEnumVar = 1;
// Instantiate enum variable as datasource
UA_LocalizedText localized_name = UA_LOCALIZEDTEXT ("en_US", "MyEnumVar");
UA_NodeId var_node_id = UA_NODEID_NUMERIC (1, node_id_serial++);
UA_VariableAttributes vAttr;
UA_VariableAttributes_init (&vAttr);
UA_LocalizedText localized_descr;
localized_descr = UA_LOCALIZEDTEXT ("en_US", "This is a variable of type MyEnumVar");
vAttr.displayName = localized_name;
vAttr.description = localized_descr;
vAttr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
vAttr.userAccessLevel = 3;
vAttr.valueRank = -1;
typeDefinition = nodeid;
parentReferenceNodeId = UA_NODEID_NUMERIC (0, UA_NS0ID_HASCOMPONENT);
nodeName = UA_QUALIFIEDNAME (1, "MyEnumVar");
UA_DataSource datasource;
datasource.handle = &myEnumVar;
datasource.read = read_builtin;
datasource.write = write_builtin;
ret = UA_Server_addDataSourceVariableNode (server, var_node_id, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
parentReferenceNodeId, nodeName,
typeDefinition, vAttr, datasource, NULL);
if (ret != UA_STATUSCODE_GOOD) {
printf ("Failed to add variable node: %s\n",
UA_StatusCode_description (ret)->explanation);
return -1;
}
UA_StatusCode retval = UA_Server_run(server, &running);
UA_Server_delete(server);
nl.deleteMembers(&nl);
return 0;
}
This is working just fine, in UAExpert I can see 'MyEnum' as a subtype of 'Enumeration' with the correct property.
But when i want to instantiate a variable of type 'MyEnum' (as a DataSourceVariableNode) , i get the error message:
The type definition node id does not reference an appropriate type node
enum class MyEnum { type_a, type_b};
struct a {
MyEnum t;
int32_t i;
};Thank you. If I change the variable type to MultiStateDiscreteType it works, but
creating a 'MyEnum' subtype of MultiStateDiscreteType does not work?
First, let me point out my intention: I have a C++ software that organizes many of
its modules and parameters in structs and arrays of structs. I want to use OPC UAfor 3 things:1. run a server that automatically creates an OPC model at runtime, which maps
my modules and parameters to OPC UA types, so I can browse them and modifytheir values if needed. At startup, a daemon would look into all registered modules
and parameters etc, and then generate a server from this information.
As far as I understand from looking at an earlier discussion you had with Julius
(https://groups.google.com/forum/#!topic/open62541/AH8K9tOkBZs) and Julius
first answer in this thread, it is not possible to create a custom datatype (modeling
a C struct) at runtime.
There seems to be some support for custom datatypes, but it is not possible to
only define a datatype for the server and have the client browse it, right?
So I thought I have to model all my C structs as object types and ran into the
enum issue. I thought when having a struct
enum class MyEnum { type_a, type_b};
struct a {
MyEnum t;
int32_t i;
};I would be able to generate a new type 'MyEnum' subtyping the OPC UA type Enumeration.
Regarding goal 2: The 'OPC UA Information Model for IEC 61131-3' describes
the Mapping of structured data types in section 5.2.3.4. For clients that do notsupport complex datatypes,