Set value for enumeration variable failed with 'DataType of the value is incompatible'

42 views
Skip to first unread message

Andreas Biel

unread,
Nov 14, 2025, 4:38:08 AMNov 14
to open62541
Hello,

I could not figure out how to set the value for an enumeration variable.
v1.4.12

I have successfully create my variable, but when I try to set the value I always run in an error. 
->
Writing the value of Node ns=5;i=819 failed with the following reason: DataType of the value is incompatible
WriteRequest returned status code BadTypeMismatch
?? DeviceHealth konnte nicht initialisiert werden: 0x80740000
<-
I use the DeviceHealthEnumeration from the DI namespace. I can see the variable 'DeviceHealth in UAExpert. Any ideas ?

Best Regards

Andreas

UA_NodeId addDeviceHealth(UA_Server *server,
                          UA_NodeId deviceNodeId,
                          UA_UInt16 nsIdx,
                          UA_DeviceHealthEnumeration initialValue) {
    if(!server || UA_NodeId_isNull(&deviceNodeId)) {
        printf("❌ Ungültige Parameter beim Hinzufügen von DeviceHealth\n");
        return UA_NODEID_NULL;
    }

    UA_NodeId deviceHealthEnumId = UA_NODEID_NUMERIC(nsDiIdx, 6244); // 6244 = DeviceHealthEnumeration in DI
    UA_VariableAttributes attr = UA_VariableAttributes_default;
    attr.displayName = UA_LOCALIZEDTEXT("en-US", "DeviceHealth");
    attr.valueRank   = UA_VALUERANK_SCALAR;
    attr.dataType = deviceHealthEnumId;
    //UA_Variant_setScalar(&attr.value, &initialValue,  &UA_TYPES_DI[UA_TYPES_DI_DEVICEHEALTHENUMERATION]);

    UA_NodeId deviceHealthNodeId = UA_NODEID_NUMERIC(nsIdx, getNextNodeIdForNamespace(nsIdx));

    UA_StatusCode rc = UA_Server_addVariableNode(server,
                                                 deviceHealthNodeId,
                                                 deviceNodeId,
                                                 UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                                                 UA_QUALIFIEDNAME(nsIdx, "DeviceHealth"),
                                                 UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
                                                 attr,
                                                 NULL,
                                                 &deviceHealthNodeId);

    if(rc != UA_STATUSCODE_GOOD) {
        printf("❌ DeviceHealth konnte nicht erstellt werden: 0x%08X\n", rc);
        return UA_NODEID_NULL;
    }
    UA_Int32 initValue = UA_DEVICEHEALTHENUMERATION_NORMAL; // oder FAILURE etc.
    UA_Variant val;
    //UA_Variant_setScalar(&val, &initValue, &UA_TYPES[UA_TYPES_INT32]);
    UA_Variant_setScalar(&val,UA_DEVICEHEALTHENUMERATION_NORMAL, &UA_TYPES_DI[UA_TYPES_DI_DEVICEHEALTHENUMERATION]);

 rc = UA_Server_writeValue(server, deviceHealthNodeId, val);
if(rc != UA_STATUSCODE_GOOD) {
    printf("⚠️ DeviceHealth konnte nicht initialisiert werden: 0x%08X\n", rc);
}
    return deviceHealthNodeId;
}

Julius Pfrommer

unread,
Nov 26, 2025, 6:42:25 AM (9 days ago) Nov 26
to open62541
Enumerations are transported as int32 values.
Just try to write it as int32 directly.

At the same time we are working to automatically get the TypeDefinition attribute for enum values. If that is evaluated by UAExpert, the v1.5 version is scheduled to have that feature.

Regards, Julius

Andreas Biel

unread,
Nov 27, 2025, 2:06:10 AM (8 days ago) Nov 27
to open62541
Hi Julius,

I have tryed it also as UA_Int32. See the line commented out. Also I tryed it for a enumeration value which I have defined myself. I got always a bad type error.

Is there maybe a sample code for this ?

Regards
Andreas

Marco Knösel

unread,
Dec 1, 2025, 2:31:17 PM (3 days ago) Dec 1
to open62541
Hi Julius and Andreas!

Andreas, I couldn't compile your example (some symbols seem to be missing), so I created a different one; hope this helps; please see below.
The enumeration is used as input value for a method. Tested with Open62541 version 1.4.14, Windows 11, UaExpert client.
Open questions:
  • I'm afraid that setting cleanup to false in my UA_DataTypeArray might lead to a memory leak, but setting it to true causes null-pointer dereferencing on server cleanup. What is the correct way of handling that?
  • Under Windows, are UA_LOCALIZEDTEXT_ALLOCUA_NODEID_STRING_ALLOC, etc. the appropriate replacements for  UA_LOCALIZEDTEXTUA_NODEID_STRING, ...? I'm asking due to implicit conversion from const char* to char* which does not compile with MSVC, being again afraid of memory leaks.
Regards,
Marco


#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "Iphlpapi.lib")

#include <open62541/server.h>
#include <open62541/plugin/log_stdout.h>
#include <vector>



/*
 * Create a fatal error log message if the UA_StatusCode passed to this function
 * is different from UA_STATUSCODE_GOOD.
 * In debug mode, the program will stop in that case.
 */
static void checkStatus(UA_StatusCode statusCode)
{
if (statusCode != UA_STATUSCODE_GOOD)
{
UA_LOG_FATAL(
UA_Log_Stdout,
UA_LOGCATEGORY_USERLAND,
"An error occured! Status code: %s",
UA_StatusCode_name(statusCode));
}

assert(statusCode == UA_STATUSCODE_GOOD);
}



/*
 * Create a new type and an object of that type.
 * Both are added to the server.
 */
static void createTypeAndObject(UA_Server* server)
{
UA_NodeId m_parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE);

const char typeName[] = "MachineStateType";

UA_NodeId typeNodeId = UA_NODEID_STRING_ALLOC(1, typeName);

UA_ObjectTypeAttributes attr = UA_ObjectTypeAttributes_default;
attr.displayName = UA_LOCALIZEDTEXT_ALLOC("en", typeName);

UA_StatusCode statusCode = UA_Server_addObjectTypeNode(
server,
typeNodeId,
m_parentNodeId,
UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
UA_QUALIFIEDNAME_ALLOC(1, typeName),
attr,
NULL,
NULL
);
checkStatus(statusCode);

const char objectName[] = "MachineState";

UA_ObjectAttributes attrObject = UA_ObjectAttributes_default;
attrObject.displayName = UA_LOCALIZEDTEXT_ALLOC("en", objectName);

statusCode = UA_Server_addObjectNode(
server,
UA_NODEID_STRING_ALLOC(1, objectName),
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
UA_QUALIFIEDNAME_ALLOC(1, objectName),
typeNodeId,
attrObject,
NULL,
NULL
);

checkStatus(statusCode);
}



/*
 * Create a new user-defined enumerator.
 *
 * At first, a data-type node ID is created, using the enumeration-type ID as parent-node ID.
 *
 * Then, a new UA_DataType is created and added to the server configuration, making use of the new data-type node ID.
 *
 * For the enumerator values, multiple instances of UA_EnumValueType are created
 * and stored as UA_VariableAttributes.
 *
 * Finally, a variable node is added, containing the abovementioned UA_EnumValueType instances.
 *
 */
static void createEnum(UA_Server* server) {

const char name[] = "MachineStateEnum";

UA_NodeId dataTypeNodeId = UA_NODEID_STRING_ALLOC(1, name);
UA_DataTypeAttributes dataTypeAttr = UA_DataTypeAttributes_default;
dataTypeAttr.displayName = UA_LOCALIZEDTEXT_ALLOC("en", "name");
UA_StatusCode statusCode = UA_Server_addDataTypeNode(
server,
dataTypeNodeId,
UA_TYPES[UA_TYPES_ENUMERATION].typeId,
UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
UA_QUALIFIEDNAME_ALLOC(1, name),
dataTypeAttr,
NULL,
NULL
);

checkStatus(statusCode);



UA_DataType* newEnumDataType = nullptr;
newEnumDataType = (UA_DataType*)UA_malloc(sizeof(UA_DataType));
*newEnumDataType = UA_TYPES[UA_TYPES_ENUMERATION];
newEnumDataType->typeId = dataTypeNodeId;
newEnumDataType->binaryEncodingId = dataTypeNodeId;
newEnumDataType->membersSize = 1;
newEnumDataType->pointerFree = true;
newEnumDataType->typeName = (char*)UA_malloc(strlen(name) + 1);
strcpy_s((char*)newEnumDataType->typeName, strlen(name) + 1, name);

UA_ServerConfig* serverConfig = UA_Server_getConfig(server);
const UA_DataTypeArray* m_prevCustomDataTypes = serverConfig->customDataTypes;

UA_DataTypeArray tempCustomDataTypes = {
m_prevCustomDataTypes,
1,
newEnumDataType,
false
};

UA_DataTypeArray* m_customDataTypeArray = (UA_DataTypeArray*)UA_malloc(sizeof(UA_DataTypeArray));
memcpy(m_customDataTypeArray, &tempCustomDataTypes, sizeof(UA_DataTypeArray));

serverConfig->customDataTypes = m_customDataTypeArray;



std::vector<const char*> enumEntries = { "on", "auto", "off" };
std::vector<UA_EnumValueType> enumValueObjects(enumEntries.size());
for (UA_Int32 i = 0; i < enumEntries.size(); i++)
{
UA_EnumValueType_init(&(enumValueObjects[i]));
enumValueObjects[i].value = i;
enumValueObjects[i].displayName = UA_LOCALIZEDTEXT_ALLOC("en", enumEntries[i]);
enumValueObjects[i].description = UA_LOCALIZEDTEXT_ALLOC("en", enumEntries[i]);
}

UA_VariableAttributes variableAttr = UA_VariableAttributes_default;
UA_Variant_setArray(&variableAttr.value, enumValueObjects.data(), enumValueObjects.size(), &UA_TYPES[UA_TYPES_ENUMVALUETYPE]);
UA_NodeId enumVariableNodeId = UA_NODEID_STRING_ALLOC(1, "MachineStateEnumValues");



statusCode = UA_Server_addVariableNode(
server,
enumVariableNodeId,
dataTypeNodeId,
UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
UA_QUALIFIEDNAME_ALLOC(0, "EnumValues"),
UA_NODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE),
variableAttr,
NULL,
NULL
);
checkStatus(statusCode);

statusCode = UA_Server_addReference(server,
enumVariableNodeId,
UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE),
UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY),
true);
checkStatus(statusCode);
}



/*
 * This function is invoked, when the OPC UA method "setMachineState" is called.
 *
 * It takes an enumerator value as input, which is converted to a UA_UInt32.
 * The function sets a UA_Int32 as output parameter and returns UA_STATUSCODE_GOOD.
 *
 * (The creation of "setMachineState" happens in a different function below.)
 */
static UA_StatusCode myCallbackFunction(
    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_UInt32 enumValue = ((UA_EnumValueType*)(input[0].data))->value;

UA_Int32 result = 42;
if (enumValue == 1) {
result = 4242;
}
else if (enumValue == 2) {
result = 424242;
}

UA_Variant_setScalarCopy(&(output[0]), &result , &UA_TYPES[UA_TYPES_INT32]);

    return UA_STATUSCODE_GOOD;
}



/*
 * Creation of OPC UA method "setMachineState".
 *
 * The method takes an enumerator value as input and gives a UA_Int32 as output.
 */
static void createMethod(UA_Server* server)
{
    std::vector<UA_Argument> inputArguments(1);
    UA_Argument_init(&(inputArguments[0]));
    inputArguments[0].description = UA_LOCALIZEDTEXT_ALLOC("en", "The state of the machine.");
    inputArguments[0].name = UA_STRING_ALLOC("newStateOfTheMachine");
inputArguments[0].dataType = UA_NODEID_STRING_ALLOC(1, "MachineStateEnum");
    inputArguments[0].valueRank = UA_VALUERANK_SCALAR;

    std::vector<UA_Argument> outputArguments(1);
    UA_Argument_init(&(outputArguments[0]));
    outputArguments[0].description = UA_LOCALIZEDTEXT_ALLOC("en", "The answer to life, the universe and everything.");
    outputArguments[0].name = UA_STRING_ALLOC("answerToEverything");
    outputArguments[0].dataType = UA_TYPES[UA_TYPES_INT32].typeId;
    outputArguments[0].valueRank = UA_VALUERANK_SCALAR;

    UA_MethodAttributes attr = UA_MethodAttributes_default;
    attr.description = UA_LOCALIZEDTEXT_ALLOC("en", "Set the state of the machine.");
    attr.displayName = UA_LOCALIZEDTEXT_ALLOC("en", "setMachineState");
    attr.executable = true;
    attr.userExecutable = true;
    UA_NodeId methodNodeId = UA_NODEID_STRING_ALLOC(1, "setMachineState");
    UA_StatusCode statusCode = UA_Server_addMethodNode(
        server,
        methodNodeId,
        UA_NODEID_STRING_ALLOC(1, "MachineState"),
        UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
        UA_QUALIFIEDNAME_ALLOC(1, "setMachineState"),
        attr,
myCallbackFunction,
        inputArguments.size(),
        inputArguments.data(),
        outputArguments.size(),
        outputArguments.data(),
        NULL,
        NULL
    );
    checkStatus(statusCode);

    statusCode = UA_Server_addReference(
        server,
        methodNodeId,
        UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE),
        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY),
        true
    );
    checkStatus(statusCode);
}



/*
 * The main function, which creates, runs and deletes the server. 
 *
 * To the server, an object with a single method is added,
 * making use of all the above-defined functions.
 *
 * The method takes a value of a user-defined enumerator as input
 * and gives a UA_Int32 back as output.
 */
int main(void)
{
UA_Server* server = UA_Server_new();

createTypeAndObject(server);
createEnum(server);
createMethod(server);

UA_Server_runUntilInterrupt(server);

UA_Server_delete(server);

return 0;
}

Andreas Biel

unread,
Dec 2, 2025, 6:59:15 AM (3 days ago) Dec 2
to open62541
Hello Marco,

after a  Deep Thought and a realy hot cup of tea , I thing this not exactly my problem. My problem was that I could not set the value for an node with a type of an enumeration.
But thankts to your example, I made a new attempt and have now success. The problem was that I had searched for the type in the wrong array UA_TYPES instaed of UA_TYPES_DI.

Here is a small sample, if someone tryes to use enumerations, from the standard enums.

Thanks for your help.



#include "open62541.h"

/*
#include <open62541/server.h>
#include <open62541/server_config_default.h>
#include <open62541/plugin/log_stdout.h>
*/

int main(void) {
    /* 1. Create server */
    UA_Server *server = UA_Server_new();
    UA_ServerConfig_setDefault(UA_Server_getConfig(server));

    /* 2. NodeId and name for the variable */
    UA_NodeId variableNodeId = UA_NODEID_STRING(1, "MyDiagnosticLevel");
    UA_QualifiedName variableName = UA_QUALIFIEDNAME(1, "MyDiagnosticLevel");

    /* 3. Prepare VariableAttributes */
    UA_VariableAttributes attr = UA_VariableAttributes_default;
    attr.displayName = UA_LOCALIZEDTEXT("en-US", "MyDiagnosticLevel");
    attr.description = UA_LOCALIZEDTEXT("en-US", "DiagnosticLevel variable");

    /* Typ: DiagnosticLevel (is an Enum from OPC UA Base Info Model) */
    attr.dataType = UA_TYPES[UA_TYPES_DIAGNOSTICSLEVEL].typeId;
    attr.valueRank = -1; /* Skalar */

    /* 4. Set initial value to  Info */
    UA_DiagnosticsLevel initValue = UA_DIAGNOSTICSLEVEL_INFO;
    UA_Variant_setScalar(&attr.value, &initValue, &UA_TYPES[UA_TYPES_DIAGNOSTICSLEVEL]);

    /* 5. Add VariableNode */
    UA_StatusCode retval = UA_Server_addVariableNode(
        server,
        variableNodeId,
        UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), /* Parent = ObjectsFolder */
        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),     /* Referenztyp */
        variableName,
        UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), /* Basistyp */
        attr,
        NULL, NULL);

    if(retval != UA_STATUSCODE_GOOD) {
        UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
                     "Failed to add DiagnosticLevel node");
        UA_Server_delete(server);
        return (int)retval;
    }
   
    /* Change value afterward to LOG */
    UA_DiagnosticsLevel newValue = UA_DIAGNOSTICSLEVEL_LOG;
    UA_Variant value;
    UA_Variant_setScalar(&value, &newValue, &UA_TYPES[UA_TYPES_DIAGNOSTICSLEVEL]);
    UA_Server_writeValue(server, variableNodeId, value);

    /* 6.Run server */
    UA_StatusCode runStatus = UA_Server_runUntilInterrupt(server);

    /* 7. Clean up*/
    UA_Server_delete(server);
    return (int)runStatus;
}

Reply all
Reply to author
Forward
0 new messages