From 33ed9619c515cefa63ef0f16e6e703f5bc1aaa88 Mon Sep 17 00:00:00 2001 From: "K. Gofron" Date: Fri, 24 Jul 2026 19:59:55 -0400 Subject: [PATCH 1/2] feat: add optional NDStatsProfiles for Phoebus row/column plots Add NDStatsProfiles.template and EXAMPLE_stats_profiles.cmd so IOCs can load pixel/scaled axis waveforms under _(P)_(R) after commonPlugins. Document usage in NDPluginStats/common_plugins and note in RELEASE.md. --- ADApp/Db/NDStatsProfiles.template | 88 ++++++++++++++++++++++++++++++ RELEASE.md | 5 ++ docs/ADCore/NDPluginStats.rst | 25 +++++++++ docs/ADCore/common_plugins.rst | 16 ++++++ iocBoot/EXAMPLE_commonPlugins.cmd | 4 ++ iocBoot/EXAMPLE_stats_profiles.cmd | 22 ++++++++ 6 files changed, 160 insertions(+) create mode 100644 ADApp/Db/NDStatsProfiles.template create mode 100644 iocBoot/EXAMPLE_stats_profiles.cmd diff --git a/ADApp/Db/NDStatsProfiles.template b/ADApp/Db/NDStatsProfiles.template new file mode 100644 index 000000000..a32bd7d3b --- /dev/null +++ b/ADApp/Db/NDStatsProfiles.template @@ -0,0 +1,88 @@ +# NDStatsProfiles.template — pixel axis waveforms and profile enable for Phoebus xyplots +# Load via EXAMPLE_stats_profiles.cmd after commonPlugins.cmd. +# Substitutions: P, R (e.g. Stats1:), XNELM, YNELM (match XSIZE/YSIZE in NDStats.template) +# +# All records use $(P)$(R) so multiple cameras / Stats instances under the same PREFIX +# do not collide (unlike some facility DBs that used $(P)Cal: only). + +record(seq, "$(P)$(R)StatsProfInit_"){ + field(DESC, "Enable NDStats profiles for Phoebus") + field(DOL1, "1") + field(LNK1, "$(P)$(R)ComputeStatistics PP") + field(DOL2, "1") + field(LNK2, "$(P)$(R)ComputeCentroid PP") + field(DOL3, "1") + field(LNK3, "$(P)$(R)ComputeProfiles PP") + field(DOL4, "1") + field(LNK4, "$(P)$(R)EnableCallbacks PP") +} + +record(bo, "$(P)$(R)Cal:PlotCalAxisC"){ + field(DESC, "Profile plot axis: 0=pixel, 1=scaled") + field(PINI, "YES") + field(VAL, "0") + field(ZNAM, "Pixel") + field(ONAM, "Scaled") +} + +record(ao, "$(P)$(R)Cal:xScalingM"){ + field(DESC, "X axis scale factor (egu/pixel)") + field(PINI, "YES") + field(PREC, "6") + field(VAL, "1") +} + +record(ao, "$(P)$(R)Cal:yScalingM"){ + field(DESC, "Y axis scale factor (egu/pixel)") + field(PINI, "YES") + field(PREC, "6") + field(VAL, "1") +} + +record(acalcout, "$(P)$(R)Cal:xPixelAxisM"){ + field(DESC, "X pixel index 0..N-1") + field(PINI, "YES") + field(NELM, "$(XNELM)") + field(CALC, "IX") +} + +record(acalcout, "$(P)$(R)Cal:yPixelAxisM"){ + field(DESC, "Y pixel index 0..N-1") + field(PINI, "YES") + field(NELM, "$(YNELM)") + field(CALC, "IX") +} + +record(acalcout, "$(P)$(R)Cal:xCalAxisM"){ + field(DESC, "Scaled X axis") + field(NELM, "$(XNELM)") + field(INAA, "$(P)$(R)Cal:xPixelAxisM.AVAL CP") + field(INPB, "$(P)$(R)Cal:xScalingM CP") + field(CALC, "AA*B") +} + +record(acalcout, "$(P)$(R)Cal:yCalAxisM"){ + field(DESC, "Scaled Y axis") + field(NELM, "$(YNELM)") + field(INAA, "$(P)$(R)Cal:yPixelAxisM.AVAL CP") + field(INPB, "$(P)$(R)Cal:yScalingM CP") + field(CALC, "AA*B") +} + +record(acalcout, "$(P)$(R)Cal:xSelAxisM"){ + field(DESC, "X axis for horizontal profile plot") + field(NELM, "$(XNELM)") + field(INAA, "$(P)$(R)Cal:xPixelAxisM.AVAL") + field(INBB, "$(P)$(R)Cal:xCalAxisM.AVAL CP") + field(INPC, "$(P)$(R)Cal:PlotCalAxisC CP") + field(CALC, "C==0?AA:BB") +} + +record(acalcout, "$(P)$(R)Cal:ySelAxisM"){ + field(DESC, "Y axis for vertical profile plot") + field(NELM, "$(YNELM)") + field(INAA, "$(P)$(R)Cal:yPixelAxisM.AVAL") + field(INBB, "$(P)$(R)Cal:yCalAxisM.AVAL CP") + field(INPC, "$(P)$(R)Cal:PlotCalAxisC CP") + field(CALC, "C==0?AA:BB") +} diff --git a/RELEASE.md b/RELEASE.md index 50968b248..7dc3cef32 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -49,6 +49,11 @@ files respectively, in the configure/ directory of the appropriate release of th ### Database template files * Fixed record names in NDROIStat.template to avoid record name duplication. * Added optional default transform type macro (DFLTTRANSTYPE) in NDTransform.template. + * Added optional ``NDStatsProfiles.template`` and ``EXAMPLE_stats_profiles.cmd`` + for Phoebus NDStats row/column profile axis waveforms (requires synApps calc). + Records use ``$(P)$(R)`` so multiple cameras under one PREFIX remain unique. + Include ``stats_profiles.cmd`` after ``commonPlugins.cmd``; see NDPluginStats + and commonPlugins documentation. ### ADTop.adl * Added ADHamammatsuDCAM and BlackflyS PGE 23S6C. diff --git a/docs/ADCore/NDPluginStats.rst b/docs/ADCore/NDPluginStats.rst index 04a2a15c6..416a25844 100644 --- a/docs/ADCore/NDPluginStats.rst +++ b/docs/ADCore/NDPluginStats.rst @@ -522,7 +522,32 @@ are just a single name, for example ``NDPluginStatsComputeStatistics``. - $(P)$(R)HistogramX_RBV - waveform +Phoebus profile axis helpers +---------------------------- +``NDPluginStats`` already publishes profile waveforms (e.g. +``$(P)$(R)ProfileAverageX_RBV`` / ``ProfileAverageY_RBV``). Phoebus XY plots +also need matching X/Y axis arrays. Optional database support is provided in +``NDStatsProfiles.template``, loaded by copying ``EXAMPLE_stats_profiles.cmd`` +to ``stats_profiles.cmd`` and including it after ``commonPlugins.cmd`` (see +:doc:`common_plugins`). + +That template requires synApps **calc** (``acalcout``). All helper records use +``$(P)$(R)`` (default ``R=Stats1:``) so multiple detectors that share a PREFIX +remain unique, for example: + +* ``$(P)$(R)StatsProfInit_`` — sequence to enable statistics, centroid, + profiles, and callbacks +* ``$(P)$(R)Cal:xSelAxisM`` / ``Cal:ySelAxisM`` — selected pixel or scaled + axis waveforms for horizontal / vertical profile plots + +After ``iocInit``, process ``StatsProfInit_`` once (or rely on autosave of +``ComputeProfiles``):: + + dbpf "$(PREFIX)Stats1:StatsProfInit_.PROC" 1 + +Facility screens that previously bound axes to ``$(P)Cal:...`` (no ``Stats1:``) +should be updated to ``$(P)$(R)Cal:...`` when adopting this ADCore template. If the values of CentroidThreshold, CursorX, or CursorY are changed then the centroid and profile calculations are performed again immediately on diff --git a/docs/ADCore/common_plugins.rst b/docs/ADCore/common_plugins.rst index 6da5c312c..ea5bafc13 100644 --- a/docs/ADCore/common_plugins.rst +++ b/docs/ADCore/common_plugins.rst @@ -18,3 +18,19 @@ at a glance, with links to bring up the detailed screen for each. .. image:: commonPlugins.png :align: center +Optional Phoebus profile axes +----------------------------- + +``EXAMPLE_commonPlugins.cmd`` loads ``NDPluginStats`` instances (e.g. ``Stats1:``) +that can compute row and column profiles when ``ComputeProfiles`` is enabled. +For Phoebus image viewers that plot those profiles against a pixel (or scaled) +axis, copy ``EXAMPLE_stats_profiles.cmd`` to ``stats_profiles.cmd`` and include +it **after** ``commonPlugins.cmd``:: + + < $(ADCORE)/iocBoot/commonPlugins.cmd + < $(ADCORE)/iocBoot/stats_profiles.cmd + +That file loads ``NDStatsProfiles.template``, which provides axis waveforms and +a ``StatsProfInit_`` sequence record. It requires the synApps **calc** module +(``acalcout``). See :doc:`NDPluginStats` for details. + diff --git a/iocBoot/EXAMPLE_commonPlugins.cmd b/iocBoot/EXAMPLE_commonPlugins.cmd index 56383cba1..10b7aa446 100644 --- a/iocBoot/EXAMPLE_commonPlugins.cmd +++ b/iocBoot/EXAMPLE_commonPlugins.cmd @@ -108,6 +108,10 @@ dbLoadRecords("NDStats.template", "P=$(PREFIX),R=Stats5:, PORT=STATS5,ADDR= NDTimeSeriesConfigure("STATS5_TS", $(QSIZE), 0, "STATS5", 1, 23) dbLoadRecords("$(ADCORE)/db/NDTimeSeries.template", "P=$(PREFIX),R=Stats5:TS:, PORT=STATS5_TS,ADDR=0,TIMEOUT=1,NDARRAY_PORT=STATS5,NDARRAY_ADDR=1,NCHANS=$(NCHANS),ENABLED=1") +# Optional: Phoebus NDStats row/column profile axis helpers (requires synApps calc). +# Copy EXAMPLE_stats_profiles.cmd to stats_profiles.cmd and include AFTER this file: +# < $(ADCORE)/iocBoot/stats_profiles.cmd + # Create a transform plugin NDTransformConfigure("TRANS1", $(QSIZE), 0, "$(PORT)", 0, 0, 0, 0, 0, $(MAX_THREADS=5)) dbLoadRecords("NDTransform.template", "P=$(PREFIX),R=Trans1:, PORT=TRANS1,ADDR=0,TIMEOUT=1,NDARRAY_PORT=$(PORT)") diff --git a/iocBoot/EXAMPLE_stats_profiles.cmd b/iocBoot/EXAMPLE_stats_profiles.cmd new file mode 100644 index 000000000..df819fb91 --- /dev/null +++ b/iocBoot/EXAMPLE_stats_profiles.cmd @@ -0,0 +1,22 @@ +# EXAMPLE_stats_profiles.cmd +# Copy to stats_profiles.cmd and include from st.cmd AFTER commonPlugins.cmd, e.g.: +# < $(ADCORE)/iocBoot/commonPlugins.cmd +# < $(ADCORE)/iocBoot/stats_profiles.cmd +# +# Requires: synApps calc (acalcout). Uses PREFIX, XSIZE, YSIZE from the IOC. +# Loads NDStatsProfiles.template: pixel/scaled axis waveforms + StatsProfInit_ +# to enable ComputeProfiles / related flags for Phoebus XY plots. +# +# Records are named $(P)$(R)... (default R=Stats1:) so multiple cameras that share +# the same PREFIX remain unique. Example PVs with PREFIX=MPX3-TEST: : +# MPX3-TEST:Stats1:StatsProfInit_ +# MPX3-TEST:Stats1:Cal:xSelAxisM / Cal:ySelAxisM +# Phoebus plot axes should use $(P)$(R)Cal:xSelAxisM.AVAL (and y), with R matching +# STATS_PROF_R / ProfileStats (typically Stats1:). + +epicsEnvSet("STATS_PROF_R", "Stats1:") + +dbLoadRecords("$(ADCORE)/db/NDStatsProfiles.template", "P=$(PREFIX),R=$(STATS_PROF_R),XNELM=$(XSIZE),YNELM=$(YSIZE)") + +# After iocInit (or via autosave of ComputeProfiles): +# dbpf "$(PREFIX)$(STATS_PROF_R)StatsProfInit_.PROC" 1 From dacfd039404b865977dfe2789ca5ca187cdedf20 Mon Sep 17 00:00:00 2001 From: "K. Gofron" Date: Wed, 29 Jul 2026 13:02:36 -0400 Subject: [PATCH 2/2] chore(iocBoot): ignore local stats_profiles.cmd like commonPlugins.cmd --- iocBoot/.gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/iocBoot/.gitignore b/iocBoot/.gitignore index 41c7800f7..aa219e73b 100644 --- a/iocBoot/.gitignore +++ b/iocBoot/.gitignore @@ -1,2 +1,4 @@ commonPlugins.cmd commonPlugin_settings.req +stats_profiles.cmd +