Hardware Objects
Creating a new hardware object is relatively straight forward. If creating a new type of object, first an abstract object needs to be created, then the controls system specific implementation.
For example consider create a motor (assuming one doesnt exist), first create the abstract object:
# daiquiri/core/hardware/abstract/motor.py
from marshmallow import fields
from daiquiri.core.hardware.abstract import HardwareObject
from daiquiri.core.schema.hardware import HardwareSchema
from daiquiri.core.schema.validators import RequireEmpty
MotorStates = ["READY", "MOVING", "FAULT", "UNKNOWN", "LOWLIMIT", "HIGHLIMIT"]
class MotorPropertiesSchema(HardwareSchema):
position = fields.Float()
...
unit = fields.Str(metadata={"readOnly": True})
class MotorCallablesSchema(HardwareSchema):
move = fields.Float()
stop = RequireEmpty()
wait = RequireEmpty()
class Motor(HardwareObject):
_type = "motor"
_state_ok = [MotorStates[0], MotorStates[1]]
_properties = MotorPropertiesSchema()
_callables = MotorCallablesSchema()
An abstract object defines Properties
and Callables
along with a validation
schema. _state_ok
is used to define which of the states are considered good,
and when objects are in a group to define whether the group status is ok.
This is displayed as a green status symbol in the synoptic view for instance.
Then create the controls system specific implementation. In the case of Bliss
this is a simple mapping between the Bliss objects properties / functions and
the abstract object.
# daiquiri/core/hardware/bliss/motor.py
from daiquiri.core.hardware.abstract import HardwareProperty
from daiquiri.core.hardware.abstract.motor import Motor as AbstractMotor, MotorStates
from daiquiri.core.hardware.bliss.object import BlissObject
class PositionProperty(HardwareProperty):
def translate_from(self, value):
return round(value, 4)
class StateProperty(HardwareProperty):
def translate_from(self, value):
states = []
for s in MotorStates:
if s in value:
states.append(s)
if len(states):
return states
return ["UNKNOWN"]
class Motor(BlissObject, AbstractMotor):
PROPERTY_MAP = {
"position": PositionProperty("position"),
...
"unit": HardwareProperty("unit"),
}
CALLABLE_MAP = {"stop": "stop", "wait": "wait_move"}
A specific transformation between the control system and Daiquiri abstract object
properties is provided by HardwareProperty
. Methods translate_from
and
translate_to
can be re-impremented.
This allows the object to translate between for example abstract states and the
control system states. For a motor this might be as simple as converting an Bliss
objects object.Ready
to a 'READY' string.
Finally for Bliss objects a mapping must be made between the defined class and
the class of the Bliss object. This can be done by appending to _class_map
in daiquiri/core/hardware/bliss/__init__.py
.
For tango objects the class is auto loaded from the type
property defined in
ghardware.yml
- name: lima
id: lima_simulator
protocol: tango
type: lima
tango_url: tango://localhost:20000/id00/limaccds/simulator1
Module Resolution
The loader expects the daiquiri classname in lower case. It will resolve to, for example:
- motor -> bliss.motor.Motor
- measurementgroup -> bliss.measurementgroup.Measurementgroup
- lima -> tango.lima.Lima