2017-03-25 21:11:52 +01:00
|
|
|
/* Sample value remapping for mux.
|
|
|
|
*
|
2022-03-15 09:18:01 -04:00
|
|
|
* Author: Steffen Vogel <post@steffenvogel.de>
|
2022-03-15 09:28:57 -04:00
|
|
|
* SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University
|
2022-07-04 18:20:03 +02:00
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
2017-03-25 21:11:52 +01:00
|
|
|
*/
|
|
|
|
|
2020-09-10 11:15:33 +02:00
|
|
|
#include <iostream>
|
|
|
|
#include <regex>
|
2017-03-25 21:11:52 +01:00
|
|
|
|
2020-07-04 16:22:10 +02:00
|
|
|
#include <villas/exceptions.hpp>
|
2021-08-10 10:12:48 -04:00
|
|
|
#include <villas/list.hpp>
|
|
|
|
#include <villas/mapping.hpp>
|
|
|
|
#include <villas/node.hpp>
|
|
|
|
#include <villas/sample.hpp>
|
|
|
|
#include <villas/signal.hpp>
|
2019-04-23 13:09:50 +02:00
|
|
|
#include <villas/utils.hpp>
|
2017-03-25 21:11:52 +01:00
|
|
|
|
2019-06-23 13:35:42 +02:00
|
|
|
using namespace villas;
|
2021-06-21 16:12:47 -04:00
|
|
|
using namespace villas::node;
|
2019-06-04 16:55:38 +02:00
|
|
|
using namespace villas::utils;
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int MappingEntry::parseString(const std::string &str) {
|
2020-09-10 11:15:33 +02:00
|
|
|
std::smatch mr;
|
|
|
|
std::regex re(RE_MAPPING);
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2020-09-10 11:15:33 +02:00
|
|
|
if (!std::regex_match(str, mr, re))
|
|
|
|
goto invalid_format;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2020-09-10 11:15:33 +02:00
|
|
|
if (mr[1].matched)
|
2021-08-10 10:12:48 -04:00
|
|
|
nodeName = mr.str(1);
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2020-09-10 11:15:33 +02:00
|
|
|
if (mr[9].matched)
|
2021-08-10 10:12:48 -04:00
|
|
|
nodeName = mr.str(9);
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2020-09-10 11:15:33 +02:00
|
|
|
if (mr[6].matched) {
|
2021-08-10 10:12:48 -04:00
|
|
|
data.first = strdup(mr.str(6).c_str());
|
2020-09-10 13:22:33 +02:00
|
|
|
data.last = mr[7].matched ? strdup(mr.str(7).c_str()) : nullptr;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
type = Type::DATA;
|
2020-09-10 11:15:33 +02:00
|
|
|
} else if (mr[10].matched) {
|
2021-08-10 10:12:48 -04:00
|
|
|
data.first = strdup(mr.str(10).c_str());
|
2020-09-10 13:22:33 +02:00
|
|
|
data.last = mr[11].matched ? strdup(mr.str(11).c_str()) : nullptr;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
type = Type::DATA;
|
2020-09-10 11:15:33 +02:00
|
|
|
} else if (mr[8].matched) {
|
2021-08-10 10:12:48 -04:00
|
|
|
data.first = strdup(mr.str(8).c_str());
|
|
|
|
data.last = nullptr;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
type = Type::DATA;
|
2020-09-10 11:15:33 +02:00
|
|
|
} else if (mr[2].matched) {
|
2021-08-10 10:12:48 -04:00
|
|
|
stats.type = Stats::lookupType(mr.str(3));
|
|
|
|
stats.metric = Stats::lookupMetric(mr.str(2));
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
type = Type::STATS;
|
2020-09-10 11:15:33 +02:00
|
|
|
} else if (mr[5].matched) {
|
|
|
|
if (mr.str(5) == "origin")
|
2021-08-10 10:12:48 -04:00
|
|
|
timestamp.type = TimestampType::ORIGIN;
|
2020-09-10 11:15:33 +02:00
|
|
|
else if (mr.str(5) == "received")
|
2021-08-10 10:12:48 -04:00
|
|
|
timestamp.type = TimestampType::RECEIVED;
|
2020-09-10 11:15:33 +02:00
|
|
|
else
|
2017-03-25 21:11:52 +01:00
|
|
|
goto invalid_format;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
type = Type::TIMESTAMP;
|
2020-09-10 11:15:33 +02:00
|
|
|
} else if (mr[4].matched) {
|
|
|
|
if (mr.str(4) == "sequence")
|
2021-08-10 10:12:48 -04:00
|
|
|
header.type = HeaderType::SEQUENCE;
|
2020-09-10 11:15:33 +02:00
|
|
|
else if (mr.str(4) == "length")
|
2021-08-10 10:12:48 -04:00
|
|
|
header.type = HeaderType::LENGTH;
|
2018-05-24 09:05:42 +02:00
|
|
|
else
|
|
|
|
goto invalid_format;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
type = Type::HEADER;
|
2017-03-25 21:11:52 +01:00
|
|
|
}
|
2020-09-10 11:15:33 +02:00
|
|
|
// Only node name given.. We map all data
|
2021-08-10 10:12:48 -04:00
|
|
|
else if (!nodeName.empty()) {
|
|
|
|
data.first = nullptr;
|
|
|
|
data.last = nullptr;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
type = Type::DATA;
|
2020-09-10 11:15:33 +02:00
|
|
|
}
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2017-03-25 21:11:52 +01:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
invalid_format:
|
2020-09-10 13:22:33 +02:00
|
|
|
|
2020-09-10 11:15:33 +02:00
|
|
|
throw RuntimeError("Failed to parse mapping expression: {}", str);
|
2017-03-25 21:11:52 +01:00
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
MappingEntry::MappingEntry()
|
|
|
|
: node(nullptr), type(Type::UNKNOWN), length(0), offset(0), nodeName() {}
|
2020-09-10 13:22:33 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int MappingEntry::parse(json_t *json) {
|
2017-03-25 21:11:52 +01:00
|
|
|
const char *str;
|
|
|
|
|
2021-02-16 14:15:14 +01:00
|
|
|
str = json_string_value(json);
|
2017-03-25 21:11:52 +01:00
|
|
|
if (!str)
|
|
|
|
return -1;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
return parseString(str);
|
2017-08-31 09:42:36 +02:00
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int MappingEntry::update(struct Sample *remapped,
|
|
|
|
const struct Sample *original) const {
|
|
|
|
unsigned len = length;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
if (offset + len > remapped->capacity)
|
2017-08-30 23:52:32 +02:00
|
|
|
return -1;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
switch (type) {
|
|
|
|
case Type::STATS:
|
|
|
|
remapped->data[offset] =
|
|
|
|
node->getStats()->getValue(stats.metric, stats.type);
|
2019-02-15 09:40:38 +01:00
|
|
|
break;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
case Type::TIMESTAMP: {
|
2018-08-06 11:08:23 +02:00
|
|
|
const struct timespec *ts;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
switch (timestamp.type) {
|
|
|
|
case TimestampType::RECEIVED:
|
2017-08-30 23:52:32 +02:00
|
|
|
ts = &original->ts.received;
|
|
|
|
break;
|
2021-08-10 10:12:48 -04:00
|
|
|
case TimestampType::ORIGIN:
|
2017-08-30 23:52:32 +02:00
|
|
|
ts = &original->ts.origin;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
remapped->data[offset + 0].i = ts->tv_sec;
|
|
|
|
remapped->data[offset + 1].i = ts->tv_nsec;
|
2017-08-28 14:35:50 +02:00
|
|
|
break;
|
2017-08-30 23:52:32 +02:00
|
|
|
}
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
case Type::HEADER:
|
|
|
|
switch (header.type) {
|
|
|
|
case HeaderType::LENGTH:
|
|
|
|
remapped->data[offset].i = original->length;
|
2017-08-30 23:52:32 +02:00
|
|
|
break;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
case HeaderType::SEQUENCE:
|
|
|
|
remapped->data[offset].i = original->sequence;
|
2017-08-30 23:52:32 +02:00
|
|
|
break;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2017-08-30 23:52:32 +02:00
|
|
|
default:
|
|
|
|
return -1;
|
2017-03-25 21:11:52 +01:00
|
|
|
}
|
2017-08-30 23:52:32 +02:00
|
|
|
break;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
case Type::DATA:
|
|
|
|
for (unsigned j = data.offset, i = offset;
|
|
|
|
j < MIN(original->length, (unsigned)(data.offset + length));
|
2019-04-07 15:13:40 +02:00
|
|
|
j++, i++) {
|
2018-08-20 18:31:27 +02:00
|
|
|
if (j >= original->length)
|
2019-03-09 00:32:22 +01:00
|
|
|
remapped->data[i].f = -1;
|
2018-08-20 18:31:27 +02:00
|
|
|
else
|
2019-03-09 00:32:22 +01:00
|
|
|
remapped->data[i] = original->data[j];
|
2017-03-25 21:11:52 +01:00
|
|
|
}
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
len = MIN((unsigned)length, original->length - data.offset);
|
2017-08-30 23:52:32 +02:00
|
|
|
break;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
case Type::UNKNOWN:
|
2020-09-10 13:22:33 +02:00
|
|
|
return -1;
|
2017-08-30 23:52:32 +02:00
|
|
|
}
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
if (offset + len > remapped->length)
|
|
|
|
remapped->length = offset + len;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2017-08-30 23:52:32 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2017-08-28 14:35:50 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int MappingEntry::prepare(NodeList &nodes) {
|
|
|
|
if (!nodeName.empty() && node == nullptr) {
|
|
|
|
node = nodes.lookup(nodeName);
|
|
|
|
if (!node)
|
|
|
|
throw RuntimeError("Invalid node name in mapping: {}", nodeName);
|
2020-09-10 11:15:33 +02:00
|
|
|
}
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
if (type == Type::DATA) {
|
2020-09-10 11:15:33 +02:00
|
|
|
int first = -1, last = -1;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
if (data.first) {
|
|
|
|
if (node)
|
|
|
|
first = node->getInputSignals()->getIndexByName(data.first);
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2020-09-10 11:15:33 +02:00
|
|
|
if (first < 0) {
|
|
|
|
char *endptr;
|
2021-08-10 10:12:48 -04:00
|
|
|
first = strtoul(data.first, &endptr, 10);
|
|
|
|
if (endptr != data.first + strlen(data.first))
|
|
|
|
throw RuntimeError("Failed to parse data index in mapping: {}",
|
|
|
|
data.first);
|
2020-09-10 11:15:33 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Map all signals
|
2021-08-10 10:12:48 -04:00
|
|
|
data.offset = 0;
|
|
|
|
length = -1;
|
2020-09-10 11:15:33 +02:00
|
|
|
goto end;
|
|
|
|
}
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
if (data.last) {
|
|
|
|
if (node)
|
|
|
|
last = node->getInputSignals()->getIndexByName(data.last);
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2020-09-10 11:15:33 +02:00
|
|
|
if (last < 0) {
|
|
|
|
char *endptr;
|
2021-08-10 10:12:48 -04:00
|
|
|
last = strtoul(data.last, &endptr, 10);
|
|
|
|
if (endptr != data.last + strlen(data.last))
|
|
|
|
throw RuntimeError("Failed to parse data index in mapping: {}",
|
|
|
|
data.last);
|
2019-03-09 00:32:22 +01:00
|
|
|
}
|
2020-09-10 11:15:33 +02:00
|
|
|
} else
|
|
|
|
last = first; // single element: data[5] => data[5-5]
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2020-09-10 11:15:33 +02:00
|
|
|
if (last < first)
|
|
|
|
throw RuntimeError("Invalid data range indices for mapping: {} < {}",
|
|
|
|
last, first);
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
data.offset = first;
|
|
|
|
length = last - first + 1;
|
|
|
|
} else {
|
|
|
|
length = 1;
|
2020-09-10 11:15:33 +02:00
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
end:
|
|
|
|
if (length < 0)
|
|
|
|
length = node->getInputSignals()->size();
|
2019-03-09 00:32:22 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
std::string MappingEntry::toString(unsigned index) const {
|
|
|
|
assert(length == 0 || (int)index < length);
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
std::stringstream ss;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
if (node)
|
|
|
|
ss << node->getNameShort() << ".";
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
switch (type) {
|
|
|
|
case Type::STATS:
|
|
|
|
ss << "stats.";
|
|
|
|
ss << Stats::metrics[stats.metric].name << ".";
|
|
|
|
ss << Stats::types[stats.type].name;
|
2018-08-02 10:46:03 +02:00
|
|
|
break;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
case Type::HEADER:
|
|
|
|
ss << "hdr.";
|
|
|
|
switch (header.type) {
|
|
|
|
case HeaderType::LENGTH:
|
|
|
|
ss << "length";
|
2018-10-21 22:37:49 +01:00
|
|
|
break;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
case HeaderType::SEQUENCE:
|
|
|
|
ss << "sequence";
|
2018-10-21 22:37:49 +01:00
|
|
|
break;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
default: {
|
|
|
|
}
|
2018-08-02 10:46:03 +02:00
|
|
|
}
|
|
|
|
break;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
case Type::TIMESTAMP:
|
|
|
|
ss << "ts.";
|
|
|
|
switch (timestamp.type) {
|
|
|
|
case TimestampType::ORIGIN:
|
|
|
|
ss << "origin.";
|
2018-10-21 22:37:49 +01:00
|
|
|
break;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
case TimestampType::RECEIVED:
|
|
|
|
ss << "received.";
|
2018-10-21 22:37:49 +01:00
|
|
|
break;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
default: {
|
2018-08-02 10:46:03 +02:00
|
|
|
}
|
2023-09-07 11:46:39 +02:00
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
ss << (index == 0 ? "sec" : "nsec");
|
2018-08-02 10:46:03 +02:00
|
|
|
break;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
case Type::DATA:
|
|
|
|
if (node && index < node->getInputSignals()->size()) {
|
|
|
|
auto s = node->getInputSignals()->getByIndex(index);
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
ss << "data[" << s->name << "]";
|
2018-08-02 10:46:03 +02:00
|
|
|
} else
|
2021-08-10 10:12:48 -04:00
|
|
|
ss << "data[" << index << "]";
|
2018-08-02 10:46:03 +02:00
|
|
|
break;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
case Type::UNKNOWN:
|
|
|
|
return "<unknown>";
|
2018-08-02 10:46:03 +02:00
|
|
|
}
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
return ss.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
Signal::Ptr MappingEntry::toSignal(unsigned index) const {
|
|
|
|
auto name = toString(index);
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
switch (type) {
|
|
|
|
case MappingEntry::Type::STATS:
|
|
|
|
return std::make_shared<Signal>(name, Stats::metrics[stats.metric].unit,
|
|
|
|
Stats::types[stats.type].signal_type);
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
case MappingEntry::Type::HEADER:
|
|
|
|
switch (header.type) {
|
|
|
|
case MappingEntry::HeaderType::LENGTH:
|
|
|
|
case MappingEntry::HeaderType::SEQUENCE:
|
|
|
|
return std::make_shared<Signal>(name, "", SignalType::INTEGER);
|
|
|
|
}
|
|
|
|
break;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
case MappingEntry::Type::TIMESTAMP:
|
|
|
|
switch (index) {
|
|
|
|
case 0:
|
|
|
|
return std::make_shared<Signal>(name, "s", SignalType::INTEGER);
|
|
|
|
case 1:
|
|
|
|
return std::make_shared<Signal>(name, "ns", SignalType::INTEGER);
|
|
|
|
}
|
|
|
|
break;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
case MappingEntry::Type::DATA: {
|
|
|
|
auto sig = std::make_shared<Signal>(data.signal->name, data.signal->unit,
|
|
|
|
data.signal->type);
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
sig->init = data.signal->init;
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
return sig;
|
|
|
|
}
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
case MappingEntry::Type::UNKNOWN:
|
|
|
|
break;
|
|
|
|
}
|
2023-09-07 11:46:39 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
return std::shared_ptr<Signal>();
|
2018-08-02 10:46:03 +02:00
|
|
|
}
|