Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Epics, Cothread and other changes #94

Merged
merged 18 commits into from
Jan 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ bin
lib
include
share
venv
venv3
venv*
pip-selfcheck.json

# build artifacts
Expand Down
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ install:
- pipenv install --dev

script:
- pipenv run python -m pytest --cov=pytac
- pipenv run python -m pytest --cov-report term-missing --cov=pytac
- pipenv run flake8

after_success:
Expand Down
3 changes: 2 additions & 1 deletion Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ pytest = "*"
pytest-cov = "*"
coveralls = "*"
mock = "*"
"flake8" = "*"
flake8 = "*"
bpython = "*"
sphinx = "*"
sphinx-rtd-theme = "*"
pytest-lazy-fixture = "*"
testfixtures = "*"

[packages]
numpy = "*"
Expand Down
3 changes: 2 additions & 1 deletion docs/developers.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
Developers
==========

The installation and initialiseation steps are slightly different if you want to work on pytac.
The installation and initialisation steps are slightly different if you want to work on pytac.
N.B. This guide uses pipenv but a normal venv will also work.


Installation
Expand Down
2 changes: 1 addition & 1 deletion docs/pytac.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
API documentation
API Documentation
=================

.. automodule:: pytac
Expand Down
6 changes: 3 additions & 3 deletions pytac/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
DEFAULT = 'default'


from . import data_source, element, epics, exceptions, lattice, load_csv, units, utils # noqa: E402
from . import data_source, element, exceptions, lattice, load_csv, units, utils # noqa: E402
"""Error 402 is suppressed as we cannot import these modules at the top of the
file as the strings above must be set first or the imports will fail.
"""
__all__ = ["data_source", "element", "epics", "exceptions", "lattice",
"load_csv", "units", "utils"]
__all__ = ["data_source", "element", "exceptions", "lattice", "load_csv",
"units", "utils"]
128 changes: 115 additions & 13 deletions pytac/cothread_cs.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,137 @@
import logging

from cothread.catools import caget, caput, ca_nothing

from pytac.cs import ControlSystem
from cothread.catools import caget, caput
from pytac.exceptions import ControlSystemException


class CothreadControlSystem(ControlSystem):
""" The EPICS control system.
"""A control system using cothread to communicate with EPICS. N.B. this is
the default control system.

It is used to communicate over channel access with the hardware
in the ring.

**Methods:**
"""
def __init__(self):
pass
def __init__(self, timeout=1.0):
self._timeout = timeout

def get(self, pv):
""" Get the value of a given PV.
def get_single(self, pv, throw=True):
"""Get the value of a given PV.

Args:
pv (string): The process variable given as a string. It can be a
readback or a setpoint PV.
throw (bool): if True, ControlSystemException will be raised on
failure

Returns:
object: the current value of the given PV.

Raises:
ControlSystemException: if it cannot connect to the specified PV.
"""
try:
T-Nicholls marked this conversation as resolved.
Show resolved Hide resolved
return caget(pv, timeout=self._timeout, throw=True)
except ca_nothing:
error_msg = 'Cannot connect to {}.'.format(pv)
if throw:
raise ControlSystemException(error_msg)
else:
logging.warning(error_msg)
return None

def get_multiple(self, pvs, throw=True):
"""Get the value for given PVs.

Args:
pvs (sequence): PVs to get values of.
throw (bool): if True, ControlSystemException will be raised on
failure. If False, None will be returned for any PV
for which the get fails.

Returns:
sequence: the current values of the PVs.

Raises:
ControlSystemException: if it cannot connect to one or more PVs.
"""
results = caget(pvs, timeout=self._timeout, throw=False)
return_values = []
failures = []
for result in results:
if isinstance(result, ca_nothing):
logging.warning('Cannot connect to {}.'.format(result.name))
if throw:
failures.append(result)
else:
return_values.append(None)
else:
return_values.append(result)
if throw and failures:
error_msg = '{} caget calls failed.'.format(len(failures))
raise ControlSystemException(error_msg)
return return_values

def set_single(self, pv, value, throw=True):
"""Set the value of a given PV.

Args:
pv (string): PV to set the value of.
value (object): The value to set the PV to.
throw (bool): if True, ControlSystemException will be raised on
failure

Returns:
float: Represents the current value of the given PV.
bool: True for success, False for failure

Raises:
ControlSystemException: if it cannot connect to the specified PV.
"""
return caget(pv)
try:
caput(pv, value, timeout=self._timeout, throw=True)
return True
except ca_nothing:
error_msg = 'Cannot connect to {}.'.format(pv)
if throw:
raise ControlSystemException(error_msg)
else:
logging.warning(error_msg)
return False

def put(self, pv, value):
""" Set the value for a given.
def set_multiple(self, pvs, values, throw=True):
"""Set the values for given PVs.

Args:
pv (string): The PV to set the value of. It must be a setpoint PV.
value (Number): The value to set the PV to.
pvs (sequence): PVs to set the values of.
values (sequence): values to set to the PVs.
throw (bool): if True, ControlSystemException will be raised on
failure. If False, a list of True and False values
will be returned corresponding to successes and
failures.

Returns:
list(bool): True for success, False for failure

Raises:
ValueError: if the lists of values and PVs are diffent lengths.
ControlSystemException: if it cannot connect to one or more PVs.
"""
caput(pv, value)
if len(pvs) != len(values):
raise ValueError("Please enter the same number of values as PVs.")
status = caput(pvs, values, timeout=self._timeout, throw=False)
return_values = []
failures = []
for stat in status:
if not stat.ok:
return_values.append(False)
failures.append(stat)
logging.warning('Cannot connect to {}.'.format(stat.name))
else:
return_values.append(True)
if throw and failures:
error_msg = '{} caput calls failed.'.format(len(failures))
raise ControlSystemException(error_msg)
return return_values
64 changes: 55 additions & 9 deletions pytac/cs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,72 @@


class ControlSystem(object):
""" Abstract base class representing a control system.
"""Abstract base class representing a control system.

A specialised implementation of this class would be used to communicate over
channel access with the hardware in the ring.

**Methods:**
"""
def get(self, pv):
""" Get the value of the given PV.
def get_single(self, pv, throw=True):
"""Get the value of a given PV.

Args:
pv (string): The PV to get the value of.
pv (string): PV to get the value of.
readback or a setpoint PV.
throw (bool): if True, ControlSystemException will be raised on
failure

Returns:
Number: The numeric value of the PV.
object: the current value of the given PV.

Raises:
ControlSystemException: if it cannot connect to the specified PVs.
"""
raise NotImplementedError()

def put(self, pv, value):
""" Put the value of a given PV.
def get_multiple(self, pvs, throw=True):
"""Get the value for given PVs.

Args:
pv (string): The PV to put the value for.
value (Number): The value to be set.
pvs (sequence): PVs to get values of.
throw (bool): if True, ControlSystemException will be raised on
failure

Returns:
list(object): the current values of the PVs.

Raises:
ControlSystemException: if it cannot connect to the specified PV.
"""
raise NotImplementedError()

def set_single(self, pv, value):
"""Set the value of a given PV.

Args:
pv (string): The PV to set the value of.
value (object): The value to set the PV to.
throw (bool): if True, ControlSystemException will be raised on
failure

Raises:
ControlSystemException: if it cannot connect to the specified PV.
"""
raise NotImplementedError()

def set_multiple(self, pvs, values):
"""Set the values for given PVs.

Args:
pvs (sequence): PVs to set the values of.
values (sequence): values to set no the PVs.
throw (bool): if True, ControlSystemException will be raised on
failure

Raises:
ValueError: if the PVs or values are not passed in as sequences
or if they have different lengths
ControlSystemException: if it cannot connect to one or more PVs.
"""
raise NotImplementedError()
25 changes: 14 additions & 11 deletions pytac/data_source.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Module containing pytac data source classes."""
import pytac
from pytac.exceptions import FieldException, DataSourceException, HandleException
from pytac.exceptions import DataSourceException, FieldException, HandleException


class DataSource(object):
Expand Down Expand Up @@ -49,11 +49,12 @@ def set_value(self, field, value):


class DataSourceManager(object):
"""Class that manages the data sources associated with a lattice or element.
"""Class that manages all the data sources and UnitConv objects associated
with a lattice or element.

It recieves requests from a lattice or element object and directs them to
the correct data source. The unit conversion objects for all fields are also
held here.
the correct data source. The unit conversion objects for all fields are
also held here.

Attributes:
default_units (str): Holds the current default unit type, pytac.PHYS or
Expand Down Expand Up @@ -86,7 +87,7 @@ def set_data_source(self, data_source, data_source_type):
self._data_sources[data_source_type] = data_source

def get_fields(self):
"""Get the all fields defined on the manager.
"""Get all the fields defined on the manager.

Includes all fields defined by all data sources.

Expand All @@ -102,8 +103,9 @@ def get_fields(self):
def add_device(self, field, device, uc):
"""Add device and unit conversion objects to a given field.

A DeviceDataSource must be set before calling this method, this defaults
to pytac.LIVE as that is the only DeviceDataSource currently.
A DeviceDataSource must be set before calling this method, this
defaults to pytac.LIVE as that is the only data source that currently
uses devices.

Args:
field (str): The key to store the unit conversion and device
Expand All @@ -124,8 +126,9 @@ def add_device(self, field, device, uc):
def get_device(self, field):
"""Get the device for the given field.

A DeviceDataSource must be set before calling this method, this defaults
to pytac.LIVE as that is the only DeviceDataSource currently.
A DeviceDataSource must be set before calling this method, this
defaults to pytac.LIVE as that is the only data source that currently
uses devices.

Args:
field (str): The lookup key to find the device on the manager.
Expand Down Expand Up @@ -203,8 +206,8 @@ def set_value(self, field, value, handle=pytac.SP, units=pytac.DEFAULT,
data_source=pytac.DEFAULT):
"""Set the value for a field.

This value can be set on the machine or the simulation. If handle, units
or data_source are not given then the lattice default values are used.
This sets a value on the machine or the simulation. If handle,units or
data_source are not given then the lattice default values are used.

Args:
field (str): The requested field.
Expand Down
Loading