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.
@@ -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())