mirror of
https://git.rwth-aachen.de/acs/public/villas/node/
synced 2025-03-09 00:00:00 +01:00
node-iec61850-goose: GooseSignal refactor + fixes
Signed-off-by: Philipp Jungkamp <philipp.jungkamp@rwth-aachen.de>
This commit is contained in:
parent
89813a8cc7
commit
9dcef61c99
2 changed files with 99 additions and 61 deletions
|
@ -36,7 +36,7 @@ public:
|
|||
struct Descriptor {
|
||||
SignalType signal_type;
|
||||
std::string name;
|
||||
std::optional<MmsType> mms_type;
|
||||
MmsType mms_type;
|
||||
Meta default_meta;
|
||||
};
|
||||
|
||||
|
@ -49,7 +49,7 @@ public:
|
|||
Type type() const;
|
||||
|
||||
// Corresponding mms type
|
||||
std::optional<MmsType> mmsType() const;
|
||||
MmsType mmsType() const;
|
||||
|
||||
// Corresponding signal type
|
||||
SignalType signalType() const;
|
||||
|
@ -58,25 +58,21 @@ public:
|
|||
static std::optional<GooseSignal> fromMmsValue(MmsValue *mms_value);
|
||||
|
||||
// Create a GooseSignal from type name and SignalData value
|
||||
static GooseSignal fromNameAndValue(char const *name, SignalData value, std::optional<Meta> meta = std::nullopt);
|
||||
static std::optional<GooseSignal> fromNameAndValue(char const *name, SignalData value, std::optional<Meta> meta = std::nullopt);
|
||||
|
||||
// Create a MmsValue from this GooseSignal
|
||||
std::optional<MmsValue *> toMmsValue() const;
|
||||
MmsValue * toMmsValue() const;
|
||||
|
||||
static std::optional<Type> lookupMmsType(int mms_type);
|
||||
|
||||
static std::optional<Type> lookupMmsTypeName(char const *name);
|
||||
|
||||
GooseSignal();
|
||||
|
||||
GooseSignal(Type type, SignalData value, std::optional<Meta> meta = std::nullopt);
|
||||
|
||||
SignalData signal_data;
|
||||
Meta meta;
|
||||
private:
|
||||
inline static std::array const descriptors {
|
||||
Descriptor { SignalType::INVALID, "invalid" },
|
||||
|
||||
// Boolean signals
|
||||
Descriptor { SignalType::BOOLEAN, "boolean", MmsType::MMS_BOOLEAN },
|
||||
|
||||
|
@ -99,9 +95,11 @@ private:
|
|||
|
||||
// Descriptor within the descriptors table above
|
||||
Descriptor const *descriptor;
|
||||
|
||||
};
|
||||
|
||||
bool operator==(GooseSignal &lhs, GooseSignal &rhs);
|
||||
bool operator!=(GooseSignal &lhs, GooseSignal &rhs);
|
||||
|
||||
class GooseNode : public Node {
|
||||
protected:
|
||||
enum InputTrigger {
|
||||
|
@ -112,7 +110,7 @@ protected:
|
|||
struct InputMapping {
|
||||
std::string subscriber;
|
||||
unsigned int index;
|
||||
GooseSignal dflt;
|
||||
GooseSignal::Type type;
|
||||
};
|
||||
|
||||
struct SubscriberConfig {
|
||||
|
@ -127,6 +125,7 @@ protected:
|
|||
|
||||
GooseNode *node;
|
||||
std::vector<GooseSignal> values;
|
||||
int last_state_num;
|
||||
};
|
||||
|
||||
struct Input {
|
||||
|
@ -143,7 +142,7 @@ protected:
|
|||
} input;
|
||||
|
||||
struct OutputData {
|
||||
std::optional<int> signal;
|
||||
std::optional<unsigned int> signal;
|
||||
GooseSignal default_value;
|
||||
};
|
||||
|
||||
|
@ -155,11 +154,13 @@ protected:
|
|||
uint16_t app_id;
|
||||
uint32_t conf_rev;
|
||||
uint32_t time_allowed_to_live;
|
||||
int burst;
|
||||
std::vector<OutputData> data;
|
||||
};
|
||||
|
||||
struct OutputContext {
|
||||
PublisherConfig config;
|
||||
std::vector<GooseSignal> values;
|
||||
|
||||
GoosePublisher publisher;
|
||||
};
|
||||
|
|
|
@ -37,7 +37,7 @@ static std::optional<std::array<uint8_t, 6>> stringToMac(char *mac_string)
|
|||
return std::optional { mac };
|
||||
}
|
||||
|
||||
std::optional<MmsType> GooseSignal::mmsType() const
|
||||
MmsType GooseSignal::mmsType() const
|
||||
{
|
||||
return descriptor->mms_type;
|
||||
}
|
||||
|
@ -83,20 +83,18 @@ std::optional<GooseSignal> GooseSignal::fromMmsValue(MmsValue *mms_value)
|
|||
return GooseSignal { descriptor.value(), data };
|
||||
}
|
||||
|
||||
GooseSignal GooseSignal::fromNameAndValue(char const *name, SignalData value, std::optional<Meta> meta)
|
||||
std::optional<GooseSignal> GooseSignal::fromNameAndValue(char const *name, SignalData value, std::optional<Meta> meta)
|
||||
{
|
||||
auto descriptor = lookupMmsTypeName(name);
|
||||
|
||||
if (!descriptor) return {};
|
||||
if (!descriptor) return std::nullopt;
|
||||
|
||||
return GooseSignal { descriptor.value(), value, meta };
|
||||
}
|
||||
|
||||
std::optional<MmsValue *> GooseSignal::toMmsValue() const
|
||||
MmsValue * GooseSignal::toMmsValue() const
|
||||
{
|
||||
if (!descriptor->mms_type) return std::nullopt;
|
||||
|
||||
switch (*descriptor->mms_type) {
|
||||
switch (descriptor->mms_type) {
|
||||
case MmsType::MMS_BOOLEAN:
|
||||
return MmsValue_newBoolean(signal_data.b);
|
||||
case MmsType::MMS_INTEGER:
|
||||
|
@ -200,12 +198,6 @@ std::optional<GooseSignal::Type> GooseSignal::lookupMmsTypeName(char const *name
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
GooseSignal::GooseSignal() :
|
||||
signal_data({}),
|
||||
descriptor(&descriptors[0])
|
||||
{
|
||||
}
|
||||
|
||||
GooseSignal::GooseSignal(GooseSignal::Descriptor const *descriptor, SignalData data, std::optional<Meta> meta) :
|
||||
signal_data(data),
|
||||
meta(meta.value_or(descriptor->default_meta)),
|
||||
|
@ -213,14 +205,49 @@ GooseSignal::GooseSignal(GooseSignal::Descriptor const *descriptor, SignalData d
|
|||
{
|
||||
}
|
||||
|
||||
bool iec61850::operator==(GooseSignal &lhs, GooseSignal &rhs) {
|
||||
if (lhs.mmsType() != rhs.mmsType())
|
||||
return false;
|
||||
|
||||
switch (lhs.mmsType()) {
|
||||
case MmsType::MMS_INTEGER:
|
||||
case MmsType::MMS_UNSIGNED:
|
||||
case MmsType::MMS_BIT_STRING:
|
||||
case MmsType::MMS_FLOAT:
|
||||
if (lhs.meta.size != rhs.meta.size)
|
||||
return false;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
switch (lhs.signalType()) {
|
||||
case SignalType::BOOLEAN:
|
||||
return lhs.signal_data.b == rhs.signal_data.b;
|
||||
case SignalType::INTEGER:
|
||||
return lhs.signal_data.i == rhs.signal_data.i;
|
||||
case SignalType::FLOAT:
|
||||
return lhs.signal_data.f == rhs.signal_data.f;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool iec61850::operator!=(GooseSignal &lhs, GooseSignal &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
void GooseNode::onEvent(GooseSubscriber subscriber, GooseNode::InputEventContext &ctx) noexcept
|
||||
{
|
||||
if (!GooseSubscriber_isValid(subscriber) || GooseSubscriber_needsCommission(subscriber))
|
||||
return;
|
||||
|
||||
int last_state_num = ctx.last_state_num;
|
||||
int state_num = GooseSubscriber_getStNum(subscriber);
|
||||
ctx.last_state_num = state_num;
|
||||
|
||||
if (ctx.subscriber_config.trigger == InputTrigger::CHANGE
|
||||
&& !ctx.values.empty()
|
||||
&& GooseSubscriber_getSqNum(subscriber) != 0)
|
||||
&& state_num == last_state_num)
|
||||
return;
|
||||
|
||||
auto mms_values = GooseSubscriber_getDataSetValues(subscriber);
|
||||
|
@ -230,12 +257,12 @@ void GooseNode::onEvent(GooseSubscriber subscriber, GooseNode::InputEventContext
|
|||
return;
|
||||
}
|
||||
|
||||
ctx.values.resize(MmsValue_getArraySize(mms_values));
|
||||
ctx.values.clear();
|
||||
|
||||
for (unsigned int i = 0; i < MmsValue_getArraySize(mms_values); i++) {
|
||||
auto mms_value = MmsValue_getElement(mms_values, i);
|
||||
auto goose_value = GooseSignal::fromMmsValue(mms_value).value();
|
||||
ctx.values[i] = goose_value;
|
||||
ctx.values.push_back(goose_value);
|
||||
}
|
||||
|
||||
uint64_t timestamp = GooseSubscriber_getTimestamp(subscriber);
|
||||
|
@ -264,12 +291,11 @@ void GooseNode::pushSample(uint64_t timestamp) noexcept
|
|||
for (unsigned int signal = 0; signal < sample->length; signal++) {
|
||||
auto& mapping = input.mappings[signal];
|
||||
auto& values = input.contexts[mapping.subscriber].values;
|
||||
sample->data[signal] = mapping.dflt.signal_data;
|
||||
|
||||
if (mapping.index >= values.size())
|
||||
continue;
|
||||
|
||||
if (mapping.dflt.mmsType() != values[mapping.index].mmsType()) {
|
||||
if (mapping.type->mms_type != values[mapping.index].mmsType()) {
|
||||
logger->error("unexpected mms_type for signal {}", sample->signals->getByIndex(signal)->toString());
|
||||
continue;
|
||||
}
|
||||
|
@ -437,6 +463,23 @@ int GooseNode::_read(Sample *samples[], unsigned sample_count)
|
|||
return available_samples;
|
||||
}
|
||||
|
||||
static void publish_values(GoosePublisher publisher, std::vector<GooseSignal> &values, bool changed, int burst) {
|
||||
auto data_set = LinkedList_create();
|
||||
|
||||
for (auto &value : values) {
|
||||
LinkedList_add(data_set, value.toMmsValue());
|
||||
}
|
||||
|
||||
if (changed)
|
||||
GoosePublisher_increaseStNum(publisher);
|
||||
|
||||
do {
|
||||
GoosePublisher_publish(publisher, data_set);
|
||||
} while (changed && --burst > 0);
|
||||
|
||||
LinkedList_destroyDeep(data_set, (LinkedListValueDeleteFunction) MmsValue_delete);
|
||||
}
|
||||
|
||||
int GooseNode::_write(Sample *samples[], unsigned sample_count)
|
||||
{
|
||||
if (output.state != Output::READY)
|
||||
|
@ -446,20 +489,25 @@ int GooseNode::_write(Sample *samples[], unsigned sample_count)
|
|||
auto sample = samples[i];
|
||||
|
||||
for (auto &ctx : output.contexts) {
|
||||
auto data_set = LinkedList_create();
|
||||
bool changed = false;
|
||||
|
||||
for (auto &data : ctx.config.data) {
|
||||
for (unsigned int data_index = 0; data_index < ctx.config.data.size(); data_index++) {
|
||||
auto data = ctx.config.data[data_index];
|
||||
auto goose_value = data.default_value;
|
||||
auto signal = data.signal.value_or(-1);
|
||||
if (signal >= 0 && signal < (int) sample->length)
|
||||
goose_value.signal_data = sample->data[signal];
|
||||
auto signal = data.signal;
|
||||
if (signal && *signal < sample->length)
|
||||
goose_value.signal_data = sample->data[*signal];
|
||||
|
||||
LinkedList_add(data_set, goose_value.toMmsValue().value());
|
||||
if (ctx.values.size() <= data_index) {
|
||||
changed = true;
|
||||
ctx.values.push_back(goose_value);
|
||||
} else if (ctx.values[data_index] != goose_value) {
|
||||
changed = true;
|
||||
ctx.values[data_index] = goose_value;
|
||||
}
|
||||
}
|
||||
|
||||
GoosePublisher_increaseStNum(ctx.publisher);
|
||||
GoosePublisher_publish(ctx.publisher, data_set);
|
||||
LinkedList_destroyDeep(data_set, (LinkedListValueDeleteFunction) MmsValue_delete);
|
||||
publish_values(ctx.publisher, ctx.values, changed, ctx.config.burst);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -625,35 +673,23 @@ int GooseNode::parseInputSignals(json_t *json, json_error_t *err, std::vector<Go
|
|||
|
||||
mappings.clear();
|
||||
|
||||
auto signals = getInputSignals();
|
||||
|
||||
json_array_foreach(json, index, value) {
|
||||
auto signal = signals->getByIndex(index);
|
||||
|
||||
char *mapping_subscriber;
|
||||
unsigned int mapping_index;
|
||||
char *mapping_type;
|
||||
char *mapping_type_name;
|
||||
ret = json_unpack_ex(value, err, 0, "{ s: s, s: i, s: s }",
|
||||
"subscriber", &mapping_subscriber,
|
||||
"index", &mapping_index,
|
||||
"mms_type", &mapping_type
|
||||
"mms_type", &mapping_type_name
|
||||
);
|
||||
if (ret) return ret;
|
||||
|
||||
auto mapping_dflt = GooseSignal::fromNameAndValue(mapping_type, signal->init);
|
||||
if (mapping_dflt.signalType() == SignalType::INVALID)
|
||||
throw RuntimeError("Invalid mms_type {}", mapping_type);
|
||||
|
||||
if (signal->type != mapping_dflt.signalType())
|
||||
throw RuntimeError("Expected signal type {} for mms_type {}, but got {}",
|
||||
signalTypeToString(mapping_dflt.signalType()),
|
||||
mapping_type,
|
||||
signalTypeToString(signal->type));
|
||||
auto mapping_type = GooseSignal::lookupMmsTypeName(mapping_type_name).value();
|
||||
|
||||
mappings.push_back(InputMapping {
|
||||
mapping_subscriber,
|
||||
mapping_index,
|
||||
mapping_dflt
|
||||
mapping_type,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -713,7 +749,7 @@ int GooseNode::parsePublisherData(json_t *json, json_error_t *err, std::vector<O
|
|||
auto goose_type = GooseSignal::lookupMmsTypeName(mms_type).value();
|
||||
std::optional<GooseSignal::Meta> meta = std::nullopt;
|
||||
|
||||
switch (goose_type->mms_type.value()) {
|
||||
switch (goose_type->mms_type) {
|
||||
case MmsType::MMS_INTEGER:
|
||||
if (integer_size != -1)
|
||||
meta = {.size = integer_size };
|
||||
|
@ -768,8 +804,9 @@ int GooseNode::parsePublisher(json_t *json, json_error_t *err, PublisherConfig &
|
|||
int app_id = 0;
|
||||
int conf_rev = 0;
|
||||
int time_allowed_to_live = 0;
|
||||
int burst = 1;
|
||||
json_t *data_json = nullptr;
|
||||
ret = json_unpack_ex(json, err, 0, "{ s:s, s:s, s:s, s:s, s:i, s:i, s:i, s:o }",
|
||||
ret = json_unpack_ex(json, err, 0, "{ s:s, s:s, s:s, s:s, s:i, s:i, s:i, s?:i, s:o }",
|
||||
"go_id", &go_id,
|
||||
"go_cb_ref", &go_cb_ref,
|
||||
"data_set_ref", &data_set_ref,
|
||||
|
@ -777,6 +814,7 @@ int GooseNode::parsePublisher(json_t *json, json_error_t *err, PublisherConfig &
|
|||
"app_id", &app_id,
|
||||
"conf_rev", &conf_rev,
|
||||
"time_allowed_to_live", &time_allowed_to_live,
|
||||
"burst", &burst,
|
||||
"data", &data_json
|
||||
);
|
||||
if (ret) return ret;
|
||||
|
@ -792,8 +830,9 @@ int GooseNode::parsePublisher(json_t *json, json_error_t *err, PublisherConfig &
|
|||
pc.app_id = app_id;
|
||||
pc.conf_rev = conf_rev;
|
||||
pc.time_allowed_to_live = time_allowed_to_live;
|
||||
pc.burst = burst;
|
||||
|
||||
parsePublisherData(data_json, err, pc.data);
|
||||
ret = parsePublisherData(data_json, err, pc.data);
|
||||
if (ret) return ret;
|
||||
|
||||
return 0;
|
||||
|
@ -827,12 +866,10 @@ int GooseNode::prepare()
|
|||
int ret;
|
||||
|
||||
ret = pool_init(&input.pool, input.queue_length, SAMPLE_LENGTH(getInputSignals(false)->size()));
|
||||
if (ret)
|
||||
return ret;
|
||||
if (ret) return ret;
|
||||
|
||||
ret = queue_signalled_init(&input.queue, input.queue_length);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (ret) return ret;
|
||||
|
||||
return Node::prepare();
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue