debug: recover the scan command from the file itself
CI for csaxs_bec / test (push) Successful in 1m44s
CI for csaxs_bec / test (push) Successful in 1m44s
Asked where the scan command can be recovered reliably. It is in the master file: the file writer stores the whole scan metadata under /entry/collection/metadata/bec (file_writer.py:255), including request_inputs, which holds the arguments the scan was actually called with -- arg_bundle, inputs and kwargs. That beats notebooks, logbooks and recollection, because it is written by the component that ran the scan rather than by anyone describing it afterwards. It also settles the open question directly: whether S00473 was 0.65 um or 6.5 um is answerable from the file, not from the email thread. --command prints it and stops, request_inputs first. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -536,7 +536,57 @@ def check_line_motion(signals, grid, fast_axis) -> None:
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
|
||||
def analyse(path: str, do_list: bool, force_lines=None, force_points=None) -> None:
|
||||
def dump_scan_command(h5: h5py.File) -> None:
|
||||
"""Print what the scan was actually asked to do.
|
||||
|
||||
This is the authoritative record: the file writer stores the whole scan
|
||||
metadata under /entry/collection/metadata/bec, including `request_inputs`,
|
||||
which holds the arguments the scan was called with. It beats notebooks,
|
||||
logbooks and memory because it is written by the thing that ran the scan.
|
||||
"""
|
||||
roots = [p for p in ("/entry/collection/metadata/bec",
|
||||
"/entry/collection/metadata") if p in h5]
|
||||
if not roots:
|
||||
print(" no metadata group found -- cannot recover the command from this file")
|
||||
return
|
||||
for root in roots[:1]:
|
||||
print(f" scan request, from {root}:")
|
||||
shown = 0
|
||||
|
||||
def show(name, obj):
|
||||
nonlocal shown
|
||||
if not isinstance(obj, h5py.Dataset):
|
||||
return
|
||||
try:
|
||||
val = obj[()]
|
||||
except Exception: # pylint: disable=broad-except
|
||||
return
|
||||
if isinstance(val, bytes):
|
||||
val = val.decode(errors="replace")
|
||||
if isinstance(val, np.ndarray):
|
||||
val = val.tolist()
|
||||
text = str(val)
|
||||
if len(text) > 300:
|
||||
text = text[:300] + " ..."
|
||||
print(f" {name:<46} {text}")
|
||||
shown += 1
|
||||
|
||||
# request_inputs first: that is the command itself
|
||||
for sub in ("request_inputs", "scan_args", "scan_parameters"):
|
||||
if sub in h5[root]:
|
||||
h5[f"{root}/{sub}"].visititems(lambda n, o, s=sub: show(f"{s}/{n}", o))
|
||||
for key in sorted(h5[root]):
|
||||
if key in ("request_inputs", "scan_args", "scan_parameters"):
|
||||
continue
|
||||
node = h5[f"{root}/{key}"]
|
||||
if isinstance(node, h5py.Dataset):
|
||||
show(key, node)
|
||||
if not shown:
|
||||
print(" (nothing readable -- run --list to see the raw structure)")
|
||||
|
||||
|
||||
def analyse(path: str, do_list: bool, force_lines=None, force_points=None,
|
||||
show_command: bool = False) -> None:
|
||||
print("=" * 80)
|
||||
print(os.path.basename(path))
|
||||
print("=" * 80)
|
||||
@@ -547,6 +597,10 @@ def analyse(path: str, do_list: bool, force_lines=None, force_points=None) -> No
|
||||
"frames_per_trigger") if k in meta}
|
||||
print(" metadata:", interesting if interesting else "(none of the expected keys found)")
|
||||
|
||||
if show_command:
|
||||
dump_scan_command(h5)
|
||||
return
|
||||
|
||||
if do_list:
|
||||
# Per-point arrays first: the file is dominated by scalar configuration
|
||||
# entries, and a naive dump buries the data under hundreds of them.
|
||||
@@ -647,6 +701,8 @@ def main(argv=None) -> int:
|
||||
ap.add_argument("--root", default="/sls/x12sa/data/p23080/raw/data",
|
||||
help="data root used with --scan")
|
||||
ap.add_argument("--list", action="store_true", help="dump the file structure and stop")
|
||||
ap.add_argument("--command", action="store_true",
|
||||
help="print the scan request recorded in the file, and stop")
|
||||
ap.add_argument("--lines", type=int, help="override number of lines")
|
||||
ap.add_argument("--points", type=int, help="override points per line")
|
||||
args = ap.parse_args(argv)
|
||||
@@ -663,7 +719,7 @@ def main(argv=None) -> int:
|
||||
|
||||
for path in paths:
|
||||
try:
|
||||
analyse(path, args.list, args.lines, args.points)
|
||||
analyse(path, args.list, args.lines, args.points, args.command)
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
print(f" FAILED on {path}: {type(exc).__name__}: {exc}")
|
||||
print()
|
||||
|
||||
Reference in New Issue
Block a user