diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..c0567ff --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,13 @@ +name: Build image +on: + push: + tags: + - 'v*' + pull_request: + +jobs: + build_and_push: + permissions: + packages: write + contents: read + uses: cnpem/epics-in-docker/.github/workflows/ioc-images.yml@main diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..8a8c88b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "docker"] + path = docker + url = https://github.com/cnpem/epics-in-docker.git + branch = release diff --git a/EigerApp/src/Makefile b/EigerApp/src/Makefile index 8a297a8..3af4133 100644 --- a/EigerApp/src/Makefile +++ b/EigerApp/src/Makefile @@ -9,19 +9,16 @@ include $(TOP)/configure/CONFIG # Build the IOC application PROD_IOC = Eiger -# Eiger.dbd will be created and installed DBD += Eiger.dbd # Eiger.dbd will be made up from these files: Eiger_DBD += base.dbd +Eiger_DBD += eigerDetectorSupport.dbd +Eiger_LIBS += eigerDetector +Eiger_LIBS += dectrisCompression +Eiger_LIBS += frozen +Eiger_LIBS += tinyCBOR -# Include dbd files from all support applications: -#Eiger_DBD += xxx.dbd - -# Add all the support libraries needed by this IOC -#Eiger_LIBS += xxx - -# Eiger_registerRecordDeviceDriver.cpp derives from Eiger.dbd Eiger_SRCS += Eiger_registerRecordDeviceDriver.cpp # Build the main IOC entry point on workstation OSs. @@ -30,6 +27,9 @@ Eiger_SRCS_vxWorks += -nil- # Add support from base/src/vxWorks if needed #Eiger_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary +# Load libraries from common areaDetector plugins +DBD_NAME = $(PROD_IOC) +include $(ADCORE)/ADApp/commonDriverMakefile # Finally link to the EPICS Base libraries Eiger_LIBS += $(EPICS_BASE_IOC_LIBS) @@ -39,4 +39,3 @@ Eiger_LIBS += $(EPICS_BASE_IOC_LIBS) include $(TOP)/configure/RULES #---------------------------------------- # ADD RULES AFTER THIS LINE - diff --git a/README.md b/README.md new file mode 100644 index 0000000..fe9f557 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# EPICS IOC for ADEiger-based detectors + +This repository contains the EPICS Input/Output Controller (IOC) used at LNLS +based on [ADEiger](https://github.com/areaDetector/ADEiger) support module. + +## Running the IOC + +You can use the following command to run it in the background using the `lnls-run.sh` script from +[epics-in-docker](https://github.com/cnpem/epics-in-docker). + +``` +docker compose up -d +``` + +## Building the IOC image + +You can build the IOC with the following command: + +```bash +TAG=latest docker compose build +``` diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..aedd8bc --- /dev/null +++ b/compose.yml @@ -0,0 +1,13 @@ +services: + ioc: + image: ghcr.io/cnpem/ad-eiger-epics-ioc:${TAG} + build: + context: ./ + dockerfile: docker/Dockerfile + target: dynamic-link + labels: + org.opencontainers.image.source: https://github.com/cnpem/ad-eiger-epics-ioc + args: + REPONAME: ad-eiger-epics-ioc + RUNDIR: /opt/ad-eiger-epics-ioc/iocBoot/iocEiger + RUNTIME_PACKAGES: libxml2 libtiff6 libzmq5 diff --git a/configure/CONFIG_SITE b/configure/CONFIG_SITE index 212485e..c11a8c0 100644 --- a/configure/CONFIG_SITE +++ b/configure/CONFIG_SITE @@ -38,6 +38,7 @@ CHECK_RELEASE = YES # These allow developers to override the CONFIG_SITE variable # settings without having to modify the configure/CONFIG_SITE # file itself. +include $(AREA_DETECTOR)/configure/CONFIG_SITE -include $(TOP)/../CONFIG_SITE.local -include $(TOP)/configure/CONFIG_SITE.local diff --git a/configure/RELEASE b/configure/RELEASE index 6418fe3..e9c75e9 100644 --- a/configure/RELEASE +++ b/configure/RELEASE @@ -22,14 +22,14 @@ # the CONFIG_SITE file. # Variables and paths to dependent modules: -#MODULES = /path/to/modules -#MYMODULE = $(MODULES)/my-module +ASYN= -# If using the sequencer, point SNCSEQ at its top directory: -#SNCSEQ = $(MODULES)/seq-ver +AREA_DETECTOR= +ADCORE= +ADEIGER= # EPICS_BASE should appear last so earlier modules can override stuff: -EPICS_BASE = /home/ABTLUS/gustavo.reis/EPICS/epics-base +EPICS_BASE= # Set RULES here if you want to use build rules from somewhere # other than EPICS_BASE: diff --git a/docker b/docker new file mode 160000 index 0000000..fb5c4eb --- /dev/null +++ b/docker @@ -0,0 +1 @@ +Subproject commit fb5c4ebd343128ce27ed8362f5132b448eef4ad5 diff --git a/iocBoot/iocEiger/device.cmd b/iocBoot/iocEiger/device.cmd new file mode 100755 index 0000000..644a839 --- /dev/null +++ b/iocBoot/iocEiger/device.cmd @@ -0,0 +1,27 @@ +# Standard configuration for Pilatus4 detector based on ADEiger. +# +# Configuration parameters: +# +# $(PREFIX) +# Prefix for all PVs. + +# Register all support components +dbLoadDatabase("$(TOP)/dbd/Eiger.dbd") +Eiger_registerRecordDeviceDriver(pdbbase) + +# Define detector driver port +epicsEnvSet("PORT", "PIL4") +# Include ADCore to path for loading its databases relatively +epicsEnvSet("EPICS_DB_INCLUDE_PATH", "$(ADCORE)/db:$(ADEIGER)/db") + +# Configure connection to detector +eigerDetectorConfig("$(PORT)", "$(IP_ADDRESS)", 0, 0) + +# Load device general records from ADEiger +dbLoadRecords("$(ADEIGER)/db/pilatus4.template", "P=$(PREFIX), R=cam1:, PORT=$(PORT), ADDR=0, TIMEOUT=1") + +# Trace error and warning messages +asynSetTraceMask("$(PORT)", 0, ERROR | WARNING) + +# Use larger callback queue to account for the high number of PVs +callbackSetQueueSize(5000) diff --git a/iocBoot/iocEiger/plugins.cmd b/iocBoot/iocEiger/plugins.cmd new file mode 100755 index 0000000..208d3d6 --- /dev/null +++ b/iocBoot/iocEiger/plugins.cmd @@ -0,0 +1,59 @@ +# Area Detector plugin configuration +# +# The following parameters must be defined before loading this configuration: +# +# $(PREFIX) +# Prefix for all records. +# +# $(PORT) +# The port name for the detector. +# +# $(MAX_IMAGE_PIXELS) +# The maximum number of pixels to be sent through channel access through +# NDPluginStdArrays. +# +# Optional parameters: +# +# $(IMAGE_ASYN_TYPE) +# Value of the DTYP field of the waveform record which defines the underlying +# asyn datatype. +# This should be consistent with IMAGE_WAVEFORM_TYPE. +# +# $(IMAGE_WAVEFORM_TYPE) +# Data type of the waveform values themselves. +# This should be consistent with IMAGE_ASYN_TYPE. +# +# $(QSIZE) +# The queue size for all plugins. +# +# $(QSIZE_HDF5) +# Queue size for HDF5 plugin. +# +# $(MAX_THREADS) +# The maximum number of threads for plugins which can run in multiple threads. + +epicsEnvSet("IMAGE_ASYN_TYPE", "$(IMAGE_ASYN_TYPE=Int32)") +epicsEnvSet("IMAGE_WAVEFORM_TYPE", "$(IMAGE_WAVEFORM_TYPE=LONG)") +epicsEnvSet("QSIZE", "$(QSIZE=20)") +epicsEnvSet("QSIZE_HDF5", "$(QSIZE_HDF5=1000)") +epicsEnvSet("MAX_THREADS", "$(MAX_THREADS=4)") + +# Create Codec plugins +NDCodecConfigure("CODEC1", $(QSIZE), 0, "$(PORT)", 0, 0, 0, 0, 0, $(MAX_THREADS)) +dbLoadRecords("NDCodec.template", "P=$(PREFIX), R=Codec1:, PORT=CODEC1, ADDR=0, TIMEOUT=1, NDARRAY_PORT=$(PORT)") + +# Create ROI plugin +NDROIConfigure("ROI1", $(QSIZE), 0, "$(PORT)", 0, 0, 0, 0, 0, $(MAX_THREADS)) +dbLoadRecords("NDROI.template", "P=$(PREFIX), R=ROI1:, PORT=ROI1, ADDR=0, TIMEOUT=1, NDARRAY_PORT=$(PORT)") + +# Create Channel Access conversion plugin +NDStdArraysConfigure("Image1", $(QSIZE), 0, "$(PORT)", 0, 0, 0, 0) +dbLoadRecords("NDStdArrays.template", "P=$(PREFIX), R=image1:, PORT=Image1, ADDR=0, TIMEOUT=1, NDARRAY_PORT=$(PORT), TYPE=$(IMAGE_ASYN_TYPE), FTVL=$(IMAGE_WAVEFORM_TYPE), NELEMENTS=$(MAX_IMAGE_PIXELS)") + +# Create PV Access conversion plugin +NDPvaConfigure("PVA1", $(QSIZE), 0, "$(PORT)", 0, $(PREFIX)PVA1:Image, 0, 0, 0) +dbLoadRecords("NDPva.template", "P=$(PREFIX), R=PVA1:, PORT=PVA1, ADDR=0, TIMEOUT=1, NDARRAY_PORT=$(PORT)") + +# Configure HDF5 file format plugin +NDFileHDF5Configure("FileHDF1", $(QSIZE_HDF5), 0, "$(PORT)", 0) +dbLoadRecords("NDFileHDF5.template", "P=$(PREFIX), R=HDF1:, PORT=FileHDF1, ADDR=0, TIMEOUT=1, NDARRAY_PORT=$(PORT)") diff --git a/iocBoot/iocEiger/setup-pipeline.cmd b/iocBoot/iocEiger/setup-pipeline.cmd new file mode 100755 index 0000000..3204679 --- /dev/null +++ b/iocBoot/iocEiger/setup-pipeline.cmd @@ -0,0 +1,12 @@ +dbpf "$(PREFIX)cam1:DataSource" "FileWriter" +dbpf "$(PREFIX)cam1:StreamDecompress" "Disable" +dbpf "$(PREFIX)cam1:CompressionAlgo" "BS LZ4" + +dbpf "$(PREFIX)cam1:FWEnable" "Enable" +dbpf "$(PREFIX)cam1:FWHDF5Format" "v2024.2" +dbpf "$(PREFIX)cam1:SaveFiles" "Enable" +dbpf "$(PREFIX)cam1:FWAutoRemove" "Enable" + +dbpf "$(PREFIX)image1:EnableCallbacks" 1 + +dbpf "$(PREFIX)PVA1:EnableCallbacks" 1 diff --git a/iocBoot/iocEiger/st.cmd b/iocBoot/iocEiger/st.cmd old mode 100644 new mode 100755 index b9a603c..e4dc998 --- a/iocBoot/iocEiger/st.cmd +++ b/iocBoot/iocEiger/st.cmd @@ -1,21 +1,25 @@ -#!../../bin/linux-x86_64/Eiger +#!/opt/ad-eiger-epics-ioc/bin/linux-x86_64/Eiger +# -*- container-image: ghcr.io/cnpem/ad-eiger-epics-ioc +# -*- mount: /ibira:/ibira +# -*- log! -#- You may have to change Eiger to something else -#- everywhere it appears in this file +cd /opt/ad-eiger-epics-ioc/iocBoot/iocEiger < envPaths -cd "${TOP}" +# IOC and device specific configuration +epicsEnvSet("PREFIX", "MNC:B:PILATUS4_4M:") +epicsEnvSet("IP_ADDRESS", "10.31.71.19") -## Register all support components -dbLoadDatabase "dbd/Eiger.dbd" -Eiger_registerRecordDeviceDriver pdbbase +< device.cmd -## Load record instances -#dbLoadRecords("db/Eiger.db","user=root") +# Configure Area Detector plugins +epicsEnvSet("MAX_IMAGE_WIDTH", 1030) +epicsEnvSet("MAX_IMAGE_HEIGHT", 1065) +epicsEnvSet("MAX_IMAGE_PIXELS", 1096950) + +< plugins.cmd -cd "${TOP}/iocBoot/${IOC}" iocInit -## Start any sequence programs -#seq sncxxx,"user=root" +< setup-pipeline.cmd