diff --git a/etc/examples/nodes/iec60870-5-104-slave.conf b/etc/examples/nodes/iec60870-5-104.conf similarity index 92% rename from etc/examples/nodes/iec60870-5-104-slave.conf rename to etc/examples/nodes/iec60870-5-104.conf index 190e9cfc0..bf017c847 100644 --- a/etc/examples/nodes/iec60870-5-104-slave.conf +++ b/etc/examples/nodes/iec60870-5-104.conf @@ -1,7 +1,7 @@ nodes = { iec104 = { - type = "iec60870-5-104-slave" + type = "iec60870-5-104" # network address and port of the server # 0.0.0.0 listens on all interfaces @@ -9,6 +9,9 @@ nodes = { port = 2404 # common address of this IEC104 slave ca = 41025 + # queue sizes for this node + low_priority_queue = 100 + high_priority_queue = 100 out = { # map signals to information object addresses and asdu data types diff --git a/include/villas/nodes/iec60870.hpp b/include/villas/nodes/iec60870.hpp index fd00f8596..db1dc47c9 100644 --- a/include/villas/nodes/iec60870.hpp +++ b/include/villas/nodes/iec60870.hpp @@ -139,8 +139,8 @@ protected: std::string local_address = "0.0.0.0"; int local_port = 2404; int common_address = 1; - int low_priority_queue_size = 100; - int high_priority_queue_size = 100; + int low_priority_queue = 100; + int high_priority_queue = 100; // config (use lib60870 defaults if std::nullopt) std::optional apci_t0 = std::nullopt; @@ -178,6 +178,8 @@ protected: bool onInterrogation(IMasterConnection connection, CS101_ASDU asdu, uint8_t _of_inter) const noexcept; bool onASDU(IMasterConnection connection, CS101_ASDU asdu) const noexcept; + unsigned fillASDU(CS101_ASDU &asdu, Sample const *sample, ASDUData::Type type) const noexcept(false); + virtual int _write(struct Sample * smps[], unsigned cnt) override; diff --git a/lib/nodes/iec60870.cpp b/lib/nodes/iec60870.cpp index 6dcbe56f3..2e15c370c 100644 --- a/lib/nodes/iec60870.cpp +++ b/lib/nodes/iec60870.cpp @@ -289,7 +289,7 @@ void SlaveNode::createSlave() noexcept this->destroySlave(); // create the slave object - server.slave = CS104_Slave_create(server.low_priority_queue_size,server.high_priority_queue_size); + server.slave = CS104_Slave_create(server.low_priority_queue,server.high_priority_queue); CS104_Slave_setServerMode(server.slave, CS104_MODE_CONNECTION_IS_REDUNDANCY_GROUP); // configure the slave according to config @@ -312,7 +312,7 @@ void SlaveNode::createSlave() noexcept return self->onClockSync(connection,asdu,new_time); }, this); - CS104_Slave_setInterrogationHandler(server.slave, [] (void *tcp_node, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi) { + CS104_Slave_setInterrogationHandler(server.slave, [] (void *tcp_node, IMasterConnection connection, CS101_ASDU asdu, QualifierOfInterrogation qoi) { auto self = static_cast (tcp_node); return self->onInterrogation(connection,asdu,qoi); }, this); @@ -411,7 +411,7 @@ bool SlaveNode::onClockSync(IMasterConnection connection, CS101_ASDU asdu, CP56T return true; } -bool SlaveNode::onInterrogation(IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi) const noexcept +bool SlaveNode::onInterrogation(IMasterConnection connection, CS101_ASDU asdu, QualifierOfInterrogation qoi) const noexcept { auto &mapping = this->output.mapping; auto &last_values = this->output.last_values; @@ -474,41 +474,41 @@ bool SlaveNode::onASDU(IMasterConnection connection, CS101_ASDU asdu) const noex return true; } +unsigned SlaveNode::fillASDU(CS101_ASDU &asdu, Sample const *sample, ASDUData::Type type) const noexcept(false) { + int asdu_elements = 0; + auto &mapping = this->output.mapping; + for (unsigned signal = 0; signal < MIN(sample->length, mapping.size()); signal++) { + if (mapping[signal].type() != type) continue; + + auto timestamp = (sample->flags & (int) SampleFlags::HAS_TS_ORIGIN) + ? std::optional{ sample->ts.origin } + : std::nullopt; + + if (mapping[signal].hasTimestamp() && !timestamp.has_value()) + throw RuntimeError("Received sample without timestamp for ASDU type with mandatory timestamp"); + + if (mapping[signal].signalType() != sample_format(sample,signal)) + throw RuntimeError("Expected signal type {}, but received {}", + signalTypeToString(mapping[signal].signalType()), + signalTypeToString(sample_format(sample,signal)) + ); + + mapping[signal].addSampleToASDU(asdu, ASDUData::Sample { + sample->data[signal], + IEC60870_QUALITY_GOOD, + timestamp + }); + + asdu_elements++; + } + + assert(CS101_ASDU_getNumberOfElements(asdu) == asdu_elements); + + return asdu_elements; +}; + int SlaveNode::_write(Sample *samples[], unsigned sample_count) { - auto fill_asdu = [this] (CS101_ASDU &asdu, Sample const *sample, ASDUData::Type type) { - int asdu_elements = 0; - auto &mapping = this->output.mapping; - for (unsigned signal = 0; signal < MIN(sample->length, mapping.size()); signal++) { - if (mapping[signal].type() != type) continue; - - auto timestamp = (sample->flags & (int) SampleFlags::HAS_TS_ORIGIN) - ? std::optional{ sample->ts.origin } - : std::nullopt; - - if (mapping[signal].hasTimestamp() && !timestamp.has_value()) - throw RuntimeError("Received sample without timestamp for ASDU type with mandatory timestamp"); - - if (mapping[signal].signalType() != sample_format(sample,signal)) - throw RuntimeError("Expected signal type {}, but received {}", - signalTypeToString(mapping[signal].signalType()), - signalTypeToString(sample_format(sample,signal)) - ); - - mapping[signal].addSampleToASDU(asdu, ASDUData::Sample { - sample->data[signal], - IEC60870_QUALITY_GOOD, - timestamp - }); - - asdu_elements++; - } - - assert(CS101_ASDU_getNumberOfElements(asdu) == asdu_elements); - - return asdu_elements; - }; - for (unsigned sample_index = 0; sample_index < sample_count; sample_index++) { Sample const *sample = samples[sample_index]; @@ -532,7 +532,7 @@ int SlaveNode::_write(Sample *samples[], unsigned sample_count) ); // if data was added to asdu, enqueue it - if (fill_asdu(asdu, sample, asdu_type) != 0) + if (this->fillASDU(asdu, sample, asdu_type) != 0) CS104_Slave_enqueueASDU(this->server.slave, asdu); CS101_ASDU_destroy(asdu); @@ -564,13 +564,15 @@ int SlaveNode::parse(json_t *json, const uuid_t sn_uuid) json_t *out_json = nullptr; char const *address = nullptr; - if(json_unpack_ex(json, &err, 0, "{ s?: o, s?: s, s?: i, s?: i }", + if(json_unpack_ex(json, &err, 0, "{ s?: o, s?: s, s?: i, s?: i, s?: i, s?: i }", "out", &out_json, "address", &address, "port", &this->server.local_port, - "ca", &this->server.common_address + "ca", &this->server.common_address, + "low_priority_queue", &this->server.low_priority_queue, + "high_priority_queue", &this->server.high_priority_queue )) - throw ConfigError(json, err, "node-config-node-iec60870-5-104-slave"); + throw ConfigError(json, err, "node-config-node-iec60870-5-104"); if (address) this->server.local_address = address; @@ -581,7 +583,7 @@ int SlaveNode::parse(json_t *json, const uuid_t sn_uuid) if(json_unpack_ex(out_json, &err, 0, "{ s: o }", "signals", &signals_json )) - throw ConfigError(out_json, err, "node-config-node-iec60870-5-104-slave"); + throw ConfigError(out_json, err, "node-config-node-iec60870-5-104"); } auto parse_asdu_data = [&err] (json_t *signal_json) -> ASDUData { @@ -595,7 +597,7 @@ int SlaveNode::parse(json_t *json, const uuid_t sn_uuid) "asdu_type_id", &asdu_type_id, "ioa", &ioa )) - throw ConfigError(signal_json, err, "node-config-node-iec60870-5-104-slave"); + throw ConfigError(signal_json, err, "node-config-node-iec60870-5-104"); if (ioa == 0) throw RuntimeError("Found invalid ioa {} in config", ioa); @@ -679,6 +681,6 @@ int SlaveNode::stop() // Plugin // ------------------------------------------ -static char name[] = "iec60870-5-104-slave"; +static char name[] = "iec60870-5-104"; static char description[] = "Provide values as protocol slave"; static NodePlugin p;