-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathpythonSoftIoc.py
More file actions
113 lines (89 loc) · 3.88 KB
/
Copy pathpythonSoftIoc.py
File metadata and controls
113 lines (89 loc) · 3.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
'''Device support files for pythonSoftIoc Python EPICS device support.
Note that these definitions don't strictly need to be loaded through the
iocbuilder in this way, but it makes structural sense this way.'''
import epicsdbbuilder
from . import device
class RecordWrapper(object):
'''This class wraps both a builder and a Python device record instance.
From the outside it looks like a builder record until the database is
written.'''
__Instances = []
__builder_reset = False
def __init__(self, builder, device, name, **fields):
assert not self.__builder_reset, \
'It\'s too late to create records'
# List of keyword arguments expected by the device constructor. The
# remaining arguments are passed to the builder. It's a shame we
# have to maintain this separately from the corresponding device list.
DeviceKeywords = [
'on_update', 'on_update_name', 'validate', 'always_update',
'initial_value', '_wf_nelm', '_wf_dtype', 'blocking']
device_kargs = {}
for keyword in DeviceKeywords:
if keyword in fields:
device_kargs[keyword] = fields.pop(keyword)
record = builder(name, **fields)
record.address = '@' + record.name
self.__set('__builder', record)
self.__set('__device', device(record.name, **device_kargs))
self.__Instances.append(self)
def __set(self, field, value):
if field[:2] == '__' and field[-2:] != '__':
field = '_' + self.__class__.__name__ + field
self.__dict__[field] = value
@classmethod
def reset_builder(cls):
cls.__builder_reset = True
for instance in cls.__Instances:
instance.__set('__builder', None)
# Most attributes delegate directly to the builder instance until the
# database has been written. At this point the builder instance has been
# deleted, and an attribute error will be raised instead.
def __getattr__(self, field):
try:
return getattr(self.__device, field)
except AttributeError:
if self.__builder is None:
raise
else:
return getattr(self.__builder, field)
def __setattr__(self, field, value):
if self.__builder is None:
raise AttributeError('builder has been written')
else:
return setattr(self.__builder, field, value)
# Some further "Duck typing" for the builder: the following are required
# for links to work properly.
def __call__(self, *specifiers):
return self.__builder(*specifiers)
def __str__(self):
return str(self.__builder)
class PythonDevice(object):
DbdFileList = ['device']
@classmethod
def __init_class__(cls):
for name in [
'ai', 'bi', 'longin', 'mbbi', 'stringin',
'ao', 'bo', 'longout', 'mbbo', 'stringout', 'waveform']:
builder = getattr(epicsdbbuilder.records, name)
record = getattr(device, name)
setattr(cls, name, cls.makeRecord(builder, record))
cls.waveform_out = cls.makeRecord(
epicsdbbuilder.records.waveform, device.waveform_out,
'PythonWfOut')
cls.long_stringin = cls.makeRecord(
epicsdbbuilder.records.waveform, device.long_stringin,
'PythonLongStringIn')
cls.long_stringout = cls.makeRecord(
epicsdbbuilder.records.waveform, device.long_stringout,
'PythonLongStringOut')
class makeRecord:
def __init__(self, builder, record, dtyp = 'Python'):
self.builder = builder
self.record = record
self.dtyp = dtyp
def __call__(self, name, **fields):
return RecordWrapper(
self.builder, self.record, name,
DTYP = self.dtyp, **fields)
PythonDevice.__init_class__()