Accessing I/O

Input and Outputs are the only way to comunicate with the external world. In the context of pysmlib each input is directly mapped to an EPICS PV. Furthermore the term “input” is used as a generic term for I/O because each input can be also an output. In fact, all the PVs can be read (get) and written (put).

The main class to access inputs is fsmIO.

fsmIO class reference

class fsmIO(...)

This represent an input as an object. The constructor should be never called directly by the user. Each input is created with the method connect(), which returns an instance of this class.

The user can access the status of each input with some simple, yet powerful, methods. These are divided in four macro categories:

  • Methods to access stationary conditions

  • Methods to detect edges.

  • Methods to detect trends

  • Methods to write outputs

Methods for stationary conditions

These kind of methods return static informations about an input. For example they can tell if an input is connected or not, but do not give any informations on when the input has connected: it may have connected yesterday or just a moment ago.

ioname()
Returns:

the name of the input.

val(as_string=False)
Parameters:

short – as_string.

Returns:

the current value of the input. If as_string is True a string representation of the value is returned.

connected()
Returns:

True if the input is connected, via Channel Access.

initialized()
Returns:

True if the input is connected and has received the first value, meaning its value is not None.

putComplete()
Returns:

True if a previous (or no) put() on this input has completed, False if a put() is being executed in this moment.

alarm()
Returns:

the alarm status: one of [-2, -1, 0, 1, 2] where 0 is the NO_ALARM state.

alarmName(short=False)
Parameters:

short – return a short version of the name.

Returns:

the corresponding alarm string: -2: UNDER THRESHOLD MAJOR ALARM, -1: UNDER THRESHOLD MINOR ALARM, 0: NO ALARM, 1: OVER THRESHOLD MINOR ALARM, 2: OVER THRESHOLD MAJOR ALARM. If the short parameter is True, only the last two words of the strings are returned.

alarmLimits()
Returns:

a tuple with the 4 alarm thresholds, in the following order: lower major threshold, lower minor threshold, upper minor threshold, upper major threshold.

pval()
Returns:

the previous value of the input.

time()
Returns:

the processing time associated with the current value. This is a datetime object.

status()
Returns:

the status of the input (1 for OK).

precision()
Returns:

the number of decimal positions to display.

units()
Returns:

the unit of measure of the value.

readAccess()
Returns:

True if you can read the input.

writeAccess()
Returns:

True if you can write the input..

enumStrings()
Returns:

the list of possible string values of an input which defines an enumeration.

displayLimits()
Returns:

the (minimum, maximum) values to use to display the input.

controlLimits()
Returns:

the (minimum, maximum) control limits.

maxLen()
Returns:

the maximum length of an input with an array value.

host()
Returns:

the IP and port of the host hosting the input.

caType()
Returns:

the channel access type of this input.

data(key=None)

PyEpics PV objects contain more informations than value and connection status. To access those fields, use this method. The available key are listed here: <http://cars9.uchicago.edu/software/python/pyepics3/pv.html#user-supplied-callback-functions>

Parameters:

key (string, optional) – the particular information to extract from a PV.

Returns:

the requested information. If key is not specified or is None the whole dictionary with all the data is returned.

Methods to detect edges

As described on Finite State Machine development, while the FSM is running the current state is executed exactly once for each event received on any of the FSM inputs, or timers. With the methods on this group the user can access the information on the reason why the FSM has been executed at each time. So, for example, if a connection event is received, the FSM is executed and the method connecting() on the correct input will return True for just this execution. After that a change event is received, and the FSM is executed again: this time the FSM was executed due to a change event, so connecting() will return False, but the input is still connected and so the connected() will still return True. In fact, this time the method changing() will return True.

So, this way these methods return True just for one state evaluation, when a certain event is happening right now, and let the user access the information on rising or falling edges on certain conditions. This is useful when an action has to be performed only once when an event occurs, and not each time a condition is true.

rising()
Returns:

True if the input has just gone from 0 to not zero. Best to use only with boolean values (binary PVs).

falling()
Returns:

True if the input has just gone from not zero to 0. Best to use only with boolean values (binary PVs).

changing()
Returns:

True if the input has just changed its value.

connecting()
Returns:

True if the input has just connected.

disconnecting()
Returns:

True if the input has just disconnected. Note that the Channel Access uses timeouts to check the connection status, so a certain delay is to be expected.

initializing()
Returns:

True if the input has just received its first value after a connection.

putCompleting()
Returns:

True if the input has just completed a previous put().

alarmIncreasing()
Returns:

True if the alarm severity has just changed from NO_ALARM to MINOR or from MINOR to MAJOR.

alarmDecreasing()
Returns:

True if the alarm severity has just changed from MAJOR to MINOR or from MINOR to NO_ALARMs.

alarmChanging()
Returns:

True if the alarm severity has just changed.

Methods to write outputs

At least, of course, this method can be used to write a new value to a output.

put(newValue)

Write newValue to output.

Parameters:

newValue (type depends on PV type) – the value to be written

Returns:

False if put() failed, True otherwise.

I/O mapping and parametrization

The inputs on pysmlib are shared resources. The class which groups all the inputs from all the FSMs is:

class fsmIOs

This is a container of all inputs of all FSMs. It can be instantiated by the user and passed to all the FSMs as a optional argument (ios, see fsmBase) on their constructor, but the easiest way is to use the Loader and fsm execution which automatically handles FSM optional arguments.

This class declares a method get() which receives a string with the input name, creates the corresponding input, if not already available, and returns it. It is used by connect() and should not be accessed directly.

Using the fsmIOs each input name must be exactly a PV name. This approach has some disadvantages:

  1. The PV name is hard-coded in the FSM implementation. If, for any reason,the PV name changes, the code must be modified!!

  2. The names are not parametric. If your logic works well for two identical objects, with PV names which differ only for a number (eg: PS01 vs PS02) you will have to implement manually a parametrization mechanism for each FSM.

  3. Inserting long PV names in the code is not much readable.

  4. The user has to check each PV name to be compatible with the Naming Convention of the facility, if present.

For all these reasons a derivate class of fsmIOs has been developed.

class mappedIOs(mapFile)
Parameters:

mapFile (string) – the path to a map file, whose syntax is described below.

This let you use short names to identify inputs, and add any number of optional arguments to specify custom parameters. For example, you can define an input like this:

class exampleFsm(fsmBase):
    def __init__(self, name, psNum, *args, **kwargs):
        super(exampleFsm, self).__init__(name, **kwargs)

        self.ps = self.connect("powerSupply", n=psNum)

This way, the number of the power supply is a parameter of the FSM and you can instantiate multiple FSMs, one for each power supply. Moreover, inside the code the “powerSupply” string is easy to read and

Then the input name has to be somehow translated to the correct PV name, which is, in our example, “PS01”. For this reason a map file has to be defined, containing the following lines:

> pattern = ({:.2s}{:02d}) (OBJ, NUM)
"powerSupply" = "PS", <n>      #this is a comment

As you can see the first thing to do is to define a pattern, which is the naming convention followed by all the PVs who are defined after (before the next pattern). In this case the pattern specify that the PV name must contain two characters, followed by an integer with 2 digits, with leading zeroes. This way the translator knows what to expect, can correctly format numbers and can check that the inputs respect this Naming Convention. The syntax of the pattern definition is the same as the one used by python format() function.

The second line defines the string “powerSupply”: this is the string that we will use inside our code to refer to that particular input. After the equal mark we can find the informations to fill the pattern to create the PV name. In particular the first two characters are provided directly: “PS”. Note that the quotation marks are optional and will be stripped away. The second part instead, which is put inside the < > signs, represent a parameters. This means that its value is not know before run time, and must be passed as an optional argument (with the exact same name) to the connect() method. In fact, we provided the optional argument n. So, at execution time the translator will format the number as required, concatenate it to the first two characters and obtain “PS01”. This offer great flexibility to connect to similar PVs who differ only for some counters.

A more complete example of a map file is the following one:

#MACROS DEFINITION:
> FAC = "Al"
> APP = "Llrf"
> SAPP = "Cryo"
> CHID = "A"
> OBJ = "Qwrs"
> AMP = "Ampl"
> CVON = "Cvon"
> CRYG = "Cryg"

#LONG PVS:
> pattern = ({:.2s}{:.4s}{:.4s}{:02d}{:.1s}_{:.4s}{:02d}{:.1s}{:s}) (FAC, APP, SAPP, NSAP, CHID, OBJ, NOBJ, TYPE, SIGNAL)
"CvonEn"             = $(FAC), $(APP), $(SAPP), <nsap>, $(CHID), $(CVON), <nobj>, ":", "ProcEn"         #enable fsm
"CvonRetc"           = $(FAC), $(APP), $(SAPP), <nsap>, $(CHID), $(CVON), <nobj>, ":", "Retc"           #fsm return code
"CvonMsgs"           = $(FAC), $(APP), $(SAPP), <nsap>, $(CHID), $(CVON), <nobj>, ":", "Msgs"           #message to user
"CvonStat"           = $(FAC), $(APP), $(SAPP), <nsap>, $(CHID), $(CVON), <nobj>, ":", "Stat"           #state of the fsm
"CvonRunn"           = $(FAC), $(APP), $(SAPP), <nsap>, $(CHID), $(CVON), <nobj>, ":", "Runn"           #running status the fsm
"CvonWdog"           = $(FAC), $(APP), $(SAPP), <nsap>, $(CHID), $(CVON), <nobj>, ":", "Wdog"           #state of the fsm

#SHORTER PVS
> pattern = ({:.2s}{:.4s}{:.4s}{:02d}{:.1s}{:.1s}{:s}) (FAC, APP, SAPP, NSAP, CHID, TYPE, SIGNAL)
"cryoName"           = $(FAC), $(APP), $(SAPP), <nsap>, $(CHID), ":", "Name"                           #cryostat string name
"cryoNext"           = $(FAC), $(APP), $(SAPP), <nsap>, $(CHID), ":", "Next"                           #pointer to next cryostat
"cryoPrev"           = $(FAC), $(APP), $(SAPP), <nsap>, $(CHID), ":", "Prev"                           #pointer to prev cryostat
"cryoNQwrs"          = $(FAC), $(APP), $(SAPP), <nsap>, $(CHID), ":", "Nqwr"                           #n of qwr in this cryostat
"cryogEn"            = $(FAC), $(CRYG), $(SAPP), <nsap>, $(CHID), ":", "RfpaEn"                        #enable from cryogenic
"storeConnWd"        = $(FAC), $(APP), , , , ":", "StorWd"                                             #store fsm connection watchdog

Syntax rules:

  • The character # is used for comments.

  • The character > signal special lines.
    • The word pattern is reserved to define a new pattern on special lines.

    • All the other cases are macro definitions.

  • Each normal line defines a input name and its link to a PV name.
    • The $( ) string means that the part inside parentesis is a macro name and should be replaced with its value

    • The < > string indicates a parameter that should be passed as optional argument of connect()

  • Each element of the PV name is divided by a comma, and each part is associated with the one on the pattern, in order.

Macro definition is used to avoid repeting the same string everywhere in the file, so each macro occurrence is substituted with its value on the whole document. For example, having defined the marco > FAC = "Al", $(FAC) is replaced with Al.

Therefore, when defining an input, one of the string on the left can be used, and then the PV name will be built concatenating all the pieces following the pattern logic, and replacing the parameters with the values passed at run time.

Summary of the steps to implement a map on inputs

  1. Use mappedIOs instead of fsmIOs. This is achieved by calling setIoMap() method of the loader class.

  2. Create the map file.

  3. Connect to the inputs using the strings defined in the map file, passing all the required parameters as optional arguments.