This page documents the python module opscore.protocols.types.
The optional values associated with message keywords are, by default, treated as opaque strings. A value type declaration associates a value with the metadata necessary to to support services such as:
The following types are currently supported:
| String | A string value |
| Float | An IEEE single-precision floating point number |
| Double | An IEEE double-precision floating point number |
| Int | A signed 32-bit decimal integer |
| UInt | An unsigned 32-bit decimal integer |
| Long | A signed 64-bit decimal integer |
| Hex | An unsigned 32-bit hexadecimal integer |
| Bool | A boolean value |
| Enum | An enumerated value |
| Bits | An unsigned 32-bit integer interpreted as packed bit fields |
The following string metadata can be associated with any type (adapted from the data dictionary keyValInfo):
| name | An optional name identifying this value. Should be a valid python identifier. Used by the archiver when building expressions and creating tables. |
| help | Descriptive help text for a value. |
| units | The display units of a value. E.g., "arcsec" |
| reprFmt | A printf-style format for the internal representation of a value. For numeric values, this should display the full precision available. E.g., "%.6f" |
| strFmt | A printf-style format for the default display string of a value (omitting its units). E.g., "%.1f" |
| invalid | A character literal that represents a case-insensitive invalid value. E.g., "???" |
Note that a reprFmt or strFmt that contains either %r or %s is potentially recursive and will raise types.ValueTypeError.
Value types are implemented as bona fide python types which share a common ValueType metaclass and generalize the built-in python types. For example:
>>> DewarTemp = Float(strFmt='%.1f',units='C',help='Dewar temperature') >>> t1 = DewarTemp(-12.345) >>> t2 = DewarTemp(0.2) >>> isinstance(DewarTemp,type) True >>> isinstance(t1,float) True
Metadata is used to assign type methods and attributes:
>>> repr(t1),str(t1)
('Float(-12.345)', '-12.3')
>>> t2.help
'dewar temperature'
>> t2.units
'C'
Since the underlying python built-in types are immutable, the same is also true of value types. First, consider what happens when we assign float literals:
pi = 3.1 # assigns a reference to a newly-created float(3.1) to the symbol 'pi' pi = 3.141 # assigns a reference to a newly-created float(3.141) to the symbol 'pi', so that id(pi) has changed
As a result, assigning a float literal to a value type instance does not do what you might expect:
temp = DewarTemp(-12.345) # assigns a reference to a newly-created DewarTemp(-12.345) to the symbol 'temp' temp = -9.99 # assigns a reference to a newly-created float(-9.99) to the symbol 'temp', so that id(temp) has changed
To avoid the unintentional use of an implicit type, use instead:
temp = DewarTemp(-12.345) temp = DewarTemp(-9.99)
Simple value types are declared as <Type>(<metadata>) where <Type> is String, Float, Int, ... and <metadata> is a sequence of field=value pairs. No metadata is required. Each declaration creates a distinct python type, even when two declarations share the same metadata. Value types with special declaration syntax and string formatting are described below.
A boolean type declaration must start with two values that identify the False and True states, in that order, followed by optional field=value metadata:
>>> Tripped = Bool('OK','FAULT',help='Interlock trip status')
>>> Tripped('FAULT')
Bool(1)
>>> print Tripped('OK')
OK
The Boolean type provides custom str() formatting and ignores any strFmt metadata with a warning.
An enumerated type declaration must start with one or more labels identifying the allowed states followed by optional field=value metadata:
>>> ReadoutState = Enum('Idle','Busy','Fault',help='Instrument readout state')
>>> ReadoutState('Fault')
Enum('Fault')
>>> print ReadoutState('Busy')
Busy
The Enum type provides custom str() formatting and silently ignores any strFmt metadata.
Enumeration objects are interchangeable with python built-in strings. Comparisons between enumeration objects and strings are case insensitive:
>>> ReadoutState('Fault') == 'fault'
True
The capitalization specified in the constructor defines the canonical form of each label that will be used in generating documentation, for example. Enum objects are internally canonicalized so the following constructors are all equivalent and you should never need to add explicit .lower() or .upper() calls in your code:
ReadoutState('Fault')
ReadoutState('fault')
ReadoutState('FAULT')
The Enum type supports optional labelHelp metadata that documents each label individually. When defining new enumerations, it is preferable to make the labels self describing, however this is not always possible when interfacing to legacy systems. The labelHelp should be a list of strings:
MsgCode = types.Enum('>','I','W',':','F','!',labelHelp=['Queued','Information','Warning','Finished','Error','Fatal'])
A bitfield type declaration must start with one or more bitfield specifications followed by optional field=value metadata. A bitfield specification has one of the following forms:
Bitfield names are restricted to the characters A-Z, a-z, 0-9 and underscore (_). Bitfields are mapped to an unsigned 32-bit integer starting from the least-significant bit, in the order they are declared.
>>> Register = Bits('addr:8','data:8',':2','rw','as',help='FPGA control register')
>>> Register(0x04beef)
Bits(311023L)
>>> print Register(0x04beef)
(addr=11101111,data=10111110,rw=1,as=0)
The Bits type provides custom str() formatting and ignores any strFmt metadata with a warning.
Any value type can be repeated exactly n times using the shorthand
Float(...)*n
An allowed range, n to m, in the number of repetitions is indicated with
Float(...)*(n,m)
Finally, a range with no maximum is indicated with
Float(...)*(n,)
A sequence of predefined types that always appear together can be represented as a compound type, for example:
CompoundValueType(
Enum('INFO','WARN','ERROR','FAIL',name='severity',help='Message severity'),
String(name='text',help='Message text'),
help="A logging message"
)
All value types can be initialized from a string value, for example:
Float()('3.141')
Enum('RED','GREEN','BLUE')('GREEN')
Bool('no','yes')('no')
This is the mechanism used to assign typed values to keywords. Assuming that the constructor argument is a string, then there are two ways in which an initialization can fail. First, the string might be a case-insensitive match to value of the invalid metadata field:
Float(invalid='???')('???')
This will raise a special InvalidValue exception defined in the types module. Otherwise, the string value might not be interpretable as the specified type:
Float()('abc')
Int()('1.2')
UInt()('-12')
This will raise a standard ValueError exception, similar to the behavior of the built-in types:
float('abc')
int('1.2')
Note that the predefined IEEE-754 special floating point values 'nan' and 'inf' will not, by default, raise any exceptions for a Float or Double type and will be correctly mapped to the database's special float values:
Float()('nan')
Double()('-inf')
However, a string such as 'nan' can be specified as the invalid value and this will take precedence over the IEEE-754 special value handling. Therefore, the following will raise opscore.protocols.types.InvalidValueError (note that the invalid value test is not case sensitive):
Float(invalid='NaN')('nan')
Any value type (or repeated value type) can describe itself in either plain text format:
print vtype.describe()
or else in html format:
print >> htmlfile, vtype.describeAsHTML()
In both cases, the return value is a printable string. Some example type declarations and their plain text descriptions follow:
Float(units='C',strFmt='%.2f',help='Air Temperature',invalid='?')
Description: Air Temperature
Type: Float (float)
Units: C
Invalid: ?
Enum('RED','GREEN','BLUE',help='Colors',invalid='PINK')*(2,)
Repeated: at least 2 times
Description: Colors
Type: Enum (int)
Values: RED,GREEN,BLUE
Invalid: PINK
Enum('>','I','W',':','F','!',labelHelp=['Queued','Information','Warning','Finished','Error','Fatal'],help='Reply header status code')
Description: Reply header status code
Type: Enum (str,int2)
Value-0: > (Queued)
Value-1: I (Information)
Value-2: W (Warning)
Value-3: : (Finished)
Value-4: F (Error)
Value-5: ! (Fatal)
Bool('no','yes',help='The answer',invalid='unknown')
Description: The answer
Type: Bool (int)
False: no
True: yes
Invalid: unknown
Bits('addr:8','data:8',':2','rw','as',help='FPGA control register')
Description: FPGA control register
Type: Bits (long)
Field-0: 00000000000011111111 addr
Field-1: 00001111111100000000 data
Field-2: 01000000000000000000 rw
Field-3: 10000000000000000000 as
Descriptions formatted for HTML contain the same (label,value) descriptor elements but wrapped in DIV and SPAN elements tagged with CSS class names for user-defined styling. For example:
<div class="vtype"> <div class="descriptor"><span class="label">Description</span><span class="value">Air Temperature</span></div> <div class="descriptor"><span class="label">Type</span><span class="value">Float (float)</span></div> <div class="descriptor"><span class="label">Units</span><span class="value">C</span></div> <div class="descriptor"><span class="label">Invalid</span><span class="value">?</span></div> </div>