node-opcua / node-opcua

Unlocking the Full Potential of OPC UA with Typescript and NodeJS - http://node-opcua.github.io/
MIT License
1.48k stars 481 forks source link

How to acknowledge alarm using Cogent DataHub #1317

Closed InstantlyMoist closed 11 months ago

InstantlyMoist commented 11 months ago

Hi,

For my intenrship I'm using Cogent Data Hub to translate AE to UA and connect to it using this library.

I've been trying to find documentation regarding AE and aknowledging alarms. But I'm out of ideas, this is the current structure.

image

One thing I tried is adding a subscription to the 'Ack' and 'Status', unfortunately I don't see anything changing in the 'Ack' once aknowledging. The status however does change. One problem is that it is not writeable.

So I'm out of ideas on what to try, is there something obvious I'm missing here?

AndreasHeine commented 11 months ago

not sure what "Cogent DataHub" creates in the UA Addressspace but if its a OPC UA Part 9 Alram/Condition it shall work with a methodcall -> https://reference.opcfoundation.org/Core/Part9/v105/docs/4.3

AndreasHeine commented 11 months ago

if the server exposes the Alarms and Conditions to the Addressspace like: image then you have the method here

InstantlyMoist commented 11 months ago

if the server exposes the Alarms and Conditions to the Addressspace like: image then you have the method here

Awesome, I checked it out and I see this:

image

Doesn't seem like I'm getting any function calls in here

AndreasHeine commented 11 months ago

whats the typedefinition of that alarm?

InstantlyMoist commented 11 months ago

I hope this is what you mean :D

image

AndreasHeine commented 11 months ago

image

AlarmConditionType is a SubType of AcknowledgeableConditionType and should have that method because its mandatory...

AndreasHeine commented 11 months ago

maybe you don't have the access rights to see and interact the method !?

InstantlyMoist commented 11 months ago

Ah awesome! I see it now within the conditiontype.

image

I will try to figure out how to call the function according to the documentation. I'll let you know!

AndreasHeine commented 11 months ago

maybe you can look into this: https://github.com/node-opcua/node-opcua/blob/master/packages/node-opcua-samples/bin/simple_client_ts.ts

AndreasHeine commented 11 months ago

Ah awesome! I see it now within the conditiontype.

image

I will try to figure out how to call the function according to the documentation. I'll let you know!

the method of the type is not ment to be called as far as i know!

InstantlyMoist commented 11 months ago

What OPC-UA browser do you use? Maybe it's something wrong with the one I use. I'm also not really able to figure out where exactly the methods get called from the link you've provided.

AndreasHeine commented 11 months ago

What OPC-UA browser do you use? Maybe it's something wrong with the one I use. I'm also not really able to figure out where exactly the methods get called from the link you've provided.

UA Expert its the Reference-Implementation of an UA Client

AndreasHeine commented 11 months ago

Did some research for you ;)

https://reference.opcfoundation.org/Core/Part9/v105/docs/5.7

5.7.3 Acknowledge Method

The Acknowledge Method is used to acknowledge an Event Notification for a Condition instance state where AckedState is False. Normally, the NodeId of the object instance is passed as the ObjectId to the Call Service. However, some Servers do not expose Condition instances in the AddressSpace. Therefore, Servers shall allow Clients to call the Acknowledge Method by specifying ConditionId as the ObjectId. The Method cannot be called with an ObjectId of the AcknowledgeableConditionType Node.

edit:

Therefore, Servers shall allow Clients to call the Acknowledge Method by specifying ConditionId as the ObjectId. The Method cannot be called with an ObjectId of the AcknowledgeableConditionType Node.

if that does not work you need to contact the support of Cogent DataHub because in that case there are not compliant to the opc ua spec...

hint: https://node-opcua.github.io/api_doc/2.68.0/interfaces/node_opcua.ClientSession.html#call

InstantlyMoist commented 11 months ago

Thanks, I think I got a working piece of code (somewhat)

Unfortunately you're right that I can't call the 'acknowledge' function from the base data-type. Using OPC Expert gives me the same result: image

No acknowledge method can be found. Honestly not sure what would be causing this...

AndreasHeine commented 11 months ago

unfortunately if all that did not work you need to contact Cogent DataHub... if the don't expose the method there are not compliant to OPC UA Spec.

which would be a shame and yet another bad implementation of OPC UA...

InstantlyMoist commented 11 months ago

Bummer! is there another application you might know of that translates OPC AE to UA? Our systems currently do not support UA but in theory they will support it in the future. Hence why I want to focus on actually implementing something future-proof-ish :D

AndreasHeine commented 11 months ago

maybe: https://integrationobjects.com/sioth-opc/sioth-opc-unified-architecture/opc-ua-wrapper/

edit: at least build by OPC-F members! https://opcconnect.opcfoundation.org/2017/12/upgrade-your-legacy-opc-systems-to-opc-ua/

InstantlyMoist commented 11 months ago

seems to have the same issue for me. cogent datahub has a lot of different options for which opc ae server to connect to.

image

This is with opc-ua wrapper enabled, and all possible servers selected. something seems of to me

InstantlyMoist commented 11 months ago

for example; right is cogent datahub.

image

InstantlyMoist commented 11 months ago

using OPC UA Gateway another different result

image

Now I can only acknowledge the complete server... somehow.

InstantlyMoist commented 11 months ago

Ah, starting to understand it. The Cogent Data Hub way was completely wrong so I now installed Matrikon OPC UA Tunneler. When using Prosys UA Browser I can monitor the events and I actually see them showing up now.

Next thing is to figure out how to actually get them in my piece of code, and be able to aknowledge them :D

InstantlyMoist commented 11 months ago

Nevermind me again, I still can't find the events in my explorer. Only in the event view. Which renders it basically useles as I need the nodeId's...

AndreasHeine commented 11 months ago

so finally:

this part is a little stange formulated...

5.7.3 Acknowledge Method

The Acknowledge Method is used to acknowledge an Event Notification for a Condition instance state where AckedState is False. Normally, the NodeId of the object instance is passed as the ObjectId to the Call Service. However, some Servers do not expose Condition instances in the AddressSpace. Therefore, Servers shall allow Clients to call the Acknowledge Method by specifying ConditionId as the ObjectId. The Method cannot be called with an ObjectId of the AcknowledgeableConditionType Node.

this should ack an alarm if it is not exposed to the addressspace

const methodToCall = {
    objectId: "ConditionId_from_the_Event",
    methodId: "i=9111",
    inputArguments: [
        new Variant({...}), // EventId (ByteString)
        new Variant({...}), // Comment (LocalizedText)
    ]
}
session.call(methodToCall,function(err,callResult) {
   if (!err) {
        console.log(" statusCode = ",callResult.statusCode);
        console.log(" inputArgumentResults[0] = ",callResult.inputArgumentResults[0].toString());
        console.log(" inputArgumentResults[1] = ",callResult.inputArgumentResults[1].toString());
        console.log(" outputArgument[0]       = ",callResult.outputArgument[0].toString()); // array of variant
   }
});

edit:

opc.tcp://opcua.umati.app:4843

image

image

image

InstantlyMoist commented 11 months ago

I dont get how I'm supposed to get the alarms when they're not exposed to the namespace. I've tried adding a subscription which worked. But on subscribing it does not send me the already existing events. It only shows me new ones.

AndreasHeine commented 11 months ago

the alarm in the addressspace is just the current state of it. you need to create a event subscription for that alarmtype!

e.g. https://github.com/node-opcua/node-opcua/blob/master/packages/node-opcua-samples/bin/simple_client_ts.ts#L711-L785

InstantlyMoist commented 11 months ago

@AndreasHeine I've actually had something similair to the listener. I still only get new/changed alarms. With a normal subscription you also get the 'already existing' alarms. For reference;

` const aeConnection = await aeClient.connect(aeEndpointUrl); // TODO: Test if this is needed const aeSession = await aeClient.createSession2();

const subscription = await aeSession.createSubscription2({
    maxNotificationsPerPublish: 10,
    priority: 10,
    publishingEnabled: true,
    requestedLifetimeCount: 1000,
    requestedMaxKeepAliveCount: 47,
    requestedPublishingInterval: 1000,
});

const fields = [
    "EventId",
    "EventType",
    "SourceNode",
    "SourceName",
    "Time",
    "ReceiveTime",
    "Message",
    "Severity",

    // ConditionType
    "ConditionClassId",
    "ConditionClassName",
    "ConditionName",
    "BranchId",
    "Retain",
    "EnabledState",
    "Quality",
    "LastSeverity",
    "Comment",
    "ClientUserId",

    // AcknowledgeConditionType
    "AckedState",
    "ConfirmedState",

    // AlarmConditionType
    "ActiveState",
    "InputNode",
    "SuppressedState",

    "HighLimit",
    "LowLimit",
    "HighHighLimit",
    "LowLowLimit",

    "Value"
];

const eventFilter = constructEventFilter(fields, ofType("ConditionType"));

const result = await subscription.monitor({
    attributeId: AttributeIds.EventNotifier,
    nodeId: ObjectIds.Server
}, {
    samplingInterval: 1000,
    queueSize: 100000,
    discardOldest: false,

    filter: eventFilter,
}, TimestampsToReturn.Both)

result.on('changed', (value) => {
    console.log("new value " + value);
});

`

EDIT: I found it out, there is a refresh function

AndreasHeine commented 11 months ago

sorry forgot to mention that...

https://reference.opcfoundation.org/Core/Part9/v104/docs/5.5.7

ConditionRefresh allows a Client to request a Refresh of all Condition instances that currently are in an interesting state (they have the Retain flag set). This includes previous states of a Condition instance for which the Server maintains Branches.

InstantlyMoist commented 11 months ago

Awesome, finally getting somewhere :D

I'm now able to Acknowledge the alarms via the code which is an amazing step so far. Meaning I'm finally able to link the alarms to my app that I'm making. In case you're wondering, this is the code that I came up with:

` const subscription = await aeSession.createSubscription2({ maxNotificationsPerPublish: 10, priority: 10, publishingEnabled: true, requestedLifetimeCount: 1000, requestedMaxKeepAliveCount: 47, requestedPublishingInterval: 1000, });

const fields = [
    "EventId",
    "SourceNode",
    "SourceName",
    "ConditionId",
    "Message",
    "Severity",
    "AckedState",
    "ConfirmedState",
    "ActiveState",
];

const eventFilter = constructEventFilter(fields, ofType("ConditionType"));

const result = await subscription.monitor({
    attributeId: AttributeIds.EventNotifier,
    nodeId: ObjectIds.Server
}, {
    samplingInterval: 1000,
    queueSize: 100000,
    discardOldest: false,

    filter: eventFilter,
}, TimestampsToReturn.Both)

callConditionRefresh(subscription) // Give us the existing alarms!!!

result.on('changed', async (values) => {
    const sourceNode = values[1].value; 
    if (sourceNode == undefined) return; // Probably not relevant for us.

    const eventId = values[0].value;
    const sourceName = values[2].value;
    const conditionId = values[3].value;
    const message = values[4].value.text;
    const serverity = values[5].value;
    const ackedState = values[6].value.text;
    const confirmedState = values[7].value;
    const activeState = values[8].value.text; // Goes to Inactive when acknowledged and removed

    console.log(conditionId.value); 
    console.log(sourceName + " " + message + " " + serverity + " " + ackedState + " " + confirmedState + " " + activeState);

    const methodToCall = {
        objectId: conditionId,
        methodId: MethodIds.AcknowledgeableConditionType_Acknowledge, // TODO: Replace to MethodIds
        inputArguments: [
            new Variant({ dataType: DataType.ByteString, value: eventId }),
            new Variant({ dataType: DataType.LocalizedText, value: "test comment!" })
        ]
    }

    aeSession.call(methodToCall, (err, result) => {
        if (err) return; // Currently not important
        console.log("Method call result: " + JSON.stringify(result, null, 2));
    });
});

`

mikakaraila commented 11 months ago

Or this way: const status = await session.acknowledgeCondition(conditionId, eventId, comment);

erossignon commented 8 months ago

For the record a enhancement has been adede to the session.acknowledgeCondition method in node-opcua@2.119.0 that address the problem and fixes the behavior found with Cogent DataHub.