STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
LcTareStore.hpp
Go to the documentation of this file.
1
#ifndef LC_TARE_STORE_HPP
2
#define LC_TARE_STORE_HPP
3
4
#include <cstdint>
5
#include <functional>
6
#include <map>
7
#include <mutex>
8
#include <string>
9
#include <vector>
10
11
namespace
fsw
{
12
namespace
calibration
{
13
14
/**
15
* The publish-path entity name for an LC channel: "LC<slot>_Cal.CH<connector>", where slot is
16
* board_id % 10 with 0 -> 10 — the same rule DatabaseConfig uses to name the calibrated LC
17
* VTables, and the same board_number the LC publish path already holds.
18
*
19
* It exists as one shared function because the failure mode of a second copy is specific and
20
* has already happened: slot is NOT board_id, so PT board 22 and LC board 42 are both slot 2,
21
* and deriving a uid back out of an entity string collides them. Node must key on this string
22
* rather than re-derive it.
23
*/
24
std::string
lc_tare_entity
(uint8_t
board_id
, uint8_t connector);
25
26
/**
27
* One load cell's tare: the scale reads zero at the load it was holding when the operator
28
* pressed Tare.
29
*
30
* A tare is NOT a calibration point, and the difference is the reason this store exists
31
* separately from CubicCalibrationStore. A vented PT genuinely is at 0 psig, so capturing a
32
* zero on one is a true reference point and belongs in the shared fit. A load cell holding a
33
* tank is not at 0 kg: capturing that as a zero would inject a false point, and because
34
* add_point re-runs a least-squares fit over every point, it would tilt the whole cubic rather
35
* than shift its intercept. LC therefore gets a display-only tare, and its zero-capture keeps
36
* meaning "unloaded".
37
*
38
* WHAT IS STORED IS THE ADC CODE, NOT THE KILOGRAMS.
39
*
40
* `offset_kg` is a derived cache, re-evaluated from `adc_at_tare` through the current curve on
41
* every re-fit, clear, and profile swap. Persisting kilograms as the truth is the bug this
42
* layout exists to prevent: tare a 20 kg tank against a bad two-point fit that reads it as 18,
43
* improve the fit until the same tank evaluates to 20, and a frozen 18 kg offset displays 2 kg
44
* for a tank that never moved — a plausible-looking wrong number on a pad display. Re-deriving
45
* from the code gives 20 - 20 = 0.
46
*
47
* Subtraction happens in kilograms, never in counts: the curve is a cubic, so offsetting its
48
* input shifts the slope you get instead of translating its output.
49
*/
50
struct
LcTare
{
51
uint16_t
uid
= 0;
// board_id*100 + connector
52
std::string
entity
;
// publish-path identity, e.g. "LC2_Cal.CH1" — see set()
53
double
adc_at_tare
= 0.0;
// the truth
54
double
offset_kg
= 0.0;
// derived from adc_at_tare through the live curve; a cache
55
double
set_at_ms
= 0.0;
// unix milliseconds, for "tared 2h ago"
56
uint64_t
curve_fp
= 0;
// fingerprint of the curve offset_kg was computed against
57
};
58
59
/**
60
* Owns the per-channel tares for the calibration service. Thread-safe, persisted atomically to
61
* one JSON file (temp + rename) that the Node backend reads to apply the subtraction — the
62
* service never sends the value, because [0x46,0x00] is one-way and there is no reply packet.
63
* This mirrors how cubic calibration already reaches the UI.
64
*
65
* Ownership, which is deliberately asymmetric: C++ is the STEADY-STATE writer. Node may remove
66
* the file, but only while sensor-calibration is not running. That is what lets a tare be
67
* cleared at session start and still survive a service restart inside a session — two
68
* requirements that together force a file that is persistent AND externally cleared. Neither
69
* half is redundant; do not "simplify" one away.
70
*/
71
class
LcTareStore
{
72
public
:
73
explicit
LcTareStore
(std::string file_path);
74
75
/** adc -> kg through whichever model this uid streams (select_lc_kg: cubic fit or the
76
* datasheet physics conversion). Taken as a callable so this class never learns which. */
77
using
Evaluator
= std::function<double(
double
adc
)>;
78
79
/**
80
* Record a tare for `uid` at `adc_at_tare` and derive its offset. `entity` is the
81
* publish-path name (LC<slot>_Cal.CH<connector>) computed by the caller from the same
82
* board_number/channel the LC publish path uses; Node keys on this string and must never
83
* re-derive it, because slot is board_id % 10 and two boards of different kinds share a
84
* slot — the collision that put a load cell's curve on a 5000 psi transducer in Sep 2026.
85
*
86
* Returns false and records nothing when the curve yields a non-finite offset (a degenerate
87
* one-point fit does), because a NaN offset downstream silently kills the whole series.
88
*/
89
bool
set
(uint16_t uid,
const
std::string& entity,
double
adc_at_tare,
const
Evaluator
& eval);
90
91
void
clear
(uint16_t uid);
92
void
clear_all
();
93
94
/** Re-derive offset_kg for one uid under the current curve. No-op when the uid is untared. */
95
void
recompute
(uint16_t uid,
const
Evaluator
& eval);
96
97
/** Re-derive every tare; `eval_for` supplies each uid's evaluator. Call after any change to
98
* an LC curve: a capture, a clear, a profile swap, or startup. */
99
void
recompute_all
(
const
std::function<
Evaluator
(uint16_t)>& eval_for);
100
101
/**
102
* Re-derive only the tares whose recorded fingerprint disagrees with the curve now live, and
103
* return how many those were. Zero is the expected answer: every path that moves a curve is
104
* supposed to recompute already.
105
*
106
* A non-zero answer means one didn't, and the caller should say so loudly — it is the audit
107
* that makes a missed recompute hook visible instead of silently subtracting kilograms
108
* derived from a curve that no longer exists. In particular it catches the startup ordering
109
* mistake of reading this file AFTER the live store reload, where the reload's own recompute
110
* runs over an empty map and every restored offset stays stale.
111
*/
112
size_t
recompute_stale
(
const
std::function<
Evaluator
(uint16_t)>& eval_for);
113
114
/**
115
* False while the curves cannot be trusted — set when the cubic store failed to load. A
116
* recompute then keeps the last good offset rather than replacing it with one computed
117
* against a zeroed curve, which would read as a confident wrong number.
118
*/
119
void
set_curves_trusted
(
bool
trusted);
120
bool
curves_trusted
()
const
;
121
122
const
LcTare
*
tare_for
(uint16_t uid)
const
;
123
std::vector<uint16_t>
uids
()
const
;
124
size_t
size
()
const
;
125
126
/** Atomically write the JSON record (temp + rename). Blocked while load_failed(). */
127
bool
save
()
const
;
128
129
/** Load the JSON record. Returns tares loaded. */
130
size_t
load
();
131
132
/** Set when load() could not read an existing file; blocks save() so an unreadable store is
133
* never replaced by the empty one we fell back to. Mirrors CubicCalibrationStore. */
134
bool
load_failed
()
const
;
135
136
/**
137
* A fingerprint of what a curve *does*, not of the coefficients that describe it: the
138
* evaluator is sampled at fixed probe codes and the results hashed. Model-agnostic by
139
* construction, so a cubic re-fit, a cleared channel and a flip to the physics conversion
140
* all move it, and a no-op re-save does not. Cheap defence in depth — if some future path
141
* mutates a curve without calling recompute, a mismatched fingerprint makes the stale
142
* offset visible instead of silently subtracted.
143
*/
144
static
uint64_t
fingerprint
(
const
Evaluator
& eval);
145
146
private
:
147
mutable
std::mutex
mutex_
;
148
std::string
file_path_
;
149
bool
load_failed_
=
false
;
150
bool
curves_trusted_
=
true
;
151
std::map<uint16_t, LcTare>
tares_
;
152
153
std::string
serialize
()
const
;
// caller holds mutex_
154
/** Caller holds mutex_. Returns false when the curve yields a non-finite offset. */
155
bool
recompute_locked
(
LcTare
& t,
const
Evaluator
& eval);
156
};
157
158
}
// namespace calibration
159
}
// namespace fsw
160
161
#endif
// LC_TARE_STORE_HPP
fsw::calibration::LcTareStore::load_failed
bool load_failed() const
Set when load() could not read an existing file; blocks save() so an unreadable store is never replac...
Definition
LcTareStore.cpp:178
fsw::calibration::LcTareStore::load_failed_
bool load_failed_
Definition
LcTareStore.hpp:149
fsw::calibration::LcTareStore::recompute_locked
bool recompute_locked(LcTare &t, const Evaluator &eval)
Caller holds mutex_.
Definition
LcTareStore.cpp:60
fsw::calibration::LcTareStore::set
bool set(uint16_t uid, const std::string &entity, double adc_at_tare, const Evaluator &eval)
Record a tare for uid at adc_at_tare and derive its offset.
Definition
LcTareStore.cpp:71
fsw::calibration::LcTareStore::curves_trusted_
bool curves_trusted_
Definition
LcTareStore.hpp:150
fsw::calibration::LcTareStore::clear
void clear(uint16_t uid)
Definition
LcTareStore.cpp:94
fsw::calibration::LcTareStore::load
size_t load()
Load the JSON record.
Definition
LcTareStore.cpp:229
fsw::calibration::LcTareStore::tare_for
const LcTare * tare_for(uint16_t uid) const
Definition
LcTareStore.cpp:158
fsw::calibration::LcTareStore::mutex_
std::mutex mutex_
Definition
LcTareStore.hpp:147
fsw::calibration::LcTareStore::recompute
void recompute(uint16_t uid, const Evaluator &eval)
Re-derive offset_kg for one uid under the current curve.
Definition
LcTareStore.cpp:104
fsw::calibration::LcTareStore::set_curves_trusted
void set_curves_trusted(bool trusted)
False while the curves cannot be trusted — set when the cubic store failed to load.
Definition
LcTareStore.cpp:148
fsw::calibration::LcTareStore::file_path_
std::string file_path_
Definition
LcTareStore.hpp:148
fsw::calibration::LcTareStore::tares_
std::map< uint16_t, LcTare > tares_
Definition
LcTareStore.hpp:151
fsw::calibration::LcTareStore::uids
std::vector< uint16_t > uids() const
Definition
LcTareStore.cpp:164
fsw::calibration::LcTareStore::size
size_t size() const
Definition
LcTareStore.cpp:173
fsw::calibration::LcTareStore::recompute_stale
size_t recompute_stale(const std::function< Evaluator(uint16_t)> &eval_for)
Re-derive only the tares whose recorded fingerprint disagrees with the curve now live,...
Definition
LcTareStore.cpp:131
fsw::calibration::LcTareStore::LcTareStore
LcTareStore(std::string file_path)
Definition
LcTareStore.cpp:35
fsw::calibration::LcTareStore::curves_trusted
bool curves_trusted() const
Definition
LcTareStore.cpp:153
fsw::calibration::LcTareStore::save
bool save() const
Atomically write the JSON record (temp + rename).
Definition
LcTareStore.cpp:201
fsw::calibration::LcTareStore::recompute_all
void recompute_all(const std::function< Evaluator(uint16_t)> &eval_for)
Re-derive every tare; eval_for supplies each uid's evaluator.
Definition
LcTareStore.cpp:118
fsw::calibration::LcTareStore::Evaluator
std::function< double(double adc)> Evaluator
adc -> kg through whichever model this uid streams (select_lc_kg: cubic fit or the datasheet physics ...
Definition
LcTareStore.hpp:77
fsw::calibration::LcTareStore::clear_all
void clear_all()
Definition
LcTareStore.cpp:99
fsw::calibration::LcTareStore::fingerprint
static uint64_t fingerprint(const Evaluator &eval)
A fingerprint of what a curve does, not of the coefficients that describe it: the evaluator is sample...
Definition
LcTareStore.cpp:38
fsw::calibration::LcTareStore::serialize
std::string serialize() const
Definition
LcTareStore.cpp:183
board_id
static uint8_t board_id
Definition
main.cpp:30
adc
static STAR_MCP3201 adc(MCP3201_CS_PIN, &mcp_spi)
fsw::calibration
Output smoothing for calibrated PT pressure, per sensor uid (ESP32 and LabJack T7 alike).
Definition
AllanVariance.hpp:8
fsw::calibration::lc_tare_entity
std::string lc_tare_entity(uint8_t board_id, uint8_t connector)
The publish-path entity name for an LC channel: "LC<slot>_Cal.CH<connector>", where slot is board_id ...
Definition
LcTareStore.cpp:29
fsw
Motorized ball valve position: raw ADC counts -> % open.
Definition
AllanVariance.hpp:7
fsw::calibration::LcTare
One load cell's tare: the scale reads zero at the load it was holding when the operator pressed Tare.
Definition
LcTareStore.hpp:50
fsw::calibration::LcTare::offset_kg
double offset_kg
Definition
LcTareStore.hpp:54
fsw::calibration::LcTare::set_at_ms
double set_at_ms
Definition
LcTareStore.hpp:55
fsw::calibration::LcTare::entity
std::string entity
Definition
LcTareStore.hpp:52
fsw::calibration::LcTare::uid
uint16_t uid
Definition
LcTareStore.hpp:51
fsw::calibration::LcTare::adc_at_tare
double adc_at_tare
Definition
LcTareStore.hpp:53
fsw::calibration::LcTare::curve_fp
uint64_t curve_fp
Definition
LcTareStore.hpp:56
daq-server
diablo_server
lib
include
calibration
LcTareStore.hpp
Generated by
1.15.0