-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] feat(pubsub): Publishing of Events #4220
base: master
Are you sure you want to change the base?
Conversation
Very good feature and also code quality from a first look. @andreasebner can we green-light the public API before doing an in-depth review? |
Thanks for your comment on our pull request. After a meeting with @andreasebner we recognized, that there still need to be made some adjustments to match the feature with the specification. |
Yes, for sure, I will trigger you if we reach the final stage. The public API should not be changed a lot since events in PubSub are "only" another type of PublishedDataSets. |
@Florianfischer42 Please provide a description for this PR. The description should explain your work, refer to the relevant PubSub specification sections and list changes/additions on the public API. |
bc939f7
to
f42124d
Compare
b23ac69
to
86bc5dc
Compare
Looking good! Two quick questions:
Keep up the good work! |
@jpfr Is the eventQueue protected against overflow? That is if events are generated faster than we can send them out, where will things break?We added a basic overflow protection, which means if the queue is full or memory could not be allocated it will just fail to add more events and return an error code. Iterating over the global list could become inefficientFor now, a global list that contains event-only PDS is the most efficient way we could come up with. If you can suggest a better solution, please let us know. Are events bubbling up in the object hierarchy?That's a good point, bubbling up is now supported. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks’ for your great contribution!
I've added some open questions and mentioned some problems/formatting issues. In addition please clean up your commit history.
* Copyright (c) 2021 Stefan Joachim Hahn, Technische Hochschule Mittelhessen | ||
* Copyright (c) 2021 Florian Fischer, Technische Hochschule Mittelhessen |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Examples (CC licence) do not mention the autor (compare with other examples). This comment applys also to other files.
src/server/ua_subscription_events.c
Outdated
* Copyright (c) 2021 Stefan Joachim Hahn, Technische Hochschule Mittelhessen | ||
* Copyright (c) 2021 Florian Fischer, Technische Hochschule Mittelhessen |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please unify the includes.
selectedFields[0].typeDefinitionId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE); | ||
selectedFields[0].attributeId = UA_ATTRIBUTEID_VALUE; | ||
selectedFields[0].browsePathSize = 1; | ||
UA_QualifiedName browsePathMessage[1]; | ||
browsePathMessage[0] = UA_QUALIFIEDNAME(0, "Message"); | ||
selectedFields[0].browsePath = browsePathMessage; | ||
|
||
selectedFields[1].typeDefinitionId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE); | ||
selectedFields[1].attributeId = UA_ATTRIBUTEID_VALUE; | ||
selectedFields[1].browsePathSize = 1; | ||
UA_QualifiedName browsePathSeverity[1]; | ||
browsePathSeverity[0] = UA_QUALIFIEDNAME(0, "Severity"); | ||
selectedFields[1].browsePath = browsePathSeverity; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can move the repeating things to the for loop.
} | ||
|
||
/*********************** Event Methods ***********************/ | ||
void callEvent(UA_Server *server, void *data); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid forward declaration here and move the function to this location.
addDataSetFields(server); | ||
addWriterGroup(server); | ||
addDataSetWriter(server); | ||
const UA_Double interval = (UA_Double)5000; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please declare a define for this config parameter.
@@ -16,12 +16,24 @@ _UA_BEGIN_DECLS | |||
|
|||
#ifdef UA_ENABLE_PUBSUB /* conditional compilation */ | |||
|
|||
#ifdef UA_ENABLE_PUBSUB_EVENTS | |||
typedef struct PublishedDataSetEventEntry { | |||
UA_PublishedDataSet *pds; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the pds needed? The dws always reffer to a pds.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It isn't needed. We added it, to improve runtime. The PublishedDataSet can't be accessed directly from a DSW, it only offers the NodeId of a PDS. The algorithm to find the PDS (UA_PublishedDataSet_findPDSbyId) iterates over all PDSs, this would affect the efficiency.
retVal |= UA_Server_addReference(server, publishedDataSet->identifier, | ||
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), | ||
UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PUBLISHEDEVENTSTYPE_MODIFYFIELDSELECTION), true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you currently did not add the implementation of the 'MODIFYFIELDSELECTION' function, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, it's still on the todo list. I commented it out with a todo notice. Probably makes sense to change the event config from DSF to PDS before implementing this method.
} else if(newField->config.dataSetFieldType == UA_PUBSUB_DATASETFIELD_EVENT){ | ||
if(newField->config.field.events.promotedField) | ||
currentDataSet->promotedFieldsCount++; | ||
currentDataSet->fieldSize++; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it necessary to call addDataSetField after creating an Event-PDS?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently yes, it sets the correct PDS fieldSize, the promotedFields and generates the FieldMetaData. It wouldn't be too difficult to rewrite it to PDS level though.
src/pubsub/ua_pubsub_writer.c
Outdated
#ifdef UA_ENABLE_PUBSUB_EVENTS | ||
if(currentDataSetContext->config.publishedDataSetType == UA_PUBSUB_DATASET_PUBLISHEDEVENTS){ | ||
SIMPLEQ_INIT(&newDataSetWriter->eventQueue); // only needs to be initalized if the linked publishedDataSet is for event | ||
newDataSetWriter->eventQueueMaxSize = 1000; //test value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This parameter should be more visible.
src/server/ua_subscription_events.c
Outdated
UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, | ||
"EventQueueMaxSize reached %lu / %lu", dsw->eventQueueEntries, dsw->eventQueueMaxSize); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a warning is enough. Just drop the new field or remove the oldest entry and add this new one.
- bubbling-up - moves eventQueueMaxSize to config - removes reference to unimplemented ModifyFieldSelection - adds comments for the global event-PDS list
This one is still open as i understand. |
I'd also like to know when this commit will be merged or if it will be merged. |
@Florianfischer42 Are you still on this? |
I am also interested in this PR, is there any plan to merge this PR to master? |
I interested in this PR, too. Is there any plan to merge this PR to master? If you have |
As @florianfischeer is not responsing, I think you can take this up @wlkrm ! |
This pull request adds the publishing of events to the PubSub part of the stack.
We added a cmake-flag, which is called
UA_ENABLE_PUBSUB_EVENTS
, a tutorial for publishing and another one for subscribing.Concept
In the PubSubManager we added a list that saves all PublishedDataSets of the type
UA_PUBSUB_DATASET_PUBLISHEDEVENTS
with their associated DataSetWriters. Besides, a DataSetWriter now contains a queue in which the information of the events is saved and then sent after the publishing interval has expired. When creating a DataSetWriter, if the associated PublishedDataSet is of the mentioned type, both are added to the PubSubManager list. This list helps us to recognize when an event has occurred whether it belongs to a PublishedDataSet. If so, the DataSetFields are added to the queue of the DataSetWriter belonging to the PublishedDataSet.Specific Changes
Added structs
To implement our concept, we had to create some new structs. First, we needed a
UA_DataSetEventConfig
, because there was only a placeholder in the UA_DataSetFieldConfig. From our point of view, an EventDataSetField contains a selectedField, a fieldname and a boolean, which shows, whether it's a promoted field. Also, we added a pointer of selectedFields to the UA_PublishedEventConfig.Moreover, we needed some structs to implement the Queue in the DataSetWriter and the list in the PubSubManager. The Queue in the DataSetWriter consists of
EventQueueEntries
, which are only a wrapper for DataValues. The list of the PubSubManager on the other hand consists ofPublishedDataSetEventEntries
. This struct contains a field for a DataSetWriter and one for its connected PublishedDataSet.Changed Methods
For our implementation, the most important method is
UA_Server_triggerEvent
, because it's necessary to call it if you want to trigger an event. In this method, we check, whether the triggered Event matches with a PublishedDataSet and if so we calladdEventToDataSetWriter
. This method is new and it iterates over all DataSetFields of the given PublishedDataSet, wraps them into a DataValue and passes it to theinsertDataValueIntoDSWQueue
-method. In here the passed DataValue is added to the Queue in the passed DataSetWriter.If the publishing interval expired and a KeyFrameMessage needs to be created, we fill the DataSetMessage with the DataValues in the Queue. After this, the Queue is cleared and all information is published.
Additions to the Informationmodel
We added functionality to support PublishedDataSets of the PublishedEventsType in the information model. Most of the functionality is implemented analogue to the PublishedDataItemsType.
Added Methods
The first method we added is
addPublishedEventsRepresentation
which is called every time a PDS is created byUA_Server_addPublishedDataSet
, provided that the information model is enabled and the created PDS is of the PublishedEventsType. This method adds the required nodes to represent a PublishedEventsType Object to the information model.We also implemented the
addPublishedEvents
method for the DataSetFolderType (9.1.4.5.3) which, when called, creates a PDS of PublishedEventsType.Added to the NodeSet files
To fully support the PublishedEventsType even when running at reduced ns0 we had to add a few nodes to the NodeSet files. More precisely we added
AddPublishedEvents
andPublishedEventsType
to Opc.Ua.NodeSet2.PubSubMinimal.xml and dependent nodes to Opc.Ua.NodeSet2.Reduced.xml.