test(e2e-chain): orchestrator + end-to-end fixes (Task 7)
Full-chain orchestrator (run_chain_e2e.sh) now runs the starter set green (3 pass). Fixes found bringing the chain up end-to-end: - client: gorilla/websocket cannot survive a read deadline (next ReadMessage panics "repeated read on failed connection"); replace the poll-with-deadline loop with a background reader goroutine + mutex-guarded state. - orchestrator: guard env.sh's unbound LD_LIBRARY_PATH under set -u. - scenarios/gen_data: centralize NUM_ROWS/ROW_DT and enforce sine freq to be a multiple of the buffer fundamental (LOOP_HZ=5 Hz) so the looped FileReader buffer is a seamless waveform; align starter freqs (5/5/10 Hz). - gen_cfg: FileReader allows exactly one consuming Function, so route tapped (oracle=fed/both) sources through the DDB (ReaderGAM->DDB, then StreamGAM and TapGAM both read DDB) instead of a second FileReader consumer. - validate_waveform: fidelity gates correctness (bit-exact / within one quant level); sine shape becomes a gross frequency-sanity gate (corr>=0.5) plus a tracked corr/nRMSE quality metric, since per-sample wall-clock calibration (Phase-A) and FULL_ARRAY packed timestamps (Phase-A4) are still pending. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+41
-10
@@ -31,7 +31,17 @@ def _ndims(elements):
|
||||
|
||||
def _gam_sig(sig, datasource):
|
||||
"""A GAM signal entry referencing a DataSource (no UDPStreamer extras)."""
|
||||
return (f"{sig['name']} = {{ Type = {sig['type']} "
|
||||
return _gam_sig_named(sig["name"], sig, datasource)
|
||||
|
||||
|
||||
def _gam_sig_named(name, sig, datasource):
|
||||
"""A GAM signal entry with an explicit (possibly renamed) signal name.
|
||||
|
||||
IOGAM copies inputs to outputs positionally, so the input and output names
|
||||
may differ; we exploit that to route through the DDB with source-prefixed
|
||||
names that never clash with the TimerGAM's Counter/Time or across sources.
|
||||
"""
|
||||
return (f"{name} = {{ Type = {sig['type']} "
|
||||
f"NumberOfDimensions = {_ndims(sig['elements'])} "
|
||||
f"NumberOfElements = {sig['elements']} DataSource = {datasource} }}")
|
||||
|
||||
@@ -96,22 +106,43 @@ def write_marte_cfg(scenario, path, input_bin, tap_bin=None):
|
||||
sid = src["id"]
|
||||
fpath = input_bin if i == 0 else f"{input_bin}.{sid}"
|
||||
rds = f"FileReaderDS_{sid}"
|
||||
in_sigs = " ".join(_gam_sig(sig, rds) for sig in src["signals"])
|
||||
out_sigs = " ".join(_gam_sig(sig, f"Streamer_{sid}") for sig in src["signals"])
|
||||
gams.append(
|
||||
f" +ReaderGAM_{sid} = {{ Class = IOGAM "
|
||||
f"InputSignals = {{ {in_sigs} }} OutputSignals = {{ {out_sigs} }} }}")
|
||||
thread_funcs.append(f"ReaderGAM_{sid}")
|
||||
# The FileReader DataSource allows exactly one consuming Function, so a
|
||||
# tapped source must route through the DDB: ReaderGAM copies FileReader
|
||||
# -> DDB (source-prefixed names), then StreamGAM and TapGAM both read DDB.
|
||||
tap_here = want_tap and i == 0
|
||||
if tap_here:
|
||||
in_sigs = " ".join(_gam_sig(sig, rds) for sig in src["signals"])
|
||||
ddb_out = " ".join(_gam_sig_named(f"{sid}_{sig['name']}", sig, "DDB")
|
||||
for sig in src["signals"])
|
||||
gams.append(
|
||||
f" +ReaderGAM_{sid} = {{ Class = IOGAM "
|
||||
f"InputSignals = {{ {in_sigs} }} OutputSignals = {{ {ddb_out} }} }}")
|
||||
ddb_in = " ".join(_gam_sig_named(f"{sid}_{sig['name']}", sig, "DDB")
|
||||
for sig in src["signals"])
|
||||
stream_out = " ".join(_gam_sig(sig, f"Streamer_{sid}")
|
||||
for sig in src["signals"])
|
||||
gams.append(
|
||||
f" +StreamGAM_{sid} = {{ Class = IOGAM "
|
||||
f"InputSignals = {{ {ddb_in} }} OutputSignals = {{ {stream_out} }} }}")
|
||||
thread_funcs.append(f"ReaderGAM_{sid}")
|
||||
thread_funcs.append(f"StreamGAM_{sid}")
|
||||
else:
|
||||
in_sigs = " ".join(_gam_sig(sig, rds) for sig in src["signals"])
|
||||
out_sigs = " ".join(_gam_sig(sig, f"Streamer_{sid}") for sig in src["signals"])
|
||||
gams.append(
|
||||
f" +ReaderGAM_{sid} = {{ Class = IOGAM "
|
||||
f"InputSignals = {{ {in_sigs} }} OutputSignals = {{ {out_sigs} }} }}")
|
||||
thread_funcs.append(f"ReaderGAM_{sid}")
|
||||
datas.append(
|
||||
f' +{rds} = {{ Class = FileReader Filename = "{fpath}" '
|
||||
f'Interpolate = "no" FileFormat = "binary" EOF = "Rewind" }}')
|
||||
datas.append(_streamer_block(src, scenario))
|
||||
|
||||
if want_tap:
|
||||
# tap the first source's signals to a FileWriter (fed reference);
|
||||
# read again from the FileReader DS (a second consumer is allowed)
|
||||
# tap the first source's signals (now in the DDB) to a FileWriter.
|
||||
src = srcs[0]
|
||||
tap_in = " ".join(_gam_sig(sig, f"FileReaderDS_{src['id']}")
|
||||
sid = src["id"]
|
||||
tap_in = " ".join(_gam_sig_named(f"{sid}_{sig['name']}", sig, "DDB")
|
||||
for sig in src["signals"])
|
||||
tap_out = " ".join(_gam_sig(sig, "TapWriterDS") for sig in src["signals"])
|
||||
gams.append(
|
||||
|
||||
Reference in New Issue
Block a user