2017-03-27 12:28:13 +02:00
|
|
|
/* The internal datastructure for a sample of simulation data.
|
|
|
|
*
|
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-27 12:28:13 +02:00
|
|
|
*/
|
|
|
|
|
2023-09-14 11:36:03 +02:00
|
|
|
#include <cassert>
|
2019-06-23 16:57:00 +02:00
|
|
|
#include <cinttypes>
|
|
|
|
#include <cstring>
|
2017-03-27 12:28:13 +02:00
|
|
|
|
2023-09-14 11:36:03 +02:00
|
|
|
#include <openssl/crypto.h>
|
2023-09-07 11:46:39 +02:00
|
|
|
#include <villas/formats/villas_human.hpp>
|
2021-08-10 10:12:48 -04:00
|
|
|
#include <villas/sample.hpp>
|
|
|
|
#include <villas/signal.hpp>
|
2023-09-07 11:46:39 +02:00
|
|
|
#include <villas/timing.hpp>
|
|
|
|
#include <villas/utils.hpp>
|
2017-07-25 11:57:01 +02:00
|
|
|
|
2021-05-10 00:12:30 +02:00
|
|
|
using namespace villas::node;
|
|
|
|
|
2023-09-07 11:46:39 +02:00
|
|
|
size_t VILLASHumanFormat::sprintLine(char *buf, size_t len,
|
|
|
|
const struct Sample *smp) {
|
|
|
|
size_t off = 0;
|
|
|
|
|
|
|
|
if (flags & (int)SampleFlags::HAS_TS_ORIGIN) {
|
|
|
|
if (smp->flags & (int)SampleFlags::HAS_TS_ORIGIN) {
|
|
|
|
off += snprintf(buf + off, len - off, "%llu",
|
|
|
|
(unsigned long long)smp->ts.origin.tv_sec);
|
|
|
|
off += snprintf(buf + off, len - off, ".%09llu",
|
|
|
|
(unsigned long long)smp->ts.origin.tv_nsec);
|
|
|
|
} else
|
|
|
|
off += snprintf(buf + off, len - off, "0.0");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & (int)SampleFlags::HAS_OFFSET) {
|
|
|
|
if (smp->flags & (int)SampleFlags::HAS_TS_RECEIVED)
|
|
|
|
off += snprintf(buf + off, len - off, "%+e",
|
|
|
|
time_delta(&smp->ts.origin, &smp->ts.received));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & (int)SampleFlags::HAS_SEQUENCE) {
|
|
|
|
if (smp->flags & (int)SampleFlags::HAS_SEQUENCE)
|
|
|
|
off += snprintf(buf + off, len - off, "(%" PRIu64 ")", smp->sequence);
|
|
|
|
}
|
|
|
|
|
2023-09-14 11:36:03 +02:00
|
|
|
if (flags & (int)SampleFlags::NEW_FRAME) {
|
|
|
|
if (smp->flags & (int)SampleFlags::NEW_FRAME)
|
|
|
|
off += snprintf(buf + off, len - off, "F");
|
|
|
|
}
|
|
|
|
|
2023-09-07 11:46:39 +02:00
|
|
|
if (flags & (int)SampleFlags::HAS_DATA) {
|
|
|
|
for (unsigned i = 0; i < smp->length; i++) {
|
|
|
|
auto sig = smp->signals->getByIndex(i);
|
|
|
|
if (!sig)
|
|
|
|
break;
|
|
|
|
|
|
|
|
off += snprintf(buf + off, len - off, "\t");
|
|
|
|
off += smp->data[i].printString(sig->type, buf + off, len - off,
|
|
|
|
real_precision);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
off += snprintf(buf + off, len - off, "%c", delimiter);
|
|
|
|
|
|
|
|
return off;
|
2017-03-27 12:28:13 +02:00
|
|
|
}
|
|
|
|
|
2023-09-07 11:46:39 +02:00
|
|
|
size_t VILLASHumanFormat::sscanLine(const char *buf, size_t len,
|
|
|
|
struct Sample *smp) {
|
|
|
|
int ret;
|
|
|
|
char *end;
|
|
|
|
const char *ptr = buf;
|
2017-03-27 12:28:13 +02:00
|
|
|
|
2023-09-07 11:46:39 +02:00
|
|
|
double offset = 0;
|
2017-03-27 12:28:13 +02:00
|
|
|
|
2023-09-07 11:46:39 +02:00
|
|
|
smp->flags = 0;
|
|
|
|
smp->signals = signals;
|
2017-09-04 14:28:55 +02:00
|
|
|
|
2023-09-14 11:36:03 +02:00
|
|
|
/* Format: Seconds.NanoSeconds+Offset(SequenceNumber)Flags Value1 Value2 ...
|
|
|
|
* RegEx: (\d+(?:\.\d+)?)([-+]\d+(?:\.\d+)?(?:e[+-]?\d+)?)?(?:\((\d+)\))?(F)?
|
2017-03-27 12:28:13 +02:00
|
|
|
*
|
|
|
|
* Please note that only the seconds and at least one value are mandatory
|
|
|
|
*/
|
|
|
|
|
2023-09-07 11:46:39 +02:00
|
|
|
// Mandatory: seconds
|
|
|
|
smp->ts.origin.tv_sec = (uint32_t)strtoul(ptr, &end, 10);
|
|
|
|
if (ptr == end || *end == delimiter)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
smp->flags |= (int)SampleFlags::HAS_TS_ORIGIN;
|
|
|
|
|
|
|
|
// Optional: nano seconds
|
|
|
|
if (*end == '.') {
|
|
|
|
ptr = end + 1;
|
|
|
|
|
|
|
|
smp->ts.origin.tv_nsec = (uint32_t)strtoul(ptr, &end, 10);
|
|
|
|
if (ptr == end)
|
|
|
|
return -3;
|
|
|
|
} else
|
|
|
|
smp->ts.origin.tv_nsec = 0;
|
|
|
|
|
|
|
|
// Optional: offset / delay
|
|
|
|
if (*end == '+' || *end == '-') {
|
|
|
|
ptr = end;
|
|
|
|
|
|
|
|
offset = strtof(ptr, &end); // offset is ignored for now
|
|
|
|
if (ptr != end)
|
|
|
|
smp->flags |= (int)SampleFlags::HAS_OFFSET;
|
|
|
|
else
|
|
|
|
return -4;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Optional: sequence
|
|
|
|
if (*end == '(') {
|
|
|
|
ptr = end + 1;
|
|
|
|
|
|
|
|
smp->sequence = strtoul(ptr, &end, 10);
|
|
|
|
if (ptr != end)
|
|
|
|
smp->flags |= (int)SampleFlags::HAS_SEQUENCE;
|
|
|
|
else
|
|
|
|
return -5;
|
|
|
|
|
|
|
|
if (*end == ')')
|
|
|
|
end++;
|
|
|
|
}
|
|
|
|
|
2023-09-14 11:36:03 +02:00
|
|
|
// Optional: NEW_FRAME flag
|
|
|
|
if (*end == 'F') {
|
|
|
|
smp->flags |= (int)SampleFlags::NEW_FRAME;
|
|
|
|
end++;
|
|
|
|
}
|
|
|
|
|
2023-09-07 11:46:39 +02:00
|
|
|
unsigned i;
|
|
|
|
for (ptr = end + 1, i = 0; i < smp->capacity; ptr = end + 1, i++) {
|
|
|
|
if (*end == delimiter)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
auto sig = signals->getByIndex(i);
|
|
|
|
if (!sig)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ret = smp->data[i].parseString(sig->type, ptr, &end);
|
|
|
|
if (ret || end == ptr) // There are no valid values anymore.
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (*end == delimiter)
|
|
|
|
end++;
|
|
|
|
|
|
|
|
smp->length = i;
|
|
|
|
if (smp->length > 0)
|
|
|
|
smp->flags |= (int)SampleFlags::HAS_DATA;
|
|
|
|
|
|
|
|
if (smp->flags & (int)SampleFlags::HAS_OFFSET) {
|
|
|
|
struct timespec off = time_from_double(offset);
|
|
|
|
smp->ts.received = time_add(&smp->ts.origin, &off);
|
|
|
|
|
|
|
|
smp->flags |= (int)SampleFlags::HAS_TS_RECEIVED;
|
|
|
|
}
|
|
|
|
|
|
|
|
return end - buf;
|
2017-03-27 12:28:13 +02:00
|
|
|
}
|
|
|
|
|
2023-09-07 11:46:39 +02:00
|
|
|
void VILLASHumanFormat::header(FILE *f, const SignalList::Ptr sigs) {
|
|
|
|
// Abort if we are not supposed to, or have already printed the header
|
|
|
|
if (!print_header || header_printed)
|
|
|
|
return;
|
2017-09-04 16:02:43 +02:00
|
|
|
|
2023-09-07 11:46:39 +02:00
|
|
|
fprintf(f, "# ");
|
2018-05-12 18:02:18 +02:00
|
|
|
|
2023-09-07 11:46:39 +02:00
|
|
|
if (flags & (int)SampleFlags::HAS_TS_ORIGIN)
|
|
|
|
fprintf(f, "seconds.nanoseconds");
|
2018-05-12 18:02:18 +02:00
|
|
|
|
2023-09-07 11:46:39 +02:00
|
|
|
if (flags & (int)SampleFlags::HAS_OFFSET)
|
|
|
|
fprintf(f, "+offset");
|
2018-05-12 18:02:18 +02:00
|
|
|
|
2023-09-07 11:46:39 +02:00
|
|
|
if (flags & (int)SampleFlags::HAS_SEQUENCE)
|
|
|
|
fprintf(f, "(sequence)");
|
2018-10-20 16:23:57 +02:00
|
|
|
|
2023-09-07 11:46:39 +02:00
|
|
|
if (flags & (int)SampleFlags::HAS_DATA) {
|
|
|
|
for (unsigned i = 0; i < sigs->size(); i++) {
|
|
|
|
auto sig = sigs->getByIndex(i);
|
|
|
|
if (!sig)
|
|
|
|
break;
|
2018-10-20 16:23:57 +02:00
|
|
|
|
2023-09-07 11:46:39 +02:00
|
|
|
if (!sig->name.empty())
|
|
|
|
fprintf(f, "\t%s", sig->name.c_str());
|
|
|
|
else
|
|
|
|
fprintf(f, "\tsignal%u", i);
|
2018-10-20 16:23:57 +02:00
|
|
|
|
2023-09-07 11:46:39 +02:00
|
|
|
if (!sig->unit.empty())
|
|
|
|
fprintf(f, "[%s]", sig->unit.c_str());
|
|
|
|
}
|
|
|
|
}
|
2018-05-12 18:02:18 +02:00
|
|
|
|
2023-09-07 11:46:39 +02:00
|
|
|
fprintf(f, "%c", delimiter);
|
2017-09-04 16:02:43 +02:00
|
|
|
|
2023-09-07 11:46:39 +02:00
|
|
|
LineFormat::header(f, sigs);
|
2019-04-09 14:27:55 +02:00
|
|
|
}
|
2017-07-28 18:11:52 +02:00
|
|
|
|
2021-05-10 00:12:30 +02:00
|
|
|
// Register format
|
|
|
|
static char n[] = "villas.human";
|
|
|
|
static char d[] = "VILLAS human readable format";
|
2023-09-14 11:36:03 +02:00
|
|
|
static LineFormatPlugin<
|
|
|
|
VILLASHumanFormat, n, d,
|
|
|
|
(int)SampleFlags::HAS_TS_ORIGIN | (int)SampleFlags::HAS_SEQUENCE |
|
|
|
|
(int)SampleFlags::HAS_DATA | (int)SampleFlags::NEW_FRAME,
|
|
|
|
'\n'>
|
2023-09-07 11:46:39 +02:00
|
|
|
p;
|