Package: emmi
The EPIC Monday-Morning Integration ToolkitEMMItool is the "Swiss army knife" for rapid ad-hoc integration of
scientific instrumentation intoEPICS-basedbeamline control sytems.Concepts and ComponentsEMMI is separated in different layers of which either can be used
for specific types of integrations, ranging from rapid "Monday morning"
hacks for attaching an existing non-EPICS capable device to a beamline,
to a writing full-fledged EPICS-IOC support for your device:The EMMI Device Architecture (EMA)offers
abstraction layers for access to motors, signals and switchesEMMIdaemonis a stand-alone application that runs
and presents an ad-hoc IOC on one side, with a REST-like HTTPS API
support on the other sideEMMI Device ArchitectureControlling MotorsEMMI uses a simple yet extensible motor interface. The philosophy is that
we're considering anidealmotor in our model. We don't concern ourselves
with backlash compensation, acclereation ramps etc. We care aboutwhatthe
motor is supposed to do, as opposed tohowit is going to do it.For this, we consider the following properties:position: R/W property which returns the current motor values,
respectively moves to the specified absolute value when written toposition_relative: R/W property that facilitates a movement relative
to the current position when written to; always returns 0.state: a R/W property that indicates the current state of the
(abstract) motor device; can be explicitly set within specific
parameters to advance to specific states.The state diagram of a motor looks like this in its most simple form:┌──────────┐
          │   INIT   ├┄┄┄┄┄┄┄┄┄┄┄┄►┄┄┐
          └────┬─────┘               ┊
               ▼                     ┊
          ┌────┴─────┐               ┊
          │          ├┄┄┄┄┄┄┄┄┄┄┄┄►┄┄┤
  ┌───►───│   IDLE   ├┄┄┄┄┄►┄┄┐      ┊
  │       └────┬─────┘        ┊      ┊
  │            ▼              ┊      ┊
  │   ┌┈┈┈┈┈┈┈┈┴┈┈┈┈┈┈┈┈┈┐    ┊      ┊
  │   ┊                  ├┄┄┄┄┄┄┄┄►┄┄┤
  │   ┊     BUSY...      ├    ┤      ┊
  │   ┊                  ┊    ┊      ┊
  │   └┈┈┈┈┈┈┈┈┬┈┈┈┈┈┈┈┈┈┘    ┊      ┊
  │            ▼              ┊      ┊
  │       ┌────┴─────┐        ┊      ┊
  ├───◄───┤          ├┄┄┄┄┄◄┄┄┘      ┊
  │       │   STOP   ├┄┄┄┄┄┄┄┄┄┄┄┄►┄┄┤
  │       └────┬─────┘               ┊
  │            ▼                     ▼
  │       ┌────┴─────┐           ┌───┴────┐
  └───◄───┤  ERROR   ├┄┄┄┄┄┄┄┄►┄┄┤  FAIL  │
          └──────────┘           └────────┘Which translates to:INITis the initial states after startup, device is undergoing
custom configuration and is not ready yet.IDLEdevice is ready to perform according to commandsBUSYis the state in which device is performing, most likely movingSTOPis the state in which the device is decelerating with the
intention of coming to a standstill. This can be part of a regularIDLE-BUSYcycle (i.e. returning toIDLEonce standstill is reached),
or can be an intermediate state towards anERRORstate, ensuring
that the device is stopped for handling of errors.ERRORis a well-defined state which represents the devicenotpeforming, but which is still part of the "well defined" behavior of
the device. Such a state, for instance, is reaching hardware limits
or impossibility to execute a command (e.g. because coordinates are
outside of allowed range). The device is always in a standstill
when inERROR, which is ensured by the fact thatERRORis only
entered throughSTOP.ERRORcan be entered from all "regular"
operational states (IDLE,BUSYorSTOP), but not fromINIT-- initialisation errors result inFAIL.FAILis the state of a fatal error, incompatible with "defined
behavior" of the device. It is a terminal state, meaning that there
is no system-supported from this state. A complete reinitialisation,
typically encompanied by a power cycle or hardware reset is the
action to be performed to advance fromFAIL. It can be entered
from any other state.TheBUSYstate deserves extra explanation, as it's the main mechanism
for extending the functionality of a motor.In its most simple form, a motor has only one task: to move an (abstract)
axis to a specified value. However, slightly more complex real-world
applications may differentiate more strictly on the type of movement
to be performed:aslewingmovement is performed autonomously with maximum
speed within parameters, towards a specific target;ajoggingmovement, e.g. triggered by a joystick, is performed
with a predefined speed as long as a condition (button press)
actively persists;atrackingmovement is a movement that is bound to time
constraints, e.g. hitting specific coordinates at specific times;ahomingmovement may be used to define a slewing towards
a hard-coded parking position;adialingortweakingmovement may be a manual correction
on top of a predefined tracking path, etc.Even more complex moves require several stacked types of movements
(e.g. atrackingrequires aslewinginto position first, and
accepttweakinginput while performing the actual tracking).As far as EMMI is concerned, we don't care about the complexity of
the movement itself, we only care about representing high-level
states of operation at an EPICS interface level -- roughly speaking,
to us, the motor "does" or "doesn't do" anything. To model this,
we allow splitting theBUSYstate into sub-states, hiearchically
denoted (e.g.BUSY.SLEWorBUSY.TRACK...). The restriction is
that they all must either end inSTOP, before returning toIDLEor enteringERROR, or must definitively fail directly intoFAIL.These supplementary states my be controlled by variables and properties
which EMMI will happily manage and pass through to the EPICS interface,
but will not understand or touch -- e.g. speed limits, accelerations,
homing coordinates etc.However, as it is customary with EPICS, EMMI will manage designated
boolean PVs to trigger these states and indicate the successful
performance of the action. This allows to a certain degree easy
implementation of the "HOMF/D", "TWF/D" or "JOG" class of commands of
anEPICS motor record.This results in a 4-layer architecture that leads from the hardware
controls to the EPICS variables:TheAxis Controlis the layer (within Python) which
directly serves the hardware interface API, e.g. typically a
lass wrapped around a pySerial interface.TheEngineis a layer which enforces an API compatible
the state diagram above, with the propertiesposition,position_relativeandstateas described.TheConnectoris a translator between the motor engine and
a generic EPICS IOC generator, e.g. as provided by pythonSoftIOC.TheIOC Generatoris a library that does the actual EPICS work.In the spirit of "integration", we acknowledge that typically the first
layer has already been written, and there is legitimate concern to reuse
it. The only restriction EMMI imposes is for theAxis Controlto not block. For the last layer, EMMI makes heavy use of pythonSoftIOC.What remains is theEngineandConnector, which EMMI
implements in the classesMotorEngineandMotorConnectorwithinemmi.eda.Typically,MotorConnectorneeds to specifically dock to user-suppliedAxis Controlcode. There are three ways to do this:Write from scratch or re-write yourAxis Controlto beEnginecompatible. This is, of course, the preferred method, but not
always available.Use the suppliedMotorConnector, which is a highly configurable
template that makes heavy use of Python's "duck typing" to attach
to a more-or-less compatibleAxis Control. This has the advantage
that it doesn't require tampering with "tried and true" hardware
control code of theAxis Controllayer.Write your ownMotor Controllerfrom scratch, paying attention
to reflect theEngineAPI, as described above.As a side note, this architecture also gives a natural layer at which
to attach useful, yet hardware-independent, unit testing: by replacing
theMotorConnectorwith a mock-up class that behaves as it's supposed
to, all the layers between there and the EPICS interface can be tested
in a suitable, automated CI/CD environment.