A web based beamline control system built with React, Bluesky, and PV Web Socket.
A docker-compose file is used to run the required services together. For full functionality, the host computer should be running an EPICS IOC or connected to one through the local network. If an existing EPICS IOC is not running, then use the script that starts EPICS.
Clone this repository with the --recurse-submodules flag.
git clone --recurse-submodules https://github.com/als-computing/bluesky-web.git
cd Bluesky-Web
At the top level of this repository there is a .env-example file. Copy this file and rename to .env, then edit the EPICS_CA_ADDR_LIST variable to match the address list of the desired computers running EPICS.
#.env
EPICS_CA_ADDR_LIST=YOUR.IP.ADDRESS.RUNNING.EPICS <---- edit this
EPICS_CA_AUTO_ADDR_LIST=NO
PV_WRITE_SUPPORT=true
Experienced EPICS users will be familiar with the EPICS_CA_ADDR_LIST environment variable, which is used to specify the list of network addressess to search for Chanel Access servers on. If you are running an EPICS IOC on the same computer as this web application, then you can provide the IP address of your computer. If you don't have any EPICS IOC previously running and intend to start EPICS from the Docker container this step can be skipped.
Note that if you already have these environment variables set in the terminal running Docker commands, the terminal's environment variables will overwrite those from the .env file.
Common Issues with EPICS_CA_ADDR_LIST
When using docker bridge network, broadcast UDP messages will not be sent outside the container network. If you can only access your EPICS IOCs via an IP address ending in .255, then the host network mode is required instead of bridge. This host network mode is only available on Linux machines.
Two different scripts are provided that will start the application in docker containers. The first script starts the main services (frontend, python server, PV Web Socket). The second script will start the same services and also run a container with EPICS.
If you already have EPICS running and want to access your own IOCs, use the first script. Otherwise the second script can be used to start a "default" EPICS environment that still works with the application.
Run Web Application Only (does not include an EPICS service)
#Bluesky-Web/
docker-compose up -d --build
Run Web Application + EPICS (starts EPICS service in container)
#Bluesky-Web/
docker-compose -f docker-compose.start-epics.yml up -d --build
Navigate to port 8081 in a web browser to view the application.
Stop Application
#Bluesky-Web/
docker-compose stop
Common Issues Preventing Startup
For configurations where Channel Access ports are mapped to the host, docker may not be able to start a service due to a 'bind: address 0.0.0.0:5065 already in use' error. One solution is to simply find the PID of the proces and stop it.
Example of searching for a service on port 5065:
sudo lsof -i :5065
#--------Output----------
#COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
#caRepeater 6259 SEIJ 3u IPv4 39500 0t0 UDP *:5065
Get the PID number of the service and kill it with:
sudo kill 6259 #<---(PID)
Then retry the docker containers with:
docker-compose down
docker-compose up -d --build
To run EPICS in a container by itself, the prjemian/synapps
image can be used. This image contains EPICS base 7.0.5, synApps 6.2.1, and Area Detector 3.11. Using EPICS in a container eliminates the time investment for installing the various libraries and modules to achieve a working EPICS setup. The docker-compose.start-epics.yml file runs this image alongside the application, but it may also be useful to run EPICS separately as shown below.
See the EPICS-Docker IOC setup section for examples on starting IOCs from within the container.
The following commands will start the prjemian/synapps image in a container. No IOC's will be started, but the user can start them by issuing commands in the container terminal.
Run EPICS-Docker (Linux Only)
docker run --name epics-synapps --network host -d prjemian/synapps:latest
docker exec -it epics-synapps /bin/bash
Run EPICS-Docker (Mac or Linux)
docker run --name epics-synapps -p 5064:5064/tcp -p 5064:5064/udp -p 5065:5065/tcp -p 5065:5065/udp -d prjemian/synapps:latest
docker exec -it epics-synapps /bin/bash
First clone down the repo for epics-docker.
git clone https://github.com/prjemian/epics-docker.git
Within the repo are starting scripts used to run the prjemian/synapps
image which can automatically start custom IOCs. It is not required to use these startup scripts to run the image, however the scripts provide utilities such as starting and stopping IOCs which is convenient for testing purposes.
To use the following script on a Linux machine, no additional configuration should be required so long as the Docker executable is within /usr/bin/docker.
Run EPICS-Docker with GP IOC (Linux Only)
./epics-docker/resources/iocmgr.sh start GP ocean
This command automatically starts the GP IOC in the container with prefix "ocean."
The React frontend and Python server can be run outside of containers for development ease. To allow for full functionality of the frontend, PV Web Socket should be running in its container. Additionally either the host computer or another computer on the LAN should be running EPICS. Instructions for running EPICS in a container are also provided.
PVWS is installed with this repository when using the --recurse-submodules flag. It can also be cloned manually with:
git clone https://github.com/ornl-epics/pvws.git
Optionally set EPICS variables as required for your setup in pvws/docker/setenv.sh At a minimum, uncomment PV_WRITE_SUPPORT and set to true as shown below.
## ----- pvws/docker/setenv.sh ----- ##
# Web Socket Settings
#export PV_DEFAULT_TYPE=ca
#export PV_THROTTLE_MS=1000
#export PV_ARRAY_THROTTLE_MS=10000
export PV_WRITE_SUPPORT=true # <------ This must be set to true
# Channel Access Settings
#export EPICS_CA_ADDR_LIST=localhost
#export EPICS_CA_MAX_ARRAY_BYTES=1000000
# PV Access Settings
#export EPICS_PVA_ADDR_LIST=localhost
#export EPICS_PVA_AUTO_ADDR_LIST=YES
#export EPICS_PVA_BROADCAST_PORT=5076
#export EPICS_PVA_NAME_SERVERS=
If you already have a running instance of EPICS on a computer and use custom EPICS environment variables, then edit the other environment variables for PVWS as required. Here is an example of a typical beamline configuration where EPICS is run within a subnet.
## ----- pvws/docker/setenv.sh ----- ##
# Web Socket Settings
#export PV_DEFAULT_TYPE=ca
#export PV_THROTTLE_MS=1000
#export PV_ARRAY_THROTTLE_MS=10000
export PV_WRITE_SUPPORT=true # <-------------------------- This must be set to true
# Channel Access Settings
export EPICS_CA_ADDR_LIST="IPaddress1 IPaddress2" #<------ This line uncommented, use quotes for multiple addresses
export EPICS_CA_AUTO_ADDR=NO #<--------------------------- Add this line when using a specific address list
#export EPICS_CA_MAX_ARRAY_BYTES=1000000
# PV Access Settings
#export EPICS_PVA_ADDR_LIST=localhost
#export EPICS_PVA_AUTO_ADDR_LIST=YES
#export EPICS_PVA_BROADCAST_PORT=5076
#export EPICS_PVA_NAME_SERVERS=
Now run PV Web Socket using its provided docker file.
cd pvws
docker-compose up
To verify it is running navigate to http://localhost:8080/pvws
More information on simulated PV's that can be subscribed to by PVWS can be found here
Optionally create a python environment prior to installing libraries.
conda create --name Bluesky-Web
conda activate Bluesky-Web
Install necessary dependencies.
pip3 install -r server/requirements.txt
Start the FastAPI backend Server
python3 server/main.py
It may be more convenient to run the python server in a container if the developer is only working with the frontend. To start the Python server in a container:
cd server
docker build -t python-server .
docker run -dp 8080:8080 python-server
For testing Bluesky in Python directly, use the following commands to run Jupyter Notebook from within a container:
cd server
docker build -t python-jupyter .
docker run -it -p 8888:8888 python-jupyter /bin/bash
#in the Container Terminal
jupyter lab --ip='0.0.0.0' --port=8888 --allow-root --no-browser --ServerApp.token='' --ServerApp.password=''
Now in a browser you can navigate to localhost:8888/lab
Install project dependencies from package.json file (only need to run the install command once).
#/Bluesky-Web
cd frontend
npm install
Run the app in development mode.
#/Bluesky-Web/frontend
npm start
Open http://localhost:3000 to view the app in a browser.
#/Bluesky-Web/frontend
npm test
Launches the test runner in the interactive watch mode.
See the section about running tests for more information.
#/Bluesky-Web/frontend
npm run build
Builds the app for production to the build
folder.
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.
Your app is ready to be deployed!
See the section about deployment for more information.
The prjemian/synapps
image contains EPICS and a few custom IOCs that can be run with provided scripts. Because it comes with synApps installed, it is also fairly simple to run additional IOCs from the container via an interactive terminal.
The following instructions are provided as a general example for how the epics docker container can be utilized and developed in. They show the steps for running the motorMotorSim IOC.
- Start and enter the EPICS container
Run EPICS-Docker (Mac or Linux)
docker run --name epics-synapps -p 5064:5064/tcp -p 5064:5064/udp -p 5065:5065/tcp -p 5065:5065/udp -d -it prjemian/synapps:latest /bin/bash
docker exec -it epics-synapps /bin/bash
Run EPICS-Docker with startup scripts (Linux Only)
git clone https://github.com/prjemian/epics-docker.git
./epics-docker/resources/iocmgr.sh start GP test1
docker exec -it ioctest1 sh
- (All following steps are inside the container) Navigate to the motor module directory
screen
cd /opt/synApps/support/motor-R7-2-2/modules
- (Optional) Download the updated motorMotorSim repo. The most recent version contains different PVs than that provided in the image.
mv motorMotorSim/ motorMotorSimOld/
git clone https://github.com/epics-motor/motorMotorSim.git
make
- Edit the configuration files so that the IOC is built during Make commands
echo "BUILD_IOCS = YES" > motorMotorSim/configure/CONFIG_SITE.release
- Run Make in the motorSimIOC directory to create the IOC.
cd motorMotorSim/iocs/motorSimIOC
make
- Start the motorMotorSim IOC
cd /opt/synApps/support/motor-R7-2-2/modules/motorMotorSim/iocs/motorSimIOC/iocBoot/iocMotorSim
../../bin/linux-x86_64/motorSim st.cmd
- Check the PV names (from within the EPICS terminal)
epics> dbl
In the EPICS-docker container terminal, issue the commands to start GP. This command has been tested on Linux only.
cd $IOCGP
../../bin/linux-x86_64/gp st.cmd.Linux
Linux machines running Docker can utilize "--network host" to map the network ports in a container to that of the host machine. This allows container services to talk to EPICS. The Mac version of Docker does not have this network host mode. Therefore some additional configuration is typically required when running any service trying to communicate with EPICS inside a container on Mac.
To run the EPICS container on a Mac while providing access outside the container, the ports used for channel access need to be explicitly mapped when running the container. By default, these are ports 5064 and 5065 with both UDP and TCP protocol. The ports can be manually configured within the running container if desired.
For example purposes, the following command can be used to run the prjemian/synapps
image with default Channel Access port mapping.
docker run -p 5064:5064/tcp -p 5064:5064/udp -p 5065:5065/tcp -p 5065:5065/udp -it prjemian/synapps:latest
The above command maps the 5064 and 5065 ports so that the IOC within the container can be reached from outside. This has been tested on an M2 Mac with Channel Access.
PVWS uses "--network host" mode in its dockerfiles, so it will only work on a Linux without additional configuration. For a Mac running PVWS in Docker, the dockerfile can be modified to use the bridge mode and map port 8080. This allows use of the simulated PVs in PVWS, but does not provide the ability for PVWS to reach actual EPICS IOC's running on the host computer. To run PVWS in Docker with access to EPICS, see the next section which details how to set up a bridge network between multiple containers.
More information on simulated PV's that can be subscribed to by PVWS can be found here
A convenient method for developing the React App on Mac is to start everything except for the frontend in containers. This allows the use of the live server that comes with Create React App, while still having full EPICS functionality.
docker-compose -f docker-compose.start-epics.no-react.yml up --build
The queue server provides a method for managing and executing Bluesky plans, it is used in the React app via the HTTP Server.
These instructions are for directly installing Queue Server outside of a container for development purposes. To run the queue server, redis will also need to be installed.
Install Redis (Mac)
brew update
brew install redis
brew services start redis
Install Redis (linux)
sudo apt-get install redis
sudo systemctl start redis
Install Python Packages
conda create -n queue_server python=3.10
activate queue_server
conda install bluesky-queueserver -c conda-forge
conda install bluesky-httpserver -c conda-forge
On testing with an M2 mac, it was found that using pyepics and ophyd installed from 'pip' resulted in errors. Only conda distributions worked.
Installing with Conda
conda install pyepics ophyd -c conda-forge
Start Queue Server with Default Simulated Devices and Plans
start-re-manager --zmq-publish-console ON
Start Queue Server with BL5.3.1 Devices
#from the /Bluesky-Web directory
start-re-manager --zmq-publish-console ON --startup-dir /server/queue-server-configuration/startup_bl531 --keep-re
Publish Queue Server Console Output in a Terminal (optional)
qserver-console-monitor
The HTTP server is part of the Queue Server system and works directly with the Queue Server.
Install HTTP Server
pip install bluesky-httpserver
Note that conda does not work for installing http server on Mac Silicon.
Start HTTP Server with key 'test'
QSERVER_HTTP_SERVER_SINGLE_USER_API_KEY=test QSERVER_HTTP_SERVER_ALLOW_ORIGINS=* uvicorn --host localhost --port 60610 bluesky_httpserver.server:app
The http server is a part of the queue server, it acts as a gateway to control the queue server. The queue server itself only communicates over ZMQ, so the http server acts as an API for web clients which otherwise cannot command the queue server directly. The http server is typically hosted on port 60610.
flowchart LR
A[Web Client] -->|http request| B(HTTP Server)
B --> |zmq msg| C(Queue Server)
C --> |zmq msg|B
B --> |http response| A
You can learn more in the Create React App documentation.
<style> mark { color: white; background-color: #37374a; border-radius: 2px; padding: 7px 10px; text-shadow: black 1px 1px 2px; } </style>