/* Sample value remapping for mux. * * Author: Steffen Vogel * SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include using namespace villas; using namespace villas::node; using namespace villas::utils; int MappingEntry::parseString(const std::string &str) { std::smatch mr; std::regex re(RE_MAPPING); if (!std::regex_match(str, mr, re)) goto invalid_format; if (mr[1].matched) nodeName = mr.str(1); if (mr[9].matched) nodeName = mr.str(9); if (mr[6].matched) { data.first = strdup(mr.str(6).c_str()); data.last = mr[7].matched ? strdup(mr.str(7).c_str()) : nullptr; type = Type::DATA; } else if (mr[10].matched) { data.first = strdup(mr.str(10).c_str()); data.last = mr[11].matched ? strdup(mr.str(11).c_str()) : nullptr; type = Type::DATA; } else if (mr[8].matched) { data.first = strdup(mr.str(8).c_str()); data.last = nullptr; type = Type::DATA; } else if (mr[2].matched) { stats.type = Stats::lookupType(mr.str(3)); stats.metric = Stats::lookupMetric(mr.str(2)); type = Type::STATS; } else if (mr[5].matched) { if (mr.str(5) == "origin") timestamp.type = TimestampType::ORIGIN; else if (mr.str(5) == "received") timestamp.type = TimestampType::RECEIVED; else goto invalid_format; type = Type::TIMESTAMP; } else if (mr[4].matched) { if (mr.str(4) == "sequence") header.type = HeaderType::SEQUENCE; else if (mr.str(4) == "length") header.type = HeaderType::LENGTH; else goto invalid_format; type = Type::HEADER; } // Only node name given.. We map all data else if (!nodeName.empty()) { data.first = nullptr; data.last = nullptr; type = Type::DATA; } return 0; invalid_format: throw RuntimeError("Failed to parse mapping expression: {}", str); } MappingEntry::MappingEntry() : node(nullptr), type(Type::UNKNOWN), length(0), offset(0), nodeName() {} int MappingEntry::parse(json_t *json) { const char *str; str = json_string_value(json); if (!str) return -1; return parseString(str); } int MappingEntry::update(struct Sample *remapped, const struct Sample *original) const { unsigned len = length; if (offset + len > remapped->capacity) return -1; switch (type) { case Type::STATS: remapped->data[offset] = node->getStats()->getValue(stats.metric, stats.type); break; case Type::TIMESTAMP: { const struct timespec *ts; switch (timestamp.type) { case TimestampType::RECEIVED: ts = &original->ts.received; break; case TimestampType::ORIGIN: ts = &original->ts.origin; break; default: return -1; } remapped->data[offset + 0].i = ts->tv_sec; remapped->data[offset + 1].i = ts->tv_nsec; break; } case Type::HEADER: switch (header.type) { case HeaderType::LENGTH: remapped->data[offset].i = original->length; break; case HeaderType::SEQUENCE: remapped->data[offset].i = original->sequence; break; default: return -1; } break; case Type::DATA: for (unsigned j = data.offset, i = offset; j < MIN(original->length, (unsigned)(data.offset + length)); j++, i++) { if (j >= original->length) remapped->data[i].f = -1; else remapped->data[i] = original->data[j]; } len = MIN((unsigned)length, original->length - data.offset); break; case Type::UNKNOWN: return -1; } if (offset + len > remapped->length) remapped->length = offset + len; return 0; } int MappingEntry::prepare(NodeList &nodes) { if (!nodeName.empty() && node == nullptr) { node = nodes.lookup(nodeName); if (!node) throw RuntimeError("Invalid node name in mapping: {}", nodeName); } if (type == Type::DATA) { int first = -1, last = -1; if (data.first) { if (node) first = node->getInputSignals()->getIndexByName(data.first); if (first < 0) { char *endptr; first = strtoul(data.first, &endptr, 10); if (endptr != data.first + strlen(data.first)) throw RuntimeError("Failed to parse data index in mapping: {}", data.first); } } else { // Map all signals data.offset = 0; length = -1; goto end; } if (data.last) { if (node) last = node->getInputSignals()->getIndexByName(data.last); if (last < 0) { char *endptr; last = strtoul(data.last, &endptr, 10); if (endptr != data.last + strlen(data.last)) throw RuntimeError("Failed to parse data index in mapping: {}", data.last); } } else last = first; // single element: data[5] => data[5-5] if (last < first) throw RuntimeError("Invalid data range indices for mapping: {} < {}", last, first); data.offset = first; length = last - first + 1; } else { length = 1; } end: if (length < 0) length = node->getInputSignals()->size(); return 0; } std::string MappingEntry::toString(unsigned index) const { assert(length == 0 || (int)index < length); std::stringstream ss; if (node) ss << node->getNameShort() << "."; switch (type) { case Type::STATS: ss << "stats."; ss << Stats::metrics[stats.metric].name << "."; ss << Stats::types[stats.type].name; break; case Type::HEADER: ss << "hdr."; switch (header.type) { case HeaderType::LENGTH: ss << "length"; break; case HeaderType::SEQUENCE: ss << "sequence"; break; default: { } } break; case Type::TIMESTAMP: ss << "ts."; switch (timestamp.type) { case TimestampType::ORIGIN: ss << "origin."; break; case TimestampType::RECEIVED: ss << "received."; break; default: { } } ss << (index == 0 ? "sec" : "nsec"); break; case Type::DATA: if (node && index < node->getInputSignals()->size()) { auto s = node->getInputSignals()->getByIndex(index); ss << "data[" << s->name << "]"; } else ss << "data[" << index << "]"; break; case Type::UNKNOWN: return ""; } return ss.str(); } Signal::Ptr MappingEntry::toSignal(unsigned index) const { auto name = toString(index); switch (type) { case MappingEntry::Type::STATS: return std::make_shared(name, Stats::metrics[stats.metric].unit, Stats::types[stats.type].signal_type); case MappingEntry::Type::HEADER: switch (header.type) { case MappingEntry::HeaderType::LENGTH: case MappingEntry::HeaderType::SEQUENCE: return std::make_shared(name, "", SignalType::INTEGER); } break; case MappingEntry::Type::TIMESTAMP: switch (index) { case 0: return std::make_shared(name, "s", SignalType::INTEGER); case 1: return std::make_shared(name, "ns", SignalType::INTEGER); } break; case MappingEntry::Type::DATA: { auto sig = std::make_shared(data.signal->name, data.signal->unit, data.signal->type); sig->init = data.signal->init; return sig; } case MappingEntry::Type::UNKNOWN: break; } return std::shared_ptr(); }