Example #1
0
    def start_processes(self, counter_pv, data_pv, frame_type_pv, logger, *args):
        """
        This function starts processes and callbacks.

        This is a main thread that starts thread reacting to the callback, starts the consuming process, and sets a
        callback on the frame counter PV change. The function then awaits for the data in the exit queue that indicates
        that all frames have been processed. The functin cancells the callback on exit.
    
        Parameters
        ----------
        counter_pv : str
            a PV string for the area detector frame counter
    
        data_pv : str
            a PV string for the area detector frame data

        frame_type_pv : str
            a PV string for the area detector data type
    
        logger : Logger
            a Logger instance, typically synchronized with the consuming process logger
    
        *args : list
            a list of arguments specific to the client process
    
        Returns
        -------
        None
        """
        data_thread = CAThread(target = self.deliver_data, args=(data_pv, frame_type_pv, logger,))
        data_thread.start()

        adapter.start_process(self.process_dataq, logger, *args)
        self.cntr_pv = PV(counter_pv)
        self.cntr_pv.add_callback(self.on_change, index = 1)
Example #2
0
    def __init__(self, prefix, ad_prefix, stream,
                 enable_callbacks=0, min_cbtime=0, queuesize=5):
        """
        Parameters
        ----------
        prefix: str
            The plugin prefix that comes after the areaDetector prefix in the
            PV names.

        ad_prefix: str
            The base areaDetector control prefix. This should match a real
            areaDetector IOC's prefix.

        stream: str
            The image stream to use for the plugins. We'll be using:
                $(ad_prefix)$(stream):ArrayData    for values
                $(ad_prefix)$(stream):UniqueId_RBV for update monitoring

        enable_callbcaks: bool, optional
            If True, start the IOC with callbacks enabled. Start disabled
            otherwise.

        min_cbtime: float, optional
            The initial value for the minimum time for each callback loop.

        queuesize: int, optional
            The initial value for the array queue. The default is 5.
        """
        self.server = PypvServer(ad_prefix + prefix)
        self.ad_prefix = ad_prefix
        self.ad_directory = {}
        self.settings_lock = RLock()
        self.plugins = {}
        self.has_update = Event()
        self.enable_callbacks = int(enable_callbacks)
        self.min_cbtime = float(min_cbtime)
        self.queue = None
        queuesize = int(queuesize)

        self._ndarray_port_cb(value=str(stream))
        self._add_builtin('NDArrayPort', str(stream), cb=self._ndarray_port_cb)
        self._enable_callbacks_cb(value=self.enable_callbacks)
        self._add_builtin('EnableCallbacks', self.enable_callbacks,
                          cb=self._enable_callbacks_cb)
        self._min_cbtime_cb(value=self.min_cbtime)
        self._add_builtin('MinCallbackTime', self.min_cbtime,
                          cb=self._min_cbtime_cb)
        self._queuesize_cb(value=queuesize)
        self._add_builtin('QueueSize', queuesize, cb=self._queuesize_cb)
        self._add_builtin('QueueUse', 0)
        self._add_builtin('DroppedArrays', 0)

        arrays = CAThread(target=self._array_cb_loop, args=(), daemon=True)
        plugins = Thread(target=self._get_queue_loop, args=(), daemon=True)
        arrays.start()
        plugins.start()
Example #3
0
    def start_processes(self, acquire_pv, counter_pv_name, data_pv, frame_type_pv, logger, reportq, *args, **kwargs):
        """
        This function starts processes and callbacks.

        This is a main thread that starts thread reacting to the callback, starts the consuming process, and sets a
        callback on the frame counter PV change. The function then awaits for the data in the exit queue that indicates
        that all frames have been processed. The functin cancells the callback on exit.

        Parameters
        ----------
        counter_pv : str
            a PV string for the area detector frame counter

        data_pv : str
            a PV string for the area detector frame data

        frame_type_pv : str
            a PV string for the area detector data type

        logger : Logger
            a Logger instance, typically synchronized with the consuming process logger

        *args : list
            a list of arguments specific to the client process

        Returns
        -------
        None
        """
        data_thread = CAThread(target=self.deliver_data, args=(data_pv, frame_type_pv, logger,))
        data_thread.start()
        p = Process(target=handler.handle_data,
                    args=(self.process_dataq, reportq, args, kwargs,))
        p.start()

        self.counter_pv = PV(counter_pv_name)
        self.counter_pv.add_callback(self.on_ctr_change, index=1)

        self.acq_pv = PV(acquire_pv)
        self.acq_pv.add_callback(self.acq_done, index=2)

        try:
            callback_pv_name = kwargs['callback_pv']
            self.callback_pv = PV(callback_pv_name)
            self.callback_pv.add_callback(self.on_change, as_string=True, index=3)
        except KeyError:
            pass
    def setup(self):
        """Set up the server.

        Starts threads and registers for EPICS callbacks.

        """
        self._publisher_thread = CAThread(target=self._publisher,
                                          args=(self.update_addr,), daemon=True)
        self._publisher_thread.start()
        self._request_thread = CAThread(target=self._request_handler,
                                        args=(self.request_addr,), daemon=True)
        self._request_thread.start()
        for attr in self.robot.attrs:
            pv = getattr(self.robot, attr)
            pv.add_callback(self._pv_callback)
        self.robot.client_update.add_callback(self._on_robot_update)
        self.logger.debug('setup complete')
Example #5
0
    def setup(self):
        """Set up the server.

        Starts threads and registers for EPICS callbacks.

        """
        self._publisher_thread = CAThread(target=self._publisher,
                                          args=(self.update_addr, ),
                                          daemon=True)
        self._publisher_thread.start()
        self._request_thread = CAThread(target=self._request_handler,
                                        args=(self.request_addr, ),
                                        daemon=True)
        self._request_thread.start()
        for attr in self.robot.attrs:
            pv = getattr(self.robot, attr)
            pv.add_callback(self._pv_callback)
        self.robot.client_update.add_callback(self._on_robot_update)
        self.logger.debug('setup complete')
Example #6
0
    def __init__(self, window, Win, first_cavity_id, last_cavity_id, first_phase, last_phase, phase_step, delay_before_scan, delay_read, num_read, mode): 
        CAThread.__init__(self)
        self.window = window
        self.Win = Win
        self.first_cavity_id = first_cavity_id
        self.last_cavity_id = last_cavity_id
        self.first_phase = first_phase
        self.last_phase = last_phase
        self.phase_step = phase_step
        self.delay_before_scan = delay_before_scan
        self.delay_read = delay_read
        self.num_read = num_read

        self.timeToQuit = threading.Event()
        self.timeToPause = threading.Event()
        self.timeToQuit.clear()
        self.timeToPause.clear()
        self.pause = False
        self.mode = mode
Example #7
0
    def start_processes(self, counter_pv, data_pv, frame_type_pv, logger,
                        *args):
        """
        This function starts processes and callbacks.

        This is a main thread that starts thread reacting to the callback, starts the consuming process, and sets a
        callback on the frame counter PV change. The function then awaits for the data in the exit queue that indicates
        that all frames have been processed. The functin cancells the callback on exit.
    
        Parameters
        ----------
        counter_pv : str
            a PV string for the area detector frame counter
    
        data_pv : str
            a PV string for the area detector frame data

        frame_type_pv : str
            a PV string for the area detector data type
    
        logger : Logger
            a Logger instance, typically synchronized with the consuming process logger
    
        *args : list
            a list of arguments specific to the client process
    
        Returns
        -------
        None
        """
        data_thread = CAThread(target=self.deliver_data,
                               args=(
                                   data_pv,
                                   frame_type_pv,
                                   logger,
                               ))
        data_thread.start()

        adapter.start_process(self.process_dataq, logger, *args)
        self.cntr_pv = PV(counter_pv)
        self.cntr_pv.add_callback(self.on_change, index=1)
Example #8
0
 def _process_request(self, message):
     """Parse requests from the clients and take the appropriate action."""
     self.logger.debug('client request: %r', message)
     operation = message.get('operation')
     parameters = message.get('parameters', {})
     try:
         target = getattr(self, operation)
     except (AttributeError, TypeError):
         self.logger.error('operation does not exist: %r', operation)
         return {'error': 'invalid request: operation does not exist'}
     try:
         operation_type = target._operation_type
     except AttributeError:
         self.logger.error('%r must be declared an operation', operation)
         return {
             'error': 'invalid request: %r not an operation' % operation
         }
     try:
         sig = inspect.signature(target)
         if operation_type == 'query':
             sig.bind(**parameters)
         else:
             sig.bind(None, **parameters)  # Must accept a handle argument
     except (ValueError, TypeError):
         self.logger.error('invalid arguments for operation %r: %r',
                           operation, parameters)
         return {'error': 'invalid request: incorrect arguments'}
     self.logger.debug('calling: %r with %r', operation, parameters)
     if operation_type == 'query':
         return target(**parameters)
     elif operation_type in {'foreground', 'background'}:
         handle = self._next_handle()
         thread = CAThread(target=target,
                           args=(handle, ),
                           kwargs=parameters,
                           daemon=True)
         thread.start()
         return {'error': None, 'handle': handle}
     else:
         return {'error': 'invalid request: unknown operation type'}
    def start_processes(self):
        """
        This function starts processes and callbacks.

        This is a main thread that starts thread reacting to the callback, starts the consuming process, and sets a
        callback on the frame counter PV change. The function then awaits for the data in the exit queue that indicates
        that all frames have been processed. The functin cancells the callback on exit.

        Parameters
        ----------
        none

        Returns
        -------
        nothing
        """
        data_thread = CAThread(target=self.handle_event, args=())
        data_thread.start()

        self.counter_pv = PV(self.get_counter_pv_name())
        self.counter_pv.add_callback(self.on_change, index=1)

        self.acq_pv = PV(self.get_acquire_pv_name())
        self.acq_pv.add_callback(self.acq_done, index=2)
Example #10
0
 def _process_request(self, message):
     """Parse requests from the clients and take the appropriate action."""
     self.logger.debug('client request: %r', message)
     operation = message.get('operation')
     parameters = message.get('parameters', {})
     try:
         target = getattr(self, operation)
     except (AttributeError, TypeError):
         self.logger.error('operation does not exist: %r', operation)
         return {'error': 'invalid request: operation does not exist'}
     try:
         operation_type = target._operation_type
     except AttributeError:
         self.logger.error('%r must be declared an operation', operation)
         return {'error': 'invalid request: %r not an operation' % operation}
     try:
         sig = inspect.signature(target)
         if operation_type == 'query':
             sig.bind(**parameters)
         else:
             sig.bind(None, **parameters)  # Must accept a handle argument
     except (ValueError, TypeError):
         self.logger.error('invalid arguments for operation %r: %r',
                           operation, parameters)
         return {'error': 'invalid request: incorrect arguments'}
     self.logger.debug('calling: %r with %r', operation, parameters)
     if operation_type == 'query':
         return target(**parameters)
     elif operation_type in {'foreground', 'background'}:
         handle = self._next_handle()
         thread = CAThread(target=target, args=(handle,),
                           kwargs=parameters, daemon=True)
         thread.start()
         return {'error': None, 'handle': handle}
     else:
         return {'error': 'invalid request: unknown operation type'}
Example #11
0
def atl(softioc, caclient, tmpdir_factory):
    AbortCh.fields['ACNT'] = ':ACNT'
    AbortCh.fields['TCNT'] = ':TCNT'
    dburi = ('sqlite:///' +
             str(tmpdir_factory.mktemp('data').join('testdata.db'))
             )
    insert_current_pv_mock(dburi)
    set_initial_abort_state()
    atl = Aborttl(dburi, 'ET_dummyHost:RESETw')

    thread = CAThread(target=atl.run)
    thread.daemon = True
    thread.start()
    time.sleep(5)
    yield atl
    atl.stop()
    thread.join()
    time.sleep(1)
Example #12
0
def test_cathread():
    write( 'Test use CAThread\n')
    th1 = CAThread(target=run_CAThread, args=(names_a, 3, 'A'))
    th2 = CAThread(target=run_CAThread, args=(names_b, 5, 'B'))
    run_threads((th1, th2))
Example #13
0
class RobotServer(object):
    """
    The ``RobotServer`` monitors the state of the robot and processes operation
    requests from ``RobotClient``\ s. The robot state is broadcast to clients via a
    Zero-MQ publish/subscribe channel. Operation requests are received via a
    seperate request/reply channel.

    Args:
        robot (Robot): An instance of the aspyrobot.Robot class.
        logger: A logging.Logger object.
        update_addr: An address to create a Zero-MQ socket to broadcast robot
            state updates to clients.
        request_addr: An address to create a Zero-MQ socket to receive operation
            requests from clients.

    """
    def __init__(self,
                 robot,
                 logger=None,
                 update_addr='tcp://*:2000',
                 request_addr='tcp://*:2001'):
        self.robot = robot
        self.logger = logger or logging.getLogger(__name__)
        self.request_addr = request_addr
        self.update_addr = update_addr
        self._zmq_context = zmq.Context()
        self.publish_queue = Queue()
        self._foreground_lock = Lock()
        self._operation_handle = 0
        self._handle_lock = Lock()
        self._shutdown_requested = False

    @withCA
    def setup(self):
        """Set up the server.

        Starts threads and registers for EPICS callbacks.

        """
        self._publisher_thread = CAThread(target=self._publisher,
                                          args=(self.update_addr, ),
                                          daemon=True)
        self._publisher_thread.start()
        self._request_thread = CAThread(target=self._request_handler,
                                        args=(self.request_addr, ),
                                        daemon=True)
        self._request_thread.start()
        for attr in self.robot.attrs:
            pv = getattr(self.robot, attr)
            pv.add_callback(self._pv_callback)
        self.robot.client_update.add_callback(self._on_robot_update)
        self.logger.debug('setup complete')

    def shutdown(self):
        """Request the server shuts down.

        Causes the publisher and request threads to exit gracefully.

        """
        self._shutdown_requested = True

    def _pv_callback(self, pvname, value, char_value, type, **kwargs):
        """When robot PVs change send a value update to clients."""
        suffix = pvname.replace(self.robot._prefix, '')
        attr = self.robot.attrs_r[suffix]
        if 'char' in type or 'string' in type:
            value = char_value
        self.values_update({attr: value})

    def _publisher(self, update_addr):
        """Publish robot state updates to clients over Zero-MQ."""
        socket = self._zmq_context.socket(zmq.PUB)
        socket.bind(update_addr)
        while not self._shutdown_requested:
            try:
                message = self.publish_queue.get(timeout=.1)
            except Empty:
                continue
            data = message.get('data', {})
            if not (len(data) == 1
                    and 'time' in data):  # Don't log time messages
                self.logger.debug('sending to client: %r', message)
            socket.send_json(message)
        socket.close()

    def _request_handler(self, request_addr):
        """Listen for operation requests from clients."""
        socket = self._zmq_context.socket(zmq.REP)
        socket.bind(request_addr)
        while not self._shutdown_requested:
            try:
                message = socket.recv_json(flags=zmq.NOBLOCK)
            except zmq.ZMQError:
                time.sleep(.05)
                continue
            response = self._process_request(message)
            socket.send_json(response)
        socket.close()

    def _process_request(self, message):
        """Parse requests from the clients and take the appropriate action."""
        self.logger.debug('client request: %r', message)
        operation = message.get('operation')
        parameters = message.get('parameters', {})
        try:
            target = getattr(self, operation)
        except (AttributeError, TypeError):
            self.logger.error('operation does not exist: %r', operation)
            return {'error': 'invalid request: operation does not exist'}
        try:
            operation_type = target._operation_type
        except AttributeError:
            self.logger.error('%r must be declared an operation', operation)
            return {
                'error': 'invalid request: %r not an operation' % operation
            }
        try:
            sig = inspect.signature(target)
            if operation_type == 'query':
                sig.bind(**parameters)
            else:
                sig.bind(None, **parameters)  # Must accept a handle argument
        except (ValueError, TypeError):
            self.logger.error('invalid arguments for operation %r: %r',
                              operation, parameters)
            return {'error': 'invalid request: incorrect arguments'}
        self.logger.debug('calling: %r with %r', operation, parameters)
        if operation_type == 'query':
            return target(**parameters)
        elif operation_type in {'foreground', 'background'}:
            handle = self._next_handle()
            thread = CAThread(target=target,
                              args=(handle, ),
                              kwargs=parameters,
                              daemon=True)
            thread.start()
            return {'error': None, 'handle': handle}
        else:
            return {'error': 'invalid request: unknown operation type'}

    def _next_handle(self):
        """Generate a new operation handle in a thread safe way."""
        with self._handle_lock:
            self._operation_handle += 1
            return self._operation_handle

    def _on_robot_update(self, char_value, **_):
        """Handle special update messages from SPEL.

        These messages use Python dictionary literal syntax and contain a key
        for the variable to be set. We call `update_x` method with the other
        key/values from the dictionary supplied as keyword arguments.

        """
        try:
            message = literal_eval(char_value)
        except SyntaxError:
            return self.logger.error('Invalid update message: %r', char_value)
        attr = message.pop('set')
        try:
            method = getattr(self, 'update_' + attr)
        except AttributeError:
            return self.logger.warning('Unhandled robot update: %r',
                                       char_value)
        try:
            method(**message)
        except TypeError:
            self.logger.error('Invalid method signature for update: %r',
                              message)

    def operation_update(self, handle, message='', stage='update', error=None):
        """Add an operation update to the queue to be sent clients.

        Args:
            handle (int): Operation handle.
            message (str): Message to be sent to clients.
            stage (str): `'start'`, `'update'` or `'end'`
            error (str): Error message.

        """
        self.publish_queue.put({
            'type': 'operation',
            'stage': stage,
            'handle': handle,
            'message': message,
            'error': error,
        })

    def values_update(self, update):
        """Add an robot attribute value update to the queue to be sent clients.

        Args:
            update (dict): robot attributes and their values. For example:
                `{'safety_gate': 1, 'motors_on': 0}`

        """
        self.publish_queue.put({'type': 'values', 'data': update})

    @query_operation
    def refresh(self):
        """Query operation to fetch the latest values of the robot state.

        This method should be overridden if the server maintains additional state
        information that needs to be sent to the clients.

        """
        return self.robot.snapshot()

    @background_operation
    def clear(self, handle, level):
        """
        Clear robot state.

        Args:
            level (str): `'status`' or `'all'`

        """
        self.logger.warning('clear: %r', level)
        self.robot.run_background_task('ResetRobotStatus', level)
Example #14
0
def run_test(runtime=1, pvnames=None,  run_name='thread c'):
    msg = '-> thread "%s" will run for %.3f sec, monitoring %s\n'
    stdout.write(msg % (run_name, runtime, pvnames))
    def onChanges(pvname=None, value=None, char_value=None, **kw):
        stdout.write('   %s = %s (%s)\n' % (pvname, char_value, run_name))
        stdout.flush()

    # epics.ca.use_initial_context()   #  epics.ca.create_context()
    start_time = time.time()
    pvs = [epics.PV(pvn, callback=onChanges) for pvn in pvnames]

    while time.time()-start_time < runtime:
        time.sleep(0.1)

    [p.clear_callbacks() for p in pvs]
    stdout.write( 'Completed Thread  %s\n' % ( run_name))

stdout.write( "First, create a PV in the main thread:\n")
p = epics.PV(updating_pvlist[0])

stdout.write("Run 2 Background Threads simultaneously:\n")
th1 = CAThread(target=run_test,args=(3, pvlist_a,  'A'))
th1.start()

th2 = CAThread(target=run_test,args=(6, pvlist_b, 'B'))
th2.start()

th2.join()
th1.join()
stdout.write('Done\n')
Example #15
0
    # pvs_b.append(pvname)
    names_b.append(pvname)

names_a = names_b[1:]
pvs_a = pvs_b[1:]

epics.ca.create_context()

styles = ('decorator', 'init', 'cathread')
style = styles[2]

if style == 'init':
    write(
        'Test use plain threading.Thread, force use of initial CA Context \n')
    th1 = Thread(target=test_initcontext, args=(names_a, 2, 'A'))
    th2 = Thread(target=test_initcontext, args=(names_b, 3, 'B'))
    run_threads((th1, th2))

elif style == 'decorator':
    write('Test use plain threading.Thread, withInitialContext decorator\n')
    th1 = Thread(target=test_decorator, args=(names_a, 3, 'A'))
    th2 = Thread(target=test_decorator, args=(names_b, 5, 'B'))
    run_threads((th1, th2))
elif style == 'cathread':
    write('Test use CAThread\n')
    th1 = CAThread(target=test_CAThread, args=(names_a, 3, 'A'))
    th2 = CAThread(target=test_CAThread, args=(names_b, 5, 'B'))
    run_threads((th1, th2))

write('Test Done\n---------------------\n')
Example #16
0
class RobotServer(object):
    """
    The ``RobotServer`` monitors the state of the robot and processes operation
    requests from ``RobotClient``\ s. The robot state is broadcast to clients via a
    Zero-MQ publish/subscribe channel. Operation requests are received via a
    seperate request/reply channel.

    Args:
        robot (Robot): An instance of the aspyrobot.Robot class.
        logger: A logging.Logger object.
        update_addr: An address to create a Zero-MQ socket to broadcast robot
            state updates to clients.
        request_addr: An address to create a Zero-MQ socket to receive operation
            requests from clients.

    """
    def __init__(self, robot, logger=None, update_addr='tcp://*:2000',
                 request_addr='tcp://*:2001'):
        self.robot = robot
        self.logger = logger or logging.getLogger(__name__)
        self.request_addr = request_addr
        self.update_addr = update_addr
        self._zmq_context = zmq.Context()
        self.publish_queue = Queue()
        self._foreground_lock = Lock()
        self._operation_handle = 0
        self._handle_lock = Lock()
        self._shutdown_requested = False

    @withCA
    def setup(self):
        """Set up the server.

        Starts threads and registers for EPICS callbacks.

        """
        self._publisher_thread = CAThread(target=self._publisher,
                                          args=(self.update_addr,), daemon=True)
        self._publisher_thread.start()
        self._request_thread = CAThread(target=self._request_handler,
                                        args=(self.request_addr,), daemon=True)
        self._request_thread.start()
        for attr in self.robot.attrs:
            pv = getattr(self.robot, attr)
            pv.add_callback(self._pv_callback)
        self.robot.client_update.add_callback(self._on_robot_update)
        self.logger.debug('setup complete')

    def shutdown(self):
        """Request the server shuts down.

        Causes the publisher and request threads to exit gracefully.

        """
        self._shutdown_requested = True

    def _pv_callback(self, pvname, value, char_value, type, **kwargs):
        """When robot PVs change send a value update to clients."""
        suffix = pvname.replace(self.robot._prefix, '')
        attr = self.robot.attrs_r[suffix]
        if 'char' in type or 'string' in type:
            value = char_value
        self.values_update({attr: value})

    def _publisher(self, update_addr):
        """Publish robot state updates to clients over Zero-MQ."""
        socket = self._zmq_context.socket(zmq.PUB)
        socket.bind(update_addr)
        while not self._shutdown_requested:
            try:
                message = self.publish_queue.get(timeout=.1)
            except Empty:
                continue
            data = message.get('data', {})
            if not (len(data) == 1 and 'time' in data):  # Don't log time messages
                self.logger.debug('sending to client: %r', message)
            socket.send_json(message)
        socket.close()

    def _request_handler(self, request_addr):
        """Listen for operation requests from clients."""
        socket = self._zmq_context.socket(zmq.REP)
        socket.bind(request_addr)
        while not self._shutdown_requested:
            try:
                message = socket.recv_json(flags=zmq.NOBLOCK)
            except zmq.ZMQError:
                time.sleep(.05)
                continue
            response = self._process_request(message)
            socket.send_json(response)
        socket.close()

    def _process_request(self, message):
        """Parse requests from the clients and take the appropriate action."""
        self.logger.debug('client request: %r', message)
        operation = message.get('operation')
        parameters = message.get('parameters', {})
        try:
            target = getattr(self, operation)
        except (AttributeError, TypeError):
            self.logger.error('operation does not exist: %r', operation)
            return {'error': 'invalid request: operation does not exist'}
        try:
            operation_type = target._operation_type
        except AttributeError:
            self.logger.error('%r must be declared an operation', operation)
            return {'error': 'invalid request: %r not an operation' % operation}
        try:
            sig = inspect.signature(target)
            if operation_type == 'query':
                sig.bind(**parameters)
            else:
                sig.bind(None, **parameters)  # Must accept a handle argument
        except (ValueError, TypeError):
            self.logger.error('invalid arguments for operation %r: %r',
                              operation, parameters)
            return {'error': 'invalid request: incorrect arguments'}
        self.logger.debug('calling: %r with %r', operation, parameters)
        if operation_type == 'query':
            return target(**parameters)
        elif operation_type in {'foreground', 'background'}:
            handle = self._next_handle()
            thread = CAThread(target=target, args=(handle,),
                              kwargs=parameters, daemon=True)
            thread.start()
            return {'error': None, 'handle': handle}
        else:
            return {'error': 'invalid request: unknown operation type'}

    def _next_handle(self):
        """Generate a new operation handle in a thread safe way."""
        with self._handle_lock:
            self._operation_handle += 1
            return self._operation_handle

    def _on_robot_update(self, char_value, **_):
        """Handle special update messages from SPEL.

        These messages use Python dictionary literal syntax and contain a key
        for the variable to be set. We call `update_x` method with the other
        key/values from the dictionary supplied as keyword arguments.

        """
        try:
            message = literal_eval(char_value)
        except SyntaxError:
            return self.logger.error('Invalid update message: %r', char_value)
        attr = message.pop('set')
        try:
            method = getattr(self, 'update_' + attr)
        except AttributeError:
            return self.logger.warning('Unhandled robot update: %r', char_value)
        try:
            method(**message)
        except TypeError:
            self.logger.error('Invalid method signature for update: %r', message)

    def operation_update(self, handle, message='', stage='update', error=None):
        """Add an operation update to the queue to be sent clients.

        Args:
            handle (int): Operation handle.
            message (str): Message to be sent to clients.
            stage (str): `'start'`, `'update'` or `'end'`
            error (str): Error message.

        """
        self.publish_queue.put({
            'type': 'operation',
            'stage': stage,
            'handle': handle,
            'message': message,
            'error': error,
        })

    def values_update(self, update):
        """Add an robot attribute value update to the queue to be sent clients.

        Args:
            update (dict): robot attributes and their values. For example:
                `{'safety_gate': 1, 'motors_on': 0}`

        """
        self.publish_queue.put({'type': 'values', 'data': update})

    @query_operation
    def refresh(self):
        """Query operation to fetch the latest values of the robot state.

        This method should be overridden if the server maintains additional state
        information that needs to be sent to the clients.

        """
        return self.robot.snapshot()

    @background_operation
    def clear(self, handle, level):
        """
        Clear robot state.

        Args:
            level (str): `'status`' or `'all'`

        """
        self.logger.warning('clear: %r', level)
        self.robot.run_background_task('ResetRobotStatus', level)
Example #17
0
        stdout.flush()

    # epics.ca.use_initial_context()   #  epics.ca.create_context()
    start_time = time.time()
    pvs = [epics.PV(pvn, callback=onChanges) for pvn in pvnames]

    while time.time() - start_time < runtime:
        time.sleep(0.001)

    [p.clear_callbacks() for p in pvs]
    stdout.write('Completed Thread  %s\n' % (run_name))


stdout.write("First, create a PV in the main thread:\n")
for pvname in pvlist_a + pvlist_b:
    p = epics.PV(pvname)
    p.connect()
    p.get()
    print(p.info)

stdout.write("Run 2 Background Threads simultaneously:\n")
th1 = CAThread(target=run_test, args=(30, pvlist_a, 'A'))
th1.start()

th2 = CAThread(target=run_test, args=(60, pvlist_b, 'B'))
th2.start()

th2.join()
th1.join()
stdout.write('Done\n')