diff --git a/.github/workflows/ci-scripts.yml b/.github/workflows/ci-scripts.yml index 7cacb35a..afb3298e 100644 --- a/.github/workflows/ci-scripts.yml +++ b/.github/workflows/ci-scripts.yml @@ -108,6 +108,7 @@ jobs: python3 test/test_generate_dependency_report.py python3 test/verify_coordinate_map.py python3 test/validate_bob_xml.py + python3 test/validate_bob_prefix_macros.py reuse lint git diff --check diff --git a/.gitignore b/.gitignore index 3a3963fe..3bbb08f2 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,11 @@ dbd/ include/ .vscode/ TemplateReadme.md + +# Python bytecode caches +__pycache__/ +*.py[cod] + # IOC autosave runtime data (README.md is tracked) iocs/tpx3IOC/iocBoot/iocTimePix/autosave/* !iocs/tpx3IOC/iocBoot/iocTimePix/autosave/README.md diff --git a/test/validate_bob_prefix_macros.py b/test/validate_bob_prefix_macros.py new file mode 100644 index 00000000..5ddf826c --- /dev/null +++ b/test/validate_bob_prefix_macros.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python3 +"""Static regression checks for ADTimePix3 service-prefix inheritance.""" + +from pathlib import Path +import sys +import xml.etree.ElementTree as ET + + +ROOT = Path(__file__).resolve().parents[1] +BOB_ROOT = ROOT / "tpx3App" / "op" / "bob" +LEGACY_PREFIXES = ("SERVAL-TEST:", "EMU-TEST:") + +CHILD_SCREENS = { + BOB_ROOT / "Serval/tpx3serval.bob": ("TPX3-TEST:", "Serval:"), + BOB_ROOT / "Emulator/emulator.bob": ("TPX3-TEST:", "Emulator:"), + BOB_ROOT / "Emulator/emulator_tpx3_embed.bob": ("TPX3-TEST:", "Emulator:"), + BOB_ROOT / "Emulator/emulator_mpx3_embed.bob": ("MPX3-TEST:", "Emulator:"), +} + +ENTRY_SCREENS = { + BOB_ROOT / "TimePix3.bob": ("TPX3-TEST:", "profiles/tpx3/main.bob"), + BOB_ROOT / "MediPix3.bob": ("MPX3-TEST:", "profiles/mpx3/main.bob"), + BOB_ROOT / "MediPix3/MediPix3.bob": ("MPX3-TEST:", "../profiles/mpx3/main.bob"), +} + +LAUNCHERS = { + BOB_ROOT / "profiles/tpx3/TimePix3Status.bob": "TPX3-TEST:", + BOB_ROOT / "profiles/tpx3/Acquire/DetectorConfig.bob": "TPX3-TEST:", + BOB_ROOT / "profiles/mpx3/Mpx3Status.bob": "MPX3-TEST:", + BOB_ROOT / "profiles/mpx3/Detector/Mpx3DetectorConfig.bob": "MPX3-TEST:", +} + +TARGETS = { + "emulator.bob": "Emulator:", + "tpx3serval.bob": "Serval:", +} + +STANDALONE_DEFAULT_SCREENS = { + BOB_ROOT / "Events/tpx3RawStream.bob", +} + + +def relative(path: Path) -> str: + return str(path.relative_to(ROOT)) + + +def main() -> int: + errors: list[str] = [] + parsed: dict[Path, ET.Element] = {} + allowed_default_screens = ( + set(CHILD_SCREENS) + | set(ENTRY_SCREENS) + | set(LAUNCHERS) + | STANDALONE_DEFAULT_SCREENS + ) + + for path in sorted(BOB_ROOT.rglob("*.bob")): + text = path.read_text(encoding="utf-8") + for prefix in LEGACY_PREFIXES: + if prefix in text: + errors.append(f"{relative(path)}: obsolete prefix {prefix}") + if "$(P=" in text and path not in allowed_default_screens: + errors.append( + f"{relative(path)}: P fallback belongs at a display boundary, " + "not in an ordinary embedded panel" + ) + try: + parsed[path] = ET.fromstring(text) + except ET.ParseError as exc: + errors.append(f"{relative(path)}: invalid XML: {exc}") + + for path, (fallback, service_r) in CHILD_SCREENS.items(): + root = parsed.get(path) + if root is None: + errors.append(f"{relative(path)}: child screen was not parsed") + continue + + if root.find("./macros/P") is not None: + errors.append(f"{relative(path)}: display-level P overrides callers") + if root.findtext("./macros/R") != service_r: + errors.append(f"{relative(path)}: expected display-level R={service_r}") + + expected = f"$(P={fallback})$(R)" + pv_names = [node.text or "" for node in root.iter("pv_name")] + if not any(expected in value for value in pv_names): + errors.append(f"{relative(path)}: missing fallback {fallback}") + for value in pv_names: + if "$(P" in value and expected not in value: + errors.append( + f"{relative(path)}: unexpected P expression in PV {value!r}" + ) + + for path, (fallback, profile_file) in ENTRY_SCREENS.items(): + root = parsed.get(path) + if root is None: + errors.append(f"{relative(path)}: entry screen was not parsed") + continue + + if root.find("./macros/P") is not None or root.find("./macros/R") is not None: + errors.append(f"{relative(path)}: display-level P/R overrides callers") + + embedded = next( + ( + widget + for widget in root.findall("./widget[@type='embedded']") + if widget.findtext("file") == profile_file + ), + None, + ) + if embedded is None: + errors.append(f"{relative(path)}: missing embedded profile {profile_file}") + continue + + expected_p = f"$(P={fallback})" + actual_p = embedded.findtext("macros/P") + actual_r = embedded.findtext("macros/R") + if actual_p != expected_p: + errors.append( + f"{relative(path)}: embedded profile passes P={actual_p!r}, " + f"expected {expected_p!r}" + ) + if actual_r != "$(R=cam1:)": + errors.append( + f"{relative(path)}: embedded profile passes R={actual_r!r}, " + "expected '$(R=cam1:)'" + ) + + action_count = 0 + for path, root in parsed.items(): + for action in root.findall(".//action[@type='open_display']"): + target_file = action.findtext("file") or "" + matching_target = next( + (target for target in TARGETS if target_file.endswith(target)), None + ) + if matching_target is None: + continue + + action_count += 1 + fallback = LAUNCHERS.get(path) + if fallback is None: + errors.append( + f"{relative(path)}: unclassified launcher action for {target_file}" + ) + continue + + expected_p = f"$(P={fallback})" + expected_r = TARGETS[matching_target] + actual_p = action.findtext("macros/P") + actual_r = action.findtext("macros/R") + if actual_p != expected_p: + errors.append( + f"{relative(path)}: {target_file} passes P={actual_p!r}, " + f"expected {expected_p!r}" + ) + if actual_r != expected_r: + errors.append( + f"{relative(path)}: {target_file} passes R={actual_r!r}, " + f"expected {expected_r!r}" + ) + + if action_count != 8: + errors.append(f"expected 8 Serval/emulator launcher actions, found {action_count}") + + if errors: + print("ADTimePix3 screen checks failed:", file=sys.stderr) + for error in errors: + print(f"- {error}", file=sys.stderr) + return 1 + + print( + f"Validated {len(parsed)} BOB files, {len(ENTRY_SCREENS)} entry screens, " + f"{len(CHILD_SCREENS)} child screens, and {action_count} " + "Serval/emulator launcher actions." + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tpx3App/op/bob/Emulator/README.md b/tpx3App/op/bob/Emulator/README.md index d9c57468..b74babf6 100644 --- a/tpx3App/op/bob/Emulator/README.md +++ b/tpx3App/op/bob/Emulator/README.md @@ -16,13 +16,13 @@ This directory contains Phoebus **Display Builder** (`.bob`) screens for the **m See the `.bob` files for exact widget placement. Logical sections: ### 1. Main Control Section (Top) -- **Start/Stop Slide Button**: Controls the TPX3 emulator process (connected to `$(P)$(R)START` PV) -- **Status LED**: Visual indicator showing if the process is running (green) or stopped (red) (connected to `$(P)$(R)STATUS` PV) -- **Process ID Display**: Shows the current process ID when running (connected to `$(P)$(R)PROCESS_ID` PV) -- **Error Message Display**: Shows any error messages or status updates (connected to `$(P)$(R)ERROR_MSG` PV) +- **Start/Stop Slide Button**: Controls the TPX3 emulator process (connected to `$(P=TPX3-TEST:)$(R)START` PV) +- **Status LED**: Visual indicator showing if the process is running (green) or stopped (red) (connected to `$(P=TPX3-TEST:)$(R)STATUS` PV) +- **Process ID Display**: Shows the current process ID when running (connected to `$(P=TPX3-TEST:)$(R)PROCESS_ID` PV) +- **Error Message Display**: Shows any error messages or status updates (connected to `$(P=TPX3-TEST:)$(R)ERROR_MSG` PV) ### 2. Shell configuration ( **`emulator.bob`** ) -- **`EMULATOR_TYPE`**: Choosing 0 vs 1 toggles visibility of the two embedded `.bob` files (rules use `$(P)$(R)EMULATOR_TYPE`; keep `$(P)` defined when launching the screen). +- **`EMULATOR_TYPE`**: Choosing 0 vs 1 toggles visibility of the two embedded `.bob` files. The shell uses `$(P=TPX3-TEST:)$(R)EMULATOR_TYPE`, so caller-provided `P` wins and a direct open uses the TPX3 test fallback. - **Firmware / network**: Shared widgets on the shell (same PVs as the IOC DB). - **Embedded Timepix3 panel**: Hit rate, TDC, chipboard, JVM `-DchipMask`, and all raw replay widgets — see **`emulator_tpx3_embed.bob`**. - **Embedded Medipix3 panel**: MPX CLI options — see **`emulator_mpx3_embed.bob`**. @@ -31,7 +31,7 @@ See the `.bob` files for exact widget placement. Logical sections: - **JAR**, **`-Xmx`**, **`-DcmdPort`**: Applicable to both emulator JAR types; replay controls are **not** here anymore (they are on the TPX embed). ### 4. Command line (bottom) -- **Generated Command Line**: Shows the complete Java command that will be executed (connected to `$(P)$(R)COMMAND_LINE` PV) +- **Generated Command Line**: Shows the complete Java command that will be executed (connected to `$(P=TPX3-TEST:)$(R)COMMAND_LINE` PV) - **Real-time Updates**: Updates automatically as parameters are changed ## Supported Widget Types @@ -79,15 +79,14 @@ The display uses the following Phoebus Display Builder compatible widgets: ## PV Naming Convention -The display uses the standard EPICS PV naming convention with predefined macros: -- **Prefix**: `$(P)` = `EMU-TEST:` (dedicated emulator service; not the camera `TPX3-TEST:` / `MPX3-TEST:` prefix) +The displays use the standard EPICS PV naming convention with caller-overridable macros: +- **Prefix**: `emulator.bob` and `emulator_tpx3_embed.bob` use `$(P=TPX3-TEST:)`; `emulator_mpx3_embed.bob` uses `$(P=MPX3-TEST:)` - **Record**: `$(R)` = `Emulator:` -- **Example**: The START PV becomes `EMU-TEST:Emulator:START` +- **Example**: The TPX3 lab-default START PV is `TPX3-TEST:Emulator:START` -Lab default matches `/epics/iocs/emulator/iocBoot/ioctpx3emulator/st.cmd` (`Sys=EMU-TEST:`). -Beamline override: open with `P=EMU-$(BL):` (e.g. `EMU-BL7:`) and set the IOC `Sys` to the same value. -**Do not inherit camera `P`** — Phoebus caller macros override display defaults, so every Emulator -`open_display` action should pass `P`/`R` explicitly. +No source screen defines a display-level `P`, because that would override `P` inherited from an upper beamline screen. Opening a source screen directly uses its detector-specific fallback; opening it from an upper screen uses the caller-provided `P`. `R=Emulator:` remains defined at display level. + +The IOC is configured independently with `DETECTOR_PREFIX`, which defaults to `TPX3-TEST:` in `iocBoot/ioctpx3emulator/st.cmd`. For beamline use, set `DETECTOR_PREFIX` to the corresponding ADTimePix3 IOC's system prefix and pass that same value as `P` from the upper screen. Sync source of truth: `/epics/iocs/emulator/tpx3emulatorApp/op/bob/` → this directory. @@ -129,7 +128,7 @@ Sync source of truth: `/epics/iocs/emulator/tpx3emulatorApp/op/bob/` → this di ### **PVs Not Connecting** - Verify EPICS Channel Access is working -- Check that macros `$(P)` and `$(R)` are properly defined +- Check that caller macro `P`, or the appropriate TPX3/MPX3 fallback, matches the IOC's `DETECTOR_PREFIX`; `R` should remain `Emulator:` - Ensure the IOC is running and accessible ### **Text Not Updating** diff --git a/tpx3App/op/bob/Emulator/emulator.bob b/tpx3App/op/bob/Emulator/emulator.bob index 3278cd3a..83ed12f5 100644 --- a/tpx3App/op/bob/Emulator/emulator.bob +++ b/tpx3App/op/bob/Emulator/emulator.bob @@ -2,7 +2,6 @@ tpx3Emulator -

EMU-TEST:

Emulator:
1150 @@ -51,7 +50,7 @@ StartStopButton - $(P)$(R)START + $(P=TPX3-TEST:)$(R)START 20 40 @@ -60,7 +59,7 @@ StatusLED - $(P)$(R)STATUS + $(P=TPX3-TEST:)$(R)STATUS 160 40 30 @@ -81,7 +80,7 @@ ErrorMessageDisplay - $(P)$(R)ERROR_MSG + $(P=TPX3-TEST:)$(R)ERROR_MSG 540 40 400 @@ -96,7 +95,7 @@ Text Update - $(P)$(R)PROCESS_ID + $(P=TPX3-TEST:)$(R)PROCESS_ID 300 40 120 @@ -135,7 +134,7 @@ EmulatorTypeCombo - $(P)$(R)EMULATOR_TYPE + $(P=TPX3-TEST:)$(R)EMULATOR_TYPE 140 36 22 @@ -165,7 +164,7 @@ FirmwareSpinner - $(P)$(R)FIRMWARE_VERSION + $(P=TPX3-TEST:)$(R)FIRMWARE_VERSION 20 35 150 @@ -173,7 +172,7 @@ FirmwareEnable - $(P)$(R)FIRMWARE_VERSION_ENABLE + $(P=TPX3-TEST:)$(R)FIRMWARE_VERSION_ENABLE 160 @@ -198,7 +197,7 @@ NetworkSpinner - $(P)$(R)NETWORK + $(P=TPX3-TEST:)$(R)NETWORK 20 35 150 @@ -206,7 +205,7 @@ NetworkEnable - $(P)$(R)NETWORK_ENABLE + $(P=TPX3-TEST:)$(R)NETWORK_ENABLE 200 80 @@ -229,7 +228,7 @@ true - $(P)$(R)EMULATOR_TYPE + $(P=TPX3-TEST:)$(R)EMULATOR_TYPE @@ -250,7 +249,7 @@ true - $(P)$(R)EMULATOR_TYPE + $(P=TPX3-TEST:)$(R)EMULATOR_TYPE @@ -286,7 +285,7 @@ JarFileNameInput - $(P)$(R)JarFileName + $(P=TPX3-TEST:)$(R)JarFileName 180 40 730 @@ -294,14 +293,14 @@ JarFileSelector - $(P)$(R)JarFileName + $(P=TPX3-TEST:)$(R)JarFileName 920 35 30 JarFileEnable - $(P)$(R)JAR_FILE_ENABLE + $(P=TPX3-TEST:)$(R)JAR_FILE_ENABLE 980 35 @@ -315,7 +314,7 @@ JarFileRBV - $(P)$(R)JarFile_RBV + $(P=TPX3-TEST:)$(R)JarFile_RBV 180 70 880 @@ -330,14 +329,14 @@ JavaXmxEntry - $(P)$(R)JAVA_XMX + $(P=TPX3-TEST:)$(R)JAVA_XMX 180 100 6 JavaXmxEnable - $(P)$(R)JAVA_XMX_ENABLE + $(P=TPX3-TEST:)$(R)JAVA_XMX_ENABLE 290 95 @@ -351,7 +350,7 @@ CmdPort - $(P)$(R)CMD_PORT + $(P=TPX3-TEST:)$(R)CMD_PORT 180 130 1.0 @@ -359,7 +358,7 @@ CmdPortEnable - $(P)$(R)CMD_PORT_ENABLE + $(P=TPX3-TEST:)$(R)CMD_PORT_ENABLE 290 125 @@ -389,7 +388,7 @@ CommandLineDisplay - $(P)$(R)COMMAND_LINE + $(P=TPX3-TEST:)$(R)COMMAND_LINE 20 40 1040 diff --git a/tpx3App/op/bob/Emulator/emulator_mpx3_embed.bob b/tpx3App/op/bob/Emulator/emulator_mpx3_embed.bob index 9b4628eb..19f36a86 100644 --- a/tpx3App/op/bob/Emulator/emulator_mpx3_embed.bob +++ b/tpx3App/op/bob/Emulator/emulator_mpx3_embed.bob @@ -2,7 +2,6 @@ emulator_mpx3_embed -

EMU-TEST:

Emulator:
1060 @@ -29,7 +28,7 @@
MegapixelEn - $(P)$(R)MEGAPIXEL_ENABLE + $(P=MPX3-TEST:)$(R)MEGAPIXEL_ENABLE 20 40 @@ -37,7 +36,7 @@ CornerTextEn - $(P)$(R)CORNER_TEXT_ENABLE + $(P=MPX3-TEST:)$(R)CORNER_TEXT_ENABLE 160 40 @@ -52,14 +51,14 @@ MpxChipMaskSp - $(P)$(R)MPX_CHIP_MASK + $(P=MPX3-TEST:)$(R)MPX_CHIP_MASK 380 45 15.0 MpxMaskEnable - $(P)$(R)MPX_CHIP_MASK_ENABLE + $(P=MPX3-TEST:)$(R)MPX_CHIP_MASK_ENABLE 485 40 @@ -73,7 +72,7 @@ LossEn - $(P)$(R)MPX_LOSS_ENABLE + $(P=MPX3-TEST:)$(R)MPX_LOSS_ENABLE 785 40 @@ -87,7 +86,7 @@ FracEn - $(P)$(R)MPX_FRAC_ENABLE + $(P=MPX3-TEST:)$(R)MPX_FRAC_ENABLE 165 90 @@ -101,26 +100,26 @@ Omr0En - $(P)$(R)MPX_OMR0_ENABLE + $(P=MPX3-TEST:)$(R)MPX_OMR0_ENABLE 435 90 Spinner - $(P)$(R)MPX_OMR0 + $(P=MPX3-TEST:)$(R)MPX_OMR0 330 95 Spinner_1 - $(P)$(R)MPX_FRAC + $(P=MPX3-TEST:)$(R)MPX_FRAC 60 95 Spinner_2 - $(P)$(R)MPX_LOSS + $(P=MPX3-TEST:)$(R)MPX_LOSS 680 45 diff --git a/tpx3App/op/bob/Emulator/emulator_tpx3_embed.bob b/tpx3App/op/bob/Emulator/emulator_tpx3_embed.bob index ca118f5c..2489c90d 100644 --- a/tpx3App/op/bob/Emulator/emulator_tpx3_embed.bob +++ b/tpx3App/op/bob/Emulator/emulator_tpx3_embed.bob @@ -2,7 +2,6 @@ emulator_tpx3_embed -

EMU-TEST:

Emulator:
1060 @@ -44,7 +43,7 @@ HitRateSpinner - $(P)$(R)HIT_RATE + $(P=TPX3-TEST:)$(R)HIT_RATE 20 35 80 @@ -52,7 +51,7 @@ HitRateEnable - $(P)$(R)HIT_RATE_ENABLE + $(P=TPX3-TEST:)$(R)HIT_RATE_ENABLE 180 80 @@ -77,13 +76,13 @@ TDCSpinner - $(P)$(R)TDC + $(P=TPX3-TEST:)$(R)TDC 20 35 TDCEnable - $(P)$(R)TDC_ENABLE + $(P=TPX3-TEST:)$(R)TDC_ENABLE 140 @@ -107,7 +106,7 @@ ChipboardSpinner - $(P)$(R)CHIPBOARD_ID + $(P=TPX3-TEST:)$(R)CHIPBOARD_ID 20 35 140 @@ -115,7 +114,7 @@ ChipboardEnable - $(P)$(R)CHIPBOARD_ID_ENABLE + $(P=TPX3-TEST:)$(R)CHIPBOARD_ID_ENABLE 140 @@ -145,7 +144,7 @@ ChipMask - $(P)$(R)CHIP_MASK + $(P=TPX3-TEST:)$(R)CHIP_MASK 120 40 80 @@ -154,7 +153,7 @@ ChipMaskEnable - $(P)$(R)CHIP_MASK_ENABLE + $(P=TPX3-TEST:)$(R)CHIP_MASK_ENABLE 220 40 @@ -183,7 +182,7 @@ ReplayFileNameInput - $(P)$(R)ReplayFileName + $(P=TPX3-TEST:)$(R)ReplayFileName 180 30 700 @@ -191,14 +190,14 @@ ReplayFileSelector - $(P)$(R)ReplayFileName + $(P=TPX3-TEST:)$(R)ReplayFileName 880 25 30 ReplayFileEnable - $(P)$(R)REPLAY_FILE_ENABLE + $(P=TPX3-TEST:)$(R)REPLAY_FILE_ENABLE 925 25 @@ -213,7 +212,7 @@ ReplayFileRBV - $(P)$(R)ReplayFile_RBV + $(P=TPX3-TEST:)$(R)ReplayFile_RBV 180 60 830 @@ -228,7 +227,7 @@ ReplayMode - $(P)$(R)REPLAY_MODE + $(P=TPX3-TEST:)$(R)REPLAY_MODE 180 90 200 @@ -250,7 +249,7 @@ RepeatCount - $(P)$(R)REPEAT_COUNT + $(P=TPX3-TEST:)$(R)REPEAT_COUNT 180 120 200 @@ -266,7 +265,7 @@ ReplayFileList - $(P)$(R)REPLAY_FILE_LIST + $(P=TPX3-TEST:)$(R)REPLAY_FILE_LIST 180 150 830 @@ -281,7 +280,7 @@ ReplayCurrentFile - $(P)$(R)REPLAY_CURRENT_FILE + $(P=TPX3-TEST:)$(R)REPLAY_CURRENT_FILE 180 180 830 @@ -296,7 +295,7 @@ ReplayIteration - $(P)$(R)REPLAY_ITERATION + $(P=TPX3-TEST:)$(R)REPLAY_ITERATION 180 210 200 @@ -310,7 +309,7 @@ ReplayRemaining - $(P)$(R)REPLAY_REMAINING + $(P=TPX3-TEST:)$(R)REPLAY_REMAINING 560 210 200 @@ -324,7 +323,7 @@ ReplayStatusMsg - $(P)$(R)REPLAY_STATUS_MSG + $(P=TPX3-TEST:)$(R)REPLAY_STATUS_MSG 180 240 830 diff --git a/tpx3App/op/bob/MediPix3.bob b/tpx3App/op/bob/MediPix3.bob index f4e88059..86bf89ea 100644 --- a/tpx3App/op/bob/MediPix3.bob +++ b/tpx3App/op/bob/MediPix3.bob @@ -10,8 +10,8 @@ MediPix3Profile -

$(P)

- $(R) +

$(P=MPX3-TEST:)

+ $(R=cam1:) $(pathADCore) $(pathADet)
diff --git a/tpx3App/op/bob/MediPix3/MediPix3.bob b/tpx3App/op/bob/MediPix3/MediPix3.bob index 43ceb653..30bf2875 100644 --- a/tpx3App/op/bob/MediPix3/MediPix3.bob +++ b/tpx3App/op/bob/MediPix3/MediPix3.bob @@ -10,8 +10,8 @@ MediPix3Profile -

$(P)

- $(R) +

$(P=MPX3-TEST:)

+ $(R=cam1:) $(pathADCore) $(pathADet)
diff --git a/tpx3App/op/bob/README.md b/tpx3App/op/bob/README.md index 1358b07b..f3f9d021 100644 --- a/tpx3App/op/bob/README.md +++ b/tpx3App/op/bob/README.md @@ -15,24 +15,25 @@ bob/ tpx3/ # main.bob, TimePix3Detector, status toolbar, TPX3 acquire mpx3/ # main.bob, Mpx3Status, Mpx3* acquire/detector panels tpx4/ # placeholder README - Emulator/ # shell + tpx3/mpx3 embed panels (EMU-TEST:) - Serval/ # shared Serval process IOC panel (SERVAL-TEST:) + Emulator/ # shell + tpx3/mpx3 embeds (inherited detector P) + Serval/ # shared Serval panel (inherited detector P) ``` ## Launch -| Entry screen | Default prefix | Profile body | -|--------------|----------------|--------------| -| `TimePix3.bob` | *(from launcher)* | `profiles/tpx3/main.bob` | -| `MediPix3.bob` | *(from launcher)* | `profiles/mpx3/main.bob` | -| `MediPix3/MediPix3.bob` | *(from launcher)* | same (legacy path) | -| `Emulator/emulator.bob` | `EMU-TEST:` | shared TPX3/MPX3 emulator shell | -| `Serval/tpx3serval.bob` | `SERVAL-TEST:` | shared Serval process IOC | +| Entry screen | Direct-open macros | Profile body | +|--------------|--------------------|--------------| +| `TimePix3.bob` | `P=TPX3-TEST:`, `R=cam1:` | `profiles/tpx3/main.bob` | +| `MediPix3.bob` | `P=MPX3-TEST:`, `R=cam1:` | `profiles/mpx3/main.bob` | +| `MediPix3/MediPix3.bob` | `P=MPX3-TEST:`, `R=cam1:` | same (legacy path) | +| `Emulator/emulator.bob` | `P=TPX3-TEST:`, `R=Emulator:` | shared TPX3/MPX3 emulator shell | +| `Serval/tpx3serval.bob` | `P=TPX3-TEST:`, `R=Serval:` | shared Serval process IOC | -Set macros **`P`**, **`R`** (`cam1:`), and **`pathADCore`** at launch or edit defaults in the root launcher (`TimePix3.bob` / `MediPix3.bob`). Profile `main.bob` files inherit **`P`/`R`** from the launcher; do not hardcode beamline prefixes there. +Callers may override **`P`**, **`R`**, and **`pathADCore`** at launch. The root launchers use fallback syntax so caller values win while direct opens use the defaults shown above. Profile `main.bob` files inherit **`P`/`R`** from the launcher; do not hardcode beamline prefixes there. Mask image / PixelConfig panels use **`$(P)$(R)`** (same as `MaskStatus`); do not pass `Sys`/`Dev`/`Cam`. -Emulator opens with explicit **`P=EMU-TEST:`** / **`R=Emulator:`** (not camera `P`); override with `EMU-$(BL):` to match a beamline IOC. -Serval opens with explicit **`P=SERVAL-TEST:`** / **`R=Serval:`**; override with `SERVAL-$(BL):`. +ADTimePix3, Serval, and emulator IOCs are configured independently but use the same detector system prefix. TPX3 launch actions pass **`P=$(P=TPX3-TEST:)`**; MPX3 actions pass **`P=$(P=MPX3-TEST:)`**. The caller's `P` therefore wins, while a directly opened family screen retains a useful test fallback. Service identity remains in **`R=Serval:`** or **`R=Emulator:`**. + +The emulator shell and TPX3 embed use the TPX3 fallback when opened directly; the MPX3 embed uses the MPX3 fallback. No Serval or emulator child display defines a display-level `P`, because that would override an incoming caller macro. ## Profile contract @@ -50,6 +51,10 @@ Serval opens with explicit **`P=SERVAL-TEST:`** / **`R=Serval:`**; override with Legacy CS-Studio **`.opi`**: **`tpx3App/op/opi/`** (not updated in R1-7-2). +## Validation + +Run `python3 test/validate_bob_prefix_macros.py` from the repository root to verify every entry screen, Serval/emulator child, and launcher macro contract. + ## Site overlays Beamline-specific display forks: gitignored `*_site.bob` or local Phoebus paths — do not fork whole profiles in git. diff --git a/tpx3App/op/bob/Serval/README.md b/tpx3App/op/bob/Serval/README.md index b60fa44f..ad03bbf6 100644 --- a/tpx3App/op/bob/Serval/README.md +++ b/tpx3App/op/bob/Serval/README.md @@ -1,6 +1,6 @@ # Serval IOC Phoebus display -Shared **Serval** process control for Timepix3 / Medipix3 (not camera `cam1:` PVs). +Shared **Serval** process control for Timepix3 / Medipix3 (service identity is `R=Serval:`, not camera `R=cam1:`). | File | Role | |------|------| @@ -8,10 +8,11 @@ Shared **Serval** process control for Timepix3 / Medipix3 (not camera `cam1:` PV ## PV macros -- **Default**: `P=SERVAL-TEST:`, `R=Serval:` → e.g. `SERVAL-TEST:Serval:START` -- Matches `/epics/iocs/serval/iocBoot/ioctpx3serval/st.cmd` (`Sys=SERVAL-TEST:`) -- Beamline override: open with `P=SERVAL-$(BL):` (e.g. `SERVAL-BL7:`) and set IOC `Sys` to match -- **Do not inherit camera `P`** — every Serval `open_display` should pass `P`/`R` explicitly +- **Prefix**: `$(P=TPX3-TEST:)` inherits `P` from the calling display and uses `TPX3-TEST:` only when `P` is undefined +- **Record**: `R=Serval:` → the direct-open lab default START PV is `TPX3-TEST:Serval:START` +- The fallback matches the IOC's `TPX3_PREFIX=TPX3-TEST:` default +- For beamline use, configure the Serval IOC with the ADTimePix3 system prefix and pass that same prefix as `P`; TPX3 launchers use `$(P=TPX3-TEST:)` and MPX3 launchers use `$(P=MPX3-TEST:)` +- The child screen deliberately has no display-level `P`, so an incoming caller macro is not overridden ## Sync @@ -20,4 +21,6 @@ Source of truth: `/epics/iocs/serval/tpx3servalApp/op/bob/tpx3serval.bob` → th ## Opened from - `profiles/tpx3/Acquire/DetectorConfig.bob` (Vendor SW) +- `profiles/tpx3/TimePix3Status.bob` - `profiles/mpx3/Mpx3Status.bob` +- `profiles/mpx3/Detector/Mpx3DetectorConfig.bob` diff --git a/tpx3App/op/bob/Serval/tpx3serval.bob b/tpx3App/op/bob/Serval/tpx3serval.bob index deaa294e..3bb78fa7 100644 --- a/tpx3App/op/bob/Serval/tpx3serval.bob +++ b/tpx3App/op/bob/Serval/tpx3serval.bob @@ -2,7 +2,6 @@ tpx3Serval -

SERVAL-TEST:

Serval:
@@ -49,7 +48,7 @@ StartStopButton - $(P)$(R)START + $(P=TPX3-TEST:)$(R)START 20 40 @@ -58,7 +57,7 @@ StatusLED - $(P)$(R)STATUS + $(P=TPX3-TEST:)$(R)STATUS 160 40 30 @@ -85,7 +84,7 @@ ErrorMessageDisplay - $(P)$(R)ERROR_MSG + $(P=TPX3-TEST:)$(R)ERROR_MSG 650 40 600 @@ -103,7 +102,7 @@ Text Update - $(P)$(R)PROCESS_ID + $(P=TPX3-TEST:)$(R)PROCESS_ID 300 40 120 @@ -118,7 +117,7 @@ ReleaseVersionDisplay - $(P)$(R)RELEASE_VERSION + $(P=TPX3-TEST:)$(R)RELEASE_VERSION 300 5 80 @@ -157,7 +156,7 @@ HTTPPortSpinner - $(P)$(R)HTTP_PORT + $(P=TPX3-TEST:)$(R)HTTP_PORT 110 35 30 @@ -166,7 +165,7 @@ HTTPPortEnableButton - $(P)$(R)HTTP_PORT_ENABLE + $(P=TPX3-TEST:)$(R)HTTP_PORT_ENABLE 220 35 @@ -181,14 +180,14 @@ HTTPLogInput - $(P)$(R)HTTP_LOG + $(P=TPX3-TEST:)$(R)HTTP_LOG 1010 35 30 HTTPLogEnableButton - $(P)$(R)HTTP_LOG_ENABLE + $(P=TPX3-TEST:)$(R)HTTP_LOG_ENABLE 1060 35 @@ -196,7 +195,7 @@ Text Entry_1 - $(P)$(R)HTTP_LOG + $(P=TPX3-TEST:)$(R)HTTP_LOG 440 35 570 @@ -235,7 +234,7 @@ SPIDRNetInput - $(P)$(R)SPIDR_NET + $(P=TPX3-TEST:)$(R)SPIDR_NET 110 35 200 @@ -253,7 +252,7 @@ SPIDRNetEnableButton - $(P)$(R)SPIDR_NET_ENABLE + $(P=TPX3-TEST:)$(R)SPIDR_NET_ENABLE 320 35 @@ -268,7 +267,7 @@ TCPIPInput - $(P)$(R)TCP_IP + $(P=TPX3-TEST:)$(R)TCP_IP 510 35 200 @@ -286,7 +285,7 @@ TCPIPEnableButton - $(P)$(R)TCP_IP_ENABLE + $(P=TPX3-TEST:)$(R)TCP_IP_ENABLE 720 35 @@ -301,7 +300,7 @@ TCPPortSpinner - $(P)$(R)TCP_PORT + $(P=TPX3-TEST:)$(R)TCP_PORT 950 35 30 @@ -310,7 +309,7 @@ TCPPortEnableButton - $(P)$(R)TCP_PORT_ENABLE + $(P=TPX3-TEST:)$(R)TCP_PORT_ENABLE 1060 35 @@ -347,7 +346,7 @@ UDPReceiversSpinner - $(P)$(R)UDP_RECEIVERS + $(P=TPX3-TEST:)$(R)UDP_RECEIVERS 130 35 140 @@ -355,7 +354,7 @@ UDPReceiversEnableButton - $(P)$(R)UDP_RECEIVERS_ENABLE + $(P=TPX3-TEST:)$(R)UDP_RECEIVERS_ENABLE 320 35 @@ -369,7 +368,7 @@ FrameAssemblersSpinner - $(P)$(R)FRAME_ASSEMBLERS + $(P=TPX3-TEST:)$(R)FRAME_ASSEMBLERS 520 35 130 @@ -377,7 +376,7 @@ FrameAssemblersEnableButton - $(P)$(R)FRAME_ASSEMBLERS_ENABLE + $(P=TPX3-TEST:)$(R)FRAME_ASSEMBLERS_ENABLE 660 35 @@ -392,7 +391,7 @@ RingBufferSizeSpinner - $(P)$(R)RING_BUFFER_SIZE + $(P=TPX3-TEST:)$(R)RING_BUFFER_SIZE 900 35 140 @@ -401,7 +400,7 @@ RingBufferSizeEnableButton - $(P)$(R)RING_BUFFER_SIZE_ENABLE + $(P=TPX3-TEST:)$(R)RING_BUFFER_SIZE_ENABLE 1050 35 @@ -415,7 +414,7 @@ NetworkBufferSizeSpinner - $(P)$(R)NETWORK_BUFFER_SIZE + $(P=TPX3-TEST:)$(R)NETWORK_BUFFER_SIZE 130 75 140 @@ -424,7 +423,7 @@ NetworkBufferSizeEnableButton - $(P)$(R)NETWORK_BUFFER_SIZE_ENABLE + $(P=TPX3-TEST:)$(R)NETWORK_BUFFER_SIZE_ENABLE 320 75 @@ -438,7 +437,7 @@ FileWritersSpinner - $(P)$(R)FILE_WRITERS + $(P=TPX3-TEST:)$(R)FILE_WRITERS 520 75 130 @@ -446,7 +445,7 @@ FileWritersEnableButton - $(P)$(R)FILE_WRITERS_ENABLE + $(P=TPX3-TEST:)$(R)FILE_WRITERS_ENABLE 660 75 @@ -461,7 +460,7 @@ CorrectionHandlersSpinner - $(P)$(R)CORRECTION_HANDLERS + $(P=TPX3-TEST:)$(R)CORRECTION_HANDLERS 900 75 140 @@ -469,7 +468,7 @@ CorrectionHandlersEnableButton - $(P)$(R)CORRECTION_HANDLERS_ENABLE + $(P=TPX3-TEST:)$(R)CORRECTION_HANDLERS_ENABLE 1050 75 @@ -484,7 +483,7 @@ ProcessingHandlersSpinner - $(P)$(R)PROCESSING_HANDLERS + $(P=TPX3-TEST:)$(R)PROCESSING_HANDLERS 170 115 140 @@ -492,7 +491,7 @@ ProcessingHandlersEnableButton - $(P)$(R)PROCESSING_HANDLERS_ENABLE + $(P=TPX3-TEST:)$(R)PROCESSING_HANDLERS_ENABLE 320 115 @@ -506,7 +505,7 @@ DeviceMaskSpinner - $(P)$(R)DEVICE_MASK + $(P=TPX3-TEST:)$(R)DEVICE_MASK 520 115 130 @@ -515,7 +514,7 @@ DeviceMaskEnableButton - $(P)$(R)DEVICE_MASK_ENABLE + $(P=TPX3-TEST:)$(R)DEVICE_MASK_ENABLE 660 115 @@ -553,7 +552,7 @@ ResourcePoolSizeSpinner - $(P)$(R)RESOURCE_POOL_SIZE + $(P=TPX3-TEST:)$(R)RESOURCE_POOL_SIZE 160 35 120 @@ -563,7 +562,7 @@ ResourcePoolSizeEnableButton - $(P)$(R)RESOURCE_POOL_SIZE_ENABLE + $(P=TPX3-TEST:)$(R)RESOURCE_POOL_SIZE_ENABLE 290 35 @@ -578,7 +577,7 @@ ImagePoolSizeSpinner - $(P)$(R)IMAGE_POOL_SIZE + $(P=TPX3-TEST:)$(R)IMAGE_POOL_SIZE 520 35 120 @@ -587,7 +586,7 @@ ImagePoolSizeEnableButton - $(P)$(R)IMAGE_POOL_SIZE_ENABLE + $(P=TPX3-TEST:)$(R)IMAGE_POOL_SIZE_ENABLE 650 35 @@ -602,7 +601,7 @@ IntegrationPoolSizeSpinner - $(P)$(R)INTEGRATION_POOL_SIZE + $(P=TPX3-TEST:)$(R)INTEGRATION_POOL_SIZE 900 35 120 @@ -611,7 +610,7 @@ IntegrationPoolSizeEnableButton - $(P)$(R)INTEGRATION_POOL_SIZE_ENABLE + $(P=TPX3-TEST:)$(R)INTEGRATION_POOL_SIZE_ENABLE 1030 35 @@ -649,14 +648,14 @@ TCPDebugInput - $(P)$(R)TCP_DEBUG + $(P=TPX3-TEST:)$(R)TCP_DEBUG 1150 35 30 TCPDebugEnableButton - $(P)$(R)TCP_DEBUG_ENABLE + $(P=TPX3-TEST:)$(R)TCP_DEBUG_ENABLE 1200 35 @@ -664,7 +663,7 @@ ReleaseResourcesButton - $(P)$(R)RELEASE_RESOURCES + $(P=TPX3-TEST:)$(R)RELEASE_RESOURCES 450 5 @@ -672,7 +671,7 @@ ExperimentalButton - $(P)$(R)EXPERIMENTAL + $(P=TPX3-TEST:)$(R)EXPERIMENTAL 660 5 @@ -680,7 +679,7 @@ Text Entry_4 - $(P)$(R)TCP_DEBUG + $(P=TPX3-TEST:)$(R)TCP_DEBUG 100 35 1050 @@ -718,14 +717,14 @@ JarFileNameInput - $(P)$(R)JarFileName + $(P=TPX3-TEST:)$(R)JarFileName 1000 35 30 JarFileEnableButton - $(P)$(R)JAR_FILE_ENABLE + $(P=TPX3-TEST:)$(R)JAR_FILE_ENABLE 1060 35 @@ -740,7 +739,7 @@ JarFileRBV - $(P)$(R)JarFile_RBV + $(P=TPX3-TEST:)$(R)JarFile_RBV 380 900 30 @@ -752,7 +751,7 @@ Text Entry_2 - $(P)$(R)JarFileName + $(P=TPX3-TEST:)$(R)JarFileName 130 35 870 @@ -768,7 +767,7 @@ JavaXmxEntry - $(P)$(R)JAVA_XMX + $(P=TPX3-TEST:)$(R)JAVA_XMX 140 73 80 @@ -777,7 +776,7 @@ JavaXmxEnableButton - $(P)$(R)JAVA_XMX_ENABLE + $(P=TPX3-TEST:)$(R)JAVA_XMX_ENABLE 240 73 @@ -808,7 +807,7 @@ ErrorMessageDisplay_1 - $(P)$(R)COMMAND_LINE + $(P=TPX3-TEST:)$(R)COMMAND_LINE 20 40 1300 diff --git a/tpx3App/op/bob/TimePix3.bob b/tpx3App/op/bob/TimePix3.bob index e7029eb1..fff9cdab 100644 --- a/tpx3App/op/bob/TimePix3.bob +++ b/tpx3App/op/bob/TimePix3.bob @@ -10,8 +10,8 @@ TimePix3Profile -

$(P)

- $(R) +

$(P=TPX3-TEST:)

+ $(R=cam1:) $(pathADCore) $(pathADet)
diff --git a/tpx3App/op/bob/profiles/mpx3/Detector/Mpx3DetectorConfig.bob b/tpx3App/op/bob/profiles/mpx3/Detector/Mpx3DetectorConfig.bob index 04e68a7d..e6721a62 100644 --- a/tpx3App/op/bob/profiles/mpx3/Detector/Mpx3DetectorConfig.bob +++ b/tpx3App/op/bob/profiles/mpx3/Detector/Mpx3DetectorConfig.bob @@ -1014,7 +1014,7 @@ Emulator ../../../Emulator/emulator.bob -

EMU-TEST:

+

$(P=MPX3-TEST:)

Emulator:
tab @@ -1029,7 +1029,7 @@ - EMU-TEST:Emulator: — not camera P + Inherited detector P (MPX3-TEST: fallback), R=Emulator: false
@@ -1039,7 +1039,7 @@ Serval ../../../Serval/tpx3serval.bob -

SERVAL-TEST:

+

$(P=MPX3-TEST:)

Serval:
tab @@ -1054,7 +1054,7 @@ - SERVAL-TEST:Serval: — not camera P + Inherited detector P (MPX3-TEST: fallback), R=Serval: false
diff --git a/tpx3App/op/bob/profiles/mpx3/Mpx3Status.bob b/tpx3App/op/bob/profiles/mpx3/Mpx3Status.bob index 052d2a81..3d03f626 100644 --- a/tpx3App/op/bob/profiles/mpx3/Mpx3Status.bob +++ b/tpx3App/op/bob/profiles/mpx3/Mpx3Status.bob @@ -192,7 +192,7 @@ Medipix3 emulator ../../Emulator/emulator.bob -

EMU-TEST:

+

$(P=MPX3-TEST:)

Emulator:
tab @@ -215,7 +215,7 @@ Serval IOC control ../../Serval/tpx3serval.bob -

SERVAL-TEST:

+

$(P=MPX3-TEST:)

Serval:
tab diff --git a/tpx3App/op/bob/profiles/tpx3/Acquire/DetectorConfig.bob b/tpx3App/op/bob/profiles/tpx3/Acquire/DetectorConfig.bob index d430548e..2bf2c3c6 100644 --- a/tpx3App/op/bob/profiles/tpx3/Acquire/DetectorConfig.bob +++ b/tpx3App/op/bob/profiles/tpx3/Acquire/DetectorConfig.bob @@ -1704,7 +1704,7 @@ $(pv_value) Serval ../../../Serval/tpx3serval.bob -

SERVAL-TEST:

+

$(P=TPX3-TEST:)

Serval:
tab @@ -1729,7 +1729,7 @@ $(pv_value) Emulator ../../../Emulator/emulator.bob -

EMU-TEST:

+

$(P=TPX3-TEST:)

Emulator:
tab diff --git a/tpx3App/op/bob/profiles/tpx3/TimePix3Status.bob b/tpx3App/op/bob/profiles/tpx3/TimePix3Status.bob index 154069c0..45e2ef0e 100644 --- a/tpx3App/op/bob/profiles/tpx3/TimePix3Status.bob +++ b/tpx3App/op/bob/profiles/tpx3/TimePix3Status.bob @@ -153,7 +153,7 @@ Timepix3 emulator ../../Emulator/emulator.bob -

EMU-TEST:

+

$(P=TPX3-TEST:)

Emulator:
tab @@ -218,7 +218,7 @@ Serval IOC control ../../Serval/tpx3serval.bob -

SERVAL-TEST:

+

$(P=TPX3-TEST:)

Serval:
tab