Skip to content

Commit 67b0944

Browse files
committed
Altering reporting and command of binary points as single or double point values
1 parent 85b851d commit 67b0944

File tree

3 files changed

+264
-13
lines changed

3 files changed

+264
-13
lines changed

src/bennu/devices/modules/comms/iec60870-5/module/DataHandler.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ void DataHandler::parseServerTree(std::shared_ptr<Server> server, const ptree& t
5353
std::string endpoint = tree.get<std::string>("endpoint");
5454
std::string log = tree.get<std::string>("event-logging", "iec60870-5-104-server.log");
5555
server->configureEventLogging(log);
56+
std::string subtype = tree.get<std::string>("subtype");
5657
auto binaryInputs = tree.equal_range("binary-input");
5758
for (auto iter = binaryInputs.first; iter != binaryInputs.second; ++iter)
5859
{
@@ -88,7 +89,7 @@ void DataHandler::parseServerTree(std::shared_ptr<Server> server, const ptree& t
8889
// Initialize and start 104 server
8990
// - Pass server pointer to Server::start() so Server::gServer can be set statically
9091
// and used inside static 104 handlers
91-
server->start(endpoint, server, rPollRate);
92+
server->start(endpoint, server, rPollRate, subtype);
9293
}
9394
catch (ptree_bad_path& e)
9495
{

src/bennu/devices/modules/comms/iec60870-5/module/Server.cpp

+254-9
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Server::Server(std::shared_ptr<field_device::DataManager> dm) :
1717
setDataManager(dm);
1818
}
1919

20-
void Server::start(const std::string& endpoint, std::shared_ptr<Server> server, const uint32_t rPollRate)
20+
void Server::start(const std::string& endpoint, std::shared_ptr<Server> server, const uint32_t rPollRate, std::string subtype)
2121
{
2222
// Set server reverse-poll rate
2323
mReversePollRate = rPollRate;
@@ -45,7 +45,14 @@ void Server::start(const std::string& endpoint, std::shared_ptr<Server> server,
4545
CS104_Slave_setServerMode(slave, CS104_MODE_SINGLE_REDUNDANCY_GROUP);
4646

4747
// Set the callback handler for the interrogation command
48-
CS104_Slave_setInterrogationHandler(slave, interrogationHandler, NULL);
48+
if (subtype.find("double") != std::string::npos)
49+
{
50+
CS104_Slave_setInterrogationHandler(slave, interrogationHandlerDoublePoint, NULL);
51+
}
52+
else
53+
{
54+
CS104_Slave_setInterrogationHandler(slave, interrogationHandlerSinglePoint, NULL);
55+
}
4956
// Set handler for other message types
5057
CS104_Slave_setASDUHandler(slave, asduHandler, NULL);
5158
// Set handler to handle connection requests (optional)
@@ -67,7 +74,14 @@ void Server::start(const std::string& endpoint, std::shared_ptr<Server> server,
6774
else
6875
{
6976
// Start server reverse-polling thread
70-
pServerPollThread.reset(new std::thread(std::bind(&Server::reversePoll, this)));
77+
if (subtype.find("double") != std::string::npos)
78+
{
79+
pServerPollThread.reset(new std::thread(std::bind(&Server::reversePollDoublePoint, this)));
80+
}
81+
else
82+
{
83+
pServerPollThread.reset(new std::thread(std::bind(&Server::reversePollSinglePoint, this)));
84+
}
7185
}
7286

7387
}
@@ -132,9 +146,98 @@ void Server::sendSpontaneousUpdate(IMasterConnection connection, int ioa, Double
132146

133147
/*
134148
* Reverse poll loop that sends local bennu datastore data to
135-
* connected clients.
149+
* connected clients. Sends indications as single point values
136150
*/
137-
void Server::reversePoll()
151+
void Server::reversePollSinglePoint()
152+
{
153+
while (1)
154+
{
155+
if (mConnected)
156+
{
157+
CS101_AppLayerParameters alParams = IMasterConnection_getApplicationLayerParameters(mConnection);
158+
159+
// Send binary values
160+
CS101_ASDU newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, 1, false, false);
161+
// kv = {<address>: {<tag>, eInput}}
162+
for (const auto &kv : gServer->mBinaryPoints)
163+
{
164+
const std::string tag = kv.second.first;
165+
if (CS101_ASDU_getPayloadSize(newAsdu) < MAX_ASDU_PAYLOAD_SIZE)
166+
{
167+
if (gServer->mDataManager->hasTag(tag))
168+
{
169+
auto status = gServer->mDataManager->getDataByTag<bool>(tag);
170+
InformationObject io = (InformationObject)SinglePointInformation_create(NULL, kv.first, status, IEC60870_QUALITY_GOOD);
171+
CS101_ASDU_addInformationObject(newAsdu, io);
172+
InformationObject_destroy(io);
173+
}
174+
}
175+
else
176+
{
177+
// Send current ASDU and create a new one for the remaining values
178+
IMasterConnection_sendASDU(mConnection, newAsdu);
179+
CS101_ASDU_destroy(newAsdu);
180+
newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, 1, false, false);
181+
if (gServer->mDataManager->hasTag(tag))
182+
{
183+
auto status = gServer->mDataManager->getDataByTag<bool>(tag);
184+
InformationObject io = (InformationObject)SinglePointInformation_create(NULL, kv.first, status, IEC60870_QUALITY_GOOD);
185+
CS101_ASDU_addInformationObject(newAsdu, io);
186+
InformationObject_destroy(io);
187+
}
188+
}
189+
}
190+
IMasterConnection_sendASDU(mConnection, newAsdu);
191+
CS101_ASDU_destroy(newAsdu);
192+
193+
// Send analog values
194+
newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, 1, false, false);
195+
// kv = {<address>: {<tag>, eInput}}
196+
for (const auto &kv : gServer->mAnalogPoints)
197+
{
198+
const std::string tag = kv.second.first;
199+
if (CS101_ASDU_getPayloadSize(newAsdu) < MAX_ASDU_PAYLOAD_SIZE)
200+
{
201+
if (gServer->mDataManager->hasTag(tag))
202+
{
203+
auto val = gServer->mDataManager->getDataByTag<double>(tag);
204+
InformationObject io = (InformationObject)MeasuredValueShort_create(NULL, kv.first, val, IEC60870_QUALITY_GOOD);
205+
CS101_ASDU_addInformationObject(newAsdu, io);
206+
InformationObject_destroy(io);
207+
}
208+
}
209+
else
210+
{
211+
// Send current ASDU and create a new one for the remaining values
212+
IMasterConnection_sendASDU(mConnection, newAsdu);
213+
CS101_ASDU_destroy(newAsdu);
214+
newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, 1, false, false);
215+
if (gServer->mDataManager->hasTag(tag))
216+
{
217+
auto val = gServer->mDataManager->getDataByTag<double>(tag);
218+
InformationObject io = (InformationObject)MeasuredValueShort_create(NULL, kv.first, val, IEC60870_QUALITY_GOOD);
219+
CS101_ASDU_addInformationObject(newAsdu, io);
220+
InformationObject_destroy(io);
221+
}
222+
}
223+
}
224+
IMasterConnection_sendASDU(mConnection, newAsdu);
225+
CS101_ASDU_destroy(newAsdu);
226+
std::this_thread::sleep_for(std::chrono::seconds(mReversePollRate));
227+
}
228+
else
229+
{
230+
// Wait until connected to client
231+
while (!mConnected) { std::this_thread::sleep_for(std::chrono::seconds(1)); }
232+
}
233+
}
234+
}
235+
236+
/*
237+
* Reverse poll loop that sends local bennu datastore data to
238+
* connected clients. Sends indications as double point values
239+
*/
240+
void Server::reversePollDoublePoint()
138241
{
139242
while (1)
140243
{
@@ -213,9 +316,98 @@ void Server::rawMessageHandler(void *parameter, IMasterConnection con, uint8_t *
213316
}
214317

215318
/*
216-
* Callback handler for interrogation messages
319+
* Callback handler for interrogation messages that reports indications as single point values
217320
*/
218-
bool Server::interrogationHandler(void *parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi)
321+
bool Server::interrogationHandlerSinglePoint(void *parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi)
322+
{
323+
printf("Received interrogation for group %i\n", qoi);
324+
325+
if (qoi == 20) /* only handle station interrogation */
326+
{
327+
CS101_AppLayerParameters alParams = IMasterConnection_getApplicationLayerParameters(connection);
328+
IMasterConnection_sendACT_CON(connection, asdu, false);
329+
330+
// Send binary values
331+
CS101_ASDU newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, 0, 1, false, false);
332+
// kv = {<address>: {<tag>, eInput}}
333+
for (const auto &kv : gServer->mBinaryPoints)
334+
{
335+
const std::string tag = kv.second.first;
336+
if (CS101_ASDU_getPayloadSize(newAsdu) < MAX_ASDU_PAYLOAD_SIZE)
337+
{
338+
if (gServer->mDataManager->hasTag(tag))
339+
{
340+
auto status = gServer->mDataManager->getDataByTag<bool>(tag);
341+
InformationObject io = (InformationObject)SinglePointInformation_create(NULL, kv.first, status, IEC60870_QUALITY_GOOD);
342+
CS101_ASDU_addInformationObject(newAsdu, io);
343+
InformationObject_destroy(io);
344+
}
345+
}
346+
else
347+
{
348+
// Send current ASDU and create a new one for the remaining values
349+
IMasterConnection_sendASDU(connection, newAsdu);
350+
CS101_ASDU_destroy(newAsdu);
351+
newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, 0, 1, false, false);
352+
if (gServer->mDataManager->hasTag(tag))
353+
{
354+
auto status = gServer->mDataManager->getDataByTag<bool>(tag);
355+
InformationObject io = (InformationObject)SinglePointInformation_create(NULL, kv.first, status, IEC60870_QUALITY_GOOD);
356+
CS101_ASDU_addInformationObject(newAsdu, io);
357+
InformationObject_destroy(io);
358+
}
359+
}
360+
}
361+
IMasterConnection_sendASDU(connection, newAsdu);
362+
CS101_ASDU_destroy(newAsdu);
363+
364+
// Send analog values
365+
newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, 0, 1, false, false);
366+
// kv = {<address>: {<tag>, eInput}}
367+
for (const auto &kv : gServer->mAnalogPoints)
368+
{
369+
const std::string tag = kv.second.first;
370+
if (CS101_ASDU_getPayloadSize(newAsdu) < MAX_ASDU_PAYLOAD_SIZE)
371+
{
372+
if (gServer->mDataManager->hasTag(tag))
373+
{
374+
auto val = gServer->mDataManager->getDataByTag<double>(tag);
375+
InformationObject io = (InformationObject)MeasuredValueShort_create(NULL, kv.first, val, IEC60870_QUALITY_GOOD);
376+
CS101_ASDU_addInformationObject(newAsdu, io);
377+
InformationObject_destroy(io);
378+
}
379+
}
380+
else
381+
{
382+
// Send current ASDU and create a new one for the remaining values
383+
IMasterConnection_sendASDU(connection, newAsdu);
384+
CS101_ASDU_destroy(newAsdu);
385+
newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, 0, 1, false, false);
386+
if (gServer->mDataManager->hasTag(tag))
387+
{
388+
auto val = gServer->mDataManager->getDataByTag<double>(tag);
389+
InformationObject io = (InformationObject)MeasuredValueShort_create(NULL, kv.first, val, IEC60870_QUALITY_GOOD);
390+
CS101_ASDU_addInformationObject(newAsdu, io);
391+
InformationObject_destroy(io);
392+
}
393+
}
394+
}
395+
IMasterConnection_sendASDU(connection, newAsdu);
396+
CS101_ASDU_destroy(newAsdu);
397+
IMasterConnection_sendACT_TERM(connection, asdu);
398+
}
399+
else
400+
{
401+
IMasterConnection_sendACT_CON(connection, asdu, true);
402+
}
403+
404+
return true;
405+
}
406+
407+
/*
408+
* Callback handler for interrogation messages that reports indications as double point values
409+
*/
410+
bool Server::interrogationHandlerDoublePoint(void *parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi)
219411
{
220412
printf("Received interrogation for group %i\n", qoi);
221413

@@ -303,7 +495,38 @@ bool Server::interrogationHandler(void *parameter, IMasterConnection connection,
303495

304496
bool Server::asduHandler(void *parameter, IMasterConnection connection, CS101_ASDU asdu)
305497
{
306-
if (CS101_ASDU_getTypeID(asdu) == C_DC_NA_1)
498+
if (CS101_ASDU_getTypeID(asdu) == C_SC_NA_1)
499+
{
500+
printf("received single command\n");
501+
502+
if (CS101_ASDU_getCOT(asdu) == CS101_COT_ACTIVATION)
503+
{
504+
InformationObject io = CS101_ASDU_getElement(asdu, 0);
505+
506+
if (io)
507+
{
508+
SingleCommand sc = (SingleCommand)io;
509+
uint16_t addr = InformationObject_getObjectAddress(io);
510+
bool state = SingleCommand_getState(sc);
511+
printf("IOA: %i switch to %i\n", addr, state);
512+
gServer->writeBinary(addr, state);
513+
CS101_ASDU_setCOT(asdu, CS101_COT_ACTIVATION_CON);
514+
InformationObject_destroy(io);
515+
}
516+
else
517+
{
518+
printf("ERROR: message has no valid information object\n");
519+
return true;
520+
}
521+
}
522+
else
523+
CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_COT);
524+
525+
IMasterConnection_sendASDU(connection, asdu);
526+
527+
return true;
528+
}
529+
else if (CS101_ASDU_getTypeID(asdu) == C_DC_NA_1)
307530
{
308531
printf("received double command\n");
309532

@@ -403,7 +626,29 @@ void Server::connectionEventHandler(void *parameter, IMasterConnection con, CS10
403626
}
404627

405628
/*
406-
* Write binary value to datastore.
629+
* Write binary value to datastore. Note this variant takes a bool argument (for single point indications)
630+
*/
631+
void Server::writeBinary(std::uint16_t address, bool value)
632+
{
633+
std::ostringstream log_stream;
634+
log_stream << "Binary point command at address " << address << " with value " << value << ".";
635+
logEvent("iec60870-5-104 Server writeBinary", "info", log_stream.str());
636+
auto iter = mBinaryPoints.find(address);
637+
if (iter == mBinaryPoints.end())
638+
{
639+
log_stream.str("");
640+
log_stream << "Invalid binary point command request address: " << address;
641+
logEvent("binary point command", "error", log_stream.str());
642+
return;
643+
}
644+
mDataManager->addUpdatedBinaryTag(iter->second.first, value);
645+
log_stream.str("");
646+
log_stream << "Data successfully written.";
647+
logEvent("write binary", "info", log_stream.str());
648+
}
649+
650+
/*
651+
* Write binary value to datastore. Note this variant takes an int argument (for double point indications)
407652
*/
408653
void Server::writeBinary(std::uint16_t address, int value)
409654
{

src/bennu/devices/modules/comms/iec60870-5/module/Server.hpp

+8-3
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,11 @@ class Server : public CommsModule, public utility::DirectLoggable, public std::e
4141
public:
4242
Server(std::shared_ptr<field_device::DataManager> dm);
4343

44-
void start(const std::string& endpoint, std::shared_ptr<Server> server, const uint32_t rPollRate);
44+
void start(const std::string& endpoint, std::shared_ptr<Server> server, const uint32_t rPollRate, std::string subtype);
4545

46-
void reversePoll();
46+
void reversePollSinglePoint();
47+
48+
void reversePollDoublePoint();
4749

4850
bool addBinaryInput(const uint16_t address, const std::string& tag);
4951

@@ -53,13 +55,16 @@ class Server : public CommsModule, public utility::DirectLoggable, public std::e
5355

5456
bool addAnalogOutput(const uint16_t address, const std::string& tag);
5557

58+
void writeBinary(uint16_t address, bool value);
59+
5660
void writeBinary(uint16_t address, int value);
5761

5862
void writeAnalog(uint16_t address, float value);
5963

6064
// IEC60870-5-104 message callback handlers
6165
static void rawMessageHandler(void* parameter, IMasterConnection con, uint8_t* msg, int msgSize, bool sent);
62-
static bool interrogationHandler(void* parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi);
66+
static bool interrogationHandlerSinglePoint(void* parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi);
67+
static bool interrogationHandlerDoublePoint(void* parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi);
6368
static bool asduHandler(void* parameter, IMasterConnection connection, CS101_ASDU asdu);
6469
static bool connectionRequestHandler(void* parameter, const char* ipAddress);
6570
static void connectionEventHandler(void* parameter, IMasterConnection con, CS104_PeerConnectionEvent event);

0 commit comments

Comments
 (0)