From 18bba34bce270234693e9afd77daedc4c4be203e Mon Sep 17 00:00:00 2001 From: Mike Langmayr <1809691+mikelangmayr@users.noreply.github.com> Date: Wed, 26 Aug 2026 16:27:32 -0700 Subject: [PATCH 1/7] Wire frame_outputs generically for every instrument via configure_frame_outputs --- camerad/camera_interface.cpp | 16 ++++++++++++++++ camerad/camera_interface.h | 7 +++++++ camerad/camerad.cpp | 1 + 3 files changed, 24 insertions(+) diff --git a/camerad/camera_interface.cpp b/camerad/camera_interface.cpp index f8dfdb3..b70df04 100644 --- a/camerad/camera_interface.cpp +++ b/camerad/camera_interface.cpp @@ -99,4 +99,20 @@ namespace Camera { return NO_ERROR; } /***** Camera::Interface::datacube *********************************************/ + + + /***** Camera::Interface::configure_frame_outputs *******************************/ + /** + * @brief build frame_outputs (SHM/FITS) from instrument defaults + config file + * @details Called unconditionally from camerad.cpp after configure_instrument(), + * so no derived override can silently skip wiring frame_outputs. + * + */ + void Interface::configure_frame_outputs() { + Camera::FrameOutputsConfig fo_cfg; + this->frame_output_defaults(fo_cfg); + Camera::apply_config_overrides(fo_cfg, this->configfile); + this->frame_outputs = Camera::make_frame_outputs(fo_cfg); + } + /***** Camera::Interface::configure_frame_outputs *******************************/ } diff --git a/camerad/camera_interface.h b/camerad/camera_interface.h index d08074d..db3681f 100644 --- a/camerad/camera_interface.h +++ b/camerad/camera_interface.h @@ -13,6 +13,7 @@ #include "camerad_commands.h" #include "exposure_modes.h" #include "frame_output.h" +#include "frame_output_factory.h" #include #include @@ -84,6 +85,7 @@ namespace Camera { void disconnect_controller(); long key(std::string args, std::string &retstring); long datacube(std::string args, std::string &retstring); + void configure_frame_outputs(); bool is_exposuremode_set() { return ( this->exposuremode && !this->exposuremode->get_type().empty() ); } void set_abortstate() { this->abortstate.store(true, std::memory_order_seq_cst); } @@ -95,6 +97,11 @@ namespace Camera { // virtual void configure_interface() = 0; virtual void configure_instrument() { } + + // Instrument-specific defaults (e.g. shm_segment_name, shm_max_frame_bytes) + // applied before configure_frame_outputs() reads the config file, so a + // .cfg entry still overrides these. No-op unless an instrument overrides it. + virtual void frame_output_defaults(FrameOutputsConfig&) { } virtual long abort( std::string args, std::string &retstring ) = 0; virtual long autodir( std::string args, std::string &retstring ) = 0; virtual long basename( std::string args, std::string &retstring ) = 0; diff --git a/camerad/camerad.cpp b/camerad/camerad.cpp index 02a173c..49d9746 100644 --- a/camerad/camerad.cpp +++ b/camerad/camerad.cpp @@ -48,6 +48,7 @@ int main( int argc, char** argv ) { camerad.interface->configure_controller(); camerad.interface->configure_interface(); camerad.interface->configure_instrument(); + camerad.interface->configure_frame_outputs(); } catch (const std::exception &e) { logwrite(function, "ERROR configuring system: "+std::string(e.what())); From 273b3d10a89c78dadbc5add9b7720c9d6a8cb740 Mon Sep 17 00:00:00 2001 From: Mike Langmayr <1809691+mikelangmayr@users.noreply.github.com> Date: Wed, 26 Aug 2026 17:22:42 -0700 Subject: [PATCH 2/7] Add SHM_MAX_FRAME_BYTES config key with a safe default --- camerad/camera_interface.cpp | 3 +-- camerad/camera_interface.h | 5 ----- utils/frame_output_factory.cpp | 1 + utils/frame_output_factory.h | 8 +++++++- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/camerad/camera_interface.cpp b/camerad/camera_interface.cpp index b70df04..9e72d4f 100644 --- a/camerad/camera_interface.cpp +++ b/camerad/camera_interface.cpp @@ -103,14 +103,13 @@ namespace Camera { /***** Camera::Interface::configure_frame_outputs *******************************/ /** - * @brief build frame_outputs (SHM/FITS) from instrument defaults + config file + * @brief build frame_outputs (SHM/FITS) from the config file * @details Called unconditionally from camerad.cpp after configure_instrument(), * so no derived override can silently skip wiring frame_outputs. * */ void Interface::configure_frame_outputs() { Camera::FrameOutputsConfig fo_cfg; - this->frame_output_defaults(fo_cfg); Camera::apply_config_overrides(fo_cfg, this->configfile); this->frame_outputs = Camera::make_frame_outputs(fo_cfg); } diff --git a/camerad/camera_interface.h b/camerad/camera_interface.h index db3681f..a9c52cc 100644 --- a/camerad/camera_interface.h +++ b/camerad/camera_interface.h @@ -97,11 +97,6 @@ namespace Camera { // virtual void configure_interface() = 0; virtual void configure_instrument() { } - - // Instrument-specific defaults (e.g. shm_segment_name, shm_max_frame_bytes) - // applied before configure_frame_outputs() reads the config file, so a - // .cfg entry still overrides these. No-op unless an instrument overrides it. - virtual void frame_output_defaults(FrameOutputsConfig&) { } virtual long abort( std::string args, std::string &retstring ) = 0; virtual long autodir( std::string args, std::string &retstring ) = 0; virtual long basename( std::string args, std::string &retstring ) = 0; diff --git a/utils/frame_output_factory.cpp b/utils/frame_output_factory.cpp index a943f1a..01c2c9d 100644 --- a/utils/frame_output_factory.cpp +++ b/utils/frame_output_factory.cpp @@ -30,6 +30,7 @@ namespace Camera { try { if (key == "SHM_ENABLED") out.shm_enabled = parse_bool(val); else if (key == "SHM_SEGMENT_NAME") out.shm_segment_name = val; + else if (key == "SHM_MAX_FRAME_BYTES") out.shm_max_frame_bytes = static_cast(std::stoull(val)); else if (key == "SHM_RING_BUFFER_SIZE") out.shm_ring_buffer_size = static_cast(std::stoul(val)); else if (key == "SHM_DIR") out.shm_dir = val; else if (key == "FITS_ENABLED") out.fits_enabled = parse_bool(val); diff --git a/utils/frame_output_factory.h b/utils/frame_output_factory.h index 99b770f..f259ff4 100644 --- a/utils/frame_output_factory.h +++ b/utils/frame_output_factory.h @@ -20,10 +20,16 @@ namespace Camera { + // Safe fallback SHM frame-size ceiling when neither a .cfg file nor an + // instrument specifies one: comfortably covers any detector geometry + // realistic for this codebase (e.g. HISPEC's 2048x2048x4B ~16 MiB) without + // being an arbitrarily large "accept anything" bound. + constexpr size_t DEFAULT_SHM_MAX_FRAME_BYTES = 4096ULL * 4096ULL * 4ULL; // 64 MiB + struct FrameOutputsConfig { bool shm_enabled{false}; std::string shm_segment_name{"camera"}; - size_t shm_max_frame_bytes{0}; // required > 0 when shm_enabled + size_t shm_max_frame_bytes{DEFAULT_SHM_MAX_FRAME_BYTES}; uint32_t shm_ring_buffer_size{4}; // depth of ImageStreamIO's internal history ring buffer std::string shm_dir{}; // ImageStreamIO base directory; empty uses its own default resolution From a90002d36d0ec7633f21b236eb6f3a5be02bd3bf Mon Sep 17 00:00:00 2001 From: Mike Langmayr <1809691+mikelangmayr@users.noreply.github.com> Date: Wed, 26 Aug 2026 17:22:54 -0700 Subject: [PATCH 3/7] Rename Config to config, move per-instrument cfgs to their own repos --- .github/workflows/emulator-integration.yml | 26 ++++++------ Config/cameraserver.cfg | 16 -------- Config/hispec_shm_test/hispec_shm_test.cfg | 30 -------------- Config/hispecatc.cfg | 34 ---------------- README.md | 5 ++- {Config => config/astrocam}/astrocam.cfg | 0 .../cryoscope-test_4224x2048_19.fits.gz | Bin {Config => config}/cryoscope/cryoscope.acf | 0 {Config => config}/cryoscope/cryoscope.cfg | 0 {Config => config}/demo/demo.acf | 0 {Config => config}/demo/demo.cfg | 0 {Config => config}/demo/demo.system | 0 .../frame_outputs_test/frame_outputs_test.cfg | 38 ++++++++++++++++++ .../frame_outputs_test.system | 0 14 files changed, 55 insertions(+), 94 deletions(-) delete mode 100644 Config/cameraserver.cfg delete mode 100644 Config/hispec_shm_test/hispec_shm_test.cfg delete mode 100644 Config/hispecatc.cfg rename {Config => config/astrocam}/astrocam.cfg (100%) rename {Config => config}/cryoscope/cryoscope-test_4224x2048_19.fits.gz (100%) rename {Config => config}/cryoscope/cryoscope.acf (100%) rename {Config => config}/cryoscope/cryoscope.cfg (100%) rename {Config => config}/demo/demo.acf (100%) rename {Config => config}/demo/demo.cfg (100%) rename {Config => config}/demo/demo.system (100%) create mode 100644 config/frame_outputs_test/frame_outputs_test.cfg rename Config/hispec_shm_test/hispec_shm_test.system => config/frame_outputs_test/frame_outputs_test.system (100%) diff --git a/.github/workflows/emulator-integration.yml b/.github/workflows/emulator-integration.yml index e52fe11..0d9394e 100644 --- a/.github/workflows/emulator-integration.yml +++ b/.github/workflows/emulator-integration.yml @@ -7,7 +7,7 @@ on: branches: [ "main" ] jobs: - hispec-shm-emulator-test: + frame-outputs-emulator-test: runs-on: ubuntu-latest timeout-minutes: 10 @@ -52,13 +52,13 @@ jobs: -DENABLE_SHM_OUTPUT=ON -DImageStreamIO_DIR=/usr/local/lib/cmake .. make camerad emulator socksend shm_reader -j$(nproc) - - name: HISPEC tracking camera SHM test + - name: SHM + FITS frame outputs test run: | - mkdir -p /tmp/ci_milk_shm + mkdir -p /tmp/ci_milk_shm /tmp/ci_fits_test - bin/emulator Config/hispec_shm_test/hispec_shm_test.cfg -i generic & + bin/emulator config/frame_outputs_test/frame_outputs_test.cfg -i generic & sleep 2 - bin/camerad --foreground --config Config/hispec_shm_test/hispec_shm_test.cfg & + bin/camerad --foreground --config config/frame_outputs_test/frame_outputs_test.cfg & sleep 3 send() { bin/socksend -p 3131 -t 60 "$1"; } @@ -70,10 +70,12 @@ jobs: echo "expose -> $resp" echo "$resp" | grep -q "DONE" || exit 1 - # DONE only means camerad accepted the command, not that SharedMemoryWriter - # succeeded -- shm_reader's exit code is the real pass/fail signal + # DONE only means camerad accepted the command, not that the writers + # succeeded -- shm_reader's exit code and the FITS file's existence + # are the real pass/fail signal sleep 1 - bin/shm_reader ci_hispec_shm /tmp/ci_milk_shm + bin/shm_reader ci_frame_outputs_shm /tmp/ci_milk_shm + ls /tmp/ci_fits_test/ci_frame_outputs_*.fits pkill -f 'bin/camerad' || true pkill -f 'bin/emulator' || true @@ -114,9 +116,9 @@ jobs: - name: CryoScope synthetic test run: | - bin/emulator Config/cryoscope/cryoscope.cfg -i generic & + bin/emulator config/cryoscope/cryoscope.cfg -i generic & sleep 2 - bin/camerad --foreground --config Config/cryoscope/cryoscope.cfg & + bin/camerad --foreground --config config/cryoscope/cryoscope.cfg & sleep 3 send() { bin/socksend -p 3031 -t 60 "$1"; } @@ -135,8 +137,8 @@ jobs: - name: CryoScope FITS playback test run: | - cp Config/cryoscope/cryoscope.cfg /tmp/cryoscope_fits_test.cfg - echo "EMULATOR_DATADIR=Config/cryoscope" >> /tmp/cryoscope_fits_test.cfg + cp config/cryoscope/cryoscope.cfg /tmp/cryoscope_fits_test.cfg + echo "EMULATOR_DATADIR=config/cryoscope" >> /tmp/cryoscope_fits_test.cfg bin/emulator /tmp/cryoscope_fits_test.cfg -i generic & sleep 2 diff --git a/Config/cameraserver.cfg b/Config/cameraserver.cfg deleted file mode 100644 index a2839ef..0000000 --- a/Config/cameraserver.cfg +++ /dev/null @@ -1,16 +0,0 @@ -# configuration file for camera-server -# -ARCHON_IP=192.168.1.2 -ARCHON_PORT=4242 -DEFAULT_FIRMWARE=/home/user/Software/acf/kpf-getimage-slot5.acf -EXPOSE_PARAM=Expose # Archon parameter which triggers exposure -#HEATER_TARGET_MIN=-150 # heater target lower limit (C) -#HEATER_TARGET_MAX=50 # heater target upper limit (C) -IMDIR=/tmp # base directory to save images -BASENAME=image # base image filename -NBPORT=3030 # server non-blocking port -BLKPORT=3031 # server blocking port -#ASYNCGROUP=239.1.1.234 # asynchronous broadcast group (or "none") -#ASYNCPORT=1234 # asynchronous message port (if ASYNCGROUP defined) -ASYNCGROUP=none -LOGPATH=/home/user/Logs/ # fully qualified path to save log files diff --git a/Config/hispec_shm_test/hispec_shm_test.cfg b/Config/hispec_shm_test/hispec_shm_test.cfg deleted file mode 100644 index 889cb24..0000000 --- a/Config/hispec_shm_test/hispec_shm_test.cfg +++ /dev/null @@ -1,30 +0,0 @@ -# hispec_tracking_camera configuration for camerad + emulator SHM testing (CI) - -DAEMON=no -IMDIR=/tmp -LOGPATH=/tmp -BASENAME=hispecshmtest -DIRMODE=0077 -TM_ZONE_LOG=local -TM_ZONE=UTC -TZ_ENV=PST8PDT,M3.2.0/2,M11.1.0/2 - -NBPORT=3130 -BLKPORT=3131 -EMULATOR_PORT=3132 -EMULATOR_SYSTEM=Config/hispec_shm_test/hispec_shm_test.system - -ASYNCGROUP=none - -ARCHON_IP=localhost -ARCHON_PORT=3132 -DEFAULT_FIRMWARE=camerad/Instruments/hispec_tracking_camera/config/hispecatc.acf -EXPOSE_PARAM=Expose -ABORT_PARAM=abort -READOUT_TIME=5000 -WRITE_TAPINFO_TO_FITS=no - -SHM_ENABLED=yes -SHM_SEGMENT_NAME=ci_hispec_shm -SHM_RING_BUFFER_SIZE=2 -SHM_DIR=/tmp/ci_milk_shm diff --git a/Config/hispecatc.cfg b/Config/hispecatc.cfg deleted file mode 100644 index 7d42914..0000000 --- a/Config/hispecatc.cfg +++ /dev/null @@ -1,34 +0,0 @@ -# configuration file for camera-server -# -ARCHON_IP=10.0.0.2 -ARCHON_PORT=4242 -DEFAULT_FIRMWARE=/home/hsdev/camera-dev/elijahab/wdlfiles/src/hispecatc/hispecatc.acf -EXPOSE_PARAM=Expose # Archon parameter which triggers exposure -ABORT_PARAM=abort # Archon parameter to trigger an abort -READOUT_TIME=50000 # Timeout waiting for new frame (ms) -WRITE_TAPINFO_TO_FITS=yes # Tapinfo (gain, offset) be written to FITS headers {yes|no} - -IMDIR=/home/hsdev/camera-dev/freedom/images # base directory to save images -BASENAME=image # base image filename - -# --- Asynchronous FITS writer (frame_output_factory) --- -FITS_ENABLED=yes # enable FITS writer output {yes|no} -FITS_OUTPUT_DIR=/home/hsdev/camera-dev/freedom/images # base directory for FITS files -FITS_AUTODIR=yes # write into a YYYYMMDD subdir of FITS_OUTPUT_DIR {yes|no} -FITS_BASENAME=image # base filename for FITS files -FITS_WRITE_INTERVAL_MS=0 # 0 = write a FITS file for every exposure (no cadence gating) - -DIRMODE=0077 -TM_ZONE_LOG=local # time zone for log entries only, can be "UTC" or "local" -TM_ZONE=UTC # time zone for everything else, can be "UTC" or "local" -TZ_ENV=PST8PDT,M3.2.0/2,M11.1.0/2 # TZ ENV variable to use when TM_ZONE_* is "local" (follows POSIX standard) -AUTODIR=no -LONGERROR=true - -NBPORT=3030 # server non-blocking port -BLKPORT=3031 # server blocking port -ASYNCGROUP=239.1.1.234 # asynchronous broadcast group (or "none") -ASYNCPORT=1234 # asynchronous message port (if ASYNCGROUP defined) -#ASYNCGROUP=none -LOGPATH=/home/hsdev/camera-dev/freedom/logs # fully qualified path to save log files -#LONGEXPOSURE=True # exptimes in seconds (not ms) diff --git a/README.md b/README.md index 24b383f..0feaaad 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ If you encounter any problems or have questions about this project, please open $ ../bin/camerad -d ``` - *Replace `` with an appropriate configuration file. See the example `.cfg` files in the `Config` and `Config/demo` directories.* + *Replace `` with an appropriate configuration file. See the example `.cfg` files in the `config` directory (per-instrument deployment configs live in each instrument's own repo under its `config/` directory; `config/demo` here is a generic example).* 6. **(Optional) Run the Archon Emulator:** @@ -95,7 +95,7 @@ If you encounter any problems or have questions about this project, please open ## Frame Outputs -Instruments that opt in publish each acquired frame to one or more outputs, configured via `.cfg` file keys read by `Camera::apply_config_overrides()`. Both outputs are independent; either or both can be enabled. +Every instrument publishes each acquired frame to one or more outputs, configured entirely via `.cfg` file keys (`Camera::Interface::configure_frame_outputs()` builds them from `Camera::apply_config_overrides()`, called once at startup for every instrument, not just HISPEC). Both outputs are independent; either, both, or neither can be enabled per instrument. ### FITS @@ -118,6 +118,7 @@ Publishes each frame as an [ImageStreamIO](https://github.com/milk-org/ImageStre |-------------------------|------------|--------------------------------------------------------------------------------------------| | `SHM_ENABLED` | `no` | Enable the shared-memory writer | | `SHM_SEGMENT_NAME` | `camera` | ImageStreamIO stream name | +| `SHM_MAX_FRAME_BYTES` | `67108864` (64 MiB) | Validation ceiling for a frame's byte size; a frame larger than this is rejected rather than written. The default comfortably covers any detector geometry realistic for this codebase; set explicitly for a tighter bound. | | `SHM_RING_BUFFER_SIZE` | `4` | Depth of ImageStreamIO's internal history ring buffer (`CBsize`); the live frame a real-time reader sees is separate from this | | `SHM_DIR` | (unset) | Base directory ImageStreamIO writes into. If unset, ImageStreamIO falls back to its own default resolution (`MILK_SHM_DIR` env var, then `/milk/shm`). If set, it must already exist and be writable. | diff --git a/Config/astrocam.cfg b/config/astrocam/astrocam.cfg similarity index 100% rename from Config/astrocam.cfg rename to config/astrocam/astrocam.cfg diff --git a/Config/cryoscope/cryoscope-test_4224x2048_19.fits.gz b/config/cryoscope/cryoscope-test_4224x2048_19.fits.gz similarity index 100% rename from Config/cryoscope/cryoscope-test_4224x2048_19.fits.gz rename to config/cryoscope/cryoscope-test_4224x2048_19.fits.gz diff --git a/Config/cryoscope/cryoscope.acf b/config/cryoscope/cryoscope.acf similarity index 100% rename from Config/cryoscope/cryoscope.acf rename to config/cryoscope/cryoscope.acf diff --git a/Config/cryoscope/cryoscope.cfg b/config/cryoscope/cryoscope.cfg similarity index 100% rename from Config/cryoscope/cryoscope.cfg rename to config/cryoscope/cryoscope.cfg diff --git a/Config/demo/demo.acf b/config/demo/demo.acf similarity index 100% rename from Config/demo/demo.acf rename to config/demo/demo.acf diff --git a/Config/demo/demo.cfg b/config/demo/demo.cfg similarity index 100% rename from Config/demo/demo.cfg rename to config/demo/demo.cfg diff --git a/Config/demo/demo.system b/config/demo/demo.system similarity index 100% rename from Config/demo/demo.system rename to config/demo/demo.system diff --git a/config/frame_outputs_test/frame_outputs_test.cfg b/config/frame_outputs_test/frame_outputs_test.cfg new file mode 100644 index 0000000..2af3391 --- /dev/null +++ b/config/frame_outputs_test/frame_outputs_test.cfg @@ -0,0 +1,38 @@ +# camerad + emulator SHM/FITS frame_outputs testing (CI). Exercises the +# generic frame_outputs mechanism (Camera::Interface::configure_frame_outputs), +# not anything hispec_tracking_camera-specific; hispec_tracking_camera is +# just the build's INSTRUMENT since it's the only fully-working one today. + +DAEMON=no +IMDIR=/tmp +LOGPATH=/tmp +BASENAME=frameoutputstest +DIRMODE=0077 +TM_ZONE_LOG=local +TM_ZONE=UTC +TZ_ENV=PST8PDT,M3.2.0/2,M11.1.0/2 + +NBPORT=3130 +BLKPORT=3131 +EMULATOR_PORT=3132 +EMULATOR_SYSTEM=config/frame_outputs_test/frame_outputs_test.system + +ASYNCGROUP=none + +ARCHON_IP=localhost +ARCHON_PORT=3132 +DEFAULT_FIRMWARE=camerad/Instruments/hispec_tracking_camera/config/hispecatc.acf +EXPOSE_PARAM=Expose +ABORT_PARAM=abort +READOUT_TIME=5000 +WRITE_TAPINFO_TO_FITS=no + +SHM_ENABLED=yes +SHM_SEGMENT_NAME=ci_frame_outputs_shm +SHM_RING_BUFFER_SIZE=2 +SHM_DIR=/tmp/ci_milk_shm + +FITS_ENABLED=yes +FITS_OUTPUT_DIR=/tmp/ci_fits_test +FITS_AUTODIR=no +FITS_BASENAME=ci_frame_outputs diff --git a/Config/hispec_shm_test/hispec_shm_test.system b/config/frame_outputs_test/frame_outputs_test.system similarity index 100% rename from Config/hispec_shm_test/hispec_shm_test.system rename to config/frame_outputs_test/frame_outputs_test.system From bea0ae16a4ab52d3264dcd7828451632371df3aa Mon Sep 17 00:00:00 2001 From: Mike Langmayr <1809691+mikelangmayr@users.noreply.github.com> Date: Wed, 26 Aug 2026 17:35:33 -0700 Subject: [PATCH 4/7] Fix internal Config path casing left over from the config rename --- config/cryoscope/cryoscope.cfg | 4 ++-- config/demo/demo.cfg | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/config/cryoscope/cryoscope.cfg b/config/cryoscope/cryoscope.cfg index b6d152b..bcb3399 100644 --- a/config/cryoscope/cryoscope.cfg +++ b/config/cryoscope/cryoscope.cfg @@ -12,14 +12,14 @@ TZ_ENV=PST8PDT,M3.2.0/2,M11.1.0/2 NBPORT=3030 BLKPORT=3031 EMULATOR_PORT=3032 -EMULATOR_SYSTEM=Config/demo/demo.system +EMULATOR_SYSTEM=config/demo/demo.system ASYNCGROUP=239.1.1.234 ASYNCPORT=1234 ARCHON_IP=localhost ARCHON_PORT=3032 -DEFAULT_FIRMWARE=Config/cryoscope/cryoscope.acf +DEFAULT_FIRMWARE=config/cryoscope/cryoscope.acf ABORT_PARAM=Abort EXPOSE_PARAM=Expose EXPTIME_MSEC_PARAM=exptime diff --git a/config/demo/demo.cfg b/config/demo/demo.cfg index c28c841..c1a84e8 100644 --- a/config/demo/demo.cfg +++ b/config/demo/demo.cfg @@ -17,7 +17,7 @@ LONGERROR=true NBPORT=3030 # server non-blocking port BLKPORT=3031 # 3031 or 3041 blocking port EMULATOR_PORT=3032 -EMULATOR_SYSTEM=Config/demo/demo.system +EMULATOR_SYSTEM=config/demo/demo.system # # If you have a firewall, you must allow # multicast address 239.192.13.165, ports 1304-1305. @@ -28,7 +28,7 @@ ASYNCPORT=1234 # asynchronous message port # the following should not be changed ARCHON_IP=localhost ARCHON_PORT=3032 -DEFAULT_FIRMWARE=Config/demo/demo.acf +DEFAULT_FIRMWARE=config/demo/demo.acf ABORT_PARAM=abort # Archon parameter to trigger an abort EXPOSE_PARAM=Expose # Archon parameter to trigger exposure EXPTIME_MSEC_PARAM=exptime # Archon parameter for exposure time in msec From 48311338d30d243603ec339912782af00cda2752 Mon Sep 17 00:00:00 2001 From: Mike Langmayr <1809691+mikelangmayr@users.noreply.github.com> Date: Wed, 26 Aug 2026 17:42:10 -0700 Subject: [PATCH 5/7] Remove shm_max_frame_bytes ceiling, ImageStreamIO already sizes to real geometry --- README.md | 1 - utils/frame_output_factory.cpp | 23 ++++++++--------------- utils/frame_output_factory.h | 7 ------- utils/shared_memory_writer.cpp | 14 +------------- utils/shared_memory_writer.h | 2 -- 5 files changed, 9 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 0feaaad..4f9a387 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,6 @@ Publishes each frame as an [ImageStreamIO](https://github.com/milk-org/ImageStre |-------------------------|------------|--------------------------------------------------------------------------------------------| | `SHM_ENABLED` | `no` | Enable the shared-memory writer | | `SHM_SEGMENT_NAME` | `camera` | ImageStreamIO stream name | -| `SHM_MAX_FRAME_BYTES` | `67108864` (64 MiB) | Validation ceiling for a frame's byte size; a frame larger than this is rejected rather than written. The default comfortably covers any detector geometry realistic for this codebase; set explicitly for a tighter bound. | | `SHM_RING_BUFFER_SIZE` | `4` | Depth of ImageStreamIO's internal history ring buffer (`CBsize`); the live frame a real-time reader sees is separate from this | | `SHM_DIR` | (unset) | Base directory ImageStreamIO writes into. If unset, ImageStreamIO falls back to its own default resolution (`MILK_SHM_DIR` env var, then `/milk/shm`). If set, it must already exist and be writable. | diff --git a/utils/frame_output_factory.cpp b/utils/frame_output_factory.cpp index 01c2c9d..badc2ef 100644 --- a/utils/frame_output_factory.cpp +++ b/utils/frame_output_factory.cpp @@ -30,7 +30,6 @@ namespace Camera { try { if (key == "SHM_ENABLED") out.shm_enabled = parse_bool(val); else if (key == "SHM_SEGMENT_NAME") out.shm_segment_name = val; - else if (key == "SHM_MAX_FRAME_BYTES") out.shm_max_frame_bytes = static_cast(std::stoull(val)); else if (key == "SHM_RING_BUFFER_SIZE") out.shm_ring_buffer_size = static_cast(std::stoul(val)); else if (key == "SHM_DIR") out.shm_dir = val; else if (key == "FITS_ENABLED") out.fits_enabled = parse_bool(val); @@ -53,22 +52,16 @@ namespace Camera { if (cfg.shm_enabled) { #ifdef CAMERAD_HAVE_SHM - if (cfg.shm_max_frame_bytes == 0) { - logwrite(function, "WARNING shm_enabled but shm_max_frame_bytes==0; SHM skipped"); + auto shm = std::make_unique( + cfg.shm_segment_name, cfg.shm_ring_buffer_size, cfg.shm_dir); + if (shm->open() == NO_ERROR) { + logwrite(function, "SHM output enabled: segment=" + cfg.shm_segment_name + + " ring_buffer_size=" + std::to_string(cfg.shm_ring_buffer_size) + + " dir=" + (cfg.shm_dir.empty() ? "(default)" : cfg.shm_dir)); + outputs.push_back(std::move(shm)); } else { - auto shm = std::make_unique( - cfg.shm_segment_name, cfg.shm_max_frame_bytes, cfg.shm_ring_buffer_size, cfg.shm_dir); - if (shm->open() == NO_ERROR) { - logwrite(function, "SHM output enabled: segment=" + cfg.shm_segment_name + - " max_bytes=" + std::to_string(cfg.shm_max_frame_bytes) + - " ring_buffer_size=" + std::to_string(cfg.shm_ring_buffer_size) + - " dir=" + (cfg.shm_dir.empty() ? "(default)" : cfg.shm_dir)); - outputs.push_back(std::move(shm)); - } - else { - logwrite(function, "WARNING SHM output failed to open; skipped"); - } + logwrite(function, "WARNING SHM output failed to open; skipped"); } #else logwrite(function, "WARNING shm_enabled but this build was compiled without SHM support (ENABLE_SHM_OUTPUT=OFF)"); diff --git a/utils/frame_output_factory.h b/utils/frame_output_factory.h index f259ff4..f66fbed 100644 --- a/utils/frame_output_factory.h +++ b/utils/frame_output_factory.h @@ -20,16 +20,9 @@ namespace Camera { - // Safe fallback SHM frame-size ceiling when neither a .cfg file nor an - // instrument specifies one: comfortably covers any detector geometry - // realistic for this codebase (e.g. HISPEC's 2048x2048x4B ~16 MiB) without - // being an arbitrarily large "accept anything" bound. - constexpr size_t DEFAULT_SHM_MAX_FRAME_BYTES = 4096ULL * 4096ULL * 4ULL; // 64 MiB - struct FrameOutputsConfig { bool shm_enabled{false}; std::string shm_segment_name{"camera"}; - size_t shm_max_frame_bytes{DEFAULT_SHM_MAX_FRAME_BYTES}; uint32_t shm_ring_buffer_size{4}; // depth of ImageStreamIO's internal history ring buffer std::string shm_dir{}; // ImageStreamIO base directory; empty uses its own default resolution diff --git a/utils/shared_memory_writer.cpp b/utils/shared_memory_writer.cpp index 0d7da3d..74d8b78 100644 --- a/utils/shared_memory_writer.cpp +++ b/utils/shared_memory_writer.cpp @@ -41,11 +41,9 @@ namespace { namespace Camera { SharedMemoryWriter::SharedMemoryWriter(const std::string &segment_name, - size_t max_frame_bytes, uint32_t ring_buffer_size, const std::string &shm_dir) : segment_name_(segment_name), - max_frame_bytes_(max_frame_bytes), ring_buffer_size_(ring_buffer_size), shm_dir_(shm_dir) { } @@ -61,10 +59,6 @@ namespace Camera { logwrite(function, "ERROR segment name is empty"); return ERROR; } - if (max_frame_bytes_ == 0) { - logwrite(function, "ERROR max_frame_bytes must be > 0"); - return ERROR; - } if (ring_buffer_size_ == 0) { logwrite(function, "ERROR ring_buffer_size must be > 0"); return ERROR; @@ -83,8 +77,7 @@ namespace Camera { opened_ = true; // Geometry is fixed for a stream's whole life, so create happens in write(), not here - logwrite(function, "ready to publish \"" + segment_name_ + "\" (max " + - std::to_string(max_frame_bytes_) + " bytes/frame, " + + logwrite(function, "ready to publish \"" + segment_name_ + "\" (" + std::to_string(ring_buffer_size_) + " frames, dir=" + (shm_dir_.empty() ? "(default)" : shm_dir_) + ")"); return NO_ERROR; @@ -109,11 +102,6 @@ namespace Camera { const size_t frame_bytes = static_cast(meta.width) * meta.height * meta.bytes_per_pixel; - if (frame_bytes > max_frame_bytes_) { - logwrite(function, "ERROR frame size " + std::to_string(frame_bytes) + - " exceeds max " + std::to_string(max_frame_bytes_)); - return ERROR; - } if (size < frame_bytes) { logwrite(function, "ERROR frame data " + std::to_string(size) + " < expected " + std::to_string(frame_bytes)); diff --git a/utils/shared_memory_writer.h b/utils/shared_memory_writer.h index b73a1c1..5b2ab88 100644 --- a/utils/shared_memory_writer.h +++ b/utils/shared_memory_writer.h @@ -19,7 +19,6 @@ namespace Camera { class SharedMemoryWriter : public FrameOutput { public: SharedMemoryWriter(const std::string &segment_name, - size_t max_frame_bytes, uint32_t ring_buffer_size = 4, const std::string &shm_dir = ""); ~SharedMemoryWriter(); @@ -30,7 +29,6 @@ namespace Camera { private: std::string segment_name_; - size_t max_frame_bytes_; uint32_t ring_buffer_size_; std::string shm_dir_; bool opened_{false}; From e8f9691cd70da21d8f5d62af3ae1778c1a02ff6a Mon Sep 17 00:00:00 2001 From: Mike Langmayr <1809691+mikelangmayr@users.noreply.github.com> Date: Wed, 26 Aug 2026 17:49:48 -0700 Subject: [PATCH 6/7] Bump hispec_tracking_camera submodule to PR 12 branch tip for CI --- camerad/Instruments/hispec_tracking_camera | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/camerad/Instruments/hispec_tracking_camera b/camerad/Instruments/hispec_tracking_camera index 320528d..2a7b55c 160000 --- a/camerad/Instruments/hispec_tracking_camera +++ b/camerad/Instruments/hispec_tracking_camera @@ -1 +1 @@ -Subproject commit 320528d7aac4befa6263363e5e172684c75421e6 +Subproject commit 2a7b55cb1aeaf9aa9a5a52741c42cf7fe9069321 From 8e70cccdcef159369b7162076e20a3be33b06a3b Mon Sep 17 00:00:00 2001 From: Mike Langmayr <1809691+mikelangmayr@users.noreply.github.com> Date: Wed, 26 Aug 2026 17:55:52 -0700 Subject: [PATCH 7/7] Bump hispec_tracking_camera submodule to main now that 12 is merged --- camerad/Instruments/hispec_tracking_camera | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/camerad/Instruments/hispec_tracking_camera b/camerad/Instruments/hispec_tracking_camera index 2a7b55c..08502aa 160000 --- a/camerad/Instruments/hispec_tracking_camera +++ b/camerad/Instruments/hispec_tracking_camera @@ -1 +1 @@ -Subproject commit 2a7b55cb1aeaf9aa9a5a52741c42cf7fe9069321 +Subproject commit 08502aadd33cb63cf0fdaad3e934701611462e4c