An EPICS Proxy
Tags: epics, node, webdev
Published: 2020-04-20 07:45:43

# Background

[EPICS][epics] is a well-known framework for controlling a wide range of hardware. Just like other inventions from the particle physicists, it reaches beyond the field of the particle physics experiments.

# Scenario

[EPICS][epics] provides a comprehensive bucket of tools for almost all use cases. In the age of the internet, however, many unprecedented demands have emerged. One of which is to allow remote control over the internet.

Here comes a trivial problem that has never been addressed before: how to communicate as a web front with the EPICS IOCs?

# Technical Consideration

As a never-before new system, one of the most fundamental engineering principles is to stick with the existing standards as much as possible. Based on this principle, 3 sub-principles must be followed.

1. use loosely-coupled microservices each provides a limited and well-defined functionality
2. communication protocols and data structure are kept simple and consistent across the smallest necessary scope.
3. use standardized protocols, deployment workflow, and libraries

Hence a bridge between IOCs of the 20th century and the web frameworks of the 21st century is necessary.

# Design

## Data Flow

The data flow is the foundation of any software system and should be drafted before the actual development.

![Basic structure](https://dev-to-uploads.s3.amazonaws.com/i/b7oijip5waywh4ls464z.png)

## Communication Protocol

According to the basic structure, the proxy needs to handle 2 types of protocols: one for the microservices and one for the IOCs. The former has many standards I can choose from. The latter only has two standards at the moment: CA and PV.

As PV is only available to EPICS 7+, for better compatibility of the proxy, CA should be supported first.

Considering that `caget` `caput` are compatible with the stateless protocols, but `camonitor` requires a stateful protocol like WebSocket, the protocol to the microservices must support both cases. Hence the most popular communication protocol HTTP-based REST is not an option. After a quick research, GraphQL from Facebook is found to satisfy all the demands.

## Framework

Both CA and GraphQL are too complicated to make from scratch. Therefore some existing frameworks must be utilized. Based on the choice of the protocols, 2 frameworks are required:

1. a GraphQL server
2. a CA library

The most popular GraphQL server is the apollo-server, and it is the only server that supports real-time subscriptions out of the box.

The only CA library comes from EPICS-base, in the form of dynamic libraries and executable binaries.

# Implementation

The development of the proxy was not smooth and simple, therefore I will divide this section into challenges.

## Feasibility

The very first question is: is such proxy even possible?

This question can be broken down into 2:

1. is it possible to integrate CA library with GraphQL server?
2. is the proxy able to communicate with the IOCs on the other hosts in the network?

The first question is answered by [this tool][pyepics] and [this tool][node-epics]. Thanks to the great pioneers who made these tools!

The second question can be cleared by a simple test using the tools mentioned above.

## Setup Apollo Server

GraphQL is way more complicated than it sounds if you are new to it. There are many useful resources out there to help newbies set up their first server. I found [this](https://www.apollographql.com/docs/apollo-server/getting-started/) and [this][type-graphql] to be very helpful.

The biggest challenge here is to understand the concept of the resolver. It is a standardized component of GraphQL so many different packages can work together without a problem. As I use TypeScript to code, [type-graphql][type-graphql] suits my needs best. Although a code-first GraphQL server is better, the Apollo server does not support the code-first approach.

Having implemented `caget`, `caput` and `camonitor` as `Query`, `Mutation` and `Subscription` respectively, the server part is done.

## Connect to IOC

Here comes the most tricky part. As Apollo Server only runs with Node.js, using JS/TS is the only choice. However, [node-epics][node-epics] is too old to support the latest Node.js. Hence I made [my own fork][epics-ioc-connection]. ~~Sadly it depends on `ref-napi` which only works on node before 13. Therefore [it][epics-ioc-connection] requires node <13, which puts this proxy to the same restriction. Luckily we have containers that minimize the impact of such restriction.~~

After some head-scratching, [this tool][epics-ioc-connection] is published. The connection to the IOC is easily implemented and tested.

This issue took me a week before the first version is published whereas the other issues only took me a couple of days.

# Deployment

As the proxy relies on a specific version of the Node, it is better shipped with the Node of the correct version. Hence docker is the best solution.

Personally I use Kubernetes to manage the containers. I recommend anyone who needs docker to consider to switch to Kubernetes as it is just awesome.

# Links

[click here](https://github.com/onichandame/epics-proxy/) for the proxy
[click here][epics-ioc-connection] for the JS binding of CA

[epics]: https://epics-controls.org/
[pyepics]: https://github.com/pyepics/pyepics
[node-epics]: https://github.com/RobbieClarken/node-epics
[type-graphql]: https://typegraphql.com/
[epics-ioc-connection]: https://github.com/onichandame/epics-ioc-connection/