Skip to content
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

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

florianfischeer
Copy link

@florianfischeer florianfischeer commented Mar 3, 2021

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 of PublishedDataSetEventEntries. 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 call addEventToDataSetWriter. This method is new and it iterates over all DataSetFields of the given PublishedDataSet, wraps them into a DataValue and passes it to the insertDataValueIntoDSWQueue-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 by UA_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 and PublishedEventsType to Opc.Ua.NodeSet2.PubSubMinimal.xml and dependent nodes to Opc.Ua.NodeSet2.Reduced.xml.

There are a few points that we mention here that are currently in progress and not merged into this branch

@CLAassistant
Copy link

CLAassistant commented Mar 3, 2021

CLA assistant check
All committers have signed the CLA.

@jpfr
Copy link
Member

jpfr commented Mar 3, 2021

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?

@florianfischeer
Copy link
Author

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.

@andreasebner
Copy link
Contributor

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?

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.

@andreasebner
Copy link
Contributor

@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.

@jpfr
Copy link
Member

jpfr commented Mar 30, 2021

Looking good! Two quick questions:

  • Is the eventQueue protected against overflow? That is, if events are generated faster than we can send them out, where will things break?
  • You manage a global list of PublishedDataSetEventEntry.
    • Iterating over the global list for all events could become inefficient when the number of PublishedDataSetEventEntry gets large.
    • Does that cover Events that "blubble up" in the object hierarchy?
      You might want to reuse functionality from the Event-MonitoredItems for that.
      They are added to a linked-list of the node where they "collect" events bubbling up from below objects in the hierarchy.

Keep up the good work!

@florianfischeer
Copy link
Author

@jpfr
Thank you for the feedback.

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 inefficient

For 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.

Copy link
Contributor

@andreasebner andreasebner left a 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.

Comment on lines 4 to 5
* Copyright (c) 2021 Stefan Joachim Hahn, Technische Hochschule Mittelhessen
* Copyright (c) 2021 Florian Fischer, Technische Hochschule Mittelhessen
Copy link
Contributor

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.

Comment on lines 7 to 8
* Copyright (c) 2021 Stefan Joachim Hahn, Technische Hochschule Mittelhessen
* Copyright (c) 2021 Florian Fischer, Technische Hochschule Mittelhessen
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please unify the includes.

Comment on lines 76 to 89
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;

Copy link
Contributor

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);
Copy link
Contributor

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;
Copy link
Contributor

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;
Copy link
Contributor

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.

Copy link
Author

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.

Comment on lines +746 to +749
retVal |= UA_Server_addReference(server, publishedDataSet->identifier,
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PUBLISHEDEVENTSTYPE_MODIFYFIELDSELECTION), true);
Copy link
Contributor

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?

Copy link

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++;
Copy link
Contributor

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?

Copy link

@Tym0r Tym0r Apr 7, 2021

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.

#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
Copy link
Contributor

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.

Comment on lines 528 to 529
UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER,
"EventQueueMaxSize reached %lu / %lu", dsw->eventQueueEntries, dsw->eventQueueMaxSize);
Copy link
Contributor

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.

@AlexexanderSavinov
Copy link

This one is still open as i understand.
I am currently on master branch and use PubSub and need this changes.
Is there a plan when they are going to be included?

@CodingLappen
Copy link

CodingLappen commented Jun 23, 2021

I'd also like to know when this commit will be merged or if it will be merged.

@jpfr
Copy link
Member

jpfr commented Oct 5, 2021

@Florianfischer42 Are you still on this?
There are a few review comments from @andreasebner still unaddressed.

@applea9
Copy link
Contributor

applea9 commented Nov 22, 2021

I am also interested in this PR, is there any plan to merge this PR to master?

@wlkrm
Copy link

wlkrm commented Apr 5, 2022

I interested in this PR, too. Is there any plan to merge this PR to master? If you have
no plans in continuing this PR, I might address the unaddressed review comments, @florianfischeer ?

@jpfr
Copy link
Member

jpfr commented Apr 12, 2022

As @florianfischeer is not responsing, I think you can take this up @wlkrm !

@andreasebner andreasebner modified the milestone: 1.4 Apr 27, 2022
@andreasebner andreasebner self-assigned this Apr 27, 2022
@jpfr jpfr added the Status: Help wanted Looking for support from the community to implement/fix this issue. Also tag with 'help wanted' label Jan 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Component: PubSub Status: Help wanted Looking for support from the community to implement/fix this issue. Also tag with 'help wanted'
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants