datactl/datatypes.h
datactl/datatypes.h
Namespaces
| Name |
|---|
| Syntalos |
Classes
| Name | |
|---|---|
| struct | Syntalos::supports_buffer_reuse |
| struct | Syntalos::supports_buffer_reuse< Frame > |
| struct | Syntalos::BaseDataType Base interface for all data types. |
| struct | Syntalos::ControlCommand A control command to a module. |
| struct | Syntalos::TableRow A new row for a table. |
| struct | Syntalos::LineCommand Command issued to a hardware signal line / channel / pin. |
| struct | Syntalos::LineReading Scalar reading from a hardware signal line at a moment in time. |
| struct | Syntalos::SignalBlockI32 A block of 32-bit signed integer signal data with timestamps. |
| struct | Syntalos::SignalBlockU16 A block of 16-bit unsigned integer signal data with timestamps. |
| struct | Syntalos::SignalBlockF32 A block of 32-bit floating-point timestamped signal data. |
Defines
| Name | |
|---|---|
| SY_DEFINE_DATA_TYPE(TypeName) Helper macro to define a Syntalos stream data type. |
Macros Documentation
define SY_DEFINE_DATA_TYPE
#define SY_DEFINE_DATA_TYPE(
TypeName
)
public: \
BaseDataType::TypeId typeId() const override \
{ \
return BaseDataType::TypeName; \
} \
static constexpr BaseDataType::TypeId staticTypeId() \
{ \
return BaseDataType::TypeName; \
} \
static constexpr const char *staticTypeName() \
{ \
return #TypeName; \
} \
\
private: \
TypeName(const TypeName &) = default; \
TypeName &operator=(const TypeName &) = default; \
\
public: \
TypeName(TypeName &&) noexcept = default; \
TypeName &operator=(TypeName &&) noexcept = default; \
[[nodiscard]] TypeName clone() const \
{ \
return TypeName(*this); \
}Helper macro to define a Syntalos stream data type.
Source code
/*
* Copyright (C) 2019-2026 Matthias Klumpp <matthias@tenstral.net>
*
* Licensed under the GNU Lesser General Public License Version 3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the license, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this software. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <memory>
#include <string>
#include <vector>
#include <concepts>
#include <charconv>
#include <cstdint>
#include <cassert>
#include <array>
#include <cmath>
#include <type_traits>
#include <tuple>
#include <utility>
#include "datactl/syclock.h"
#include "datactl/binarystream.h"
#include "datactl/eigenaux.h"
#include "datactl/flags.h"
namespace Syntalos
{
template<typename T>
struct supports_buffer_reuse : std::false_type {
};
// Frame carries a cv::Mat whose pixel buffer can be reused
struct Frame;
template<>
struct supports_buffer_reuse<Frame> : std::true_type {
};
enum class ModuleState : uint8_t {
UNKNOWN,
INITIALIZING,
IDLE,
PREPARING,
DORMANT,
READY,
RUNNING,
ERROR
};
std::string toString(ModuleState state);
struct BaseDataType {
public:
virtual ~BaseDataType() = default;
protected:
BaseDataType() = default;
BaseDataType(const BaseDataType &) = default;
BaseDataType &operator=(const BaseDataType &) = default;
BaseDataType(BaseDataType &&) noexcept = default;
BaseDataType &operator=(BaseDataType &&) noexcept = default;
public:
enum TypeId {
Unknown,
ControlCommand,
TableRow,
Frame,
LineCommand,
LineReading,
SignalBlockI32,
SignalBlockU16,
SignalBlockF32,
Last
};
static bool typeIdIsValid(int value)
{
return value >= 1 && value < TypeId::Last;
}
static std::string typeIdToString(int value);
static std::string typeIdToString(TypeId value);
static TypeId typeIdFromString(const std::string &str);
[[nodiscard]] virtual TypeId typeId() const = 0;
[[nodiscard]] virtual ssize_t memorySize() const
{
// Size is not known in advance
return -1;
}
virtual bool writeToMemory(void *memory, ssize_t size = -1) const
{
return false;
};
virtual bool toBytes(ByteVector &output) const = 0;
};
#define SY_DEFINE_DATA_TYPE(TypeName) \
public: \
BaseDataType::TypeId typeId() const override \
{ \
return BaseDataType::TypeName; \
} \
static constexpr BaseDataType::TypeId staticTypeId() \
{ \
return BaseDataType::TypeName; \
} \
static constexpr const char *staticTypeName() \
{ \
return #TypeName; \
} \
\
private: \
TypeName(const TypeName &) = default; \
TypeName &operator=(const TypeName &) = default; \
\
public: \
TypeName(TypeName &&) noexcept = default; \
TypeName &operator=(TypeName &&) noexcept = default; \
[[nodiscard]] TypeName clone() const \
{ \
return TypeName(*this); \
}
template<typename T>
concept supports_explicit_clone = requires(const T &obj) {
{ obj.clone() } -> std::same_as<T>;
};
template<typename T>
constexpr int syDataTypeId()
requires std::is_base_of_v<BaseDataType, T>
{
return T::staticTypeId();
}
template<typename T>
T deserializeFromMemory(const void *memory, size_t size)
requires std::is_base_of_v<BaseDataType, T>
{
return T::fromMemory(memory, size);
}
enum class ControlCommandKind {
UNKNOWN,
START,
PAUSE,
STOP,
STEP,
CUSTOM
};
// Forward declarations
struct Frame;
struct SignalBlockU16;
struct ControlCommand : BaseDataType {
SY_DEFINE_DATA_TYPE(ControlCommand)
ControlCommandKind kind{ControlCommandKind::UNKNOWN};
milliseconds_t duration;
std::string command;
explicit ControlCommand()
: duration(0)
{
}
explicit ControlCommand(ControlCommandKind ckind)
: kind(ckind),
duration(0)
{
}
void setDuration(ulong value)
{
duration = milliseconds_t(value);
}
[[nodiscard]] ulong getDurationAsInt() const
{
return duration.count();
}
bool toBytes(ByteVector &output) const override
{
BinaryStreamWriter stream(output);
stream.write(kind);
stream.write(static_cast<uint64_t>(duration.count()));
stream.write(command);
return true;
}
static ControlCommand fromMemory(const void *memory, size_t size)
{
ControlCommand obj;
BinaryStreamReader stream(memory, size);
uint64_t durationValue;
stream.read(obj.kind);
stream.read(durationValue);
stream.read(obj.command);
obj.duration = milliseconds_t(durationValue);
return obj;
}
};
struct TableRow : BaseDataType {
SY_DEFINE_DATA_TYPE(TableRow)
std::vector<std::string> data;
explicit TableRow() = default;
explicit TableRow(const std::vector<std::string> &row)
: data(row)
{
}
void reserve(int size)
{
data.reserve(size);
}
void append(const std::string &t)
{
data.push_back(t);
}
[[nodiscard]] int length() const
{
return data.size();
}
bool toBytes(ByteVector &output) const override
{
BinaryStreamWriter stream(output);
stream.write(data);
return true;
}
static TableRow fromMemory(const void *memory, size_t size)
{
TableRow obj;
BinaryStreamReader stream(memory, size);
stream.read(obj.data);
return obj;
}
};
enum class LineCommandKind {
UNKNOWN,
SET_MODE,
WRITE_DIGITAL,
WRITE_ANALOG,
WRITE_DIGITAL_PULSE,
WRITE_ANALOG_PULSE,
CUSTOM
};
enum class LineModeFlag : uint32_t {
NONE = 0,
IS_INPUT = 1u << 0,
IS_OUTPUT = 1u << 1,
PULL_UP = 1u << 2
};
using LineModeFlags = Flags<LineModeFlag>;
struct LineCommand : BaseDataType {
SY_DEFINE_DATA_TYPE(LineCommand)
LineCommandKind kind{LineCommandKind::UNKNOWN};
uint16_t lineId{0};
uint32_t value{0};
microseconds_t duration{};
LineModeFlags flags{LineModeFlag::NONE};
ByteVector extra;
explicit LineCommand() = default;
explicit LineCommand(LineCommandKind k)
: kind(k)
{
}
LineCommand(LineCommandKind k, uint16_t line)
: kind(k),
lineId(line)
{
}
LineCommand(LineCommandKind k, uint16_t line, uint32_t v)
: kind(k),
lineId(line),
value(v)
{
}
bool toBytes(ByteVector &output) const override
{
BinaryStreamWriter stream(output);
stream.write(kind);
stream.write(lineId);
stream.write(value);
stream.write(static_cast<int64_t>(duration.count()));
stream.write(flags.toInt());
stream.write(extra);
return true;
}
static LineCommand fromMemory(const void *memory, size_t size)
{
LineCommand obj;
BinaryStreamReader stream(memory, size);
int64_t durationUs;
uint32_t flagsRaw;
stream.read(obj.kind);
stream.read(obj.lineId);
stream.read(obj.value);
stream.read(durationUs);
stream.read(flagsRaw);
stream.read(obj.extra);
obj.duration = microseconds_t(durationUs);
obj.flags = LineModeFlags(flagsRaw);
return obj;
}
};
struct LineReading : BaseDataType {
SY_DEFINE_DATA_TYPE(LineReading)
uint16_t lineId{0};
uint32_t value{0};
microseconds_t time{};
explicit LineReading() = default;
bool toBytes(ByteVector &output) const override
{
BinaryStreamWriter stream(output);
stream.write(lineId);
stream.write(value);
stream.write(static_cast<int64_t>(time.count()));
return true;
}
static LineReading fromMemory(const void *memory, size_t size)
{
LineReading obj;
BinaryStreamReader stream(memory, size);
int64_t timeUs;
stream.read(obj.lineId);
stream.read(obj.value);
stream.read(timeUs);
obj.time = microseconds_t(timeUs);
return obj;
}
};
enum class SignalDataType {
Amplifier,
AuxInput,
SupplyVoltage,
BoardAdc,
BoardDigIn,
BoardDigOut
};
struct SignalBlockI32 : BaseDataType {
SY_DEFINE_DATA_TYPE(SignalBlockI32)
VectorXu64 timestamps;
MatrixXi32 data;
explicit SignalBlockI32(uint sampleCount = 60, uint channelCount = 1)
{
assert(channelCount > 0);
timestamps.resize(sampleCount);
data.resize(sampleCount, channelCount);
}
explicit SignalBlockI32(struct SignalBlockU16 &&src);
[[nodiscard]] size_t length() const
{
return timestamps.size();
}
[[nodiscard]] size_t rows() const
{
return data.rows();
}
[[nodiscard]] size_t cols() const
{
return data.cols();
}
bool toBytes(ByteVector &output) const override
{
BinaryStreamWriter stream(output);
serializeEigen(stream, timestamps);
serializeEigen(stream, data);
return true;
}
static SignalBlockI32 fromMemory(const void *memory, size_t size)
{
SignalBlockI32 obj;
BinaryStreamReader stream(memory, size);
obj.timestamps = deserializeEigen<VectorXu64>(stream);
obj.data = deserializeEigen<MatrixXi32>(stream);
return obj;
}
};
struct SignalBlockU16 : BaseDataType {
SY_DEFINE_DATA_TYPE(SignalBlockU16)
VectorXu64 timestamps;
MatrixXu16 data;
explicit SignalBlockU16(uint sampleCount = 60, uint channelCount = 1)
{
assert(channelCount > 0);
timestamps.resize(sampleCount);
data.resize(sampleCount, channelCount);
}
explicit SignalBlockU16(struct SignalBlockI32 &&src);
[[nodiscard]] size_t length() const
{
return timestamps.size();
}
[[nodiscard]] size_t rows() const
{
return data.rows();
}
[[nodiscard]] size_t cols() const
{
return data.cols();
}
bool toBytes(ByteVector &output) const override
{
BinaryStreamWriter stream(output);
serializeEigen(stream, timestamps);
serializeEigen(stream, data);
return true;
}
static SignalBlockU16 fromMemory(const void *memory, size_t size)
{
SignalBlockU16 obj;
BinaryStreamReader stream(memory, size);
obj.timestamps = deserializeEigen<VectorXu64>(stream);
obj.data = deserializeEigen<MatrixXu16>(stream);
return obj;
}
};
struct SignalBlockF32 : BaseDataType {
SY_DEFINE_DATA_TYPE(SignalBlockF32)
VectorXu64 timestamps;
MatrixXf data;
explicit SignalBlockF32(uint sampleCount = 60, uint channelCount = 1)
{
assert(channelCount > 0);
timestamps.resize(sampleCount);
data.resize(sampleCount, channelCount);
}
explicit SignalBlockF32(const std::vector<float> &floatVec, uint timestamp)
{
timestamps.array() += timestamp;
data.resize(1, floatVec.size());
for (size_t i = 0; i < floatVec.size(); ++i)
data(0, i) = floatVec[i];
}
[[nodiscard]] size_t length() const
{
return timestamps.size();
}
[[nodiscard]] size_t rows() const
{
return data.rows();
}
[[nodiscard]] size_t cols() const
{
return data.cols();
}
bool toBytes(ByteVector &output) const override
{
BinaryStreamWriter stream(output);
serializeEigen(stream, timestamps);
serializeEigen(stream, data);
return true;
}
static SignalBlockF32 fromMemory(const void *memory, size_t size)
{
SignalBlockF32 obj;
BinaryStreamReader stream(memory, size);
obj.timestamps = deserializeEigen<VectorXu64>(stream);
obj.data = deserializeEigen<MatrixXf>(stream);
return obj;
}
};
using StreamTypeList = std::
tuple<ControlCommand, TableRow, Frame, LineCommand, LineReading, SignalBlockI32, SignalBlockU16, SignalBlockF32>;
template<typename Fn>
constexpr bool forEachStreamType(Fn &&fn)
{
return [&]<size_t... Is>(std::index_sequence<Is...>) {
return (fn(std::type_identity<std::tuple_element_t<Is, StreamTypeList>>{}) || ...);
}(std::make_index_sequence<std::tuple_size_v<StreamTypeList>>{});
}
void registerStreamMetaTypes();
std::vector<std::pair<std::string, int>> streamTypeIdIndex();
template<typename T>
inline std::string numToString(T x)
requires std::is_arithmetic_v<T>
{
if constexpr (std::is_same_v<T, bool>) {
return x ? "true" : "false";
} else {
// Handle floating-point special cases
if constexpr (std::is_floating_point_v<T>) {
if (std::isnan(x))
return "nan";
if (std::isinf(x))
return std::signbit(x) ? "-inf" : "inf";
if (x == 0.0)
x = 0.0; // canonicalize -0 to +0
}
std::array<char, 128> buf{};
std::to_chars_result result{};
if constexpr (std::is_floating_point_v<T>) {
result = std::to_chars(buf.data(), buf.data() + buf.size(), x, std::chars_format::general);
} else {
result = std::to_chars(buf.data(), buf.data() + buf.size(), x);
}
if (result.ec != std::errc{}) {
assert(false);
return "<<conversion error>>";
}
return {buf.data(), result.ptr};
}
}
} // namespace Syntalos
Updated on 2026-05-20 at 12:42:23 +0000