Resolve "moving Edcmund where it already is can leave is_moving True"
Closes #4191 (closed)
# Issue occurs with this CalcAxis Architecture (3 layers of calc at least on one branch and one moving physical axis in another branch)
#
# CalcAxis(track_undu)
# | |
# CalcAxis(twotheta) Axis(undu)
# |
# CalcAxis(theta)
# |
# Axis(robz)
#
# === Context: On GroupMove.move(axis_pos_dict, ...) with axis_pos_dict = {track_undu: pos}
#
# 1) fill_motion_dict:
# - CalcAxis always have a motion (even if already in place)
# - If Axis is already_in_place, motion is None and added to excluded_axes list
#
# 2) force possible CalcAxis linked these excluded Axis to re-compute their pseudo pos.
# for axis in excluded_axes:
# event.send(axis, "internal_position", axis.position))
#
# 3) discard motions if:
# - math.isclose(motion.delta, 0)
# - motion.axis is CalcAxis and set(motion.axis.controller.reals) - excluded_axes is empty
# - !!! discarded motions are not re-injected in excluded_axes !!! (FIXED BY MR !6058)
#
# 4) del controllers with no motions
#
# 5) cancel move if there are only motions of CalcAxis
#
# 6) for remaining motions do:
# - motion.axis._set_position = motion.user_target_pos
# - motion.axis._set_moving_state() !!! On Axis and CalcAxis. on Axis should be enough !!!
# - spawn move_task and link task's end to _end_of_move() callback
# - _end_of_move() calls axis._set_move_done() !!! on Axis only !!!.
# - Axis._set_move_done(), emit signal "internal_move_done" and "move_done"
# - "internal_move_done" triggers a callback _real_axis_move_done() on CalcControllers
# - _set_moving_state() is called on pseudos only if all its reals are not moving !!!
# === Use case 1: mv(track_undu, 8) when all axes already in place => OK
#
# 1) motions_dict = {ctrl1: [track_undu], ctrl2: [twotheta], ctrl3: [theta]}
# excluded_axes = {undu, robz}
#
# 2) send internal_position: robz => theta => theta => track_undu
#
# 3) set(motion.axis.controller.reals) - excluded_axes:
# track_undu => {twotheta, undu} - {undu, robz} = {twotheta} => not empty
# twotheta => {theta} - {undu, robz} = {theta} => not empty
# theta => {robz} - {undu, robz} = {} => empty => theta motion discarded
#
# 4) motions_dict = {ctrl1: [track_undu], ctrl2: [twotheta]}
#
# 5) cancel move because only CalcAxis
#
# === Use case 2: mv(track_undu, 8) when all axes already in place except undu => ISSUE 4191
#
# 1) motions_dict = {ctrl1: [track_undu], ctrl2: [twotheta], ctrl3: [theta], ctrl4: [undu]}
# excluded_axes = {robz}
#
# 2) send internal_position: robz => theta => theta => track_undu
#
# 3) set(motion.axis.controller.reals) - excluded_axes:
# track_undu => {twotheta, undu} - {robz} = {twotheta, undu} => not empty
# twotheta => {theta} - {robz} = {theta} => not empty
# theta => {robz} - {robz} = {} => empty => theta motion discarded
#
# 4) motions_dict = {ctrl1: [track_undu], ctrl2: [twotheta], ctrl4: [undu]}
#
# 5) move not canceled
#
# 6) _set_moving_state() on => track_undu, twotheta, undu
# _end_of_move() calls axis._set_move_done() on => undu (track_undu, twotheta are skipped)
# undu emits "internal_move_done" and triggers _real_axis_move_done on track_undu controller
# but twotheta is still moving and will remain like that forever!!! (because robz and theta have been discarded)
# so track_undu will also stay moving forever!!!
#
# CCL: twotheta motion should have been discarded too
# excluded_axes must be updated with discarded CalcAxis motions
Edited by Perceval Guillou