From 85b851d433788c94304230684d7df36c07f934fa Mon Sep 17 00:00:00 2001 From: zbthoma Date: Thu, 28 Mar 2024 16:24:15 -0600 Subject: [PATCH 1/3] Updates to IEC-104 server and client to support double point binary values. Bennu datastore still uses booleans for binary points, so we have to convert back and forth to make this work --- .../iec60870-5/module/ClientConnection.cpp | 57 ++++++--- .../iec60870-5/module/ClientConnection.hpp | 4 + .../comms/iec60870-5/module/Server.cpp | 119 ++++++++++++------ .../comms/iec60870-5/module/Server.hpp | 9 +- 4 files changed, 134 insertions(+), 55 deletions(-) diff --git a/src/bennu/devices/modules/comms/iec60870-5/module/ClientConnection.cpp b/src/bennu/devices/modules/comms/iec60870-5/module/ClientConnection.cpp index 8286814f..26059f7a 100644 --- a/src/bennu/devices/modules/comms/iec60870-5/module/ClientConnection.cpp +++ b/src/bennu/devices/modules/comms/iec60870-5/module/ClientConnection.cpp @@ -60,6 +60,14 @@ void ClientConnection::start(std::shared_ptr clientConnection) } } +int ClientConnection::convertBoolToDPValue(bool value) +{ + if (value == 0) + return IEC60870_DOUBLE_POINT_OFF; + else + return IEC60870_DOUBLE_POINT_ON; +} + StatusMessage ClientConnection::readRegisterByTag(const std::string& tag, comms::RegisterDescriptor& rd) { auto status = getRegisterDescriptorByTag(tag, rd) ? STATUS_SUCCESS : STATUS_FAIL; @@ -73,7 +81,7 @@ StatusMessage ClientConnection::readRegisterByTag(const std::string& tag, comms: return sm; } -StatusMessage ClientConnection::writeBinary(const std::string& tag, bool value) +StatusMessage ClientConnection::writeBinary(const std::string& tag, bool bvalue) { StatusMessage sm = STATUS_INIT; comms::RegisterDescriptor rd; @@ -86,15 +94,18 @@ StatusMessage ClientConnection::writeBinary(const std::string& tag, bool value) return sm; } - // Write boolean using protocol - std::cout << "Send single command C_SC_NA_1: " << tag << " -- " << value << std::endl; - InformationObject sc = (InformationObject) - SingleCommand_create(NULL, rd.mRegisterAddress, value, true, 0); - CS104_Connection_sendProcessCommandEx(mConnection, CS101_COT_ACTIVATION, 1, sc); - InformationObject_destroy(sc); + // Convert boolean to double point value + int value = ClientConnection::convertBoolToDPValue(bvalue); - // Update local data so we don't have to wait until the next poll - updateBinary(rd.mRegisterAddress, value); + // Write double point using protocol + std::cout << "Send double command C_DC_NA_1: " << tag << " -- " << bvalue << std::endl; + InformationObject dc = (InformationObject) + DoubleCommand_create(NULL, rd.mRegisterAddress, value, true, 0); + CS104_Connection_sendProcessCommandEx(mConnection, CS101_COT_ACTIVATION, 1, dc); + InformationObject_destroy(dc); + + // Update local data so we don't have to wait until the next poll; save boolean to datastore + updateBinary(rd.mRegisterAddress, bvalue); return sm; } @@ -194,29 +205,45 @@ bool ClientConnection::asduReceivedHandler(void* parameter, int address, CS101_A } } // Binary values - else if (CS101_ASDU_getTypeID(asdu) == M_SP_NA_1) { - printf(" single point information:\n"); + else if (CS101_ASDU_getTypeID(asdu) == M_DP_NA_1) { + printf(" double point information:\n"); int i; for (i = 0; i < CS101_ASDU_getNumberOfElements(asdu); i++) { - SinglePointInformation io = - (SinglePointInformation) CS101_ASDU_getElement(asdu, i); + DoublePointInformation io = + (DoublePointInformation) CS101_ASDU_getElement(asdu, i); uint16_t addr = InformationObject_getObjectAddress((InformationObject) io); - bool status = SinglePointInformation_getValue((SinglePointInformation) io); + DoublePointValue dp_value = DoublePointInformation_getValue((DoublePointInformation) io); + + bool status = 0; + if (dp_value == IEC60870_DOUBLE_POINT_OFF) + { + status = 0; + } + else if (dp_value == IEC60870_DOUBLE_POINT_ON) + { + status = 1; + } + else + { + printf("IOA: %i- Double point value is in indetrminate state..defaulting to 0\n", addr); + status = 0; + } printf(" IOA: %i value: %i\n", addr, status); gClientConnection->updateBinary(addr, status); - SinglePointInformation_destroy(io); + DoublePointInformation_destroy(io); } } return true; } + } // namespace iec60870 } // namespace comms } // namespace bennu diff --git a/src/bennu/devices/modules/comms/iec60870-5/module/ClientConnection.hpp b/src/bennu/devices/modules/comms/iec60870-5/module/ClientConnection.hpp index 37343e39..4f43d89b 100644 --- a/src/bennu/devices/modules/comms/iec60870-5/module/ClientConnection.hpp +++ b/src/bennu/devices/modules/comms/iec60870-5/module/ClientConnection.hpp @@ -9,6 +9,7 @@ #include "bennu/devices/modules/comms/base/Common.hpp" #include "bennu/devices/modules/comms/iec60870-5/protocol/src/inc/api/cs104_connection.h" +#include "bennu/devices/modules/comms/iec60870-5/protocol/src/inc/api/cs101_information_objects.h" namespace bennu { @@ -74,6 +75,8 @@ class ClientConnection : public std::enable_shared_from_this static void connectionHandler(void* parameter, CS104_Connection connection, CS104_ConnectionEvent event); static bool asduReceivedHandler(void* parameter, int address, CS101_ASDU asdu); + static int convertBoolToDPValue(bool value); + private: bool mRunning; std::string mRtuEndpoint; // IP/Port or DevName of remote RTU @@ -81,6 +84,7 @@ class ClientConnection : public std::enable_shared_from_this std::map mBinaryAddressToTagMapping; std::map mAnalogAddressToTagMapping; std::map mRegisters; + }; diff --git a/src/bennu/devices/modules/comms/iec60870-5/module/Server.cpp b/src/bennu/devices/modules/comms/iec60870-5/module/Server.cpp index ed0895c8..d7683f57 100644 --- a/src/bennu/devices/modules/comms/iec60870-5/module/Server.cpp +++ b/src/bennu/devices/modules/comms/iec60870-5/module/Server.cpp @@ -85,6 +85,51 @@ void Server::start(const std::string& endpoint, std::shared_ptr server, fflush(stdout); } +DoublePointValue Server::convertBoolToDPValue(bool status) +{ + if (status == 0) + return IEC60870_DOUBLE_POINT_OFF; + else + return IEC60870_DOUBLE_POINT_ON; +} + +DoublePointValue Server::convertIntToDPValue(int value) +{ + switch (value) + { + case 0: + return IEC60870_DOUBLE_POINT_INTERMEDIATE; + break; + case 1: + return IEC60870_DOUBLE_POINT_OFF; + break; + case 2: + return IEC60870_DOUBLE_POINT_ON; + break; + case 3: + return IEC60870_DOUBLE_POINT_INDETERMINATE; + break; + default: + return IEC60870_DOUBLE_POINT_INTERMEDIATE; + } +} + +/* + * Send spontaneous monitor update on specific data point + */ +void Server::sendSpontaneousUpdate(IMasterConnection connection, int ioa, DoublePointValue status) +{ + CS101_AppLayerParameters alParams = IMasterConnection_getApplicationLayerParameters(connection); + CS101_ASDU newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_SPONTANEOUS, 0, 1, false, false); + struct sCP56Time2a currentTime; + CP56Time2a_createFromMsTimestamp(¤tTime, Hal_getTimeInMs()); + InformationObject io = (InformationObject)DoublePointWithCP56Time2a_create(NULL, ioa, status, IEC60870_QUALITY_GOOD, ¤tTime); + CS101_ASDU_addInformationObject(newAsdu, io); + InformationObject_destroy(io); + IMasterConnection_sendASDU(connection, newAsdu); + CS101_ASDU_destroy(newAsdu); +} + /* * Reverse poll loop that sends local bennu datastore data to * connected clients. @@ -98,41 +143,15 @@ void Server::reversePoll() CS101_AppLayerParameters alParams = IMasterConnection_getApplicationLayerParameters(mConnection); // Send binary values - CS101_ASDU newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, 1, false, false); // kv = {
: {, eInput}} for (const auto &kv : gServer->mBinaryPoints) { const std::string tag = kv.second.first; - if (CS101_ASDU_getPayloadSize(newAsdu) < MAX_ASDU_PAYLOAD_SIZE) - { - if (gServer->mDataManager->hasTag(tag)) - { - auto status = gServer->mDataManager->getDataByTag(tag); - InformationObject io = (InformationObject)SinglePointInformation_create(NULL, kv.first, status, IEC60870_QUALITY_GOOD); - CS101_ASDU_addInformationObject(newAsdu, io); - InformationObject_destroy(io); - } - } - else - { - // Send current ASDU and create a new one for the remaining values - IMasterConnection_sendASDU(mConnection, newAsdu); - CS101_ASDU_destroy(newAsdu); - newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, 1, false, false); - if (gServer->mDataManager->hasTag(tag)) - { - auto status = gServer->mDataManager->getDataByTag(tag); - InformationObject io = (InformationObject)SinglePointInformation_create(NULL, kv.first, status, IEC60870_QUALITY_GOOD); - CS101_ASDU_addInformationObject(newAsdu, io); - InformationObject_destroy(io); - } - } + sendSpontaneousUpdate(mConnection, kv.first, convertBoolToDPValue(gServer->mDataManager->getDataByTag(tag))); } - IMasterConnection_sendASDU(mConnection, newAsdu); - CS101_ASDU_destroy(newAsdu); // Send analog values - newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, 1, false, false); + CS101_ASDU newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, 1, false, false); // kv = {
: {, eInput}} for (const auto &kv : gServer->mAnalogPoints) { @@ -215,8 +234,8 @@ bool Server::interrogationHandler(void *parameter, IMasterConnection connection, { if (gServer->mDataManager->hasTag(tag)) { - auto status = gServer->mDataManager->getDataByTag(tag); - InformationObject io = (InformationObject)SinglePointInformation_create(NULL, kv.first, status, IEC60870_QUALITY_GOOD); + auto status = Server::convertBoolToDPValue(gServer->mDataManager->getDataByTag(tag)); + InformationObject io = (InformationObject)DoublePointInformation_create(NULL, kv.first, status, IEC60870_QUALITY_GOOD); CS101_ASDU_addInformationObject(newAsdu, io); InformationObject_destroy(io); } @@ -229,8 +248,8 @@ bool Server::interrogationHandler(void *parameter, IMasterConnection connection, newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, 0, 1, false, false); if (gServer->mDataManager->hasTag(tag)) { - auto status = gServer->mDataManager->getDataByTag(tag); - InformationObject io = (InformationObject)SinglePointInformation_create(NULL, kv.first, status, IEC60870_QUALITY_GOOD); + auto status = Server::convertBoolToDPValue(gServer->mDataManager->getDataByTag(tag)); + InformationObject io = (InformationObject)DoublePointInformation_create(NULL, kv.first, status, IEC60870_QUALITY_GOOD); CS101_ASDU_addInformationObject(newAsdu, io); InformationObject_destroy(io); } @@ -284,9 +303,9 @@ bool Server::interrogationHandler(void *parameter, IMasterConnection connection, bool Server::asduHandler(void *parameter, IMasterConnection connection, CS101_ASDU asdu) { - if (CS101_ASDU_getTypeID(asdu) == C_SC_NA_1) + if (CS101_ASDU_getTypeID(asdu) == C_DC_NA_1) { - printf("received single command\n"); + printf("received double command\n"); if (CS101_ASDU_getCOT(asdu) == CS101_COT_ACTIVATION) { @@ -294,12 +313,16 @@ bool Server::asduHandler(void *parameter, IMasterConnection connection, CS101_AS if (io) { - SingleCommand sc = (SingleCommand)io; + // Send activation confirmation (application layer ACK) + IMasterConnection_sendACT_CON(connection, asdu, false); + + DoubleCommand dc = (DoubleCommand)io; uint16_t addr = InformationObject_getObjectAddress(io); - bool state = SingleCommand_getState(sc); + int state = DoubleCommand_getState(dc); printf("IOA: %i switch to %i\n", addr, state); gServer->writeBinary(addr, state); - CS101_ASDU_setCOT(asdu, CS101_COT_ACTIVATION_CON); + // Send activation termination + CS101_ASDU_setCOT(asdu, CS101_COT_ACTIVATION_TERMINATION); InformationObject_destroy(io); } else @@ -382,7 +405,7 @@ void Server::connectionEventHandler(void *parameter, IMasterConnection con, CS10 /* * Write binary value to datastore. */ -void Server::writeBinary(std::uint16_t address, bool value) +void Server::writeBinary(std::uint16_t address, int value) { std::ostringstream log_stream; log_stream << "Binary point command at address " << address << " with value " << value << "."; @@ -395,7 +418,24 @@ void Server::writeBinary(std::uint16_t address, bool value) logEvent("binary point command", "error", log_stream.str()); return; } - mDataManager->addUpdatedBinaryTag(iter->second.first, value); + + // Convert DoublePoint value to bool for datastore equivalence + bool bvalue = 0; + if (value == IEC60870_DOUBLE_POINT_OFF) + { + bvalue = 0; + } + else if (value == IEC60870_DOUBLE_POINT_ON) + { + bvalue = 1; + } + else + { + log_stream << "Double Point value is in indeterminate state..defaulting to 0"; + logEvent("binary point command", "error", log_stream.str()); + bvalue = 0; + } + mDataManager->addUpdatedBinaryTag(iter->second.first, bvalue); log_stream.str(""); log_stream << "Data successfully written."; logEvent("write binary", "info", log_stream.str()); @@ -463,6 +503,7 @@ bool Server::addAnalogOutput(const std::uint16_t address, const std::string &tag return false; } + } // namespace iec60870 } // namespace comms } // namespace bennu diff --git a/src/bennu/devices/modules/comms/iec60870-5/module/Server.hpp b/src/bennu/devices/modules/comms/iec60870-5/module/Server.hpp index d094ea72..ab772f44 100644 --- a/src/bennu/devices/modules/comms/iec60870-5/module/Server.hpp +++ b/src/bennu/devices/modules/comms/iec60870-5/module/Server.hpp @@ -9,6 +9,8 @@ #include "bennu/devices/field-device/DataManager.hpp" #include "bennu/devices/modules/comms/base/CommsModule.hpp" #include "bennu/devices/modules/comms/iec60870-5/protocol/src/inc/api/cs104_slave.h" +#include "bennu/devices/modules/comms/iec60870-5/protocol/src/inc/api/cs101_information_objects.h" +#include "bennu/devices/modules/comms/iec60870-5/protocol/src/hal/inc/hal_time.h" #include "bennu/utility/DirectLoggable.hpp" namespace bennu { @@ -51,7 +53,7 @@ class Server : public CommsModule, public utility::DirectLoggable, public std::e bool addAnalogOutput(const uint16_t address, const std::string& tag); - void writeBinary(uint16_t address, bool value); + void writeBinary(uint16_t address, int value); void writeAnalog(uint16_t address, float value); @@ -62,6 +64,10 @@ class Server : public CommsModule, public utility::DirectLoggable, public std::e static bool connectionRequestHandler(void* parameter, const char* ipAddress); static void connectionEventHandler(void* parameter, IMasterConnection con, CS104_PeerConnectionEvent event); + static DoublePointValue convertBoolToDPValue(bool status); + static DoublePointValue convertIntToDPValue(int status); + static void sendSpontaneousUpdate(IMasterConnection connection, int ioa, DoublePointValue value); + private: bool mConnected; uint32_t mReversePollRate; // Server reverse-poll rate @@ -70,6 +76,7 @@ class Server : public CommsModule, public utility::DirectLoggable, public std::e std::map> mBinaryPoints; std::map> mAnalogPoints; + }; // Static shared_ptr to server instance so it can be used inside static 104 callback handlers From 67b0944462ed28dddb48ac7f20dc241ed887dceb Mon Sep 17 00:00:00 2001 From: zbthoma Date: Tue, 7 May 2024 17:46:27 -0600 Subject: [PATCH 2/3] Altering reporting and command of binary points as single or double point values --- .../comms/iec60870-5/module/DataHandler.cpp | 3 +- .../comms/iec60870-5/module/Server.cpp | 263 +++++++++++++++++- .../comms/iec60870-5/module/Server.hpp | 11 +- 3 files changed, 264 insertions(+), 13 deletions(-) diff --git a/src/bennu/devices/modules/comms/iec60870-5/module/DataHandler.cpp b/src/bennu/devices/modules/comms/iec60870-5/module/DataHandler.cpp index 7316476f..9ea5fc53 100644 --- a/src/bennu/devices/modules/comms/iec60870-5/module/DataHandler.cpp +++ b/src/bennu/devices/modules/comms/iec60870-5/module/DataHandler.cpp @@ -53,6 +53,7 @@ void DataHandler::parseServerTree(std::shared_ptr server, const ptree& t std::string endpoint = tree.get("endpoint"); std::string log = tree.get("event-logging", "iec60870-5-104-server.log"); server->configureEventLogging(log); + std::string subtype = tree.get("subtype"); auto binaryInputs = tree.equal_range("binary-input"); for (auto iter = binaryInputs.first; iter != binaryInputs.second; ++iter) { @@ -88,7 +89,7 @@ void DataHandler::parseServerTree(std::shared_ptr server, const ptree& t // Initialize and start 104 server // - Pass server pointer to Server::start() so Server::gServer can be set statically // and used inside static 104 handlers - server->start(endpoint, server, rPollRate); + server->start(endpoint, server, rPollRate, subtype); } catch (ptree_bad_path& e) { diff --git a/src/bennu/devices/modules/comms/iec60870-5/module/Server.cpp b/src/bennu/devices/modules/comms/iec60870-5/module/Server.cpp index d7683f57..c8e3cdcc 100644 --- a/src/bennu/devices/modules/comms/iec60870-5/module/Server.cpp +++ b/src/bennu/devices/modules/comms/iec60870-5/module/Server.cpp @@ -17,7 +17,7 @@ Server::Server(std::shared_ptr dm) : setDataManager(dm); } -void Server::start(const std::string& endpoint, std::shared_ptr server, const uint32_t rPollRate) +void Server::start(const std::string& endpoint, std::shared_ptr server, const uint32_t rPollRate, std::string subtype) { // Set server reverse-poll rate mReversePollRate = rPollRate; @@ -45,7 +45,14 @@ void Server::start(const std::string& endpoint, std::shared_ptr server, CS104_Slave_setServerMode(slave, CS104_MODE_SINGLE_REDUNDANCY_GROUP); // Set the callback handler for the interrogation command - CS104_Slave_setInterrogationHandler(slave, interrogationHandler, NULL); + if (subtype.find("double") != std::string::npos) + { + CS104_Slave_setInterrogationHandler(slave, interrogationHandlerDoublePoint, NULL); + } + else + { + CS104_Slave_setInterrogationHandler(slave, interrogationHandlerSinglePoint, NULL); + } // Set handler for other message types CS104_Slave_setASDUHandler(slave, asduHandler, NULL); // Set handler to handle connection requests (optional) @@ -67,7 +74,14 @@ void Server::start(const std::string& endpoint, std::shared_ptr server, else { // Start server reverse-polling thread - pServerPollThread.reset(new std::thread(std::bind(&Server::reversePoll, this))); + if (subtype.find("double") != std::string::npos) + { + pServerPollThread.reset(new std::thread(std::bind(&Server::reversePollDoublePoint, this))); + } + else + { + pServerPollThread.reset(new std::thread(std::bind(&Server::reversePollSinglePoint, this))); + } } } @@ -132,9 +146,98 @@ void Server::sendSpontaneousUpdate(IMasterConnection connection, int ioa, Double /* * Reverse poll loop that sends local bennu datastore data to - * connected clients. + * connected clients. Sends indications as single point values */ -void Server::reversePoll() +void Server::reversePollSinglePoint() +{ + while (1) + { + if (mConnected) + { + CS101_AppLayerParameters alParams = IMasterConnection_getApplicationLayerParameters(mConnection); + + // Send binary values + CS101_ASDU newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, 1, false, false); + // kv = {
: {, eInput}} + for (const auto &kv : gServer->mBinaryPoints) + { + const std::string tag = kv.second.first; + if (CS101_ASDU_getPayloadSize(newAsdu) < MAX_ASDU_PAYLOAD_SIZE) + { + if (gServer->mDataManager->hasTag(tag)) + { + auto status = gServer->mDataManager->getDataByTag(tag); + InformationObject io = (InformationObject)SinglePointInformation_create(NULL, kv.first, status, IEC60870_QUALITY_GOOD); + CS101_ASDU_addInformationObject(newAsdu, io); + InformationObject_destroy(io); + } + } + else + { + // Send current ASDU and create a new one for the remaining values + IMasterConnection_sendASDU(mConnection, newAsdu); + CS101_ASDU_destroy(newAsdu); + newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, 1, false, false); + if (gServer->mDataManager->hasTag(tag)) + { + auto status = gServer->mDataManager->getDataByTag(tag); + InformationObject io = (InformationObject)SinglePointInformation_create(NULL, kv.first, status, IEC60870_QUALITY_GOOD); + CS101_ASDU_addInformationObject(newAsdu, io); + InformationObject_destroy(io); + } + } + } + IMasterConnection_sendASDU(mConnection, newAsdu); + CS101_ASDU_destroy(newAsdu); + + // Send analog values + newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, 1, false, false); + // kv = {
: {, eInput}} + for (const auto &kv : gServer->mAnalogPoints) + { + const std::string tag = kv.second.first; + if (CS101_ASDU_getPayloadSize(newAsdu) < MAX_ASDU_PAYLOAD_SIZE) + { + if (gServer->mDataManager->hasTag(tag)) + { + auto val = gServer->mDataManager->getDataByTag(tag); + InformationObject io = (InformationObject)MeasuredValueShort_create(NULL, kv.first, val, IEC60870_QUALITY_GOOD); + CS101_ASDU_addInformationObject(newAsdu, io); + InformationObject_destroy(io); + } + } + else + { + // Send current ASDU and create a new one for the remaining values + IMasterConnection_sendASDU(mConnection, newAsdu); + CS101_ASDU_destroy(newAsdu); + newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, 1, false, false); + if (gServer->mDataManager->hasTag(tag)) + { + auto val = gServer->mDataManager->getDataByTag(tag); + InformationObject io = (InformationObject)MeasuredValueShort_create(NULL, kv.first, val, IEC60870_QUALITY_GOOD); + CS101_ASDU_addInformationObject(newAsdu, io); + InformationObject_destroy(io); + } + } + } + IMasterConnection_sendASDU(mConnection, newAsdu); + CS101_ASDU_destroy(newAsdu); + std::this_thread::sleep_for(std::chrono::seconds(mReversePollRate)); + } + else + { + // Wait until connected to client + while (!mConnected) { std::this_thread::sleep_for(std::chrono::seconds(1)); } + } + } +} + +/* + * Reverse poll loop that sends local bennu datastore data to + * connected clients. Sends indications as double point values + */ +void Server::reversePollDoublePoint() { while (1) { @@ -213,9 +316,98 @@ void Server::rawMessageHandler(void *parameter, IMasterConnection con, uint8_t * } /* -* Callback handler for interrogation messages +* Callback handler for interrogation messages that reports indications as single point values */ -bool Server::interrogationHandler(void *parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi) +bool Server::interrogationHandlerSinglePoint(void *parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi) +{ + printf("Received interrogation for group %i\n", qoi); + + if (qoi == 20) /* only handle station interrogation */ + { + CS101_AppLayerParameters alParams = IMasterConnection_getApplicationLayerParameters(connection); + IMasterConnection_sendACT_CON(connection, asdu, false); + + // Send binary values + CS101_ASDU newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, 0, 1, false, false); + // kv = {
: {, eInput}} + for (const auto &kv : gServer->mBinaryPoints) + { + const std::string tag = kv.second.first; + if (CS101_ASDU_getPayloadSize(newAsdu) < MAX_ASDU_PAYLOAD_SIZE) + { + if (gServer->mDataManager->hasTag(tag)) + { + auto status = gServer->mDataManager->getDataByTag(tag); + InformationObject io = (InformationObject)SinglePointInformation_create(NULL, kv.first, status, IEC60870_QUALITY_GOOD); + CS101_ASDU_addInformationObject(newAsdu, io); + InformationObject_destroy(io); + } + } + else + { + // Send current ASDU and create a new one for the remaining values + IMasterConnection_sendASDU(connection, newAsdu); + CS101_ASDU_destroy(newAsdu); + newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, 0, 1, false, false); + if (gServer->mDataManager->hasTag(tag)) + { + auto status = gServer->mDataManager->getDataByTag(tag); + InformationObject io = (InformationObject)SinglePointInformation_create(NULL, kv.first, status, IEC60870_QUALITY_GOOD); + CS101_ASDU_addInformationObject(newAsdu, io); + InformationObject_destroy(io); + } + } + } + IMasterConnection_sendASDU(connection, newAsdu); + CS101_ASDU_destroy(newAsdu); + + // Send analog values + newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, 0, 1, false, false); + // kv = {
: {, eInput}} + for (const auto &kv : gServer->mAnalogPoints) + { + const std::string tag = kv.second.first; + if (CS101_ASDU_getPayloadSize(newAsdu) < MAX_ASDU_PAYLOAD_SIZE) + { + if (gServer->mDataManager->hasTag(tag)) + { + auto val = gServer->mDataManager->getDataByTag(tag); + InformationObject io = (InformationObject)MeasuredValueShort_create(NULL, kv.first, val, IEC60870_QUALITY_GOOD); + CS101_ASDU_addInformationObject(newAsdu, io); + InformationObject_destroy(io); + } + } + else + { + // Send current ASDU and create a new one for the remaining values + IMasterConnection_sendASDU(connection, newAsdu); + CS101_ASDU_destroy(newAsdu); + newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, 0, 1, false, false); + if (gServer->mDataManager->hasTag(tag)) + { + auto val = gServer->mDataManager->getDataByTag(tag); + InformationObject io = (InformationObject)MeasuredValueShort_create(NULL, kv.first, val, IEC60870_QUALITY_GOOD); + CS101_ASDU_addInformationObject(newAsdu, io); + InformationObject_destroy(io); + } + } + } + IMasterConnection_sendASDU(connection, newAsdu); + CS101_ASDU_destroy(newAsdu); + IMasterConnection_sendACT_TERM(connection, asdu); + } + else + { + IMasterConnection_sendACT_CON(connection, asdu, true); + } + + return true; +} + +/* +* Callback handler for interrogation messages that reports indications as double point values +*/ +bool Server::interrogationHandlerDoublePoint(void *parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi) { printf("Received interrogation for group %i\n", qoi); @@ -303,7 +495,38 @@ bool Server::interrogationHandler(void *parameter, IMasterConnection connection, bool Server::asduHandler(void *parameter, IMasterConnection connection, CS101_ASDU asdu) { - if (CS101_ASDU_getTypeID(asdu) == C_DC_NA_1) + if (CS101_ASDU_getTypeID(asdu) == C_SC_NA_1) + { + printf("received single command\n"); + + if (CS101_ASDU_getCOT(asdu) == CS101_COT_ACTIVATION) + { + InformationObject io = CS101_ASDU_getElement(asdu, 0); + + if (io) + { + SingleCommand sc = (SingleCommand)io; + uint16_t addr = InformationObject_getObjectAddress(io); + bool state = SingleCommand_getState(sc); + printf("IOA: %i switch to %i\n", addr, state); + gServer->writeBinary(addr, state); + CS101_ASDU_setCOT(asdu, CS101_COT_ACTIVATION_CON); + InformationObject_destroy(io); + } + else + { + printf("ERROR: message has no valid information object\n"); + return true; + } + } + else + CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_COT); + + IMasterConnection_sendASDU(connection, asdu); + + return true; + } + else if (CS101_ASDU_getTypeID(asdu) == C_DC_NA_1) { printf("received double command\n"); @@ -403,7 +626,29 @@ void Server::connectionEventHandler(void *parameter, IMasterConnection con, CS10 } /* -* Write binary value to datastore. +* Write binary value to datastore. Note this variant takes a bool argument (for single point indications) +*/ +void Server::writeBinary(std::uint16_t address, bool value) +{ + std::ostringstream log_stream; + log_stream << "Binary point command at address " << address << " with value " << value << "."; + logEvent("iec60870-5-104 Server writeBinary", "info", log_stream.str()); + auto iter = mBinaryPoints.find(address); + if (iter == mBinaryPoints.end()) + { + log_stream.str(""); + log_stream << "Invalid binary point command request address: " << address; + logEvent("binary point command", "error", log_stream.str()); + return; + } + mDataManager->addUpdatedBinaryTag(iter->second.first, value); + log_stream.str(""); + log_stream << "Data successfully written."; + logEvent("write binary", "info", log_stream.str()); +} + +/* +* Write binary value to datastore. Note this variant takes an int argument (for double point indications) */ void Server::writeBinary(std::uint16_t address, int value) { diff --git a/src/bennu/devices/modules/comms/iec60870-5/module/Server.hpp b/src/bennu/devices/modules/comms/iec60870-5/module/Server.hpp index ab772f44..f60b83ac 100644 --- a/src/bennu/devices/modules/comms/iec60870-5/module/Server.hpp +++ b/src/bennu/devices/modules/comms/iec60870-5/module/Server.hpp @@ -41,9 +41,11 @@ class Server : public CommsModule, public utility::DirectLoggable, public std::e public: Server(std::shared_ptr dm); - void start(const std::string& endpoint, std::shared_ptr server, const uint32_t rPollRate); + void start(const std::string& endpoint, std::shared_ptr server, const uint32_t rPollRate, std::string subtype); - void reversePoll(); + void reversePollSinglePoint(); + + void reversePollDoublePoint(); bool addBinaryInput(const uint16_t address, const std::string& tag); @@ -53,13 +55,16 @@ class Server : public CommsModule, public utility::DirectLoggable, public std::e bool addAnalogOutput(const uint16_t address, const std::string& tag); + void writeBinary(uint16_t address, bool value); + void writeBinary(uint16_t address, int value); void writeAnalog(uint16_t address, float value); // IEC60870-5-104 message callback handlers static void rawMessageHandler(void* parameter, IMasterConnection con, uint8_t* msg, int msgSize, bool sent); - static bool interrogationHandler(void* parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi); + static bool interrogationHandlerSinglePoint(void* parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi); + static bool interrogationHandlerDoublePoint(void* parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi); static bool asduHandler(void* parameter, IMasterConnection connection, CS101_ASDU asdu); static bool connectionRequestHandler(void* parameter, const char* ipAddress); static void connectionEventHandler(void* parameter, IMasterConnection con, CS104_PeerConnectionEvent event); From 17e6cb3fba5a50299233c0b321413f99029fd297 Mon Sep 17 00:00:00 2001 From: zbthoma Date: Wed, 8 May 2024 18:49:37 -0600 Subject: [PATCH 3/3] Edits and cleanup for merge --- .../iec60870-5/module/ClientConnection.cpp | 2 +- .../comms/iec60870-5/module/Server.cpp | 211 ++++++------------ 2 files changed, 74 insertions(+), 139 deletions(-) diff --git a/src/bennu/devices/modules/comms/iec60870-5/module/ClientConnection.cpp b/src/bennu/devices/modules/comms/iec60870-5/module/ClientConnection.cpp index 26059f7a..30e338bf 100644 --- a/src/bennu/devices/modules/comms/iec60870-5/module/ClientConnection.cpp +++ b/src/bennu/devices/modules/comms/iec60870-5/module/ClientConnection.cpp @@ -229,7 +229,7 @@ bool ClientConnection::asduReceivedHandler(void* parameter, int address, CS101_A } else { - printf("IOA: %i- Double point value is in indetrminate state..defaulting to 0\n", addr); + printf("IOA: %i- Double point value is in indeterminate state..defaulting to 0\n", addr); status = 0; } printf(" IOA: %i value: %i\n", addr, status); diff --git a/src/bennu/devices/modules/comms/iec60870-5/module/Server.cpp b/src/bennu/devices/modules/comms/iec60870-5/module/Server.cpp index c8e3cdcc..1a2c18da 100644 --- a/src/bennu/devices/modules/comms/iec60870-5/module/Server.cpp +++ b/src/bennu/devices/modules/comms/iec60870-5/module/Server.cpp @@ -113,16 +113,12 @@ DoublePointValue Server::convertIntToDPValue(int value) { case 0: return IEC60870_DOUBLE_POINT_INTERMEDIATE; - break; case 1: return IEC60870_DOUBLE_POINT_OFF; - break; case 2: return IEC60870_DOUBLE_POINT_ON; - break; case 3: return IEC60870_DOUBLE_POINT_INDETERMINATE; - break; default: return IEC60870_DOUBLE_POINT_INTERMEDIATE; } @@ -162,29 +158,20 @@ void Server::reversePollSinglePoint() for (const auto &kv : gServer->mBinaryPoints) { const std::string tag = kv.second.first; - if (CS101_ASDU_getPayloadSize(newAsdu) < MAX_ASDU_PAYLOAD_SIZE) - { - if (gServer->mDataManager->hasTag(tag)) - { - auto status = gServer->mDataManager->getDataByTag(tag); - InformationObject io = (InformationObject)SinglePointInformation_create(NULL, kv.first, status, IEC60870_QUALITY_GOOD); - CS101_ASDU_addInformationObject(newAsdu, io); - InformationObject_destroy(io); - } - } - else + if (CS101_ASDU_getPayloadSize(newAsdu) >= MAX_ASDU_PAYLOAD_SIZE) { // Send current ASDU and create a new one for the remaining values IMasterConnection_sendASDU(mConnection, newAsdu); CS101_ASDU_destroy(newAsdu); newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, 1, false, false); - if (gServer->mDataManager->hasTag(tag)) - { - auto status = gServer->mDataManager->getDataByTag(tag); - InformationObject io = (InformationObject)SinglePointInformation_create(NULL, kv.first, status, IEC60870_QUALITY_GOOD); - CS101_ASDU_addInformationObject(newAsdu, io); - InformationObject_destroy(io); - } + } + + if (gServer->mDataManager->hasTag(tag)) + { + auto status = gServer->mDataManager->getDataByTag(tag); + InformationObject io = (InformationObject)SinglePointInformation_create(NULL, kv.first, status, IEC60870_QUALITY_GOOD); + CS101_ASDU_addInformationObject(newAsdu, io); + InformationObject_destroy(io); } } IMasterConnection_sendASDU(mConnection, newAsdu); @@ -196,29 +183,20 @@ void Server::reversePollSinglePoint() for (const auto &kv : gServer->mAnalogPoints) { const std::string tag = kv.second.first; - if (CS101_ASDU_getPayloadSize(newAsdu) < MAX_ASDU_PAYLOAD_SIZE) - { - if (gServer->mDataManager->hasTag(tag)) - { - auto val = gServer->mDataManager->getDataByTag(tag); - InformationObject io = (InformationObject)MeasuredValueShort_create(NULL, kv.first, val, IEC60870_QUALITY_GOOD); - CS101_ASDU_addInformationObject(newAsdu, io); - InformationObject_destroy(io); - } - } - else + if (CS101_ASDU_getPayloadSize(newAsdu) >= MAX_ASDU_PAYLOAD_SIZE) { // Send current ASDU and create a new one for the remaining values IMasterConnection_sendASDU(mConnection, newAsdu); CS101_ASDU_destroy(newAsdu); newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, 1, false, false); - if (gServer->mDataManager->hasTag(tag)) - { - auto val = gServer->mDataManager->getDataByTag(tag); - InformationObject io = (InformationObject)MeasuredValueShort_create(NULL, kv.first, val, IEC60870_QUALITY_GOOD); - CS101_ASDU_addInformationObject(newAsdu, io); - InformationObject_destroy(io); - } + } + + if (gServer->mDataManager->hasTag(tag)) + { + auto val = gServer->mDataManager->getDataByTag(tag); + InformationObject io = (InformationObject)MeasuredValueShort_create(NULL, kv.first, val, IEC60870_QUALITY_GOOD); + CS101_ASDU_addInformationObject(newAsdu, io); + InformationObject_destroy(io); } } IMasterConnection_sendASDU(mConnection, newAsdu); @@ -259,29 +237,20 @@ void Server::reversePollDoublePoint() for (const auto &kv : gServer->mAnalogPoints) { const std::string tag = kv.second.first; - if (CS101_ASDU_getPayloadSize(newAsdu) < MAX_ASDU_PAYLOAD_SIZE) - { - if (gServer->mDataManager->hasTag(tag)) - { - auto val = gServer->mDataManager->getDataByTag(tag); - InformationObject io = (InformationObject)MeasuredValueShort_create(NULL, kv.first, val, IEC60870_QUALITY_GOOD); - CS101_ASDU_addInformationObject(newAsdu, io); - InformationObject_destroy(io); - } - } - else + if (CS101_ASDU_getPayloadSize(newAsdu) >= MAX_ASDU_PAYLOAD_SIZE) { // Send current ASDU and create a new one for the remaining values IMasterConnection_sendASDU(mConnection, newAsdu); CS101_ASDU_destroy(newAsdu); newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, 1, false, false); - if (gServer->mDataManager->hasTag(tag)) - { - auto val = gServer->mDataManager->getDataByTag(tag); - InformationObject io = (InformationObject)MeasuredValueShort_create(NULL, kv.first, val, IEC60870_QUALITY_GOOD); - CS101_ASDU_addInformationObject(newAsdu, io); - InformationObject_destroy(io); - } + } + + if (gServer->mDataManager->hasTag(tag)) + { + auto val = gServer->mDataManager->getDataByTag(tag); + InformationObject io = (InformationObject)MeasuredValueShort_create(NULL, kv.first, val, IEC60870_QUALITY_GOOD); + CS101_ASDU_addInformationObject(newAsdu, io); + InformationObject_destroy(io); } } IMasterConnection_sendASDU(mConnection, newAsdu); @@ -320,7 +289,7 @@ void Server::rawMessageHandler(void *parameter, IMasterConnection con, uint8_t * */ bool Server::interrogationHandlerSinglePoint(void *parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi) { - printf("Received interrogation for group %i\n", qoi); + std::cout << "Received interrogation for group " << static_cast(qoi) << std::endl; if (qoi == 20) /* only handle station interrogation */ { @@ -333,29 +302,20 @@ bool Server::interrogationHandlerSinglePoint(void *parameter, IMasterConnection for (const auto &kv : gServer->mBinaryPoints) { const std::string tag = kv.second.first; - if (CS101_ASDU_getPayloadSize(newAsdu) < MAX_ASDU_PAYLOAD_SIZE) - { - if (gServer->mDataManager->hasTag(tag)) - { - auto status = gServer->mDataManager->getDataByTag(tag); - InformationObject io = (InformationObject)SinglePointInformation_create(NULL, kv.first, status, IEC60870_QUALITY_GOOD); - CS101_ASDU_addInformationObject(newAsdu, io); - InformationObject_destroy(io); - } - } - else + if (CS101_ASDU_getPayloadSize(newAsdu) >= MAX_ASDU_PAYLOAD_SIZE) { // Send current ASDU and create a new one for the remaining values IMasterConnection_sendASDU(connection, newAsdu); CS101_ASDU_destroy(newAsdu); newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, 0, 1, false, false); - if (gServer->mDataManager->hasTag(tag)) - { - auto status = gServer->mDataManager->getDataByTag(tag); - InformationObject io = (InformationObject)SinglePointInformation_create(NULL, kv.first, status, IEC60870_QUALITY_GOOD); - CS101_ASDU_addInformationObject(newAsdu, io); - InformationObject_destroy(io); - } + } + + if (gServer->mDataManager->hasTag(tag)) + { + auto status = gServer->mDataManager->getDataByTag(tag); + InformationObject io = (InformationObject)SinglePointInformation_create(NULL, kv.first, status, IEC60870_QUALITY_GOOD); + CS101_ASDU_addInformationObject(newAsdu, io); + InformationObject_destroy(io); } } IMasterConnection_sendASDU(connection, newAsdu); @@ -367,29 +327,20 @@ bool Server::interrogationHandlerSinglePoint(void *parameter, IMasterConnection for (const auto &kv : gServer->mAnalogPoints) { const std::string tag = kv.second.first; - if (CS101_ASDU_getPayloadSize(newAsdu) < MAX_ASDU_PAYLOAD_SIZE) - { - if (gServer->mDataManager->hasTag(tag)) - { - auto val = gServer->mDataManager->getDataByTag(tag); - InformationObject io = (InformationObject)MeasuredValueShort_create(NULL, kv.first, val, IEC60870_QUALITY_GOOD); - CS101_ASDU_addInformationObject(newAsdu, io); - InformationObject_destroy(io); - } - } - else + if (CS101_ASDU_getPayloadSize(newAsdu) >= MAX_ASDU_PAYLOAD_SIZE) { // Send current ASDU and create a new one for the remaining values IMasterConnection_sendASDU(connection, newAsdu); CS101_ASDU_destroy(newAsdu); newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, 0, 1, false, false); - if (gServer->mDataManager->hasTag(tag)) - { - auto val = gServer->mDataManager->getDataByTag(tag); - InformationObject io = (InformationObject)MeasuredValueShort_create(NULL, kv.first, val, IEC60870_QUALITY_GOOD); - CS101_ASDU_addInformationObject(newAsdu, io); - InformationObject_destroy(io); - } + } + + if (gServer->mDataManager->hasTag(tag)) + { + auto val = gServer->mDataManager->getDataByTag(tag); + InformationObject io = (InformationObject)MeasuredValueShort_create(NULL, kv.first, val, IEC60870_QUALITY_GOOD); + CS101_ASDU_addInformationObject(newAsdu, io); + InformationObject_destroy(io); } } IMasterConnection_sendASDU(connection, newAsdu); @@ -409,7 +360,7 @@ bool Server::interrogationHandlerSinglePoint(void *parameter, IMasterConnection */ bool Server::interrogationHandlerDoublePoint(void *parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi) { - printf("Received interrogation for group %i\n", qoi); + std::cout << "Received interrogation for group " << static_cast(qoi) << std::endl; if (qoi == 20) /* only handle station interrogation */ { @@ -422,29 +373,20 @@ bool Server::interrogationHandlerDoublePoint(void *parameter, IMasterConnection for (const auto &kv : gServer->mBinaryPoints) { const std::string tag = kv.second.first; - if (CS101_ASDU_getPayloadSize(newAsdu) < MAX_ASDU_PAYLOAD_SIZE) - { - if (gServer->mDataManager->hasTag(tag)) - { - auto status = Server::convertBoolToDPValue(gServer->mDataManager->getDataByTag(tag)); - InformationObject io = (InformationObject)DoublePointInformation_create(NULL, kv.first, status, IEC60870_QUALITY_GOOD); - CS101_ASDU_addInformationObject(newAsdu, io); - InformationObject_destroy(io); - } - } - else + if (CS101_ASDU_getPayloadSize(newAsdu) >= MAX_ASDU_PAYLOAD_SIZE) { // Send current ASDU and create a new one for the remaining values IMasterConnection_sendASDU(connection, newAsdu); CS101_ASDU_destroy(newAsdu); newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, 0, 1, false, false); - if (gServer->mDataManager->hasTag(tag)) - { - auto status = Server::convertBoolToDPValue(gServer->mDataManager->getDataByTag(tag)); - InformationObject io = (InformationObject)DoublePointInformation_create(NULL, kv.first, status, IEC60870_QUALITY_GOOD); - CS101_ASDU_addInformationObject(newAsdu, io); - InformationObject_destroy(io); - } + } + + if (gServer->mDataManager->hasTag(tag)) + { + auto status = Server::convertBoolToDPValue(gServer->mDataManager->getDataByTag(tag)); + InformationObject io = (InformationObject)DoublePointInformation_create(NULL, kv.first, status, IEC60870_QUALITY_GOOD); + CS101_ASDU_addInformationObject(newAsdu, io); + InformationObject_destroy(io); } } IMasterConnection_sendASDU(connection, newAsdu); @@ -456,29 +398,20 @@ bool Server::interrogationHandlerDoublePoint(void *parameter, IMasterConnection for (const auto &kv : gServer->mAnalogPoints) { const std::string tag = kv.second.first; - if (CS101_ASDU_getPayloadSize(newAsdu) < MAX_ASDU_PAYLOAD_SIZE) - { - if (gServer->mDataManager->hasTag(tag)) - { - auto val = gServer->mDataManager->getDataByTag(tag); - InformationObject io = (InformationObject)MeasuredValueShort_create(NULL, kv.first, val, IEC60870_QUALITY_GOOD); - CS101_ASDU_addInformationObject(newAsdu, io); - InformationObject_destroy(io); - } - } - else + if (CS101_ASDU_getPayloadSize(newAsdu) >= MAX_ASDU_PAYLOAD_SIZE) { // Send current ASDU and create a new one for the remaining values IMasterConnection_sendASDU(connection, newAsdu); CS101_ASDU_destroy(newAsdu); newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, 0, 1, false, false); - if (gServer->mDataManager->hasTag(tag)) - { - auto val = gServer->mDataManager->getDataByTag(tag); - InformationObject io = (InformationObject)MeasuredValueShort_create(NULL, kv.first, val, IEC60870_QUALITY_GOOD); - CS101_ASDU_addInformationObject(newAsdu, io); - InformationObject_destroy(io); - } + } + + if (gServer->mDataManager->hasTag(tag)) + { + auto val = gServer->mDataManager->getDataByTag(tag); + InformationObject io = (InformationObject)MeasuredValueShort_create(NULL, kv.first, val, IEC60870_QUALITY_GOOD); + CS101_ASDU_addInformationObject(newAsdu, io); + InformationObject_destroy(io); } } IMasterConnection_sendASDU(connection, newAsdu); @@ -497,7 +430,7 @@ bool Server::asduHandler(void *parameter, IMasterConnection connection, CS101_AS { if (CS101_ASDU_getTypeID(asdu) == C_SC_NA_1) { - printf("received single command\n"); + std::cout << "received single command" << std::endl; if (CS101_ASDU_getCOT(asdu) == CS101_COT_ACTIVATION) { @@ -515,8 +448,8 @@ bool Server::asduHandler(void *parameter, IMasterConnection connection, CS101_AS } else { - printf("ERROR: message has no valid information object\n"); - return true; + std::cout << "ERROR: message has no valid information object" << std::endl; + return false; } } else @@ -528,7 +461,7 @@ bool Server::asduHandler(void *parameter, IMasterConnection connection, CS101_AS } else if (CS101_ASDU_getTypeID(asdu) == C_DC_NA_1) { - printf("received double command\n"); + std::cout << "received double command" << std::endl; if (CS101_ASDU_getCOT(asdu) == CS101_COT_ACTIVATION) { @@ -550,12 +483,14 @@ bool Server::asduHandler(void *parameter, IMasterConnection connection, CS101_AS } else { - printf("ERROR: message has no valid information object\n"); - return true; + std::cout << "ERROR: message has no valid information object" << std::endl; + return false; } } else + { CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_COT); + } IMasterConnection_sendASDU(connection, asdu);