diff --git a/examples/server_example_basic_io/server_example_basic_io.c b/examples/server_example_basic_io/server_example_basic_io.c index 4966f33d..d2fc9279 100644 --- a/examples/server_example_basic_io/server_example_basic_io.c +++ b/examples/server_example_basic_io/server_example_basic_io.c @@ -136,6 +136,12 @@ main(int argc, char** argv) IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL); + /* By default access to variables with FC=DC and FC=CF is not allowed. + * This allow to write to simpleIOGenericIO/GGIO1.NamPlt.vendor variable used + * by iec61850_client_example1. + */ + IedServer_setWriteAccessPolicy(iedServer, IEC61850_FC_DC, ACCESS_POLICY_ALLOW); + /* MMS server will be instructed to start listening for client connections. */ IedServer_start(iedServer, 102); diff --git a/pyiec61850/eventHandlers/eventHandler.hpp b/pyiec61850/eventHandlers/eventHandler.hpp new file mode 100644 index 00000000..1ef20f7b --- /dev/null +++ b/pyiec61850/eventHandlers/eventHandler.hpp @@ -0,0 +1,82 @@ +#ifndef PYIEC61850_EVENTHANDLER_HPP +#define PYIEC61850_EVENTHANDLER_HPP + + +#include "iec61850_client.h" +#include +#include + + +class PyThreadStateLock +{ +public: + PyThreadStateLock(void) + { + state = PyGILState_Ensure( ); + } + + ~PyThreadStateLock(void) + { + PyGILState_Release( state ); + } + +private: + PyGILState_STATE state; +}; + + + +class EventHandler { + public: + virtual ~EventHandler() {} + virtual void setReceivedData(void *i_data_p) = 0; + virtual void trigger() = 0; +}; + + +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(); + } + + virtual ~EventSubscriber() + { + deleteEventHandler(); + m_last_created_event_subscriber = nullptr; + } + + virtual void subscribe() = 0; + + void deleteEventHandler() + { + if (_event_handler_p) { + delete _event_handler_p; + } + _event_handler_p = nullptr; + } + + void setEventHandler(EventHandler *i_event_handler_p) + { + deleteEventHandler(); + _event_handler_p = i_event_handler_p; + } + + EventHandler *getEventHandler() + { + return _event_handler_p; + } + + private: + EventHandler *_event_handler_p; +}; + +#endif diff --git a/pyiec61850/eventHandlers/gooseHandler.hpp b/pyiec61850/eventHandlers/gooseHandler.hpp new file mode 100644 index 00000000..a01d3a6a --- /dev/null +++ b/pyiec61850/eventHandlers/gooseHandler.hpp @@ -0,0 +1,63 @@ +#ifndef PYIEC61850_GOOSEHANDLER_HPP +#define PYIEC61850_GOOSEHANDLER_HPP + +#include "eventHandler.hpp" + + +class GooseHandler: public EventHandler { + public: + virtual ~GooseHandler() {} + + virtual void setReceivedData(void *i_data_p) + { + // copy the received data + GooseSubscriber *l_my_data_p = static_cast(i_data_p); + _libiec61850_goose_subscriber = *l_my_data_p; + } + + GooseSubscriber _libiec61850_goose_subscriber; +}; + + + +class GooseSubscriberForPython: public EventSubscriber { + public: + + virtual void subscribe() { + // install the libiec61850 callback: + // the 'function pointer' is the 'static' method of this class + GooseSubscriber_setListener(m_libiec61850_goose_subscriber, + GooseSubscriberForPython::triggerGooseHandler, + NULL); + } + + // Static method: it is the 'callback' for libiec61850 in C + static void triggerGooseHandler(GooseSubscriber subscriber, void *parameter) + { + 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(&subscriber); + l_event_handler_p->trigger(); + } + else { + printf("The EventHandler is undefined\n"); + } + } + } + + // Setters + void setLibiec61850GooseSubscriber(const GooseSubscriber &i_libiec61850_goose_subscriber) + { + m_libiec61850_goose_subscriber = i_libiec61850_goose_subscriber; + } + + protected: + // Parameters + GooseSubscriber m_libiec61850_goose_subscriber; +}; + +#endif diff --git a/pyiec61850/eventHandlers/reportControlBlockHandler.hpp b/pyiec61850/eventHandlers/reportControlBlockHandler.hpp new file mode 100644 index 00000000..ad798c1d --- /dev/null +++ b/pyiec61850/eventHandlers/reportControlBlockHandler.hpp @@ -0,0 +1,66 @@ +#ifndef PYIEC61850_RCBHANDLER_HPP +#define PYIEC61850_RCBHANDLER_HPP + +#include "eventHandler.hpp" + + +class RCBHandler: public EventHandler { + public: + virtual ~RCBHandler() {} + + virtual void setReceivedData(void *i_data_p) + { + // copy the received data + ClientReport *l_my_data_p = static_cast(i_data_p); + _client_report = *l_my_data_p; + } + + ClientReport _client_report; +}; + + + +class RCBSubscriber: public EventSubscriber { + public: + + virtual void subscribe() { + // install the libiec61850 callback: + // the 'function pointer' is the 'static' method of this class + IedConnection_installReportHandler(m_ied_connection, + m_rcb_reference.c_str(), + m_rcb_rpt_id.c_str(), + RCBSubscriber::triggerRCBHandler, + NULL); + } + + // Static method: it is the 'callback' for libiec61850 in C + static void triggerRCBHandler(void *parameter, ClientReport report) + { + 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(&report); + l_event_handler_p->trigger(); + } + else { + printf("The EventHandler is undefined\n"); + } + } + } + + // Setters + void setIedConnection(const IedConnection &i_ied_connection) {m_ied_connection = i_ied_connection;} + void setRcbReference(const char *i_rcb_reference) {m_rcb_reference = i_rcb_reference;} + void setRcbRptId(const char *i_rcb_rpt_id) {m_rcb_rpt_id = i_rcb_rpt_id;} + + protected: + // Parameters + IedConnection m_ied_connection; + std::string m_rcb_reference; + std::string m_rcb_rpt_id; +}; + +#endif diff --git a/pyiec61850/iec61850.i b/pyiec61850/iec61850.i index 65e0906f..a99b01cb 100644 --- a/pyiec61850/iec61850.i +++ b/pyiec61850/iec61850.i @@ -1,5 +1,5 @@ /* File : iec61850.i */ -%module iec61850 +%module(directors="1") iec61850 %ignore ControlObjectClient_setTestMode(ControlObjectClient self); %ignore CDA_OperBoolean(ModelNode* parent, bool isTImeActivated); %ignore LogicalNode_hasBufferedReports(LogicalNode* node); @@ -29,6 +29,9 @@ DataAttribute* toDataAttribute(ModelNode * MN) %} %apply int *OUTPUT {IedClientError* error}; +%include "cstring.i" +%cstring_bounded_output(char *buffer, 1024); + %include "libiec61850_common_api.h" %include "iec61850_client.h" %include "iso_connection_parameters.h" @@ -41,8 +44,99 @@ DataAttribute* toDataAttribute(ModelNode * MN) %include "iec61850_dynamic_model.h" %include "iec61850_cdc.h" %include "linked_list.h" + +/* User-defined data types, also used: */ +typedef uint64_t msSinceEpoch; +typedef uint64_t nsSinceEpoch; + ModelNode* toModelNode(LogicalNode *); ModelNode* toModelNode(DataObject *); DataAttribute* toDataAttribute(DataObject *); DataAttribute* toDataAttribute(ModelNode *); char* toCharP(void *); + +/* Goose Subscriber section */ +%{ +struct sGooseSubscriber; +typedef struct sGooseSubscriber* GooseSubscriber; +#include "goose_subscriber.h" +#include "goose_receiver.h" + +void GooseSubscriber_setDstMac(GooseSubscriber subscriber, + uint8_t dst_mac_0, + uint8_t dst_mac_1, + uint8_t dst_mac_2, + uint8_t dst_mac_3, + uint8_t dst_mac_4, + uint8_t dst_mac_5) +{ + uint8_t dst_mac[6]; + dst_mac[0] = dst_mac_0; + dst_mac[1] = dst_mac_1; + dst_mac[2] = dst_mac_2; + dst_mac[3] = dst_mac_3; + dst_mac[4] = dst_mac_4; + dst_mac[5] = dst_mac_5; + + GooseSubscriber_setDstMac(subscriber, dst_mac); +} +%} + +%include "goose_subscriber.h" +%include "goose_receiver.h" + +void GooseSubscriber_setDstMac(GooseSubscriber subscriber, + uint8_t dst_mac_0, + uint8_t dst_mac_1, + uint8_t dst_mac_2, + uint8_t dst_mac_3, + uint8_t dst_mac_4, + uint8_t dst_mac_5); + +/* Event Handler section */ +%feature("director") RCBHandler; +%feature("director") GooseHandler; +%{ +#include "eventHandlers/eventHandler.hpp" +#include "eventHandlers/reportControlBlockHandler.hpp" +#include "eventHandlers/gooseHandler.hpp" +EventSubscriber* EventSubscriber::m_last_created_event_subscriber = nullptr; +%} +%include "eventHandlers/eventHandler.hpp" +%include "eventHandlers/reportControlBlockHandler.hpp" +%include "eventHandlers/gooseHandler.hpp" + +/* Goose Publisher section */ +%{ +#include "goose_publisher.h" + +void LinkedList_destroyDeep_MmsValueDelete(LinkedList dataSetValues) +{ + LinkedList_destroyDeep(dataSetValues, (LinkedListValueDeleteFunction) MmsValue_delete); +} +void CommParameters_setDstAddress(CommParameters *gooseCommParameters, + uint8_t dst_mac_0, + uint8_t dst_mac_1, + uint8_t dst_mac_2, + uint8_t dst_mac_3, + uint8_t dst_mac_4, + uint8_t dst_mac_5) +{ + gooseCommParameters->dstAddress[0] = dst_mac_0; + gooseCommParameters->dstAddress[1] = dst_mac_1; + gooseCommParameters->dstAddress[2] = dst_mac_2; + gooseCommParameters->dstAddress[3] = dst_mac_3; + gooseCommParameters->dstAddress[4] = dst_mac_4; + gooseCommParameters->dstAddress[5] = dst_mac_5; +} +%} +%include "goose_publisher.h" +void LinkedList_destroyDeep_MmsValueDelete(LinkedList dataSetValues); +void CommParameters_setDstAddress(CommParameters *gooseCommParameters, + uint8_t dst_mac_0, + uint8_t dst_mac_1, + uint8_t dst_mac_2, + uint8_t dst_mac_3, + uint8_t dst_mac_4, + uint8_t dst_mac_5); +