Avatar

Please consider registering
guest

sp_LogInOut Log In sp_Registration Register

Register | Lost password?
Advanced Search

— Forum Scope —




— Match —





— Forum Options —





Minimum search word length is 3 characters - maximum search word length is 84 characters

No permission to create posts
sp_Feed Topic RSS sp_TopicIcon
help TCDHelpOffNormalAlarmTypeNode (subtype of HelpOffNormalAlarmType defined in OPC 40083)
February 3, 2023
17:15, EET
Avatar
Bjarne Boström
Moderator
Moderators
Forum Posts: 1026
Member Since:
April 3, 2012
sp_UserOfflineSmall Offline

Maybe there was a misunderstanding, but this is a somewhat complicated topic to explain, sorry. It is like 5 p.m. here, thus more complex explanations (yes, this is the “short version”) is skipped here. I might return to this next week, if you have more questions.

In short very many things in OPC UA are optional. OPC UA also constantly evolves and gets more features. No-one has implemented everything (including us, obviously it would be nice to get to that point eventually).

Technically, if you wish to implement something, you would list what Profiles and Facets you support in your application. They are listed in Part 7, but since 1.05 it doesn’t anymore contain the info, it is at https://profiles.opcfoundation.org/ (though you would anyway want to read this as the Part 7 was horror to navigate in sequential form). SDK-wise, we have a list https://www.prosysopc.com/products/opc-ua-java-sdk/technical-details/ (some require user implementation parts for stuff), except OPC Foundation updates them all the time (I think they do now have year as a “prefix/postfix” sometimes) so it is a bit of a mess, our page could use an update. However, I would assume most users would not approach it like this even though this would be the intended “OPC UA way” I believe even still of today.

So, there is no need “mandatory need” to support history at all in the first place. At least as far as I’m aware, not in the most common standard profiles. There might be a “history profile/facet”, which might specify that some parts are mandatory (though even profiles have optional components). So it kinda depends what your users want. Obviously it is nicer to support more, but since you basically never implement every possible feature (I have not yet seen a server that would support everything, including ourselves), it is a balancing act.

February 3, 2023
17:17, EET
Avatar
Bjarne Boström
Moderator
Moderators
Forum Posts: 1026
Member Since:
April 3, 2012
sp_UserOfflineSmall Offline

P.S.
The MyHistorian is just a sample, but should cover most common use-cases. But there are so many ways to use HistoryRead so it doesn’t cover all of them, thus why I linked the spec parts in case you wish to support more, as there are so many combinations of flags etc. (plus more if you use aggregates), that it is outside of the scope to try to explain everything here. However, SDK does have some help for Aggregates via AggregateCalculator, that is in the sample).

March 1, 2023
15:01, EET
Avatar
Francesco Zambon
Member
Members
Forum Posts: 83
Member Since:
December 20, 2021
sp_UserOfflineSmall Offline

Dear Bjarne,

please I need clarifications regarding the EventId attribute.

When I notify an alarm to clients with the method:

alarm.triggerEvent

a new EventId is generated.

Questions:
– EventId generated by the alarm.triggerEvent method, is it the same one used to identify the alarm status (com.prosysopc.ua.EventData)?

– Do I have to save the EventIds? If I restart the server and notify a new alarm, the EventId restarts from 0.

Thanks,
Francesco

March 1, 2023
16:05, EET
Avatar
Bjarne Boström
Moderator
Moderators
Forum Posts: 1026
Member Since:
April 3, 2012
sp_UserOfflineSmall Offline

Good question.

Short version:
Pass a non-null ‘userEventId’ ByteString for the triggerEvent(userEventId), if you need them to be unique even after restart, use something that is. Maybe UUID and e.g. something like https://www.baeldung.com/java-byte-array-to-uuid (maybe we should make some helpers in the SDK in the future to make this easier). NOTE the result of using the link might differ from the “OPC UA Guid encoding rules”, this is OK here, because it is just raw bytes. Technically it is possible to use BinaryEncoder of the SDK to encode any UA values, but also note that doing that might be more complex than the link. Also, if you pass in something “real data”, sanitize the inputs (or check BinaryDecoder outputs carefully) when the client calling Acknowledge.

Long version:
The return value (the ByteString) of triggerEvent is the same as EventData.getEventId(). Note that you will also input a ByteString userEventId to the method. SDK will concatenate that value to the one it internally generates for a “full event id”, except if you give null, then it will just use the one it generated internally. The generated one is just a long that is incremented for each event. You can extract your userEventId via static EventManager.extractUserEventId(full_EventId_here) (basically it just returns one without the first 8 bytes).

The idea is that you will most likely have some external system outside of the SDK that is generating the event or alarm. It might generate it’s own identifier. You would then use that somehow as the userEventId and SDK basically just ensures they are unique by prefixing it with the counter. Since the EventId comes back when the client acknowledges the alarms, you can feed this info back to the external system to do the real acknowledge.

As to the uniqueness. I’m not 100% sure. The specification does say in https://reference.opcfoundation.org/v104/Core/docs/Part5/6.4.2 “EventId is generated by the Server to uniquely identify a particular Event Notification. The Server is responsible to ensure that each Event has its unique EventId.” (Then it says that one way for this is to use a Guid). I could see 2 different interpretations (i.e. unique just for _that server_ to identify the event for the purposes of e.g. Acknowledge OR in general; a restarted server could be thought to be a different server).

I guess it matters more where is the real state of the alarm. If this would be just a simulation or a test server, maybe there is no true state for the alarm after the server is shut down, i.e. that would make all previous alarms meaningless. After it starts again, it would re-use the EventId. So I guess the only danger is then if a client reconnected, and there was an alarm acknowledge action queued in some way that the client is acknowledging an old alarm that has the same EventId as a new alarm, which could then potentially be missed by the user of the client. If this is a use-case where this can cause problems, I would recommend NOT using null as the userEventId and just using something else. If you have no external system etc. test the UUID mentioned in the short version.

P.S.
The SDK logic is very old, back then alarms were basically not used. Though, as far as I see, they are still very much underused part of OPC UA. Maybe the SDK logic of using the long increment should also change in some future version. For example, maybe SDK prefix logic should just already use a UUID (it is only 8 more bytes anyway to a total of 16 bytes).

March 1, 2023
18:34, EET
Avatar
Francesco Zambon
Member
Members
Forum Posts: 83
Member Since:
December 20, 2021
sp_UserOfflineSmall Offline

Dear Bjarne,

in order to retrieve the history of events, I am implementing the following method of the HistoryManagerListener interface:

public Object onReadEvents(ServiceContext serviceContext, Object operationContext, NodeId nodeId, UaNode node,
Object continuationPoint, DateTime startTime, DateTime endTime, UnsignedInteger numValuesPerNode,
EventFilter filter, HistoryEvent historyEvent)

.

I have already an external system outside of the SDK that is generating the event or alarm.
A SQL table with the following structure:
ID (long) – DEVICE (string) – STATUS (string) – TIME (date time) – SEVERITY (int)

I had thought of using the following method:

EventData com.prosysopc.ua.EventData.fromNodeHierarchy(UaNode node, ByteString eventId, NodeClass… excludeNodeClasses)

to create the historical data:

history.add(new HistoryEventFieldList(Variant.asObjectArray(eventData.getFieldValues(fieldPaths))));

Actually I’m not saving the EventIDs generated by trigger method.

Maybe I could use my table ID as EventID for trigger method and for fromNodeHierarchy method. What do you think about it?

Thanks,
Francesco

March 2, 2023
8:55, EET
Avatar
Bjarne Boström
Moderator
Moderators
Forum Posts: 1026
Member Since:
April 3, 2012
sp_UserOfflineSmall Offline

Most likely it is ok, maybe even a good idea, but this sort of starts to go beyond what I would say is within SDK-level support. That is to say, to give better suggestions we would need to know a lot more, and then it would be within the scopes of https://www.prosysopc.com/services/.

No permission to create posts
Forum Timezone: Europe/Helsinki

Most Users Ever Online: 1919

Currently Online:
33 Guest(s)

Currently Browsing this Page:
1 Guest(s)

Top Posters:

Heikki Tahvanainen: 402

hbrackel: 144

rocket science: 88

pramanj: 86

Francesco Zambon: 83

Ibrahim: 78

Sabari: 62

kapsl: 57

gjevremovic: 49

Xavier: 43

Member Stats:

Guest Posters: 0

Members: 734

Moderators: 7

Admins: 1

Forum Stats:

Groups: 3

Forums: 15

Topics: 1523

Posts: 6449

Newest Members:

christamcdowall, redaahern07571, nigelbdhmp, travistimmons, AnnelCib, dalenegettinger, howardkennerley, Thomassnism, biancacraft16, edgardo3518

Moderators: Jouni Aro: 1026, Pyry: 1, Petri: 0, Bjarne Boström: 1026, Jimmy Ni: 26, Matti Siponen: 346, Lusetti: 0

Administrators: admin: 1