17:15, EET
April 3, 2012
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.
17:17, EET
April 3, 2012
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).
15:01, EET
December 20, 2021
Dear Bjarne,
please I need clarifications regarding the EventId attribute.
When I notify an alarm to clients with the method:
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
16:05, EET
April 3, 2012
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).
18:34, EET
December 20, 2021
Dear Bjarne,
in order to retrieve the history of events, I am implementing the following method of the HistoryManagerListener interface:
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:
to create the historical data:
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
8:55, EET
April 3, 2012
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/.
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, edgardo3518Moderators: Jouni Aro: 1026, Pyry: 1, Petri: 0, Bjarne Boström: 1026, Jimmy Ni: 26, Matti Siponen: 346, Lusetti: 0
Administrators: admin: 1