diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 8a2322c..1a7970f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -29,6 +29,26 @@ jobs: - name: Run linting checks run: pixi run lint + docs: + name: Build Docs + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Setup pixi + uses: prefix-dev/setup-pixi@2253d9c9310442c09975d64615591b5b381da6e7 # v0.10.0 + with: + pixi-version: v0.71.3 + cache: false + frozen: true + environments: docs + + - name: Build docs + run: pixi run -e docs build-docs + build-and-test: name: Build and Test ADXSPD on EL${{ matrix.rhel_version }} diff --git a/.gitignore b/.gitignore index 1fa7ffd..cb10540 100644 --- a/.gitignore +++ b/.gitignore @@ -29,4 +29,6 @@ checkVar.sh setVar.sh coverage.info coverage-report/ +# rendered docs from `pixi run build-docs` +build/ __pycache__/ diff --git a/docs/ADXSPD/ADXSPD.rst b/docs/ADXSPD/ADXSPD.rst new file mode 100644 index 0000000..d38c0b0 --- /dev/null +++ b/docs/ADXSPD/ADXSPD.rst @@ -0,0 +1,524 @@ +====================================== +ADXSPD +====================================== + +:author: Jakub Wlodek (Brookhaven National Laboratory) + +.. _ADDriver: https://areadetector.github.io/areaDetector/ADCore/ADDriver.html +.. _areaDetector: https://areadetector.github.io/areaDetector/index.html +.. _EPICS: https://epics-controls.org +.. _X-Spectrum: https://x-spectrum.de +.. _Lambda: https://x-spectrum.de/products/lambda/ +.. _ADDriverTemplate: https://github.com/NSLS2/ADDriverTemplate +.. _cpr: https://github.com/libcpr/cpr +.. _nlohmann/json: https://github.com/nlohmann/json +.. _magic_enum: https://github.com/Neargye/magic_enum +.. _ZeroMQ: https://zeromq.org +.. _ADLambda: https://github.com/areaDetector/ADLambda + +.. contents:: Contents + +.. |br| raw:: html + +
+ +Introduction +------------ + +This is an `EPICS`_ `areaDetector`_ driver for the `Lambda`_ family of photon counting +pixel detectors from `X-Spectrum`_. The driver communicates with the detector through +XSPD, the X-Spectrum detector remote-control service, which exposes a REST API for control and a +`ZeroMQ`_ publisher (the "data port") for streaming frames. + +.. note:: + + The `ADLambda`_ driver supports the same detector types, but must run on the control + computer provided by X-Spectrum, since it uses the C++ `libxsp` API directly. + Because ADXSPD talks to XSPD over the network (with XSPD handling the lower level `libxsp` interactions), + the IOC can instead run on any other host. This has several benefits: + + * The IOC can run on a standard system configuration, managed the same way as your + other IOC hosts, rather than on the specific Debian-based server provided by X-Spectrum. + * The X-Spectrum control computer can be placed on a locked-down, dedicated detector + network, with only the IOC host able to reach it. This allows for a less strict policy + regarding software updates and patches on the host, which may improve stability and reduce maintenance overhead. + * Since all communication between the driver and XSPD is plain HTTP and ZeroMQ, it is + much simpler to simulate the detector. X-Spectrum provides a simulator for a + single-module detector as a container image, and this repository includes a + multi-module simulator in ``scripts/xspdSimulator.py``. + + ADXSPD also currently supports some features that ADLambda does not, including beam + energy, flatfield correction, bit depth and ROI readout configuration. Adding these to + ADLambda is tracked in + `ADLambda issue #16 `__. + +The driver is made up of three layers: + +* ``XSPDAPI`` - a standalone C++ wrapper around the XSPD REST API. It uses `cpr`_ for HTTP, + `nlohmann/json`_ for parsing responses, and `magic_enum`_ to map XSPD string values to + C++ enumerations. It has no dependency on EPICS. +* ``ADXSPD`` - the main areaDetector driver, which inherits from `ADDriver`_. It controls + detector-wide settings, runs an acquisition thread that reads frames from the ZeroMQ + data port into NDArrays, and runs a monitor thread that polls detector status. +* ``ADXSPDModule`` - an ``asynPortDriver`` that is created automatically for each detector + module, and which exposes per-module information such as temperatures, humidity, + flatfield state and chip IDs. + +The driver requires XSPD version 1.6.0 or later. It was initially generated with +`ADDriverTemplate`_. + +Acquisition overview +~~~~~~~~~~~~~~~~~~~~ + +When ``Acquire`` is set to 1, the driver sends the ``start`` command to XSPD. Each frame +published on the data port is a 3-part ZeroMQ message; the driver copies the final part +(the frame data) into an NDArray and passes it to any connected plugins. Acquisition stops +automatically once ``NumImages`` frames have been received (or ``2 * NumImages`` when +``CounterMode`` is ``Dual``). Setting ``Acquire`` to 0 sends the ``stop`` command. + +If XSPD is configured to compress frames, the driver can either pass the compressed data +on as a compressed NDArray (with the ``codec`` field set so that ``NDPluginCodec`` or the +HDF5 file plugin can handle it), or decompress the data itself. See +:ref:`ADXSPD_compression` for details. + +Implementation of standard driver parameters +-------------------------------------------- + +The following table describes how the ADXSPD driver implements some of the standard +driver parameters. + +.. cssclass:: table-bordered table-striped table-hover +.. flat-table:: + :header-rows: 2 + :widths: 20 20 60 + + * - **Parameter Definitions in ADXSPD.cpp and EPICS Record Definitions in ADXSPD.template** + * - Parameter index variable + - EPICS record name + - Description + * - ADImageMode + - $(P)$(R)ImageMode + - Only ``Single`` and ``Multiple`` are supported. ``Continuous`` has been removed from the + record choices. Selecting ``Single`` sets the XSPD ``n_frames`` variable to 1. + The image mode is also updated automatically when ``NumImages`` is changed. + * - ADNumImages + - $(P)$(R)NumImages + - Sets the XSPD ``n_frames`` variable. The value must be between 1 and the smallest + ``MaxFrames_RBV`` reported by any of the detector modules. + * - ADTriggerMode + - $(P)$(R)TriggerMode + - Sets the XSPD ``trigger_mode`` variable. The choices are: |br| + ``Software`` - acquisition starts immediately. |br| + ``External Frames`` - each external trigger acquires a single frame. |br| + ``External Sequence`` - an external trigger starts the full sequence of frames. |br| + Cannot be changed while acquiring. + * - ADAcquireTime + - $(P)$(R)AcquireTime + - Sets the XSPD ``shutter_time`` variable. The record is in seconds; the driver converts + to milliseconds for XSPD. Cannot be changed while acquiring. + * - ADAcquire + - $(P)$(R)Acquire + - Sends the ``start`` or ``stop`` command to XSPD. + * - NDDataType + - $(P)$(R)DataType + - Disabled. The data type is set by the driver based on ``BitDepth``: 1 and 6 bit use + ``UInt8``, 12 bit uses ``UInt16``, and 24 bit uses ``UInt32``. + * - ADMaxSizeX, ADMaxSizeY, ADSizeX, ADSizeY + - $(P)$(R)MaxSizeX_RBV, $(P)$(R)MaxSizeY_RBV, $(P)$(R)SizeX_RBV, $(P)$(R)SizeY_RBV + - Read from the data port ``frame_width`` and ``frame_height`` variables at startup, + and whenever ``ROIRows`` is changed. The ``SizeX``, ``SizeY``, ``MinX``, ``MinY``, + ``BinX``, ``BinY``, ``ReverseX``, ``ReverseY`` and ``ColorMode`` output records are + disabled; use ``ROIRows`` to reduce the readout area. + * - ADSerialNumber, ADFirmwareVersion, ADSDKVersion, ADModel + - $(P)$(R)SerialNumber_RBV, $(P)$(R)FirmwareVersion_RBV, $(P)$(R)SDKVersion_RBV, $(P)$(R)Model_RBV + - Read from XSPD at startup. ``SDKVersion_RBV`` reports the libxsp version. + * - NDFilePath, NDFileName, NDFullFileName + - $(P)$(R)FilePath, $(P)$(R)FileName, $(P)$(R)FullFileName_RBV + - The driver loads ``NDFile.template``, but does not save image files itself. These + records are used to choose where HTTP request logs are written. See + :ref:`ADXSPD_request_logging`. + +ADXSPD specific parameters +-------------------------- + +The ADXSPD driver implements the following parameters in addition to those in +``asynNDArrayDriver.h`` and ``ADDriver.h``. Parameters marked "Cannot be changed while +acquiring" are rejected with an error if ``Acquire`` is 1. + +.. cssclass:: table-bordered table-striped table-hover +.. flat-table:: + :header-rows: 2 + :widths: 55 15 20 10 + + * - **Parameter Definitions in ADXSPDParamDefs.h and EPICS Record Definitions in ADXSPD.template** + * - Description + - drvInfo string + - EPICS record name + - EPICS record type + * - :cspan:`3` **Version and system information** + * - XSPD REST API version in use. + - XSPD_API_VERSION + - $(P)$(R)APIVersion_RBV + - stringin + * - XSPD server version. + - XSPD_VERSION + - $(P)$(R)XSPDVersion_RBV + - stringin + * - Number of detector modules. One ``ADXSPDModule`` port is created for each module. + - XSPD_NUM_MODULES + - $(P)$(R)NumModules_RBV + - ai + * - Sensor material, read from the detector ``user_data/sensor_material`` variable. Empty, + with a warning printed at startup, if this has not been set in the XSPD user data. + - XSPD_SENSOR_MATERIAL + - $(P)$(R)SensorMaterial_RBV + - stringin + * - Sensor thickness, read from the detector ``user_data/sensor_thickness`` variable. 0 if + this has not been set in the XSPD user data. + - XSPD_SENSOR_THICKNESS + - $(P)$(R)SensorThickness_RBV + - ai + * - :cspan:`3` **Detector settings** + * - X-ray beam energy in keV (XSPD ``beam_energy``). + - XSPD_BEAM_ENERGY + - $(P)$(R)BeamEnergy, $(P)$(R)BeamEnergy_RBV + - ao, ai + * - Low energy threshold in keV. Written to XSPD as the first element of ``thresholds``. + - XSPD_LOW_THRESHOLD + - $(P)$(R)LowThreshold, $(P)$(R)LowThreshold_RBV + - ao, ai + * - High energy threshold in keV. Written to XSPD as the second element of ``thresholds``. + The low threshold must be set first. + - XSPD_HIGH_THRESHOLD + - $(P)$(R)HighThreshold, $(P)$(R)HighThreshold_RBV + - ao, ai + * - Counter bit depth. Choices are ``1 bit``, ``6 bit``, ``12 bit`` and ``24 bit``. + Changing this updates ``DataType`` and each module's ``MaxFrames_RBV``. + Cannot be changed while acquiring. + - XSPD_BIT_DEPTH + - $(P)$(R)BitDepth, $(P)$(R)BitDepth_RBV + - mbbo, mbbi + * - Counter mode. Choices are ``Single`` and ``Dual``. In ``Dual`` mode two frames are + read out per image. Changing this updates each module's ``MaxFrames_RBV`` and + flatfield state. Cannot be changed while acquiring. + - XSPD_COUNTER_MODE + - $(P)$(R)CounterMode, $(P)$(R)CounterMode_RBV + - bo, bi + * - Number of rows to read out from each module (XSPD ``roi_rows``). Choices are powers of + 2 from 1 to 256. Changing this updates the frame size and each module's + ``MaxFrames_RBV``. + - XSPD_ROI_ROWS + - $(P)$(R)ROIRows, $(P)$(R)ROIRows_RBV + - mbbo, mbbi + * - Number of frames to sum in the detector (XSPD ``summed_frames``). + - XSPD_SUMMED_FRAMES + - $(P)$(R)SummedFrames, $(P)$(R)SummedFrames_RBV + - ao, ai + * - Enable or disable gating mode (XSPD ``gating_mode``). + - XSPD_GATING_MODE + - $(P)$(R)GatingMode, $(P)$(R)GatingMode_RBV + - bo, bi + * - Enable or disable charge summing (XSPD ``charge_summing``). + - XSPD_CHARGE_SUMMING + - $(P)$(R)ChargeSumming, $(P)$(R)ChargeSumming_RBV + - bo, bi + * - Enable or disable flatfield correction (XSPD ``flatfield_correction``). + - XSPD_F_F_CORRECTION + - $(P)$(R)FlatFieldCorrection, $(P)$(R)FlatFieldCorrection_RBV + - bo, bi + * - Enable or disable count rate correction (XSPD ``countrate_correction``). + - XSPD_CR_CORR + - $(P)$(R)CountrateCorrection, $(P)$(R)CountrateCorrection_RBV + - bo, bi + * - Enable or disable the saturation flag (XSPD ``saturation_flag``). + - XSPD_SATURATION_FLAG + - $(P)$(R)SaturationFlag, $(P)$(R)SaturationFlag_RBV + - bo, bi + * - :cspan:`3` **Compression and data handling** + * - Compressor used by XSPD for data port frames. Choices are ``none``, ``zlib``, + ``blosc/blosclz``, ``blosc/lz4``, ``blosc/lz4hc``, ``blosc/snappy``, ``blosc/zlib`` + and ``blosc/zstd``. Read-only; configure the compressor in XSPD. + - XSPD_COMPRESSOR + - $(P)$(R)Compressor_RBV + - mbbi + * - Compression level (0-9) used by XSPD. Read-only. + - XSPD_COMPRESS_LEVEL + - $(P)$(R)CompressLevel_RBV + - mbbi + * - Blosc shuffle mode. Choices are ``None``, ``Byte Shuffle``, ``Bit Shuffle`` and + ``Auto``. Cannot be changed while acquiring. + - XSPD_SHUFFLE_MODE + - $(P)$(R)ShuffleMode, $(P)$(R)ShuffleMode_RBV + - mbbo, mbbi + * - If enabled, compressed frames are decompressed in the driver. If disabled, compressed + frames are passed on as compressed NDArrays. + - XSPD_DECOMPRESS + - $(P)$(R)Decompress, $(P)$(R)Decompress_RBV + - bo, bi + * - Number of threads used for Blosc decompression when ``Decompress`` is enabled. + - XSPD_BLOSC_NUM_THREADS + - $(P)$(R)BloscNumThreads, $(P)$(R)BloscNumThreads_RBV + - ao, ai + * - Number of frames queued on the data port and not yet sent (XSPD ``frames_queued``). + - XSPD_FRAMES_QUEUED + - $(P)$(R)FramesQueued_RBV + - ai + * - :cspan:`3` **Status monitoring** + * - Enable or disable the monitor thread, which polls the detector ``status`` and data port + ``frames_queued`` variables and updates ``DetectorState_RBV`` and ``FramesQueued_RBV``. + - XSPD_MONITOR_MODE + - $(P)$(R)MonitorMode, $(P)$(R)MonitorMode_RBV + - bo, bi + * - Interval in seconds between monitor thread polls. The minimum is 0.5 seconds. + - XSPD_MONITOR_INTERVAL + - $(P)$(R)MonitorInterval, $(P)$(R)MonitorInterval_RBV + - ao, ai + * - :cspan:`3` **Diagnostics** + * - Enable or disable logging of every HTTP request sent to XSPD. See + :ref:`ADXSPD_request_logging`. + - XSPD_LOG_REQUESTS + - $(P)$(R)LogRequests, $(P)$(R)LogRequests_RBV + - bo, bi + * - :cspan:`3` **Reserved (records exist but are not yet implemented by the driver)** + * - Reset the detector. + - XSPD_RESET + - $(P)$(R)Reset + - bo + * - Generate a new flatfield. + - XSPD_GENERATE_FLATFIELD + - $(P)$(R)GenerateFlatfield + - bo + * - Flatfield status message. + - XSPD_FLATFIELD_STATUS + - $(P)$(R)FlatfieldStatus_RBV + - waveform + * - Average board, FPGA and sensor temperatures across all modules. + - XSPD_AVG_BOARD_TEMP, XSPD_AVG_FPGA_TEMP, XSPD_AVG_SENSOR_TEMP + - $(P)$(R)AvgBoardTemp_RBV, $(P)$(R)AvgFPGATemp_RBV, $(P)$(R)AvgSensorTemp_RBV + - ai + +ADXSPDModule parameters +----------------------- + +One ``ADXSPDModule`` asyn port is created for each detector module, named +``_MOD1``, ``_MOD2``, etc. Load ``ADXSPDModule.template`` once for each module. +Values are read from XSPD when the IOC starts. Module values are not currently polled by +the monitor thread, because reading module status takes too long. + +.. cssclass:: table-bordered table-striped table-hover +.. flat-table:: + :header-rows: 2 + :widths: 55 15 20 10 + + * - **Parameter Definitions in ADXSPDModuleParamDefs.h and EPICS Record Definitions in ADXSPDModule.template** + * - Description + - drvInfo string + - EPICS record name + - EPICS record type + * - Board, FPGA and humidity sensor temperatures in degrees C. + - XSPD_BOARD_TEMP, XSPD_FPGA_TEMP, XSPD_HUM_TEMP + - $(P)$(R)BoardTemp_RBV, $(P)$(R)FPGATemp_RBV, $(P)$(R)HumiditySensorTemp_RBV + - ai + * - Relative humidity in %. + - XSPD_HUM + - $(P)$(R)Humidity_RBV + - ai + * - Sensor high voltage in V. Only the readback is currently implemented. + - XSPD_VOLTAGE + - $(P)$(R)Voltage, $(P)$(R)Voltage_RBV + - ao, ai + * - Sensor current in uA. + - XSPD_SENS_CURR + - $(P)$(R)SensorCurrent_RBV + - ai + * - Pixel saturation threshold. + - XSPD_SAT_THRESH + - $(P)$(R)SaturationThresh_RBV + - ai + * - Module rotation (yaw, pitch, roll) in degrees. + - XSPD_ROT_YAW, XSPD_ROT_PITCH, XSPD_ROT_ROLL + - $(P)$(R)RotationYaw_RBV, $(P)$(R)RotationPitch_RBV, $(P)$(R)RotationRoll_RBV + - ai + * - Module position (X, Y, Z). + - XSPD_POS_X, XSPD_POS_Y, XSPD_POS_Z + - $(P)$(R)PositionX_RBV, $(P)$(R)PositionY_RBV, $(P)$(R)PositionZ_RBV + - ai + * - Maximum number of frames the module can buffer with the current settings. This limits + ``NumImages``, and is refreshed when ``BitDepth``, ``ROIRows`` or ``CounterMode`` + change. + - XSPD_MAX_FRAMES + - $(P)$(R)MaxFrames_RBV + - ai + * - Number of sub-frames. + - XSPD_NUM_SUBFRAMES + - $(P)$(R)NumSubFrames_RBV + - ai + * - Number of connectors. + - XSPD_NUM_CONS + - $(P)$(R)NumConnectors_RBV + - ai + * - Whether pixel interpolation is enabled. + - XSPD_INTERP_MODE + - $(P)$(R)InterpolationMode_RBV + - bi + * - Bitmask of features supported by the module: |br| + bit 0 - ``FEAT_HV`` |br| + bit 1 - ``FEAT_1_6_BIT`` |br| + bit 2 - ``FEAT_MEDIPIX_DAC_IO`` |br| + bit 3 - ``FEAT_EXTENDED_GATING`` |br| + bit 4 - ``FEAT_ROI`` + - XSPD_FEAT_BITMASK + - $(P)$(R)Features_RBV + - mbbiDirect + * - Module compressor and compression level. + - XSPD_COMPRESSOR, XSPD_COMPRESS_LEVEL + - $(P)$(R)Compressor_RBV, $(P)$(R)CompressLevel_RBV + - mbbi + * - Whether RAM has been allocated on the module. + - XSPD_RAM_ALLOCATED + - $(P)$(R)RamAllocated_RBV + - bi + * - Whether flatfield correction is enabled on the module. + - XSPD_FF_ENABLED + - $(P)$(R)FlatfieldEnabled_RBV + - bi + * - Flatfield status message. + - XSPD_FF_STATUS + - $(P)$(R)FlatfieldStatus_RBV + - waveform + * - Author and date of the low and high threshold flatfields. + - XSPD_LOW_THRESH_FF_AUTHOR, XSPD_HIGH_THRESH_FF_AUTHOR, XSPD_LOW_THRESH_FF_DATE, + XSPD_HIGH_THRESH_FF_DATE + - $(P)$(R)LowThreshFfAuthor_RBV, $(P)$(R)HighThreshFfAuthor_RBV, + $(P)$(R)LowThreshFfDate_RBV, $(P)$(R)HighThreshFfDate_RBV + - stringin + * - Number of frames queued on the module. + - XSPD_FRAMES_QUEUED + - $(P)$(R)FramesQueued_RBV + - ai + * - Whether the pixel mask is applied. Only the readback is currently implemented. + - XSPD_PIXEL_MASK + - $(P)$(R)PixelMask, $(P)$(R)PixelMask_RBV + - bo, bi + * - Module shuffle mode. Not currently implemented. + - XSPD_SHUFFLE_MODE + - $(P)$(R)ShuffleMode, $(P)$(R)ShuffleMode_RBV + - mbbo, mbbi + * - Number of chips on the module, and the ID of each chip (up to 12). + - XSPD_NUM_CHIPS, XSPD_CHIP1_ID ... XSPD_CHIP12_ID + - $(P)$(R)NumChips_RBV, $(P)$(R)Chip1ID_RBV ... $(P)$(R)Chip12ID_RBV + - ai, stringin + +.. _ADXSPD_compression: + +Compressed data +--------------- + +XSPD can compress frames before publishing them on the data port. The compressor and +level are shown in ``Compressor_RBV`` and ``CompressLevel_RBV``. The ``Decompress`` +record controls how the driver handles compressed frames: + +* ``Decompress = Enabled``: the driver decompresses each frame with zlib or Blosc before + passing it to plugins. Blosc decompression uses ``BloscNumThreads`` threads. +* ``Decompress = Disabled``: the compressed bytes are copied into the NDArray as-is, and the + NDArray ``codec`` field is set to ``zlib`` or ``blosc`` (with the Blosc sub-compressor, + level and ``ShuffleMode``). Plugins such as ``NDPluginCodec`` or ``NDFileHDF5`` can then + decompress or write the data directly. zlib-compressed NDArrays require ADCore R3-15 or + later. + +.. _ADXSPD_request_logging: + +HTTP request logging +-------------------- + +For debugging communication with XSPD, the driver can log every HTTP request it sends. +Each log entry contains a timestamp, the HTTP method, the full URI, the HTTP status code +(0 if no response was received) and the time taken in milliseconds, for example:: + + 2026-09-30 12:00:00.123 | GET localhost:8008/api/v1/devices/lambda01/variables?path=lambda/status | status=200 | elapsed=1.842 ms + +Set ``LogRequests`` to ``Enabled`` to start logging. Where logs are written depends on the +``NDFile.template`` records: + +* If ``FilePath`` is empty, logs are written to stdout. +* Otherwise, logs are appended to ``FilePath`` + ``FileName``. If ``FileName`` is empty, + ``xspd_requests.log`` is used. ``FilePath`` must already exist; enabling logging fails + if it does not. +* The full path of the log file is shown in ``FullFileName_RBV``. +* Changing ``FilePath`` or ``FileName`` while logging is enabled switches to the new file. + If the new file cannot be opened, logging is disabled and an error is reported. + +Request logging can also be used directly from the ``XSPDAPI`` library with +``XSPD::API::EnableRequestLogging(path)`` and ``XSPD::API::DisableRequestLogging()``. + +Configuration +------------- + +The ADXSPD driver is created with the ``ADXSPDConfig`` command, either from C/C++ or from +the EPICS IOC shell. + +:: + + int ADXSPDConfig(const char *portName, const char *ip, int portNum, const char *deviceId) + +.. cssclass:: table-bordered table-striped table-hover +.. flat-table:: + :header-rows: 1 + :widths: 20 80 + + * - Argument + - Description + * - portName + - Name of the asyn port to create. Module ports are named ``_MOD1``, + ``_MOD2``, etc. + * - ip + - Hostname or IP address of the machine running XSPD. + * - portNum + - Port number of the XSPD REST API. If 0, the default of 8008 is used. + * - deviceId + - Optional. The XSPD device ID to connect to (e.g. ``lambda01``), or a single-digit index + into the list of devices. If omitted, the first device is used. + +Example st.cmd startup file +--------------------------- + +The following startup script is provided with ADXSPD. + +.. literalinclude:: ../../../ADXSPD/iocs/xspdIOC/iocBoot/iocXSPD/st_base.cmd + +Screens +------- + +Phoebus screens are provided in ``xspdApp/op/bob``: + +* ``ADXSPD.bob`` - the main screen for controlling the detector. +* ``ADXSPD_Module.bob`` - per-module status and information. + +Auto-generated screens covering every record in the database templates are in +``xspdApp/op/bob/autogenerated``. + +Building +-------- + +ADXSPD depends on the following, in addition to EPICS base, asyn and ADCore: + +* `cpr`_, `nlohmann/json`_ and `magic_enum`_, which are included in ``xspdSupport``. + ``cpr`` requires libcurl; set ``CURL_EXTERNAL=YES`` in ``configure/CONFIG_SITE`` to use + the system libcurl. +* `ZeroMQ`_ (``libzmq``). Set ``ZMQ_LIB`` to use a non-system build. +* zlib and Blosc, which can be provided by ADSupport or the system. + +A C++17 compiler is required. + +Testing +------- + +Unit tests for the API are in ``xspdApp/tests`` and use GoogleTest and GoogleMock. Set +``BUILD_TESTS=YES`` in ``configure/CONFIG_SITE`` to build them, then run:: + + ./xspdApp/tests/O.linux-x86_64/TestADXSPD + +A simulator of the XSPD REST API and data port is provided in +``scripts/xspdSimulator.py``, and can be started with:: + + pixi run simulator diff --git a/pixi.lock b/pixi.lock index 0b9e3a0..8742c2c 100644 --- a/pixi.lock +++ b/pixi.lock @@ -146,6 +146,76 @@ environments: - pypi: git+https://github.com/jwlodek/epicsdbtools.git#9c1913b694cea222a7296662a9431400d9435e91 - pypi: https://files.pythonhosted.org/packages/c6/f3/ccadde3be7063fd2630908fe533ad8d9812d86c20996ba572196235ba78f/phoebusgen-3.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d0/fb/b39d311d90400166c73f91d2007704df0c08d8ee076f7ad63b2ef7e82c2d/rectangle_packer-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + docs: + channels: + - url: https://conda.anaconda.org/conda-forge/ + indexes: + - https://pypi.org/simple + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.7.0-py312h3f22e6b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312he9c40d5_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/charset-normalizer-3.5.2-h1114479_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_102.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.5-hd2095e1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h81df57d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.2.0-ha9f2e26_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpython-3.12.14-h0c77377_3_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.4-h13e7031_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.4-hcfc3c73_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.38-h280c20c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.4-h781a0a9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h5f976f7_3_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h1df4ec4_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/alabaster-1.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/babel-2.18.0-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.12.14-py312hd8ed1ab_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.22.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.20-pyh5ded981_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/imagesize-2.0.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.21.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.12.14-hd8ed1ab_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-9_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/roman-numerals-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-3.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-9.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx_rtd_theme-3.1.0-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-applehelp-2.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-devhelp-2.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-htmlhelp-2.1.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jquery-4.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jsmath-1.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-qthelp-2.0.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-2.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.8.0-pyhd8ed1ab_0.conda + - pypi: https://files.pythonhosted.org/packages/55/39/c9136af840b1dce6e835d5a665b0cea00edf8b022375d99ed75c79df0203/fspath-20230629-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/95/9c/c510029fc6ef33a6275cd2c5d3cecd6613dfd6aa401d57c54f1c18852ccf/setuptools-84.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/95/ad/793c51946ac0a67d5ae10b4cf7ba1620aaa8488464be97d85ed2e9552eb7/linuxdoc-20260504-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl packages: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda build_number: 20 @@ -159,6 +229,9 @@ packages: license: BSD-3-Clause license_family: BSD purls: [] + run_exports: + strong: + - _openmp_mutex >=4.5 size: 28948 timestamp: 1770939786096 - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.3.0-py312h90b7ffd_0.conda @@ -175,6 +248,19 @@ packages: - pkg:pypi/backports-zstd?source=hash-mapping size: 237970 timestamp: 1767045004512 +- conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.7.0-py312h3f22e6b_1.conda + sha256: b377a4f053c4e184b031253c702984194d10c98228004d7cf07c1eca86247d62 + md5: b0e9b44b494bb001c4d35b206022eba2 + depends: + - python + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - python_abi 3.12.* *_cp312 + - zstd >=1.5.7,<1.6.0a0 + license: BSD-3-Clause AND MIT AND EPL-2.0 + run_exports: {} + size: 241113 + timestamp: 1788491295568 - conda: https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda sha256: e7af5d1183b06a206192ff440e08db1c4e8b2ca1f8376ee45fb2f3a85d4ee45d md5: 2c2fae981fd2afd00812c92ac47d023d @@ -208,6 +294,35 @@ packages: - pkg:pypi/brotli?source=hash-mapping size: 368300 timestamp: 1764017300621 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312he9c40d5_4.conda + sha256: 6e4f440a7015d7d78120b3a3f90a4ec3d7bb6de7bc65458123c52433755db5f0 + md5: edb667e7ce56106424e8afab2dc0f0cb + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - libstdcxx >=15 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + constrains: + - libbrotlicommon 1.2.0 h39a168f_4 + license: MIT + license_family: MIT + run_exports: {} + size: 367657 + timestamp: 1788480501027 +- conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda + sha256: 1a0d382c515ebf55f8ee1f38c8b81bc95af5c2acc42ad53b66bc5df932032f96 + md5: e675fabcf81499adc7edf58124fb1e01 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: bzip2-1.0.6 + license_family: BSD + run_exports: + weak: + - bzip2 >=1.0.8,<2.0a0 + size: 257808 + timestamp: 1785906269155 - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda sha256: 0b75d45f0bba3e95dc693336fa51f40ea28c980131fec438afb7ce6118ed05f6 md5: d2ffd7602c02f2b316fd921d39876885 @@ -235,6 +350,20 @@ packages: - pkg:pypi/cffi?source=hash-mapping size: 295716 timestamp: 1761202958833 +- conda: https://conda.anaconda.org/conda-forge/linux-64/charset-normalizer-3.5.2-h1114479_0.conda + noarch: python + sha256: e4f04b45d68716fcc47c808aeacbf165f6f295fb91bd20b91aa38942b8d2cdc6 + md5: 78d5c6112c9320a1a797935cecfec7c2 + depends: + - python + - libgcc >=15 + - __glibc >=2.17,<3.0.a0 + - _python_abi3_support 1.* + - cpython >=3.9 + license: MIT + run_exports: {} + size: 217200 + timestamp: 1790766086110 - conda: https://conda.anaconda.org/conda-forge/linux-64/clang-format-21-21.1.8-default_h99862b1_3.conda sha256: 10d5448a9fd8c9413d28ed328df4765646c122d24ba28fea70a25671a123d09b md5: a6fcbe2c5df28f02d03a05faf532d908 @@ -307,6 +436,20 @@ packages: purls: [] size: 12723451 timestamp: 1773822285671 +- conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda + sha256: 9f07834f0c546ab14d885ce0366285f61f44e326c0edd1fc63b8294e113ae432 + md5: 72a381cbad04f24b1c2a43ef707f45b4 + depends: + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + license: MIT + license_family: MIT + run_exports: + weak: + - icu >=78.3,<79.0a0 + size: 14459115 + timestamp: 1786545741408 - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda sha256: 0960d06048a7185d3542d850986d807c6e37ca2e644342dd0c72feefcf26c2a4 md5: b38117a3c920364aff79f870c984b4a3 @@ -356,6 +499,19 @@ packages: purls: [] size: 728002 timestamp: 1774197446916 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_102.conda + sha256: 27d83f1188cd19bcb7754a078b3fa7f4cfb8527f8eb2fde54dd01fc529d1adec + md5: 449500f2c089da11c40f5c21312e3e07 + depends: + - __glibc >=2.17,<3.0.a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - binutils_impl_linux-64 2.46.1 + license: GPL-3.0-only + license_family: GPL + run_exports: {} + size: 745303 + timestamp: 1784214507189 - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-6_h4a7cf45_openblas.conda build_number: 6 sha256: 7bfe936dbb5db04820cf300a9cc1f5ee8d5302fc896c2d66e30f1ee2f20fbfd6 @@ -428,6 +584,19 @@ packages: purls: [] size: 76624 timestamp: 1774719175983 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.5-hd2095e1_0.conda + sha256: 6de73033a7c5f712c9c5a54124e4581e025691029835e5bceaf10a333a7e7bc0 + md5: 72cfd2b95b0540247220b42f58a148d7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + constrains: + - expat 2.8.5.* + license: MIT + license_family: MIT + run_exports: {} + size: 77893 + timestamp: 1790346247149 - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda sha256: 31f19b6a88ce40ebc0d5a992c131f57d919f73c0b92cd1617a5bec83f6e961e6 md5: a360c33a5abe61c07959e449fa1453eb @@ -439,6 +608,19 @@ packages: purls: [] size: 58592 timestamp: 1769456073053 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h81df57d_1.conda + sha256: c8c7583ef063bc3c430f1d48298e5ad24f796a35c22ed5b14183325825a61106 + md5: 6525a0b06aa4fd390795f0740636a9dd + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + license: MIT + license_family: MIT + run_exports: + weak: + - libffi >=3.7.0,<3.8.0a0 + size: 68004 + timestamp: 1787753412298 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_18.conda sha256: faf7d2017b4d718951e3a59d081eb09759152f93038479b768e3d612688f83f5 md5: 0aa00f03f9e39fb9876085dee11a85d4 @@ -453,6 +635,20 @@ packages: purls: [] size: 1041788 timestamp: 1771378212382 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.2.0-ha9f2e26_7.conda + sha256: e031634c3a928f9594eba868bc46ef5230d6571a364a0c63215b06c4563bfcea + md5: 735629b400b70eeab21a2ba8f5b1c2ce + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + constrains: + - libgcc-ng ==16.2.0=*_7 + - libgomp 16.2.0 he0feb66_7 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + run_exports: {} + size: 1058444 + timestamp: 1790303109576 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_18.conda sha256: e318a711400f536c81123e753d4c797a821021fb38970cebfb3f454126016893 md5: d5e96b1ed75ca01906b3d2469b4ce493 @@ -498,6 +694,18 @@ packages: purls: [] size: 603262 timestamp: 1771378117851 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_7.conda + sha256: 92a0bb350c7be6a4eedcb4e111c60aefd176018e6b1c009a6e025dbe9bbcd758 + md5: d904c7ddd95e026254b4b80808ee9e12 + depends: + - __glibc >=2.17,<3.0.a0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + run_exports: + strong: + - _openmp_mutex >=4.5 + size: 640566 + timestamp: 1790303046034 - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda sha256: c467851a7312765447155e071752d7bf9bf44d610a5687e32706f480aad2833f md5: 915f5995e94f60e9a4826e0b0920ee88 @@ -551,6 +759,20 @@ packages: purls: [] size: 113478 timestamp: 1775825492909 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda + sha256: 9787df8c22a59c9a70d3e5a10db9ad663485e75e9ccc3f09bd092cb7b95e0dab + md5: 1390b7c5ac0b1d8e447bc5efa6d3c8c2 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - xz 5.8.3.* + license: 0BSD + run_exports: + weak: + - liblzma >=5.8.3,<6.0a0 + size: 112995 + timestamp: 1786348617826 - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda sha256: 927fe72b054277cde6cb82597d0fcf6baf127dcbce2e0a9d8925a68f1265eef5 md5: d864d34357c3b65a4b731f78c0801dc4 @@ -560,6 +782,9 @@ packages: license: LGPL-2.1-only license_family: GPL purls: [] + run_exports: + weak: + - libnsl >=2.0.1,<2.1.0a0 size: 33731 timestamp: 1750274110928 - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.32-pthreads_h94d23a6_0.conda @@ -577,6 +802,18 @@ packages: purls: [] size: 5928890 timestamp: 1774471724897 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpython-3.12.14-h0c77377_3_cpython.conda + build_number: 3 + sha256: 6be16a4906d83eb8e9e04375d524f339f426a847cffd294f1ceb0611988dc17a + md5: d247b7632f09324c11f24b5270361385 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - libstdcxx >=15 + license: Python-2.0 + run_exports: {} + size: 8770625 + timestamp: 1788392466381 - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.21-h280c20c_3.conda sha256: 64e5c80cbce4680a2d25179949739a6def695d72c40ca28f010711764e372d97 md5: 7af961ef4aa2c1136e11dd43ded245ab @@ -599,6 +836,20 @@ packages: purls: [] size: 958864 timestamp: 1775753750179 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.4-h13e7031_1.conda + sha256: f20d70da54e5b31dd4a51fb1efeaafafd5ab6dd8d7ac9d1438eaa2526ac4ed3d + md5: e72bbec309c2b0f37823ee7d4fabfcd3 + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=78.3,<79.0a0 + - libgcc >=15 + - libzlib >=1.3.2,<2.0a0 + license: blessing + run_exports: + weak: + - libsqlite >=3.53.4,<4.0a0 + size: 974348 + timestamp: 1787051145557 - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_18.conda sha256: 78668020064fdaa27e9ab65cd2997e2c837b564ab26ce3bf0e58a2ce1a525c6e md5: 1b08cd684f34175e4514474793d44bcb @@ -612,6 +863,19 @@ packages: purls: [] size: 5852330 timestamp: 1771378262446 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_7.conda + sha256: fa7018298629fc429971fffd6ecba3d27a7b02e4e890d6a93ea01ffebe8cb745 + md5: 9b6707e0caca917fa6ea9ee473f37a5d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc 16.2.0 ha9f2e26_7 + constrains: + - libstdcxx-ng ==16.2.0=*_7 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + run_exports: {} + size: 6615436 + timestamp: 1790303132935 - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42-h5347b49_0.conda sha256: bc1b08c92626c91500fd9f26f2c797f3eb153b627d53e9c13cd167f1e12b2829 md5: 38ffe67b78c9d4de527be8315e5ada2c @@ -623,6 +887,19 @@ packages: purls: [] size: 40297 timestamp: 1775052476770 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.4-hcfc3c73_0.conda + sha256: f34b8d1de9a2bf62476d8608057d9b8a918d97444236dc53a47128f2fae79041 + md5: 54a948337e66706380ff24d6b5fde63f + depends: + - libgcc >=15 + - __glibc >=2.17,<3.0.a0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - libuuid >=2.42.4,<3.0a0 + size: 34442 + timestamp: 1790121449161 - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.51.0-hb03c661_1.conda sha256: c180f4124a889ac343fc59d15558e93667d894a966ec6fdb61da1604481be26b md5: 0f03292cc56bf91a077a134ea8747118 @@ -643,6 +920,18 @@ packages: purls: [] size: 100393 timestamp: 1702724383534 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.38-h280c20c_0.conda + sha256: f7e9292dd219a6435bbb1223da9586c3e70d66d169c5a92f08db3f2127df04e9 + md5: f7a7ff5a6ab331e037abd34f379a631d + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: LGPL-2.1-or-later + run_exports: + weak: + - libxcrypt >=4.4.38 + size: 101957 + timestamp: 1785887123445 - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.3-hca6bf5a_0.conda sha256: 3d44f737c5ae52d5af32682cc1530df433f401f8e58a7533926536244127572a md5: e79d2c2f24b027aa8d5ab1b1ba3061e7 @@ -688,6 +977,20 @@ packages: purls: [] size: 63629 timestamp: 1774072609062 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda + sha256: eb8a0db0aa570124f7d2a93d7c7f596e3390df5e047818d873baad32985fc736 + md5: 0de0122d9570a8ab637c6b73db268389 + depends: + - __glibc >=2.17,<3.0.a0 + constrains: + - zlib 1.3.2 *_3 + license: Zlib + license_family: Other + run_exports: + weak: + - libzlib >=1.3.2,<2.0a0 + size: 63713 + timestamp: 1785362952714 - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda sha256: 47326f811392a5fd3055f0f773036c392d26fdb32e4d8e7a8197eed951489346 md5: 9de5350a85c4a20c685259b889aa6393 @@ -714,6 +1017,7 @@ packages: license_family: BSD purls: - pkg:pypi/markupsafe?source=hash-mapping + run_exports: {} size: 26057 timestamp: 1772445297924 - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda @@ -726,6 +1030,18 @@ packages: purls: [] size: 891641 timestamp: 1738195959188 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda + sha256: 5d46557214ed184381dafe835b7c94a474a1c3b307a08a250b1ea4779b44ffb3 + md5: ee6c0cd80a60961a1f48aa3e0b91f986 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: X11 AND BSD-3-Clause + run_exports: + weak: + - ncurses >=6.6,<7.0a0 + size: 911196 + timestamp: 1786355078102 - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.3-py312h33ff503_0.conda sha256: 1aab7ba963affa572956b1bd8d239df52a9c7bc799c560f98bc658ab70224e10 md5: 5930ee8a175a242b4f001b1e9e72024f @@ -758,6 +1074,20 @@ packages: purls: [] size: 3167099 timestamp: 1775587756857 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.4-h781a0a9_0.conda + sha256: 4747b2d6a8336f52343bceb8a1ebf41a9e6e665b9d2e3f989972de53a310599e + md5: 16e3034a330cc625f2c685edec60cf4e + depends: + - __glibc >=2.17,<3.0.a0 + - ca-certificates + - libgcc >=15 + license: Apache-2.0 + license_family: Apache + run_exports: + weak: + - openssl >=3.6.4,<4.0a0 + size: 3202955 + timestamp: 1787698780103 - conda: https://conda.anaconda.org/conda-forge/linux-64/perl-5.32.1-7_hd590300_perl5.conda build_number: 7 sha256: 9ec32b6936b0e37bcb0ed34f22ec3116e75b3c0964f9f50ecea5f58734ed6ce9 @@ -813,6 +1143,39 @@ packages: purls: [] size: 31608571 timestamp: 1772730708989 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.14-h5f976f7_3_cpython.conda + build_number: 3 + sha256: 14c579b1016da04e4c9f1c5c857272d83ec447317d8c4074a07d59de3cef70ef + md5: 98be3cf76eca2e8871f907a03aed3b84 + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-64 >=2.36.1 + - libexpat >=2.8.1,<3.0a0 + - libffi >=3.7.0,<3.8.0a0 + - libgcc >=15 + - liblzma >=5.8.3,<6.0a0 + - libnsl >=2.0.1,<2.1.0a0 + - libpython 3.12.14 h0c77377_3_cpython + - libsqlite >=3.53.4,<4.0a0 + - libuuid >=2.42.3,<3.0a0 + - libxcrypt >=4.4.38 + - libzlib >=1.3.2,<2.0a0 + - ncurses >=6.6,<7.0a0 + - openssl >=3.5.8,<4.0a0 + - readline >=8.3,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + constrains: + - python_abi 3.12.* *_cp312 + license: Python-2.0 + run_exports: + weak: + - python_abi 3.12.* *_cp312 + noarch: + - python + size: 23044161 + timestamp: 1788392496306 - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_1.conda sha256: cb142bfd92f6e55749365ddc244294fa7b64db6d08c45b018ff1c658907bfcbf md5: 15878599a87992e44c059731771591cb @@ -858,6 +1221,20 @@ packages: purls: [] size: 345073 timestamp: 1765813471974 +- conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda + sha256: 01b3fe073a66e321970d09e04b388708f8cbdca5cdfbfcb7c9eeb470ad10383d + md5: 69c01c781e7c8190bbdaf79e3848c5ca + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - ncurses >=6.6,<7.0a0 + license: GPL-3.0-only + license_family: GPL + run_exports: + weak: + - readline >=8.3,<9.0a0 + size: 348899 + timestamp: 1787033801506 - conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.14.14-h40fa522_1.conda noarch: python sha256: 0c6c9825ff88195fd13936d63872213d6c88c1fe795d136881c0753c3037c5ff @@ -887,6 +1264,22 @@ packages: purls: [] size: 45829 timestamp: 1762948049098 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h1df4ec4_4.conda + build_number: 104 + sha256: a1a241d172c1ccab067ba245206dd048bc3c2d1b84504b53c9468e99adfc16a1 + md5: 676a2f9b1c4fcf57b70aa5c65f6c60d0 + depends: + - libgcc >=15 + - __glibc >=2.17,<3.0.a0 + - libzlib >=1.3.2,<2.0a0 + constrains: + - xorg-libx11 >=1.8.13,<2.0a0 + license: TCL + run_exports: + weak: + - tk >=8.6.13,<8.7.0a0 + size: 3566806 + timestamp: 1787272857910 - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda sha256: cafeec44494f842ffeca27e9c8b0c27ed714f93ac77ddadc6aaf726b5554ebac md5: cffd3bdd58090148f4cfcd831f4b26ab @@ -998,6 +1391,19 @@ packages: purls: [] size: 601375 timestamp: 1764777111296 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda + sha256: 47d682b9f6d6ec9eb1a6e6c3e75ea6273e899e78fb7fc59f81d39745009fbc60 + md5: aa459086047c0e5e27023ab19f8cb86a + depends: + - __glibc >=2.17,<3.0.a0 + - libzlib >=1.3.2,<2.0a0 + license: BSD-3-Clause + license_family: BSD + run_exports: + weak: + - zstd >=1.5.7,<1.6.0a0 + size: 601301 + timestamp: 1786599621503 - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda sha256: a3967b937b9abf0f2a99f3173fa4630293979bd1644709d89580e7c62a544661 md5: aaa2a381ccc56eac91d63b6c1240312f @@ -1009,6 +1415,27 @@ packages: purls: [] size: 8191 timestamp: 1744137672556 +- conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_3.conda + sha256: 2a7204314663eeda5dec482a956f0e2eaf289bd5b9953eaaaad0e81aa64638f2 + md5: 3845f3d75991bae0fb90884662f4327c + depends: + - cpython + - python-gil + license: MIT + license_family: MIT + run_exports: {} + size: 8144 + timestamp: 1784221492234 +- conda: https://conda.anaconda.org/conda-forge/noarch/alabaster-1.0.0-pyhd8ed1ab_1.conda + sha256: 6c4456a138919dae9edd3ac1a74b6fbe5fd66c05675f54df2f8ab8c8d0cc6cea + md5: 1fd9696649f65fd6611fcdb4ffec738a + depends: + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 18684 + timestamp: 1733750512696 - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-doc-0.0.4-pyhcf101f3_0.conda sha256: cc9fbc50d4ee7ee04e49ee119243e6f1765750f0fd0b4d270d5ef35461b643b1 md5: 52be5139047efadaeeb19c6a5103f92a @@ -1052,6 +1479,19 @@ packages: - pkg:pypi/anyio?source=hash-mapping size: 146764 timestamp: 1774359453364 +- conda: https://conda.anaconda.org/conda-forge/noarch/babel-2.18.0-pyhcf101f3_1.conda + sha256: a14a9ad02101aab25570543a59c5193043b73dc311a25650134ed9e6cb691770 + md5: f1976ce927373500cc19d3c0b2c85177 + depends: + - python >=3.10 + - python + constrains: + - pytz >=2015.7 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 7684321 + timestamp: 1772555330347 - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda sha256: 67cc7101b36421c5913a1687ef1b99f85b5d6868da3abbf6ec1a4181e79782fc md5: 4492fd26db29495f0ba23f146cd5638d @@ -1061,6 +1501,15 @@ packages: purls: [] size: 147413 timestamp: 1772006283803 +- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + sha256: 0a0544cf95f64394fe4959286f5c71f5444ad58feb0602e53becb27448d24da6 + md5: 0f51e2391ade309db462a55611263e9c + depends: + - __unix + license: ISC + run_exports: {} + size: 131780 + timestamp: 1784754889428 - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.2.25-pyhd8ed1ab_0.conda sha256: a6b118fd1ed6099dc4fc03f9c492b88882a780fadaef4ed4f93dc70757713656 md5: 765c4d97e877cdbbb88ff33152b86125 @@ -1071,6 +1520,15 @@ packages: - pkg:pypi/certifi?source=hash-mapping size: 151445 timestamp: 1772001170301 +- conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.7.22-pyhd8ed1ab_0.conda + sha256: fb167de4388e64e52aa3907ed099afab944c1fa6e5f74b281a312dae1bcf7f3b + md5: 37e13edbe3b48f1095a9d085ef9cd83b + depends: + - python >=3.10 + license: ISC + run_exports: {} + size: 137015 + timestamp: 1784717699092 - conda: https://conda.anaconda.org/conda-forge/noarch/cfgv-3.5.0-pyhd8ed1ab_0.conda sha256: aa589352e61bb221351a79e5946d56916e3c595783994884accdb3b97fe9d449 md5: 381bd45fb7aa032691f3063aff47e3a1 @@ -1106,6 +1564,16 @@ packages: - pkg:pypi/click?source=hash-mapping size: 98253 timestamp: 1775578217828 +- conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + sha256: ab29d57dc70786c1269633ba3dff20288b81664d3ff8d21af995742e2bb03287 + md5: 962b9857ee8e7018c22f2776ffa0b2d7 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 27011 + timestamp: 1733218222191 - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.12.13-py312hd8ed1ab_0.conda noarch: generic sha256: d3e9bbd7340199527f28bbacf947702368f31de60c433a16446767d3c6aaf6fe @@ -1117,6 +1585,17 @@ packages: purls: [] size: 46463 timestamp: 1772728929620 +- conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.12.14-py312hd8ed1ab_3.conda + noarch: generic + sha256: 1052869d599008850098c033f9568a2e28bb31238705969719be69a87474cc93 + md5: adee9213d9a98d7f8b5627209c8b4ae0 + depends: + - python >=3.12,<3.13.0a0 + - python_abi * *_cp312 + license: Python-2.0 + run_exports: {} + size: 47172 + timestamp: 1788391292962 - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda sha256: 6d977f0b2fc24fee21a9554389ab83070db341af6d6f09285360b2e09ef8b26e md5: 003b8ba0a94e2f1e117d0bd46aebc901 @@ -1149,6 +1628,15 @@ packages: - pkg:pypi/dnspython?source=hash-mapping size: 196500 timestamp: 1757292856922 +- conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.22.4-pyhd8ed1ab_0.conda + sha256: 0d605569a77350fb681f9ed8d357cc71649b59a304099dc9d09fbeec5e84a65e + md5: d6bd3cd217e62bbd7efe67ff224cd667 + depends: + - python >=3.10 + license: CC-PDDC AND BSD-3-Clause AND BSD-2-Clause AND ZPL-2.1 + run_exports: {} + size: 438002 + timestamp: 1766092633160 - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.3.0-pyhd8ed1ab_0.conda sha256: c37320864c35ef996b0e02e289df6ee89582d6c8e233e18dc9983375803c46bb md5: 3bc0ac31178387e8ed34094d9481bfe8 @@ -1280,6 +1768,19 @@ packages: - pkg:pypi/h2?source=hash-mapping size: 95967 timestamp: 1756364871835 +- conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.4.1-pyhcf101f3_0.conda + sha256: 307dd6ec90140c3cf4171071b0e5e870abec314f4565c1edd5bc433e942cdcc0 + md5: e652ac7756069c456d0da2a922cd7df5 + depends: + - python >=3.10 + - hyperframe >=6.1,<7 + - hpack >=4.2,<5 + - python + license: MIT + license_family: MIT + run_exports: {} + size: 100789 + timestamp: 1785796355216 - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda sha256: 6ad78a180576c706aabeb5b4c8ceb97c0cb25f1e112d76495bff23e3779948ba md5: 0a802cb9888dd14eeefc611f05c40b6e @@ -1291,6 +1792,16 @@ packages: - pkg:pypi/hpack?source=hash-mapping size: 30731 timestamp: 1737618390337 +- conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.2.0-pyhd8ed1ab_0.conda + sha256: fdcea5d7cb314485d3907192ef024c704311548c5b0cbeb390cd1951051e29d2 + md5: b395909221b9bd1df066e5930e18855b + depends: + - python >=3.10 + license: MIT + license_family: MIT + run_exports: {} + size: 32884 + timestamp: 1782283986153 - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda sha256: 04d49cb3c42714ce533a8553986e1642d0549a05dc5cc48e0d43ff5be6679a5b md5: 4f14640d58e2cc0aa0819d9d8ba125bb @@ -1332,6 +1843,7 @@ packages: license_family: MIT purls: - pkg:pypi/hyperframe?source=hash-mapping + run_exports: {} size: 17397 timestamp: 1737618427549 - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.19-pyhd8ed1ab_0.conda @@ -1357,6 +1869,27 @@ packages: - pkg:pypi/idna?source=hash-mapping size: 50721 timestamp: 1760286526795 +- conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.20-pyh5ded981_0.conda + sha256: e044b1ec829e2e24f971fa3f3408eb26f1db836e521a58f08b0fd9a03fee47be + md5: ee73be987f9de9ea5c2fdaeefa7b6491 + depends: + - python >=3.11 + - python + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 178572 + timestamp: 1790020377150 +- conda: https://conda.anaconda.org/conda-forge/noarch/imagesize-2.0.1-pyhd8ed1ab_0.conda + sha256: 4e787f9ccc31053ccea56d98ecd284a4f788988a3f9c4627db72fdd0b127529b + md5: ebc56022e4ef7e74a829be94c53797ca + depends: + - python >=3.10 + license: MIT + license_family: MIT + run_exports: {} + size: 21467 + timestamp: 1787649248672 - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.8.0-pyhcf101f3_0.conda sha256: 82ab2a0d91ca1e7e63ab6a4939356667ef683905dea631bc2121aa534d347b16 md5: 080594bf4493e6bae2607e65390c520a @@ -1381,6 +1914,7 @@ packages: license_family: BSD purls: - pkg:pypi/jinja2?source=hash-mapping + run_exports: {} size: 120685 timestamp: 1764517220861 - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda @@ -1430,6 +1964,17 @@ packages: - pkg:pypi/packaging?source=hash-mapping size: 89360 timestamp: 1776209387231 +- conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.3-pyhc364b38_0.conda + sha256: c432626b16768b8dab228bfb706f7060c2d462a21c516d240f68f2f902b5a044 + md5: 936687ed80f295a1f5dbcf8bd34c252c + depends: + - python >=3.9 + - python + license: Apache-2.0 + license_family: APACHE + run_exports: {} + size: 116363 + timestamp: 1785888127370 - conda: https://conda.anaconda.org/conda-forge/noarch/pip-25.3-pyh8b19718_0.conda sha256: b67692da1c0084516ac1c9ada4d55eaf3c5891b54980f30f3f444541c2706f1e md5: c55515ca43c6444d2572e0f0d93cb6b9 @@ -1545,6 +2090,17 @@ packages: - pkg:pypi/pygments?source=hash-mapping size: 893031 timestamp: 1774796815820 +- conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.21.0-pyhcf101f3_0.conda + sha256: f5f015ff1bc3e1b7fc08eee096b1865234189ae0b82bebdb86a5369116e42fa0 + md5: 2882dee445dfa45b0c5afce3ffb7730a + depends: + - python >=3.10 + - python + license: BSD-2-Clause + license_family: BSD + run_exports: {} + size: 959376 + timestamp: 1786995678795 - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda sha256: ba3b032fa52709ce0d9fd388f63d330a026754587a2f461117cac9ab73d8d0d8 md5: 461219d1a5bd61342293efa2c0c90eac @@ -1555,6 +2111,7 @@ packages: license_family: BSD purls: - pkg:pypi/pysocks?source=hash-mapping + run_exports: {} size: 21085 timestamp: 1733217331982 - conda: https://conda.anaconda.org/conda-forge/noarch/python-discovery-1.2.2-pyhcf101f3_0.conda @@ -1592,6 +2149,16 @@ packages: purls: [] size: 46449 timestamp: 1772728979370 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.12.14-hd8ed1ab_3.conda + sha256: 7096d98d7dd0cdb9256c5714d7228a8ce7aca3bd014304ea5c1673c259df72ef + md5: 7fafcd204183cfa7c1ae4ad9c2d8d414 + depends: + - cpython 3.12.14.* + - python_abi * *_cp312 + license: Python-2.0 + run_exports: {} + size: 47142 + timestamp: 1788391303150 - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.26-pyhcf101f3_0.conda sha256: 2c683f35fac0e6d5c314872bbe89f1ff9100e93a6ed3c2c249b5ba7f95baa139 md5: 8fe038d7eacf6aa4e0885f6ae9c560e5 @@ -1615,6 +2182,17 @@ packages: purls: [] size: 6958 timestamp: 1752805918820 +- conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-9_cp312.conda + build_number: 9 + sha256: ee9c2922e07afc85fc82d5fa82c9ac2c79da3be3283a5e17bf2f2ae38dbd02fb + md5: 4c32076993e6270825441d059ab5c18b + constrains: + - python 3.12.* *_cpython + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 6751 + timestamp: 1788302823694 - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.33.1-pyhcf101f3_0.conda sha256: c0249bc4bf4c0e8e06d0e7b4d117a5d593cc4ab2144d5006d6d47c83cb0af18e md5: 10afbb4dbf06ff959ad25a92ccee6e59 @@ -1633,6 +2211,23 @@ packages: - pkg:pypi/requests?source=hash-mapping size: 63712 timestamp: 1774894783063 +- conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda + sha256: 1715246b19c9f85ee022933b4845f2fc14ac9184981b7b7d9b728bec8e9588da + md5: 4a85203c1d80c1059086ae860836ffb9 + depends: + - python >=3.10 + - certifi >=2023.5.7 + - charset-normalizer >=2,<4 + - idna >=2.5,<4 + - urllib3 >=1.26,<3 + - python + constrains: + - chardet >=3.0.2,<8 + license: Apache-2.0 + license_family: APACHE + run_exports: {} + size: 68709 + timestamp: 1778851103479 - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda sha256: 3d6ba2c0fcdac3196ba2f0615b4104e532525ffa1335b50a2878be5ff488814a md5: 0242025a3c804966bf71aa04eee82f66 @@ -1663,6 +2258,15 @@ packages: - pkg:pypi/rich-toolkit?source=hash-mapping size: 32484 timestamp: 1771977622605 +- conda: https://conda.anaconda.org/conda-forge/noarch/roman-numerals-4.1.0-pyhd8ed1ab_0.conda + sha256: 30f3c04fcfb64c44d821d392a4a0b8915650dbd900c8befc20ade8fde8ec6aa2 + md5: 0dc48b4b570931adc8641e55c6c17fe4 + depends: + - python >=3.10 + license: 0BSD OR CC0-1.0 + run_exports: {} + size: 13814 + timestamp: 1766003022813 - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda sha256: 82088a6e4daa33329a30bc26dc19a98c7c1d3f05c0f73ce9845d4eab4924e9e1 md5: 8e194e7b992f99a5015edbd4ebd38efd @@ -1696,6 +2300,132 @@ packages: - pkg:pypi/sniffio?source=hash-mapping size: 15698 timestamp: 1762941572482 +- conda: https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-3.1.1-pyhd8ed1ab_0.conda + sha256: ad89284ea94821c20ff87e64b948e4afc690cf5202d14c009355b0594cf23aea + md5: 46b6abe31482f6bca064b965696ae807 + depends: + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + run_exports: {} + size: 74456 + timestamp: 1780468201547 +- conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-9.1.0-pyhd8ed1ab_0.conda + sha256: 035ca4b17afca3d53650380dd94c564555b7ec2b4f8818111f98c15c7a991b7b + md5: aabfbc2813712b71ba8beb217a978498 + depends: + - alabaster >=0.7.14 + - babel >=2.13 + - colorama >=0.4.6 + - docutils >=0.21,<0.23 + - imagesize >=1.3 + - jinja2 >=3.1 + - packaging >=23.0 + - pygments >=2.17 + - python >=3.12 + - requests >=2.30.0 + - roman-numerals >=1.0.0 + - snowballstemmer >=2.2 + - sphinxcontrib-applehelp >=1.0.7 + - sphinxcontrib-devhelp >=1.0.6 + - sphinxcontrib-htmlhelp >=2.0.6 + - sphinxcontrib-jsmath >=1.0.1 + - sphinxcontrib-qthelp >=1.0.6 + - sphinxcontrib-serializinghtml >=1.1.9 + license: BSD-2-Clause + license_family: BSD + run_exports: {} + size: 1584836 + timestamp: 1767271941650 +- conda: https://conda.anaconda.org/conda-forge/noarch/sphinx_rtd_theme-3.1.0-pyhcf101f3_2.conda + sha256: 3581a8335a0da4de41f4344c4f1113a11fcbbb7933577ace813602e263842b02 + md5: 787507b1d0f5c00c186b2f66adacc3ab + depends: + - python >=3.10 + - sphinx >=6,<10 + - docutils >0.18,<0.23 + - sphinxcontrib-jquery >=4,<5 + - python + license: MIT + license_family: MIT + run_exports: {} + size: 6616185 + timestamp: 1785314437148 +- conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-applehelp-2.0.0-pyhd8ed1ab_1.conda + sha256: d7433a344a9ad32a680b881c81b0034bc61618d12c39dd6e3309abeffa9577ba + md5: 16e3f039c0aa6446513e94ab18a8784b + depends: + - python >=3.9 + - sphinx >=5 + license: BSD-2-Clause + license_family: BSD + run_exports: {} + size: 29752 + timestamp: 1733754216334 +- conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-devhelp-2.0.0-pyhd8ed1ab_1.conda + sha256: 55d5076005d20b84b20bee7844e686b7e60eb9f683af04492e598a622b12d53d + md5: 910f28a05c178feba832f842155cbfff + depends: + - python >=3.9 + - sphinx >=5 + license: BSD-2-Clause + license_family: BSD + run_exports: {} + size: 24536 + timestamp: 1733754232002 +- conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-htmlhelp-2.1.0-pyhd8ed1ab_1.conda + sha256: c1492c0262ccf16694bdcd3bb62aa4627878ea8782d5cd3876614ffeb62b3996 + md5: e9fb3fe8a5b758b4aff187d434f94f03 + depends: + - python >=3.9 + - sphinx >=5 + license: BSD-2-Clause + license_family: BSD + run_exports: {} + size: 32895 + timestamp: 1733754385092 +- conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jquery-4.1-pyhd8ed1ab_1.conda + sha256: 69c08d18663b57ebc8e4187c64c8d29b10996bb465a515cd288d87b6f2f52a5e + md5: 403185829255321ea427333f7773dd1f + depends: + - python >=3.9 + - sphinx >=1.8 + license: 0BSD AND MIT + run_exports: {} + size: 112964 + timestamp: 1734344603903 +- conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jsmath-1.0.1-pyhd8ed1ab_1.conda + sha256: 578bef5ec630e5b2b8810d898bbbf79b9ae66d49b7938bcc3efc364e679f2a62 + md5: fa839b5ff59e192f411ccc7dae6588bb + depends: + - python >=3.9 + license: BSD-2-Clause + license_family: BSD + run_exports: {} + size: 10462 + timestamp: 1733753857224 +- conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-qthelp-2.0.0-pyhd8ed1ab_1.conda + sha256: c664fefae4acdb5fae973bdde25836faf451f41d04342b64a358f9a7753c92ca + md5: 00534ebcc0375929b45c3039b5ba7636 + depends: + - python >=3.9 + - sphinx >=5 + license: BSD-2-Clause + license_family: BSD + run_exports: {} + size: 26959 + timestamp: 1733753505008 +- conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-2.0.0-pyhd8ed1ab_0.conda + sha256: 20b49741065fd7d3fabf98caf6d19b6436badb06b6d41f66b58f1fc2b52f37a1 + md5: f77df1fcf9af03b7287342638befca77 + depends: + - python >=3.10 + - sphinx >=5 + license: BSD-2-Clause + license_family: BSD + run_exports: {} + size: 30640 + timestamp: 1781260357443 - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-1.0.0-pyhcf101f3_0.conda sha256: 1a1dc376e95491f4b2003f4428e6f7caf4a3de0ef9869248b29dcc9704c34b39 md5: 8fbd5b6879350f1b5303c1a652d4b781 @@ -1779,6 +2509,13 @@ packages: purls: [] size: 119135 timestamp: 1767016325805 +- conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + sha256: b928c30ddcb0e3f544c6eade8352737e6e610e263276b90232db6a578ef899d8 + md5: fcb489df604d100968b737f2cb6076c6 + license: LicenseRef-Public-Domain + run_exports: {} + size: 118849 + timestamp: 1784250406640 - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.6.3-pyhd8ed1ab_0.conda sha256: af641ca7ab0c64525a96fd9ad3081b0f5bcf5d1cbb091afb3f6ed5a9eee6111a md5: 9272daa869e03efe68833e3dc7a02130 @@ -1794,6 +2531,20 @@ packages: - pkg:pypi/urllib3?source=hash-mapping size: 103172 timestamp: 1767817860341 +- conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.8.0-pyhd8ed1ab_0.conda + sha256: c5511c190ab55168ca412f3c54592fbf677ae6250784cb72b227a7cf3f538f7d + md5: 407a3d3778570bae8e8fcf554e803e43 + depends: + - backports.zstd >=1.0.0 + - brotli-python >=1.2.0 + - h2 >=4,<5 + - pysocks >=1.5.6,<2.0,!=1.5.7 + - python >=3.11 + license: MIT + license_family: MIT + run_exports: {} + size: 107485 + timestamp: 1789566710244 - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.45.0-pyhc90fa1f_0.conda sha256: 9e926875ad2c57d70cb09a9840e4976a9198526cd7cac94e4ee89d94c910f08e md5: 2cdce4f4118633582c76dd2f596e8358 @@ -1900,6 +2651,83 @@ packages: - types-mock ; extra == 'dev' - import-linter ; extra == 'dev' requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/55/39/c9136af840b1dce6e835d5a665b0cea00edf8b022375d99ed75c79df0203/fspath-20230629-py2.py3-none-any.whl + name: fspath + version: '20230629' + sha256: f64b560c7e3ed77da61b55f35764e03e2fb9dd8955c5c140535dbd07711693af + requires_dist: + - six +- pypi: https://files.pythonhosted.org/packages/95/9c/c510029fc6ef33a6275cd2c5d3cecd6613dfd6aa401d57c54f1c18852ccf/setuptools-84.0.0-py3-none-any.whl + name: setuptools + version: 84.0.0 + sha256: 51a52592b3b99e102b609654876bd65f19f999935166d1352678931132b0c670 + requires_dist: + - pytest>=6,!=8.1.* ; extra == 'test' + - virtualenv>=13.0.0 ; extra == 'test' + - wheel>=0.44.0 ; extra == 'test' + - pip>=19.1 ; extra == 'test' + - packaging>=24.2 ; extra == 'test' + - jaraco-envs>=2.2 ; extra == 'test' + - pytest-xdist>=3 ; extra == 'test' + - jaraco-path>=3.7.2 ; extra == 'test' + - build[virtualenv]>=1.0.3 ; extra == 'test' + - filelock>=3.4.0 ; extra == 'test' + - ini2toml[lite]>=0.14 ; extra == 'test' + - tomli-w>=1.0.0 ; extra == 'test' + - pytest-timeout ; extra == 'test' + - pytest-perf ; sys_platform != 'cygwin' and extra == 'test' + - jaraco-develop>=7.21 ; python_full_version >= '3.9' and sys_platform != 'cygwin' and extra == 'test' + - pytest-home>=0.5 ; extra == 'test' + - pytest-subprocess ; extra == 'test' + - pyproject-hooks!=1.1 ; extra == 'test' + - jaraco-test>=5.5 ; extra == 'test' + - sphinx>=3.5 ; extra == 'doc' + - jaraco-packaging>=9.3 ; extra == 'doc' + - rst-linker>=1.9 ; extra == 'doc' + - furo ; extra == 'doc' + - sphinx-lint ; extra == 'doc' + - jaraco-tidelift>=1.4 ; extra == 'doc' + - pygments-github-lexers==0.0.5 ; extra == 'doc' + - sphinx-favicon ; extra == 'doc' + - sphinx-inline-tabs ; extra == 'doc' + - sphinx-reredirects ; extra == 'doc' + - sphinxcontrib-towncrier ; extra == 'doc' + - sphinx-notfound-page>=1,<2 ; extra == 'doc' + - pyproject-hooks!=1.1 ; extra == 'doc' + - towncrier<24.7 ; extra == 'doc' + - packaging>=24.2 ; extra == 'core' + - more-itertools>=8.8 ; extra == 'core' + - jaraco-text>=3.7 ; extra == 'core' + - importlib-metadata>=6 ; python_full_version < '3.10' and extra == 'core' + - tomli>=2.0.1 ; python_full_version < '3.11' and extra == 'core' + - wheel>=0.43.0 ; extra == 'core' + - jaraco-functools>=4 ; extra == 'core' + - more-itertools ; extra == 'core' + - pytest-checkdocs>=2.14 ; extra == 'check' + - pytest-ruff>=0.2.1 ; sys_platform != 'cygwin' and extra == 'check' + - ruff>=0.13.0 ; sys_platform != 'cygwin' and extra == 'check' + - pytest-cov ; extra == 'cover' + - pytest-enabler>=3.4 ; extra == 'enabler' + - pytest-mypy>=1.0.1 ; platform_python_implementation != 'PyPy' and extra == 'type' + - mypy==1.18.* ; extra == 'type' + - importlib-metadata>=7.0.2 ; python_full_version < '3.10' and extra == 'type' + - jaraco-develop>=7.21 ; sys_platform != 'cygwin' and extra == 'type' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/95/ad/793c51946ac0a67d5ae10b4cf7ba1620aaa8488464be97d85ed2e9552eb7/linuxdoc-20260504-py3-none-any.whl + name: linuxdoc + version: '20260504' + sha256: cca33f1499a96ab30fdcab5296bfd28b0ae2371ae56e5dfe929f18b9a7dc7a2f + requires_dist: + - docutils + - fspath + - setuptools + - sphinx + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + name: six + version: 1.17.0 + sha256: 4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 + requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*' - pypi: https://files.pythonhosted.org/packages/c6/f3/ccadde3be7063fd2630908fe533ad8d9812d86c20996ba572196235ba78f/phoebusgen-3.2.0-py3-none-any.whl name: phoebusgen version: 3.2.0 diff --git a/pixi.toml b/pixi.toml index bc2ebb6..2d9ff19 100644 --- a/pixi.toml +++ b/pixi.toml @@ -34,3 +34,17 @@ fastapi = ">=0.136.0,<0.137" [pypi-dependencies] epicsdbtools = { git = "https://github.com/jwlodek/epicsdbtools.git" } epicsdb2bob = { git = "https://github.com/NSLS2/epicsdb2bob.git" } + +[feature.docs.dependencies] +python = ">=3.12,<3.13" +sphinx = ">=9.1.0,<10" +sphinx_rtd_theme = ">=3.1.0,<4" + +[feature.docs.pypi-dependencies] +linuxdoc = ">=20260504" + +[feature.docs.tasks] +build-docs = "sphinx-build -W --keep-going -c scripts/sphinx -b html docs build/docs/html" + +[environments] +docs = { features = ["docs"], no-default-feature = true } diff --git a/scripts/sphinx/conf.py b/scripts/sphinx/conf.py new file mode 100644 index 0000000..056b717 --- /dev/null +++ b/scripts/sphinx/conf.py @@ -0,0 +1,16 @@ +# Standalone Sphinx config for previewing the ADXSPD docs outside of the areaDetector docs build. +# Kept out of docs/ so it isn't merged into the areaDetector docs tree. + +project = "ADXSPD" +author = "Jakub Wlodek" +copyright = "2026, Brookhaven National Laboratory" + +extensions = ["linuxdoc.rstFlatTable"] + +root_doc = "ADXSPD/ADXSPD" +source_suffix = {".rst": "restructuredtext"} +exclude_patterns = ["_build"] + +pygments_style = "sphinx" +html_theme = "sphinx_rtd_theme" +html_title = "ADXSPD"