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 a yaml that instantiates your device(s).
An example could be the following :the following yaml newport.yaml. It instatiate a motor with 1 axis. This support implicitly assumes that this serial control is connected to a Ethernet2Serial server like a moxa that responds on address 192.168.190.56 and port 4001
Code Block | ||||
---|---|---|---|---|
| ||||
ioc_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 |
The project contains .devcontainer to test developments, however I often create a local image:
Then we can test if simply having the docker image: baltig.infn.it:4567/epics-containers/infn-epics-ioc
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:devel |
A generic asyn motor OPI interface can be used to drive the motor. Motor OPIs Remember to add localhost to your phoebus
org.phoebus.pv.ca/addr_list settings:
Code Block | ||||
---|---|---|---|---|
| ||||
org.phoebus.pv.ca/addr_list=localhost
org.phoebus.pv.ca/auto_addr_list=false
org.csstudio.trends.databrowser3/urls=pbraw://sparc-archiver.apps.okd-datest.lnf.infn.it/retrieval
org.csstudio.trends.databrowser3/archives=pbraw://sparc-archiver.apps.okd-datest.lnf.infn.it/retrieval
org.phoebus.olog.es.api/olog_url=http://sparc-olog.apps.okd-datest.lnf.infn.it/Olog
org.phoebus.olog.api/olog_url=http://sparc-olog.apps.okd-datest.lnf.infn.it/Olog
org.phoebus.logbook/logbook_factory=olog-es
org.phoebus.olog.api/username=epics
org.phoebus.olog.api/password=epics
org.phoebus.channelfinder/channelfinder.serviceURL=http://sparc-channelfinder.apps.okd-datest.lnf.infn.it/ChannelFinder
org.phoebus.applications.saveandrestore.client/jmasar.service.url=http://sparc-saveandrestore.apps.okd-datest.lnf.infn.it/save-restore
org.csstudio.scan.client/host=http://sparc-scanserver.apps.okd-datest.lnf.infn.it
org.csstudio.scan.client/port=4810 |
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
| ||||
Code Block | ||||
| ||||
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> <project with yaml config > 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:local devel root@3870c1890419:/epics/generic-source/ioc/config# /epics/ioc/start.sh ... log ... |
For test refer to existing ibek support