newportxps 0.3.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.
- newportxps/XPS_C8_drivers.py +2068 -0
- newportxps/__init__.py +1 -0
- newportxps/debugtime.py +47 -0
- newportxps/ftp_wrapper.py +141 -0
- newportxps/newportxps.py +1207 -0
- newportxps/utils.py +33 -0
- newportxps-0.3.0.dist-info/LICENSE +25 -0
- newportxps-0.3.0.dist-info/METADATA +121 -0
- newportxps-0.3.0.dist-info/RECORD +11 -0
- newportxps-0.3.0.dist-info/WHEEL +5 -0
- newportxps-0.3.0.dist-info/top_level.txt +1 -0
newportxps/newportxps.py
ADDED
|
@@ -0,0 +1,1207 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import posixpath
|
|
5
|
+
import sys
|
|
6
|
+
import time
|
|
7
|
+
import socket
|
|
8
|
+
from io import StringIO
|
|
9
|
+
from configparser import ConfigParser
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
from .debugtime import debugtime
|
|
13
|
+
from .utils import clean_text
|
|
14
|
+
from .XPS_C8_drivers import XPS, XPSException
|
|
15
|
+
from .ftp_wrapper import SFTPWrapper, FTPWrapper
|
|
16
|
+
|
|
17
|
+
IDLE, ARMING, ARMED, RUNNING, COMPLETE, WRITING, READING = \
|
|
18
|
+
'IDLE', 'ARMING', 'ARMED', 'RUNNING', 'COMPLETE', 'WRITING', 'READING'
|
|
19
|
+
|
|
20
|
+
def withConnectedXPS(fcn):
|
|
21
|
+
"""decorator to ensure a NewportXPS is connected before a method is called"""
|
|
22
|
+
def wrapper(self, *args, **kwargs):
|
|
23
|
+
if self._sid is None or len(self.groups) < 1 or len(self.stages) < 1:
|
|
24
|
+
self.connect()
|
|
25
|
+
return fcn(self, *args, **kwargs)
|
|
26
|
+
wrapper.__doc__ = fcn.__doc__
|
|
27
|
+
wrapper.__name__ = fcn.__name__
|
|
28
|
+
wrapper.__dict__.update(fcn.__dict__)
|
|
29
|
+
|
|
30
|
+
return wrapper
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class NewportXPS:
|
|
34
|
+
gather_header = '# XPS Gathering Data\n#--------------'
|
|
35
|
+
def __init__(self, host, group=None,
|
|
36
|
+
username='Administrator', password='Administrator',
|
|
37
|
+
port=5001, timeout=10, extra_triggers=0,
|
|
38
|
+
outputs=('CurrentPosition', 'SetpointPosition')):
|
|
39
|
+
|
|
40
|
+
self.host = host
|
|
41
|
+
self.port = port
|
|
42
|
+
self.username = username
|
|
43
|
+
self.password = password
|
|
44
|
+
self.timeout = timeout
|
|
45
|
+
self.extra_triggers = extra_triggers
|
|
46
|
+
|
|
47
|
+
self.gather_outputs = tuple(outputs)
|
|
48
|
+
self.trajectories = {}
|
|
49
|
+
self.traj_state = IDLE
|
|
50
|
+
self.traj_group = None
|
|
51
|
+
self.traj_file = None
|
|
52
|
+
self.traj_positioners = None
|
|
53
|
+
|
|
54
|
+
self.nsegments = -1
|
|
55
|
+
self.stages = {}
|
|
56
|
+
self.groups = {}
|
|
57
|
+
self.firmware_version = None
|
|
58
|
+
|
|
59
|
+
self.ftpconn = None
|
|
60
|
+
self.ftpargs = dict(host=self.host,
|
|
61
|
+
username=self.username,
|
|
62
|
+
password=self.password)
|
|
63
|
+
self._sid = None
|
|
64
|
+
self._xps = XPS()
|
|
65
|
+
self.connect()
|
|
66
|
+
if group is not None:
|
|
67
|
+
self.set_trajectory_group(group)
|
|
68
|
+
|
|
69
|
+
def __repr__(self):
|
|
70
|
+
return f"NewportXPS(host='{self.host}', port={self.port})"
|
|
71
|
+
|
|
72
|
+
@withConnectedXPS
|
|
73
|
+
def status_report(self):
|
|
74
|
+
"""return printable status report"""
|
|
75
|
+
err, uptime = self._xps.ElapsedTimeGet(self._sid)
|
|
76
|
+
self.check_error(err, msg="Elapsed Time")
|
|
77
|
+
boottime = time.time() - uptime
|
|
78
|
+
out = ["# XPS host: %s (%s)" % (self.host, socket.getfqdn(self.host)),
|
|
79
|
+
"# Firmware: %s" % self.firmware_version,
|
|
80
|
+
"# Current Time: %s" % time.ctime(),
|
|
81
|
+
"# Last Reboot: %s" % time.ctime(boottime),
|
|
82
|
+
"# Trajectory Group: %s" % self.traj_group,
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
out.append("# Groups and Stages")
|
|
86
|
+
hstat = self.get_hardware_status()
|
|
87
|
+
perrs = self.get_positioner_errors()
|
|
88
|
+
|
|
89
|
+
for groupname, status in self.get_group_status().items():
|
|
90
|
+
this = self.groups[groupname]
|
|
91
|
+
out.append("%s (%s), Status: %s" %
|
|
92
|
+
(groupname, this['category'], status))
|
|
93
|
+
for pos in this['positioners']:
|
|
94
|
+
stagename = '%s.%s' % (groupname, pos)
|
|
95
|
+
stage = self.stages[stagename]
|
|
96
|
+
out.append(" %s (%s)" % (stagename, stage['stagetype']))
|
|
97
|
+
out.append(" Hardware Status: %s" % (hstat[stagename]))
|
|
98
|
+
out.append(" Positioner Errors: %s" % (perrs[stagename]))
|
|
99
|
+
return "\n".join(out)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def connect(self):
|
|
103
|
+
self._sid = self._xps.TCP_ConnectToServer(self.host,
|
|
104
|
+
self.port, self.timeout)
|
|
105
|
+
try:
|
|
106
|
+
self._xps.Login(self._sid, self.username, self.password)
|
|
107
|
+
except:
|
|
108
|
+
raise XPSException('Login failed for %s' % self.host)
|
|
109
|
+
|
|
110
|
+
err, val = self._xps.FirmwareVersionGet(self._sid)
|
|
111
|
+
self.firmware_version = val
|
|
112
|
+
self.ftphome = ''
|
|
113
|
+
|
|
114
|
+
if any([m in self.firmware_version for m in ['XPS-D', 'HXP-D']]):
|
|
115
|
+
err, val = self._xps.Send(self._sid, 'InstallerVersionGet(char *)')
|
|
116
|
+
self.firmware_version = val
|
|
117
|
+
self.ftpconn = SFTPWrapper(**self.ftpargs)
|
|
118
|
+
else:
|
|
119
|
+
self.ftpconn = FTPWrapper(**self.ftpargs)
|
|
120
|
+
if 'XPS-C' in self.firmware_version:
|
|
121
|
+
self.ftphome = '/Admin'
|
|
122
|
+
try:
|
|
123
|
+
self.read_systemini()
|
|
124
|
+
except:
|
|
125
|
+
print("Could not read system.ini!!!")
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def check_error(self, err, msg='', with_raise=True):
|
|
129
|
+
if err != 0:
|
|
130
|
+
err = "%d" % err
|
|
131
|
+
desc = self._xps.errorcodes.get(err, 'unknown error')
|
|
132
|
+
print("XPSError: message= %s, error=%s, description=%s" % (msg, err, desc))
|
|
133
|
+
if with_raise:
|
|
134
|
+
raise XPSException("%s %s [Error %s]" % (msg, desc, err))
|
|
135
|
+
|
|
136
|
+
def save_systemini(self, fname='system.ini'):
|
|
137
|
+
"""
|
|
138
|
+
save system.ini to disk
|
|
139
|
+
Parameters:
|
|
140
|
+
fname (string): name of file to save to ['system.ini']
|
|
141
|
+
"""
|
|
142
|
+
self.ftpconn.connect(**self.ftpargs)
|
|
143
|
+
self.ftpconn.cwd(posixpath.join(self.ftphome, 'Config'))
|
|
144
|
+
self.ftpconn.save('system.ini', fname)
|
|
145
|
+
self.ftpconn.close()
|
|
146
|
+
|
|
147
|
+
def save_stagesini(self, fname='stages.ini'):
|
|
148
|
+
"""save stages.ini to disk
|
|
149
|
+
|
|
150
|
+
Parameters:
|
|
151
|
+
fname (string): name of file to save to ['stages.ini']
|
|
152
|
+
"""
|
|
153
|
+
self.ftpconn.connect(**self.ftpargs)
|
|
154
|
+
self.ftpconn.cwd(posixpath.join(self.ftphome, 'Config'))
|
|
155
|
+
self.ftpconn.save('stages.ini', fname)
|
|
156
|
+
self.ftpconn.close()
|
|
157
|
+
|
|
158
|
+
def read_systemini(self):
|
|
159
|
+
"""read group info from system.ini
|
|
160
|
+
this is part of the connection process
|
|
161
|
+
"""
|
|
162
|
+
self.ftpconn.connect(**self.ftpargs)
|
|
163
|
+
self.ftpconn.cwd(posixpath.join(self.ftphome, 'Config'))
|
|
164
|
+
lines = self.ftpconn.getlines('system.ini')
|
|
165
|
+
self.ftpconn.close()
|
|
166
|
+
initext = '\n'.join([line.strip() for line in lines])
|
|
167
|
+
|
|
168
|
+
pvtgroups = []
|
|
169
|
+
self.stages= {}
|
|
170
|
+
self.groups = {}
|
|
171
|
+
sconf = ConfigParser()
|
|
172
|
+
sconf.readfp(StringIO(initext))
|
|
173
|
+
|
|
174
|
+
# read and populate lists of groups first
|
|
175
|
+
for gtype, glist in sconf.items('GROUPS'): # ].items():
|
|
176
|
+
if len(glist) > 0:
|
|
177
|
+
for gname in glist.split(','):
|
|
178
|
+
gname = gname.strip()
|
|
179
|
+
self.groups[gname] = {}
|
|
180
|
+
self.groups[gname]['category'] = gtype.strip()
|
|
181
|
+
self.groups[gname]['positioners'] = []
|
|
182
|
+
if gtype.lower().startswith('multiple'):
|
|
183
|
+
pvtgroups.append(gname)
|
|
184
|
+
|
|
185
|
+
for section in sconf.sections():
|
|
186
|
+
if section in ('DEFAULT', 'GENERAL', 'GROUPS'):
|
|
187
|
+
continue
|
|
188
|
+
items = sconf.options(section)
|
|
189
|
+
if section in self.groups: # this is a Group Section!
|
|
190
|
+
poslist = sconf.get(section, 'positionerinuse')
|
|
191
|
+
posnames = [a.strip() for a in poslist.split(',')]
|
|
192
|
+
self.groups[section]['positioners'] = posnames
|
|
193
|
+
elif 'plugnumber' in items: # this is a stage
|
|
194
|
+
self.stages[section] = {'stagetype': sconf.get(section, 'stagename')}
|
|
195
|
+
|
|
196
|
+
if len(pvtgroups) == 1:
|
|
197
|
+
self.set_trajectory_group(pvtgroups[0])
|
|
198
|
+
|
|
199
|
+
for sname in self.stages:
|
|
200
|
+
ret = self._xps.PositionerMaximumVelocityAndAccelerationGet(self._sid, sname)
|
|
201
|
+
try:
|
|
202
|
+
self.stages[sname]['max_velo'] = ret[1]
|
|
203
|
+
self.stages[sname]['max_accel'] = ret[2]/3.0
|
|
204
|
+
except:
|
|
205
|
+
print("could not set max velo/accel for %s" % sname)
|
|
206
|
+
ret = self._xps.PositionerUserTravelLimitsGet(self._sid, sname)
|
|
207
|
+
try:
|
|
208
|
+
self.stages[sname]['low_limit'] = ret[1]
|
|
209
|
+
self.stages[sname]['high_limit'] = ret[2]
|
|
210
|
+
except:
|
|
211
|
+
print("could not set limits for %s" % sname)
|
|
212
|
+
|
|
213
|
+
return self.groups
|
|
214
|
+
|
|
215
|
+
def download_trajectory(self, filename):
|
|
216
|
+
"""download text of trajectory file
|
|
217
|
+
|
|
218
|
+
Arguments:
|
|
219
|
+
----------
|
|
220
|
+
filename (str): name of trajectory file
|
|
221
|
+
text (str): full text of trajectory file
|
|
222
|
+
"""
|
|
223
|
+
self.ftpconn.connect(**self.ftpargs)
|
|
224
|
+
self.ftpconn.cwd(posixpath.join(self.ftphome, 'Public', 'Trajectories'))
|
|
225
|
+
self.ftpconn.save(filename, filename)
|
|
226
|
+
self.ftpconn.close()
|
|
227
|
+
|
|
228
|
+
def upload_trajectory(self, filename, text):
|
|
229
|
+
"""upload text of trajectory file
|
|
230
|
+
|
|
231
|
+
Arguments:
|
|
232
|
+
----------
|
|
233
|
+
filename (str): name of trajectory file
|
|
234
|
+
text (str): full text of trajectory file
|
|
235
|
+
"""
|
|
236
|
+
self.ftpconn.connect(**self.ftpargs)
|
|
237
|
+
self.ftpconn.cwd(posixpath.join(self.ftphome, 'Public', 'Trajectories'))
|
|
238
|
+
self.ftpconn.put(clean_text(text), filename)
|
|
239
|
+
self.ftpconn.close()
|
|
240
|
+
|
|
241
|
+
def list_scripts(self):
|
|
242
|
+
"""list all existent scripts files
|
|
243
|
+
"""
|
|
244
|
+
remotefiles = ""
|
|
245
|
+
self.ftpconn.connect(**self.ftpargs)
|
|
246
|
+
self.ftpconn.cwd(posixpath.join(self.ftphome, 'Public', 'Scripts'))
|
|
247
|
+
remotefiles = self.ftpconn.list()
|
|
248
|
+
self.ftpconn.close()
|
|
249
|
+
|
|
250
|
+
return remotefiles
|
|
251
|
+
|
|
252
|
+
def read_script(self, filename):
|
|
253
|
+
"""read script content
|
|
254
|
+
|
|
255
|
+
Arguments:
|
|
256
|
+
----------
|
|
257
|
+
filename (str): name of script file
|
|
258
|
+
"""
|
|
259
|
+
filecontent = ""
|
|
260
|
+
self.ftpconn.connect(**self.ftpargs)
|
|
261
|
+
self.ftpconn.cwd(posixpath.join(self.ftphome, 'Public', 'Scripts'))
|
|
262
|
+
filecontent = self.ftpconn.getlines(filename)
|
|
263
|
+
self.ftpconn.close()
|
|
264
|
+
|
|
265
|
+
return filecontent
|
|
266
|
+
|
|
267
|
+
def download_script(self, filename):
|
|
268
|
+
"""download script file
|
|
269
|
+
|
|
270
|
+
Arguments:
|
|
271
|
+
----------
|
|
272
|
+
filename (str): name of script file
|
|
273
|
+
"""
|
|
274
|
+
self.ftpconn.connect(**self.ftpargs)
|
|
275
|
+
self.ftpconn.cwd(posixpath.join(self.ftphome, 'Public', 'Scripts'))
|
|
276
|
+
self.ftpconn.save(filename, filename)
|
|
277
|
+
self.ftpconn.close()
|
|
278
|
+
|
|
279
|
+
def upload_script(self, filename, text):
|
|
280
|
+
"""upload script file
|
|
281
|
+
|
|
282
|
+
Arguments:
|
|
283
|
+
----------
|
|
284
|
+
filename (str): name of script file
|
|
285
|
+
text (str): full text of script file
|
|
286
|
+
"""
|
|
287
|
+
self.ftpconn.connect(**self.ftpargs)
|
|
288
|
+
self.ftpconn.cwd(posixpath.join(self.ftphome, 'Public', 'Scripts'))
|
|
289
|
+
self.ftpconn.put(clean_text(text), filename)
|
|
290
|
+
self.ftpconn.close()
|
|
291
|
+
|
|
292
|
+
def delete_script(self, filename):
|
|
293
|
+
"""delete script file
|
|
294
|
+
|
|
295
|
+
Arguments:
|
|
296
|
+
----------
|
|
297
|
+
filename (str): name of script file
|
|
298
|
+
"""
|
|
299
|
+
self.ftpconn.connect(**self.ftpargs)
|
|
300
|
+
self.ftpconn.cwd(posixpath.join(self.ftphome, 'Public', 'Scripts'))
|
|
301
|
+
self.ftpconn.delete(filename)
|
|
302
|
+
self.ftpconn.close()
|
|
303
|
+
|
|
304
|
+
def upload_systemini(self, text):
|
|
305
|
+
"""upload text of system.ini
|
|
306
|
+
|
|
307
|
+
Arguments:
|
|
308
|
+
----------
|
|
309
|
+
text (str): full text of system.ini
|
|
310
|
+
"""
|
|
311
|
+
self.ftpconn.connect(**self.ftpargs)
|
|
312
|
+
self.ftpconn.cwd(posixpath.join(self.ftphome, 'Config'))
|
|
313
|
+
self.ftpconn.put(clean_text(text), 'system.ini')
|
|
314
|
+
self.ftpconn.close()
|
|
315
|
+
|
|
316
|
+
def upload_stagesini(self, text):
|
|
317
|
+
"""upload text of stages.ini
|
|
318
|
+
|
|
319
|
+
Arguments:
|
|
320
|
+
----------
|
|
321
|
+
text (str): full text of stages.ini
|
|
322
|
+
|
|
323
|
+
Notes:
|
|
324
|
+
------
|
|
325
|
+
you may have to read the stages.ini file with:
|
|
326
|
+
>>> fh = open('mystages.ini', 'r', encoding='ISO8859')
|
|
327
|
+
>>> text = fh.read()
|
|
328
|
+
>>> xps.upload_stageini(text)
|
|
329
|
+
|
|
330
|
+
"""
|
|
331
|
+
self.ftpconn.connect(**self.ftpargs)
|
|
332
|
+
self.ftpconn.cwd(posixpath.join(self.ftphome, 'Config'))
|
|
333
|
+
self.ftpconn.put(clean_text(text), 'stages.ini')
|
|
334
|
+
self.ftpconn.close()
|
|
335
|
+
|
|
336
|
+
@withConnectedXPS
|
|
337
|
+
def set_tuning(self, stage, kp=None, ki=None, kd=None, ks=None,
|
|
338
|
+
inttime=None, dfilter=None, closedloopstatus=1,
|
|
339
|
+
gkp=None, gki=None, gkd=None, kform=None, ffgain=None):
|
|
340
|
+
"""set tuning parameters for a stage:
|
|
341
|
+
closedloopstatus, kp, ki, kd, ks, inttime, dfilter,
|
|
342
|
+
gkp, gki, gkd, kform, ffgain
|
|
343
|
+
"""
|
|
344
|
+
if stage not in self.stages:
|
|
345
|
+
print("Stage '%s' not found: " % stage)
|
|
346
|
+
return
|
|
347
|
+
params = self._xps.PositionerCorrectorPIDFFVelocityGet(self._sid, stage)
|
|
348
|
+
if params[0] != 0 or len(params) != 13:
|
|
349
|
+
print("error getting tuning parameters for %s" % stage)
|
|
350
|
+
return
|
|
351
|
+
|
|
352
|
+
params = params[1:]
|
|
353
|
+
params[0] = closedloopstatus
|
|
354
|
+
if kp is not None: params[1] = kp
|
|
355
|
+
if ki is not None: params[2] = ki
|
|
356
|
+
if kd is not None: params[3] = kd
|
|
357
|
+
if ks is not None: params[4] = ks
|
|
358
|
+
if inttime is not None: params[5] = inttime
|
|
359
|
+
if dfilter is not None: params[6] = dfilter
|
|
360
|
+
if gkp is not None: params[7] = gkp
|
|
361
|
+
if gki is not None: params[8] = gki
|
|
362
|
+
if gkd is not None: params[9] = gkd
|
|
363
|
+
if kform is not None: params[10] = kform
|
|
364
|
+
if ffgain is not None: params[11] = ffgain
|
|
365
|
+
ret = self._xps.PositionerCorrectorPIDFFVelocitySet(self._sid, stage, *params)
|
|
366
|
+
|
|
367
|
+
@withConnectedXPS
|
|
368
|
+
def get_tuning(self, stage):
|
|
369
|
+
"""get tuning parameters for a stage:
|
|
370
|
+
closedloopstatus, kp, ki, kd, ks, inttime, dfilter,
|
|
371
|
+
gkp, gki, gkd, kform, ffgain
|
|
372
|
+
"""
|
|
373
|
+
if stage not in self.stages:
|
|
374
|
+
print("Stage '%s' not found: " % stage)
|
|
375
|
+
return
|
|
376
|
+
params = self._xps.PositionerCorrectorPIDFFVelocityGet(self._sid, stage)
|
|
377
|
+
if params[0] != 0 or len(params) != 13:
|
|
378
|
+
print("error getting tuning parameters for %s" % stage)
|
|
379
|
+
return
|
|
380
|
+
|
|
381
|
+
params = params[1:]
|
|
382
|
+
out = {}
|
|
383
|
+
for i, name in enumerate(('closedloopstatus', 'kp', 'ki', 'kd', 'ks',
|
|
384
|
+
'inttime', 'dfilter', 'gkp', 'gki', 'gkd',
|
|
385
|
+
'kform', 'ffgain')):
|
|
386
|
+
out[name] = params[i]
|
|
387
|
+
return(out)
|
|
388
|
+
|
|
389
|
+
@withConnectedXPS
|
|
390
|
+
def set_trajectory_group(self, group, reenable=False):
|
|
391
|
+
"""set group name for upcoming trajectories"""
|
|
392
|
+
valid = False
|
|
393
|
+
if group in self.groups:
|
|
394
|
+
if self.groups[group]['category'].lower().startswith('multiple'):
|
|
395
|
+
valid = True
|
|
396
|
+
|
|
397
|
+
if not valid:
|
|
398
|
+
pvtgroups = []
|
|
399
|
+
for gname, group in self.groups.items():
|
|
400
|
+
if group['category'].lower().startswith('multiple'):
|
|
401
|
+
pvtgroups.append(gname)
|
|
402
|
+
pvtgroups = ', '.join(pvtgroups)
|
|
403
|
+
msg = "'%s' cannot be a trajectory group, must be one of %s"
|
|
404
|
+
raise XPSException(msg % (group, pvtgroups))
|
|
405
|
+
|
|
406
|
+
self.traj_group = group
|
|
407
|
+
self.traj_positioners = self.groups[group]['positioners']
|
|
408
|
+
|
|
409
|
+
if reenable:
|
|
410
|
+
try:
|
|
411
|
+
self.disable_group(self.traj_group)
|
|
412
|
+
except XPSException:
|
|
413
|
+
pass
|
|
414
|
+
|
|
415
|
+
time.sleep(0.1)
|
|
416
|
+
try:
|
|
417
|
+
self.enable_group(self.traj_group)
|
|
418
|
+
except XPSException:
|
|
419
|
+
print("Warning: could not enable trajectory group '%s'"% self.traj_group)
|
|
420
|
+
return
|
|
421
|
+
|
|
422
|
+
for i in range(64):
|
|
423
|
+
self._xps.EventExtendedRemove(self._sid, i)
|
|
424
|
+
|
|
425
|
+
# build template for linear trajectory file:
|
|
426
|
+
trajline1 = ['%(ramptime)f']
|
|
427
|
+
trajline2 = ['%(scantime)f']
|
|
428
|
+
trajline3 = ['%(ramptime)f']
|
|
429
|
+
for p in self.traj_positioners:
|
|
430
|
+
trajline1.append('%%(%s_ramp)f' % p)
|
|
431
|
+
trajline1.append('%%(%s_velo)f' % p)
|
|
432
|
+
trajline2.append('%%(%s_dist)f' % p)
|
|
433
|
+
trajline2.append('%%(%s_velo)f' % p)
|
|
434
|
+
trajline3.append('%%(%s_ramp)f' % p)
|
|
435
|
+
trajline3.append('%8.6f' % 0.0)
|
|
436
|
+
trajline1 = (','.join(trajline1)).strip()
|
|
437
|
+
trajline2 = (','.join(trajline2)).strip()
|
|
438
|
+
trajline3 = (','.join(trajline3)).strip()
|
|
439
|
+
self.linear_template = '\n'.join(['', trajline1, trajline2, trajline3])
|
|
440
|
+
self.linear_template = '\n'.join(['', trajline1, trajline2, trajline3, ''])
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
@withConnectedXPS
|
|
444
|
+
def _group_act(self, method, group=None, action='doing something',
|
|
445
|
+
with_raise=True):
|
|
446
|
+
"""wrapper for many group actions"""
|
|
447
|
+
method = getattr(self._xps, method)
|
|
448
|
+
if group is None:
|
|
449
|
+
for group in self.groups:
|
|
450
|
+
err, ret = method(self._sid, group)
|
|
451
|
+
self.check_error(err, msg="%s group '%s'" % (action, group),
|
|
452
|
+
with_raise=with_raise)
|
|
453
|
+
elif group in self.groups:
|
|
454
|
+
err, ret = method(self._sid, group)
|
|
455
|
+
self.check_error(err, msg="%s group '%s'" % (action, group),
|
|
456
|
+
with_raise=with_raise)
|
|
457
|
+
else:
|
|
458
|
+
raise ValueError("Group '%s' not found" % group)
|
|
459
|
+
|
|
460
|
+
def kill_group(self, group=None):
|
|
461
|
+
"""
|
|
462
|
+
initialize groups, optionally homing each.
|
|
463
|
+
|
|
464
|
+
Parameters:
|
|
465
|
+
with_encoder (bool): whethter to initialize with encoder [True]
|
|
466
|
+
home (bool): whether to home all groups [False]
|
|
467
|
+
"""
|
|
468
|
+
|
|
469
|
+
method = 'GroupKill'
|
|
470
|
+
self._group_act(method, group=group, action='killing')
|
|
471
|
+
|
|
472
|
+
def initialize_allgroups(self, with_encoder=True, home=False):
|
|
473
|
+
"""
|
|
474
|
+
initialize all groups, no homing
|
|
475
|
+
"""
|
|
476
|
+
for g in self.groups:
|
|
477
|
+
try:
|
|
478
|
+
self.initialize_group(group=g)
|
|
479
|
+
except XPSException:
|
|
480
|
+
print(f"Warning: could not initialize '{g}' (already initialized?)")
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
def home_allgroups(self, with_encoder=True, home=False):
|
|
484
|
+
"""
|
|
485
|
+
home all groups
|
|
486
|
+
"""
|
|
487
|
+
for g in self.groups:
|
|
488
|
+
self.home_group(group=g)
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
def initialize_group(self, group=None, with_encoder=True, home=False,
|
|
492
|
+
with_raise=True):
|
|
493
|
+
"""
|
|
494
|
+
initialize groups, optionally homing each.
|
|
495
|
+
|
|
496
|
+
Parameters:
|
|
497
|
+
with_encoder (bool): whethter to initialize with encoder [True]
|
|
498
|
+
home (bool): whether to home all groups [False]
|
|
499
|
+
"""
|
|
500
|
+
method = 'GroupInitialize'
|
|
501
|
+
if with_encoder:
|
|
502
|
+
method = 'GroupInitializeWithEncoderCalibration'
|
|
503
|
+
self._group_act(method, group=group, action='initializing',
|
|
504
|
+
with_raise=with_raise)
|
|
505
|
+
if home:
|
|
506
|
+
self.home_group(group=group, with_raise=with_raise)
|
|
507
|
+
|
|
508
|
+
def home_group(self, group=None, with_raise=True):
|
|
509
|
+
"""
|
|
510
|
+
home group
|
|
511
|
+
|
|
512
|
+
Parameters:
|
|
513
|
+
group (None or string): name of group to home [None]
|
|
514
|
+
|
|
515
|
+
Notes:
|
|
516
|
+
if group is `None`, all groups will be homed.
|
|
517
|
+
"""
|
|
518
|
+
self._group_act('GroupHomeSearch', group=group, action='homing',
|
|
519
|
+
with_raise=with_raise)
|
|
520
|
+
|
|
521
|
+
def enable_group(self, group=None):
|
|
522
|
+
"""enable group
|
|
523
|
+
|
|
524
|
+
Parameters:
|
|
525
|
+
group (None or string): name of group to enable [None]
|
|
526
|
+
|
|
527
|
+
Notes:
|
|
528
|
+
if group is `None`, all groups will be enabled.
|
|
529
|
+
"""
|
|
530
|
+
self._group_act('GroupMotionEnable', group=group, action='enabling')
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
def disable_group(self, group=None):
|
|
534
|
+
"""disable group
|
|
535
|
+
|
|
536
|
+
Parameters:
|
|
537
|
+
group (None or string): name of group to enable [None]
|
|
538
|
+
|
|
539
|
+
Notes:
|
|
540
|
+
if group is `None`, all groups will be enabled.
|
|
541
|
+
"""
|
|
542
|
+
self._group_act('GroupMotionDisable', group=group, action='disabling')
|
|
543
|
+
|
|
544
|
+
@withConnectedXPS
|
|
545
|
+
def get_group_status(self):
|
|
546
|
+
"""
|
|
547
|
+
get dictionary of status for each group
|
|
548
|
+
"""
|
|
549
|
+
out = {}
|
|
550
|
+
for group in self.groups:
|
|
551
|
+
err, stat = self._xps.GroupStatusGet(self._sid, group)
|
|
552
|
+
self.check_error(err, msg="GroupStatus '%s'" % (group))
|
|
553
|
+
|
|
554
|
+
err, val = self._xps.GroupStatusStringGet(self._sid, stat)
|
|
555
|
+
self.check_error(err, msg="GroupStatusString '%s'" % (stat))
|
|
556
|
+
|
|
557
|
+
out[group] = val
|
|
558
|
+
return out
|
|
559
|
+
|
|
560
|
+
@withConnectedXPS
|
|
561
|
+
def get_hardware_status(self):
|
|
562
|
+
"""
|
|
563
|
+
get dictionary of hardware status for each stage
|
|
564
|
+
"""
|
|
565
|
+
out = {}
|
|
566
|
+
for stage in self.stages:
|
|
567
|
+
if stage in ('', None): continue
|
|
568
|
+
err, stat = self._xps.PositionerHardwareStatusGet(self._sid, stage)
|
|
569
|
+
self.check_error(err, msg="Pos HardwareStatus '%s'" % (stage))
|
|
570
|
+
|
|
571
|
+
err, val = self._xps.PositionerHardwareStatusStringGet(self._sid, stat)
|
|
572
|
+
self.check_error(err, msg="Pos HardwareStatusString '%s'" % (stat))
|
|
573
|
+
out[stage] = val
|
|
574
|
+
return out
|
|
575
|
+
|
|
576
|
+
@withConnectedXPS
|
|
577
|
+
def get_positioner_errors(self):
|
|
578
|
+
"""
|
|
579
|
+
get dictionary of positioner errors for each stage
|
|
580
|
+
"""
|
|
581
|
+
out = {}
|
|
582
|
+
for stage in self.stages:
|
|
583
|
+
if stage in ('', None): continue
|
|
584
|
+
err, stat = self._xps.PositionerErrorGet(self._sid, stage)
|
|
585
|
+
self.check_error(err, msg="Pos Error '%s'" % (stage))
|
|
586
|
+
|
|
587
|
+
err, val = self._xps.PositionerErrorStringGet(self._sid, stat)
|
|
588
|
+
self.check_error(err, msg="Pos ErrorString '%s'" % (stat))
|
|
589
|
+
|
|
590
|
+
if len(val) < 1:
|
|
591
|
+
val = 'OK'
|
|
592
|
+
out[stage] = val
|
|
593
|
+
return out
|
|
594
|
+
|
|
595
|
+
@withConnectedXPS
|
|
596
|
+
def set_velocity(self, stage, velo, accl=None,
|
|
597
|
+
min_jerktime=None, max_jerktime=None):
|
|
598
|
+
"""
|
|
599
|
+
set velocity for stage
|
|
600
|
+
"""
|
|
601
|
+
if stage not in self.stages:
|
|
602
|
+
print("Stage '%s' not found" % stage)
|
|
603
|
+
return
|
|
604
|
+
ret, v_cur, a_cur, jt0_cur, jt1_cur = \
|
|
605
|
+
self._xps.PositionerSGammaParametersGet(self._sid, stage)
|
|
606
|
+
if accl is None:
|
|
607
|
+
accl = a_cur
|
|
608
|
+
if min_jerktime is None:
|
|
609
|
+
min_jerktime = jt0_cur
|
|
610
|
+
if max_jerktime is None:
|
|
611
|
+
max_jerktime = jt1_cur
|
|
612
|
+
self._xps.PositionerSGammaParametersSet(self._sid, stage, velo, accl,
|
|
613
|
+
min_jerktime, max_jerktime)
|
|
614
|
+
|
|
615
|
+
@withConnectedXPS
|
|
616
|
+
def abort_group(self, group=None):
|
|
617
|
+
"""abort group move"""
|
|
618
|
+
if group is None or group not in self.groups:
|
|
619
|
+
group = self.traj_group
|
|
620
|
+
if group is None:
|
|
621
|
+
print("Do have a group to move")
|
|
622
|
+
return
|
|
623
|
+
ret = self._xps.GroupMoveAbort(self._sid, group)
|
|
624
|
+
print('abort group ', group, ret)
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
@withConnectedXPS
|
|
628
|
+
def move_group(self, group=None, **kws):
|
|
629
|
+
"""move group to supplied position
|
|
630
|
+
"""
|
|
631
|
+
if group is None or group not in self.groups:
|
|
632
|
+
group = self.traj_group
|
|
633
|
+
if group is None:
|
|
634
|
+
print("Do have a group to move")
|
|
635
|
+
return
|
|
636
|
+
posnames = [p.lower() for p in self.groups[group]['positioners']]
|
|
637
|
+
ret = self._xps.GroupPositionCurrentGet(self._sid, group, len(posnames))
|
|
638
|
+
|
|
639
|
+
kwargs = {}
|
|
640
|
+
for k, v in kws.items():
|
|
641
|
+
kwargs[k.lower()] = v
|
|
642
|
+
|
|
643
|
+
vals = []
|
|
644
|
+
for i, p in enumerate(posnames):
|
|
645
|
+
if p in kwargs:
|
|
646
|
+
vals.append(kwargs[p])
|
|
647
|
+
else:
|
|
648
|
+
vals.append(ret[i+1])
|
|
649
|
+
self._xps.GroupMoveAbsolute(self._sid, group, vals)
|
|
650
|
+
|
|
651
|
+
@withConnectedXPS
|
|
652
|
+
def execute_script(self, script, task, arguments):
|
|
653
|
+
"""
|
|
654
|
+
Execute a TCL script
|
|
655
|
+
|
|
656
|
+
Parameters:
|
|
657
|
+
script (string): name of script file
|
|
658
|
+
task (string): task name to be identified
|
|
659
|
+
arguments (string): script arguments
|
|
660
|
+
"""
|
|
661
|
+
self._xps.TCLScriptExecute(self._sid, script, task, arguments)
|
|
662
|
+
|
|
663
|
+
@withConnectedXPS
|
|
664
|
+
def move_stage(self, stage, value, relative=False):
|
|
665
|
+
"""
|
|
666
|
+
move stage to position, optionally relative
|
|
667
|
+
|
|
668
|
+
Parameters:
|
|
669
|
+
stage (string): name of stage -- must be in self.stages
|
|
670
|
+
value (float): target position
|
|
671
|
+
relative (bool): whether move is relative [False]
|
|
672
|
+
"""
|
|
673
|
+
if stage not in self.stages:
|
|
674
|
+
print("Stage '%s' not found" % stage)
|
|
675
|
+
return
|
|
676
|
+
|
|
677
|
+
move = self._xps.GroupMoveAbsolute
|
|
678
|
+
if relative:
|
|
679
|
+
move = self._xps.GroupMoveRelative
|
|
680
|
+
|
|
681
|
+
err, ret = move(self._sid, stage, [value])
|
|
682
|
+
self.check_error(err, msg="Moving stage '%s'" % (stage))
|
|
683
|
+
return ret
|
|
684
|
+
|
|
685
|
+
@withConnectedXPS
|
|
686
|
+
def get_stage_position(self, stage):
|
|
687
|
+
"""
|
|
688
|
+
return current stage position
|
|
689
|
+
|
|
690
|
+
Parameters:
|
|
691
|
+
stage (string): name of stage -- must be in self.stages
|
|
692
|
+
"""
|
|
693
|
+
if stage not in self.stages:
|
|
694
|
+
print("Stage '%s' not found: " % stage)
|
|
695
|
+
return
|
|
696
|
+
|
|
697
|
+
err, val = self._xps.GroupPositionCurrentGet(self._sid, stage, 1)
|
|
698
|
+
self.check_error(err, msg="Get Stage Position '%s'" % (stage))
|
|
699
|
+
return val
|
|
700
|
+
|
|
701
|
+
read_stage_position = get_stage_position
|
|
702
|
+
|
|
703
|
+
@withConnectedXPS
|
|
704
|
+
def reboot(self, reconnect=True, timeout=120.0):
|
|
705
|
+
"""
|
|
706
|
+
reboot XPS, optionally waiting to reconnect
|
|
707
|
+
|
|
708
|
+
Parameters:
|
|
709
|
+
reconnect (bool): whether to wait for reconnection [True]
|
|
710
|
+
timeout (float): how long to wait before giving up, in seconds [60]
|
|
711
|
+
"""
|
|
712
|
+
self.ftpconn.close()
|
|
713
|
+
self._xps.CloseAllOtherSockets(self._sid)
|
|
714
|
+
self._xps.Reboot(self._sid)
|
|
715
|
+
self._sid = -1
|
|
716
|
+
self.groups = self.stages = self.stagetypes = None
|
|
717
|
+
time.sleep(5.0)
|
|
718
|
+
if reconnect:
|
|
719
|
+
maxtime = time.time() + timeout
|
|
720
|
+
while self._sid < 0:
|
|
721
|
+
time.sleep(5.0)
|
|
722
|
+
try:
|
|
723
|
+
self._sid = self._xps.TCP_ConnectToServer(self.host,
|
|
724
|
+
self.port,
|
|
725
|
+
self.timeout)
|
|
726
|
+
except:
|
|
727
|
+
print("Connection Failed ", time.ctime(), sys.exc_info())
|
|
728
|
+
|
|
729
|
+
if time.time() > maxtime:
|
|
730
|
+
break
|
|
731
|
+
if self._sid >=0:
|
|
732
|
+
self.connect()
|
|
733
|
+
else:
|
|
734
|
+
print("Could not reconnect to XPS.")
|
|
735
|
+
|
|
736
|
+
|
|
737
|
+
@withConnectedXPS
|
|
738
|
+
def define_line_trajectories(self, axis, group=None,
|
|
739
|
+
start=0, stop=1, step=0.001, scantime=10.0,
|
|
740
|
+
accel=None, upload=True, verbose=False):
|
|
741
|
+
"""defines 'forward' and 'backward' trajectories for a simple
|
|
742
|
+
single element line scan in PVT Mode
|
|
743
|
+
"""
|
|
744
|
+
if group is not None:
|
|
745
|
+
self.set_trajectory_group(group)
|
|
746
|
+
|
|
747
|
+
if self.traj_group is None:
|
|
748
|
+
print("Must define a trajectory group first!")
|
|
749
|
+
return
|
|
750
|
+
|
|
751
|
+
for axname in (axis, axis.upper(), axis.lower(), axis.title()):
|
|
752
|
+
stage = "%s.%s" % (self.traj_group, axname)
|
|
753
|
+
if stage in self.stages:
|
|
754
|
+
break
|
|
755
|
+
|
|
756
|
+
# print(" Stage ", stage, self.stages[stage])
|
|
757
|
+
max_velo = 0.75*self.stages[stage]['max_velo']
|
|
758
|
+
max_accel = 0.5*self.stages[stage]['max_accel']
|
|
759
|
+
|
|
760
|
+
if accel is None:
|
|
761
|
+
accel = max_accel
|
|
762
|
+
accel = min(accel, max_accel)
|
|
763
|
+
|
|
764
|
+
scandir = 1.0
|
|
765
|
+
if start > stop:
|
|
766
|
+
scandir = -1.0
|
|
767
|
+
step = scandir*abs(step)
|
|
768
|
+
|
|
769
|
+
npulses = int((abs(stop - start) + abs(step)*1.1) / abs(step))
|
|
770
|
+
scantime = float(abs(scantime))
|
|
771
|
+
pixeltime= scantime / (npulses-1)
|
|
772
|
+
scantime = pixeltime*npulses
|
|
773
|
+
|
|
774
|
+
distance = (abs(stop - start) + abs(step))*1.0
|
|
775
|
+
velocity = min(distance/scantime, max_velo)
|
|
776
|
+
|
|
777
|
+
ramptime = max(2.e-5, abs(velocity/accel))
|
|
778
|
+
rampdist = velocity*ramptime
|
|
779
|
+
offset = step/2.0 + scandir*rampdist
|
|
780
|
+
|
|
781
|
+
trajbase = {'axes': [axis], 'pixeltime': pixeltime,
|
|
782
|
+
'npulses': npulses, 'nsegments': 3}
|
|
783
|
+
|
|
784
|
+
self.trajectories['foreward'] = {'start': [start-offset],
|
|
785
|
+
'stop': [stop +offset]}
|
|
786
|
+
self.trajectories['foreward'].update(trajbase)
|
|
787
|
+
|
|
788
|
+
self.trajectories['backward'] = {'start': [stop +offset],
|
|
789
|
+
'stop': [start-offset]}
|
|
790
|
+
self.trajectories['backward'].update(trajbase)
|
|
791
|
+
|
|
792
|
+
base = {'start': start, 'stop': stop, 'step': step,
|
|
793
|
+
'velo': velocity, 'ramp': rampdist, 'dist': distance}
|
|
794
|
+
fore = {'ramptime': ramptime, 'scantime': scantime}
|
|
795
|
+
for attr in base:
|
|
796
|
+
for ax in self.traj_positioners:
|
|
797
|
+
val = 0.0
|
|
798
|
+
if ax == axis:
|
|
799
|
+
val = base[attr]
|
|
800
|
+
fore["%s_%s" % (ax, attr)] = val
|
|
801
|
+
|
|
802
|
+
back = fore.copy()
|
|
803
|
+
back["%s_start" % axis] = fore["%s_stop" % axis]
|
|
804
|
+
back["%s_stop" % axis] = fore["%s_start" % axis]
|
|
805
|
+
for attr in ('velo', 'ramp', 'dist'):
|
|
806
|
+
back["%s_%s" % (axis, attr)] *= -1.0
|
|
807
|
+
|
|
808
|
+
if verbose:
|
|
809
|
+
print("TRAJ Text Fore, Back:")
|
|
810
|
+
print(self.linear_template % fore)
|
|
811
|
+
print(self.linear_template % back)
|
|
812
|
+
|
|
813
|
+
ret = True
|
|
814
|
+
|
|
815
|
+
if upload:
|
|
816
|
+
ret = False
|
|
817
|
+
try:
|
|
818
|
+
self.upload_trajectory('foreward.trj',
|
|
819
|
+
self.linear_template % fore)
|
|
820
|
+
self.upload_trajectory('backward.trj',
|
|
821
|
+
self.linear_template % back)
|
|
822
|
+
ret = True
|
|
823
|
+
except:
|
|
824
|
+
raise ValueError("error uploading trajectory")
|
|
825
|
+
return ret
|
|
826
|
+
|
|
827
|
+
@withConnectedXPS
|
|
828
|
+
def arm_trajectory(self, name, verbose=False):
|
|
829
|
+
"""
|
|
830
|
+
set up the trajectory from previously defined, uploaded trajectory
|
|
831
|
+
"""
|
|
832
|
+
if self.traj_group is None:
|
|
833
|
+
print("Must set group name!")
|
|
834
|
+
|
|
835
|
+
traj = self.trajectories.get(name, None)
|
|
836
|
+
if traj is None:
|
|
837
|
+
raise XPSException("Cannot find trajectory named '%s'" % name)
|
|
838
|
+
|
|
839
|
+
self.traj_state = ARMING
|
|
840
|
+
self.traj_file = '%s.trj' % name
|
|
841
|
+
|
|
842
|
+
# move_kws = {}
|
|
843
|
+
outputs = []
|
|
844
|
+
for out in self.gather_outputs:
|
|
845
|
+
for i, ax in enumerate(traj['axes']):
|
|
846
|
+
outputs.append('%s.%s.%s' % (self.traj_group, ax, out))
|
|
847
|
+
|
|
848
|
+
end_segment = traj['nsegments'] # - 1 + self.extra_triggers
|
|
849
|
+
self.nsegments = end_segment
|
|
850
|
+
|
|
851
|
+
self.gather_titles = "%s\n#%s\n" % (self.gather_header, " ".join(outputs))
|
|
852
|
+
err, ret = self._xps.GatheringReset(self._sid)
|
|
853
|
+
self.check_error(err, msg="GatheringReset")
|
|
854
|
+
if verbose:
|
|
855
|
+
print(" GatheringReset returned ", ret)
|
|
856
|
+
|
|
857
|
+
err, ret = self._xps.GatheringConfigurationSet(self._sid, outputs)
|
|
858
|
+
self.check_error(err, msg="GatheringConfigSet")
|
|
859
|
+
|
|
860
|
+
if verbose:
|
|
861
|
+
print(" GatheringConfigurationSet outputs ", outputs)
|
|
862
|
+
print(" GatheringConfigurationSet returned ", ret)
|
|
863
|
+
print(" segments, pixeltime" , end_segment, traj['pixeltime'])
|
|
864
|
+
|
|
865
|
+
err, ret = self._xps.MultipleAxesPVTPulseOutputSet(self._sid, self.traj_group,
|
|
866
|
+
2, end_segment,
|
|
867
|
+
traj['pixeltime'])
|
|
868
|
+
self.check_error(err, msg="PVTPulseOutputSet", with_raise=False)
|
|
869
|
+
if verbose:
|
|
870
|
+
print(" PVTPulse ", ret)
|
|
871
|
+
err, ret = self._xps.MultipleAxesPVTVerification(self._sid,
|
|
872
|
+
self.traj_group,
|
|
873
|
+
self.traj_file)
|
|
874
|
+
|
|
875
|
+
self.check_error(err, msg="PVTVerification", with_raise=False)
|
|
876
|
+
if verbose:
|
|
877
|
+
print(" PVTVerify ", ret)
|
|
878
|
+
self.traj_state = ARMED
|
|
879
|
+
|
|
880
|
+
@withConnectedXPS
|
|
881
|
+
def run_trajectory(self, name=None, save=True, clean=False,
|
|
882
|
+
output_file='Gather.dat', verbose=False):
|
|
883
|
+
|
|
884
|
+
"""run a trajectory in PVT mode
|
|
885
|
+
|
|
886
|
+
The trajectory *must be in the ARMED state
|
|
887
|
+
"""
|
|
888
|
+
|
|
889
|
+
if 'xps-d' in self.firmware_version.lower():
|
|
890
|
+
self._xps.CleanTmpFolder(self._sid)
|
|
891
|
+
|
|
892
|
+
if clean:
|
|
893
|
+
self._xps.CleanCoreDumpFolder(self._sid)
|
|
894
|
+
|
|
895
|
+
if name in self.trajectories and self.traj_state != ARMED:
|
|
896
|
+
self.arm_trajectory(name, verbose=verbose)
|
|
897
|
+
|
|
898
|
+
if self.traj_state != ARMED:
|
|
899
|
+
raise XPSException("Must arm trajectory before running!")
|
|
900
|
+
|
|
901
|
+
buffer = ('Always', '%s.PVT.TrajectoryPulse' % self.traj_group,)
|
|
902
|
+
err, ret = self._xps.EventExtendedConfigurationTriggerSet(self._sid, buffer,
|
|
903
|
+
('0','0'), ('0','0'),
|
|
904
|
+
('0','0'), ('0','0'))
|
|
905
|
+
self.check_error(err, msg="EventConfigTrigger")
|
|
906
|
+
if verbose:
|
|
907
|
+
print( " EventExtended Trigger Set ", ret)
|
|
908
|
+
|
|
909
|
+
err, ret = self._xps.EventExtendedConfigurationActionSet(self._sid,
|
|
910
|
+
('GatheringOneData',),
|
|
911
|
+
('',), ('',),('',),('',))
|
|
912
|
+
self.check_error(err, msg="EventConfigAction")
|
|
913
|
+
if verbose:
|
|
914
|
+
print( " EventExtended Action Set ", ret)
|
|
915
|
+
|
|
916
|
+
eventID, m = self._xps.EventExtendedStart(self._sid)
|
|
917
|
+
self.traj_state = RUNNING
|
|
918
|
+
|
|
919
|
+
if verbose:
|
|
920
|
+
print( " EventExtended ExtendedStart ", eventID, m)
|
|
921
|
+
|
|
922
|
+
err, ret = self._xps.MultipleAxesPVTExecution(self._sid,
|
|
923
|
+
self.traj_group,
|
|
924
|
+
self.traj_file, 1)
|
|
925
|
+
self.check_error(err, msg="PVT Execute", with_raise=False)
|
|
926
|
+
if verbose:
|
|
927
|
+
print( " PVT Execute ", ret)
|
|
928
|
+
|
|
929
|
+
ret = self._xps.EventExtendedRemove(self._sid, eventID)
|
|
930
|
+
ret = self._xps.GatheringStop(self._sid)
|
|
931
|
+
|
|
932
|
+
self.traj_state = COMPLETE
|
|
933
|
+
npulses = 0
|
|
934
|
+
if save:
|
|
935
|
+
self.read_and_save(output_file, verbose=verbose)
|
|
936
|
+
self.traj_state = IDLE
|
|
937
|
+
return npulses
|
|
938
|
+
|
|
939
|
+
@withConnectedXPS
|
|
940
|
+
def read_and_save(self, output_file, verbose=False):
|
|
941
|
+
"read and save gathering file"
|
|
942
|
+
self.ngathered = 0
|
|
943
|
+
npulses, buff = self.read_gathering(set_idle_when_done=False,
|
|
944
|
+
verbose=verbose)
|
|
945
|
+
if npulses < 1:
|
|
946
|
+
return
|
|
947
|
+
self.save_gathering_file(output_file, buff,
|
|
948
|
+
verbose=verbose,
|
|
949
|
+
set_idle_when_done=False)
|
|
950
|
+
self.ngathered = npulses
|
|
951
|
+
|
|
952
|
+
@withConnectedXPS
|
|
953
|
+
def read_gathering(self, set_idle_when_done=True, verbose=False,
|
|
954
|
+
debug_time=False):
|
|
955
|
+
"""
|
|
956
|
+
read gathering data from XPS
|
|
957
|
+
"""
|
|
958
|
+
verbose = verbose or debug_time
|
|
959
|
+
if verbose:
|
|
960
|
+
print("READ Gathering XPS ", self.host, self._sid,
|
|
961
|
+
self.nsegments, time.ctime())
|
|
962
|
+
dt = debugtime()
|
|
963
|
+
self.traj_state = READING
|
|
964
|
+
npulses = -1
|
|
965
|
+
t0 = time.time()
|
|
966
|
+
while npulses < 1:
|
|
967
|
+
try:
|
|
968
|
+
ret, npulses, nx = self._xps.GatheringCurrentNumberGet(self._sid)
|
|
969
|
+
except SyntaxError:
|
|
970
|
+
print("#XPS Gathering Read failed, will try again")
|
|
971
|
+
pass
|
|
972
|
+
if time.time()-t0 > 5:
|
|
973
|
+
print("Failed to get gathering size after 5 seconds: return 0 points")
|
|
974
|
+
print("Gather Returned: ", ret, npulses, nx, self._xps, time.ctime())
|
|
975
|
+
return (0, ' \n')
|
|
976
|
+
if npulses < 1 or ret != 0:
|
|
977
|
+
time.sleep(0.05)
|
|
978
|
+
dt.add("gather num %d npulses=%d (%d)" % (ret, npulses, self.nsegments))
|
|
979
|
+
counter = 0
|
|
980
|
+
while npulses < 1 and counter < 5:
|
|
981
|
+
counter += 1
|
|
982
|
+
time.sleep(0.25)
|
|
983
|
+
ret, npulses, nx = self._xps.GatheringCurrentNumberGet(self._sid)
|
|
984
|
+
print( 'Had to do repeat XPS Gathering: ', ret, npulses, nx)
|
|
985
|
+
dt.add("gather before multilinesget, npulses=%d" % (npulses))
|
|
986
|
+
try:
|
|
987
|
+
ret, buff = self._xps.GatheringDataMultipleLinesGet(self._sid, 0, npulses)
|
|
988
|
+
except ValueError:
|
|
989
|
+
print("Failed to read gathering: ", ret, buff)
|
|
990
|
+
return (0, ' \n')
|
|
991
|
+
dt.add("gather after multilinesget %d" % ret)
|
|
992
|
+
nchunks = -1
|
|
993
|
+
if ret < 0: # gathering too long: need to read in chunks
|
|
994
|
+
nchunks = 3
|
|
995
|
+
nx = int((npulses-2) / nchunks)
|
|
996
|
+
ret = 1
|
|
997
|
+
while True:
|
|
998
|
+
time.sleep(0.05)
|
|
999
|
+
ret, xbuff = self._xps.GatheringDataMultipleLinesGet(self._sid, 0, nx)
|
|
1000
|
+
if ret == 0:
|
|
1001
|
+
break
|
|
1002
|
+
nchunks = nchunks + 2
|
|
1003
|
+
nx = int((npulses-2) / nchunks)
|
|
1004
|
+
if nchunks > 10:
|
|
1005
|
+
print('looks like something is wrong with the XPS!')
|
|
1006
|
+
break
|
|
1007
|
+
buff = [xbuff]
|
|
1008
|
+
for i in range(1, nchunks):
|
|
1009
|
+
ret, xbuff = self._xps.GatheringDataMultipleLinesGet(self._sid, i*nx, nx)
|
|
1010
|
+
buff.append(xbuff)
|
|
1011
|
+
ret, xbuff = self._xps.GatheringDataMultipleLinesGet(self._sid, nchunks*nx,
|
|
1012
|
+
npulses-nchunks*nx)
|
|
1013
|
+
buff.append(xbuff)
|
|
1014
|
+
buff = ''.join(buff)
|
|
1015
|
+
dt.add("gather after got buffer %d" % len(buff))
|
|
1016
|
+
obuff = buff[:]
|
|
1017
|
+
for x in ';\r\t':
|
|
1018
|
+
obuff = obuff.replace(x,' ')
|
|
1019
|
+
dt.add("gather cleaned buffer %d" % len(obuff))
|
|
1020
|
+
if set_idle_when_done:
|
|
1021
|
+
self.traj_state = IDLE
|
|
1022
|
+
if verbose:
|
|
1023
|
+
dt.show()
|
|
1024
|
+
return npulses, obuff
|
|
1025
|
+
|
|
1026
|
+
def save_gathering_file(self, fname, buff, verbose=False, set_idle_when_done=True):
|
|
1027
|
+
"""save gathering buffer read from read_gathering() to text file"""
|
|
1028
|
+
self.traj_state = WRITING
|
|
1029
|
+
f = open(fname, 'w')
|
|
1030
|
+
f.write(self.gather_titles)
|
|
1031
|
+
f.write(buff)
|
|
1032
|
+
f.close()
|
|
1033
|
+
nlines = len(buff.split('\n')) - 1
|
|
1034
|
+
if verbose:
|
|
1035
|
+
print('Wrote %i lines, %i bytes to %s' % (nlines, len(buff), fname))
|
|
1036
|
+
if set_idle_when_done:
|
|
1037
|
+
self.traj_state = IDLE
|
|
1038
|
+
|
|
1039
|
+
def define_line_trajectories_general(self, name='default',
|
|
1040
|
+
start_values=None,
|
|
1041
|
+
stop_values=None,
|
|
1042
|
+
accel_values=None,
|
|
1043
|
+
pulse_time=0.1, scan_time=10.0):
|
|
1044
|
+
"""
|
|
1045
|
+
Clemens' code for line trajectories -- should probably be
|
|
1046
|
+
unified with define_line_trajectories(),
|
|
1047
|
+
"""
|
|
1048
|
+
if start_values is None:
|
|
1049
|
+
start_values = np.zeros(len(self.traj_positioners))
|
|
1050
|
+
else:
|
|
1051
|
+
start_values = np.array(start_values)
|
|
1052
|
+
|
|
1053
|
+
if stop_values is None:
|
|
1054
|
+
stop_values = np.zeros(len(self.traj_positioners))
|
|
1055
|
+
else:
|
|
1056
|
+
stop_values = np.array(stop_values)
|
|
1057
|
+
|
|
1058
|
+
if len(stop_values.shape) > 2:
|
|
1059
|
+
stop_values = stop_values[0]
|
|
1060
|
+
print("Cannot yet do multi-segment lines -- only doing first section")
|
|
1061
|
+
|
|
1062
|
+
|
|
1063
|
+
if accel_values is None:
|
|
1064
|
+
accel_values = []
|
|
1065
|
+
for posname in self.traj_positioners:
|
|
1066
|
+
accel = self.stages["%s.%s"%(self.traj_group, posname)]['max_accel']
|
|
1067
|
+
accel_values.append(accel)
|
|
1068
|
+
accel_values = np.array(accel_values)
|
|
1069
|
+
|
|
1070
|
+
distances = stop_values - start_values
|
|
1071
|
+
velocities = abs(distances / (scan_time))
|
|
1072
|
+
scan_time = float(abs(scan_time))
|
|
1073
|
+
|
|
1074
|
+
ramp_time = 1.5 * max(abs(velocities / accel_values))
|
|
1075
|
+
ramp = velocities * ramp_time
|
|
1076
|
+
print("ramp : ", ramp_time, ramp)
|
|
1077
|
+
|
|
1078
|
+
ramp_attr = {'ramptime': ramp_time}
|
|
1079
|
+
down_attr = {'ramptime': ramp_time}
|
|
1080
|
+
|
|
1081
|
+
for ind, positioner in enumerate(self.traj_positioners):
|
|
1082
|
+
ramp_attr[positioner + 'ramp'] = ramp[ind]
|
|
1083
|
+
ramp_attr[positioner + 'velo'] = velocities[ind]
|
|
1084
|
+
|
|
1085
|
+
down_attr[positioner + 'ramp'] = ramp[ind]
|
|
1086
|
+
down_attr[positioner + 'zero'] = 0
|
|
1087
|
+
|
|
1088
|
+
ramp_template = "%(ramptime)f"
|
|
1089
|
+
move_template = "%(scantime)f"
|
|
1090
|
+
down_template = "%(ramptime)f"
|
|
1091
|
+
|
|
1092
|
+
for positioner in self.traj_positioners:
|
|
1093
|
+
ramp_template += ", %({0}ramp)f, %({0}velo)f".format(positioner)
|
|
1094
|
+
move_template += ", %({0}dist)f, %({0}velo)f".format(positioner)
|
|
1095
|
+
down_template += ", %({0}ramp)f, %({0}zero)f".format(positioner)
|
|
1096
|
+
|
|
1097
|
+
ramp_str = ramp_template % ramp_attr
|
|
1098
|
+
down_str = down_template % down_attr
|
|
1099
|
+
move_strings = []
|
|
1100
|
+
|
|
1101
|
+
attr = {'scantime': scan_time}
|
|
1102
|
+
for pos_ind, positioner in enumerate(self.traj_positioners):
|
|
1103
|
+
attr[positioner + 'dist'] = distances[pos_ind]
|
|
1104
|
+
attr[positioner + 'velo'] = velocities[pos_ind]
|
|
1105
|
+
move_strings.append(move_template % attr)
|
|
1106
|
+
|
|
1107
|
+
#construct trajectory:
|
|
1108
|
+
trajectory_str = ramp_str + '\n'
|
|
1109
|
+
for move_string in move_strings:
|
|
1110
|
+
trajectory_str += move_string + '\n'
|
|
1111
|
+
trajectory_str += down_str + '\n'
|
|
1112
|
+
|
|
1113
|
+
self.trajectories[name] = {'pulse_time': pulse_time,
|
|
1114
|
+
'step_number': len(distances)}
|
|
1115
|
+
|
|
1116
|
+
for ind, positioner in enumerate(self.traj_positioners):
|
|
1117
|
+
self.trajectories[name][positioner + 'ramp'] = ramp[ind]
|
|
1118
|
+
|
|
1119
|
+
ret = False
|
|
1120
|
+
try:
|
|
1121
|
+
self.upload_trajectory(name + '.trj', trajectory_str)
|
|
1122
|
+
ret = True
|
|
1123
|
+
# print('Trajectory File uploaded.')
|
|
1124
|
+
except:
|
|
1125
|
+
print('Failed to upload trajectory file')
|
|
1126
|
+
|
|
1127
|
+
return trajectory_str
|
|
1128
|
+
|
|
1129
|
+
def run_line_trajectory_general(self, name='default', verbose=False, save=True,
|
|
1130
|
+
outfile='Gather.dat'):
|
|
1131
|
+
"""run trajectory in PVT mode"""
|
|
1132
|
+
traj = self.trajectories.get(name, None)
|
|
1133
|
+
if traj is None:
|
|
1134
|
+
print('Cannot find trajectory named %s' % name)
|
|
1135
|
+
return
|
|
1136
|
+
|
|
1137
|
+
traj_file = '%s.trj' % name
|
|
1138
|
+
dtime = traj['pulse_time']
|
|
1139
|
+
ramps = []
|
|
1140
|
+
for positioner in self.traj_positioners:
|
|
1141
|
+
ramps.append(-traj[positioner + 'ramp'])
|
|
1142
|
+
ramps = np.array(ramps)
|
|
1143
|
+
|
|
1144
|
+
try:
|
|
1145
|
+
step_number = traj['step_number']
|
|
1146
|
+
except KeyError:
|
|
1147
|
+
step_number = 1
|
|
1148
|
+
|
|
1149
|
+
self._xps.GroupMoveRelative(self._sid, self.traj_group, ramps)
|
|
1150
|
+
|
|
1151
|
+
# self.gather_outputs = []
|
|
1152
|
+
## gather_titles = []
|
|
1153
|
+
|
|
1154
|
+
# for positioner in self.traj_positioners:
|
|
1155
|
+
# for out in self.gather_outputs:
|
|
1156
|
+
# self.gather_outputs.append('%s.%s.%s' % (self.traj_group, positioner, out))
|
|
1157
|
+
# gather_titles.append('%s.%s' % (positioner, out))
|
|
1158
|
+
## self.gather_titles = "%s\n#%s\n" % (xps_config['GATHER TITLES'],
|
|
1159
|
+
## " ".join(gather_titles))
|
|
1160
|
+
|
|
1161
|
+
outputs = []
|
|
1162
|
+
for out in self.gather_outputs:
|
|
1163
|
+
for i, ax in enumerate(traj['axes']):
|
|
1164
|
+
outputs.append('%s.%s.%s' % (self.traj_group, ax, out))
|
|
1165
|
+
# move_kws[ax] = float(traj['start'][i])
|
|
1166
|
+
|
|
1167
|
+
end_segment = traj['nsegments'] - 1 + self.extra_triggers
|
|
1168
|
+
# self.move_group(self.traj_group, **move_kws)
|
|
1169
|
+
self.gather_titles = "%s\n#%s\n" % (self.gather_header, " ".join(outputs))
|
|
1170
|
+
|
|
1171
|
+
self._xps.GatheringReset(self._sid)
|
|
1172
|
+
self._xps.GatheringConfigurationSet(self._sid, self.gather_outputs)
|
|
1173
|
+
|
|
1174
|
+
# print("step_number", step_number)
|
|
1175
|
+
ret = self._xps.MultipleAxesPVTPulseOutputSet(self._sid, self.traj_group,
|
|
1176
|
+
2, step_number + 1, dtime)
|
|
1177
|
+
ret = self._xps.MultipleAxesPVTVerification(self._sid, self.traj_group, traj_file)
|
|
1178
|
+
|
|
1179
|
+
buffer = ('Always', self.traj_group + '.PVT.TrajectoryPulse')
|
|
1180
|
+
o = self._xps.EventExtendedConfigurationTriggerSet(self._sid, buffer,
|
|
1181
|
+
('0', '0'), ('0', '0'),
|
|
1182
|
+
('0', '0'), ('0', '0'))
|
|
1183
|
+
|
|
1184
|
+
o = self._xps.EventExtendedConfigurationActionSet(self._sid, ('GatheringOneData',),
|
|
1185
|
+
('',), ('',), ('',), ('',))
|
|
1186
|
+
|
|
1187
|
+
eventID, m = self._xps.EventExtendedStart(self._sid)
|
|
1188
|
+
|
|
1189
|
+
ret = self._xps.MultipleAxesPVTExecution(self._sid, self.traj_group, traj_file, 1)
|
|
1190
|
+
o = self._xps.EventExtendedRemove(self._sid, eventID)
|
|
1191
|
+
o = self._xps.GatheringStop(self._sid)
|
|
1192
|
+
|
|
1193
|
+
npulses = 0
|
|
1194
|
+
if save:
|
|
1195
|
+
npulses, outbuff = self.read_and_save(outfile)
|
|
1196
|
+
|
|
1197
|
+
self._xps.GroupMoveRelative(self._sid, self.traj_group, ramps)
|
|
1198
|
+
return npulses
|
|
1199
|
+
|
|
1200
|
+
|
|
1201
|
+
|
|
1202
|
+
if __name__ == '__main__':
|
|
1203
|
+
import sys
|
|
1204
|
+
ipaddr = sys.argv[1]
|
|
1205
|
+
x = NewportXPS(ipaddr)
|
|
1206
|
+
x.read_systemini()
|
|
1207
|
+
print(x.status_report())
|