From a85095c5487af0df00796d5bd6c9d5805b9b4043 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Wed, 8 Feb 2023 19:20:48 +0000 Subject: [PATCH 1/7] - started to implement north side connection handling based on mode and south side connection status (RFG-70) Signed-off-by: Michael Zillgith --- include/iec104.h | 3 + include/iec104_config.hpp | 49 ++++ src/iec104.cpp | 142 +++++++++-- src/iec104_config.cpp | 52 ++++ tests/test_clockSyncHandler.cpp | 4 + tests/test_connectionHandler.cpp | 20 +- tests/test_control.cpp | 29 ++- tests/test_interrogationHandler.cpp | 6 + tests/test_legacy_mode.cpp | 383 ++++++++++++++++++++++++++++ tests/test_sendSpontData.cpp | 26 +- 10 files changed, 694 insertions(+), 20 deletions(-) create mode 100644 tests/test_legacy_mode.cpp diff --git a/include/iec104.h b/include/iec104.h index 985f677..0ce7617 100644 --- a/include/iec104.h +++ b/include/iec104.h @@ -102,6 +102,8 @@ class IEC104Server void m_enqueueSpontDatapoint(IEC104DataPoint* dp, CS101_CauseOfTransmission cot, IEC60870_5_TypeID typeId); void m_updateDataPoint(IEC104DataPoint* dp, IEC60870_5_TypeID typeId, DatapointValue* value, CP56Time2a ts, uint8_t quality); + bool checkIfSouthConnected(); + bool checkTimestamp(CP56Time2a timestamp); bool checkIfCmdTimeIsValid(int typeId, InformationObject io); void addToOutstandingCommands(CS101_ASDU asdu, IMasterConnection connection, bool isSelect); @@ -128,6 +130,7 @@ class IEC104Server const char* ipAddress); static void connectionEventHandler(void* parameter, IMasterConnection con, CS104_PeerConnectionEvent event); + CS104_Slave m_slave{}; TLSConfiguration m_tlsConfig = nullptr; CS101_AppLayerParameters alParams; diff --git a/include/iec104_config.hpp b/include/iec104_config.hpp index bc0c3c0..e7d78db 100644 --- a/include/iec104_config.hpp +++ b/include/iec104_config.hpp @@ -61,11 +61,58 @@ class IEC104Config std::vector& GetRemoteCertificates() {return m_remoteCertificates;}; std::vector& GetCaCertificates() {return m_caCertificates;}; + enum class Mode + { + CONNECT_ALWAYS, + CONNECT_IF_SOUTH_CONNX_STARTED + }; + + Mode GetMode() {return m_mode;}; + + enum class ConnectionStatus + { + STARTED, + NOT_CONNECTED + }; + + enum class GiStatus + { + IDLE, + STARTED, + IN_PROGRESS, + FAILED, + FINISHED + }; + + class SouthPluginMonitor { + + public: + SouthPluginMonitor(std::string& assetName); + + std::string& GetAssetName() {return m_assetName;}; + + ConnectionStatus GetConnxStatus() {return m_connxStatus;}; + GiStatus GetGiStatus() {return m_giStatus;}; + + void SetConnxStatus(ConnectionStatus status) {m_connxStatus = status;}; + void SetGiStatus(GiStatus status) {m_giStatus = status;}; + + private: + + std::string m_assetName; + ConnectionStatus m_connxStatus; + GiStatus m_giStatus; + }; + + std::vector GetMonitoredSouthPlugins() {return m_monitoredSouthPlugins;}; + private: static bool isValidIPAddress(const string& addrStr); void deleteExchangeDefinitions(); + + Mode m_mode = Mode::CONNECT_ALWAYS; bool m_protocolConfigComplete; bool m_exchangeConfigComplete; @@ -100,6 +147,8 @@ class IEC104Config std::vector m_configuredRedundancyGroups; + std::vector m_monitoredSouthPlugins; + std::map>* m_exchangeDefinitions = nullptr; std::map m_allowedOriginators; diff --git a/src/iec104.cpp b/src/iec104.cpp index c4d1cb6..207b109 100644 --- a/src/iec104.cpp +++ b/src/iec104.cpp @@ -263,9 +263,7 @@ IEC104Server::setJsonConfig(const std::string& stackConfig, m_started = true; m_monitoringThread = new std::thread(&IEC104Server::_monitoringThread, this); - CS104_Slave_start(m_slave); - - m_log->info("CS104 server started"); + m_log->info("CS104 server initialized"); } else { m_log->error("Failed to create CS104 server instance"); @@ -321,8 +319,38 @@ IEC104Server::registerControl(int (* operation)(char *operation, int paramCount, void IEC104Server::_monitoringThread() { + //TODO request south connection status + //requestSouthConnectionStatus(); + + bool serverRunning = false; + while (m_started) { + if (m_config->GetMode() == IEC104Config::Mode::CONNECT_ALWAYS) { + if (serverRunning == false) { + CS104_Slave_start(m_slave); + printf("Server started - mode: CONNECT_ALWAYS\n"); + serverRunning = true; + } + } + else if (m_config->GetMode() == IEC104Config::Mode::CONNECT_IF_SOUTH_CONNX_STARTED) { + if (serverRunning == false) { + + if (checkIfSouthConnected()) { + printf("Server started - mode: CONNECT_IF_SOUTH_CONNX_STARTED\n"); + CS104_Slave_start(m_slave); + serverRunning = true; + } + } + else { + if (checkIfSouthConnected() == false) { + printf("Server stopped - mode: CONNECT_IF_SOUTH_CONNX_STARTED\n"); + CS104_Slave_stop(m_slave); + serverRunning = false; + } + } + } + /* check timeouts for outstanding commands */ m_outstandingCommandsLock.lock(); @@ -350,6 +378,11 @@ IEC104Server::_monitoringThread() Thread_sleep(100); } + + if (serverRunning) { + CS104_Slave_stop(m_slave); + serverRunning = false; + } } static void @@ -586,6 +619,22 @@ createLongDatapoint(const std::string& dataname, return new Datapoint(dataname, dp_value); } +bool +IEC104Server::checkIfSouthConnected() +{ + bool connected = false; + + for (auto southPlugin : m_config->GetMonitoredSouthPlugins()) + { + if (southPlugin->GetConnxStatus() == IEC104Config::ConnectionStatus::STARTED) { + connected = true; + break; + } + } + + return connected; +} + bool IEC104Server::checkTimestamp(CP56Time2a timestamp) { @@ -1017,6 +1066,56 @@ IEC104Server::forwardCommand(CS101_ASDU asdu, InformationObject command, IMaster } } +static void +updateSouthMonitoringInstance(Datapoint* dp, IEC104Config::SouthPluginMonitor* southPluginMonitor) +{ + DatapointValue dpv = dp->getData(); + + vector* sdp = dpv.getDpVec(); + + for (Datapoint* objDp : *sdp) + { + DatapointValue attrVal = objDp->getData(); + + if (objDp->getName() == "connx_status") { + std::string connxStatusValue = attrVal.toStringValue(); + + IEC104Config::ConnectionStatus connxStatus = IEC104Config::ConnectionStatus::NOT_CONNECTED; + + if (connxStatusValue == "not connected") { + connxStatus = IEC104Config::ConnectionStatus::NOT_CONNECTED; + } + else if (connxStatusValue == "started") { + connxStatus = IEC104Config::ConnectionStatus::STARTED; + } + + printf("south connection status for %s changed to %s\n", southPluginMonitor->GetAssetName().c_str(), connxStatusValue.c_str()); + + southPluginMonitor->SetConnxStatus(connxStatus); + } + else if (objDp->getName() == "gi_status") { + std::string giStatusValue = attrVal.toStringValue(); + + IEC104Config::GiStatus giStatus = IEC104Config::GiStatus::IDLE; + + if (giStatusValue == "started") { + giStatus = IEC104Config::GiStatus::STARTED; + } + else if (giStatusValue == "in progress") { + giStatus = IEC104Config::GiStatus::IN_PROGRESS; + } + else if (giStatusValue == "failed") { + giStatus = IEC104Config::GiStatus::FAILED; + } + else if (giStatusValue == "finished") { + giStatus = IEC104Config::GiStatus::FINISHED; + } + + southPluginMonitor->SetGiStatus(giStatus); + } + } +} + /** * Send a block of reading to IEC104 Server * @@ -1042,7 +1141,19 @@ IEC104Server::send(const vector& readings) for (Datapoint* dp : dataPoints) { - if (dp->getName() == "data_object") + if (dp->getName() == "iec104_south_event") { + + // check if we know the south plugin + for (auto southPluginMonitor : m_config->GetMonitoredSouthPlugins()) { + if (assetName == southPluginMonitor->GetAssetName()) { + + updateSouthMonitoringInstance(dp, southPluginMonitor); + + break; + } + } + } + else if (dp->getName() == "data_object") { int ca = -1; int ioa = -1; @@ -1748,9 +1859,19 @@ IEC104Server::connectionEventHandler(void* parameter, void IEC104Server::stop() { + if (m_started == true) + { + m_started = false; + + if (m_monitoringThread != nullptr) { + m_monitoringThread->join(); + delete m_monitoringThread; + m_monitoringThread = nullptr; + } + } + if (m_slave) { - CS104_Slave_stop(m_slave); CS104_Slave_destroy(m_slave); m_slave = nullptr; } @@ -1760,15 +1881,4 @@ IEC104Server::stop() TLSConfiguration_destroy(m_tlsConfig); m_tlsConfig = nullptr; } - - if (m_started == true) - { - m_started = false; - - if (m_monitoringThread != nullptr) { - m_monitoringThread->join(); - delete m_monitoringThread; - m_monitoringThread = nullptr; - } - } } diff --git a/src/iec104_config.cpp b/src/iec104_config.cpp index 96b5f8b..8614597 100644 --- a/src/iec104_config.cpp +++ b/src/iec104_config.cpp @@ -44,9 +44,20 @@ IEC104Config::deleteExchangeDefinitions() } } +IEC104Config::SouthPluginMonitor::SouthPluginMonitor(std::string& assetName) +{ + m_assetName = assetName; + m_giStatus = GiStatus::IDLE; + m_connxStatus = ConnectionStatus::NOT_CONNECTED; +} + IEC104Config::~IEC104Config() { deleteExchangeDefinitions(); + + for (auto southPluginMonitor : m_monitoredSouthPlugins) { + delete southPluginMonitor; + } } bool @@ -99,6 +110,31 @@ IEC104Config::importProtocolConfig(const string& protocolConfig) const Value& transportLayer = protocolStack["transport_layer"]; const Value& applicationLayer = protocolStack["application_layer"]; + if (protocolStack.HasMember("south_monitoring")) { + const Value& southMonitoring = protocolStack["south_monitoring"]; + + if (southMonitoring.IsArray()) { + for (const Value& southMonInst : southMonitoring.GetArray()) {; + + if (southMonInst.HasMember("asset")) { + if (southMonInst["asset"].IsString()) { + std::string assetName = southMonInst["asset"].GetString(); + + SouthPluginMonitor* monitor = new SouthPluginMonitor(assetName); + + m_monitoredSouthPlugins.push_back(monitor); + } + else { + Logger::getLogger()->error("south_monitoring \"asset\" element has wrong type"); + } + } + else { + Logger::getLogger()->error("south_monitoring is missing \"asset\" element"); + } + } + } + } + if (transportLayer.HasMember("redundancy_groups")) { if (transportLayer["redundancy_groups"].IsArray()) { @@ -154,6 +190,22 @@ IEC104Config::importProtocolConfig(const string& protocolConfig) } } + if (transportLayer.HasMember("mode")) { + if (transportLayer["mode"].IsString()) { + std::string modeValue = transportLayer["mode"].GetString(); + + if (modeValue == "accept_always") { + m_mode = IEC104Config::Mode::CONNECT_ALWAYS; + } + else if (modeValue == "accept_if_south_connx_started") { + m_mode = IEC104Config::Mode::CONNECT_IF_SOUTH_CONNX_STARTED; + } + else { + Logger::getLogger()->warn("transport_layer.mode has unknown value -> using mode: connect always"); + } + } + } + if (transportLayer.HasMember("port")) { if (transportLayer["port"].IsInt()) { int tcpPort = transportLayer["port"].GetInt(); diff --git a/tests/test_clockSyncHandler.cpp b/tests/test_clockSyncHandler.cpp index 020bfd1..9bdd798 100644 --- a/tests/test_clockSyncHandler.cpp +++ b/tests/test_clockSyncHandler.cpp @@ -185,6 +185,8 @@ TEST_F(ClockSyncHandlerTest, clockSyncFalse) { iec104Server->setJsonConfig(protocol_stack_1, exchanged_data, tls); + Thread_sleep(500); /* wait for the server to start */ + CS104_Connection_setASDUReceivedHandler(connection, asduHandler, this); bool result = CS104_Connection_connect(connection); @@ -216,6 +218,8 @@ TEST_F(ClockSyncHandlerTest, clockSyncTrue) { iec104Server->setJsonConfig(protocol_stack_2, exchanged_data, tls); + Thread_sleep(500); /* wait for the server to start */ + CS104_Connection_setASDUReceivedHandler(connection, asduHandler, this); bool result = CS104_Connection_connect(connection); diff --git a/tests/test_connectionHandler.cpp b/tests/test_connectionHandler.cpp index a78c17f..8f2596c 100644 --- a/tests/test_connectionHandler.cpp +++ b/tests/test_connectionHandler.cpp @@ -144,7 +144,17 @@ static string protocol_stack_2 = QUOTE({ "orig_addr":2 } ] - } + }, + "south_monitoring": [ + { + "connx_status": "CONSTAT-1", + "gi_status": "GISTAT-1" + }, + { + "connx_status": "CONSTAT-2", + "gi_status": "GISTAT-2" + } + ] } }); @@ -275,6 +285,8 @@ TEST_F(ConnectionHandlerTest, NormalConnection) { iec104Server->setJsonConfig(protocol_stack, exchanged_data, tls); + Thread_sleep(500); /* wait for the server to start */ + // Create connection connection = CS104_Connection_create("127.0.0.1", IEC_60870_5_104_DEFAULT_PORT); @@ -302,6 +314,8 @@ TEST_F(ConnectionHandlerTest, TLSConnection) iec104Server->setJsonConfig(protocol_stack_2, exchanged_data, tls); + Thread_sleep(500); /* wait for the server to start */ + bool result = CS104_Connection_connect(connection); ASSERT_TRUE(result); @@ -327,6 +341,8 @@ TEST_F(ConnectionHandlerTest, TLSConnectionNoCaCertificate) iec104Server->setJsonConfig(protocol_stack_2, exchanged_data, tls_2); + Thread_sleep(500); /* wait for the server to start */ + bool result = CS104_Connection_connect(connection); ASSERT_TRUE(result); @@ -352,6 +368,8 @@ TEST_F(ConnectionHandlerTest, TLSConnectionNoRemoteOrCaCertificate) iec104Server->setJsonConfig(protocol_stack_2, exchanged_data, tls_4); + Thread_sleep(500); /* wait for the server to start */ + bool result = CS104_Connection_connect(connection); ASSERT_FALSE(result); diff --git a/tests/test_control.cpp b/tests/test_control.cpp index 8468ede..86f5cae 100644 --- a/tests/test_control.cpp +++ b/tests/test_control.cpp @@ -53,7 +53,8 @@ static string protocol_stack = QUOTE({ "t0_timeout":10, "t1_timeout":15, "t2_timeout":10, - "t3_timeout":20 + "t3_timeout":20, + "mode": "accept_always" }, "application_layer" : { "ca_asdu_size":2, @@ -75,7 +76,11 @@ static string protocol_stack = QUOTE({ "orig_addr":2 } ] - } + }, + "south_monitoring": [ + {"asset": "CONSTAT-1"}, + {"asset": "CONSTAT-2"} + ] } }); @@ -386,6 +391,8 @@ TEST_F(ControlTest, ReceiveSinglePointCommand) iec104Server->registerControl(operateHandler); + Thread_sleep(500); /* wait for the server to start */ + ASSERT_TRUE(CS104_Connection_connect(connection)); CS104_Connection_sendStartDT(connection); @@ -410,6 +417,8 @@ TEST_F(ControlTest, ReceiveSetpointCommandShortWithTimestamp) iec104Server->ActConTimeout(200); iec104Server->ActTermTimeout(200); + Thread_sleep(500); /* wait for the server to start */ + ASSERT_TRUE(CS104_Connection_connect(connection)); CS104_Connection_sendStartDT(connection); @@ -438,6 +447,8 @@ TEST_F(ControlTest, ReceiveSetpointCommandShortWithInvalidTimestamp) iec104Server->ActConTimeout(200); iec104Server->ActTermTimeout(200); + Thread_sleep(500); /* wait for the server to start */ + ASSERT_TRUE(CS104_Connection_connect(connection)); CS104_Connection_sendStartDT(connection); @@ -469,6 +480,8 @@ TEST_F(ControlTest, SinglePointCommandUnknownCA) iec104Server->registerControl(operateHandler); + Thread_sleep(500); /* wait for the server to start */ + ASSERT_TRUE(CS104_Connection_connect(connection)); CS104_Connection_sendStartDT(connection); @@ -490,6 +503,8 @@ TEST_F(ControlTest, ReceiveUnexpectedDoublePointCommand) iec104Server->registerControl(operateHandler); + Thread_sleep(500); /* wait for the server to start */ + ASSERT_TRUE(CS104_Connection_connect(connection)); CS104_Connection_sendStartDT(connection); @@ -514,6 +529,8 @@ TEST_F(ControlTest, CommandAckTimeout) iec104Server->ActConTimeout(200); iec104Server->ActTermTimeout(200); + Thread_sleep(500); /* wait for the server to start */ + ASSERT_TRUE(CS104_Connection_connect(connection)); CS104_Connection_sendStartDT(connection); @@ -535,6 +552,8 @@ TEST_F(ControlTest, CommandActCon) iec104Server->registerControl(operateHandler); + Thread_sleep(500); /* wait for the server to start */ + iec104Server->ActConTimeout(1000); iec104Server->ActTermTimeout(1000); @@ -577,6 +596,8 @@ TEST_F(ControlTest, CommandActConNegative) iec104Server->ActConTimeout(1000); iec104Server->ActTermTimeout(1000); + Thread_sleep(500); /* wait for the server to start */ + ASSERT_TRUE(CS104_Connection_connect(connection)); CS104_Connection_sendStartDT(connection); @@ -608,6 +629,8 @@ TEST_F(ControlTest, SinglePointCommandIOMissing) iec104Server->registerControl(operateHandler); + Thread_sleep(500); /* wait for the server to start */ + ASSERT_TRUE(CS104_Connection_connect(connection)); CS104_Connection_sendStartDT(connection); @@ -633,6 +656,8 @@ TEST_F(ControlTest, ReceiveSinglePointCommandWithTime) iec104Server->registerControl(operateHandler); + Thread_sleep(500); /* wait for the server to start */ + ASSERT_TRUE(CS104_Connection_connect(connection)); CS104_Connection_sendStartDT(connection); diff --git a/tests/test_interrogationHandler.cpp b/tests/test_interrogationHandler.cpp index e679374..9a274ac 100644 --- a/tests/test_interrogationHandler.cpp +++ b/tests/test_interrogationHandler.cpp @@ -263,6 +263,8 @@ TEST_F(InterrogationHandlerTest, InterrogationHandlerSingleCA) iec104Server->setJsonConfig(protocol_stack, exchanged_data, tls); + Thread_sleep(500); /* wait for the server to start */ + CS104_Connection_setASDUReceivedHandler(connection, test1_ASDUReceivedHandler, this); bool result = CS104_Connection_connect(connection); @@ -335,6 +337,8 @@ TEST_F(InterrogationHandlerTest, InterrogationHandlerBroadcastCA) iec104Server->setJsonConfig(protocol_stack, exchanged_data, tls); + Thread_sleep(500); /* wait for the server to start */ + CS104_Connection_setASDUReceivedHandler(connection, test1_ASDUReceivedHandler, this); bool result = CS104_Connection_connect(connection); @@ -427,6 +431,8 @@ TEST_F(InterrogationHandlerTest, InterrogationForUnknownCA) iec104Server->setJsonConfig(protocol_stack, exchanged_data, tls); + Thread_sleep(500); /* wait for the server to start */ + CS104_Connection_setASDUReceivedHandler(connection, test1_ASDUReceivedHandler, this); bool result = CS104_Connection_connect(connection); diff --git a/tests/test_legacy_mode.cpp b/tests/test_legacy_mode.cpp new file mode 100644 index 0000000..c958a66 --- /dev/null +++ b/tests/test_legacy_mode.cpp @@ -0,0 +1,383 @@ +#include +#include +#include + +#include +#include +#include + +#include "cs104_connection.h" +using namespace std; + +static string protocol_stack = QUOTE({ + "protocol_stack" : { + "name" : "iec104server", + "version" : "1.0", + "transport_layer" : { + "redundancy_groups":[ + { + "connections":[ + { + "clt_ip":"192.168.2.244" + }, + { + "clt_ip":"192.168.0.11" + } + ], + "rg_name":"red-group-1" + }, + { + "connections":[ + { + "clt_ip":"192.168.2.224" + }, + { + "clt_ip":"192.168.0.11" + }, + { + "clt_ip":"192.168.0.12" + } + ], + "rg_name":"red-group-2" + }, + { + "rg_name":"catch-all" + } + ], + "bind_on_ip":false, + "srv_ip":"0.0.0.0", + "port":2404, + "tls":false, + "k_value":12, + "w_value":8, + "t0_timeout":10, + "t1_timeout":15, + "t2_timeout":10, + "t3_timeout":20, + "mode": "accept_if_south_connx_started" + }, + "application_layer" : { + "ca_asdu_size":2, + "ioaddr_size":3, + "asdu_size":0, + "time_sync":false, + "cmd_exec_timeout":1, + "cmd_recv_timeout":1, + "accept_cmd_with_time":2, + "filter_orig":false, + "filter_list":[ + { + "orig_addr":0 + }, + { + "orig_addr":1 + }, + { + "orig_addr":2 + } + ] + }, + "south_monitoring": [ + {"asset": "CONSTAT-1"}, + {"asset": "CONSTAT-2"} + ] + } + }); + +static string tls = QUOTE({ + "tls_conf" : { + "private_key" : "iec104_server.key", + "own_cert" : "iec104_server.cer", + "ca_certs" : [ + { + "cert_file": "iec104_ca.cer" + } + ] + } + }); + +static string exchanged_data = QUOTE({ + "exchanged_data" : { + "name" : "iec104server", + "version" : "1.0", + "datapoints":[ + { + "label":"TS1", + "protocols":[ + { + "name":"iec104", + "address":"45-672", + "typeid":"M_SP_NA_1" + }, + { + "name":"tase2", + "address":"S_114562", + "typeid":"Data_StateQTimeTagExtended" + } + ] + }, + { + "label":"TM1", + "protocols":[ + { + "name":"iec104", + "address":"45-984", + "typeid":"M_ME_NA_1" + }, + { + "name":"tase2", + "address":"S_114562", + "typeid":"Data_RealQ" + } + ] + }, + { + "label":"CM1", + "protocols":[ + { + "name":"iec104", + "address":"45-10005", + "typeid":"C_SC_NA_1", + "termination_timeout": 3000 + } + ] + } + ] + } + }); + +static string exchanged_data_2 = QUOTE({ + "exchanged_data" : { + "name" : "iec104server", + "version" : "1.0", + "datapoints":[ + { + "label":"TS1", + "protocols":[ + { + "name":"iec104", + "address":"45-672", + "typeid":"M_SP_NA_1" + }, + { + "name":"tase2", + "address":"S_114562", + "typeid":"Data_StateQTimeTagExtended" + } + ] + }, + { + "label":"TM1", + "protocols":[ + { + "name":"iec104", + "address":"45-984", + "typeid":"M_ME_NA_1" + }, + { + "name":"tase2", + "address":"S_114562", + "typeid":"Data_RealQ" + } + ] + }, + { + "label":"CM1", + "protocols":[ + { + "name":"iec104", + "address":"45-10005", + "typeid":"C_SC_NA_1", + "termination_timeout": 3000 + } + ] + }, + { + "label":"CM2", + "protocols":[ + { + "name":"iec104", + "address":"45-10010", + "typeid":"C_SE_TC_1", + "termination_timeout": 1 + } + ] + } + ] + } + }); + +// Class to be called in each test, contains fixture to be used in +class LegacyModeTest : public testing::Test +{ +protected: + IEC104Server* iec104Server; // Object on which we call for tests + CS104_Connection connection; + + // Setup is ran for every tests, so each variable are reinitialised + void SetUp() override + { + operateHandlerCalled = 0; + asduHandlerCalled = 0; + actConReceived = 0; + actConNegative = false; + actTermReceived = 0; + + // Init iec104server object + iec104Server = new IEC104Server(); + const char* ip = "127.0.0.1"; + uint16_t port = IEC_60870_5_104_DEFAULT_PORT; + // Create connection + connection = CS104_Connection_create(ip, port); + + CS104_Connection_setASDUReceivedHandler(connection, m_asduReceivedHandler, this); + } + + static int operateHandlerCalled; + + static int operateHandler(char *operation, int paramCount, char* names[], char *parameters[], ControlDestination destination, ...); + + // TearDown is ran for every tests, so each variable are destroyed again + void TearDown() override + { + CS104_Connection_destroy(connection); + iec104Server->stop(); + + delete iec104Server; + } + + void ForwardCommandAck(const char* cmdName, const char* type, int ca, int ioa, int cot, bool negative); + + int asduHandlerCalled = 0; + int actConReceived = 0; + bool actConNegative = false; + int actTermReceived = 0; + + static bool m_asduReceivedHandler(void* parameter, int address, CS101_ASDU asdu); +}; + +bool +LegacyModeTest::m_asduReceivedHandler(void* parameter, int address, CS101_ASDU asdu) +{ + LegacyModeTest* self = (LegacyModeTest*)parameter; + + self->asduHandlerCalled++; + + printf("CS101_ASDU: type: %i ca: %i cot: %i\n", CS101_ASDU_getTypeID(asdu), CS101_ASDU_getCA(asdu), CS101_ASDU_getCOT(asdu)); + + self->actConNegative = false; + + if (CS101_ASDU_getCOT(asdu) == CS101_COT_ACTIVATION_CON) { + self->actConReceived++; + self->actConNegative = CS101_ASDU_isNegative(asdu); + } + + if (CS101_ASDU_getCOT(asdu) == CS101_COT_ACTIVATION_TERMINATION) { + self->actTermReceived++; + } + + return true; +} + +int LegacyModeTest::operateHandlerCalled; + +int LegacyModeTest::operateHandler(char *operation, int paramCount, char* names[], char *parameters[], ControlDestination destination, ...) +{ + printf("operateHandler called\n"); + operateHandlerCalled++; + + return 1; +} + +template +static Datapoint* createDatapoint(const std::string& dataname, + const T value) +{ + DatapointValue dp_value = DatapointValue(value); + return new Datapoint(dataname, dp_value); +} + +template +static Datapoint* createDataObject(const char* type, int ca, int ioa, int cot, + const T value, bool iv, bool bl, bool ov, bool sb, bool nt) +{ + auto* datapoints = new vector; + + datapoints->push_back(createDatapoint("do_type", type)); + datapoints->push_back(createDatapoint("do_ca", (int64_t)ca)); + datapoints->push_back(createDatapoint("do_oa", (int64_t)0)); + datapoints->push_back(createDatapoint("do_cot", (int64_t)cot)); + datapoints->push_back(createDatapoint("do_test", (int64_t)0)); + datapoints->push_back(createDatapoint("do_negative", (int64_t)0)); + datapoints->push_back(createDatapoint("do_ioa", (int64_t)ioa)); + datapoints->push_back(createDatapoint("do_value", value)); + datapoints->push_back(createDatapoint("do_quality_iv", (int64_t)iv)); + datapoints->push_back(createDatapoint("do_quality_bl", (int64_t)bl)); + datapoints->push_back(createDatapoint("do_quality_ov", (int64_t)ov)); + datapoints->push_back(createDatapoint("do_quality_sb", (int64_t)sb)); + datapoints->push_back(createDatapoint("do_quality_nt", (int64_t)nt)); + + DatapointValue dpv(datapoints, true); + + Datapoint* dp = new Datapoint("data_object", dpv); + + return dp; +} + +static Datapoint* +createCommandAck(const char* type, int ca, int ioa, int cot, bool negative) +{ + auto* datapoints = new vector; + + datapoints->push_back(createDatapoint("do_type", type)); + datapoints->push_back(createDatapoint("do_ca", (int64_t)ca)); + datapoints->push_back(createDatapoint("do_oa", (int64_t)0)); + datapoints->push_back(createDatapoint("do_cot", (int64_t)cot)); + datapoints->push_back(createDatapoint("do_test", (int64_t)0)); + datapoints->push_back(createDatapoint("do_negative", (int64_t)negative)); + datapoints->push_back(createDatapoint("do_ioa", (int64_t)ioa)); + + DatapointValue dpv(datapoints, true); + + Datapoint* dp = new Datapoint("data_object", dpv); + + return dp; +} + +void +LegacyModeTest::ForwardCommandAck(const char* cmdName, const char* type, int ca, int ioa, int cot, bool negative) +{ + auto* dataobjects = new vector; + + dataobjects->push_back(createCommandAck(type, ca, ioa, cot, negative)); + + Reading* reading = new Reading(std::string(cmdName), *dataobjects); + + vector readings; + + readings.push_back(reading); + + iec104Server->send(readings); + + delete reading; + delete dataobjects; +} + +TEST_F(LegacyModeTest, ConnectWhileSouthNotStarted) +{ + iec104Server->setJsonConfig(protocol_stack, exchanged_data, tls); + + iec104Server->registerControl(operateHandler); + + Thread_sleep(500); /* wait for the server to start */ + + ASSERT_FALSE(CS104_Connection_connect(connection)); + + //TODO send south event connx_started + + Thread_sleep(500); /* wait for the server to start */ + + //ASSERT_TRUECS104_Connection_connect(connection)); + + Thread_sleep(500); +} diff --git a/tests/test_sendSpontData.cpp b/tests/test_sendSpontData.cpp index 92cf698..2503767 100644 --- a/tests/test_sendSpontData.cpp +++ b/tests/test_sendSpontData.cpp @@ -299,6 +299,8 @@ TEST_F(SendSpontDataTest, CreateReading_M_SP_NA_1) { iec104Server->setJsonConfig(protocol_stack, exchanged_data, tls); + Thread_sleep(500); /* wait for the server to start */ + CS104_Connection_setASDUReceivedHandler(connection, test1_ASDUReceivedHandler, this); bool result = CS104_Connection_connect(connection); @@ -361,6 +363,8 @@ TEST_F(SendSpontDataTest, CreateReading_M_SP_TB_1_On) { iec104Server->setJsonConfig(protocol_stack, exchanged_data, tls); + Thread_sleep(500); /* wait for the server to start */ + CS104_Connection_setASDUReceivedHandler(connection, test1_ASDUReceivedHandler, this); bool result = CS104_Connection_connect(connection); @@ -418,6 +422,8 @@ TEST_F(SendSpontDataTest, CreateReading_M_SP_TB_1_Off) { iec104Server->setJsonConfig(protocol_stack, exchanged_data, tls); + Thread_sleep(500); /* wait for the server to start */ + CS104_Connection_setASDUReceivedHandler(connection, test1_ASDUReceivedHandler, this); bool result = CS104_Connection_connect(connection); @@ -475,6 +481,8 @@ TEST_F(SendSpontDataTest, CreateReading_M_DP_NA_1) { iec104Server->setJsonConfig(protocol_stack, exchanged_data, tls); + Thread_sleep(500); /* wait for the server to start */ + CS104_Connection_setASDUReceivedHandler(connection, test1_ASDUReceivedHandler, this); bool result = CS104_Connection_connect(connection); @@ -571,6 +579,8 @@ TEST_F(SendSpontDataTest, CreateReading_M_DP_TB_1) { iec104Server->setJsonConfig(protocol_stack, exchanged_data, tls); + Thread_sleep(500); /* wait for the server to start */ + CS104_Connection_setASDUReceivedHandler(connection, test1_ASDUReceivedHandler, this); bool result = CS104_Connection_connect(connection); @@ -642,6 +652,8 @@ TEST_F(SendSpontDataTest, CreateReading_M_ME_NA_1) { iec104Server->setJsonConfig(protocol_stack, exchanged_data, tls); + Thread_sleep(500); /* wait for the server to start */ + CS104_Connection_setASDUReceivedHandler(connection, test1_ASDUReceivedHandler, this); bool result = CS104_Connection_connect(connection); @@ -702,6 +714,8 @@ TEST_F(SendSpontDataTest, CreateReading_M_ME_NB_1) { iec104Server->setJsonConfig(protocol_stack, exchanged_data, tls); + Thread_sleep(500); /* wait for the server to start */ + CS104_Connection_setASDUReceivedHandler(connection, test1_ASDUReceivedHandler, this); bool result = CS104_Connection_connect(connection); @@ -747,6 +761,8 @@ TEST_F(SendSpontDataTest, CreateReading_M_ME_NC_1) { iec104Server->setJsonConfig(protocol_stack, exchanged_data, tls); + Thread_sleep(500); /* wait for the server to start */ + CS104_Connection_setASDUReceivedHandler(connection, test1_ASDUReceivedHandler, this); bool result = CS104_Connection_connect(connection); @@ -792,6 +808,8 @@ TEST_F(SendSpontDataTest, CreateReading_M_ME_TD_1) { iec104Server->setJsonConfig(protocol_stack, exchanged_data, tls); + Thread_sleep(500); /* wait for the server to start */ + CS104_Connection_setASDUReceivedHandler(connection, test1_ASDUReceivedHandler, this); bool result = CS104_Connection_connect(connection); @@ -849,6 +867,8 @@ TEST_F(SendSpontDataTest, CreateReading_M_ME_TE_1) { iec104Server->setJsonConfig(protocol_stack, exchanged_data, tls); + Thread_sleep(500); /* wait for the server to start */ + CS104_Connection_setASDUReceivedHandler(connection, test1_ASDUReceivedHandler, this); bool result = CS104_Connection_connect(connection); @@ -906,6 +926,8 @@ TEST_F(SendSpontDataTest, CreateReading_M_ME_TF_1) { iec104Server->setJsonConfig(protocol_stack, exchanged_data, tls); + Thread_sleep(500); /* wait for the server to start */ + CS104_Connection_setASDUReceivedHandler(connection, test1_ASDUReceivedHandler, this); bool result = CS104_Connection_connect(connection); @@ -982,6 +1004,8 @@ TEST_F(SendSpontDataTest, CreateReading_differentSpontaneousCOTs) { iec104Server->setJsonConfig(protocol_stack, exchanged_data, tls); + Thread_sleep(500); /* wait for the server to start */ + CS104_Connection_setASDUReceivedHandler(connection, test1_ASDUReceivedHandler, this); bool result = CS104_Connection_connect(connection); @@ -1013,4 +1037,4 @@ TEST_F(SendSpontDataTest, CreateReading_differentSpontaneousCOTs) delete reading; delete dataobjects; -} \ No newline at end of file +} From d5397ae818ac4c3506c58cf6760b2f679961a33b Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 10 Feb 2023 12:30:08 +0000 Subject: [PATCH 2/7] - handle control center connection based on mode and south connection status (#31)(RFD-70) Signed-off-by: Michael Zillgith --- src/iec104.cpp | 22 ++++++++++------ tests/test_legacy_mode.cpp | 51 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 63 insertions(+), 10 deletions(-) diff --git a/src/iec104.cpp b/src/iec104.cpp index 207b109..14372db 100644 --- a/src/iec104.cpp +++ b/src/iec104.cpp @@ -1125,14 +1125,10 @@ updateSouthMonitoringInstance(Datapoint* dp, IEC104Config::SouthPluginMonitor* s uint32_t IEC104Server::send(const vector& readings) { - if (CS104_Slave_isRunning(m_slave) == false) - { - //m_log->error("Failed to send data: server not running"); - return 0; - } - int n = 0; + int readingsSent = 0; + for (auto reading = readings.cbegin(); reading != readings.cend(); reading++) { @@ -1149,12 +1145,22 @@ IEC104Server::send(const vector& readings) updateSouthMonitoringInstance(dp, southPluginMonitor); + readingsSent++; + break; } } } else if (dp->getName() == "data_object") - { + { + if (CS104_Slave_isRunning(m_slave) == false) { + m_log->warn("Failed to send data: server not running"); + printf("Failed to send data: server not running\n"); + continue; + } + + readingsSent++; + int ca = -1; int ioa = -1; CS101_CauseOfTransmission cot = CS101_COT_UNKNOWN_COT; @@ -1291,7 +1297,7 @@ IEC104Server::send(const vector& readings) n++; } - return n; + return readingsSent; } /** diff --git a/tests/test_legacy_mode.cpp b/tests/test_legacy_mode.cpp index c958a66..59dc198 100644 --- a/tests/test_legacy_mode.cpp +++ b/tests/test_legacy_mode.cpp @@ -253,6 +253,8 @@ class LegacyModeTest : public testing::Test bool actConNegative = false; int actTermReceived = 0; + void SendSouthEvent(std::string asset, bool withConnx, std::string connxValue, bool withGiStatus, std::string giStatusValue); + static bool m_asduReceivedHandler(void* parameter, int address, CS101_ASDU asdu); }; @@ -344,6 +346,45 @@ createCommandAck(const char* type, int ca, int ioa, int cot, bool negative) return dp; } +static Datapoint* +createSouthEvent(bool withConnx, std::string connxValue, bool withGiStatus, std::string giStatusValue) +{ + auto* datapoints = new vector; + + if (withConnx) { + datapoints->push_back(createDatapoint("connx_status", connxValue)); + } + + if (withGiStatus) { + datapoints->push_back(createDatapoint("gi_status", giStatusValue)); + } + + DatapointValue dpv(datapoints, true); + + Datapoint* dp = new Datapoint("iec104_south_event", dpv); + + return dp; +} + +void +LegacyModeTest::SendSouthEvent(std::string asset, bool withConnx, std::string connxValue, bool withGiStatus, std::string giStatusValue) +{ + Datapoint* southEvent = createSouthEvent(true, connxValue, false, giStatusValue); + + auto* southEvents = new vector; + + southEvents->push_back(southEvent); + + //TODO send south event connx_started + Reading* reading = new Reading(asset, *southEvents); + + vector readings; + + readings.push_back(reading); + + iec104Server->send(readings); +} + void LegacyModeTest::ForwardCommandAck(const char* cmdName, const char* type, int ca, int ioa, int cot, bool negative) { @@ -373,11 +414,17 @@ TEST_F(LegacyModeTest, ConnectWhileSouthNotStarted) ASSERT_FALSE(CS104_Connection_connect(connection)); - //TODO send south event connx_started + SendSouthEvent("CONSTAT-1", true, "started", false, ""); + + Thread_sleep(500); /* wait for the server to start */ + + ASSERT_TRUE(CS104_Connection_connect(connection)); + + SendSouthEvent("CONSTAT-1", true, "not connected", true, "failed"); Thread_sleep(500); /* wait for the server to start */ - //ASSERT_TRUECS104_Connection_connect(connection)); + ASSERT_FALSE(CS104_Connection_connect(connection)); Thread_sleep(500); } From d70b6435b8a450b82a9e7398622404e45c0bcad9 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 10 Feb 2023 19:03:04 +0000 Subject: [PATCH 3/7] - send request_connection_status operation after startup (RFD-70) Signed-off-by: Michael Zillgith --- include/iec104.h | 3 ++- src/iec104.cpp | 19 +++++++++++++++---- tests/test_legacy_mode.cpp | 12 ++++++++++-- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/include/iec104.h b/include/iec104.h index 0ce7617..b17d78e 100644 --- a/include/iec104.h +++ b/include/iec104.h @@ -112,6 +112,7 @@ class IEC104Server void removeAllOutstandingCommands(); void handleActCon(int type, int ca, int ioa, bool isNegative); void handleActTerm(int type, int ca, int ioa, bool isNegative); + void requestSouthConnectionStatus(); static void printCP56Time2a(CP56Time2a time); static void rawMessageHandler(void* parameter, IMasterConnection connection, @@ -141,7 +142,7 @@ class IEC104Server int m_actConTimeout = 1000; int m_actTermTimeout = 1000; - int (*m_oper)(char *operation, int paramCount, char* names[], char *parameters[], ControlDestination destination, ...); + int (*m_oper)(char *operation, int paramCount, char* names[], char* parameters[], ControlDestination destination, ...) = NULL; bool m_started = false; std::thread* m_monitoringThread = nullptr; diff --git a/src/iec104.cpp b/src/iec104.cpp index 14372db..6dc70e9 100644 --- a/src/iec104.cpp +++ b/src/iec104.cpp @@ -191,8 +191,6 @@ IEC104Server::setJsonConfig(const std::string& stackConfig, if (m_slave) { - m_oper = NULL; - CS104_Slave_setLocalPort(m_slave, m_config->TcpPort()); m_log->info("TCP/IP parameters:"); @@ -316,11 +314,24 @@ IEC104Server::registerControl(int (* operation)(char *operation, int paramCount, m_oper = operation; } +void +IEC104Server::requestSouthConnectionStatus() +{ + if (m_oper) { + if (m_config->CmdDest() == "") + m_oper((char*)"request_connection_status", 0, nullptr, nullptr, DestinationBroadcast, nullptr); + else + m_oper((char*)"request_connection_status", 0, nullptr, nullptr, DestinationService, m_config->CmdDest().c_str()); + } + else { + printf("WARNING: m_oper not set -> call registerControl\n"); + } +} + void IEC104Server::_monitoringThread() { - //TODO request south connection status - //requestSouthConnectionStatus(); + requestSouthConnectionStatus(); bool serverRunning = false; diff --git a/tests/test_legacy_mode.cpp b/tests/test_legacy_mode.cpp index 59dc198..6cfd3b2 100644 --- a/tests/test_legacy_mode.cpp +++ b/tests/test_legacy_mode.cpp @@ -234,6 +234,7 @@ class LegacyModeTest : public testing::Test } static int operateHandlerCalled; + static std::string calledOperation; static int operateHandler(char *operation, int paramCount, char* names[], char *parameters[], ControlDestination destination, ...); @@ -282,12 +283,15 @@ LegacyModeTest::m_asduReceivedHandler(void* parameter, int address, CS101_ASDU a } int LegacyModeTest::operateHandlerCalled; +std::string LegacyModeTest::calledOperation = ""; int LegacyModeTest::operateHandler(char *operation, int paramCount, char* names[], char *parameters[], ControlDestination destination, ...) { printf("operateHandler called\n"); operateHandlerCalled++; + calledOperation.assign(operation); + return 1; } @@ -406,12 +410,16 @@ LegacyModeTest::ForwardCommandAck(const char* cmdName, const char* type, int ca, TEST_F(LegacyModeTest, ConnectWhileSouthNotStarted) { - iec104Server->setJsonConfig(protocol_stack, exchanged_data, tls); - iec104Server->registerControl(operateHandler); + iec104Server->setJsonConfig(protocol_stack, exchanged_data, tls); + Thread_sleep(500); /* wait for the server to start */ + // expect operate handler called with "request_connection_status" + ASSERT_EQ(1, operateHandlerCalled); + ASSERT_EQ("request_connection_status", calledOperation); + ASSERT_FALSE(CS104_Connection_connect(connection)); SendSouthEvent("CONSTAT-1", true, "started", false, ""); From 2a39068d84bdbb37a715c0e9433a3426ad745ebc Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sat, 11 Feb 2023 22:35:38 +0000 Subject: [PATCH 4/7] - send request_connection_status command with parameter Signed-off-by: Michael Zillgith --- include/iec104.h | 3 ++- src/iec104.cpp | 62 +++++++++++++++++++++++++++++++++++------------- src/plugin.cpp | 2 ++ 3 files changed, 50 insertions(+), 17 deletions(-) diff --git a/include/iec104.h b/include/iec104.h index b17d78e..47fa98a 100644 --- a/include/iec104.h +++ b/include/iec104.h @@ -112,7 +112,8 @@ class IEC104Server void removeAllOutstandingCommands(); void handleActCon(int type, int ca, int ioa, bool isNegative); void handleActTerm(int type, int ca, int ioa, bool isNegative); - void requestSouthConnectionStatus(); + bool requestSouthConnectionStatus(); + void updateSouthMonitoringInstance(Datapoint* dp, IEC104Config::SouthPluginMonitor* southPluginMonitor); static void printCP56Time2a(CP56Time2a time); static void rawMessageHandler(void* parameter, IMasterConnection connection, diff --git a/src/iec104.cpp b/src/iec104.cpp index 6dc70e9..acd05b4 100644 --- a/src/iec104.cpp +++ b/src/iec104.cpp @@ -275,7 +275,7 @@ IEC104Server::setJsonConfig(const std::string& stackConfig, void IEC104Server::configure(const ConfigCategory* config) { - m_log->debug("configure called"); + m_log->info("configure called"); if (config->itemExists("name")) m_name = config->getValue("name"); @@ -312,35 +312,55 @@ void IEC104Server::registerControl(int (* operation)(char *operation, int paramCount, char *names[], char *parameters[], ControlDestination destination, ...)) { m_oper = operation; + + m_log->warn("RegisterControl is called"); } -void +bool IEC104Server::requestSouthConnectionStatus() { if (m_oper) { + m_log->warn("Send request_connection_status operation"); + + char* parameters[1]; + char* names[1]; + + names[0] = (char*)"desc"; + + parameters[0] = (char*)"request connection status"; + if (m_config->CmdDest() == "") - m_oper((char*)"request_connection_status", 0, nullptr, nullptr, DestinationBroadcast, nullptr); + m_oper((char*)"request_connection_status", 1, names, parameters, DestinationBroadcast, nullptr); else - m_oper((char*)"request_connection_status", 0, nullptr, nullptr, DestinationService, m_config->CmdDest().c_str()); + m_oper((char*)"request_connection_status", 1, names, parameters, DestinationService, m_config->CmdDest().c_str()); + + return true; } else { + m_log->warn("m_oper not set -> call registerControl"); printf("WARNING: m_oper not set -> call registerControl\n"); + return false; } } void IEC104Server::_monitoringThread() { - requestSouthConnectionStatus(); + bool southStatusRequested = false; bool serverRunning = false; while (m_started) { + if (southStatusRequested == false) { + southStatusRequested = requestSouthConnectionStatus(); + } + if (m_config->GetMode() == IEC104Config::Mode::CONNECT_ALWAYS) { if (serverRunning == false) { CS104_Slave_start(m_slave); printf("Server started - mode: CONNECT_ALWAYS\n"); + m_log->warn("Server started - mode: CONNECT_ALWAYS"); serverRunning = true; } } @@ -349,6 +369,7 @@ IEC104Server::_monitoringThread() if (checkIfSouthConnected()) { printf("Server started - mode: CONNECT_IF_SOUTH_CONNX_STARTED\n"); + m_log->warn("Server started - mode: CONNECT_IF_SOUTH_CONNX_STARTED"); CS104_Slave_start(m_slave); serverRunning = true; } @@ -356,6 +377,7 @@ IEC104Server::_monitoringThread() else { if (checkIfSouthConnected() == false) { printf("Server stopped - mode: CONNECT_IF_SOUTH_CONNX_STARTED\n"); + m_log->warn("Server stopped - mode: CONNECT_IF_SOUTH_CONNX_STARTED"); CS104_Slave_stop(m_slave); serverRunning = false; } @@ -1077,8 +1099,8 @@ IEC104Server::forwardCommand(CS101_ASDU asdu, InformationObject command, IMaster } } -static void -updateSouthMonitoringInstance(Datapoint* dp, IEC104Config::SouthPluginMonitor* southPluginMonitor) +void +IEC104Server::updateSouthMonitoringInstance(Datapoint* dp, IEC104Config::SouthPluginMonitor* southPluginMonitor) { DatapointValue dpv = dp->getData(); @@ -1102,6 +1124,9 @@ updateSouthMonitoringInstance(Datapoint* dp, IEC104Config::SouthPluginMonitor* s printf("south connection status for %s changed to %s\n", southPluginMonitor->GetAssetName().c_str(), connxStatusValue.c_str()); + m_log->warn("south connection status for %s changed to %s", southPluginMonitor->GetAssetName().c_str(), connxStatusValue.c_str()); + + southPluginMonitor->SetConnxStatus(connxStatus); } else if (objDp->getName() == "gi_status") { @@ -1121,6 +1146,8 @@ updateSouthMonitoringInstance(Datapoint* dp, IEC104Config::SouthPluginMonitor* s else if (giStatusValue == "finished") { giStatus = IEC104Config::GiStatus::FINISHED; } + + m_log->warn("south gi status for %s changed to %s", southPluginMonitor->GetAssetName().c_str(), giStatusValue.c_str()); southPluginMonitor->SetGiStatus(giStatus); } @@ -1140,20 +1167,23 @@ IEC104Server::send(const vector& readings) int readingsSent = 0; - for (auto reading = readings.cbegin(); reading != readings.cend(); - reading++) + for (auto reading = readings.cbegin(); reading != readings.cend(); reading++) { vector& dataPoints = (*reading)->getReadingData(); - string assetName = (*reading)->getAssetName(); + string assetName = (*reading)->getAssetName(); for (Datapoint* dp : dataPoints) { if (dp->getName() == "iec104_south_event") { + + m_log->warn("Receive iec104_south_event"); // check if we know the south plugin for (auto southPluginMonitor : m_config->GetMonitoredSouthPlugins()) { if (assetName == southPluginMonitor->GetAssetName()) { + m_log->warn("Found matching monitored plugin for iec104_south_event"); + updateSouthMonitoringInstance(dp, southPluginMonitor); readingsSent++; @@ -1164,14 +1194,13 @@ IEC104Server::send(const vector& readings) } else if (dp->getName() == "data_object") { + readingsSent++; + if (CS104_Slave_isRunning(m_slave) == false) { - m_log->warn("Failed to send data: server not running"); - printf("Failed to send data: server not running\n"); + //m_log->warn("Failed to send data: server not running"); continue; } - readingsSent++; - int ca = -1; int ioa = -1; CS101_CauseOfTransmission cot = CS101_COT_UNKNOWN_COT; @@ -1301,14 +1330,15 @@ IEC104Server::send(const vector& readings) if (value != nullptr) delete value; } else { - m_log->error(" --> Unknown data point name"); + // m_log->error(" --> Unknown data point name: %s", dp->getName().c_str()); + readingsSent++; } } n++; } - return readingsSent; + return n; } /** diff --git a/src/plugin.cpp b/src/plugin.cpp index 35a06f6..c4f7298 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -260,6 +260,8 @@ void plugin_register(PLUGIN_HANDLE handle, bool ( *write)(const char *name, const char *value, ControlDestination destination, ...), int (* operation)(char *operation, int paramCount, char *names[], char *parameters[], ControlDestination destination, ...)) { + Logger::getLogger()->info("plugin_register"); + IEC104Server* iec104 = (IEC104Server*)handle; iec104->registerControl(operation); From 6ecc6206340293794cb4f4d32ab3c6d7ad2e91f0 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 17 Feb 2023 17:14:44 +0000 Subject: [PATCH 5/7] - fixed control test cases Signed-off-by: Michael Zillgith --- tests/test_control.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/test_control.cpp b/tests/test_control.cpp index 86f5cae..96d245a 100644 --- a/tests/test_control.cpp +++ b/tests/test_control.cpp @@ -218,6 +218,7 @@ class ControlTest : public testing::Test void SetUp() override { operateHandlerCalled = 0; + requestSouthStatusCalled = 0; asduHandlerCalled = 0; actConReceived = 0; actConNegative = false; @@ -234,6 +235,7 @@ class ControlTest : public testing::Test } static int operateHandlerCalled; + static int requestSouthStatusCalled; static int operateHandler(char *operation, int paramCount, char* names[], char *parameters[], ControlDestination destination, ...); @@ -280,11 +282,16 @@ ControlTest::m_asduReceivedHandler(void* parameter, int address, CS101_ASDU asdu } int ControlTest::operateHandlerCalled; +int ControlTest::requestSouthStatusCalled; int ControlTest::operateHandler(char *operation, int paramCount, char* names[], char *parameters[], ControlDestination destination, ...) { - printf("operateHandler called\n"); - operateHandlerCalled++; + if (!strcmp(operation, "request_connection_status")) { + requestSouthStatusCalled++; + } + else { + operateHandlerCalled++; + } return 1; } From 77a544b063899df82371bce5b0aa9437fdea541e Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Mon, 20 Feb 2023 19:21:36 +0000 Subject: [PATCH 6/7] - update ci.yml Signed-off-by: Michael Zillgith --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0da2009..ca757a2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,7 +70,7 @@ jobs: - name: Download and install lib60870 env: LIB60870_REPO_URL: "https://github.com/mz-automation/lib60870.git" - MBEDTLS_REPO_URL: "https://github.com/ARMmbed/mbedtls/archive/refs/tags/v2.28.1.tar.gz" + MBEDTLS_REPO_URL: "https://github.com/ARMmbed/mbedtls/archive/refs/tags/v2.28.2.tar.gz" run: | cd $HOME git clone ${{ env.LIB60870_REPO_URL }} @@ -78,6 +78,7 @@ jobs: cd dependencies wget ${{ env.MBEDTLS_REPO_URL }} tar xf v2.28.1.tar.gz + mv mbedtls-2.28.2 mbedtls-2.28 cd .. mkdir build cd build From 77fcfa4bce5a1c70afa9c84e09881fe45f3bfab1 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Mon, 20 Feb 2023 20:11:31 +0000 Subject: [PATCH 7/7] - updated ci.yml Signed-off-by: Michael Zillgith --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ca757a2..f9ea3df 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -77,7 +77,7 @@ jobs: cd $HOME/lib60870/lib60870-C cd dependencies wget ${{ env.MBEDTLS_REPO_URL }} - tar xf v2.28.1.tar.gz + tar xf v2.28.2.tar.gz mv mbedtls-2.28.2 mbedtls-2.28 cd .. mkdir build