update phytron driver

+ fixes in secop_psi/sea.py (iodev)
This commit is contained in:
l_samenv 2022-04-22 16:25:14 +02:00
parent f6f2dd189b
commit e9f687e6dd
3 changed files with 57 additions and 31 deletions

View File

@ -5,15 +5,15 @@ description = phytron motor test
[INTERFACE]
uri = tcp://5000
[drv_iodev]
[drv_io]
description =
class = secop_psi.phytron.PhytronIO
uri = ma15-ts.psi.ch:3005
uri = ma10-ts.psi.ch:3004
# uri = serial:///dev/tty.usbserial?baudrate=57600
# uri = serial:///dev/ttyUSB0?baudrate=9600
[drv]
description = a phytron motor
class = secop_psi.phytron.Motor
iodev = drv_iodev
io = drv_io
encoder_mode = CHECK

View File

@ -23,50 +23,62 @@
"""driver for pythron motors"""
from secop.core import Done, Command, EnumType, FloatRange, IntRange, \
HasIodev, Parameter, Property, Drivable, PersistentMixin, PersistentParam, StringIO, StringType
HasIO, Parameter, Property, Drivable, PersistentMixin, PersistentParam, StringIO, StringType
from secop.errors import CommunicationFailedError, HardwareError
class PhytronIO(StringIO):
end_of_line = '\x03' # ETX
timeout = 0.2
identification = [('0IVR', 'MCC Minilog .*')]
def communicate(self, command):
def communicate(self, command, expect_response=True):
for ntry in range(5, 0, -1):
try:
head, _, reply = super().communicate('\x02' + command).partition('\x02')
if reply[0] != '\x06': # ACK
if reply[0] == '\x06': # ACK
if len(reply) == 1 and expect_response:
raise CommunicationFailedError('empty response')
break
raise CommunicationFailedError('missing ACK %r' % reply)
except Exception as e:
if ntry == 1:
raise
self.log.warning('%s - retry', e)
return reply[1:]
class Motor(PersistentMixin, HasIodev, Drivable):
class Motor(PersistentMixin, HasIO, Drivable):
axis = Property('motor axis X or Y', StringType(), default='X')
address = Property('address', IntRange(0, 15), default=0)
speed_factor = Property('steps / degree', FloatRange(0, None), default=2000)
encoder_mode = Parameter('how to treat the encoder', EnumType('encoder', NO=0, READ=1, CHECK=2),
default=1, readonly=False)
value = Parameter('angle', FloatRange(unit='deg'), poll=True)
value = Parameter('angle', FloatRange(unit='deg'))
target = Parameter('target angle', FloatRange(unit='deg'), readonly=False)
speed = Parameter('', FloatRange(0, 20, unit='deg/s'), readonly=False, poll=True)
accel = Parameter('', FloatRange(2, 250, unit='deg/s/s'), readonly=False, poll=True)
speed = Parameter('', FloatRange(0, 20, unit='deg/s'), readonly=False)
accel = Parameter('', FloatRange(2, 250, unit='deg/s/s'), readonly=False)
encoder_tolerance = Parameter('', FloatRange(unit='deg'), readonly=False, default=0.01)
zero = PersistentParam('', FloatRange(unit='deg'), readonly=False, default=0)
encoder = Parameter('encoder reading', FloatRange(unit='deg'), poll=True)
encoder = Parameter('encoder reading', FloatRange(unit='deg'))
sameside_offset = Parameter('offset when always approaching from the same side',
FloatRange(unit='deg'), readonly=True, default=0)
FloatRange(unit='deg'), readonly=False, default=0)
iodevClass = PhytronIO
fast_pollfactor = 0.02
ioClass = PhytronIO
fast_poll = 0.1
_sameside_pending = False
_mismatch_count = 0
def earlyInit(self):
super().earlyInit()
self.loadParameters()
def get(self, cmd):
return self._iodev.communicate('\x02%x%s%s' % (self.address, self.axis, cmd))
return self.communicate('\x02%x%s%s' % (self.address, self.axis, cmd))
def set(self, cmd, value):
self._iodev.communicate('\x02%x%s%s%g' % (self.address, self.axis, cmd, value))
self.communicate('\x02%x%s%s%g' % (self.address, self.axis, cmd, value), False)
def set_get(self, cmd, value, query):
self.set(cmd, value)
@ -79,6 +91,9 @@ class Motor(PersistentMixin, HasIodev, Drivable):
enc = self.read_encoder()
else:
enc = pos
status = self.communicate('\x02%xSE' % self.address)
status = status[0:4] if self.axis == 'X' else status[4:8]
self.log.debug('run %s enc %s end %s', status[1], status[2], status[3])
status = self.get('=H')
if status == 'N':
if self.encoder_mode == 'CHECK':
@ -86,9 +101,11 @@ class Motor(PersistentMixin, HasIodev, Drivable):
if e1 - self.encoder_tolerance <= pos <= e2 + self.encoder_tolerance:
self.status = self.Status.BUSY, 'driving'
else:
self.log.error('encoder lag: %g not within %g..%g' % (pos, e1, e2))
self.log.error('encoder lag: %g not within %g..%g',
pos, e1, e2)
self.get('S') # stop
self.status = self.Status.ERROR, 'encoder lag error'
self.setFastPoll(False)
else:
self.status = self.Status.BUSY, 'driving'
else:
@ -99,9 +116,16 @@ class Motor(PersistentMixin, HasIodev, Drivable):
return pos
if (self.encoder_mode == 'CHECK' and
abs(enc - pos) > self.encoder_tolerance):
if self._mismatch_count < 2:
self._mismatch_count += 1
else:
self.log.error('encoder mismatch: abs(%g - %g) < %g',
enc, pos, self.encoder_tolerance)
self.status = self.Status.ERROR, 'encoder does not match pos'
else:
self._mismatch_count = 0
self.status = self.Status.IDLE, ''
self.setFastPoll(False)
return pos
def read_encoder(self):
@ -113,8 +137,9 @@ class Motor(PersistentMixin, HasIodev, Drivable):
return float(self.get('P14R')) / self.speed_factor
def write_speed(self, value):
if abs(float(self.get('P03R')) * self.speed_factor - 1) > 0.001:
raise HardwareError('speed factor does not match')
inv_factor = float(self.get('P03R'))
if abs(inv_factor * self.speed_factor - 1) > 0.001:
raise HardwareError('speed factor does not match %g' % (1.0 / inv_factor))
return float(self.set_get('P14S', int(value * self.speed_factor), 'P14R')) / self.speed_factor
def read_accel(self):
@ -136,6 +161,7 @@ class Motor(PersistentMixin, HasIodev, Drivable):
self.set('A', value - self.zero + self.sameside_offset)
else:
self.set('A', value - self.zero)
self.setFastPoll(True, self.fast_poll)
return value
def write_zero(self, value):

View File

@ -62,7 +62,7 @@ service = %(service)s
CFG_MODULE = """
[%(module)s]
class = secop_psi.sea.%(modcls)s
iodev = %(seaconn)s
io = %(seaconn)s
sea_object = %(module)s
"""
@ -367,7 +367,7 @@ def get_datatype(paramdesc):
class SeaModule(Module):
iodev = Attached()
io = Attached()
# pollerClass=None
path2param = None
@ -380,7 +380,7 @@ class SeaModule(Module):
else:
extra_modules = {}
srv.extra_sea_modules = extra_modules
json_file = cfgdict.pop('json_file', None) or SeaClient.default_json_file[cfgdict['iodev']]
json_file = cfgdict.pop('json_file', None) or SeaClient.default_json_file[cfgdict['io']]
visibility_level = cfgdict.pop('visibility_level', 2)
single_module = cfgdict.pop('single_module', None)
@ -518,7 +518,7 @@ class SeaModule(Module):
# print('override %s.read_%s' % (cls.__name__, key))
def rfunc(self, cmd='hval %s/%s' % (base, path)):
reply = self._iodev.query(cmd)
reply = self.io.query(cmd)
try:
reply = float(reply)
except ValueError:
@ -538,7 +538,7 @@ class SeaModule(Module):
value = int(value)
# TODO: check if more has to be done for valid tcl data (strings?)
cmd = "%s %s" % (command, value)
self._iodev.query(cmd)
self.io.query(cmd)
return Done
attributes['write_' + key] = wfunc
@ -584,7 +584,7 @@ class SeaModule(Module):
self.DISPATCHER.broadcast_event(make_update(self.name, pobj))
def initModule(self):
self._iodev.register_obj(self, self.sea_object)
self.io.register_obj(self, self.sea_object)
super().initModule()
@ -623,7 +623,7 @@ class SeaDrivable(SeaModule, Drivable):
# return self.target
def write_target(self, value):
self._iodev.query('run %s %s' % (self.sea_object, value))
self.io.query('run %s %s' % (self.sea_object, value))
#self.status = [self.Status.BUSY, 'driving']
return value
@ -656,4 +656,4 @@ class SeaDrivable(SeaModule, Drivable):
- on stdsct drivables this will call the halt script
- on EaseDriv this will set the stopped state
"""
self._iodev.query('%s is_running 0' % self.sea_object)
self.io.query('%s is_running 0' % self.sea_object)