From 26bec663d2ca88634dbbda2bd7f7952cd2d26efe Mon Sep 17 00:00:00 2001 From: Thibaut Vermeulen Date: Tue, 3 May 2022 22:12:25 +0200 Subject: [PATCH 1/2] control server for python: add control action handler for operate server --- .../eventHandlers/controlActionHandler.hpp | 407 ++++++++++++++++++ pyiec61850/eventHandlers/eventHandler.hpp | 4 +- pyiec61850/iec61850.i | 9 + 3 files changed, 417 insertions(+), 3 deletions(-) create mode 100644 pyiec61850/eventHandlers/controlActionHandler.hpp diff --git a/pyiec61850/eventHandlers/controlActionHandler.hpp b/pyiec61850/eventHandlers/controlActionHandler.hpp new file mode 100644 index 00000000..4b6913ea --- /dev/null +++ b/pyiec61850/eventHandlers/controlActionHandler.hpp @@ -0,0 +1,407 @@ +#ifndef PYIEC61850_CONTROLACTIONHANDLER_HPP +#define PYIEC61850_CONTROLACTIONHANDLER_HPP + +#include "eventHandler.hpp" + + + +/* + * Abstract class for processing the received 'Control' check events. + */ +class CheckHandlerForPython: public EventHandler { + public: + virtual ~CheckHandlerForPython() {} + + virtual void setReceivedData(void *i_data_p) + { + // copy the received data + ControlAction *l_my_data_p = static_cast(i_data_p); + _libiec61850_control_action = *l_my_data_p; + } + + void setMmsValue(MmsValue *i_data_value) + { + _libiec61850_mms_value = i_data_value; + } + + void setIedServer(IedServer *i_ied_server) + { + // copy the pointer and get to server + IedServer *l_my_server_p = i_ied_server; + _libiec61850_ied_server = *l_my_server_p; + } + + void setTest(bool i_test) + { + _libiec61850_test = i_test; + } + + void setInterlockCheck(bool i_interlock_check) + { + _libiec61850_interlock_check = i_interlock_check; + } + + CheckHandlerResult getCheckHandlerResult() + { + return _libiec61850_check_handler_result; + } + + CheckHandlerResult _libiec61850_check_handler_result; // Should be set during "trigger" callback function + ControlAction _libiec61850_control_action; + MmsValue* _libiec61850_mms_value; + IedServer _libiec61850_ied_server; + bool _libiec61850_test; + bool _libiec61850_interlock_check; +}; + + + +/* + * Abstract class for processing the received 'Control' wait for execution events. + */ +class WaitForExecutionHandlerForPython: public EventHandler { + public: + virtual ~WaitForExecutionHandlerForPython() {} + + virtual void setReceivedData(void *i_data_p) + { + // copy the received data + ControlAction *l_my_data_p = static_cast(i_data_p); + _libiec61850_control_action = *l_my_data_p; + } + + void setMmsValue(MmsValue *i_data_value) + { + _libiec61850_mms_value = i_data_value; + } + + void setIedServer(IedServer *i_ied_server) + { + // copy the pointer and get to server + IedServer *l_my_server_p = i_ied_server; + _libiec61850_ied_server = *l_my_server_p; + } + + void setTest(bool i_test) + { + _libiec61850_test = i_test; + } + + void setSynchroCheck(bool i_synchro_check) + { + _libiec61850_synchro_check = i_synchro_check; + } + + ControlHandlerResult getControlHandlerResult() + { + return _libiec61850_control_handler_result; + } + + ControlHandlerResult _libiec61850_control_handler_result; // Should be set during "trigger" callback function + ControlAction _libiec61850_control_action; + MmsValue* _libiec61850_mms_value; + IedServer _libiec61850_ied_server; + bool _libiec61850_test; + bool _libiec61850_synchro_check; + +}; + + + +/* + * Abstract class for processing the received 'Control' events. + */ +class ControlHandlerForPython: public EventHandler { + public: + virtual ~ControlHandlerForPython() {} + + virtual void setReceivedData(void *i_data_p) + { + // copy the received data + ControlAction *l_my_data_p = static_cast(i_data_p); + _libiec61850_control_action = *l_my_data_p; + } + + void setMmsValue(MmsValue *i_data_value) + { + _libiec61850_mms_value = i_data_value; + } + + void setIedServer(IedServer *i_ied_server) + { + // copy the pointer and get to server + IedServer *l_my_server_p = i_ied_server; + _libiec61850_ied_server = *l_my_server_p; + } + + void setTest(bool i_test) + { + _libiec61850_test = i_test; + } + + ControlHandlerResult getControlHandlerResult() + { + return _libiec61850_control_handler_result; + } + + ControlHandlerResult _libiec61850_control_handler_result; // Should be set during "trigger" callback function + ControlAction _libiec61850_control_action; + MmsValue* _libiec61850_mms_value; + IedServer _libiec61850_ied_server; + bool _libiec61850_test; +}; + + +/* + * Class for the subscription to the 'Control' events + */ +class ControlSubscriberForPython: public EventSubscriber { + public: + + ControlSubscriberForPython(): EventSubscriber() + { + m_ied_server = nullptr; + m_control_object = nullptr; + m_perform_check_handler_p = nullptr; + m_wait_handler_p = nullptr; + m_control_handler_p = nullptr; + } + + virtual ~ControlSubscriberForPython() { + if (m_perform_check_handler_p) { + delete m_perform_check_handler_p; + } + m_perform_check_handler_p = nullptr; + if (m_wait_handler_p) { + delete m_wait_handler_p; + } + m_wait_handler_p = nullptr; + if (m_control_handler_p) { + delete m_control_handler_p; + } + m_control_handler_p = nullptr; + } + + // Getters + CheckHandlerForPython *getCheckHandler() + { + return m_perform_check_handler_p; + } + + WaitForExecutionHandlerForPython *getWaitHandler() + { + return m_wait_handler_p; + } + + ControlHandlerForPython *getControlHandler() + { + return m_control_handler_p; + } + + // Subscriber: assign callbacks to IedServer + virtual bool subscribe() + { + // preconditions + if (nullptr == m_ied_server) { + fprintf(stderr, "ControlSubscriber::subscribe() failed: 'ied server' is null\n"); + return false; + } + if (nullptr == m_control_object) { + fprintf(stderr, "ControlSubscriber::subscribe() failed: 'control object' is null\n"); + return false; + } + + // install the libiec61850 callbacks for the different control states, if handler are set: + // the 'function pointer' is the 'static' method of this class + if (m_perform_check_handler_p) { + IedServer_setPerformCheckHandler(m_ied_server, + m_control_object, + (ControlPerformCheckHandler) ControlSubscriberForPython::triggerPerformCheckHandler, + &m_ied_server); + } + if (m_wait_handler_p){ + IedServer_setWaitForExecutionHandler(m_ied_server, + m_control_object, + (ControlWaitForExecutionHandler) ControlSubscriberForPython::triggerWaitForExecutionHandler, + &m_ied_server); + } + + if (m_control_handler_p) { + IedServer_setControlHandler(m_ied_server, + m_control_object, + (ControlHandler) ControlSubscriberForPython::triggerControlHandler, + &m_ied_server); + } + + char *l_object_ref = ModelNode_getObjectReference( (ModelNode*) m_control_object, NULL); + std::string objs(l_object_ref); + std::string dirs = std::string(ModelNode_getObjectReference( (ModelNode*) m_control_object, NULL)); + + return (EventSubscriber::registerNewSubscriber(this, l_object_ref)); + } + + + + // Static method: it is the control perform check 'callback' for libiec61850 in C + static CheckHandlerResult triggerPerformCheckHandler(ControlAction action, void* parameter, MmsValue* ctlVal, bool test, bool interlockCheck) + { + PyThreadStateLock PyThreadLock; + + // Preconditions + if (nullptr == parameter) { + fprintf(stderr, "ControlSubscriber::triggerPerformCheckHandler() failed: input object is null\n"); + return CONTROL_HARDWARE_FAULT; // Status may not + } + + DataObject* control_object = ControlAction_getControlObject(action); + std::string l_object_ref = ModelNode_getObjectReference( (ModelNode*) control_object, NULL); + + // Search the appropriate 'EventSubscriber' object + ControlSubscriberForPython *l_registered_subscriber = (ControlSubscriberForPython*) EventSubscriber::findSubscriber(l_object_ref); + + if (l_registered_subscriber) { + CheckHandlerForPython *l_perform_check_handler_p = l_registered_subscriber->getCheckHandler(); + if (l_perform_check_handler_p) { + l_perform_check_handler_p->setReceivedData(&action); + l_perform_check_handler_p->setMmsValue(ctlVal); + l_perform_check_handler_p->setIedServer((IedServer*) parameter); + l_perform_check_handler_p->setTest(test); + l_perform_check_handler_p->setInterlockCheck(interlockCheck); + l_perform_check_handler_p->trigger(); + fprintf(stderr, "triggerPerformCheckHandler::triggerPerformCheckHandler() end\n"); + + return l_perform_check_handler_p->getCheckHandlerResult(); + } + else { + fprintf(stderr, "ControlSubscriber::triggerPerformCheckHandler() failed: EventHandler is undefined\n"); + return CONTROL_HARDWARE_FAULT; + } + } + else { + fprintf(stderr, "ControlSubscriber::triggerPerformCheckHandler() failed: subscriber is not registered\n"); + return CONTROL_HARDWARE_FAULT; + } + + } + + // Static method: it is the control wait for execution 'callback' for libiec61850 in C + static ControlHandlerResult triggerWaitForExecutionHandler(ControlAction controlAction, void* parameter, MmsValue* value, bool test, bool synchroCheck) + { + PyThreadStateLock PyThreadLock; + + // Preconditions + if (nullptr == parameter) { + fprintf(stderr, "ControlSubscriber::triggerWaitForExecutionHandler() failed: input object is null\n"); + return CONTROL_RESULT_FAILED; + } + + DataObject* control_object = ControlAction_getControlObject(controlAction); + std::string l_object_ref = ModelNode_getObjectReference( (ModelNode*) control_object, NULL); + + // Search the appropriate 'EventSubscriber' object + ControlSubscriberForPython *l_registered_subscriber = (ControlSubscriberForPython*) EventSubscriber::findSubscriber(l_object_ref); + + if (l_registered_subscriber) { + WaitForExecutionHandlerForPython *l_wait_handler_p = l_registered_subscriber->getWaitHandler(); + if (l_wait_handler_p) { + l_wait_handler_p->setReceivedData(&controlAction); + l_wait_handler_p->setMmsValue(value); + l_wait_handler_p->setIedServer((IedServer*) parameter); + l_wait_handler_p->setTest(test); + l_wait_handler_p->setSynchroCheck(synchroCheck); + l_wait_handler_p->trigger(); + fprintf(stderr, "triggerWaitForExecutionHandler::triggerWaitForExecutionHandler() end\n"); + + return l_wait_handler_p->getControlHandlerResult(); + } + else { + fprintf(stderr, "ControlSubscriber::triggerWaitForExecutionHandler() failed: EventHandler is undefined\n"); + return CONTROL_RESULT_FAILED; + } + } + else { + fprintf(stderr, "ControlSubscriber::triggerWaitForExecutionHandler() failed: subscriber is not registered\n"); + return CONTROL_RESULT_FAILED; + } + + } + + + // Static method: it is the control 'callback' for libiec61850 in C + static ControlHandlerResult triggerControlHandler(ControlAction controlAction, void* parameter, MmsValue* value, bool test) + { + PyThreadStateLock PyThreadLock; + + // Preconditions + if (nullptr == parameter) { + fprintf(stderr, "ControlSubscriber::triggerControlHandler() failed: input object is null\n"); + return CONTROL_RESULT_FAILED; + } + + DataObject* control_object = ControlAction_getControlObject(controlAction); + std::string l_object_ref = ModelNode_getObjectReference( (ModelNode*) control_object, NULL); + + // Search the appropriate 'EventSubscriber' object + ControlSubscriberForPython *l_registered_subscriber = (ControlSubscriberForPython*) EventSubscriber::findSubscriber(l_object_ref); + + if (l_registered_subscriber) { + ControlHandlerForPython *l_control_handler_p = l_registered_subscriber->getControlHandler(); + if (l_control_handler_p) { + l_control_handler_p->setReceivedData(&controlAction); + l_control_handler_p->setMmsValue(value); + l_control_handler_p->setIedServer((IedServer*) parameter); + l_control_handler_p->setTest(test); + l_control_handler_p->trigger(); + fprintf(stderr, "triggerControlHandler::triggerControlHandler() end\n"); + + return l_control_handler_p->getControlHandlerResult(); + } + else { + fprintf(stderr, "ControlSubscriber::triggerControlHandler() failed: EventHandler is undefined\n"); + return CONTROL_RESULT_FAILED; + } + } + else { + fprintf(stderr, "ControlSubscriber::triggerControlHandler() failed: subscriber is not registered\n"); + return CONTROL_RESULT_FAILED; + } + + } + + // Setters + void setIedServer(const IedServer &i_ied_server) + { + m_ied_server = i_ied_server; + } + + void setControlObject(DataObject *i_control_object) + { + m_control_object = i_control_object; + } + + void setCheckHandler(CheckHandlerForPython* i_check_handler_p) + { + m_perform_check_handler_p = i_check_handler_p; + } + + void setWaitHandler(WaitForExecutionHandlerForPython* i_wait_handler_p) + { + m_wait_handler_p = i_wait_handler_p; + } + + void setControlHandler(ControlHandlerForPython* i_control_handler_p) + { + m_control_handler_p = i_control_handler_p; + } + + private: + // Parameters + IedServer m_ied_server; + DataObject* m_control_object; + CheckHandlerForPython* m_perform_check_handler_p; + WaitForExecutionHandlerForPython* m_wait_handler_p; + ControlHandlerForPython* m_control_handler_p; + +}; + +#endif diff --git a/pyiec61850/eventHandlers/eventHandler.hpp b/pyiec61850/eventHandlers/eventHandler.hpp index e69bf476..a93e15d5 100644 --- a/pyiec61850/eventHandlers/eventHandler.hpp +++ b/pyiec61850/eventHandlers/eventHandler.hpp @@ -6,7 +6,6 @@ #include #include - class PyThreadStateLock { public: @@ -25,7 +24,6 @@ private: }; - class EventHandler { public: EventHandler() {} @@ -118,7 +116,7 @@ class EventSubscriber { m_subscriber_map.erase(l_it); } else { - fprintf(stderr, "EventSubscriber::unregisterSubscriber() failed: '%s' is not registered\n"); + fprintf(stderr, "EventSubscriber::unregisterSubscriber() failed: '%s' is not registered\n", i_subscriber_id.c_str()); } } diff --git a/pyiec61850/iec61850.i b/pyiec61850/iec61850.i index 26f6655f..e4542544 100644 --- a/pyiec61850/iec61850.i +++ b/pyiec61850/iec61850.i @@ -26,6 +26,8 @@ DataAttribute* toDataAttribute(DataObject * DO) { return (DataAttribute*)DO;} DataAttribute* toDataAttribute(ModelNode * MN) { return (DataAttribute*)MN;} +DataObject* toDataObject(ModelNode * MN) +{ return (DataObject*)MN;} %} %apply int *OUTPUT {IedClientError* error}; @@ -55,6 +57,7 @@ ModelNode* toModelNode(LogicalNode *); ModelNode* toModelNode(DataObject *); DataAttribute* toDataAttribute(DataObject *); DataAttribute* toDataAttribute(ModelNode *); +DataObject* toDataObject(ModelNode *); char* toCharP(void *); /* Goose Subscriber section */ @@ -99,17 +102,23 @@ void GooseSubscriber_setDstMac(GooseSubscriber subscriber, %feature("director") RCBHandler; %feature("director") GooseHandler; %feature("director") CommandTermHandler; +%feature("director") CheckHandlerForPython; +%feature("director") WaitForExecutionHandlerForPython; +%feature("director") ControlHandlerForPython; %{ #include "eventHandlers/eventHandler.hpp" #include "eventHandlers/reportControlBlockHandler.hpp" #include "eventHandlers/gooseHandler.hpp" #include "eventHandlers/commandTermHandler.hpp" +#include "eventHandlers/controlActionHandler.hpp" std::map< std::string, EventSubscriber*> EventSubscriber::m_subscriber_map = {}; %} + %include "eventHandlers/eventHandler.hpp" %include "eventHandlers/reportControlBlockHandler.hpp" %include "eventHandlers/gooseHandler.hpp" %include "eventHandlers/commandTermHandler.hpp" +%include "eventHandlers/controlActionHandler.hpp" /* Goose Publisher section */ %{ From 8b43a644b13fdcc18463cee150eeb6613fc29ae7 Mon Sep 17 00:00:00 2001 From: Thibaut Vermeulen Date: Wed, 4 May 2022 18:23:39 +0200 Subject: [PATCH 2/2] python service wrapper: add state management for blocking IO in python --- pyiec61850/iec61850.i | 6 +++ pyiec61850/servicePythonWrapper.hpp | 63 +++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 pyiec61850/servicePythonWrapper.hpp diff --git a/pyiec61850/iec61850.i b/pyiec61850/iec61850.i index e4542544..254fec64 100644 --- a/pyiec61850/iec61850.i +++ b/pyiec61850/iec61850.i @@ -154,3 +154,9 @@ void CommParameters_setDstAddress(CommParameters *gooseCommParameters, uint8_t dst_mac_4, uint8_t dst_mac_5); + +/* Wrapper for synchronous functions */ +%{ +#include "servicePythonWrapper.hpp" +%} +%include "servicePythonWrapper.hpp" diff --git a/pyiec61850/servicePythonWrapper.hpp b/pyiec61850/servicePythonWrapper.hpp new file mode 100644 index 00000000..369e86c3 --- /dev/null +++ b/pyiec61850/servicePythonWrapper.hpp @@ -0,0 +1,63 @@ +#ifndef PYIEC61850_SERVICEPYTHONWRAPPER_HPP +#define PYIEC61850_SERVICEPYTHONWRAPPER_HPP + +#include "iec61850_client.h" +#include + + +class PyThreadStateSave +{ +public: + PyThreadStateSave(void) + { + if (PyGILState_Check()) + state = PyEval_SaveThread(); + else + state = nullptr; + } + + ~PyThreadStateSave(void) + { + if (state) + PyEval_RestoreThread(state); + } + +private: + PyThreadState* state; +}; + +/* +Wrapping of synchronous functions to prevent deadlocks +*/ +static MmsValue* +pyWrap_IedConnection_readObject(IedConnection con, IedClientError* error, const char* objRef, FunctionalConstraint fc) +{ + PyThreadStateSave gilStateSave; + return IedConnection_readObject(con, error, objRef, fc); +} + +static void +pyWrap_IedConnection_writeObject(IedConnection con, IedClientError* error, const char* objectReference, + FunctionalConstraint fc, MmsValue* value) +{ + PyThreadStateSave gilStateSave; + IedConnection_writeObject(con, error, objectReference, fc, value); +} + +static void +pyWrap_IedConnection_setRCBValues(IedConnection con, IedClientError* error, ClientReportControlBlock rcb, uint32_t parametersMask, + bool singleRequest) +{ + PyThreadStateSave gilStateSave; + IedConnection_setRCBValues(con, error, rcb, parametersMask, singleRequest); +} + +static ClientReportControlBlock +pyWrap_IedConnection_getRCBValues(IedConnection con, IedClientError* error, const char* rcbReference, + ClientReportControlBlock updateRcb) +{ + PyThreadStateSave gilStateSave; + return IedConnection_getRCBValues(con, error, rcbReference, updateRcb); +} + +#endif