Example #1
0
    def action_loop(self):
        i = 0

        states, values = {}, {}
        for element in self._channels:
            states[element] = None
            #values[element] = None

        nap = self._acq_sleep_time
        nb_states_per_value = self._nb_states_per_value

        # read values to send a first event when starting to acquire
        with ActionContext(self):
            self.raw_read_value_loop(ret=values)
            for acquirable, value in values.items():
                acquirable.put_value(value, propagate=2)

        while True:
            self.read_state_info(ret=states)

            if not self.in_acquisition(states):
                break

            # read value every n times
            if not i % nb_states_per_value:
                self.read_value_loop(ret=values)
                for acquirable, value in values.items():
                    acquirable.put_value(value)

            time.sleep(nap)
            i += 1

        for slave in self._slaves:
            try:
                slave.stop_action()
            except:
                self.warning("Unable to stop slave acquisition %s",
                             slave.getLogName())
                self.debug("Details", exc_info=1)

        with ActionContext(self):
            self.raw_read_state_info(ret=states)
            self.raw_read_value_loop(ret=values)

        for acquirable, state_info in states.items():
            # first update the element state so that value calculation
            # that is done after takes the updated state into account
            acquirable.set_state_info(state_info, propagate=0)
            if acquirable in values:
                value = values[acquirable]
                acquirable.put_value(value, propagate=2)
            with acquirable:
                acquirable.clear_operation()
                state_info = acquirable._from_ctrl_state_info(state_info)
                acquirable.set_state_info(state_info, propagate=2)
Example #2
0
    def start_action(self, *args, **kwargs):
        """kwargs['items'] is a dict<moveable, (pos, dial, do_backlash, backlash)
        """
        items = kwargs.pop("items")

        pool = self.pool

        # prepare data structures
        self._aborted = False
        self._stopped = False
        self._motion_sleep_time = kwargs.pop("motion_sleep_time",
                                             pool.motion_loop_sleep_time)
        self._nb_states_per_position = \
            kwargs.pop("nb_states_per_position",
                       pool.motion_loop_states_per_position)

        self._motion_info = motion_info = {}
        for moveable, motion_data in items.items():
            it = moveable.instability_time
            motion_info[moveable] = PoolMotionItem(moveable,
                                                   *motion_data,
                                                   instability_time=it)

        pool_ctrls = self.get_pool_controller_list()
        moveables = self.get_elements()

        with ActionContext(self):
            self.pre_start_all(pool_ctrls)
            self.pre_start_one(moveables, items)
            self.start_one(moveables, motion_info)
            self.start_all(pool_ctrls, moveables, motion_info)
Example #3
0
    def action_loop(self):
        states, values = {}, {}
        for element in self._channels:
            states[element] = None
            values[element] = None

        nap = self._acq_sleep_time
        while True:
            self.read_value(ret=values)
            for acquirable, value in values.items():
                acquirable.put_value(value)
            if self._stopped or self._aborted:
                break
            time.sleep(nap)

        with ActionContext(self):
            self.raw_read_state_info(ret=states)

        for acquirable, state_info in states.items():
            # first update the element state so that value calculation
            # that is done after takes the updated state into account
            state_info = acquirable._from_ctrl_state_info(state_info)
            acquirable.set_state_info(state_info, propagate=0)
            with acquirable:
                acquirable.clear_operation()
                acquirable.set_state_info(state_info, propagate=2)
Example #4
0
    def start_action(self, *args, **kwargs):
        """Prepares everything for acquisition and starts it.

           :param: config"""

        pool = self.pool

        # prepare data structures
        self._aborted = False
        self._stopped = False

        self._acq_sleep_time = kwargs.pop("acq_sleep_time",
                                          pool.acq_loop_sleep_time)
        self._nb_states_per_value = \
            kwargs.pop("nb_states_per_value",
                       pool.acq_loop_states_per_value)

        integ_time = kwargs.get("integ_time")
        mon_count = kwargs.get("monitor_count")
        if integ_time is None and mon_count is None:
            raise Exception("must give integration time or monitor counts")
        if integ_time is not None and mon_count is not None:
            raise Exception(
                "must give either integration time or monitor counts (not both)"
            )

        items = kwargs.get("items")
        if items is None:
            items = self.get_elements()
        cfg = kwargs['config']

        pool_ctrls_dict = dict(cfg['controllers'])
        pool_ctrls_dict.pop('__tango__', None)
        pool_ctrls = []
        for ctrl in pool_ctrls_dict:
            if ElementType.ZeroDExpChannel in ctrl.get_ctrl_types():
                pool_ctrls.append(ctrl)

        # Determine which channels are active
        self._channels = channels = {}
        for pool_ctrl in pool_ctrls:
            ctrl = pool_ctrl.ctrl
            pool_ctrl_data = pool_ctrls_dict[pool_ctrl]
            main_unit_data = pool_ctrl_data['units']['0']
            elements = main_unit_data['channels']

            for element, element_info in elements.items():
                channel = Channel(element, info=element_info)
                channels[element] = channel

        with ActionContext(self):
            # set the state of all elements to  and inform their listeners
            for channel in channels:
                channel.clear_buffer()
                channel.set_state(State.Moving, propagate=2)
Example #5
0
    def start_action(self, *args, **kwargs):
        """Prepares everything for acquisition and starts it.

           :param: config"""

        pool = self.pool

        self._index = kwargs.get("idx")

        # prepare data structures
        # TODO: rollback this change when a proper synchronization between
        # acquisition actions will be develop.
        # Now the meta acquisition action is resettung them to 0.
        #         self._aborted = False
        #         self._stopped = False

        self._acq_sleep_time = kwargs.pop("acq_sleep_time",
                                          pool.acq_loop_sleep_time)
        self._nb_states_per_value = \
            kwargs.pop("nb_states_per_value",
                       pool.acq_loop_states_per_value)

        items = kwargs.get("items")
        if items is None:
            items = self.get_elements()
        cfg = kwargs['config']

        pool_ctrls_dict = dict(cfg['controllers'])
        pool_ctrls_dict.pop('__tango__', None)
        pool_ctrls = []
        for ctrl in pool_ctrls_dict:
            if ElementType.ZeroDExpChannel in ctrl.get_ctrl_types():
                pool_ctrls.append(ctrl)

        # Determine which channels are active
        self._channels = channels = {}
        for pool_ctrl in pool_ctrls:
            ctrl = pool_ctrl.ctrl
            pool_ctrl_data = pool_ctrls_dict[pool_ctrl]
            elements = pool_ctrl_data['channels']

            for element, element_info in elements.items():
                channel = Channel(element, info=element_info)
                channels[element] = channel

        with ActionContext(self):
            # set the state of all elements to  and inform their listeners
            for channel in channels:
                channel.clear_buffer()
                channel.set_state(State.Moving, propagate=2)
Example #6
0
    def action_loop(self):
        i = 0

        states, values = {}, {}
        for element in self._channels:
            states[element] = None
            values[element] = None

        nap = self._acq_sleep_time
        nb_states_per_value = self._nb_states_per_value

        while True:
            self.read_state_info(ret=states)
            if not self.in_acquisition(states):
                break

            # read value every n times
            if not i % nb_states_per_value:
                self.read_value_loop(ret=values)
                for acquirable, value in values.items():
                    if is_value_error(value):
                        self.error("Loop read value error for %s" %
                                   acquirable.name)
                        acquirable.put_value(value)
                    else:
                        acquirable.extend_value_buffer(value)

            time.sleep(nap)
            i += 1

        with ActionContext(self):
            self.raw_read_state_info(ret=states)
            self.raw_read_value_loop(ret=values)

        for acquirable, state_info in states.items():
            # first update the element state so that value calculation
            # that is done after takes the updated state into account
            acquirable.set_state_info(state_info, propagate=0)
            if acquirable in values:
                value = values[acquirable]
                if is_value_error(value):
                    self.error("Loop final read value error for: %s" %
                               acquirable.name)
                    acquirable.put_value(value)
                else:
                    acquirable.extend_value_buffer(value, propagate=2)
            with acquirable:
                acquirable.clear_operation()
                state_info = acquirable._from_ctrl_state_info(state_info)
                acquirable.set_state_info(state_info, propagate=2)
Example #7
0
    def action_loop(self):
        states, values = {}, {}
        for element in self._channels:
            states[element] = None
            values[element] = None

        nap = self._acq_sleep_time

        while True:
            self.read_state_info(ret=states)
            if not self.in_acquisition(states):
                break

            time.sleep(nap)

        for slave in self._slaves:
            try:
                slave.stop_action()
            except:
                self.warning("Unable to stop slave acquisition %s",
                             slave.getLogName())
                self.debug("Details", exc_info=1)

        with ActionContext(self):
            self.raw_read_state_info(ret=states)
            self.raw_read_value_loop(ret=values)

        for acquirable, state_info in states.items():
            # first update the element state so that value calculation
            # that is done after takes the updated state into account
            acquirable.set_state_info(state_info, propagate=0)
            if acquirable in values:
                value = values[acquirable]
                if is_value_error(value):
                    self.error("Loop final read value error for: %s" %
                               acquirable.name)
                    acquirable.put_value(value)
                else:
                    acquirable.append_value_buffer(value, self.index)
            with acquirable:
                acquirable.clear_operation()
                state_info = acquirable._from_ctrl_state_info(state_info)
                acquirable.set_state_info(state_info, propagate=2)
    def start_action(self, *args, **kwargs):
        '''Start action method. Expects the following kwargs:
        - config - dictionary containing measurement group configuration
        - synchronization - list of dictionaries containing information about
          the expected synchronization
        - moveable (optional)- moveable object used as the synchronization
          source in the Position domain
        - monitor (optional) - counter/timer object used as the synchronization
          source in the Monitor domain
        '''
        cfg = kwargs['config']
        synchronization = kwargs.get('synchronization')
        moveable = kwargs.get('moveable')
        ctrls_config = cfg.get('controllers')
        pool_ctrls = ctrls_config.keys()

        # Prepare a dictionary with the involved channels
        self._channels = channels = {}
        for pool_ctrl in pool_ctrls:
            pool_ctrl_data = ctrls_config[pool_ctrl]
            elements = pool_ctrl_data['channels']

            for element, element_info in elements.items():
                channel = TGChannel(element, info=element_info)
                channels[element] = channel

        with ActionContext(self):

            # loads synchronization description
            for pool_ctrl in pool_ctrls:
                ctrl = pool_ctrl.ctrl
                pool_ctrl_data = ctrls_config[pool_ctrl]
                ctrl.PreSynchAll()
                elements = pool_ctrl_data['channels']
                for element in elements:
                    axis = element.axis
                    ret = ctrl.PreSynchOne(axis, synchronization)
                    if not ret:
                        msg = ("%s.PreSynchOne(%d) returns False" %
                               (pool_ctrl.name, axis))
                        raise Exception(msg)
                    ctrl.SynchOne(axis, synchronization)
                ctrl.SynchAll()

            # attaching listener (usually acquisition action)
            # to the software trigger gate generator
            if self._listener is not None:
                self._synch_soft.set_configuration(synchronization)
                self._synch_soft.add_listener(self._listener)
                remove_acq_listener = partial(self._synch_soft.remove_listener,
                                              self._listener)
                self.add_finish_hook(remove_acq_listener, False)
                self._synch_soft.add_listener(
                    self.main_element.on_element_changed)
                remove_mg_listener = partial(self._synch_soft.remove_listener,
                                             self.main_element)
                self.add_finish_hook(remove_mg_listener, False)
            # subscribing to the position change events to generate events
            # in position domain
            if moveable is not None:
                position = moveable.get_position_attribute()
                position.add_listener(self._synch_soft)
                remove_pos_listener = partial(position.remove_listener,
                                              self._synch_soft)
                self.add_finish_hook(remove_pos_listener, False)

            # PreStartAll on all controllers
            for pool_ctrl in pool_ctrls:
                pool_ctrl.ctrl.PreStartAll()

            # PreStartOne & StartOne on all elements
            for pool_ctrl in pool_ctrls:
                ctrl = pool_ctrl.ctrl
                pool_ctrl_data = ctrls_config[pool_ctrl]
                elements = pool_ctrl_data['channels']
                for element in elements:
                    axis = element.axis
                    channel = channels[element]
                    ret = ctrl.PreStartOne(axis)
                    if not ret:
                        raise Exception("%s.PreStartOne(%d) returns False" %
                                        (pool_ctrl.name, axis))
                    ctrl.StartOne(axis)

            # set the state of all elements to inform their listeners
            for channel in channels:
                channel.set_state(State.Moving, propagate=2)

            # StartAll on all controllers
            for pool_ctrl in pool_ctrls:
                pool_ctrl.ctrl.StartAll()

            if self._listener is not None:
                self._synch_soft.start()
                get_thread_pool().add(self._synch_soft.run)
Example #9
0
    def start_action(self, *args, **kwargs):
        """Prepares everything for acquisition and starts it.

           :param: config"""

        pool = self.pool

        # prepare data structures
        self._aborted = False
        self._stopped = False

        self._acq_sleep_time = kwargs.pop("acq_sleep_time",
                                             pool.acq_loop_sleep_time)
        self._nb_states_per_value = \
            kwargs.pop("nb_states_per_value",
                       pool.acq_loop_states_per_value)

        self._integ_time = integ_time = kwargs.get("integ_time")
        self._mon_count = mon_count = kwargs.get("monitor_count")
        if integ_time is None and mon_count is None:
            raise Exception("must give integration time or monitor counts")
        if integ_time is not None and mon_count is not None:
            raise Exception("must give either integration time or monitor counts (not both)")

        _ = kwargs.get("items", self.get_elements())
        cfg = kwargs['config']

        # determine which is the controller which holds the master channel

        if integ_time is not None:
            master_key = 'timer'
            master_value = integ_time
        if mon_count is not None:
            master_key = 'monitor'
            master_value = -mon_count

        master = cfg[master_key]
        master_ctrl = master.controller

        pool_ctrls_dict = dict(cfg['controllers'])
        pool_ctrls_dict.pop('__tango__', None)
        pool_ctrls = []
        self._pool_ctrl_dict_loop = _pool_ctrl_dict_loop = {}
        for ctrl, v in pool_ctrls_dict.items():
            if ctrl.is_timerable():
                pool_ctrls.append(ctrl)
            if ElementType.CTExpChannel in ctrl.get_ctrl_types():
                _pool_ctrl_dict_loop[ctrl] = v

        # make sure the controller which has the master channel is the last to
        # be called
        pool_ctrls.remove(master_ctrl)
        pool_ctrls.append(master_ctrl)

        # Determine which channels are active
        self._channels = channels = {}
        for pool_ctrl in pool_ctrls:
            ctrl = pool_ctrl.ctrl
            pool_ctrl_data = pool_ctrls_dict[pool_ctrl]
            main_unit_data = pool_ctrl_data['units']['0']
            elements = main_unit_data['channels']

            for element, element_info in elements.items():
                axis = element.axis
                channel = Channel(element, info=element_info)
                channels[element] = channel

        #for channel in channels:
        #    channel.prepare_to_acquire(self)

        with ActionContext(self):

            # PreLoadAll, PreLoadOne, LoadOne and LoadAll
            for pool_ctrl in pool_ctrls:
                ctrl = pool_ctrl.ctrl
                pool_ctrl_data = pool_ctrls_dict[pool_ctrl]
                main_unit_data = pool_ctrl_data['units']['0']
                ctrl.PreLoadAll()
                master = main_unit_data[master_key]
                axis = master.axis
                res = ctrl.PreLoadOne(axis, master_value)
                if not res:
                    raise Exception("%s.PreLoadOne(%d) returns False" % (pool_ctrl.name, axis,))
                ctrl.LoadOne(axis, master_value)
                ctrl.LoadAll()

            # PreStartAll on all controllers
            for pool_ctrl in pool_ctrls:
                pool_ctrl.ctrl.PreStartAll()

            # PreStartOne & StartOne on all elements
            for pool_ctrl in pool_ctrls:
                ctrl = pool_ctrl.ctrl
                pool_ctrl_data = pool_ctrls_dict[pool_ctrl]
                main_unit_data = pool_ctrl_data['units']['0']
                elements = main_unit_data['channels']
                for element in elements:
                    axis = element.axis
                    channel = channels[element]
                    if channel.enabled:
                        ret = ctrl.PreStartOne(axis, master_value)
                        if not ret:
                            raise Exception("%s.PreStartOne(%d) returns False" \
                                            % (pool_ctrl.name, axis))
                        ctrl.StartOne(axis, master_value)

            # set the state of all elements to  and inform their listeners
            for channel in channels:
                channel.set_state(State.Moving, propagate=2)

            # StartAll on all controllers
            for pool_ctrl in pool_ctrls:
                pool_ctrl.ctrl.StartAll()
Example #10
0
    def start_action(self, *args, **kwargs):
        """Prepares everything for acquisition and starts it.
        :param acq_sleep_time: sleep time between state queries
        :param nb_states_per_value: how many state queries between readouts
        :param integ_time: integration time(s)
        :type integ_time: float or seq<float>
        :param repetitions: repetitions
        :type repetitions: int
        :param config: configuration dictionary (with information about
            involved controllers and channels)
        """
        pool = self.pool

        self._aborted = False
        self._stopped = False

        self._acq_sleep_time = kwargs.pop("acq_sleep_time",
                                          pool.acq_loop_sleep_time)
        self._nb_states_per_value = kwargs.pop("nb_states_per_value",
                                               pool.acq_loop_states_per_value)

        self._integ_time = integ_time = kwargs.get("integ_time")
        self._mon_count = mon_count = kwargs.get("monitor_count")
        self._repetitions = repetitions = kwargs.get("repetitions")
        if integ_time is None and mon_count is None:
            raise Exception("must give integration time or monitor counts")
        if integ_time is not None and mon_count is not None:
            msg = ("must give either integration time or monitor counts "
                   "(not both)")
            raise Exception(msg)

        _ = kwargs.get("items", self.get_elements())
        cfg = kwargs['config']
        # determine which is the controller which holds the master channel

        if integ_time is not None:
            master_key = 'timer'
            master_value = integ_time
        if mon_count is not None:
            master_key = 'monitor'
            master_value = -mon_count
        master = cfg[master_key]
        if master is None:
            self.main_element.set_state(State.Fault, propagate=2)
            msg = "master {0} is unknown (probably disabled)".format(
                master_key)
            raise RuntimeError(msg)
        master_ctrl = master.controller

        pool_ctrls_dict = dict(cfg['controllers'])
        pool_ctrls_dict.pop('__tango__', None)

        # controllers to be started (only enabled) in the right order
        pool_ctrls = []
        # controllers that will be read at the end of the action
        self._pool_ctrl_dict_loop = _pool_ctrl_dict_loop = {}
        # channels that are acquired (only enabled)
        self._channels = channels = {}

        # select only suitable e.g. enabled, timerable controllers & channels
        for ctrl, pool_ctrl_data in pool_ctrls_dict.items():
            # skip not timerable controllers e.g. 0D
            if not ctrl.is_timerable():
                continue
            ctrl_enabled = False
            elements = pool_ctrl_data['channels']
            for element, element_info in elements.items():
                # skip disabled elements
                if not element_info['enabled']:
                    continue
                # Add only the enabled channels
                channel = Channel(element, info=element_info)
                channels[element] = channel
                ctrl_enabled = True
            # check if the ctrl has enabled channels
            if ctrl_enabled:
                # enabled controller can no be offline
                if not ctrl.is_online():
                    self.main_element.set_state(State.Fault, propagate=2)
                    msg = "controller {0} is offline".format(ctrl.name)
                    raise RuntimeError(msg)
                pool_ctrls.append(ctrl)
                # only CT will be read in the loop, 1D and 2D not
                if ElementType.CTExpChannel in ctrl.get_ctrl_types():
                    _pool_ctrl_dict_loop[ctrl] = pool_ctrl_data

        # timer/monitor channels can not be disabled
        for pool_ctrl in pool_ctrls:
            ctrl = pool_ctrl.ctrl
            pool_ctrl_data = pool_ctrls_dict[pool_ctrl]
            timer_monitor = pool_ctrl_data[master_key]
            if timer_monitor not in channels:
                self.main_element.set_state(State.Fault, propagate=2)
                msg = "timer/monitor ({0}) of {1} controller is "\
                      "disabled)".format(timer_monitor.name, pool_ctrl.name)
                raise RuntimeError(msg)

        # make sure the controller which has the master channel is the last to
        # be called
        pool_ctrls.remove(master_ctrl)
        pool_ctrls.append(master_ctrl)

        with ActionContext(self):
            # PreLoadAll, PreLoadOne, LoadOne and LoadAll
            for pool_ctrl in pool_ctrls:
                try:
                    ctrl = pool_ctrl.ctrl
                    pool_ctrl_data = pool_ctrls_dict[pool_ctrl]
                    ctrl.PreLoadAll()
                    master = pool_ctrl_data[master_key]
                    axis = master.axis
                    try:
                        res = ctrl.PreLoadOne(axis, master_value, repetitions)
                    except TypeError:
                        msg = ("PreLoadOne(axis, value) is deprecated since "
                               "version 2.3.0. Use PreLoadOne(axis, value, "
                               "repetitions) instead.")
                        self.warning(msg)
                        res = ctrl.PreLoadOne(axis, master_value)
                    if not res:
                        msg = ("%s.PreLoadOne(%d) returned False" %
                               (pool_ctrl.name, axis))
                        raise Exception(msg)
                    try:
                        ctrl.LoadOne(axis, master_value, repetitions)
                    except TypeError:
                        msg = ("LoadOne(axis, value) is deprecated since "
                               "version 2.3.0. Use LoadOne(axis, value, "
                               "repetitions) instead.")
                        self.warning(msg)
                        ctrl.LoadOne(axis, master_value)
                    ctrl.LoadAll()
                except Exception, e:
                    self.debug(e, exc_info=True)
                    master.set_state(State.Fault, propagate=2)
                    msg = ("Load sequence of %s failed" % pool_ctrl.name)
                    raise Exception(msg)

            # PreStartAll on all enabled controllers
            for pool_ctrl in pool_ctrls:
                pool_ctrl.ctrl.PreStartAll()

            # PreStartOne & StartOne on all enabled elements
            for pool_ctrl in pool_ctrls:
                ctrl = pool_ctrl.ctrl
                pool_ctrl_data = pool_ctrls_dict[pool_ctrl]
                elements = pool_ctrl_data['channels'].keys()
                timer_monitor = pool_ctrl_data[master_key]
                # make sure that the timer/monitor is started as the last one
                elements.remove(timer_monitor)
                elements.append(timer_monitor)
                for element in elements:
                    try:
                        channel = channels[element]
                    except KeyError:
                        continue
                    axis = element.axis
                    ret = ctrl.PreStartOne(axis, master_value)
                    if not ret:
                        msg = ("%s.PreStartOne(%d) returns False" %
                               (pool_ctrl.name, axis))
                        raise Exception(msg)
                    try:
                        ctrl.StartOne(axis, master_value)
                    except Exception, e:
                        self.debug(e, exc_info=True)
                        element.set_state(State.Fault, propagate=2)
                        msg = ("%s.StartOne(%d) failed" %
                               (pool_ctrl.name, axis))
                        raise Exception(msg)
Example #11
0
    def start_action(self, ctrls, synch_description, moveable=None,
                     sw_synch_initial_domain=None, *args, **kwargs):
        """Start synchronization action.

        :param ctrls: list of enabled trigger/gate controllers
        :type ctrls: list
        :param synch_description: synchronization description
        :type synch_description:
         :class:`~sardana.pool.poolsynchronization.SynchDescription`
        :param moveable: (optional) moveable object used as the
         synchronization source in the Position domain
        :type moveable: :class:`~sardna.pool.poolmotor.PoolMotor` or
         :class:`~sardana.pool.poolpseudomotor.PoolPseudoMotor`
        :param sw_synch_initial_domain: (optional) - initial domain for
         software synchronizer, can be either
         :obj:`~sardana.pool.pooldefs.SynchDomain.Time` or
         :obj:`~sardana.pool.pooldefs.SynchDomain.Position`
        """
        with ActionContext(self):

            # loads synchronization description
            for ctrl in ctrls:
                pool_ctrl = ctrl.element
                pool_ctrl.ctrl.PreSynchAll()
                for channel in ctrl.get_channels(enabled=True):
                    axis = channel.axis
                    ret = pool_ctrl.ctrl.PreSynchOne(axis, synch_description)
                    if not ret:
                        msg = ("%s.PreSynchOne(%d) returns False" %
                               (ctrl.name, axis))
                        raise Exception(msg)
                    pool_ctrl.ctrl.SynchOne(axis, synch_description)
                pool_ctrl.ctrl.SynchAll()

            # attaching listener (usually acquisition action)
            # to the software trigger gate generator
            if self._listener is not None:
                if sw_synch_initial_domain is not None:
                    self._synch_soft.initial_domain = sw_synch_initial_domain
                self._synch_soft.set_configuration(synch_description)
                self._synch_soft.add_listener(self._listener)
                remove_acq_listener = partial(self._synch_soft.remove_listener,
                                              self._listener)
                self.add_finish_hook(remove_acq_listener, False)
                self._synch_soft.add_listener(
                    self.main_element.on_element_changed)
                remove_mg_listener = partial(self._synch_soft.remove_listener,
                                             self.main_element)
                self.add_finish_hook(remove_mg_listener, False)
            # subscribing to the position change events to generate events
            # in position domain
            if moveable is not None:
                position = moveable.get_position_attribute()
                position.add_listener(self._synch_soft)
                remove_pos_listener = partial(position.remove_listener,
                                              self._synch_soft)
                self.add_finish_hook(remove_pos_listener, False)

            # start software synchronizer
            if self._listener is not None:
                self._synch_soft.start()
                get_thread_pool().add(self._synch_soft.run)

            # PreStartAll on all controllers
            for ctrl in ctrls:
                pool_ctrl = ctrl.element
                pool_ctrl.ctrl.PreStartAll()

            # PreStartOne & StartOne on all elements
            for ctrl in ctrls:
                pool_ctrl = ctrl.element
                for channel in ctrl.get_channels(enabled=True):
                    axis = channel.axis
                    ret = pool_ctrl.ctrl.PreStartOne(axis)
                    if not ret:
                        raise Exception("%s.PreStartOne(%d) returns False"
                                        % (pool_ctrl.name, axis))
                    pool_ctrl.ctrl.StartOne(axis)

            # set the state of all elements to inform their listeners
            self._channels = []
            for ctrl in ctrls:
                for channel in ctrl.get_channels(enabled=True):
                    channel.set_state(State.Moving, propagate=2)
                    self._channels.append(channel)

            # StartAll on all controllers
            for ctrl in ctrls:
                pool_ctrl = ctrl.element
                pool_ctrl.ctrl.StartAll()