Table of Contents |
---|
For the integration of existing developments into docker/kubernates we use part of the Diamond flow for a beamline under kubernates.
...
- support already present in <ibek-support>
- support present as git project
- support not present at all → put in the condition 2
Use an existing ibek support
Anchor | ||||
---|---|---|---|---|
|
If a support is present in <ibek-support>, it's very simple to use it just create a yaml that instantiates your device(s).
...
Code Block | ||||
---|---|---|---|---|
| ||||
docker run -p 5064:5064/udp -p 5064:5064/tcp -p 5065:5065/udp -p 5065:5065/tcp -it -v .:/epics/ioc/config baltig.infn.it:4567/epics-containers/infn-epics-ioc:localdevel |
A generic asyn motor OPI interface can be used to drive the motor. Motor OPIs Remember to add localhost to your phoebus
...
Once the IOC runs correctly as docker can be inserted in the EPIK8S configuration
Build a support from existing git support
This is path is more complex, but gives much more satisfaction, in fact each time you add a new support as much generic possible you greatly simplify the life of your self and in principle many other people will use the your support.
Step 1 create a directory inside ibek-support
Create by copy and renaming one of the existing support. Suppose you want to add the support for <mydevmodule>, you should have:
Code Block | ||||
---|---|---|---|---|
| ||||
.
├── Dockerfile
├── LICENSE
├── README.md
├── build
├── ibek-support
│ ├── ADAravis
│ ├── mydevmodule
│ │ ├── install.sh
│ │ └── mydevmodule.ibek.support.yaml
│ ├── opcua
│ │ └── install.sh
│ ├── pmac
│ │ ├── install.sh
│ │ ├── make_pvi.sh
│ │ ├── pmac.ibek.support.todo
│ │ ├── pmac.ibek.support.yaml
│ │ ├── pmacAxis.pvi.device.yaml
│ │ ├── pmacCSController.pvi.device.yaml
│ │ ├── pmacController.pvi.device.yaml
│ │ └── pmacTrajectory.pvi.device.yaml
│
|
So in general just two file must be added: install.sh and mydevmodule.ibek.support.yaml.
install.sh
This file contains instructions on howto retrieve, compile and link dependencies of your module mydevmodule. In general if the mydevmodule is well structured from a build epics point of view needs only to be patched like that:
Code Block | ||||
---|---|---|---|---|
| ||||
#!/bin/bash
# ARGUMENTS:
# $1 VERSION to install (must match repo tag)
VERSION=${1}
NAME=<mydevmodule> ## as compare in the git repository
FOLDER=$(dirname $(readlink -f $0))
# log output and abort on failure
set -xe
# get the source and fix up the configure/RELEASE files
ibek support git-clone ${NAME} ${VERSION} --org http://gitrepo ## repository git root
ibek support register ${NAME}
# declare the libs and DBDs that are required in ioc/iocApp/src/Makefile
ibek support add-libs <mydevmodulelib> <support1lib> <support2lib> asyn ## optional libraries that must be linked
ibek support add-dbds <mydevmodule>.dbd <optsupport1.dbd> <optsupport2>.dbd ### optional dbd supports
# global config settings
${FOLDER}/../_global/install.sh ${NAME}
# compile the support module
ibek support compile ${NAME}
# prepare *.bob, *.pvi, *.ibek.support.yaml for access outside the container.
ibek support generate-links ${FOLDER}
|
mydevmodule.ibek.support.yaml
This also has a very simple syntax and gives instructions on how to expand a future yaml configuration.
The simplest way to proceed is to understand how the st.cmd of your ioc should be constructed and having this in mind build a set of rules that wraps that code.
The best way is look other supports to understand, here below I put a recently support that I added for newport motors. The comments at the beginning is more a general st.cmd of a SMC100 newport motor.
In the example yaml I instruct ibek to correcty replace a file configuration like the previous newport.yaml.
Code Block | ||||
---|---|---|---|---|
| ||||
# yaml-language-server: $schema=https://github.com/epics-containers/ibek/releases/download/1.5.0/ibek.support.schema.json
# #errlogInit(5000)
# < envPaths
# # Tell EPICS all about the record types, device-support modules, drivers,
# # etc.
# dbLoadDatabase("../../dbd/newport.dbd")
# newport_registerRecordDeviceDriver(pdbbase)
# ### Motors
# dbLoadTemplate "motor.substitutions.SMC100"
# ### Serial port setup
# drvAsynSerialPortConfigure("serial1", "/dev/ttyS0", 0, 0, 0)
# asynSetOption(serial1,0,baud,57600)
# asynOctetSetInputEos("serial1",0,"\r\n")
# asynOctetSetOutputEos("serial1",0,"\r\n")
# ### Newport SMC100 support
# # (driver port, serial port, axis num, ms mov poll, ms idle poll, egu per step)
# SMC100CreateController("SMC100_1", "serial1",1, 100, 0, "0.00005")
# file "$(TOP)/db/basic_asyn_motor.db"
# {
# pattern
# {P, N, M, DTYP, PORT, ADDR, DESC, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, MRES, PREC, DHLM, DLLM, INIT, RTRY}
# {IOC:, 1, "m$(N)", "asynMotor", "SMC100_1", 0, "GTS30V", mm, Pos, 1, 0, .2, 0, .5, .2, 0.00001, 6, 25, -5, ""}
# }
# iocInit
module: motorNewport
defs:
- name: SMC100CreateController
description: |-
Creates a SMC100 motion controller connected to an ethernetToSerialServer
args:
- type: id
name: controllerName
description: |-
The name of the controller and its Asyn Port Name
- type: str
name: P
description: |-
Device PV Prefix
- type: str
name: IP
description: |-
IP address of the ethernet2serial
default: 127.0.0.1 ## localhost
- type: int
name: TCPPORT
description: |-
Port of the ethernet2serial
default: 4001
- type: int
name: POLL
description: |-
Movement poll ms
default: 100
- type: float
name: EGUXSTEP
description: |-
EGU PER STEP
default: 0.00005
- type: int
name: ASYNPRIO
description: |-
ASYN PRIORITY, Default : 0
default: 0
- type: int
name: AUTOCONNECT
description: |-
Asyn auto connect
0: Auto connection
1: no Auto connection
default: 0
- type: int
name: NOPRECESSESOS
description: |-
ASYN noProcessEos, Default : 0
https://epics.anl.gov/tech-talk/2020/msg01705.php
default: 0
- type: int
name: numAxes
description: |-
The number of axes to create
pre_init:
- value: |
# epicsEnvSet "STREAM_PROTOCOL_PATH", "$(MOTORNEWPORT)/protocol/"
# Create Asyn Port
drvAsynIPPortConfigure("{{controllerName}}_ASYN", "{{IP}}:{{TCPPORT}}", {{ASYNPRIO}}, {{AUTOCONNECT}}, {{NOPRECESSESOS}})
# asynInterposeEosConfig("{{controllerName}}_ASYN",0,2000,0)
SMC100CreateController("SMC100_{{controllerName}}", "{{controllerName}}_ASYN","{{numAxes}}", "{{POLL}}", 0, "{{EGUXSTEP}}")
asynOctetSetInputEos({{controllerName}}_ASYN,0,"\r\n")
asynOctetSetOutputEos({{controllerName}}_ASYN,0,"\r\n")
asynReport 10
- name: motorAxis
description: |-
Creates a motor axis
args:
- type: object
name: controller
description: |-
a reference to the motion controller
- type: str
name: M
description: |-
PV suffix for the motor record
- type: int
name: ADDR
description: |-
The axis number (allowed to be from 0 to controller.numAxes-1)
- type: str
name: DESC
description: |-
The description of the axis
- type: int
name: DLLM
description: |-
The low limit of the axis
default: -5
- type: int
name: DHLM
description: |-
The high limit of the axis
default: 25
- type: int
name: VELO
description: |-
Velocity
default: 1
- type: int
name: home
description: |-
The home position of the axis (in counts)
- type: int
name: start
description: |-
The starting position of the axis (in counts)
default: 0
- type: enum
name: DIR
description: |-
The direction of the axis
default: 0
values:
Pos: 0
Neg: 1
- type: str
name: EGU
description: |-
Engineering Units
default: "mm"
- type: float
name: VBAS
description: |-
Base Velocity (EGU/s)
default: 0.2
- type: float
name: ACCL
description: |-
Seconds to Velocity
default: 0.2
- type: int
name: BDST
description: |-
BL Distance (EGU)
default: 0
- type: float
name: BVEL
description: |-
BL Velocity (EGU/s)
default: 0.5
- type: float
name: BACC
description: |-
BL Seconds to Veloc.
default: 0.2
- type: float
name: MRES
description: |-
Motor Step Size (EGU)
default: 0.00001
- type: int
name: PREC
description: |-
Display precision (EGU)
default: 6
databases:
# TODO as this is a simulation I have hard coded some of the DB fields,
# but these could easily be made into arguments above
#
# Note: supplying no value means that the argument of the same name is used
# (the most common case - if you contrive to make args and db fields the same.
# Which is good idea for ease of transition from traditional IOCs)
- file: basic_asyn_motor.db
args:
P: "{{controller.P}}"
N: "{{ADDR +1 }}"
M:
DTYP: "asynMotor"
PORT: "SMC100_{{controller}}"
ADDR:
DESC:
EGU:
DIR:
VELO:
VBAS:
ACCL:
BDST:
BVEL:
BACC:
MRES:
PREC:
DHLM:
DLLM:
INIT: ""
post_init:
- value: |
dbl |
Step 2 add an entry into Dockerfile
Modify the Dockerfile in the project:
Code Block | ||
---|---|---|
| ||
##### build stage ##############################################################
ARG TARGET_ARCHITECTURE
ARG BASE=7.0.8ec1b3
ARG REGISTRY=ghcr.io/epics-containers
FROM ${REGISTRY}/epics-base-${TARGET_ARCHITECTURE}-developer:${BASE} AS developer
# The devcontainer mounts the project root to /epics/generic-source
# Using the same location here makes devcontainer/runtime differences transparent.
ENV SOURCE_FOLDER=/epics/generic-source
# connect ioc source folder to its know location
RUN ln -s ${SOURCE_FOLDER}/ioc ${IOC}
# Get latest ibek while in development. Will come from epics-base when stable
COPY requirements.txt requirements.txt
RUN pip install --upgrade -r requirements.txt
WORKDIR ${SOURCE_FOLDER}/ibek-support
# copy the global ibek files
COPY ibek-support/_global/ _global
COPY ibek-support/iocStats/ iocStats
RUN iocStats/install.sh 3.2.0
################################################################################
# TODO - Add further support module installations here
################################################################################
COPY ibek-support/asyn/ asyn/
RUN asyn/install.sh R4-44
COPY ibek-support/autosave/ autosave/
RUN autosave/install.sh R5-11
COPY ibek-support/busy/ busy/
RUN busy/install.sh R1-7-3
COPY ibek-support/StreamDevice/ StreamDevice/
RUN StreamDevice/install.sh 2.8.24
COPY ibek-support/sscan/ sscan/
RUN sscan/install.sh R2-11-6
COPY ibek-support/calc/ calc/
RUN calc/install.sh R3-7-5
COPY ibek-support/motor/ motor/
RUN motor/install.sh R7-3
COPY ibek-support/motorMotorSim/ motorMotorSim/
RUN motorMotorSim/install.sh R1-2
COPY ibek-support/ADCore/ ADCore/
RUN ADCore/install.sh R3-13
COPY ibek-support/ADGenICam ADGenICam/
RUN ADGenICam/install.sh R1-9
COPY ibek-support/ADSimDetector ADSimDetector/
RUN ADSimDetector/install.sh R2-10
COPY ibek-support/modbus/ modbus/
RUN modbus/install.sh R3-3
COPY ibek-support/screen-epics-ioc screen-epics-ioc/
RUN screen-epics-ioc/install.sh v1.3.1
COPY ibek-support/motorNewport motorNewport/
RUN motorNewport/install.sh R1-2-1
# COPY ibek-support/easy-driver-epics/ easy-driver-epics/
# RUN easy-driver-epics/install.sh master
COPY ibek-support/mydevmodule mydevmodule ### HERE
RUN mydevmodule/install.sh master ## HERE
# get the ioc source and build it
COPY ioc/ ${SOURCE_FOLDER}/ioc
RUN cd ${IOC} && ./install.sh && make
##### runtime preparation stage ################################################
FROM developer AS runtime_prep
# get the products from the build stage and reduce to runtime assets only
RUN ibek ioc extract-runtime-assets /assets ${SOURCE_FOLDER}/ibek*
##### runtime stage ############################################################
FROM ${REGISTRY}/epics-base-${TARGET_ARCHITECTURE}-runtime:${BASE} AS runtime
# get runtime assets from the preparation stage
COPY --from=runtime_prep /assets /
# install runtime system dependencies, collected from install.sh scripts
RUN ibek support apt-install-runtime-packages --skip-non-native
ENV TARGET_ARCHITECTURE ${TARGET_ARCHITECTURE}
CMD ["/bin/bash", "-c", "${IOC}/start.sh"] |
Step 3 build docker image
building:
Code Block | ||
---|---|---|
| ||
git clone https://baltig.infn.it/epics-containers/infn-epics-ioc.git --recurse-submodules
cd infn-epics-ioc
docker build --build-arg TARGET_ARCHITECTURE="linux" --build-arg TARGETARCH="amd64" -t baltig.infn.it:4567/epics-containers/infn-epics-ioc:local . |
building development:
Code Block | ||
---|---|---|
| ||
git clone https://baltig.infn.it/epics-containers/infn-epics-ioc.git --recurse-submodules
cd infn-epics-ioc
docker build -f Dockerfile.devel --build-arg TARGET_ARCHITECTURE="linux" --build-arg TARGETARCH="amd64" -t baltig.infn.it:4567/epics-containers/infn-epics-ioc:devel . |
Step 4 motor yaml instance
Write a mymotor.yaml ibek instance of the support just created and built:
Code Block | ||||
---|---|---|---|---|
| ||||
gioc_name: rfmotors
description: RF motors
entities:
- type: motorNewport.SMC100CreateController
controllerName: NEWPORT001
P: "SPARC:RF:"
IP: 192.168.190.56
TCPPORT: 4001
numAxes: 1
- type: motorNewport.motorAxis
controller: NEWPORT001
M: "m0"
DESC: "Axis"
ADDR: 0
DLLM: -25
DHLM: 25
home: 1
start: 10
VELO: 1 |
test instance
the following lines will mount the current directory that contains mymotor.yaml in the container /epics/ioc/config and will expose CA ports, then run the support just created:
Code Block | ||||
---|---|---|---|---|
| ||||
docker run -p 5064:5064/udp -p 5064:5064/tcp -p 5065:5065/udp -p 5065:5065/tcp -p 5075:5075/tcp -p 5076:5076/udp -it -v .:/epics/ioc/config -v .:/epics/ioc/config baltig.infn.it:4567/epics-containers/infn-epics-ioc:local
root@3870c1890419:/epics/generic-source/ioc/config# /epics/ioc/start.sh
... io log
....
....
|
the following lines use the development image:
Code Block | ||||
---|---|---|---|---|
| ||||
cd <myibek yaml instance>
docker run -p 5064:5064/udp -p 5064:5064/tcp -p 5065:5065/udp -p 5065:5065/tcp -p 5075:5075/tcp -p 5076:5076/udp -it -v .:/epics/ioc/config baltig.infn.it:4567/epics-containers/infn-epics-ioc:devel
root@3870c1890419:/epics/generic-source/ioc/config# /epics/ioc/start.sh
...
log
...
|
For test refer to existing ibek support