known_sources() reads the session files the layout store already owns, keeping only those that name a signal: a development machine accumulates one file per probe run on a throwaway port, and all 140 of them here hold empty slots.
98 lines
4.3 KiB
C++
98 lines
4.3 KiB
C++
// SPDX-License-Identifier: MIT
|
|
// SPDX-FileCopyrightText: 2026 Martino Ferrari
|
|
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "math_channel.hpp"
|
|
#include "measure.hpp"
|
|
|
|
namespace udpscope {
|
|
|
|
/// The user-arranged part of a session, persisted per data source in
|
|
/// `~/.config/udpscope/<host>_<port>.toml`: the display window, the plot layout
|
|
/// (rows and which signal each of a row's slots holds) and the per-signal
|
|
/// calibration. Signal names are the stable identity (channel indexes are
|
|
/// renumbered by every enable/disable, see the model's own invariant).
|
|
struct LayoutState {
|
|
struct Row {
|
|
/// One entry per slot; an empty string is an empty slot.
|
|
std::vector<std::string> signals;
|
|
};
|
|
|
|
double window_s = 0.0; ///< 0 keeps the default window
|
|
std::vector<Row> rows;
|
|
|
|
struct Calibration {
|
|
std::string name;
|
|
double gain = 1.0;
|
|
double offset = 0.0;
|
|
};
|
|
std::vector<Calibration> calibration;
|
|
|
|
/// Signals the session had enabled. Enabling is what starts acquisition, so this is what makes a
|
|
/// reopened scope look like the one that was closed -- and it is saved by *name*, like every other
|
|
/// identity here, because a CONFIG renumbers the channels.
|
|
std::vector<std::string> enabled;
|
|
|
|
/// How this source was reached, so the startup chooser can reconnect to it exactly. The file is
|
|
/// named `<host>_<port>.toml` and that name can say neither of these. Empty `multicast` is
|
|
/// unicast; empty `interface_addr` is the default route.
|
|
std::string multicast;
|
|
std::string interface_addr;
|
|
|
|
/// The derived channels, graphs and all: the editor's work is part of the session, not of the
|
|
/// process. A graph is stored as one string per node so the file stays in this store's TOML
|
|
/// subset (tables, string arrays, scalars) rather than growing a nested-table parser for it; see
|
|
/// `math_node_text()` for the field order.
|
|
std::vector<MathChannel> math;
|
|
|
|
/// The measurements pane, by signal *name* and with the type and options a row was built with.
|
|
std::vector<Measure> measures;
|
|
};
|
|
|
|
/// A data source udpscope has a stored session for: one row of the startup chooser.
|
|
struct KnownSource {
|
|
std::string host; ///< the CONFIG server, even for a multicast session
|
|
std::uint16_t port = 0;
|
|
std::string multicast; ///< empty = unicast
|
|
std::string interface_addr; ///< empty = the default route
|
|
int rows = 0; ///< plots the session had
|
|
int signals = 0; ///< named slots across those plots
|
|
std::int64_t mtime = 0; ///< orders the list; never shown
|
|
};
|
|
|
|
/// Every session file that names at least one signal, newest first.
|
|
///
|
|
/// The filter is what makes the list usable: a development machine accumulates one session file
|
|
/// per probe run on a throwaway port -- 140 of them when this was written -- and every one holds
|
|
/// `signals = ["", "", "", ""]`. A listed source is one somebody actually configured.
|
|
std::vector<KnownSource> known_sources();
|
|
|
|
/// Deletes one source's session file; the chooser's `x`. False means the file was already gone or
|
|
/// could not be removed, which costs the caller nothing -- there is no state to roll back.
|
|
bool forget_source(const KnownSource& src);
|
|
|
|
/// One `MathNode` as `kind|source|in_a|in_b|gain|offset|fc|q|lo|hi|x|y`, and back. The kind is the
|
|
/// op's *index* into `math_op_at()`, which is why the enum's order is a stored format and not just
|
|
/// an implementation detail: reordering it renames every saved graph's operations.
|
|
std::string math_node_text(const MathNode& node);
|
|
bool math_node_from_text(const std::string& text, MathNode& out);
|
|
|
|
/// `~/.config/udpscope/<host>_<port>.toml`, with the directory created.
|
|
/// Characters unsafe in a filename are replaced in the host part.
|
|
std::string layout_path(const std::string& host, std::uint16_t port);
|
|
|
|
/// Parses the file's TOML subset. Returns false when the file is missing or
|
|
/// malformed; `out` is left usable either way (a malformed file loads as much as
|
|
/// parsed, which beats refusing a hand-edited file over one stray character).
|
|
bool load_layout(const std::string& path, LayoutState& out);
|
|
|
|
/// Writes `state` as TOML. Atomic in effect: writes a sibling temp file and
|
|
/// renames it over the target.
|
|
bool save_layout(const std::string& path, const LayoutState& state);
|
|
|
|
} // namespace udpscope
|