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