open62541/examples/tutorial_server_alarms_conditions.c
Sameer AL-Qadasi ff87c2876d (feat): First Implementation of Alarms and Conditions (#2083)
* Update src/server/ua_subscription_events.c

* Alarms and Conditions First Implementation

implemented facets:
1- A&C Base Condition Server Facet (only mandatory)
2- A&C Enable Server Facet (only mandatory)
3- A&C Alarm Server Facet (only mandatory)
4- A&C Acknowledgeable Alarm Server Facet (complete, with ConfirmedState and Confirm Method)

Notes:
1) Change in ua_subscriptions_monitoreditem.c is related to issue #2114. Without that fix, the server crashes when generating OverflowEvents.

2) The Events: RefreshStart and RefreshEnd are abstract according to the specification. So to make it possible to create instances of them the fix in function "create_event()" is added (change abstract attribute of the EventType to false).

@basysKom-jvoe, I will be glad if you help reviewing the code:)

* some fixes

Update src/server/ua_subscription_alarms_conditions.c

* correcting example name

adding function prototype

* some fixes

1) keep events and A&C disabled and NS0 reduced in main CMakeLists
2) enable A&C in test scripts (Travis and Veyor)

* Update ua_subscription_alarms_conditions.c

fix som error handling

* Update ua_subscription_alarms_conditions.c

* fixing issues from first review

1) calculate size of array
2) correct misspelling
3) correct some comments

* Update ua_subscription_alarms_conditions.c

to retrigger travis test.
14 - subscription_events test fails randomly!

* fix memory leak issues

the function UA_NodeId_copy was used multiple times, however,  the memory allocated is not freed because only numeric NodeIds were used in testing. So using nun numeric NodeIds will cause memory leak.
Next commit/version should fix that issue.

* review fixes: comments 1 and 2

1) use new server logger function (PR rebase).
2) use values instead of pointers in A&C API functions for consistency

* Review: reduce indent

reducing indent by using "continue" inside if statements in loops

* Update tutorial_server_alarms_conditions.c

modify includes

* update to upstream master and fix some A&C Module issues

* fix CI test and some review issues

* fix some review issues

the previous commits fix issues mentioned in comments:
- https://github.com/open62541/open62541/pull/2083#discussion_r241070233
- https://github.com/open62541/open62541/pull/2083#discussion_r241072127
- https://github.com/open62541/open62541/pull/2083#discussion_r241076137
- https://github.com/open62541/open62541/pull/2083#discussion_r241076137
- https://github.com/open62541/open62541/pull/2083#discussion_r241079915
- https://github.com/open62541/open62541/pull/2083#discussion_r241082415
- https://github.com/open62541/open62541/pull/2083#discussion_r241083539
- https://github.com/open62541/open62541/pull/2083#discussion_r284586642
- https://github.com/open62541/open62541/pull/2083#discussion_r284587406
- https://github.com/open62541/open62541/pull/2083#discussion_r284589129

not resolved yet, need further discussion:
- https://github.com/open62541/open62541/pull/2083#discussion_r241072659
- https://github.com/open62541/open62541/pull/2083#discussion_r241075205

* some modification after rebase

* remove unused code + some fixes

* some fixes and modifications

changes:
- fix conflicting variable names
- pass FieldNames directly without help variable in
  "UA_Server_setConditionField"
- remove unused code
- add some comments
- correct indentation

* fix issues mentioned in review

* enable setting ConditionId when creating Condition Instances

* fix issues based on review

- pass variant instead of void* and type when setting Condition Fields and their Properties.

- Change RefreshEvents IsAbstract attribute to "False" when creating the Condition instead of doing that when generating the event.

- comment issue #2114 fix out (it seems to work fine after recent commits).

- modify A&C example according to changes.

* use A&C public API to set condition fields and properties

* check for Null pointer before comparing NodeIds

Co-Authored-By: andr <aandranikk@gmail.com>

* correct Typo

Co-Authored-By: andr <aandranikk@gmail.com>

* rebase + fix issues from review

some issues need further discussion before fixing them. Please see:
https://github.com/open62541/open62541/pull/2083#discussion_r326857289
https://github.com/open62541/open62541/pull/2083#discussion_r326857373

* fixup after rebase
2019-12-17 20:28:40 +01:00

525 lines
24 KiB
C

/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
* See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
#include <open62541/plugin/log_stdout.h>
#include <open62541/server.h>
#include <open62541/server_config_default.h>
#include <signal.h>
#include <stdlib.h>
/**
* Using Alarms and Conditions Server
* -----------------
* Besides the usage of monitored items and events to observe the changes in the server, it is also important to
* make use of the Alarms and Conditions Server Model. Alarms are events which are triggered automatically by the server
* dependent on internal server logic or user specific logic when the state of server components change.
* The state of a component is represented through a condition. So the values of all the condition children (Fields)
* are the actual state of the component.
*
* Trigger Alarm events by changing States
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* The following example will be based on the server events tutorial. Please make sure to understand the principle
* of normal events before proceeding with this example!
**/
static UA_NodeId conditionSource;
static UA_NodeId conditionInstance_1;
static UA_NodeId conditionInstance_2;
static UA_StatusCode
addConditionSourceObject(UA_Server *server) {
UA_ObjectAttributes object_attr = UA_ObjectAttributes_default;
object_attr.eventNotifier = 1;
object_attr.displayName = UA_LOCALIZEDTEXT("en", "ConditionSourceObject");
UA_StatusCode retval = UA_Server_addObjectNode(server, UA_NODEID_NULL,
UA_NODEID_NULL,
UA_NODEID_NULL,
UA_QUALIFIEDNAME(0, "ConditionSourceObject"),
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
object_attr, NULL, &conditionSource);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"creating Condition Source failed. StatusCode %s", UA_StatusCode_name(retval));
}
/* ConditionSource should be EventNotifier of another Object (usually the Server Object).
* If this Reference is not created by user then the A&C Server will create "HasEventSource"
* reference to the Server Object automatically when the condition is created*/
retval = UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
UA_NODEID_NUMERIC(0, UA_NS0ID_HASNOTIFIER),
UA_EXPANDEDNODEID_NUMERIC(conditionSource.namespaceIndex,
conditionSource.identifier.numeric),
UA_TRUE);
return retval;
}
/**
* create a condition instance from OffNormalAlarmType. The condition source is the Object created in
* addConditionSourceObject(). The condition will be exposed in Address Space through the HasComponent
* reference to the condition source.
*/
static UA_StatusCode
addCondition_1(UA_Server *server) {
UA_StatusCode retval = addConditionSourceObject(server);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"creating Condition Source failed. StatusCode %s", UA_StatusCode_name(retval));
}
retval = UA_Server_createCondition(server,
UA_NODEID_NULL, UA_NODEID_NUMERIC(0, UA_NS0ID_OFFNORMALALARMTYPE),
UA_QUALIFIEDNAME(0, "Condition 1"), conditionSource,
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), &conditionInstance_1);
return retval;
}
/**
* create a condition instance from OffNormalAlarmType. The condition source is the server Object.
* The condition won't be exposed in Address Space.
*/
static UA_StatusCode
addCondition_2(UA_Server *server) {
UA_StatusCode retval = UA_Server_createCondition(server,
UA_NODEID_NULL, UA_NODEID_NUMERIC(0, UA_NS0ID_OFFNORMALALARMTYPE),
UA_QUALIFIEDNAME(0, "Condition 2"), UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
UA_NODEID_NULL, &conditionInstance_2);
return retval;
}
static void
addVariable_1_triggerAlarmOfCondition_1(UA_Server *server, UA_NodeId* outNodeId) {
UA_VariableAttributes attr = UA_VariableAttributes_default;
attr.displayName = UA_LOCALIZEDTEXT("en", "Activate Condition 1");
attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
UA_Boolean tboolValue = UA_FALSE;
UA_Variant_setScalar(&attr.value, &tboolValue, &UA_TYPES[UA_TYPES_BOOLEAN]);
UA_QualifiedName CallbackTestVariableName = UA_QUALIFIEDNAME(0, "Activate Condition 1");
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
UA_NodeId variableTypeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
UA_Server_addVariableNode(server, UA_NODEID_NULL, parentNodeId,
parentReferenceNodeId, CallbackTestVariableName,
variableTypeNodeId, attr, NULL, outNodeId);
}
static void
addVariable_2_changeSeverityOfCondition_2(UA_Server *server, UA_NodeId* outNodeId) {
UA_VariableAttributes attr = UA_VariableAttributes_default;
attr.displayName = UA_LOCALIZEDTEXT("en", "Change Severity Condition 2");
attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
UA_UInt16 severityValue = 0;
UA_Variant_setScalar(&attr.value, &severityValue, &UA_TYPES[UA_TYPES_UINT16]);
UA_QualifiedName CallbackTestVariableName = UA_QUALIFIEDNAME(0, "Change Severity Condition 2");
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
UA_NodeId variableTypeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
UA_Server_addVariableNode(server, UA_NODEID_NULL, parentNodeId,
parentReferenceNodeId, CallbackTestVariableName,
variableTypeNodeId, attr, NULL, outNodeId);
}
static void
addVariable_3_returnCondition_1_toNormalState(UA_Server *server, UA_NodeId* outNodeId) {
UA_VariableAttributes attr = UA_VariableAttributes_default;
attr.displayName = UA_LOCALIZEDTEXT("en", "Return to Normal Condition 1");
attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
UA_Boolean rtn = 0;
UA_Variant_setScalar(&attr.value, &rtn, &UA_TYPES[UA_TYPES_BOOLEAN]);
UA_QualifiedName CallbackTestVariableName = UA_QUALIFIEDNAME(0, "Return to Normal Condition 1");
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
UA_NodeId variableTypeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
UA_Server_addVariableNode(server, UA_NODEID_NULL, parentNodeId,
parentReferenceNodeId, CallbackTestVariableName,
variableTypeNodeId, attr, NULL, outNodeId);
}
static void
afterWriteCallbackVariable_1(UA_Server *server,
const UA_NodeId *sessionId, void *sessionContext,
const UA_NodeId *nodeId, void *nodeContext,
const UA_NumericRange *range, const UA_DataValue *data) {
UA_QualifiedName activeStateField = UA_QUALIFIEDNAME(0,"ActiveState");
UA_QualifiedName activeStateIdField = UA_QUALIFIEDNAME(0,"Id");
UA_Variant value;
UA_StatusCode retval = UA_Server_writeObjectProperty_scalar(server, conditionInstance_1, UA_QUALIFIEDNAME(0, "Time"),
&data->sourceTimestamp, &UA_TYPES[UA_TYPES_DATETIME]);
if(*(UA_Boolean *)(data->value.data) == true) {
/**
* by writing "true" in ActiveState/Id, the A&C server will set the related fields
* automatically and then will trigger event notification.
*/
UA_Boolean activeStateId = true;
UA_Variant_setScalar(&value, &activeStateId, &UA_TYPES[UA_TYPES_BOOLEAN]);
retval |= UA_Server_setConditionVariableFieldProperty(server, conditionInstance_1,
&value, activeStateField, activeStateIdField);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Setting ActiveState/Id Field failed. StatusCode %s", UA_StatusCode_name(retval));
return;
}
}
else {
/**
* by writing "false" in ActiveState/Id, the A&C server will set only the ActiveState field
* automatically to the value "Inactive". The user should trigger the event manually by calling
* UA_Server_triggerConditionEvent inside the application or call
* ConditionRefresh method with client to update the event notification.
*/
UA_Boolean activeStateId = false;
UA_Variant_setScalar(&value, &activeStateId, &UA_TYPES[UA_TYPES_BOOLEAN]);
retval = UA_Server_setConditionVariableFieldProperty(server, conditionInstance_1,
&value, activeStateField, activeStateIdField);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Setting ActiveState/Id Field failed. StatusCode %s", UA_StatusCode_name(retval));
return;
}
retval = UA_Server_triggerConditionEvent(server, conditionInstance_1, conditionSource, NULL);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Triggering condition event failed. StatusCode %s", UA_StatusCode_name(retval));
return;
}
}
}
/**
* the callback only changes the severity field of the condition 2. The severity
* field is of ConditionVariableType, so changes in it triggers an event notification
* automatically by the server.
*/
static void
afterWriteCallbackVariable_2(UA_Server *server,
const UA_NodeId *sessionId, void *sessionContext,
const UA_NodeId *nodeId, void *nodeContext,
const UA_NumericRange *range, const UA_DataValue *data) {
/**
* another way to set fields of conditions
*/
UA_Server_writeObjectProperty_scalar(server, conditionInstance_2, UA_QUALIFIEDNAME(0, "Severity"),
(UA_UInt16 *)data->value.data, &UA_TYPES[UA_TYPES_UINT16]);
}
/**
* RTN = return to normal.
* Retain will be set to false, thus no events will be generated for condition 1 (although EnabledState/=true).
* To set Retain to true again, the disable and enable methods should be called respectively.
*/
static void
afterWriteCallbackVariable_3(UA_Server *server,
const UA_NodeId *sessionId, void *sessionContext,
const UA_NodeId *nodeId, void *nodeContext,
const UA_NumericRange *range, const UA_DataValue *data) {
//UA_QualifiedName enabledStateField = UA_QUALIFIEDNAME(0,"EnabledState");
UA_QualifiedName ackedStateField = UA_QUALIFIEDNAME(0,"AckedState");
UA_QualifiedName confirmedStateField = UA_QUALIFIEDNAME(0,"ConfirmedState");
UA_QualifiedName activeStateField = UA_QUALIFIEDNAME(0,"ActiveState");
UA_QualifiedName severityField = UA_QUALIFIEDNAME(0,"Severity");
UA_QualifiedName messageField = UA_QUALIFIEDNAME(0,"Message");
UA_QualifiedName commentField = UA_QUALIFIEDNAME(0,"Comment");
UA_QualifiedName retainField = UA_QUALIFIEDNAME(0,"Retain");
UA_QualifiedName idField = UA_QUALIFIEDNAME(0,"Id");
UA_StatusCode retval = UA_Server_writeObjectProperty_scalar(server, conditionInstance_1, UA_QUALIFIEDNAME(0, "Time"),
&data->serverTimestamp, &UA_TYPES[UA_TYPES_DATETIME]);
UA_Variant value;
UA_Boolean idValue = false;
UA_Variant_setScalar(&value, &idValue, &UA_TYPES[UA_TYPES_BOOLEAN]);
retval |= UA_Server_setConditionVariableFieldProperty(server, conditionInstance_1,
&value, activeStateField, idField);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Setting ActiveState/Id Field failed. StatusCode %s", UA_StatusCode_name(retval));
return;
}
retval = UA_Server_setConditionVariableFieldProperty(server, conditionInstance_1,
&value, ackedStateField, idField);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Setting AckedState/Id Field failed. StatusCode %s", UA_StatusCode_name(retval));
return;
}
retval = UA_Server_setConditionVariableFieldProperty(server, conditionInstance_1,
&value, confirmedStateField, idField);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Setting ConfirmedState/Id Field failed. StatusCode %s", UA_StatusCode_name(retval));
return;
}
UA_UInt16 severityValue = 100;
UA_Variant_setScalar(&value, &severityValue, &UA_TYPES[UA_TYPES_UINT16]);
retval = UA_Server_setConditionField(server, conditionInstance_1,
&value, severityField);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Setting Severity Field failed. StatusCode %s", UA_StatusCode_name(retval));
return;
}
UA_LocalizedText messageValue = UA_LOCALIZEDTEXT("en", "Condition returned to normal state");
UA_Variant_setScalar(&value, &messageValue, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
retval = UA_Server_setConditionField(server, conditionInstance_1,
&value, messageField);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Setting Message Field failed. StatusCode %s", UA_StatusCode_name(retval));
return;
}
UA_LocalizedText commentValue = UA_LOCALIZEDTEXT("en", "Normal State");
UA_Variant_setScalar(&value, &commentValue, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
retval = UA_Server_setConditionField(server, conditionInstance_1,
&value, commentField);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Setting Comment Field failed. StatusCode %s", UA_StatusCode_name(retval));
return;
}
UA_Boolean retainValue = false;
UA_Variant_setScalar(&value, &retainValue, &UA_TYPES[UA_TYPES_BOOLEAN]);
retval = UA_Server_setConditionField(server, conditionInstance_1,
&value, retainField);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Setting Retain Field failed. StatusCode %s", UA_StatusCode_name(retval));
return;
}
retval = UA_Server_triggerConditionEvent(server, conditionInstance_1, conditionSource, NULL);
if (retval != UA_STATUSCODE_GOOD) {
UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Triggering condition event failed. StatusCode %s", UA_StatusCode_name(retval));
return;
}
}
static UA_StatusCode
enteringEnabledStateCallback(UA_Server *server, const UA_NodeId *condition) {
UA_Boolean retain = UA_TRUE;
return UA_Server_writeObjectProperty_scalar(server, *condition, UA_QUALIFIEDNAME(0, "Retain"),
&retain, &UA_TYPES[UA_TYPES_BOOLEAN]);
}
/**
* this is user specific function which will be called upon acknowledging an alarm notification.
* In this example we will set the Alarm to Inactive state. The server is responsible of setting
* standard fields related to Acknowledge Method and triggering the alarm notification.
*/
static UA_StatusCode
enteringAckedStateCallback(UA_Server *server, const UA_NodeId *condition) {
/* deactivate Alarm when acknowledging*/
UA_Boolean activeStateId = false;
UA_Variant value;
UA_QualifiedName activeStateField = UA_QUALIFIEDNAME(0,"ActiveState");
UA_QualifiedName activeStateIdField = UA_QUALIFIEDNAME(0,"Id");
UA_Variant_setScalar(&value, &activeStateId, &UA_TYPES[UA_TYPES_BOOLEAN]);
UA_StatusCode retval = UA_Server_setConditionVariableFieldProperty(server, *condition,
&value, activeStateField, activeStateIdField);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Setting ActiveState/Id Field failed. StatusCode %s", UA_StatusCode_name(retval));
}
return retval;
}
static UA_StatusCode
enteringConfirmedStateCallback(UA_Server *server, const UA_NodeId *condition) {
/* deactivate Alarm and put it out of the interesting state (by writing false to Retain field) when confirming*/
UA_Boolean activeStateId = false;
UA_Boolean retain = false;
UA_Variant value;
UA_QualifiedName activeStateField = UA_QUALIFIEDNAME(0,"ActiveState");
UA_QualifiedName activeStateIdField = UA_QUALIFIEDNAME(0,"Id");
UA_QualifiedName retainField = UA_QUALIFIEDNAME(0,"Retain");
UA_Variant_setScalar(&value, &activeStateId, &UA_TYPES[UA_TYPES_BOOLEAN]);
UA_StatusCode retval = UA_Server_setConditionVariableFieldProperty(server, *condition,
&value, activeStateField, activeStateIdField);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Setting ActiveState/Id Field failed. StatusCode %s", UA_StatusCode_name(retval));
return retval;
}
UA_Variant_setScalar(&value, &retain, &UA_TYPES[UA_TYPES_BOOLEAN]);
retval = UA_Server_setConditionField(server, *condition,
&value, retainField);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Setting ActiveState/Id Field failed. StatusCode %s", UA_StatusCode_name(retval));
}
return retval;
}
static UA_StatusCode
setUpEnvironment(UA_Server *server) {
UA_NodeId variable_1;
UA_NodeId variable_2;
UA_NodeId variable_3;
UA_ValueCallback callback;
callback.onRead = NULL;
/**
* exposed condition 1. We will add to it user specific callbacks when entering enabled state,
* when acknowledging and when confirming
*/
UA_StatusCode retval = addCondition_1(server);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"adding condition 1 failed. StatusCode %s", UA_StatusCode_name(retval));
return retval;
}
UA_TwoStateVariableChangeCallback userSpecificCallback = enteringEnabledStateCallback;
retval = UA_Server_setConditionTwoStateVariableCallback(server, conditionInstance_1,
conditionSource, false, userSpecificCallback,
UA_ENTERING_ENABLEDSTATE);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"adding entering enabled state callback failed. StatusCode %s", UA_StatusCode_name(retval));
return retval;
}
userSpecificCallback = enteringAckedStateCallback;
retval = UA_Server_setConditionTwoStateVariableCallback(server, conditionInstance_1,
conditionSource, false, userSpecificCallback,
UA_ENTERING_ACKEDSTATE);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"adding entering acked state callback failed. StatusCode %s", UA_StatusCode_name(retval));
return retval;
}
userSpecificCallback = enteringConfirmedStateCallback;
retval = UA_Server_setConditionTwoStateVariableCallback(server, conditionInstance_1,
conditionSource, false, userSpecificCallback,
UA_ENTERING_CONFIRMEDSTATE);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"adding entering confirmed state callback failed. StatusCode %s", UA_StatusCode_name(retval));
return retval;
}
/**
* unexposed condition 2. No user specific callbacks, so the server will behave in a standard manner
* upon entering enabled state, acknowledging and confirming.
* We will set Retain field to true and enable the condition so we can receive event
* notifications (we cannot call enable method on unexposed condition using a
* client like UaExpert or Softing).
*/
retval = addCondition_2(server);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"adding condition 2 failed. StatusCode %s", UA_StatusCode_name(retval));
return retval;
}
UA_Boolean retain = UA_TRUE;
UA_Server_writeObjectProperty_scalar(server, conditionInstance_2, UA_QUALIFIEDNAME(0, "Retain"),
&retain, &UA_TYPES[UA_TYPES_BOOLEAN]);
UA_Variant value;
UA_Boolean enabledStateId = true;
UA_QualifiedName enabledStateField = UA_QUALIFIEDNAME(0,"EnabledState");
UA_QualifiedName enabledStateIdField = UA_QUALIFIEDNAME(0,"Id");
UA_Variant_setScalar(&value, &enabledStateId, &UA_TYPES[UA_TYPES_BOOLEAN]);
retval = UA_Server_setConditionVariableFieldProperty(server, conditionInstance_2,
&value, enabledStateField, enabledStateIdField);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Setting EnabledState/Id Field failed. StatusCode %s", UA_StatusCode_name(retval));
return retval;
}
/**
* add 3 variables to trigger condition events
*/
addVariable_1_triggerAlarmOfCondition_1(server, &variable_1);
callback.onWrite = afterWriteCallbackVariable_1;
retval = UA_Server_setVariableNode_valueCallback(server, variable_1, callback);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"setting variable 1 Callback failed. StatusCode %s", UA_StatusCode_name(retval));
return retval;
}
/**
* severity can change internally also when the condition disabled and retain is false. However,
* in this case no events will be generated.
*/
addVariable_2_changeSeverityOfCondition_2(server, &variable_2);
callback.onWrite = afterWriteCallbackVariable_2;
retval = UA_Server_setVariableNode_valueCallback(server, variable_2, callback);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"setting variable 2 Callback failed. StatusCode %s", UA_StatusCode_name(retval));
return retval;
}
addVariable_3_returnCondition_1_toNormalState(server, &variable_3);
callback.onWrite = afterWriteCallbackVariable_3;
retval = UA_Server_setVariableNode_valueCallback(server, variable_3, callback);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"setting variable 3 Callback failed. StatusCode %s", UA_StatusCode_name(retval));
}
return retval;
}
/** It follows the main server code, making use of the above definitions. */
static UA_Boolean running = true;
static void stopHandler(int sig) {
running = false;
}
int main (void) {
/* default server values */
signal(SIGINT, stopHandler);
signal(SIGTERM, stopHandler);
UA_Server *server = UA_Server_new();
UA_ServerConfig_setDefault(UA_Server_getConfig(server));
UA_StatusCode retval = setUpEnvironment(server);
if(retval == UA_STATUSCODE_GOOD)
{
retval = UA_Server_run(server, &running);
}
UA_Server_delete(server);
return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
}