18 return std::chrono::duration<double, std::milli>(
19 std::chrono::system_clock::now().time_since_epoch())
25constexpr double kProbes[] = {-1.0e9, -1.0e6, 0.0, 1.0e6, 1.0e9};
30 const int m =
static_cast<int>(
board_id) % 10;
31 const int slot = (m == 0 ? 10 : m);
32 return "LC" + std::to_string(slot) +
"_Cal.CH" + std::to_string(
static_cast<int>(connector));
44 uint64_t h = 1469598103934665603ull;
45 for (
double probe : kProbes) {
46 const double v = eval(
probe);
49 std::memcpy(&bits, &v,
sizeof(bits));
51 bits = 0xDEADBEEFDEADBEEFull;
52 for (
size_t i = 0; i <
sizeof(bits); ++i) {
53 h ^=
static_cast<uint64_t
>((bits >> (i * 8)) & 0xFF);
54 h *= 1099511628211ull;
63 const double kg = eval(t.adc_at_tare);
64 if (!std::isfinite(kg))
74 if (!std::isfinite(adc_at_tare))
80 t.adc_at_tare = adc_at_tare;
81 t.set_at_ms = unix_now_ms();
85 std::cout <<
"[LcTare] uid " <<
static_cast<int>(uid)
86 <<
": curve yields no finite offset at adc " << adc_at_tare
87 <<
" — tare not recorded" << std::endl;
108 auto it =
tares_.find(uid);
124 for (
auto& [uid, t] :
tares_) {
136 for (
auto& [uid, t] :
tares_) {
160 auto it =
tares_.find(uid);
166 std::vector<uint16_t> out;
167 out.reserve(
tares_.size());
168 for (
const auto& [uid, t] :
tares_)
187 for (
const auto& [uid, t] :
tares_) {
190 tj[
"entity"] = t.entity;
191 tj[
"adc_at_tare"] = t.adc_at_tare;
192 tj[
"offset_kg"] = t.offset_kg;
193 tj[
"set_at_ms"] = t.set_at_ms;
194 tj[
"curve_fp"] = t.curve_fp;
210 if (
p.has_parent_path())
211 std::filesystem::create_directories(
p.parent_path());
214 std::ofstream f(tmp, std::ios::trunc);
240 std::string content((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
244 root = nlohmann::json::parse(content);
247 std::cout <<
"[LcTare] " <<
file_path_ <<
" could not be parsed — refusing to overwrite it"
251 if (!root.contains(
"tares") || !root[
"tares"].is_array()) {
253 std::cout <<
"[LcTare] " <<
file_path_ <<
" has no tares array — refusing to overwrite it"
260 for (
const auto& tj : root[
"tares"]) {
264 t.uid =
static_cast<uint16_t
>(tj.value(
"uid", 0));
267 t.entity = tj.value(
"entity", std::string());
268 t.adc_at_tare = tj.value(
"adc_at_tare", 0.0);
269 t.offset_kg = tj.value(
"offset_kg", 0.0);
270 t.set_at_ms = tj.value(
"set_at_ms", 0.0);
271 t.curve_fp = tj.value(
"curve_fp",
static_cast<uint64_t
>(0));
272 if (!std::isfinite(t.adc_at_tare) || !std::isfinite(t.offset_kg))
* array(stored with pointer to save storage) array_t *array
bool load_failed() const
Set when load() could not read an existing file; blocks save() so an unreadable store is never replac...
bool recompute_locked(LcTare &t, const Evaluator &eval)
Caller holds mutex_.
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.
size_t load()
Load the JSON record.
const LcTare * tare_for(uint16_t uid) const
void recompute(uint16_t uid, const Evaluator &eval)
Re-derive offset_kg for one uid under the current curve.
void set_curves_trusted(bool trusted)
False while the curves cannot be trusted — set when the cubic store failed to load.
std::map< uint16_t, LcTare > tares_
std::vector< uint16_t > uids() const
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,...
LcTareStore(std::string file_path)
bool curves_trusted() const
bool save() const
Atomically write the JSON record (temp + rename).
void recompute_all(const std::function< Evaluator(uint16_t)> &eval_for)
Re-derive every tare; eval_for supplies each uid's evaluator.
std::function< double(double adc)> Evaluator
adc -> kg through whichever model this uid streams (select_lc_kg: cubic fit or the datasheet physics ...
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...
std::string serialize() const
static emmc_status_t probe(emmc_log_t *log, uint32_t k, bool *valid, uint32_t *session)
*brief remove last reference token *sa JSON pointer has no nullptr
constexpr double kProbes[]
Probe codes spanning the signed 32-bit ADC range the LC boards report.
Output smoothing for calibrated PT pressure, per sensor uid (ESP32 and LabJack T7 alike).
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 ...
Motorized ball valve position: raw ADC counts -> % open.
One load cell's tare: the scale reads zero at the load it was holding when the operator pressed Tare.