fixed dilsc, fist try before tests
Change-Id: Iaac5f07c27879c7c17be2e25defde19e9ddd1566
This commit is contained in:
@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# *****************************************************************************
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
@ -18,79 +19,78 @@
|
||||
# *****************************************************************************
|
||||
"""vector field"""
|
||||
|
||||
from frappy.core import Drivable, Done, BUSY, IDLE, WARN, ERROR
|
||||
from frappy.errors import BadValueError
|
||||
import math
|
||||
from frappy.core import Drivable, Done, BUSY, IDLE, ERROR, Parameter, TupleOf, ArrayOf, FloatRange
|
||||
from frappy.errors import RangeError
|
||||
from frappy_psi.vector import Vector
|
||||
from frappy.states import HasStates, Retry, status_code
|
||||
|
||||
|
||||
DECREASE = 1
|
||||
INCREASE = 2
|
||||
|
||||
|
||||
class VectorField(Vector, Drivable):
|
||||
_state = None
|
||||
class VectorField(HasStates, Vector, Drivable):
|
||||
sphere_radius = Parameter('max. sphere', datatype=FloatRange(0, 0.7, unit='T'), readonly=True, default=0.6)
|
||||
cylinders = Parameter('allowed cylinders (list of radius and height)',
|
||||
datatype=ArrayOf(TupleOf(FloatRange(0, 0.6, unit='T'), FloatRange(0, 5.2, unit='T')), 1, 9),
|
||||
readonly=True, default=((0.23, 5.2), (0.45, 0.8)))
|
||||
|
||||
def doPoll(self):
|
||||
"""periodically called method"""
|
||||
try:
|
||||
if self._starting:
|
||||
# first decrease components
|
||||
driving = False
|
||||
for target, component in zip(self.target, self.components):
|
||||
if target * component.value < 0:
|
||||
# change sign: drive to zero first
|
||||
target = 0
|
||||
if abs(target) < abs(component.target):
|
||||
if target != component.target:
|
||||
component.write_target(target)
|
||||
if component.isDriving():
|
||||
driving = True
|
||||
if driving:
|
||||
return
|
||||
# now we can go to the final targets
|
||||
for target, component in zip(self.target, self.components):
|
||||
component.write_target(target)
|
||||
self._starting = False
|
||||
else:
|
||||
for component in self.components:
|
||||
if component.isDriving():
|
||||
return
|
||||
self.setFastPoll(False)
|
||||
finally:
|
||||
super().doPoll()
|
||||
def initModule(self):
|
||||
super().initModule()
|
||||
# override check_limits of the components with a check for restrictions on the vector
|
||||
for idx, component in enumerate(self.components):
|
||||
|
||||
def outer_check(target, vector=self, i=idx, inner_check=component.check_target):
|
||||
inner_check(target)
|
||||
value = [c.value - math.copysign(c.tolerance, c.value)
|
||||
for c in vector.components]
|
||||
value[i] = target
|
||||
vector._check_limits(value)
|
||||
|
||||
component.check_target = outer_check
|
||||
|
||||
def merge_status(self):
|
||||
names = [c.name for c in self.components if c.status[0] >= ERROR]
|
||||
if names:
|
||||
return ERROR, 'error in %s' % ', '.join(names)
|
||||
names = [c.name for c in self.components if c.isDriving()]
|
||||
if self._state:
|
||||
# self.log.info('merge %r', [c.status for c in self.components])
|
||||
if names:
|
||||
direction = 'down ' if self._state == DECREASE else ''
|
||||
return BUSY, 'ramping %s%s' % (direction, ', '.join(names))
|
||||
if self.status[0] == BUSY:
|
||||
return self.status
|
||||
return BUSY, 'driving'
|
||||
if names:
|
||||
return WARN, 'moving %s directly' % ', '.join(names)
|
||||
names = [c.name for c in self.components if c.status[0] >= WARN]
|
||||
if names:
|
||||
return WARN, 'warnings in %s' % ', '.join(names)
|
||||
return IDLE, ''
|
||||
return self.status
|
||||
|
||||
def write_target(self, value):
|
||||
def _check_limits(self, value):
|
||||
"""check if value is within one of the safe shapes"""
|
||||
if sum((v ** 2 for v in value)) <= self.sphere_radius ** 2:
|
||||
return
|
||||
for r, h in self.cylinders:
|
||||
if sum(v ** 2 for v in value[0:2]) <= r ** 2 and abs(value[2]) <= h:
|
||||
return
|
||||
raise RangeError('vector %s does not fit in any limiting shape' % repr(value))
|
||||
|
||||
def write_target(self, target):
|
||||
"""initiate target change"""
|
||||
# first make sure target is valid
|
||||
for target, component in zip(self.target, self.components):
|
||||
# check against limits if individual components
|
||||
component.check_limits(target)
|
||||
if sum(v * v for v in value) > 1:
|
||||
raise BadValueError('norm of vector too high')
|
||||
self.log.info('decrease')
|
||||
self.setFastPoll(True)
|
||||
self.target = value
|
||||
self._state = DECREASE
|
||||
self.doPoll()
|
||||
self.log.info('done write_target %r', value)
|
||||
return Done
|
||||
# check limits first
|
||||
for component_target, component in zip(target, self.components):
|
||||
# check against limits of individual components
|
||||
component.check_target(component_target, vector=None) # no outer check here!
|
||||
self._check_limits(target)
|
||||
for component_target, component in zip(target, self.components):
|
||||
if component_target * component.value < 0:
|
||||
# change sign: drive to zero first
|
||||
component_target = 0
|
||||
if abs(component_target) > abs(component.value):
|
||||
continue # do not drive yet
|
||||
component.write_target(component_target)
|
||||
self.start_machine(self.ramp_down, target=target)
|
||||
return target
|
||||
|
||||
@status_code(BUSY)
|
||||
def ramp_down(self, state):
|
||||
for target, component in zip(state.target, self.components):
|
||||
if component.isDriving():
|
||||
return Retry()
|
||||
for target, component in zip(state.target, self.components):
|
||||
component.write_target(target)
|
||||
return self.final_ramp
|
||||
|
||||
@status_code(BUSY)
|
||||
def final_ramp(self, state):
|
||||
for component in self.components:
|
||||
if component.isDriving():
|
||||
return Retry()
|
||||
return self.final_status()
|
||||
|
Reference in New Issue
Block a user