epicsdev 0.0.0__py3-none-any.whl → 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- epicsdev/epicsdev.py +210 -122
- {epicsdev-0.0.0.dist-info → epicsdev-1.0.0.dist-info}/METADATA +15 -1
- epicsdev-1.0.0.dist-info/RECORD +6 -0
- epicsdev-0.0.0.dist-info/RECORD +0 -6
- {epicsdev-0.0.0.dist-info → epicsdev-1.0.0.dist-info}/WHEEL +0 -0
- {epicsdev-0.0.0.dist-info → epicsdev-1.0.0.dist-info}/licenses/LICENSE +0 -0
epicsdev/epicsdev.py
CHANGED
|
@@ -1,123 +1,127 @@
|
|
|
1
1
|
"""Skeleton and helper functions for creating EPICS PVAccess server"""
|
|
2
2
|
# pylint: disable=invalid-name
|
|
3
|
-
__version__= '
|
|
4
|
-
#TODO: Do not start if another device is already running
|
|
3
|
+
__version__= 'v1.0.0 26-01-16'# re-factored and simplified, comments added. Main() re-writtedn.
|
|
5
4
|
#TODO: NTEnums do not have structure display
|
|
6
|
-
#TODO: Find a way to indicate that a PV is writable.
|
|
7
|
-
# Options:
|
|
8
|
-
# 1) add structure control with (0,0) limits as indication of Writable.
|
|
9
|
-
# 2) use an extra field of the NTScalar.
|
|
10
5
|
|
|
11
|
-
import
|
|
6
|
+
import sys
|
|
12
7
|
import time
|
|
13
8
|
from p4p.nt import NTScalar, NTEnum
|
|
14
9
|
from p4p.nt.enum import ntenum
|
|
15
10
|
from p4p.server import Server
|
|
16
11
|
from p4p.server.thread import SharedPV
|
|
12
|
+
from p4p.client.thread import Context
|
|
17
13
|
|
|
18
14
|
#``````````````````Module Storage`````````````````````````````````````````````
|
|
19
15
|
class C_():
|
|
20
16
|
"""Storage for module members"""
|
|
21
|
-
|
|
17
|
+
prefix = ''
|
|
18
|
+
verbose = 0
|
|
22
19
|
cycle = 0
|
|
23
|
-
lastRareUpdate = 0.
|
|
24
|
-
server = None
|
|
25
20
|
serverState = ''
|
|
26
21
|
PVs = {}
|
|
27
22
|
PVDefs = []
|
|
28
23
|
#```````````````````Helper methods````````````````````````````````````````````
|
|
29
|
-
def
|
|
30
|
-
|
|
24
|
+
def serverState():
|
|
25
|
+
"""Return current server state. That is the value of the server PV, but cached in C_ to avoid unnecessary get() calls."""
|
|
26
|
+
return C_.serverState
|
|
27
|
+
def _printTime():
|
|
28
|
+
return time.strftime("%m%d:%H%M%S")
|
|
29
|
+
def printi(msg):
|
|
30
|
+
"""Print info message and publish it to status PV."""
|
|
31
|
+
print(f'inf_@{_printTime()}: {msg}')
|
|
31
32
|
def printw(msg):
|
|
32
|
-
|
|
33
|
+
"""Print warning message and publish it to status PV."""
|
|
34
|
+
txt = f'WAR_@{_printTime()}: {msg}'
|
|
33
35
|
print(txt)
|
|
34
|
-
|
|
36
|
+
publish('status',txt)
|
|
35
37
|
def printe(msg):
|
|
36
|
-
|
|
38
|
+
"""Print error message and publish it to status PV."""
|
|
39
|
+
txt = f'ERR_{_printTime()}: {msg}'
|
|
37
40
|
print(txt)
|
|
38
|
-
|
|
41
|
+
publish('status',txt)
|
|
39
42
|
def _printv(msg, level):
|
|
40
|
-
if
|
|
41
|
-
|
|
42
|
-
def
|
|
43
|
-
|
|
43
|
+
if C_.verbose >= level:
|
|
44
|
+
print(f'DBG{level}: {msg}')
|
|
45
|
+
def printv(msg):
|
|
46
|
+
"""Print debug message if verbosity level >=1."""
|
|
47
|
+
_printv(msg, 1)
|
|
48
|
+
def printvv(msg):
|
|
49
|
+
"""Print debug message if verbosity level >=2."""
|
|
50
|
+
_printv(msg, 2)
|
|
51
|
+
def printv3(msg):
|
|
52
|
+
"""Print debug message if verbosity level >=3."""
|
|
53
|
+
_printv(msg, 3)
|
|
44
54
|
|
|
45
|
-
def pvobj(
|
|
55
|
+
def pvobj(pvName):
|
|
46
56
|
"""Return PV with given name"""
|
|
47
|
-
return C_.PVs[
|
|
57
|
+
return C_.PVs[C_.prefix+pvName]
|
|
48
58
|
|
|
49
|
-
def pvv(
|
|
59
|
+
def pvv(pvName:str):
|
|
50
60
|
"""Return PV value"""
|
|
51
|
-
return pvobj(
|
|
61
|
+
return pvobj(pvName).current()
|
|
52
62
|
|
|
53
|
-
def publish(
|
|
54
|
-
"""
|
|
63
|
+
def publish(pvName:str, value, ifChanged=False, t=None):
|
|
64
|
+
"""Publish value to PV. If ifChanged is True, then publish only if the value is different from the current value. If t is not None, then use it as timestamp, otherwise use current time."""
|
|
55
65
|
try:
|
|
56
|
-
pv = pvobj(
|
|
66
|
+
pv = pvobj(pvName)
|
|
57
67
|
except KeyError:
|
|
68
|
+
printw(f'PV {pvName} not found. Cannot publish value.')
|
|
58
69
|
return
|
|
59
70
|
if t is None:
|
|
60
71
|
t = time.time()
|
|
61
72
|
if not ifChanged or pv.current() != value:
|
|
62
73
|
pv.post(value, timestamp=t)
|
|
63
74
|
|
|
64
|
-
def SPV(initial, vtype=None):
|
|
65
|
-
"""Construct SharedPV
|
|
66
|
-
|
|
75
|
+
def SPV(initial, meta='', vtype=None):
|
|
76
|
+
"""Construct SharedPV.
|
|
77
|
+
meta is a string with characters W,A,E indicating if the PV is writable, has alarm or it is NTEnum.
|
|
78
|
+
vtype should be one of the p4p.nt type definitions (see https://epics-base.github.io/p4p/values.html).
|
|
79
|
+
if vtype is None then the nominal type will be determined automatically.
|
|
67
80
|
"""
|
|
68
81
|
typeCode = {
|
|
69
|
-
'
|
|
70
|
-
'
|
|
82
|
+
's8':'b', 'u8':'B', 's16':'h', 'u16':'H', 'i32':'i', 'u32':'I', 'i64':'l',
|
|
83
|
+
'u64':'L', 'f32':'f', 'f64':'d', str:'s',
|
|
71
84
|
}
|
|
72
85
|
iterable = type(initial) not in (int,float,str)
|
|
73
86
|
if vtype is None:
|
|
74
87
|
firstItem = initial[0] if iterable else initial
|
|
75
88
|
itype = type(firstItem)
|
|
76
|
-
vtype = {int: '
|
|
89
|
+
vtype = {int: 'i32', float: 'f32'}.get(itype,itype)
|
|
77
90
|
tcode = typeCode[vtype]
|
|
78
|
-
if
|
|
91
|
+
if 'E' in meta:
|
|
79
92
|
initial = {'choices': initial, 'index': 0}
|
|
80
|
-
nt = NTEnum(display=True
|
|
93
|
+
nt = NTEnum(display=True, control='W' in meta)
|
|
81
94
|
else:
|
|
82
95
|
prefix = 'a' if iterable else ''
|
|
83
|
-
nt = NTScalar(prefix+tcode, display=True, control=
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def _define_PVs():
|
|
88
|
-
"""Example of PV definitions"""
|
|
89
|
-
R,W,SET,U,ENUM,LL,LH = 'R','W','setter','units','enum','limitLow','limitHigh'
|
|
90
|
-
alarm = {'valueAlarm':{'lowAlarmLimit':0, 'highAlarmLimit':100}}
|
|
91
|
-
return [
|
|
92
|
-
# device-specific PVs
|
|
93
|
-
['VoltOffset', 'Offset', SPV(0.), W, {U:'V'}],
|
|
94
|
-
['VoltPerDiv', 'Vertical scale', SPV(0.), W, {U:'V/du'}],
|
|
95
|
-
['TimePerDiv', 'Horizontal scale', SPV('0.01 0.02 0.05 0.1 0.2 0.5 1 2 5'.split(),ENUM), W, {U:'S/du'}],
|
|
96
|
-
['trigDelay', 'Trigger delay', SPV(0.), W, {U:'S'}],
|
|
97
|
-
['Waveform', 'Waveform array', SPV([0.]), R, {}],
|
|
98
|
-
['tAxis', 'Full scale of horizontal axis', SPV([0.]), R, {}],
|
|
99
|
-
['recordLength','Max number of points', SPV(100,'U32'), W, {}],
|
|
100
|
-
['peak2peak', 'Peak-to-peak amplitude', SPV(0.), R, {}],
|
|
101
|
-
['alarm', 'PV with alarm', SPV(0), 'WA', alarm],
|
|
102
|
-
]
|
|
96
|
+
nt = NTScalar(prefix+tcode, display=True, control='W' in meta, valueAlarm='A' in meta)
|
|
97
|
+
pv = SharedPV(nt=nt, initial=initial)
|
|
98
|
+
pv.writable = 'W' in meta
|
|
99
|
+
return pv
|
|
103
100
|
|
|
104
101
|
#``````````````````create_PVs()```````````````````````````````````````````````
|
|
105
|
-
def _create_PVs():
|
|
106
|
-
"""Create PVs
|
|
102
|
+
def _create_PVs(pvDefs):
|
|
103
|
+
"""Create PVs, using definitions from pvDEfs list. Each definition is a list of the form:
|
|
104
|
+
[pvname, description, SPV object, extra], where extra is a dictionary of extra parameters, like setter, units, limits etc. Setter is a function, that will be called when"""
|
|
107
105
|
ts = time.time()
|
|
108
|
-
for defs in
|
|
109
|
-
pname,desc,spv,
|
|
110
|
-
|
|
111
|
-
ivalue = pv.current()
|
|
106
|
+
for defs in pvDefs:
|
|
107
|
+
pname,desc,spv,extra = defs
|
|
108
|
+
ivalue = spv.current()
|
|
112
109
|
printv(f'created pv {pname}, initial: {type(ivalue),ivalue}, extra: {extra}')
|
|
113
|
-
C_.PVs[
|
|
114
|
-
|
|
110
|
+
C_.PVs[C_.prefix+pname] = spv
|
|
111
|
+
v = spv._wrap(ivalue, timestamp=ts)
|
|
112
|
+
if spv.writable:
|
|
113
|
+
try:
|
|
114
|
+
# To indicate that the PV is writable, set control limits to (0,0). Not very elegant, but it works for numerics and enums, not for strings.
|
|
115
|
+
v['control.limitLow'] = 0
|
|
116
|
+
v['control.limitHigh'] = 0
|
|
117
|
+
except KeyError as e:
|
|
118
|
+
#print(f'control not set for {pname}: {e}')
|
|
119
|
+
pass
|
|
115
120
|
if 'ntenum' in str(type(ivalue)):
|
|
116
|
-
|
|
121
|
+
spv.post(ivalue, timestamp=ts)
|
|
117
122
|
else:
|
|
118
|
-
v = pv._wrap(ivalue, timestamp=ts)
|
|
119
123
|
v['display.description'] = desc
|
|
120
|
-
for field in extra.keys():
|
|
124
|
+
for field in extra.keys():
|
|
121
125
|
if field in ['limitLow','limitHigh','format','units']:
|
|
122
126
|
v[f'display.{field}'] = extra[field]
|
|
123
127
|
if field.startswith('limit'):
|
|
@@ -125,34 +129,44 @@ def _create_PVs():
|
|
|
125
129
|
if field == 'valueAlarm':
|
|
126
130
|
for key,value in extra[field].items():
|
|
127
131
|
v[f'valueAlarm.{key}'] = value
|
|
128
|
-
|
|
132
|
+
spv.post(v)
|
|
129
133
|
|
|
130
134
|
# add new attributes. To my surprise that works!
|
|
131
|
-
|
|
132
|
-
|
|
135
|
+
spv.name = pname
|
|
136
|
+
spv.setter = extra.get('setter')
|
|
133
137
|
|
|
134
|
-
writable
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
def handle(pv, op):
|
|
138
|
+
if spv.writable:
|
|
139
|
+
@spv.put
|
|
140
|
+
def handle(spv, op):
|
|
138
141
|
ct = time.time()
|
|
139
142
|
vv = op.value()
|
|
140
143
|
vr = vv.raw.value
|
|
144
|
+
current = spv._wrap(spv.current())
|
|
145
|
+
# check limits, if they are defined. That will be a good example of using control structure and valueAlarm.
|
|
146
|
+
try:
|
|
147
|
+
limitLow = current['control.limitLow']
|
|
148
|
+
limitHigh = current['control.limitHigh']
|
|
149
|
+
if limitLow != limitHigh and not (limitLow <= vr <= limitHigh):
|
|
150
|
+
printw(f'Value {vr} is out of limits [{limitLow}, {limitHigh}]. Ignoring.')
|
|
151
|
+
op.done(error=f'Value out of limits [{limitLow}, {limitHigh}]')
|
|
152
|
+
return
|
|
153
|
+
except KeyError:
|
|
154
|
+
pass
|
|
141
155
|
if isinstance(vv, ntenum):
|
|
142
156
|
vr = vv
|
|
143
|
-
if
|
|
144
|
-
|
|
145
|
-
# value
|
|
146
|
-
vr = pvv(
|
|
147
|
-
printv(f'putting {
|
|
148
|
-
|
|
157
|
+
if spv.setter:
|
|
158
|
+
spv.setter(vr)
|
|
159
|
+
# value will be updated by the setter, so get it again
|
|
160
|
+
vr = pvv(spv.name)
|
|
161
|
+
printv(f'putting {spv.name} = {vr}')
|
|
162
|
+
spv.post(vr, timestamp=ct) # update subscribers
|
|
149
163
|
op.done()
|
|
150
|
-
#print(f'PV {pv.name} created: {
|
|
164
|
+
#print(f'PV {pv.name} created: {spv}')
|
|
151
165
|
#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
|
|
152
166
|
#``````````````````Setters
|
|
153
167
|
def set_verbosity(level):
|
|
154
168
|
"""Set verbosity level for debugging"""
|
|
155
|
-
|
|
169
|
+
C_.verbose = level
|
|
156
170
|
publish('verbosity',level)
|
|
157
171
|
|
|
158
172
|
def set_server(state=None):
|
|
@@ -164,72 +178,143 @@ def set_server(state=None):
|
|
|
164
178
|
state = str(state)
|
|
165
179
|
if state == 'Start':
|
|
166
180
|
printi('Starting the server')
|
|
167
|
-
#
|
|
168
|
-
#adopt_local_setting()
|
|
181
|
+
# configure_instrument()
|
|
182
|
+
# adopt_local_setting()
|
|
169
183
|
publish('server','Started')
|
|
184
|
+
publish('status','Started')
|
|
170
185
|
elif state == 'Stop':
|
|
171
186
|
printi('server stopped')
|
|
172
187
|
publish('server','Stopped')
|
|
188
|
+
publish('status','Stopped')
|
|
173
189
|
elif state == 'Exit':
|
|
174
190
|
printi('server is exiting')
|
|
175
191
|
publish('server','Exited')
|
|
192
|
+
publish('status','Exited')
|
|
176
193
|
elif state == 'Clear':
|
|
177
194
|
publish('acqCount', 0)
|
|
178
|
-
#publish('lostTrigs', 0)
|
|
179
|
-
#C_.triggersLost = 0
|
|
180
195
|
publish('status','Cleared')
|
|
181
196
|
# set server to previous state
|
|
182
197
|
set_server(C_.serverState)
|
|
183
198
|
C_.serverState = state
|
|
184
199
|
|
|
185
|
-
def
|
|
186
|
-
"""
|
|
187
|
-
C_.cycle += 1
|
|
188
|
-
printv(f'cycle {C_.cycle}')
|
|
189
|
-
publish('cycle', C_.cycle)
|
|
190
|
-
|
|
191
|
-
def create_PVs(pvDefs:list):
|
|
192
|
-
"""Creates manadatory PVs and adds PVs, using definitions from pvDEfs list"""
|
|
200
|
+
def create_PVs(pvDefs=None):
|
|
201
|
+
"""Creates manadatory PVs and adds PVs specified in pvDefs list"""
|
|
193
202
|
U,LL,LH = 'units','limitLow','limitHigh'
|
|
194
203
|
C_.PVDefs = [
|
|
195
|
-
['version', 'Program version', SPV(__version__),
|
|
196
|
-
['status', 'Server status', SPV('?'
|
|
204
|
+
['version', 'Program version', SPV(__version__), {}],
|
|
205
|
+
['status', 'Server status', SPV('?','W'), {}],
|
|
197
206
|
['server', 'Server control',
|
|
198
|
-
SPV('Start Stop Clear Exit Started Stopped Exited'.split(), '
|
|
199
|
-
|
|
200
|
-
['verbosity', 'Debugging verbosity', SPV(0,'
|
|
201
|
-
|
|
202
|
-
['polling', 'Polling interval', SPV(1.0
|
|
203
|
-
['cycle', 'Cycle number', SPV(0,'
|
|
207
|
+
SPV('Start Stop Clear Exit Started Stopped Exited'.split(), 'WE'),
|
|
208
|
+
{'setter':set_server}],
|
|
209
|
+
['verbosity', 'Debugging verbosity', SPV(0,'W','u8'),
|
|
210
|
+
{'setter':set_verbosity}],
|
|
211
|
+
['polling', 'Polling interval', SPV(1.0,'W'), {U:'S', LL:0.001, LH:10.1}],
|
|
212
|
+
['cycle', 'Cycle number', SPV(0,'','u32'), {}],
|
|
204
213
|
]
|
|
205
|
-
# append application PVs, defined in
|
|
206
|
-
|
|
207
|
-
|
|
214
|
+
# append application's PVs, defined in the pvDefs and create map of providers
|
|
215
|
+
if pvDefs is not None:
|
|
216
|
+
C_.PVDefs += pvDefs
|
|
217
|
+
_create_PVs(C_.PVDefs)
|
|
208
218
|
return C_.PVs
|
|
209
219
|
|
|
210
|
-
|
|
220
|
+
def get_externalPV(pvName, timeout=0.5):
|
|
221
|
+
"""Get value of PV from another server. That can be used to check if the server is already running, or to get values from other servers."""
|
|
222
|
+
ctxt = Context('pva')
|
|
223
|
+
return ctxt.get(pvName, timeout=timeout)
|
|
224
|
+
|
|
225
|
+
def init_epicsdev(prefix, pvDefs, verbose=0):
|
|
226
|
+
"""Check if no other server is running with the same prefix, create PVs and return them as a dictionary."""
|
|
227
|
+
C_.prefix = prefix
|
|
228
|
+
C_.verbose = verbose
|
|
229
|
+
try:
|
|
230
|
+
get_externalPV(prefix+'version')
|
|
231
|
+
print(f'Server for {prefix} already running. Exiting.')
|
|
232
|
+
sys.exit(1)
|
|
233
|
+
except TimeoutError:
|
|
234
|
+
pass
|
|
235
|
+
pvs = create_PVs(pvDefs)
|
|
236
|
+
return pvs
|
|
237
|
+
|
|
238
|
+
#``````````````````Testing stuff``````````````````````````````````````````````
|
|
211
239
|
if __name__ == "__main__":
|
|
240
|
+
import numpy as np
|
|
241
|
+
import argparse
|
|
242
|
+
|
|
243
|
+
def myPVDefs():
|
|
244
|
+
"""Example of PV definitions"""
|
|
245
|
+
SET,U,LL,LH = 'setter','units','limitLow','limitHigh'
|
|
246
|
+
alarm = {'valueAlarm':{'lowAlarmLimit':0, 'highAlarmLimit':100}}
|
|
247
|
+
return [ # device-specific PVs
|
|
248
|
+
['noiseLevel', 'Noise amplitude', SPV(1.E-6,'W'), {SET:set_noise}],
|
|
249
|
+
['tAxis', 'Full scale of horizontal axis', SPV([0.]), {U:'S'}],
|
|
250
|
+
['recordLength','Max number of points', SPV(100,'W','u32'),
|
|
251
|
+
{LL:4,LH:1000000, SET:set_recordLength}],
|
|
252
|
+
['ch1Offset', 'Offset', SPV(0.,'W'), {U:'du'}],
|
|
253
|
+
['ch1VoltsPerDiv', 'Vertical scale', SPV(1E-3,'W'), {U:'V/du'}],
|
|
254
|
+
['timePerDiv', 'Horizontal scale', SPV(1.E-6,'W'), {U:'S/du'}],
|
|
255
|
+
['ch1Waveform', 'Waveform array', SPV([0.]), {}],
|
|
256
|
+
['ch1Mean', 'Mean of the waveform', SPV(0.,'A'), {}],
|
|
257
|
+
['ch1Peak2Peak','Peak-to-peak amplitude', SPV(0.,'A'), {}],
|
|
258
|
+
['alarm', 'PV with alarm', SPV(0,'WA'), alarm],
|
|
259
|
+
]
|
|
260
|
+
nPatterns = 100 # number of patterns in the waveform.
|
|
261
|
+
pargs = None
|
|
262
|
+
nDivs = 10 # number of divisions on the oscilloscope screen. That is needed to set tAxis when recordLength is changed.
|
|
263
|
+
|
|
264
|
+
def set_recordLength(value):
|
|
265
|
+
"""Record length have changed. The tAxis should be updated accordingly."""
|
|
266
|
+
printi(f'Setting tAxis to {value}')
|
|
267
|
+
publish('tAxis', np.arange(value)*pvv('timePerDiv')/nDivs)
|
|
268
|
+
publish('recordLength', value)
|
|
269
|
+
|
|
270
|
+
def set_noise(level):
|
|
271
|
+
"""Noise level have changed. Update noise array."""
|
|
272
|
+
printi(f'Setting noise level to {level}')
|
|
273
|
+
pargs.noise = np.random.normal(scale=0.5*level, size=pargs.npoints+nPatterns)
|
|
274
|
+
#printv(f'Noise array updated with level {level}: {pargs.noise}')
|
|
275
|
+
publish('noiseLevel', level)
|
|
276
|
+
|
|
277
|
+
def init(recordLength):
|
|
278
|
+
"""Testing function. Do not use in production code."""
|
|
279
|
+
set_recordLength(recordLength)
|
|
280
|
+
set_noise(pvv('noiseLevel'))
|
|
281
|
+
|
|
282
|
+
def poll():
|
|
283
|
+
"""Example of polling function"""
|
|
284
|
+
pattern = C_.cycle % nPatterns
|
|
285
|
+
C_.cycle += 1
|
|
286
|
+
printv(f'cycle {C_.cycle}')
|
|
287
|
+
publish('cycle', C_.cycle)
|
|
288
|
+
wf = pargs.noise[pattern:pattern+pvv('recordLength')].copy()
|
|
289
|
+
wf *= pvv('ch1VoltsPerDiv')*nDivs
|
|
290
|
+
wf += pvv('ch1Offset')
|
|
291
|
+
publish('ch1Waveform', wf)
|
|
292
|
+
publish('ch1Peak2Peak', np.ptp(wf))
|
|
293
|
+
publish('ch1Mean', np.mean(wf))
|
|
294
|
+
|
|
212
295
|
# Argument parsing
|
|
213
296
|
parser = argparse.ArgumentParser(description = __doc__,
|
|
214
297
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
215
298
|
epilog=f'{__version__}')
|
|
216
|
-
parser.add_argument('-
|
|
217
|
-
|
|
218
|
-
parser.add_argument('-p', '--prefix', default='
|
|
219
|
-
|
|
220
|
-
parser.add_argument('-
|
|
221
|
-
|
|
222
|
-
parser.add_argument('-v', '--verbose', action='count', default=0, help
|
|
223
|
-
|
|
299
|
+
parser.add_argument('-l', '--listPVs', action='store_true', help=
|
|
300
|
+
'List all generated PVs')
|
|
301
|
+
parser.add_argument('-p', '--prefix', default='epicsDev0:', help=
|
|
302
|
+
'Prefix to be prepended to all PVs')
|
|
303
|
+
parser.add_argument('-n', '--npoints', type=int, default=100, help=
|
|
304
|
+
'Number of points in the waveform')
|
|
305
|
+
parser.add_argument('-v', '--verbose', action='count', default=0, help=
|
|
306
|
+
'Show more log messages (-vv: show even more)')
|
|
224
307
|
pargs = parser.parse_args()
|
|
225
308
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
# List the PVs
|
|
309
|
+
# Initialize epicsdev and PVs
|
|
310
|
+
PVs = init_epicsdev(pargs.prefix, myPVDefs(), pargs.verbose)
|
|
229
311
|
if pargs.listPVs:
|
|
230
|
-
print(
|
|
231
|
-
for
|
|
232
|
-
print(
|
|
312
|
+
print('List of PVs:')
|
|
313
|
+
for _pvname in PVs:
|
|
314
|
+
print(_pvname)
|
|
315
|
+
|
|
316
|
+
# Initialize the device, using pargs if needed. That can be used to set the number of points in the waveform, for example.
|
|
317
|
+
init(pargs.npoints)
|
|
233
318
|
|
|
234
319
|
# Start the Server. Use your set_server, if needed.
|
|
235
320
|
set_server('Start')
|
|
@@ -237,8 +322,11 @@ if __name__ == "__main__":
|
|
|
237
322
|
# Main loop
|
|
238
323
|
server = Server(providers=[PVs])
|
|
239
324
|
printi(f'Server started with polling interval {repr(pvv("polling"))} S.')
|
|
240
|
-
while
|
|
241
|
-
|
|
242
|
-
if
|
|
325
|
+
while True:
|
|
326
|
+
state = serverState()
|
|
327
|
+
if state.startswith('Exit'):
|
|
328
|
+
break
|
|
329
|
+
if not state.startswith('Stop'):
|
|
243
330
|
poll()
|
|
331
|
+
time.sleep(pvv("polling"))
|
|
244
332
|
printi('Server is exited')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: epicsdev
|
|
3
|
-
Version:
|
|
3
|
+
Version: 1.0.0
|
|
4
4
|
Summary: Helper module for creating EPICS PVAccess servers using p4p
|
|
5
5
|
Project-URL: Homepage, https://github.com/ASukhanov/epicsdev
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/ASukhanov/epicsdev
|
|
@@ -15,3 +15,17 @@ Description-Content-Type: text/markdown
|
|
|
15
15
|
|
|
16
16
|
# epicsdev
|
|
17
17
|
Helper module for creating EPICS PVAccess servers.
|
|
18
|
+
|
|
19
|
+
Demo:
|
|
20
|
+
```
|
|
21
|
+
python pip install epicsdev
|
|
22
|
+
python -m epicsdev.epicsdev -l
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
To control and plot:
|
|
26
|
+
```
|
|
27
|
+
python pip install pypeto,pvplot
|
|
28
|
+
python -m pypeto -c config -f epicsdev
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
epicsdev/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
epicsdev/epicsdev.py,sha256=iqXkOCwDA91IwIXf3a8ALyMstRLkX_TLTdsHwFS3SrA,13286
|
|
3
|
+
epicsdev-1.0.0.dist-info/METADATA,sha256=MiqIHi5DIXESKvq7_4Q1GYmlsyem0BEYnabLrERRTRM,786
|
|
4
|
+
epicsdev-1.0.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
5
|
+
epicsdev-1.0.0.dist-info/licenses/LICENSE,sha256=qj3cUKUrX4oXTb0NwuJQ44ThYDEMUfOeIjw9kkT6Qck,1072
|
|
6
|
+
epicsdev-1.0.0.dist-info/RECORD,,
|
epicsdev-0.0.0.dist-info/RECORD
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
epicsdev/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
epicsdev/epicsdev.py,sha256=BiiRZJw9CsmUO0arADg9ZKiiSv-uzvcoh02T6jQ7y_o,8913
|
|
3
|
-
epicsdev-0.0.0.dist-info/METADATA,sha256=7JnJzpkOMPVVH4mqjv6wQBqTUmA_LHH-Jh6s9AWkRV8,608
|
|
4
|
-
epicsdev-0.0.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
5
|
-
epicsdev-0.0.0.dist-info/licenses/LICENSE,sha256=qj3cUKUrX4oXTb0NwuJQ44ThYDEMUfOeIjw9kkT6Qck,1072
|
|
6
|
-
epicsdev-0.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|