Using notifications with C++
Use Case
The PLCnext Runtime System contains several services that notify of their own status. These notifications can be received by other services and are available for diagnose purposes, e. g. using the PLCnext Engineer.
For developers of C++ applications, this provides versatile opportunities:
- let your app react to changes in the PLC's status
- make your app detect a new PROFINET connection
- spot a wire that's connected to the wrong port
- ...and many, many more!
Of course, you can create and use your own notifications as well and use them throughout your applications.
Concept
For registering, sending and receiving notifications between components of a controller, the Notification Manager is used. The header files required for using the Notification Manager are provided via the PLCnext Technology SDK (see PLCnext CLI).
The SDK contains classes for the Notification Manager, classes for user-defined user data (payload) as well as the payload classes of the PLCnext Runtime System itself.
How to
If you want to use a class, integrate it into your program via an #include
command (e.g. #include <Arp/System/Nm/NotificationManager.hpp>
). Further information on the classes and their applications is available directly in the code commentary.
Defining the structure of user data
To enable correct interpretation of the user data of a notification, the structure of the user data has to be defined. Use Arp/System/Nm/SpecializedPayload.hpp for this. User data with up to 50 fields can be used as a character string.
Hide exampleClick to see a code example with two fields
// Example payload with two payload fields
#include "Arp/System/Nm/SpecializedPayload.hpp"
// Use the class name as template parameter for SpecializedPayload
class UserLoggedInPayload
: public Arp::System::Nm::SpecializedPayload<userloggedinpayload>
{
public:
// Inherit base class constructors
using SpecializedPayload::SpecializedPayload;
// Define a convenient constructor to create the payload
// This constructor will be called by a variadic template and perfect forwarding when
// a notification is sent. Therefore this constructor defines the signature of the
// function to send a notification.
// Pass a format string for a user visible representation of the payload to the base
// class' constructor. This format string will be used by the NotificationLogger to
// serialize the payload.
UserLoggedInPayload(const String& username, const String& permissions)
: SpecializedPayload("User logged in: {0} (permissions={1})")
{
// Store the values of the parameters in the underlying payload data structure
this->SetFieldvalue(this->fieldIndexUsername, username);
this->SetFieldvalue(this->fieldIndexPermissions, permissions);
}
// Add getters for a type-safe access to the payload fields
String GetUsername() const
{
return this->GetFieldValueAs<string>(this->fieldIndexUsername);
}
String GetPermissions() const
{
return this->GetFieldValueAs<string>(this->fieldIndexPermissions);
}
private:
// Define the fields of the payload type. Each field is accessed by an index.
// The type infomation is used to ensure only valid information is set to the
// payload fields.
// Use direct member initialization here to ensure these members are also initialized
// when the inherited constructors are used.
// The order of these declarations matters, so don't change them.
const size_t fieldIndexUsername = this->AddField<string>();
const size_t fieldIndexPermissions = this->AddField<string>();
};
Registering and sending notifications
Before a notification can be sent, it must be registered with the Notification Manager. If a component that registered a notification is no longer available, it has to unregister the notification first. Both operations are implemented using the NotificationRegistration
proxy object.
Hide exampleClick to see a code example
// in UserManager
NotificationManager& nm= NotificationManager::GetInstance();
auto UserLoggedInRegistration = nm.CreateNotificationRegistration<userloggedinpayload>("Arp.System.Um.Login",
"UserManager", Severity::Info);
UserLoggedInRegistration.SendNotification("hans", "admin");
// notification is deregistered automatically in destructor of UserLoggedInRegistration
Receiving notifications
To receive notifications, the recipient must subscribe to the notification name with the Notification Manager. ANotificationSubscriber
object is used for receiving notifications. If a recipient is no longer to receive notifications, it has to unsubscribe. To make sure this is done at the end of the recipient's life cycle, it is recommended to use a proxy object as it is described for registering of notifications.
Hide exampleClick to see a code example
// in eHMI
void HandleUserLogins(const Notification& notification)
{
auto payload = notification.GetPayloadAs<userloggedinpayload>();
// do something with payload.GetUsername() and payload.GetPermissions()
}
// in some long living object of eHMI
NotificationManager& nm = NotificationManager::GetInstance();
auto UserLoggedIn = nm.CreateNotificationSubscriber("Arp.System.Um.Login");
UserLoggedIn.OnNotification += make_delegate(HandleUserLogins);
// notification is unsubscribed automatically in destructor of UserLoggedIn
Querying information
The INotificationManagerInfo
interface is the interface for querying information from the Notification Manager. The following options are available:
GetNotificationName()
This method returns the notification name.GetNotificationNameId()
This method returns the ID of a notification name.GetNotificationRegistration()
This method returns the information about a notification provided during registration (see Metadata of a notification.)GetAllKnownNotificationNameIds()
This method returns a list ofNotificationNameIds
of all known notifications, independent of their status.GetNotificationsNameIdsByStatus()
This method returns a list ofNotificationNameIds
of notifications with a certain status.
Reference
Structure of a notification
Metadata of a notification
To register a notification, the sender must specify metadata information:
notificationName
The notification name defines under which name the notification is published. The parts of a name are separated by ".
" and, to the level of the component, should correspond to the name of the user component to be sent.
Users may define their own notifications in any namespace except those that are predefined by the PLCnext Runtime System which all derive from theArp
branch.
Note: Even if you're creating additional notifications for core components of the PLCnext Runtime, keep them in your own namespace. In further firmware releases there will be additional notifications implemented, so to avoid conflicts strictly do not use theArp
or theSecurity.Arp
namespace with your own notifications.senderName
Name of the component sending the notification.Severity
Information on the severity of the notification. Processing is not affected by the severity.-
Default
Information sent between devices -
Info
General information -
Warning
Warning for the user -
Error
Error without serious impacts -
Critical
Error with medium impact -
Fatal
Error with serious impact
-
PayloadTypeId
Unique identification for the user data type.NotificationNameId
is returned:
The ID is used for identifying the instance of a notification. It is required for sending and unregistering a notification.
The status of a notification registration can have the following states:
Subscribed
: A recipient subscribed but has not yet been registered by a component. Subscribing to a notification that has not yet been registered or already been unregistered is only permitted during startup of the firmware and the controller. Otherwise, an exception will be triggered.Registered
: Registered by a componentUnregistered
: Unregistered by a component
TheGetNotificationRegistration()
method returns the above meta information about a notification.