mirror of
https://github.com/open62541/open62541.git
synced 2025-06-03 04:00:21 +00:00
571 lines
23 KiB
C
571 lines
23 KiB
C
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* Copyright 2022 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
|
|
*/
|
|
|
|
#include "ua_server_internal.h"
|
|
#include "ua_session.h"
|
|
#include "ua_subscription.h"
|
|
|
|
#ifdef UA_ENABLE_DIAGNOSTICS
|
|
|
|
static UA_Boolean
|
|
equalBrowseName(UA_String *bn, char *n) {
|
|
UA_String name = UA_STRING(n);
|
|
return UA_String_equal(bn, &name);
|
|
}
|
|
|
|
#ifdef UA_ENABLE_SUBSCRIPTIONS
|
|
|
|
/****************************/
|
|
/* Subscription Diagnostics */
|
|
/****************************/
|
|
|
|
static void
|
|
fillSubscriptionDiagnostics(UA_Subscription *sub,
|
|
UA_SubscriptionDiagnosticsDataType *diag) {
|
|
UA_NodeId_copy(&sub->session->sessionId, &diag->sessionId); /* ignore status */
|
|
diag->subscriptionId = sub->subscriptionId;
|
|
diag->priority = sub->priority;
|
|
diag->publishingInterval = sub->publishingInterval;
|
|
diag->maxKeepAliveCount = sub->maxKeepAliveCount;
|
|
diag->maxLifetimeCount = sub->lifeTimeCount;
|
|
diag->maxNotificationsPerPublish = sub->notificationsPerPublish;
|
|
diag->publishingEnabled = sub->publishingEnabled;
|
|
diag->modifyCount = sub->modifyCount;
|
|
diag->enableCount = sub->enableCount;
|
|
diag->disableCount = sub->disableCount;
|
|
diag->republishRequestCount = sub->republishRequestCount;
|
|
diag->republishMessageRequestCount =
|
|
sub->republishRequestCount; /* Always equal to the previous republishRequestCount */
|
|
diag->republishMessageCount = sub->republishMessageCount;
|
|
diag->transferRequestCount = sub->transferRequestCount;
|
|
diag->transferredToAltClientCount = sub->transferredToAltClientCount;
|
|
diag->transferredToSameClientCount = sub->transferredToSameClientCount;
|
|
diag->publishRequestCount = sub->publishRequestCount;
|
|
diag->dataChangeNotificationsCount = sub->dataChangeNotificationsCount;
|
|
diag->eventNotificationsCount = sub->eventNotificationsCount;
|
|
diag->notificationsCount = sub->notificationsCount;
|
|
diag->latePublishRequestCount = sub->latePublishRequestCount;
|
|
diag->currentKeepAliveCount = sub->currentKeepAliveCount;
|
|
diag->currentLifetimeCount = sub->currentLifetimeCount;
|
|
diag->unacknowledgedMessageCount = (UA_UInt32)sub->retransmissionQueueSize;
|
|
diag->discardedMessageCount = sub->discardedMessageCount;
|
|
diag->monitoredItemCount = sub->monitoredItemsSize;
|
|
diag->monitoringQueueOverflowCount = sub->monitoringQueueOverflowCount;
|
|
diag->nextSequenceNumber = sub->nextSequenceNumber;
|
|
diag->eventQueueOverFlowCount = sub->eventQueueOverFlowCount;
|
|
|
|
/* Count the disabled MonitoredItems */
|
|
UA_MonitoredItem *mon;
|
|
LIST_FOREACH(mon, &sub->monitoredItems, listEntry) {
|
|
if(mon->monitoringMode == UA_MONITORINGMODE_DISABLED)
|
|
diag->disabledMonitoredItemCount++;
|
|
}
|
|
}
|
|
|
|
/* The node context points to the subscription */
|
|
static UA_StatusCode
|
|
readSubscriptionDiagnostics(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) {
|
|
/* Check the Subscription pointer */
|
|
UA_Subscription *sub = (UA_Subscription*)nodeContext;
|
|
if(!sub)
|
|
return UA_STATUSCODE_BADINTERNALERROR;
|
|
|
|
/* Read the BrowseName */
|
|
UA_QualifiedName bn;
|
|
UA_StatusCode res = readWithReadValue(server, nodeId, UA_ATTRIBUTEID_BROWSENAME, &bn);
|
|
if(res != UA_STATUSCODE_GOOD)
|
|
return res;
|
|
|
|
/* Set the value */
|
|
UA_SubscriptionDiagnosticsDataType sddt;
|
|
UA_SubscriptionDiagnosticsDataType_init(&sddt);
|
|
fillSubscriptionDiagnostics(sub, &sddt);
|
|
|
|
char memberName[128];
|
|
memcpy(memberName, bn.name.data, bn.name.length);
|
|
memberName[bn.name.length] = 0;
|
|
|
|
size_t memberOffset;
|
|
const UA_DataType *memberType;
|
|
UA_Boolean isArray;
|
|
UA_Boolean found =
|
|
UA_DataType_getStructMember(&UA_TYPES[UA_TYPES_SUBSCRIPTIONDIAGNOSTICSDATATYPE],
|
|
memberName, &memberOffset, &memberType, &isArray);
|
|
if(!found) {
|
|
/* Not the member, but the main subscription diagnostics variable... */
|
|
memberOffset = 0;
|
|
memberType = &UA_TYPES[UA_TYPES_SUBSCRIPTIONDIAGNOSTICSDATATYPE];
|
|
}
|
|
|
|
void *content = (void*)(((uintptr_t)&sddt) + memberOffset);
|
|
res = UA_Variant_setScalarCopy(&value->value, content, memberType);
|
|
if(UA_LIKELY(res == UA_STATUSCODE_GOOD))
|
|
value->hasValue = true;
|
|
|
|
UA_SubscriptionDiagnosticsDataType_clear(&sddt);
|
|
UA_QualifiedName_clear(&bn);
|
|
return res;
|
|
}
|
|
|
|
/* If the nodeContext == NULL, return all subscriptions in the server.
|
|
* Otherwise only for the current session. */
|
|
UA_StatusCode
|
|
readSubscriptionDiagnosticsArray(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) {
|
|
/* Get the current session */
|
|
size_t sdSize = 0;
|
|
UA_Session *session = NULL;
|
|
session_list_entry *sentry;
|
|
if(nodeContext) {
|
|
session = UA_Server_getSessionById(server, sessionId);
|
|
if(!session)
|
|
return UA_STATUSCODE_BADINTERNALERROR;
|
|
sdSize = session->subscriptionsSize;
|
|
} else {
|
|
LIST_FOREACH(sentry, &server->sessions, pointers) {
|
|
sdSize += sentry->session.subscriptionsSize;
|
|
}
|
|
}
|
|
|
|
/* Allocate the output array */
|
|
UA_SubscriptionDiagnosticsDataType *sd = (UA_SubscriptionDiagnosticsDataType*)
|
|
UA_Array_new(sdSize, &UA_TYPES[UA_TYPES_SUBSCRIPTIONDIAGNOSTICSDATATYPE]);
|
|
if(!sd)
|
|
return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
|
/* Collect the statistics */
|
|
size_t i = 0;
|
|
UA_Subscription *sub;
|
|
if(session) {
|
|
TAILQ_FOREACH(sub, &session->subscriptions, sessionListEntry) {
|
|
fillSubscriptionDiagnostics(sub, &sd[i]);
|
|
i++;
|
|
}
|
|
} else {
|
|
LIST_FOREACH(sentry, &server->sessions, pointers) {
|
|
TAILQ_FOREACH(sub, &sentry->session.subscriptions, sessionListEntry) {
|
|
fillSubscriptionDiagnostics(sub, &sd[i]);
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Set the output */
|
|
value->hasValue = true;
|
|
UA_Variant_setArray(&value->value, sd, sdSize,
|
|
&UA_TYPES[UA_TYPES_SUBSCRIPTIONDIAGNOSTICSDATATYPE]);
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
|
|
void
|
|
createSubscriptionObject(UA_Server *server, UA_Session *session,
|
|
UA_Subscription *sub) {
|
|
UA_ExpandedNodeId *children = NULL;
|
|
size_t childrenSize = 0;
|
|
UA_ReferenceTypeSet refTypes;
|
|
UA_NodeId hasComponent = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT);
|
|
|
|
char subIdStr[32];
|
|
snprintf(subIdStr, 32, "%u", sub->subscriptionId);
|
|
|
|
/* Find the NodeId of the SubscriptionDiagnosticsArray */
|
|
UA_BrowsePath bp;
|
|
UA_BrowsePath_init(&bp);
|
|
bp.startingNode = sub->session->sessionId;
|
|
UA_RelativePathElement rpe[1];
|
|
memset(rpe, 0, sizeof(UA_RelativePathElement) * 1);
|
|
rpe[0].targetName = UA_QUALIFIEDNAME(0, "SubscriptionDiagnosticsArray");
|
|
bp.relativePath.elements = rpe;
|
|
bp.relativePath.elementsSize = 1;
|
|
UA_BrowsePathResult bpr = translateBrowsePathToNodeIds(server, &bp);
|
|
if(bpr.targetsSize < 1)
|
|
return;
|
|
|
|
/* Create an object for the subscription. Instantiates all the mandatory
|
|
* children. */
|
|
UA_VariableAttributes var_attr = UA_VariableAttributes_default;
|
|
var_attr.displayName.text = UA_STRING(subIdStr);
|
|
var_attr.dataType = UA_TYPES[UA_TYPES_SUBSCRIPTIONDIAGNOSTICSDATATYPE].typeId;
|
|
UA_NodeId refId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT);
|
|
UA_QualifiedName browseName = UA_QUALIFIEDNAME(0, subIdStr);
|
|
UA_NodeId typeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SUBSCRIPTIONDIAGNOSTICSTYPE);
|
|
UA_NodeId objId = UA_NODEID_NUMERIC(1, 0); /* 0 => assign a random free id */
|
|
UA_StatusCode res = addNode(server, UA_NODECLASS_VARIABLE,
|
|
&objId,
|
|
&bpr.targets[0].targetId.nodeId /* parent */,
|
|
&refId, browseName, &typeId,
|
|
(UA_NodeAttributes*)&var_attr,
|
|
&UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES], NULL, &objId);
|
|
UA_CHECK_STATUS(res, goto cleanup);
|
|
|
|
/* Add a second reference from the overall SubscriptionDiagnosticsArray variable */
|
|
const UA_NodeId subDiagArray =
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SUBSCRIPTIONDIAGNOSTICSARRAY);
|
|
res = addRef(server, session, &subDiagArray, &refId, &objId, true);
|
|
|
|
/* Get all children (including the variable itself) and set the contenxt + callback */
|
|
res = referenceTypeIndices(server, &hasComponent, &refTypes, false);
|
|
if(res != UA_STATUSCODE_GOOD)
|
|
goto cleanup;
|
|
res = browseRecursive(server, 1, &objId,
|
|
UA_BROWSEDIRECTION_FORWARD, &refTypes,
|
|
UA_NODECLASS_VARIABLE, true, &childrenSize, &children);
|
|
if(res != UA_STATUSCODE_GOOD)
|
|
goto cleanup;
|
|
|
|
/* Add the callback to all variables */
|
|
UA_DataSource subDiagSource = {readSubscriptionDiagnostics, NULL};
|
|
for(size_t i = 0; i < childrenSize; i++) {
|
|
setVariableNode_dataSource(server, children[i].nodeId, subDiagSource);
|
|
setNodeContext(server, children[i].nodeId, sub);
|
|
}
|
|
|
|
UA_Array_delete(children, childrenSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]);
|
|
|
|
cleanup:
|
|
UA_BrowsePathResult_clear(&bpr);
|
|
if(res != UA_STATUSCODE_GOOD) {
|
|
UA_LOG_WARNING_SESSION(&server->config.logger, session,
|
|
"Creating the subscription diagnostics object failed "
|
|
"with StatusCode %s", UA_StatusCode_name(res));
|
|
}
|
|
}
|
|
|
|
#endif /* UA_ENABLE_SUBSCRIPTIONS */
|
|
|
|
/***********************/
|
|
/* Session Diagnostics */
|
|
/***********************/
|
|
|
|
static void
|
|
setSessionDiagnostics(UA_Session *session, UA_SessionDiagnosticsDataType *sd) {
|
|
UA_SessionDiagnosticsDataType_copy(&session->diagnostics, sd);
|
|
UA_NodeId_copy(&session->sessionId, &sd->sessionId);
|
|
UA_String_copy(&session->sessionName, &sd->sessionName);
|
|
UA_ApplicationDescription_copy(&session->clientDescription,
|
|
&sd->clientDescription);
|
|
sd->maxResponseMessageSize = session->maxResponseMessageSize;
|
|
#ifdef UA_ENABLE_SUBSCRIPTIONS
|
|
sd->currentPublishRequestsInQueue = (UA_UInt32)session->responseQueueSize;
|
|
#endif
|
|
sd->actualSessionTimeout = session->timeout;
|
|
|
|
/* Set LocaleIds */
|
|
UA_StatusCode res =
|
|
UA_Array_copy(session->localeIds, session->localeIdsSize,
|
|
(void **)&sd->localeIds, &UA_TYPES[UA_TYPES_STRING]);
|
|
if(UA_LIKELY(res == UA_STATUSCODE_GOOD))
|
|
sd->localeIdsSize = session->localeIdsSize;
|
|
|
|
/* Set Subscription diagnostics */
|
|
#ifdef UA_ENABLE_SUBSCRIPTIONS
|
|
sd->currentSubscriptionsCount = (UA_UInt32)session->subscriptionsSize;
|
|
|
|
UA_Subscription *sub;
|
|
TAILQ_FOREACH(sub, &session->subscriptions, sessionListEntry) {
|
|
sd->currentMonitoredItemsCount += (UA_UInt32)sub->monitoredItemsSize;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
UA_StatusCode
|
|
readSessionDiagnosticsArray(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) {
|
|
/* Allocate the output array */
|
|
UA_SessionDiagnosticsDataType *sd = (UA_SessionDiagnosticsDataType*)
|
|
UA_Array_new(server->sessionCount,
|
|
&UA_TYPES[UA_TYPES_SESSIONDIAGNOSTICSDATATYPE]);
|
|
if(!sd)
|
|
return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
|
/* Collect the statistics */
|
|
size_t i = 0;
|
|
session_list_entry *session;
|
|
LIST_FOREACH(session, &server->sessions, pointers) {
|
|
setSessionDiagnostics(&session->session, &sd[i]);
|
|
i++;
|
|
}
|
|
|
|
/* Set the output */
|
|
value->hasValue = true;
|
|
UA_Variant_setArray(&value->value, sd, server->sessionCount,
|
|
&UA_TYPES[UA_TYPES_SESSIONDIAGNOSTICSDATATYPE]);
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
|
|
static void
|
|
setSessionSecurityDiagnostics(UA_Session *session,
|
|
UA_SessionSecurityDiagnosticsDataType *sd) {
|
|
UA_SessionSecurityDiagnosticsDataType_copy(&session->securityDiagnostics, sd);
|
|
UA_NodeId_copy(&session->sessionId, &sd->sessionId);
|
|
UA_SecureChannel *channel = session->header.channel;
|
|
if(channel) {
|
|
UA_ByteString_copy(&channel->remoteCertificate, &sd->clientCertificate);
|
|
UA_String_copy(&channel->securityPolicy->policyUri, &sd->securityPolicyUri);
|
|
sd->securityMode = channel->securityMode;
|
|
sd->encoding = UA_STRING_ALLOC("UA Binary"); /* The only one atm */
|
|
sd->transportProtocol = UA_STRING_ALLOC("opc.tcp"); /* The only one atm */
|
|
}
|
|
}
|
|
|
|
static UA_StatusCode
|
|
readSessionDiagnostics(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) {
|
|
/* Get the Session */
|
|
UA_Session *session = UA_Server_getSessionById(server, sessionId);
|
|
if(!session)
|
|
return UA_STATUSCODE_BADINTERNALERROR;
|
|
|
|
/* Read the BrowseName */
|
|
UA_QualifiedName bn;
|
|
UA_StatusCode res = readWithReadValue(server, nodeId, UA_ATTRIBUTEID_BROWSENAME, &bn);
|
|
if(res != UA_STATUSCODE_GOOD)
|
|
return res;
|
|
|
|
union {
|
|
UA_SessionDiagnosticsDataType sddt;
|
|
UA_SessionSecurityDiagnosticsDataType ssddt;
|
|
} data;
|
|
void *content;
|
|
UA_Boolean isArray = false;
|
|
const UA_DataType *type = NULL;
|
|
UA_Boolean securityDiagnostics = false;
|
|
|
|
char memberName[128];
|
|
size_t memberOffset;
|
|
UA_Boolean found;
|
|
|
|
if(equalBrowseName(&bn.name, "SubscriptionDiagnosticsArray")) {
|
|
/* Reuse the datasource callback. Forward a non-null nodeContext to
|
|
* indicate that we want to see only the subscriptions for the current
|
|
* session. */
|
|
res = readSubscriptionDiagnosticsArray(server, sessionId, sessionContext,
|
|
nodeId, (void*)0x01,
|
|
sourceTimestamp, range, value);
|
|
goto cleanup;
|
|
} else if(equalBrowseName(&bn.name, "SessionDiagnostics")) {
|
|
setSessionDiagnostics(session, &data.sddt);
|
|
content = &data.sddt;
|
|
type = &UA_TYPES[UA_TYPES_SESSIONDIAGNOSTICSDATATYPE];
|
|
goto set_value;
|
|
} else if(equalBrowseName(&bn.name, "SessionSecurityDiagnostics")) {
|
|
setSessionSecurityDiagnostics(session, &data.ssddt);
|
|
securityDiagnostics = true;
|
|
content = &data.ssddt;
|
|
type = &UA_TYPES[UA_TYPES_SESSIONSECURITYDIAGNOSTICSDATATYPE];
|
|
goto set_value;
|
|
}
|
|
|
|
/* Try to find the member in SessionDiagnosticsDataType and
|
|
* SessionSecurityDiagnosticsDataType */
|
|
memcpy(memberName, bn.name.data, bn.name.length);
|
|
memberName[bn.name.length] = 0;
|
|
found = UA_DataType_getStructMember(&UA_TYPES[UA_TYPES_SESSIONDIAGNOSTICSDATATYPE],
|
|
memberName, &memberOffset, &type, &isArray);
|
|
if(found) {
|
|
setSessionDiagnostics(session, &data.sddt);
|
|
content = (void*)(((uintptr_t)&data.sddt) + memberOffset);
|
|
} else {
|
|
found = UA_DataType_getStructMember(&UA_TYPES[UA_TYPES_SESSIONSECURITYDIAGNOSTICSDATATYPE],
|
|
memberName, &memberOffset, &type, &isArray);
|
|
if(!found) {
|
|
res = UA_STATUSCODE_BADNOTIMPLEMENTED;
|
|
goto cleanup;
|
|
}
|
|
setSessionSecurityDiagnostics(session, &data.ssddt);
|
|
securityDiagnostics = true;
|
|
content = (void*)(((uintptr_t)&data.ssddt) + memberOffset);
|
|
}
|
|
|
|
set_value:
|
|
if(!isArray) {
|
|
res = UA_Variant_setScalarCopy(&value->value, content, type);
|
|
} else {
|
|
size_t len = *(size_t*)content;
|
|
content = (void*)(((uintptr_t)content) + sizeof(size_t));
|
|
res = UA_Variant_setArrayCopy(&value->value, content, len, type);
|
|
}
|
|
if(UA_LIKELY(res == UA_STATUSCODE_GOOD))
|
|
value->hasValue = true;
|
|
|
|
if(securityDiagnostics)
|
|
UA_SessionSecurityDiagnosticsDataType_clear(&data.ssddt);
|
|
else
|
|
UA_SessionDiagnosticsDataType_clear(&data.sddt);
|
|
|
|
cleanup:
|
|
UA_QualifiedName_clear(&bn);
|
|
return res;
|
|
}
|
|
|
|
UA_StatusCode
|
|
readSessionSecurityDiagnostics(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) {
|
|
/* Allocate the output array */
|
|
UA_SessionSecurityDiagnosticsDataType *sd = (UA_SessionSecurityDiagnosticsDataType*)
|
|
UA_Array_new(server->sessionCount,
|
|
&UA_TYPES[UA_TYPES_SESSIONSECURITYDIAGNOSTICSDATATYPE]);
|
|
if(!sd)
|
|
return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
|
/* Collect the statistics */
|
|
size_t i = 0;
|
|
session_list_entry *session;
|
|
LIST_FOREACH(session, &server->sessions, pointers) {
|
|
setSessionSecurityDiagnostics(&session->session, &sd[i]);
|
|
i++;
|
|
}
|
|
|
|
/* Set the output */
|
|
value->hasValue = true;
|
|
UA_Variant_setArray(&value->value, sd, server->sessionCount,
|
|
&UA_TYPES[UA_TYPES_SESSIONSECURITYDIAGNOSTICSDATATYPE]);
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
|
|
void
|
|
createSessionObject(UA_Server *server, UA_Session *session) {
|
|
UA_ExpandedNodeId *children = NULL;
|
|
size_t childrenSize = 0;
|
|
UA_ReferenceTypeSet refTypes;
|
|
UA_NodeId hasComponent = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT);
|
|
|
|
/* Create an object for the session. Instantiates all the mandatory children. */
|
|
UA_ObjectAttributes object_attr = UA_ObjectAttributes_default;
|
|
object_attr.displayName.text = session->sessionName;
|
|
UA_NodeId parentId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SESSIONSDIAGNOSTICSSUMMARY);
|
|
UA_NodeId refId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT);
|
|
UA_QualifiedName browseName = UA_QUALIFIEDNAME(0, "");
|
|
browseName.name = session->sessionName; /* shallow copy */
|
|
UA_NodeId typeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SESSIONDIAGNOSTICSOBJECTTYPE);
|
|
UA_StatusCode res = addNode(server, UA_NODECLASS_OBJECT,
|
|
&session->sessionId, &parentId, &refId, browseName, &typeId,
|
|
(UA_NodeAttributes*)&object_attr,
|
|
&UA_TYPES[UA_TYPES_OBJECTATTRIBUTES], NULL, NULL);
|
|
if(res != UA_STATUSCODE_GOOD)
|
|
goto cleanup;
|
|
|
|
/* Recursively browse all children */
|
|
res = referenceTypeIndices(server, &hasComponent, &refTypes, false);
|
|
if(res != UA_STATUSCODE_GOOD)
|
|
goto cleanup;
|
|
res = browseRecursive(server, 1, &session->sessionId,
|
|
UA_BROWSEDIRECTION_FORWARD, &refTypes,
|
|
UA_NODECLASS_VARIABLE, false, &childrenSize, &children);
|
|
if(res != UA_STATUSCODE_GOOD)
|
|
goto cleanup;
|
|
|
|
/* Add the callback to all variables */
|
|
UA_DataSource sessionDiagSource = {readSessionDiagnostics, NULL};
|
|
for(size_t i = 0; i < childrenSize; i++) {
|
|
setVariableNode_dataSource(server, children[i].nodeId, sessionDiagSource);
|
|
}
|
|
|
|
cleanup:
|
|
if(res != UA_STATUSCODE_GOOD) {
|
|
UA_LOG_WARNING_SESSION(&server->config.logger, session,
|
|
"Creating the session diagnostics object failed "
|
|
"with StatusCode %s", UA_StatusCode_name(res));
|
|
}
|
|
UA_Array_delete(children, childrenSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]);
|
|
}
|
|
|
|
/***************************/
|
|
/* Server-Wide Diagnostics */
|
|
/***************************/
|
|
|
|
UA_StatusCode
|
|
readDiagnostics(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;
|
|
value->status = UA_STATUSCODE_BADINDEXRANGEINVALID;
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
|
|
if(sourceTimestamp) {
|
|
value->hasSourceTimestamp = true;
|
|
value->sourceTimestamp = UA_DateTime_now();
|
|
}
|
|
|
|
UA_assert(nodeId->identifierType == UA_NODEIDTYPE_NUMERIC);
|
|
|
|
void *data = NULL;
|
|
const UA_DataType *type = &UA_TYPES[UA_TYPES_UINT32]; /* Default */
|
|
|
|
switch(nodeId->identifier.numeric) {
|
|
case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY:
|
|
server->serverDiagnosticsSummary.currentSessionCount =
|
|
server->activeSessionCount;
|
|
data = &server->serverDiagnosticsSummary;
|
|
type = &UA_TYPES[UA_TYPES_SERVERDIAGNOSTICSSUMMARYDATATYPE];
|
|
break;
|
|
case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_SERVERVIEWCOUNT:
|
|
data = &server->serverDiagnosticsSummary.serverViewCount;
|
|
break;
|
|
case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_CURRENTSESSIONCOUNT:
|
|
data = &server->activeSessionCount;
|
|
break;
|
|
case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_CUMULATEDSESSIONCOUNT:
|
|
data = &server->serverDiagnosticsSummary.cumulatedSessionCount;
|
|
break;
|
|
case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_SECURITYREJECTEDSESSIONCOUNT:
|
|
data = &server->serverDiagnosticsSummary.securityRejectedSessionCount;
|
|
break;
|
|
case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_REJECTEDSESSIONCOUNT:
|
|
data = &server->serverDiagnosticsSummary.rejectedSessionCount;
|
|
break;
|
|
case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_SESSIONTIMEOUTCOUNT:
|
|
data = &server->serverDiagnosticsSummary.sessionTimeoutCount;
|
|
break;
|
|
case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_SESSIONABORTCOUNT:
|
|
data = &server->serverDiagnosticsSummary.sessionAbortCount;
|
|
break;
|
|
case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_CURRENTSUBSCRIPTIONCOUNT:
|
|
data = &server->serverDiagnosticsSummary.currentSubscriptionCount;
|
|
break;
|
|
case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_CUMULATEDSUBSCRIPTIONCOUNT:
|
|
data = &server->serverDiagnosticsSummary.cumulatedSubscriptionCount;
|
|
break;
|
|
case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_PUBLISHINGINTERVALCOUNT:
|
|
data = &server->serverDiagnosticsSummary.publishingIntervalCount;
|
|
break;
|
|
case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_SECURITYREJECTEDREQUESTSCOUNT:
|
|
data = &server->serverDiagnosticsSummary.securityRejectedRequestsCount;
|
|
break;
|
|
case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_REJECTEDREQUESTSCOUNT:
|
|
data = &server->serverDiagnosticsSummary.rejectedRequestsCount;
|
|
break;
|
|
default:
|
|
return UA_STATUSCODE_BADINTERNALERROR;
|
|
}
|
|
|
|
UA_StatusCode res = UA_Variant_setScalarCopy(&value->value, data, type);
|
|
if(res == UA_STATUSCODE_GOOD)
|
|
value->hasValue = true;
|
|
return res;
|
|
}
|
|
|
|
#endif /* UA_ENABLE_DIAGNOSTICS */
|