Without period, y=k*t+b and y=base+drift_rate*t grow infinitely, which is
unrealistic for industrial simulation. period_ms makes t wrap modulo the period,
producing sawtooth patterns that simulate per-cycle behavior (e.g. tension build-up
per coil, sensor drift per shift).
- read_csv.hpp: replace std::exit(1) with std::runtime_error throw
- read_csv.hpp: remove duplicate #pragma once
- RNG.cc: wrap JSON load in try-catch to prevent crash on missing config
- Generator.cc: fix comments to reflect actual 3-pass structure
Add CompositeModel for combining base+noise models, ModelRegistry singleton
with JSON-based model template loading, per-instance-key model isolation,
and inline CSV/valve pair/composite syntax parsing in createModel.
Config-driven random number generation with model templates in JSON and
signal-to-model binding via DB2 tables[1] field. Supports 9 analog modes,
4 boolean modes including valve_pair timing simulation.
Add AsyncDbWorker: a persistent background thread with dedup queue that
executes DB2 writes asynchronously, keeping the mon 20ms cycle free of
blocking I/O.
Changes:
- async_db_worker.h/.cc: singleton worker, submit() with rule_id dedup,
drain_and_stop() for clean shutdown
- eqp_stat.h/.cc: new update_static(ruleid, shear_times, running_time)
overload that skips redundant DB reads for known values (reduces
5 SELECTs to 3 per persist cycle)
- exp_times.cc: extract persist_exp_times() as a standalone function,
update_history_times() snapshots values and submits to worker
(returns immediately), reset_dev_data() uses direct SHM update
- eqpalg_icei.cpp: alg_mgr_.reset() → drain_and_stop() in destructor
ensures all algorithm threads are stopped before draining the worker
Risk: re-run cmake .. to pick up the new async_db_worker.cc file.
Three fixes in update_history_times():
1. Wrap DB operations in try-catch — exception no longer skips the
snapshot restore, preventing permanent loss of accumulated counts
2. Treat get_history_times() -1 return (DB failure) as skip, not as
"record exists" → no more silent UPDATE on non-existent rows
3. Only call update_static and advance last_load_time_ on success,
so a failed persist retries on the next cycle instead of waiting
another rw_time_ minutes
Mon's update_map_rule() called update_cold() which blindly copied
RuleStatLocal's stat_values (always empty in mon) and fetch_mark
(always false in mon) into SHM, destroying accumulated data and
breaking the mon-cron handshake.
stat_values and fetch_mark are managed exclusively by the
add_stat_value/get_stat_value handshake. The cold sync path only
needs to transport running_time and shear_times.