working on todo.md features

This commit is contained in:
Martino Ferrari committed 2026-10-10 22:52:12 +02:00
1 parent 210d124814
commit 8f54841c2c
110 files changed
+15932 -6576

No files matched your search

+60
View File
@@ -170,6 +170,35 @@ ScopeModel::ScopeModel(const Config& cfg) : cfg_(cfg) {
trigger_.holdoff_s = cfg.trigger_holdoff_s;
}
namespace {
/// True when the incoming CONFIG names exactly the stream the model already has: same
/// signals, same shapes, rates, ranges and time modes. A reconnect re-sends the CONFIG,
/// and EMI makes that happen at the worst possible moment; the buffer, its history, the
/// holes, the trigger's held event and every measurement are all still valid and must
/// survive the reconnect untouched. Only a *different* stream is worth a re-plan, and a
/// re-plan is what drops history (see `rebuild_locked`).
bool same_stream_signals(const std::vector<SignalInfo>& incoming,
const std::vector<SignalInfo>& current) {
// `current` is the stream's signals with the math channels' synthetic entries appended,
// so compare the incoming list against current's prefix, skipping nothing on the left.
std::size_t n_stream = 0;
while (n_stream < current.size() && !current[n_stream].math) ++n_stream;
if (incoming.size() != n_stream) return false;
for (std::size_t i = 0; i < incoming.size(); ++i) {
const SignalInfo& a = incoming[i];
const SignalInfo& b = current[i];
if (a.name != b.name || a.unit != b.unit || a.type_code != b.type_code ||
a.quant_type != b.quant_type || a.rows != b.rows || a.cols != b.cols ||
a.range_min != b.range_min || a.range_max != b.range_max ||
a.time_mode != b.time_mode || a.sampling_rate != b.sampling_rate ||
a.time_signal_idx != b.time_signal_idx) {
return false;
}
}
return true;
}
} // namespace
void ScopeModel::set_signals(std::vector<SignalInfo> signals) {
std::lock_guard<std::mutex> lock(mutex_);
// Keep the enable state across CONFIG updates for signals that kept their name. The math
@@ -184,6 +213,14 @@ void ScopeModel::set_signals(std::vector<SignalInfo> signals) {
signals.erase(std::remove_if(signals.begin(), signals.end(),
[](const SignalInfo& s) { return s.math; }),
signals.end());
if (!signals_.empty() && same_stream_signals(signals, signals_)) {
// A reconnect's identical CONFIG: the honest recovery is *nothing* -- the ring keeps
// its history, the trigger keeps its state and its held event, and the outage shows
// as the gap in the data that it was.
std::fprintf(stderr, "[udpscope] CONFIG unchanged: keeping the buffer and its history\n");
std::fflush(stderr);
return;
}
signals_ = std::move(signals);
enabled_.assign(signals_.size(), false);
for (std::size_t i = 0; i < signals_.size(); ++i) {
@@ -257,6 +294,22 @@ void ScopeModel::prepare_math_locked() {
}
}
void ScopeModel::push_loaded_slot(const std::vector<f32>& values, f64 t, f64 abs_t) {
std::lock_guard<std::mutex> lock(mutex_);
if (!store_) return;
BufferStore& store = *store_;
const std::size_t channels = channels_.size();
std::vector<f32> slot(channels, 0.0f);
for (std::size_t i = 0; i < channels; ++i) {
const f32 v = i < values.size() ? values[i] : 0.0f;
store.values(i).push(v);
slot[i] = v;
}
store.time().push(t);
if (auto* abs = store.abs_time()) abs->push(abs_t);
push_math_sample(slot);
}
void ScopeModel::push_math_sample(const std::vector<f32>& sample) {
// The common case is no derived channels at all, and this runs once per *stored slot* -- so the
// early-out is what keeps a stream with no math from paying for the feature.
@@ -441,6 +494,13 @@ void ScopeModel::set_signal_enabled(std::size_t signal, bool on) {
generation_.fetch_add(1, std::memory_order_release);
}
std::pair<double, double> ScopeModel::calibration(std::size_t signal) {
std::lock_guard<std::mutex> lock(mutex_);
if (signal >= signals_.size()) return {1.0, 0.0};
const auto it = calibration_.find(signals_[signal].name);
return it != calibration_.end() ? it->second : std::pair<double, double>{1.0, 0.0};
}
void ScopeModel::set_calibration(std::size_t signal, double gain, double offset) {
std::lock_guard<std::mutex> lock(mutex_);
if (signal < signals_.size()) calibration_[signals_[signal].name] = {gain, offset};