mirror of
https://git.rwth-aachen.de/acs/public/villas/node/
synced 2025-03-09 00:00:00 +01:00
Final code-style tweaks
This commit is contained in:
parent
f54ad3c6fa
commit
56e8918196
3 changed files with 154 additions and 101 deletions
|
@ -15,38 +15,45 @@ allOf:
|
|||
type: object
|
||||
properties:
|
||||
duplicate_ioa_is_sequence:
|
||||
description: Treat consecutive signals with the same IOA as a sequence by assigning subsequent IOAs.
|
||||
type: boolean
|
||||
default: false
|
||||
description: |
|
||||
Treat consecutive signals with the same IOA as a sequence by assigning subsequent IOAs.
|
||||
|
||||
signals:
|
||||
type: array
|
||||
items:
|
||||
$ref: ./signals/iec60870_signal.yaml
|
||||
|
||||
address:
|
||||
description: Hostname or IP address for the IEC60870 slave to listen on.
|
||||
type: string
|
||||
default: localhost
|
||||
description: |
|
||||
Hostname or IP address for the IEC60870 slave to listen on.
|
||||
|
||||
port:
|
||||
description: Port number of the IEC60870 slave.
|
||||
type: number
|
||||
default: 2404
|
||||
description: |
|
||||
Port number of the IEC60870 slave.
|
||||
|
||||
ca:
|
||||
description: Common Address of the IEC60870 slave.
|
||||
type: number
|
||||
default: 1
|
||||
description: |
|
||||
Common Address of the IEC60870 slave.
|
||||
|
||||
low_priority_queue:
|
||||
description: Message queue size for the periodic messages (increase on dropped simulation data messages).
|
||||
type: number
|
||||
default: 100
|
||||
description: |
|
||||
Message queue size for the periodic messages (increase on dropped simulation data messages).
|
||||
|
||||
high_priority_queue:
|
||||
description: Message queue size for interrogation responses (increase on missing signals in interrogation response).
|
||||
type: number
|
||||
default: 100
|
||||
description: |
|
||||
Message queue size for interrogation responses (increase on missing signals in interrogation response).
|
||||
|
||||
apci_t0:
|
||||
type: number
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* @copyright 2014-2022, Institute for Automation of Complex Power Systems, EONERC
|
||||
* @license Apache 2.0
|
||||
*********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
@ -28,26 +29,16 @@ namespace iec60870 {
|
|||
class ASDUData {
|
||||
public:
|
||||
enum Type {
|
||||
// SinglePointInformation
|
||||
SINGLE_POINT = M_SP_NA_1,
|
||||
// SinglePointWithCP56Time2a
|
||||
SINGLE_POINT_WITH_TIMESTAMP = M_SP_TB_1,
|
||||
// DoublePointInformation
|
||||
DOUBLE_POINT = M_DP_NA_1,
|
||||
// DoublePointWithCP56Time2a
|
||||
DOUBLE_POINT_WITH_TIMESTAMP = M_DP_TB_1,
|
||||
// MeasuredValueScaled
|
||||
SCALED_INT = M_ME_NB_1,
|
||||
// MeasuredValueScaledWithCP56Time2a
|
||||
SCALED_INT_WITH_TIMESTAMP = M_ME_TE_1,
|
||||
// MeasuredValueNormalized
|
||||
NORMALIZED_FLOAT = M_ME_NA_1,
|
||||
// MeasuredValueNormalizedWithCP56Time2a
|
||||
NORMALIZED_FLOAT_WITH_TIMESTAMP = M_ME_TD_1,
|
||||
// MeasuredValueShort
|
||||
SHORT_FLOAT = M_ME_NC_1,
|
||||
// MeasuredValueShortWithCP56Time2a
|
||||
SHORT_FLOAT_WITH_TIMESTAMP = M_ME_TF_1,
|
||||
SINGLE_POINT = M_SP_NA_1, // SinglePointInformation
|
||||
SINGLE_POINT_WITH_TIMESTAMP = M_SP_TB_1, // SinglePointWithCP56Time2a
|
||||
DOUBLE_POINT = M_DP_NA_1, // DoublePointInformation
|
||||
DOUBLE_POINT_WITH_TIMESTAMP = M_DP_TB_1, // DoublePointWithCP56Time2a
|
||||
SCALED_INT = M_ME_NB_1, // MeasuredValueScaled
|
||||
SCALED_INT_WITH_TIMESTAMP = M_ME_TE_1, // MeasuredValueScaledWithCP56Time2a
|
||||
NORMALIZED_FLOAT = M_ME_NA_1, // MeasuredValueNormalized
|
||||
NORMALIZED_FLOAT_WITH_TIMESTAMP = M_ME_TD_1, // MeasuredValueNormalizedWithCP56Time2a
|
||||
SHORT_FLOAT = M_ME_NC_1, // MeasuredValueShort
|
||||
SHORT_FLOAT_WITH_TIMESTAMP = M_ME_TF_1, // MeasuredValueShortWithCP56Time2a
|
||||
};
|
||||
|
||||
struct Sample {
|
||||
|
@ -56,29 +47,37 @@ public:
|
|||
std::optional<timespec> timestamp;
|
||||
};
|
||||
|
||||
// parse the config json
|
||||
// Parse the config json
|
||||
static ASDUData parse(json_t *signal_json, std::optional<ASDUData> last_data, bool duplicate_ioa_is_sequence);
|
||||
|
||||
// does this data include a timestamp
|
||||
// Does this data include a timestamp
|
||||
bool hasTimestamp() const;
|
||||
// the IEC104 type
|
||||
|
||||
// The IEC104 type
|
||||
ASDUData::Type type() const;
|
||||
// the config file identifier for this type
|
||||
|
||||
// The config file identifier for this type
|
||||
char const * name() const;
|
||||
// get equivalent IEC104 type without timestamp (e.g. for general interrogation response)
|
||||
|
||||
// Get equivalent IEC104 type without timestamp (e.g. for general interrogation response)
|
||||
ASDUData::Type typeWithoutTimestamp() const;
|
||||
// get equivalent ASDUData without timestamp (e.g. for general interrogation response)
|
||||
|
||||
// Get equivalent ASDUData without timestamp (e.g. for general interrogation response)
|
||||
ASDUData withoutTimestamp() const;
|
||||
// corresponding signal type
|
||||
|
||||
// Corresponding signal type
|
||||
SignalType signalType() const;
|
||||
// check if ASDU contains this data
|
||||
|
||||
// Check if ASDU contains this data
|
||||
std::optional<ASDUData::Sample> checkASDU(CS101_ASDU const &asdu) const;
|
||||
// add SignalData to an ASDU, returns false when sample couldn't be added (insufficient space in ASDU)
|
||||
|
||||
// Add SignalData to an ASDU, returns false when sample couldn't be added (insufficient space in ASDU)
|
||||
bool addSampleToASDU(CS101_ASDU &asdu, ASDUData::Sample sample) const;
|
||||
|
||||
// every value in an ASDU has an associated "information object address" (ioa)
|
||||
// Every value in an ASDU has an associated "information object address" (ioa)
|
||||
int ioa;
|
||||
// start of the ioa sequence
|
||||
|
||||
// Start of the ioa sequence
|
||||
int ioa_sequence_start;
|
||||
private:
|
||||
struct Descriptor {
|
||||
|
@ -105,31 +104,33 @@ private:
|
|||
|
||||
ASDUData(ASDUData::Descriptor const *descriptor, int ioa, int ioa_sequence_start);
|
||||
|
||||
// lookup datatype for config key asdu_type
|
||||
// Lookup datatype for config key asdu_type
|
||||
static std::optional<ASDUData> lookupName(char const *name, bool with_timestamp, int ioa, int ioa_sequence_start);
|
||||
// lookup datatype for config key asdu_type_id
|
||||
|
||||
// Lookup datatype for config key asdu_type_id
|
||||
static std::optional<ASDUData> lookupTypeId(char const *type_id, int ioa, int ioa_sequence_start);
|
||||
// lookup datatype for numeric type identifier
|
||||
|
||||
// Lookup datatype for numeric type identifier
|
||||
static std::optional<ASDUData> lookupType(int type, int ioa, int ioa_sequence_start);
|
||||
|
||||
// descriptor within the descriptors table above
|
||||
// Descriptor within the descriptors table above
|
||||
ASDUData::Descriptor const *descriptor;
|
||||
};
|
||||
|
||||
class SlaveNode : public Node {
|
||||
protected:
|
||||
struct Server {
|
||||
// slave state
|
||||
// Slave state
|
||||
enum { NONE, STOPPED, READY } state;
|
||||
|
||||
// config (use explicit defaults)
|
||||
// Config (use explicit defaults)
|
||||
std::string local_address;
|
||||
int local_port;
|
||||
int common_address;
|
||||
int low_priority_queue;
|
||||
int high_priority_queue;
|
||||
|
||||
// config (use lib60870 defaults if std::nullopt)
|
||||
// Config (use lib60870 defaults if std::nullopt)
|
||||
std::optional<int> apci_t0;
|
||||
std::optional<int> apci_t1;
|
||||
std::optional<int> apci_t2;
|
||||
|
@ -137,7 +138,7 @@ protected:
|
|||
std::optional<int> apci_k;
|
||||
std::optional<int> apci_w;
|
||||
|
||||
// lib60870
|
||||
// Lib60870
|
||||
CS104_Slave slave;
|
||||
CS101_AppLayerParameters asdu_app_layer_parameters;
|
||||
} server;
|
||||
|
@ -183,9 +184,6 @@ public:
|
|||
|
||||
virtual
|
||||
int stop() override;
|
||||
|
||||
// virtual
|
||||
// std::string & getDetails() override;
|
||||
};
|
||||
|
||||
} /* namespace iec60870 */
|
||||
|
|
|
@ -4,12 +4,13 @@
|
|||
* @copyright 2014-2022, Institute for Automation of Complex Power Systems, EONERC
|
||||
* @license Apache 2.0
|
||||
*********************************************************************************/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <villas/node_compat.hpp>
|
||||
#include <villas/nodes/iec60870.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
#include <villas/sample.hpp>
|
||||
#include <villas/exceptions.hpp>
|
||||
#include <villas/super_node.hpp>
|
||||
#include <villas/exceptions.hpp>
|
||||
|
||||
|
@ -52,7 +53,7 @@ ASDUData ASDUData::parse(json_t *signal_json, std::optional<ASDUData> last_data,
|
|||
|
||||
with_timestamp = with_timestamp != -1 ? with_timestamp != 0 : false;
|
||||
|
||||
// increase the ioa if it is found twice to make it a sequence
|
||||
// Increase the ioa if it is found twice to make it a sequence
|
||||
if ( duplicate_ioa_is_sequence &&
|
||||
last_data &&
|
||||
ioa == last_data->ioa_sequence_start) {
|
||||
|
@ -82,6 +83,7 @@ std::optional<ASDUData> ASDUData::lookupTypeId(char const *type_id, int ioa, int
|
|||
auto check = [type_id] (Descriptor descriptor) {
|
||||
return !strcmp(descriptor.type_id, type_id);
|
||||
};
|
||||
|
||||
auto descriptor = std::find_if(begin(descriptors), end(descriptors), check);
|
||||
if (descriptor != end(descriptors))
|
||||
return ASDUData { &*descriptor, ioa, ioa_sequence_start };
|
||||
|
@ -94,6 +96,7 @@ std::optional<ASDUData> ASDUData::lookupName(char const *name, bool with_timesta
|
|||
auto check = [name, with_timestamp] (Descriptor descriptor) {
|
||||
return !strcmp(descriptor.name, name) && descriptor.has_timestamp == with_timestamp;
|
||||
};
|
||||
|
||||
auto descriptor = std::find_if(begin(descriptors), end(descriptors), check);
|
||||
if (descriptor != end(descriptors))
|
||||
return ASDUData { &*descriptor, ioa, ioa_sequence_start };
|
||||
|
@ -106,6 +109,7 @@ std::optional<ASDUData> ASDUData::lookupType(int type, int ioa, int ioa_sequence
|
|||
auto check = [type] (Descriptor descriptor) {
|
||||
return descriptor.type == type;
|
||||
};
|
||||
|
||||
auto descriptor = std::find_if(begin(descriptors), end(descriptors), check);
|
||||
if (descriptor != end(descriptors))
|
||||
return ASDUData { &*descriptor, ioa, ioa_sequence_start };
|
||||
|
@ -198,7 +202,8 @@ std::optional<ASDUData::Sample> ASDUData::checkASDU(CS101_ASDU const &asdu) cons
|
|||
break;
|
||||
}
|
||||
|
||||
default: assert(!"unreachable");
|
||||
default:
|
||||
assert(!"unreachable");
|
||||
}
|
||||
|
||||
std::optional<CP56Time2a> time_cp56;
|
||||
|
@ -233,7 +238,8 @@ std::optional<ASDUData::Sample> ASDUData::checkASDU(CS101_ASDU const &asdu) cons
|
|||
break;
|
||||
}
|
||||
|
||||
default: time_cp56 = std::nullopt;
|
||||
default:
|
||||
time_cp56 = std::nullopt;
|
||||
}
|
||||
|
||||
InformationObject_destroy(io);
|
||||
|
@ -326,10 +332,13 @@ bool ASDUData::addSampleToASDU(CS101_ASDU &asdu, ASDUData::Sample sample) const
|
|||
break;
|
||||
}
|
||||
|
||||
default: assert(!"unreachable");
|
||||
default:
|
||||
assert(!"unreachable");
|
||||
}
|
||||
|
||||
bool successfully_added = CS101_ASDU_addInformationObject(asdu, io);
|
||||
InformationObject_destroy(io);
|
||||
|
||||
return successfully_added;
|
||||
}
|
||||
|
||||
|
@ -339,28 +348,39 @@ ASDUData::ASDUData(ASDUData::Descriptor const *descriptor, int ioa, int ioa_sequ
|
|||
|
||||
void SlaveNode::createSlave() noexcept
|
||||
{
|
||||
// destroy slave id it was already created
|
||||
// Destroy slave id it was already created
|
||||
destroySlave();
|
||||
|
||||
// create the slave object
|
||||
// Create the slave object
|
||||
server.slave = CS104_Slave_create(server.low_priority_queue, server.high_priority_queue);
|
||||
CS104_Slave_setServerMode(server.slave, CS104_MODE_SINGLE_REDUNDANCY_GROUP);
|
||||
|
||||
// configure the slave according to config
|
||||
// Configure the slave according to config
|
||||
server.asdu_app_layer_parameters = CS104_Slave_getAppLayerParameters(server.slave);
|
||||
CS104_APCIParameters apci_parameters = CS104_Slave_getConnectionParameters(server.slave);
|
||||
|
||||
if (server.apci_t0) apci_parameters->t0 = *server.apci_t0;
|
||||
if (server.apci_t1) apci_parameters->t1 = *server.apci_t1;
|
||||
if (server.apci_t2) apci_parameters->t2 = *server.apci_t2;
|
||||
if (server.apci_t3) apci_parameters->t3 = *server.apci_t3;
|
||||
if (server.apci_k) apci_parameters->k = *server.apci_k;
|
||||
if (server.apci_w) apci_parameters->w = *server.apci_w;
|
||||
if (server.apci_t0)
|
||||
apci_parameters->t0 = *server.apci_t0;
|
||||
|
||||
if (server.apci_t1)
|
||||
apci_parameters->t1 = *server.apci_t1;
|
||||
|
||||
if (server.apci_t2)
|
||||
apci_parameters->t2 = *server.apci_t2;
|
||||
|
||||
if (server.apci_t3)
|
||||
apci_parameters->t3 = *server.apci_t3;
|
||||
|
||||
if (server.apci_k)
|
||||
apci_parameters->k = *server.apci_k;
|
||||
|
||||
if (server.apci_w)
|
||||
apci_parameters->w = *server.apci_w;
|
||||
|
||||
CS104_Slave_setLocalAddress(server.slave, server.local_address.c_str());
|
||||
CS104_Slave_setLocalPort(server.slave, server.local_port);
|
||||
|
||||
// setup callbacks into the class
|
||||
// Setup callbacks into the class
|
||||
CS104_Slave_setClockSyncHandler(server.slave, [] (void *tcp_node, IMasterConnection connection, CS101_ASDU asdu, CP56Time2a new_time) {
|
||||
auto self = static_cast<SlaveNode const *> (tcp_node);
|
||||
return self->onClockSync(connection, asdu, new_time);
|
||||
|
@ -423,8 +443,9 @@ void SlaveNode::stopSlave() noexcept
|
|||
server.state = SlaveNode::Server::STOPPED;
|
||||
|
||||
if (CS104_Slave_getNumberOfQueueEntries(server.slave, NULL) != 0)
|
||||
logger->info("waiting for last messages in queue");
|
||||
// wait for all messages to be send before really stopping
|
||||
logger->info("Waiting for last messages in queue");
|
||||
|
||||
// Wait for all messages to be send before really stopping
|
||||
while ( (CS104_Slave_getNumberOfQueueEntries(server.slave, NULL) != 0) &&
|
||||
(CS104_Slave_getOpenConnections(server.slave) != 0))
|
||||
std::this_thread::sleep_for(100ms);
|
||||
|
@ -434,45 +455,48 @@ void SlaveNode::stopSlave() noexcept
|
|||
|
||||
void SlaveNode::debugPrintMessage(IMasterConnection connection, uint8_t* message, int message_size, bool sent) const noexcept
|
||||
{
|
||||
/// ToDo: debug print the message bytes as trace
|
||||
/// TODO: debug print the message bytes as trace
|
||||
}
|
||||
|
||||
void SlaveNode::debugPrintConnection(IMasterConnection connection, CS104_PeerConnectionEvent event) const noexcept
|
||||
{
|
||||
switch (event) {
|
||||
case CS104_CON_EVENT_CONNECTION_OPENED:
|
||||
logger->info("client connected");
|
||||
logger->info("Client connected");
|
||||
break;
|
||||
|
||||
case CS104_CON_EVENT_CONNECTION_CLOSED:
|
||||
logger->info("client disconnected");
|
||||
logger->info("Client disconnected");
|
||||
break;
|
||||
|
||||
case CS104_CON_EVENT_ACTIVATED:
|
||||
logger->info("connection activated");
|
||||
logger->info("Connection activated");
|
||||
break;
|
||||
|
||||
case CS104_CON_EVENT_DEACTIVATED:
|
||||
logger->info("connection closed");
|
||||
logger->info("Connection closed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool SlaveNode::onClockSync(IMasterConnection connection, CS101_ASDU asdu, CP56Time2a new_time) const noexcept
|
||||
{
|
||||
logger->warn("received clock sync command (unimplemented)");
|
||||
logger->warn("Received clock sync command (unimplemented)");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SlaveNode::onInterrogation(IMasterConnection connection, CS101_ASDU asdu, QualifierOfInterrogation qoi) const noexcept
|
||||
{
|
||||
switch (qoi) {
|
||||
// send last values without timestamps
|
||||
// Send last values without timestamps
|
||||
case IEC60870_QOI_STATION: {
|
||||
IMasterConnection_sendACT_CON(connection, asdu, false);
|
||||
|
||||
logger->debug("received general interrogation");
|
||||
logger->debug("Received general interrogation");
|
||||
|
||||
auto guard = std::lock_guard { output.last_values_mutex };
|
||||
|
||||
for(auto const &asdu_type : output.asdu_types) {
|
||||
for (auto const &asdu_type : output.asdu_types) {
|
||||
for (unsigned i = 0; i < output.mapping.size();) {
|
||||
auto signal_asdu = CS101_ASDU_create(
|
||||
IMasterConnection_getApplicationLayerParameters(connection),
|
||||
|
@ -491,7 +515,7 @@ bool SlaveNode::onInterrogation(IMasterConnection connection, CS101_ASDU asdu, Q
|
|||
if (asdu_data.type() != asdu_type)
|
||||
continue;
|
||||
|
||||
if(asdu_data.addSampleToASDU(signal_asdu, ASDUData::Sample {
|
||||
if (asdu_data.addSampleToASDU(signal_asdu, ASDUData::Sample {
|
||||
last_value,
|
||||
IEC60870_QUALITY_GOOD,
|
||||
std::nullopt
|
||||
|
@ -509,10 +533,10 @@ bool SlaveNode::onInterrogation(IMasterConnection connection, CS101_ASDU asdu, Q
|
|||
break;
|
||||
}
|
||||
|
||||
// negative acknowledgement
|
||||
// Negative acknowledgement
|
||||
default:
|
||||
IMasterConnection_sendACT_CON(connection, asdu, true);
|
||||
logger->warn("ignoring interrogation type {}", qoi);
|
||||
logger->warn("Ignoring interrogation type {}", qoi);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -520,17 +544,17 @@ bool SlaveNode::onInterrogation(IMasterConnection connection, CS101_ASDU asdu, Q
|
|||
|
||||
bool SlaveNode::onASDU(IMasterConnection connection, CS101_ASDU asdu) const noexcept
|
||||
{
|
||||
logger->warn("ignoring asdu type {}", CS101_ASDU_getTypeID(asdu));
|
||||
logger->warn("Ignoring ASDU type {}", CS101_ASDU_getTypeID(asdu));
|
||||
return true;
|
||||
}
|
||||
|
||||
void SlaveNode::sendPeriodicASDUsForSample(Sample const *sample) const noexcept(false)
|
||||
{
|
||||
// ASDUs may only carry one type of asdu
|
||||
// ASDUs may only carry one type of ASDU
|
||||
for (auto const &type : output.asdu_types) {
|
||||
// search all occurences of this ASDU type
|
||||
// Search all occurrences of this ASDU type
|
||||
for (unsigned signal = 0; signal < MIN(sample->length, output.mapping.size());) {
|
||||
// create an ASDU for periodic transimission
|
||||
// Create an ASDU for periodic transmission
|
||||
CS101_ASDU asdu = CS101_ASDU_create(
|
||||
server.asdu_app_layer_parameters,
|
||||
0,
|
||||
|
@ -544,7 +568,7 @@ void SlaveNode::sendPeriodicASDUsForSample(Sample const *sample) const noexcept(
|
|||
do {
|
||||
auto &asdu_data = output.mapping[signal];
|
||||
|
||||
// this signal_data does not belong in this ASDU
|
||||
// This signal_data does not belong in this ASDU
|
||||
if (asdu_data.type() != type)
|
||||
continue;
|
||||
|
||||
|
@ -586,13 +610,12 @@ int SlaveNode::_write(Sample *samples[], unsigned sample_count)
|
|||
for (unsigned sample_index = 0; sample_index < sample_count; sample_index++) {
|
||||
Sample const *sample = samples[sample_index];
|
||||
|
||||
// update last_values
|
||||
// Update last_values
|
||||
output.last_values_mutex.lock();
|
||||
for (unsigned i = 0; i < MIN(sample->length, output.last_values.size()); i++) {
|
||||
for (unsigned i = 0; i < MIN(sample->length, output.last_values.size()); i++)
|
||||
output.last_values[i] = sample->data[i];
|
||||
}
|
||||
output.last_values_mutex.unlock();
|
||||
|
||||
output.last_values_mutex.unlock();
|
||||
sendPeriodicASDUsForSample(sample);
|
||||
}
|
||||
|
||||
|
@ -604,14 +627,14 @@ SlaveNode::SlaveNode(const std::string &name) :
|
|||
{
|
||||
server.state = SlaveNode::Server::NONE;
|
||||
|
||||
// server config (use explicit defaults)
|
||||
// Server config (use explicit defaults)
|
||||
server.local_address = "0.0.0.0";
|
||||
server.local_port = 2404;
|
||||
server.common_address = 1;
|
||||
server.low_priority_queue = 100;
|
||||
server.high_priority_queue = 100;
|
||||
|
||||
// config (use lib60870 defaults if std::nullopt)
|
||||
// Config (use lib60870 defaults if std::nullopt)
|
||||
server.apci_t0 = std::nullopt;
|
||||
server.apci_t1 = std::nullopt;
|
||||
server.apci_t2 = std::nullopt;
|
||||
|
@ -619,7 +642,7 @@ SlaveNode::SlaveNode(const std::string &name) :
|
|||
server.apci_k = std::nullopt;
|
||||
server.apci_w = std::nullopt;
|
||||
|
||||
// output config
|
||||
// Output config
|
||||
output.enabled = false;
|
||||
output.mapping = {};
|
||||
output.asdu_types = {};
|
||||
|
@ -648,7 +671,8 @@ int SlaveNode::parse(json_t *json, const uuid_t sn_uuid)
|
|||
int apci_t3 = -1;
|
||||
int apci_k = -1;
|
||||
int apci_w = -1;
|
||||
if(json_unpack_ex(json, &err, 0, "{ s?: o, s?: s, s?: i, s?: i, s?: i, s?: i, s?: i, s?: i, s?: i, s?: i, s?: i, s?: i }",
|
||||
|
||||
ret = json_unpack_ex(json, &err, 0, "{ s?: o, s?: s, s?: i, s?: i, s?: i, s?: i, s?: i, s?: i, s?: i, s?: i, s?: i, s?: i }",
|
||||
"out", &out_json,
|
||||
"address", &address,
|
||||
"port", &server.local_port,
|
||||
|
@ -661,27 +685,42 @@ int SlaveNode::parse(json_t *json, const uuid_t sn_uuid)
|
|||
"apci_t3", &apci_t3,
|
||||
"apci_k", &apci_k,
|
||||
"apci_w", &apci_w
|
||||
))
|
||||
);
|
||||
if (ret)
|
||||
throw ConfigError(json, err, "node-config-node-iec60870-5-104");
|
||||
|
||||
if(apci_t0 != -1) server.apci_t0 = apci_t0;
|
||||
if(apci_t1 != -1) server.apci_t1 = apci_t1;
|
||||
if(apci_t2 != -1) server.apci_t2 = apci_t2;
|
||||
if(apci_t3 != -1) server.apci_t3 = apci_t3;
|
||||
if(apci_k != -1) server.apci_k = apci_k;
|
||||
if(apci_w != -1) server.apci_w = apci_w;
|
||||
if (apci_t0 != -1)
|
||||
server.apci_t0 = apci_t0;
|
||||
|
||||
if (apci_t1 != -1)
|
||||
server.apci_t1 = apci_t1;
|
||||
|
||||
if (apci_t2 != -1)
|
||||
server.apci_t2 = apci_t2;
|
||||
|
||||
if (apci_t3 != -1)
|
||||
server.apci_t3 = apci_t3;
|
||||
|
||||
if (apci_k != -1)
|
||||
server.apci_k = apci_k;
|
||||
|
||||
if (apci_w != -1)
|
||||
server.apci_w = apci_w;
|
||||
|
||||
if (address)
|
||||
server.local_address = address;
|
||||
|
||||
json_t *signals_json = nullptr;
|
||||
int duplicate_ioa_is_sequence = false;
|
||||
|
||||
if (out_json) {
|
||||
output.enabled = true;
|
||||
if(json_unpack_ex(out_json, &err, 0, "{ s: o, s?: b }",
|
||||
|
||||
ret = json_unpack_ex(out_json, &err, 0, "{ s: o, s?: b }",
|
||||
"signals", &signals_json,
|
||||
"duplicate_ioa_is_sequence", &duplicate_ioa_is_sequence
|
||||
))
|
||||
);
|
||||
if (ret)
|
||||
throw ConfigError(out_json, err, "node-config-node-iec60870-5-104");
|
||||
}
|
||||
|
||||
|
@ -689,32 +728,41 @@ int SlaveNode::parse(json_t *json, const uuid_t sn_uuid)
|
|||
json_t *signal_json;
|
||||
size_t i;
|
||||
std::optional<ASDUData> last_data = std::nullopt;
|
||||
|
||||
json_array_foreach(signals_json, i, signal_json) {
|
||||
auto signal = signals ? signals->getByIndex(i) : Signal::Ptr{};
|
||||
auto asdu_data = ASDUData::parse(signal_json, last_data, duplicate_ioa_is_sequence);
|
||||
last_data = asdu_data;
|
||||
SignalData initial_value;
|
||||
|
||||
if (signal) {
|
||||
if (signal->type != asdu_data.signalType())
|
||||
if (signal->type != asdu_data.signalType()) {
|
||||
throw RuntimeError("Type mismatch! Expected type {} for signal {}, but found {}",
|
||||
signalTypeToString(asdu_data.signalType()),
|
||||
signal->name,
|
||||
signalTypeToString(signal->type)
|
||||
);
|
||||
}
|
||||
|
||||
switch (signal->type) {
|
||||
case SignalType::BOOLEAN:
|
||||
initial_value.b = false;
|
||||
break;
|
||||
|
||||
case SignalType::INTEGER:
|
||||
initial_value.i = 0;
|
||||
break;
|
||||
|
||||
case SignalType::FLOAT:
|
||||
initial_value.f = 0;
|
||||
break;
|
||||
default: assert(!"unreachable");
|
||||
|
||||
default:
|
||||
assert(!"unreachable");
|
||||
}
|
||||
} else
|
||||
initial_value.f = 0.0;
|
||||
|
||||
output.mapping.push_back(asdu_data);
|
||||
output.last_values.push_back(initial_value);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue