@@ -17,7 +17,7 @@ Server::Server(std::shared_ptr<field_device::DataManager> dm) :
17
17
setDataManager (dm);
18
18
}
19
19
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 )
21
21
{
22
22
// Set server reverse-poll rate
23
23
mReversePollRate = rPollRate;
@@ -45,7 +45,14 @@ void Server::start(const std::string& endpoint, std::shared_ptr<Server> server,
45
45
CS104_Slave_setServerMode (slave, CS104_MODE_SINGLE_REDUNDANCY_GROUP);
46
46
47
47
// 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
+ }
49
56
// Set handler for other message types
50
57
CS104_Slave_setASDUHandler (slave, asduHandler, NULL );
51
58
// Set handler to handle connection requests (optional)
@@ -67,7 +74,14 @@ void Server::start(const std::string& endpoint, std::shared_ptr<Server> server,
67
74
else
68
75
{
69
76
// 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
+ }
71
85
}
72
86
73
87
}
@@ -132,9 +146,98 @@ void Server::sendSpontaneousUpdate(IMasterConnection connection, int ioa, Double
132
146
133
147
/*
134
148
* Reverse poll loop that sends local bennu datastore data to
135
- * connected clients.
149
+ * connected clients. Sends indications as single point values
136
150
*/
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 ()
138
241
{
139
242
while (1 )
140
243
{
@@ -213,9 +316,98 @@ void Server::rawMessageHandler(void *parameter, IMasterConnection con, uint8_t *
213
316
}
214
317
215
318
/*
216
- * Callback handler for interrogation messages
319
+ * Callback handler for interrogation messages that reports indications as single point values
217
320
*/
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)
219
411
{
220
412
printf (" Received interrogation for group %i\n " , qoi);
221
413
@@ -303,7 +495,38 @@ bool Server::interrogationHandler(void *parameter, IMasterConnection connection,
303
495
304
496
bool Server::asduHandler (void *parameter, IMasterConnection connection, CS101_ASDU asdu)
305
497
{
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)
307
530
{
308
531
printf (" received double command\n " );
309
532
@@ -403,7 +626,29 @@ void Server::connectionEventHandler(void *parameter, IMasterConnection con, CS10
403
626
}
404
627
405
628
/*
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)
407
652
*/
408
653
void Server::writeBinary (std::uint16_t address, int value)
409
654
{
0 commit comments