From da9b77af7a1c790238e348ae5376729154ad8431 Mon Sep 17 00:00:00 2001 From: Mikael Bourhis Date: Tue, 2 Feb 2021 10:59:28 +0100 Subject: [PATCH 1/3] Python wrapper: add handler class for the reception of CommandTermination events --- .../eventHandlers/commandTermHandler.hpp | 68 +++++++++++++++++++ pyiec61850/iec61850.i | 3 + 2 files changed, 71 insertions(+) create mode 100644 pyiec61850/eventHandlers/commandTermHandler.hpp diff --git a/pyiec61850/eventHandlers/commandTermHandler.hpp b/pyiec61850/eventHandlers/commandTermHandler.hpp new file mode 100644 index 00000000..2595f15e --- /dev/null +++ b/pyiec61850/eventHandlers/commandTermHandler.hpp @@ -0,0 +1,68 @@ +#ifndef PYIEC61850_COMMANDTERMHANDLER_HPP +#define PYIEC61850_COMMANDTERMHANDLER_HPP + +#include "eventHandler.hpp" + +/* + * Abstract class for processing the received 'Command Termination' events. + */ +class CommandTermHandler: public EventHandler { + public: + virtual ~CommandTermHandler() {} + + virtual void setReceivedData(void *i_data_p) + { + // copy the received data + ControlObjectClient *l_my_data_p = static_cast(i_data_p); + _libiec61850_control_object_client = *l_my_data_p; + } + + ControlObjectClient _libiec61850_control_object_client; +}; + + +/* + * Class for the subscription to the 'Command Termination' events + */ +class CommandTermSubscriber: public EventSubscriber { + public: + + virtual void subscribe() { + // install the libiec61850 callback: + // the 'function pointer' is the 'static' method of this class + ControlObjectClient_setCommandTerminationHandler( + m_libiec61850_control_object_client, + CommandTermSubscriber::triggerCommandTermHandler, + NULL); + } + + // Static method: it is the 'callback' for libiec61850 in C + static void triggerCommandTermHandler(void *parameter, ControlObjectClient connection) + { + PyThreadStateLock PyThreadLock; + + // TODO: search the appropriate 'EventSubscriber' object + if (m_last_created_event_subscriber) { + EventHandler *l_event_handler_p = m_last_created_event_subscriber->getEventHandler(); + if (l_event_handler_p) { + l_event_handler_p->setReceivedData(&connection); + l_event_handler_p->trigger(); + } + else { + printf("The EventHandler is undefined\n"); + } + } + } + + // Setters + void setLibiec61850ControlObjectClient(const ControlObjectClient &i_libiec61850_control_object_client) + { + m_libiec61850_control_object_client = i_libiec61850_control_object_client; + } + + protected: + // Parameters + ControlObjectClient m_libiec61850_control_object_client; +}; + +#endif diff --git a/pyiec61850/iec61850.i b/pyiec61850/iec61850.i index 71aceaef..64cd3298 100644 --- a/pyiec61850/iec61850.i +++ b/pyiec61850/iec61850.i @@ -98,15 +98,18 @@ void GooseSubscriber_setDstMac(GooseSubscriber subscriber, /* Event Handler section */ %feature("director") RCBHandler; %feature("director") GooseHandler; +%feature("director") CommandTermHandler; %{ #include "eventHandlers/eventHandler.hpp" #include "eventHandlers/reportControlBlockHandler.hpp" #include "eventHandlers/gooseHandler.hpp" +#include "eventHandlers/commandTermHandler.hpp" EventSubscriber* EventSubscriber::m_last_created_event_subscriber = nullptr; %} %include "eventHandlers/eventHandler.hpp" %include "eventHandlers/reportControlBlockHandler.hpp" %include "eventHandlers/gooseHandler.hpp" +%include "eventHandlers/commandTermHandler.hpp" /* Goose Publisher section */ %{ From b1fc481ab88e0c771e93e42cb112ee680079891c Mon Sep 17 00:00:00 2001 From: Mikael Bourhis Date: Wed, 3 Feb 2021 14:37:47 +0100 Subject: [PATCH 2/3] Python wrapper: add some quality improvements --- .../eventHandlers/commandTermHandler.hpp | 26 ++++++++++++++++-- pyiec61850/eventHandlers/gooseHandler.hpp | 27 +++++++++++++++++-- .../reportControlBlockHandler.hpp | 24 ++++++++++++++++- 3 files changed, 72 insertions(+), 5 deletions(-) diff --git a/pyiec61850/eventHandlers/commandTermHandler.hpp b/pyiec61850/eventHandlers/commandTermHandler.hpp index 2595f15e..2c91e338 100644 --- a/pyiec61850/eventHandlers/commandTermHandler.hpp +++ b/pyiec61850/eventHandlers/commandTermHandler.hpp @@ -26,8 +26,21 @@ class CommandTermHandler: public EventHandler { */ class CommandTermSubscriber: public EventSubscriber { public: + CommandTermSubscriber(): EventSubscriber() + { + m_libiec61850_control_object_client = nullptr; + } + + virtual ~CommandTermSubscriber() {} + + virtual void subscribe() + { + // preconditions + if (nullptr == m_libiec61850_control_object_client) { + fprintf(stderr, "CommandTermSubscriber::subscribe() failed: 'control object client' is null\n"); + return; + } - virtual void subscribe() { // install the libiec61850 callback: // the 'function pointer' is the 'static' method of this class ControlObjectClient_setCommandTerminationHandler( @@ -41,6 +54,12 @@ class CommandTermSubscriber: public EventSubscriber { { PyThreadStateLock PyThreadLock; + // Preconditions + if (nullptr == connection) { + fprintf(stderr, "CommandTermSubscriber::triggerCommandTermHandler() failed: input object is null\n"); + return; + } + // TODO: search the appropriate 'EventSubscriber' object if (m_last_created_event_subscriber) { EventHandler *l_event_handler_p = m_last_created_event_subscriber->getEventHandler(); @@ -49,9 +68,12 @@ class CommandTermSubscriber: public EventSubscriber { l_event_handler_p->trigger(); } else { - printf("The EventHandler is undefined\n"); + fprintf(stderr, "CommandTermSubscriber::triggerCommandTermHandler() failed: EventHandler is undefined\n"); } } + else { + fprintf(stderr, "CommandTermSubscriber::triggerCommandTermHandler() failed: subscriber is not registered\n"); + } } // Setters diff --git a/pyiec61850/eventHandlers/gooseHandler.hpp b/pyiec61850/eventHandlers/gooseHandler.hpp index a01d3a6a..f0b4f1d2 100644 --- a/pyiec61850/eventHandlers/gooseHandler.hpp +++ b/pyiec61850/eventHandlers/gooseHandler.hpp @@ -22,8 +22,22 @@ class GooseHandler: public EventHandler { class GooseSubscriberForPython: public EventSubscriber { public: + GooseSubscriberForPython(): EventSubscriber() + { + m_libiec61850_goose_subscriber = nullptr; + } + + virtual ~GooseSubscriberForPython() {} + + + virtual void subscribe() + { + // preconditions + if (nullptr == m_libiec61850_goose_subscriber) { + fprintf(stderr, "GooseSubscriberForPython::subscribe() failed: 'GOOSE subscriber' is null\n"); + return; + } - virtual void subscribe() { // install the libiec61850 callback: // the 'function pointer' is the 'static' method of this class GooseSubscriber_setListener(m_libiec61850_goose_subscriber, @@ -36,6 +50,12 @@ class GooseSubscriberForPython: public EventSubscriber { { PyThreadStateLock PyThreadLock; + // Preconditions + if (nullptr == subscriber) { + fprintf(stderr, "GooseSubscriberForPython::triggerGooseHandler() failed: input object is null\n"); + return; + } + // TODO: search the appropriate 'EventSubscriber' object if (m_last_created_event_subscriber) { EventHandler *l_event_handler_p = m_last_created_event_subscriber->getEventHandler(); @@ -44,9 +64,12 @@ class GooseSubscriberForPython: public EventSubscriber { l_event_handler_p->trigger(); } else { - printf("The EventHandler is undefined\n"); + fprintf(stderr, "GooseSubscriberForPython::triggerGooseHandler() failed: EventHandler is undefined\n"); } } + else { + fprintf(stderr, "GooseSubscriberForPython::triggerGooseHandler() failed: subscriber is not registered\n"); + } } // Setters diff --git a/pyiec61850/eventHandlers/reportControlBlockHandler.hpp b/pyiec61850/eventHandlers/reportControlBlockHandler.hpp index ad798c1d..13d71e9d 100644 --- a/pyiec61850/eventHandlers/reportControlBlockHandler.hpp +++ b/pyiec61850/eventHandlers/reportControlBlockHandler.hpp @@ -22,8 +22,21 @@ class RCBHandler: public EventHandler { class RCBSubscriber: public EventSubscriber { public: + RCBSubscriber(): EventSubscriber() + { + m_ied_connection = nullptr; + } + + virtual ~RCBSubscriber() {} + virtual void subscribe() { + // preconditions + if (nullptr == m_ied_connection) { + fprintf(stderr, "RCBSubscriber::subscribe() failed: 'IedConnection' is null\n"); + return; + } + // install the libiec61850 callback: // the 'function pointer' is the 'static' method of this class IedConnection_installReportHandler(m_ied_connection, @@ -38,6 +51,12 @@ class RCBSubscriber: public EventSubscriber { { PyThreadStateLock PyThreadLock; + // Preconditions + if (nullptr == report) { + fprintf(stderr, "RCBSubscriber::triggerRCBHandler() failed: input object is null\n"); + return; + } + // TODO: search the appropriate 'EventSubscriber' object if (m_last_created_event_subscriber) { EventHandler *l_event_handler_p = m_last_created_event_subscriber->getEventHandler(); @@ -46,9 +65,12 @@ class RCBSubscriber: public EventSubscriber { l_event_handler_p->trigger(); } else { - printf("The EventHandler is undefined\n"); + fprintf(stderr, "RCBSubscriber::triggerRCBHandler() failed: EventHandler is undefined\n"); } } + else { + fprintf(stderr, "RCBSubscriber::triggerRCBHandler() failed: subscriber is not registered\n"); + } } // Setters From e47601a81e5cb0a2fa0a1c49f6800a2579bd96ef Mon Sep 17 00:00:00 2001 From: Mikael Bourhis Date: Wed, 3 Feb 2021 14:46:50 +0100 Subject: [PATCH 3/3] Python wrapper: about the wrapped callbacks, maintain a 'map' of subscribers The 'callback' function is a part of the 'Subscriber' class. Once the 'event' (or asynchronous message) is received, the 'Subscriber' object forwards the data to the 'Handler' object. With this approach, the 'event' processing algorithm can be defined in a Python subclass by the user. Each 'subscriber' class has a function that matches the C function pointer. But this function is a static method, shared with all instances of the class. In order to dispatch the message/data to the right instance, we maintain a dictionary of instantiated objects. Therfore, we have added the following internal services : * bool registerNewSubscriber(EventSubscriber*, id); * EventSubscriber* findSubscriber(id); * void unregisterSubscriber(id); --- .../eventHandlers/commandTermHandler.hpp | 17 ++++-- pyiec61850/eventHandlers/eventHandler.hpp | 60 +++++++++++++++++-- pyiec61850/eventHandlers/gooseHandler.hpp | 18 ++++-- .../reportControlBlockHandler.hpp | 16 +++-- pyiec61850/iec61850.i | 2 +- 5 files changed, 89 insertions(+), 24 deletions(-) diff --git a/pyiec61850/eventHandlers/commandTermHandler.hpp b/pyiec61850/eventHandlers/commandTermHandler.hpp index 2c91e338..64fb6e38 100644 --- a/pyiec61850/eventHandlers/commandTermHandler.hpp +++ b/pyiec61850/eventHandlers/commandTermHandler.hpp @@ -33,12 +33,12 @@ class CommandTermSubscriber: public EventSubscriber { virtual ~CommandTermSubscriber() {} - virtual void subscribe() + virtual bool subscribe() { // preconditions if (nullptr == m_libiec61850_control_object_client) { fprintf(stderr, "CommandTermSubscriber::subscribe() failed: 'control object client' is null\n"); - return; + return false; } // install the libiec61850 callback: @@ -47,6 +47,10 @@ class CommandTermSubscriber: public EventSubscriber { m_libiec61850_control_object_client, CommandTermSubscriber::triggerCommandTermHandler, NULL); + + std::string l_object_ref = ControlObjectClient_getObjectReference(m_libiec61850_control_object_client); + + return (EventSubscriber::registerNewSubscriber(this, l_object_ref)); } // Static method: it is the 'callback' for libiec61850 in C @@ -60,9 +64,12 @@ class CommandTermSubscriber: public EventSubscriber { return; } - // TODO: search the appropriate 'EventSubscriber' object - if (m_last_created_event_subscriber) { - EventHandler *l_event_handler_p = m_last_created_event_subscriber->getEventHandler(); + // Search the appropriate 'EventSubscriber' object + std::string l_subscriber_id = ControlObjectClient_getObjectReference(connection); + EventSubscriber *l_registered_subscriber = EventSubscriber::findSubscriber(l_subscriber_id); + + if (l_registered_subscriber) { + EventHandler *l_event_handler_p = l_registered_subscriber->getEventHandler(); if (l_event_handler_p) { l_event_handler_p->setReceivedData(&connection); l_event_handler_p->trigger(); diff --git a/pyiec61850/eventHandlers/eventHandler.hpp b/pyiec61850/eventHandlers/eventHandler.hpp index 1ef20f7b..e69bf476 100644 --- a/pyiec61850/eventHandlers/eventHandler.hpp +++ b/pyiec61850/eventHandlers/eventHandler.hpp @@ -28,6 +28,7 @@ private: class EventHandler { public: + EventHandler() {} virtual ~EventHandler() {} virtual void setReceivedData(void *i_data_p) = 0; virtual void trigger() = 0; @@ -36,13 +37,9 @@ class EventHandler { class EventSubscriber { public: - // TODO: use a map to store and find the instantiated EventSubscriber - static EventSubscriber* m_last_created_event_subscriber; EventSubscriber(): _event_handler_p(nullptr) { - m_last_created_event_subscriber = this; - // add python thread support Py_Initialize(); PyEval_InitThreads(); @@ -50,11 +47,11 @@ class EventSubscriber { virtual ~EventSubscriber() { + EventSubscriber::unregisterSubscriber(m_subscriber_id); deleteEventHandler(); - m_last_created_event_subscriber = nullptr; } - virtual void subscribe() = 0; + virtual bool subscribe() = 0; void deleteEventHandler() { @@ -75,8 +72,59 @@ class EventSubscriber { return _event_handler_p; } + void setSubscriberId(const std::string &i_id) + { + m_subscriber_id = i_id; + } + + protected: + static std::map m_subscriber_map; + + static bool registerNewSubscriber(EventSubscriber *i_new_subscriber, const std::string &i_id) + { + // Preconditions + if (i_id.empty()) { + fprintf(stderr, "EventSubscriber::subscribe() failed: the subscriber id is empty\n"); + return false; + } + if (m_subscriber_map.end() != m_subscriber_map.find(i_id)) { + fprintf(stderr, "EventSubscriber::subscribe() failed: the subscriber is already registered\n"); + return false; + } + + m_subscriber_map[i_id] = i_new_subscriber; + i_new_subscriber->setSubscriberId(i_id); + + return true; + } + + static EventSubscriber* findSubscriber(const std::string &i_id) + { + EventSubscriber *o_found_event_subscriber_p = nullptr; + std::map::iterator l_it = m_subscriber_map.find(i_id); + + if (m_subscriber_map.end() != l_it) { + o_found_event_subscriber_p = l_it->second; + } + + return o_found_event_subscriber_p; + } + + static void unregisterSubscriber(const std::string &i_subscriber_id) + { + std::map::iterator l_it = m_subscriber_map.find(i_subscriber_id); + + if (m_subscriber_map.end() != l_it) { + m_subscriber_map.erase(l_it); + } + else { + fprintf(stderr, "EventSubscriber::unregisterSubscriber() failed: '%s' is not registered\n"); + } + } + private: EventHandler *_event_handler_p; + std::string m_subscriber_id; }; #endif diff --git a/pyiec61850/eventHandlers/gooseHandler.hpp b/pyiec61850/eventHandlers/gooseHandler.hpp index f0b4f1d2..5984d478 100644 --- a/pyiec61850/eventHandlers/gooseHandler.hpp +++ b/pyiec61850/eventHandlers/gooseHandler.hpp @@ -29,13 +29,12 @@ class GooseSubscriberForPython: public EventSubscriber { virtual ~GooseSubscriberForPython() {} - - virtual void subscribe() + virtual bool subscribe() { // preconditions if (nullptr == m_libiec61850_goose_subscriber) { fprintf(stderr, "GooseSubscriberForPython::subscribe() failed: 'GOOSE subscriber' is null\n"); - return; + return false; } // install the libiec61850 callback: @@ -43,6 +42,10 @@ class GooseSubscriberForPython: public EventSubscriber { GooseSubscriber_setListener(m_libiec61850_goose_subscriber, GooseSubscriberForPython::triggerGooseHandler, NULL); + + std::string l_go_cb_ref = GooseSubscriber_getGoCbRef(m_libiec61850_goose_subscriber); + + return (EventSubscriber::registerNewSubscriber(this, l_go_cb_ref)); } // Static method: it is the 'callback' for libiec61850 in C @@ -56,9 +59,12 @@ class GooseSubscriberForPython: public EventSubscriber { return; } - // TODO: search the appropriate 'EventSubscriber' object - if (m_last_created_event_subscriber) { - EventHandler *l_event_handler_p = m_last_created_event_subscriber->getEventHandler(); + // Search the appropriate 'EventSubscriber' object + std::string l_subscriber_id = GooseSubscriber_getGoCbRef(subscriber); + EventSubscriber *l_registered_subscriber = EventSubscriber::findSubscriber(l_subscriber_id); + + if (l_registered_subscriber) { + EventHandler *l_event_handler_p = l_registered_subscriber->getEventHandler(); if (l_event_handler_p) { l_event_handler_p->setReceivedData(&subscriber); l_event_handler_p->trigger(); diff --git a/pyiec61850/eventHandlers/reportControlBlockHandler.hpp b/pyiec61850/eventHandlers/reportControlBlockHandler.hpp index 13d71e9d..25411ae3 100644 --- a/pyiec61850/eventHandlers/reportControlBlockHandler.hpp +++ b/pyiec61850/eventHandlers/reportControlBlockHandler.hpp @@ -29,12 +29,11 @@ class RCBSubscriber: public EventSubscriber { virtual ~RCBSubscriber() {} - - virtual void subscribe() { + virtual bool subscribe() { // preconditions if (nullptr == m_ied_connection) { fprintf(stderr, "RCBSubscriber::subscribe() failed: 'IedConnection' is null\n"); - return; + return false; } // install the libiec61850 callback: @@ -44,6 +43,8 @@ class RCBSubscriber: public EventSubscriber { m_rcb_rpt_id.c_str(), RCBSubscriber::triggerRCBHandler, NULL); + + return (EventSubscriber::registerNewSubscriber(this, m_rcb_reference)); } // Static method: it is the 'callback' for libiec61850 in C @@ -57,9 +58,12 @@ class RCBSubscriber: public EventSubscriber { return; } - // TODO: search the appropriate 'EventSubscriber' object - if (m_last_created_event_subscriber) { - EventHandler *l_event_handler_p = m_last_created_event_subscriber->getEventHandler(); + // Search the appropriate 'EventSubscriber' object + std::string l_subscriber_id = ClientReport_getRcbReference(report); + EventSubscriber *l_registered_subscriber = EventSubscriber::findSubscriber(l_subscriber_id); + + if (l_registered_subscriber) { + EventHandler *l_event_handler_p = l_registered_subscriber->getEventHandler(); if (l_event_handler_p) { l_event_handler_p->setReceivedData(&report); l_event_handler_p->trigger(); diff --git a/pyiec61850/iec61850.i b/pyiec61850/iec61850.i index 64cd3298..26f6655f 100644 --- a/pyiec61850/iec61850.i +++ b/pyiec61850/iec61850.i @@ -104,7 +104,7 @@ void GooseSubscriber_setDstMac(GooseSubscriber subscriber, #include "eventHandlers/reportControlBlockHandler.hpp" #include "eventHandlers/gooseHandler.hpp" #include "eventHandlers/commandTermHandler.hpp" -EventSubscriber* EventSubscriber::m_last_created_event_subscriber = nullptr; +std::map< std::string, EventSubscriber*> EventSubscriber::m_subscriber_map = {}; %} %include "eventHandlers/eventHandler.hpp" %include "eventHandlers/reportControlBlockHandler.hpp"