mupd: various fixes frappy_psi.dpm/trinamic

- make sure motor is stopped even when the wring limit switch is hit
- poll load_cell sensors at he same interval as the drive motor
This commit is contained in:
2026-07-03 17:30:33 +02:00
parent 040945dbaa
commit 8ab30428a7
3 changed files with 92 additions and 53 deletions
+47 -30
View File
@@ -3,13 +3,13 @@ Node('mupd.psi.ch',
interface='tcp://5000',
)
usb_sample = 'serial:///dev/serial/by-path/platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.3.4:1.0-port0'
usb_displacement = 'serial:///dev/serial/by-path/platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.3.1:1.0-port0'
usb_displacement = 'serial:///dev/serial/by-path/platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.3.4:1.0-port0'
usb_pillars = 'serial:///dev/serial/by-path/platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.3.1:1.0-port0'
usb_load_cell = 'serial:///dev/serial/by-path/platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.3.2:1.0-port0'
usb_drv = 'serial:///dev/serial/by-path/platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.3.3:1.0-port0?baudrate=38400'
usb_drv = 'serial:///dev/serial/by-path/platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.3.3:1.0-port0?baudrate=9600'
if not 'drv':
if 'drv':
Mod('drv',
'frappy_psi.trinamic.Motor',
'trinamic motor test',
@@ -25,39 +25,56 @@ if not 'drv':
power_down_delay=0.1,
has_limit_switches=True,
has_home=True,
fast_poll = 0.1,
)
Mod('F_sample',
'frappy_psi.dpm.DPM3',
'DPM driver to read out the transducer value, write and read the offset and scale factor',
# drive='drv',
value=Param(unit='N'),
uri=usb_sample,
digits=3,
scale_factor=100,
offset=0,
)
if not 'pillars':
Mod('pillars',
'frappy_psi.dpm.DPM3',
'DPM driver to read out the transducer value, write and read the offset and scale factor',
drive='drv',
value=Param(unit='N'),
uri=usb_pillars,
digits=1,
scale_factor=100,
offset=0,
alarm_limit=2000,
)
Mod('F_displacement',
'frappy_psi.dpm.DPM3',
'DPM driver to read out the transducer value, write and read the offset and scale factor',
# drive='drv',
value=Param(unit='mV'),
uri=usb_displacement,
digits=3,
scale_factor=100,
offset=0,
)
if not 'displacement':
Mod('displacement',
'frappy_psi.dpm.DPM3',
'DPM driver to read out the transducer value, write and read the offset and scale factor',
drive='drv',
value=Param(unit='mV'),
uri=usb_displacement,
digits=3,
scale_factor=100,
offset=0,
alarm_limit=2000,
)
Mod('F_load_cell',
Mod('load_cell',
'frappy_psi.dpm.DPM3',
'DPM driver to read out the transducer value, write and read the offset and scale factor',
# drive='drv',
drive='drv',
value=Param(unit='N'),
uri=usb_load_cell,
digits=3,
scale_factor=25696, # value at=s on the device, but without dec.pnt.
offset=64.53,
digits=1,
#scale_factor=25696, # value at=s on the device, but without dec.pnt.
#scale_factor=25696*2*10,
#scale_factor=10000,
scale_factor=-105.775*1000,
offset=65.66,
#offset=0,
alarm_limit=200,
)
Mod('force',
'frappy_psi.uniax.Uniax',
'force control',
motor='drv',
transducer='load_cell',
limit=200,
pollinterval = 0.1,
)
+17 -14
View File
@@ -59,24 +59,16 @@ class DPM3(HasIO, Readable):
value = Parameter(datatype=FloatRange(unit='N'))
digits = Parameter('number of digits for value', IntRange(0, 5), value=2, readonly=False)
# Note: we have to treat the units properly.
# We got an output of 150 for 10N. The maximal force we want to deal with is 100N,
# thus a maximal output of 1500. 10=150/f
offset = Parameter('', FloatRange(-1e5, 1e5), readonly=False)
scale_factor = Parameter('all shown digits in the device menu, but without dec.pt.',
FloatRange(-1e6, 1e6), readonly=False)
alarm_limit = Parameter('alarm limit', FloatRange(unit='N'), readonly=False, default=2000)
_offset_done = 0
_offset_changed = 0
def doPoll(self):
if time.time() < self._offset_done:
offset = self.query(self.OFFSET)
if offset != self.offset:
self.log.info('offset has changed: %g, set back', offset)
self.query(self.OFFSET, offset)
self._offset_done = 0
return
super().doPoll()
def initModule(self):
super().initModule()
if self.drive:
self.drive.register_interlock(self)
def writeInitParams(self):
for param in 'digits', 'offset', 'scale_factor':
@@ -119,6 +111,15 @@ class DPM3(HasIO, Readable):
return hex2float(hexvalue, self.digits)
def read_value(self):
if self._offset_changed:
if time.time() > self._offset_changed:
self._offset_changed = 0
else:
offset = self.query(self.OFFSET)
if offset != self.offset:
self.log.info('offset has changed: %g, set back to %g', offset, self.offset)
self.query(self.OFFSET, self.offset)
self._offset_changed = 0
value = float(self.communicate('*1B1'))
if abs(value) > self.alarm_limit:
if self.drive:
@@ -132,7 +133,7 @@ class DPM3(HasIO, Readable):
self.communicate('*1F135%02X\r*1G135' % (digits + 1))
self.query(self.SCALE, scale * 10 ** (digits - 6))
self.query(self.OFFSET, offset)
self._offset_done = time.time() + 10
self._offset_changed = time.time() + 30
def read_digits(self):
back_value = self.communicate('*1G135')
@@ -143,6 +144,8 @@ class DPM3(HasIO, Readable):
return digits
def read_offset(self):
if self._offset_changed:
return self.offset
reply = self.query(self.OFFSET)
return reply
+28 -9
View File
@@ -166,13 +166,14 @@ class Motor(PersistentMixin, HasIO, Drivable):
pullup_inputs = Parameter('activate pullup', BoolType(), group='more', default=False)
interlock_reason = Parameter('external interlock', StringType(),
group='more', readonly=False, default='')
fast_poll = Parameter('poll interval while driving', FloatRange(0),
group='more', readonly=False, default=0.5)
pollinterval = Parameter(group='more')
target_min = Limit()
target_max = Limit()
ioClass = BytesIO
fast_poll = 0.5
_run_mode = RUN_IDLE
_calc_timeout = True
_need_reset = None
@@ -181,6 +182,7 @@ class Motor(PersistentMixin, HasIO, Drivable):
_drv_try = 0
_limit_error_state = 0
_idle_status = IDLE, ''
_interlock_sensors = None
def comm(self, cmd, adr, value=0, bank=0):
"""set or get a parameter
@@ -266,6 +268,11 @@ class Motor(PersistentMixin, HasIO, Drivable):
self.status = self._idle_status = ERROR, 'saved encoder value does not match reading'
self._write_axispar(adjusted_encoder - self.zero, ENCODER_ADR, ANGLE_SCALE, readback=False)
def register_interlock(self, sensor):
if self._interlock_sensors is None:
self._interlock_sensors = {}
self._interlock_sensors[sensor.name] = sensor
def _read_axispar(self, adr, scale=1):
value = self.comm(GET_AXIS_PAR, adr)
# do not apply scale when 1 (datatype might not be float)
@@ -304,6 +311,8 @@ class Motor(PersistentMixin, HasIO, Drivable):
return self._write_axispar(value, *HW_ARGS[pname])
def doPoll(self):
for sensor in self._interlock_sensors.values():
sensor.doPoll()
self.read_status() # read_value is called by read_status
def read_value(self):
@@ -368,15 +377,11 @@ class Motor(PersistentMixin, HasIO, Drivable):
return WARN, LIMITS_PAST[self._limit_error_state]
return self._idle_status
if self.interlock_reason:
self._run_mode = RUN_IDLE
return ERROR, self.interlock_reason
now = time.time()
if self.steppos != oldpos:
self._last_change = now
if now < self._last_change + 0.25 + self.fast_poll:
if not self.read_target_reached():
if self.status[0] == BUSY:
return self.status
return BUSY, 'moving'
if self.has_limit_switches:
bits = self.read_input_bits()
if self._run_mode == RUN_DRIVE:
@@ -388,6 +393,11 @@ class Motor(PersistentMixin, HasIO, Drivable):
self._idle_status = ERROR, lim_text
self._stop_motor(lim_text)
return self.status
if now < self._last_change + 0.25 + self.fast_poll:
if not self.read_target_reached():
if self.status[0] == BUSY and not self.status[1].startswith('changed'):
return self.status
return BUSY, 'moving'
reason = ''
if self.read_move_status():
reason = self.move_status.name
@@ -462,7 +472,11 @@ class Motor(PersistentMixin, HasIO, Drivable):
self._limit_error_state = 0
self._limits_check_mask = LIMITS_MASK
if self.interlock_reason:
raise ImpossibleError(self.interlock_reason)
if self.auto_reset:
self.write_interlock_reason('')
self.doPoll()
if self.interlock_reason:
raise ImpossibleError(self.interlock_reason)
limit_state = self.read_input_bits() & LIMITS_MASK
if limit_state:
diff = target - self.steppos
@@ -481,7 +495,7 @@ class Motor(PersistentMixin, HasIO, Drivable):
self._drv_try = 0
self._stable_since = 0
self.start_motor(target)
self.status = BUSY, 'changed target'
self.status = BUSY, f'changed target {self._limits_check_mask}'
def write_zero(self, value):
self.zero = value
@@ -530,8 +544,13 @@ class Motor(PersistentMixin, HasIO, Drivable):
@Command()
def reset(self):
"""set steppos to encoder value, if not within tolerance"""
self.write_interlock_reason('')
if self._run_mode:
raise IsBusyError('can not reset while moving')
if self._run_mode == RUN_DRIVE:
raise IsBusyError(f'can not reset while moving {self._run_mode}')
self.doPoll() # read sensors again
if self.interlock_reason:
raise IsBusyError(f'can not reset {self.interlock_reason}')
tol = ENCODER_RESOLUTION * 1.1
for itry in range(10):
diff = self.read_encoder() - self.read_steppos()