From e7c452c75cf84db3c4d2430981a1f1ff5016ed2c Mon Sep 17 00:00:00 2001 From: Wolfgang Draxinger Date: Tue, 6 Dec 2011 11:23:24 +0100 Subject: sctarchpad --- README | 32 +++++++++++++++++++++++++++ scratchpad.py | 71 +++++++++++++++++++++++++++++++++-------------------------- 2 files changed, 72 insertions(+), 31 deletions(-) diff --git a/README b/README index 1c3a340..8212d3d 100644 --- a/README +++ b/README @@ -18,6 +18,38 @@ to form a application specific stage. --- +Overview + +PyMotionControl forms *model* part of a Model View Controller +(MVC) design. The "MotionControl" class provides an abstraction +for a generic motion control stage. As such the only interface +it exposes are function for setting a motion target position +and starting and aborting a *cycle*. + +*Cycles* are sequences of "Actions", instances of classes derived +from "AbstractAction" base class. Each action effects on all assigned +axes simulatanously and the action queue is advaned upon successfull +completion of the currently active action. If a action fails, the +rest of the queue is not executed, but cleared and the +*emergency_abort_action* executed, which usually just stops all axes, +but may also perform a movement into a safe position. + +A MotionControl has a number of axes, which are instances of +classes detived from "Axis". "Axis" abstracts a typical motion +axis interface. + +To program complex action sequences, subclass from "MotionControl". +Provide interface functions which turn a given target destination +or ultimate action into a sequence of sub-actions, queueing them +up in MotionControl.cycle_queue + +"Constraints" interface with "MotionControl": Every "MotionControl" +instance has a set of constraints. In the most simple case just the +axis limits. Upon setting a new target position the constraints +are checked. Only if all constraints are met a cycle can be started. + +--- + NOTE scratchpad.py: scratchpad.py is the source file where new things are diff --git a/scratchpad.py b/scratchpad.py index 05f25bf..3dd62e6 100644 --- a/scratchpad.py +++ b/scratchpad.py @@ -14,17 +14,17 @@ class Action: return do_ended() def abort(self): - self.do_abort() self.aborted = True + self.do_abort() def do_execute(): raise NotImplementedError - def do_abort(): - raise NotImplementedError - def do_ended(): raise NotImplementedError + + def do_abort(): + pass class NullAction(Action): def do_execute(self): @@ -33,18 +33,15 @@ class NullAction(Action): def do_ended(self): return True - def do_abort(self): - pass - -class Initialize(Action): +class Initiate(Action): def __init__(self, axes): self.axes = axes def do_execute(self): for a in self.axis: - a.initialize() - + a.initiate() def do_ended(): + all_stopped = not (True in [axis.running for axis in self.axes]) if not all_stopped: return False @@ -55,7 +52,7 @@ class Initialize(Action): for axis in self.axes: axis.halt() -class MotionAbsolute(Action): +class GotoAbsolute(Action): def __init__(self, axes, target, speed = None): if len(axes) != len(target): raise ValueError() @@ -92,9 +89,6 @@ class EmergencyStop(Action): def do_ended(self): return not (True in [axis.running for axis in self.axes]) - def do_abort(self): - pass - class CycleAbort(Exception): pass @@ -102,9 +96,11 @@ class CycleFinished(Exception): pass class MotionControl(object): - def __init__(self, name): + def __init__(self, axes, constraints): + self.axes = axes + self.constraints = constraints + import weakref, threading - self.name = name self.on_cycle_started = Signal() self.on_cycle_finished = Signal() self.on_cycle_aborted = Signal() @@ -112,7 +108,16 @@ class MotionControl(object): def __del__(self): self.abort() + def can_cycle_start(self): + return True # FIXME: + + def prepare_cycle(self): + pass + def start_cycle(self): + if not self.can_cycle_start() + return False + self.prepare_cycle() self.active = True self.worker_thread = threading.Thread(target = MotionControl.cycle_worker, name = "MotionControl.worker", args=(weakref.proxy(self),)) self.worker_thread.daemon =True @@ -212,17 +217,10 @@ class Axis(object): while self.running: self.update() - def initialize(self): + def initiate(self): raise NotImplementedError() - """ - if self.running: - self.stop() - self.wait_for_stop() - self.do_initialize() - self.wait_for_stop() - """ - def goto_absolute(self, targeti, speed = None): + def goto_absolute(self, target, speed = None): raise NotImplementedError() def goto_relative(self, offset, speed = None): @@ -231,14 +229,19 @@ class Axis(object): class PhytronAxis(Axis): INITIATOR_MINUS = 1 INITIATOR_PLUS = 2 - def __init__(self, ipcomm_axis, scale=1, max_run_freq=None, initiator=INITIATOR_MINUS, inverted = False): + def __init__(self, ipcomm_axis, max_run_freq=None, initiator=INITIATOR_MINUS, initiator_position = 0, inverted = False): super(PhytronAxis, self).__init__() self.ipcomm_axis = ipcomm_axis if not max_run_freq: max_run_freq = self.ipcomm_axis.getRunFrequency() self.max_run_freq = max_run_freq self.initiator = initiator - self.scale = scale if not inverted else -scale + self.initiator_position = initiator_position + self.onInitialized.connect(self.set_initator_position) + + def set_initator_position(self, sender, initialized): + if sender == self: + self.ipcomm_axis.setPosition(self.initiator_position) def do_update(self): if self.running: @@ -256,7 +259,8 @@ class PhytronAxis(Axis): def emergency_stop(self): self.ipcomm_axis.stop() - def initialize(self): + def initiate(self): + self.ipcomm_axis.stop() self.wait_for_stop() self.ipcomm_axis.setRunFrequency(self.max_run_freq) if self.initiator == PhytronAxis.INITIATOR_MINUS: @@ -264,15 +268,20 @@ class PhytronAxis(Axis): if self.initiator == PhytronAxis.INITIATOR_PLUS: self.ipcomm_axis.initializePlus() + def set_initiator_position(self, sender, initialized): + if sender == self and initialized: + self.ipcomm_axis.setPosition(self.initiator_position) + def goto_absolute(self, target, speed = None): - self.stop_sync() + self.ipcomm_axis.stop() + self.wait_for_stop() if not speed: speed = 1. self.ipcomm_axis.setRunFrequency(self.max_run_freq * speed) - self.ipcomm_axis.gotoAbs(target * self.scale) + self.ipcomm_axis.gotoAbs(target) if __name__ == '__main__': ipcomm = Phytron.IPCOMM('/dev/ttyERDASTEP', axes=5) axis = PhytronAxis(ipcomm[0]) action = MotionAbsolute([axis,], [200000,]) - + motctl = MotionControl([axis,]) -- cgit v1.2.3