Example #1
0
    def __init__(self):

        # create logger
        self.logger = logging.getLogger('frame_receiver_client')
        self.logger.setLevel(logging.DEBUG)

        # create console handler and set level to debug
        ch = logging.StreamHandler(sys.stdout)
        ch.setLevel(logging.DEBUG)

        # create formatter
        formatter = logging.Formatter(
            '%(asctime)s %(levelname)s %(name)s - %(message)s')

        # add formatter to ch
        ch.setFormatter(formatter)

        # add ch to logger
        self.logger.addHandler(ch)

        # Create the appropriate IPC channels
        self.ctrl_channel = IpcChannel(IpcChannel.CHANNEL_TYPE_DEALER)
        self.ctrl_channel.connect('tcp://127.0.0.1:5000')

        self._run = True
Example #2
0
    def __init__(self):

        prog_name = os.path.basename(sys.argv[0])

        # Parse command line arguments
        self.args = self._parse_arguments(prog_name)

        # create logger
        self.logger = logging.getLogger(prog_name)
        self.logger.setLevel(logging.DEBUG)

        # create console handler and set level to debug
        ch = logging.StreamHandler(sys.stdout)
        ch.setLevel(logging.DEBUG)

        # create formatter
        formatter = logging.Formatter(
            '%(asctime)s %(levelname)s %(name)s - %(message)s')

        # add formatter to ch
        ch.setFormatter(formatter)

        # add ch to logger
        self.logger.addHandler(ch)

        # Create the appropriate IPC channels
        self.ctrl_channel = IpcChannel(IpcChannel.CHANNEL_TYPE_DEALER)
        self.ctrl_channel.connect(self.args.ctrl_endpoint)

        self._run = True
    def __init__(self, run_status_signal, num_frames, app_main, live_view_signal):
        ''' This is executed on the first run of each GUI session - this object is retained
            throughout the lifecycle of the session.
        '''

        try:
            self.run_status_signal = run_status_signal
            self.num_frames = num_frames
            self.app_main = app_main
            self.live_view_signal = live_view_signal
            self.hdf_file_location = None

            print("Launching Frame Receiver and Frame Processor")
            # Getting location of FR & FP and paths of their config files
            fr_fp_location = self.app_main.getCachedParam("frameReceiverProcessorLocation")
            frame_receiver_config_path = self.app_main.getCachedParam("frameReceiverConfigFile")
            frame_processor_config_path = self.app_main.getCachedParam("frameProcessorConfigFile")

            # Using Subprocess to launch the frame receiver and frame processor
            self.frame_receiver = Popen(["./frameReceiver", "--logconfig",
                                         frame_receiver_config_path], cwd=fr_fp_location)
            self.frame_processor = Popen(["./frameProcessor", "--logconfig",
                                         frame_processor_config_path], cwd=fr_fp_location)

            # Create the appropriate IPC channels for receiver and proccessor
            print("Connecting to Frame Receiver's IPC Control Channel")
            self.fr_ctrl_channel = IpcChannel(IpcChannel.CHANNEL_TYPE_DEALER)
            self.fr_ctrl_channel.connect(self.app_main.getCachedParam('odinFrCtrlChannel'))

            print("Connecting to Frame Proccessor's IPC Control Channel")
            self.fp_ctrl_channel = IpcChannel(IpcChannel.CHANNEL_TYPE_DEALER)
            self.fp_ctrl_channel.connect(self.app_main.getCachedParam('odinFpCtrlChannel'))
        except Exception as e:
            print("LdpFemOdinDataReceiver got exception during initialisation: %s" % e)
    def __init__(self, args=None, prog_name=None, logger=None):

        if prog_name is None:
            prog_name = os.path.basename(sys.argv[0])
        
        # Parse command line arguments
        self.args = self._parse_arguments(prog_name, args)
        
        # Create logger if not specified
        if logger is None:
            
            self.logger = logging.getLogger(prog_name)
            self.logger.setLevel(logging.DEBUG)
            
            # create console handler and set level to debug
            ch = logging.StreamHandler(sys.stdout)
            ch.setLevel(logging.DEBUG)
            
            # create formatter
            formatter = logging.Formatter('%(asctime)s %(levelname)s %(name)s - %(message)s')
            
            # add formatter to ch
            ch.setFormatter(formatter)
            
            # add ch to logger
            self.logger.addHandler(ch)

        else:
            self.logger = logger
    
        # Create the appropriate IPC channels
        self.fr_ctrl_channel = IpcChannel(IpcChannel.CHANNEL_TYPE_DEALER)
        self.fr_ctrl_channel.connect(self.args.fr_ctrl_endpoint)
        self.fp_ctrl_channel = IpcChannel(IpcChannel.CHANNEL_TYPE_DEALER)
        self.fp_ctrl_channel.connect(self.args.fp_ctrl_endpoint)
        
        self.channels = [('receiver', self.fr_ctrl_channel), ('processor', self.fp_ctrl_channel)]
        
        # Create empty receiver and processor configuration parameter dicts
        self.fr_config = {}
        self.fp_config = {}
        self.fp_plugins = []

        # Set default number of frames, file path and name
        self.frames = 1
        self.file_path = '/tmp'
        self.file_name = 'test.hdf5'
        
        # Internal IpcMessage ID counter
        self._msg_id = 0
Example #5
0
    def __init__(self, ip_address, port):
        self.logger = logging.getLogger(self.__class__.__name__)

        self._ip_address = ip_address
        self._port = port

        self.ctrl_endpoint = self.ENDPOINT_TEMPLATE.format(IP=ip_address,
                                                           PORT=port)
        self.logger.debug("Connecting to client at %s", self.ctrl_endpoint)
        self.ctrl_channel = IpcChannel(IpcChannel.CHANNEL_TYPE_DEALER)
        self.ctrl_channel.connect(self.ctrl_endpoint)

        self.message_id = 0

        self._lock = RLock()
Example #6
0
    def afterEditing(self):
        self.parentApp.ctrl_endpoint = self.ctrl.value
        self.parentApp.ready_endpoint = self.ready.value
        self.parentApp.release_endpoint = self.release.value
        self.parentApp.shared_buffer = self.buffer.value

        self.parentApp._ctrl_channel = IpcChannel(
            IpcChannel.CHANNEL_TYPE_DEALER)
        self.parentApp._ctrl_channel.connect(self.ctrl.value)
        self.parentApp.setNextForm("MAIN_MENU")
    def __init__(self):

        # Initialise the logging module with log messages directed to stdout
        #logging.basicConfig(format='%(asctime)s %(levelname)s FrameProcessor - %(message)s', level=logging.DEBUG)
        #ch = logging.StreamHandler(sys.stdout)
        #logging.addHandler(ch)

        # create logger
        self.logger = logging.getLogger('frame_processor')
        self.logger.setLevel(logging.DEBUG)

        # create console handler and set level to debug
        ch = logging.StreamHandler(sys.stdout)
        ch.setLevel(logging.DEBUG)

        # create formatter
        formatter = logging.Formatter(
            '%(asctime)s %(levelname)s %(name)s - %(message)s')

        # add formatter to ch
        ch.setFormatter(formatter)

        # add ch to logger
        self.logger.addHandler(ch)

        # Instantiate a configuration container object, which will be populated
        # with sensible default values
        self.config = FrameProcessorConfig(
            "FrameProcessor",
            "FrameProcessor - test harness to simulate operation of FrameProcessor application"
        )

        # Create the appropriate IPC channels
        self.ctrl_channel = IpcChannel(IpcChannel.CHANNEL_TYPE_REQ)
        self.ready_channel = IpcChannel(IpcChannel.CHANNEL_TYPE_SUB)
        self.release_channel = IpcChannel(IpcChannel.CHANNEL_TYPE_PUB)

        # Zero frames recevied counter
        self.frames_received = 0

        # Create the thread to handle frame processing
        self.frame_processor = threading.Thread(target=self.process_frames)
        self.frame_processor.daemon = True

        self._run = True
Example #8
0
class FrameReceiverClient(object):
    def __init__(self):

        # create logger
        self.logger = logging.getLogger('frame_receiver_client')
        self.logger.setLevel(logging.DEBUG)

        # create console handler and set level to debug
        ch = logging.StreamHandler(sys.stdout)
        ch.setLevel(logging.DEBUG)

        # create formatter
        formatter = logging.Formatter(
            '%(asctime)s %(levelname)s %(name)s - %(message)s')

        # add formatter to ch
        ch.setFormatter(formatter)

        # add ch to logger
        self.logger.addHandler(ch)

        # Create the appropriate IPC channels
        self.ctrl_channel = IpcChannel(IpcChannel.CHANNEL_TYPE_DEALER)
        self.ctrl_channel.connect('tcp://127.0.0.1:5000')

        self._run = True

    def run(self):

        self.logger.info("Frame receiver client starting up")

        self.logger.debug("Control IPC channel has identity {}".format(
            self.ctrl_channel.identity))

        msg = IpcMessage('cmd', 'configure')
        msg.set_param('test', {'list': True})
        self.ctrl_channel.send(msg.encode())

        pollevts = self.ctrl_channel.poll(1000)
        if pollevts == IpcChannel.POLLIN:
            reply = IpcMessage(from_str=self.ctrl_channel.recv())
            self.logger.info("Got response: {}".format(reply))
Example #9
0
class FrameReceiverClient(object):
    def __init__(self):

        prog_name = os.path.basename(sys.argv[0])

        # Parse command line arguments
        self.args = self._parse_arguments(prog_name)

        # create logger
        self.logger = logging.getLogger(prog_name)
        self.logger.setLevel(logging.DEBUG)

        # create console handler and set level to debug
        ch = logging.StreamHandler(sys.stdout)
        ch.setLevel(logging.DEBUG)

        # create formatter
        formatter = logging.Formatter(
            '%(asctime)s %(levelname)s %(name)s - %(message)s')

        # add formatter to ch
        ch.setFormatter(formatter)

        # add ch to logger
        self.logger.addHandler(ch)

        # Create the appropriate IPC channels
        self.ctrl_channel = IpcChannel(IpcChannel.CHANNEL_TYPE_DEALER)
        self.ctrl_channel.connect(self.args.ctrl_endpoint)

        self._run = True

    def _parse_arguments(self, prog_name=sys.argv[0]):

        parser = argparse.ArgumentParser(
            prog=prog_name, description='ODIN Frame Receiver Client')
        parser.add_argument(
            '--ctrl',
            type=str,
            default='tcp://127.0.0.1:5000',
            dest='ctrl_endpoint',
            help='Specify the IPC control channel endpoint URL')
        parser.add_argument(
            '--config',
            type=argparse.FileType('r'),
            dest='config_file',
            nargs='?',
            default=None,
            const=sys.stdin,
            help='Specify JSON configuration file to send as configure command'
        )
        parser.add_argument(
            '--status',
            action='store_true',
            help='Request a status report from the frame receiver')
        parser.add_argument('--shutdown',
                            action='store_true',
                            help='Instruct the frame receiver to shut down')

        args = parser.parse_args()
        return args

    def run(self):

        self.logger.info("Frame receiver client starting up")

        self.logger.debug("Control IPC channel has identity {}".format(
            self.ctrl_channel.identity))

        if self.args.config_file is not None:
            self.do_config_cmd(self.args.config_file)

        if self.args.status:
            self.do_status_cmd()

        if self.args.shutdown:
            self.do_shutdown_cmd()

    def do_config_cmd(self, config_file):

        try:
            config_params = json.load(config_file)

            config_msg = IpcMessage('cmd', 'configure')
            for param, value in config_params.items():
                config_msg.set_param(param, value)

            self.logger.info(
                "Sending configure command to frame receiver with specified parameters"
            )
            self.ctrl_channel.send(config_msg.encode())
            self.await_response()

        except JSONDecodeError as e:
            self.logger.error(
                "Failed to parse configuration file: {}".format(e))

    def do_status_cmd(self):

        status_msg = IpcMessage('cmd', 'status')
        self.logger.info("Sending status request to frame receiver")
        self.ctrl_channel.send(status_msg.encode())
        self.await_response()

    def do_shutdown_cmd(self):

        shutdown_msg = IpcMessage('cmd', 'shutdown')
        self.logger.info("Sending shutdown command to frame receiver")
        self.ctrl_channel.send(shutdown_msg.encode())
        self.await_response()

    def await_response(self, timeout_ms=1000):

        pollevts = self.ctrl_channel.poll(1000)
        if pollevts == IpcChannel.POLLIN:
            reply = IpcMessage(from_str=self.ctrl_channel.recv())
            self.logger.info("Got response: {}".format(reply))
class OdinDataClient(object):
    
    MESSAGE_ID_MAX = 2**32

    def __init__(self, args=None, prog_name=None, logger=None):

        if prog_name is None:
            prog_name = os.path.basename(sys.argv[0])
        
        # Parse command line arguments
        self.args = self._parse_arguments(prog_name, args)
        
        # Create logger if not specified
        if logger is None:
            
            self.logger = logging.getLogger(prog_name)
            self.logger.setLevel(logging.DEBUG)
            
            # create console handler and set level to debug
            ch = logging.StreamHandler(sys.stdout)
            ch.setLevel(logging.DEBUG)
            
            # create formatter
            formatter = logging.Formatter('%(asctime)s %(levelname)s %(name)s - %(message)s')
            
            # add formatter to ch
            ch.setFormatter(formatter)
            
            # add ch to logger
            self.logger.addHandler(ch)

        else:
            self.logger = logger
    
        # Create the appropriate IPC channels
        self.fr_ctrl_channel = IpcChannel(IpcChannel.CHANNEL_TYPE_DEALER)
        self.fr_ctrl_channel.connect(self.args.fr_ctrl_endpoint)
        self.fp_ctrl_channel = IpcChannel(IpcChannel.CHANNEL_TYPE_DEALER)
        self.fp_ctrl_channel.connect(self.args.fp_ctrl_endpoint)
        
        self.channels = [('receiver', self.fr_ctrl_channel), ('processor', self.fp_ctrl_channel)]
        
        # Create empty receiver and processor configuration parameter dicts
        self.fr_config = {}
        self.fp_config = {}
        self.fp_plugins = []

        # Set default number of frames, file path and name
        self.frames = 1
        self.file_path = '/tmp'
        self.file_name = 'test.hdf5'
        
        # Internal IpcMessage ID counter
        self._msg_id = 0
        
    def _next_msg_id(self):
        
        self._msg_id = (self._msg_id + 1) % self.MESSAGE_ID_MAX
        return self._msg_id
        
    def run(self):
        
        self.logger.info("Odin data client starting up")
        
        self.logger.debug("Frame receiver control IPC channel has identity {}".format(self.fr_ctrl_channel.identity))
        self.logger.debug("Frame processor control IPC channel has identity {}".format(self.fp_ctrl_channel.identity))
        
        if self.args.config_file is not None:
            self.load_config(self.args.config_file)

        if self.args.frames:
            self.set_num_frames(self.args.frames)
            
        if self.args.bitdepth:
            self.set_bitdepth(self.args.bitdepth)
            
        if self.args.file_path:
            self.set_file_path(self.args.file_path)
            
        if self.args.file_name:
            self.set_file_name(self.args.file_name)
        
        if self.args.config:
            self.do_config_cmd()
        
        if self.args.start:
            self.set_file_writing(True)
        elif self.args.stop:
            self.set_file_writing(False)
            
        if self.args.status:
            self.do_status_cmd()
            
        if self.args.getconfig:
            self.do_request_config_cmd()

        if self.args.shutdown:
            self.do_shutdown_cmd()
    
    def load_config(self, config_file):
        
        self.logger.debug("Parsing default configuration from file {}".format(config_file.name))
        try:
            config_params = json.load(config_file)

            if 'receiver_default_config' in config_params:
                self.fr_config = config_params['receiver_default_config']
                
            if 'processor_default_config' in config_params:
                self.fp_config = config_params['processor_default_config']
                
            if 'processor_plugins' in config_params:
                self.fp_plugins = config_params['processor_plugins']
                
        except JSONDecodeError as e:
            self.logger.error("Failed to parse configuration file: {}".format(e))
            
    def _ensure_config_section(self, config, section):
        
        if section not in config:
            config[section] = {}
            self.logger.debug("Created {} section in config params".format(section))
            
    def set_num_frames(self, frames):
        
        self.frames = frames
        
        self._ensure_config_section(self.fp_config, 'hdf')            
        self.fp_config['hdf']['frames'] = frames
        
    def set_file_path(self, file_path):
        
        self.file_path = file_path
        
        self._ensure_config_section(self.fp_config, 'hdf')
        self._ensure_config_section(self.fp_config['hdf'], 'file')
        self.fp_config['hdf']['file']['path'] = file_path
        
    def set_file_name(self, file_name):
        
        self.file_name = file_name
        
        self._ensure_config_section(self.fp_config, 'hdf')
        self._ensure_config_section(self.fp_config['hdf'], 'file')
        self.fp_config['hdf']['file']['name'] = file_name

    def set_bitdepth(self, bitdepth):
        
        bitdepth_str = '{:d}-bit'.format(bitdepth)
        
        self._ensure_config_section(self.fr_config, 'decoder_config')           
        self.fr_config['decoder_config']['bitdepth'] = bitdepth_str

        self._ensure_config_section(self.fp_config, 'excalibur')                       
        self.fp_config['excalibur']['bitdepth'] = bitdepth_str
        
        rdtype_map = {
            1:  0,
            6:  0,
            12: 1,
            24: 2,
        }
        rdtype = rdtype_map[bitdepth]
        
        self._ensure_config_section(self.fp_config, 'hdf')
        self._ensure_config_section(self.fp_config['hdf'], 'dataset')
        self._ensure_config_section(self.fp_config['hdf']['dataset'], 'data')
        self.fp_config['hdf']['dataset']['data']['datatype'] = rdtype
        
        #print(self.fp_config)
 
    def do_config_cmd(self):
        
        self.logger.info("Sending configuration command to frame receiver")
        self.send_config_cmd(self.fr_ctrl_channel, self.fr_config)
        
        if 'fr_setup' in self.fp_config:
            self.logger.info("Sending receiver plugin configuration command to frame processor")
            params = {'fr_setup' : self.fp_config['fr_setup']}
            self.send_config_cmd(self.fp_ctrl_channel, params)
            
        if len(self.fp_plugins):

            self.logger.info(
                "Sending {} plugin chain configuration commands to frame processor".format(
                len(self.fp_plugins)))
            for plugin in self.fp_plugins:
                self.send_config_cmd(self.fp_ctrl_channel, plugin)
                
        self.logger.info("Sending plugin parameter configuration command to frame processor")
        self.send_config_cmd(self.fp_ctrl_channel, self.fp_config)
                    
    def send_config_cmd(self, channel, params):
                
        config_msg = IpcMessage('cmd', 'configure', id=self._next_msg_id())
        config_msg.attrs['params'] = params
        channel.send(config_msg.encode())
        self.await_response(channel)
         
    def set_file_writing(self, enable):

        self.set_file_name(self.file_name)
        self.set_file_path(self.file_path)
        self.set_num_frames(self.frames)

        self.fp_config['hdf']['frames'] = self.frames
        self.fp_config['hdf']['write'] = enable

        # write_config = {}
        # if enable:
        #     write_config['file'] = {
        #                 "path": self.file_path,
        #                 "name": self.file_name,
        #             }
        #     write_config["frames"] =  self.frames

        # write_config["write"] = enable

        config_msg = IpcMessage('cmd', 'configure', id=self._next_msg_id())
        #config_msg.set_param('hdf', write_config)
        config_msg.attrs['params'] = {'hdf': self.fp_config['hdf']}

        print(config_msg)

        self.logger.info('Sending file writing {} command to frame processor'.format(
            'enable' if enable else 'disable'))
        
        self.fp_ctrl_channel.send(config_msg.encode())
        self.await_response(self.fp_ctrl_channel)
        
    def do_status_cmd(self):

        for (name, channel) in self.channels:
            self.logger.info("Sending status request to frame {}".format(name))
            reply = self.send_status_cmd(channel)
            if reply is not None:
                self.logger.info("Got response: {}".format(reply))

    def send_status_cmd(self, channel):
        
        status_msg =  IpcMessage('cmd', 'status', id=self._next_msg_id())
        channel.send(status_msg.encode())
        reply = self.await_response(channel)

        return reply
    
    def do_request_config_cmd(self):
        
        request_msg = IpcMessage('cmd', 'request_configuration', id=self._next_msg_id())
        
        for (name, channel) in self.channels:                        
            self.logger.info("Sending configuration request for frame {}".format(name))
            channel.send(request_msg.encode())
            reply = self.await_response(channel)
            if reply is not None:
                self.logger.info("Got response: {}".format(reply))
            
    def await_response(self, channel, timeout_ms=1000):

        reply = None
        pollevts = channel.poll(1000)
        if pollevts == IpcChannel.POLLIN:
            reply = IpcMessage(from_str=channel.recv())
        
        return reply
               
    def _parse_arguments(self, prog_name=sys.argv[0], args=None):
        
        parser = argparse.ArgumentParser(prog=prog_name, description='ODIN data client')
        parser.add_argument('--frctrl', type=str, default='tcp://127.0.0.1:5000', 
                            dest='fr_ctrl_endpoint',
                            help='Specify the frame recevier IPC control channel endpoint URL')
        parser.add_argument('--fpctrl', type=str, default='tcp://127.0.0.1:5004', 
                            dest='fp_ctrl_endpoint',
                            help='Specify the frame processor IPC control channel endpoint URL')
        parser.add_argument('--default', type=argparse.FileType('r'), dest='config_file', nargs='?',
                            default=None, const=sys.stdin, 
                            help='Specify JSON configuration file to parse for default config')
        parser.add_argument('--config', action='store_true',
                            help='Send a configuration command to the processes')
        parser.add_argument('--frames', type=int, dest='frames',
                            help='Override the number of frames in configuration')
        parser.add_argument('--bitdepth', type=int, dest='bitdepth', 
                            choices=[1, 6, 12, 24],
                            help='Override the bit depth param in configuration')
        
        file_writing = parser.add_mutually_exclusive_group()
        file_writing.add_argument('--start', action='store_true',
                            help='Start frame processor file writing')
        file_writing.add_argument('--stop', action='store_true',
                            help='Stop frame processor file writing')
        
        parser.add_argument('--path', type=str, dest='file_path',
                            help='Set file writing path')
        parser.add_argument('--file', type=str, dest='file_name',
                            help='Set file writing name')
        
        parser.add_argument('--status', action='store_true',
                            help='Request a status report from the processes')
        parser.add_argument('--getconfig', action='store_true',
                            help='Get the current configuration of the processes')
        parser.add_argument('--shutdown', action='store_true',
                            help='Instruct the processes to shut down')
        
        args = parser.parse_args(args)
        return args
class FrameProcessor(object):
    def __init__(self):

        # Initialise the logging module with log messages directed to stdout
        #logging.basicConfig(format='%(asctime)s %(levelname)s FrameProcessor - %(message)s', level=logging.DEBUG)
        #ch = logging.StreamHandler(sys.stdout)
        #logging.addHandler(ch)

        # create logger
        self.logger = logging.getLogger('frame_processor')
        self.logger.setLevel(logging.DEBUG)

        # create console handler and set level to debug
        ch = logging.StreamHandler(sys.stdout)
        ch.setLevel(logging.DEBUG)

        # create formatter
        formatter = logging.Formatter(
            '%(asctime)s %(levelname)s %(name)s - %(message)s')

        # add formatter to ch
        ch.setFormatter(formatter)

        # add ch to logger
        self.logger.addHandler(ch)

        # Instantiate a configuration container object, which will be populated
        # with sensible default values
        self.config = FrameProcessorConfig(
            "FrameProcessor",
            "FrameProcessor - test harness to simulate operation of FrameProcessor application"
        )

        # Create the appropriate IPC channels
        self.ctrl_channel = IpcChannel(IpcChannel.CHANNEL_TYPE_REQ)
        self.ready_channel = IpcChannel(IpcChannel.CHANNEL_TYPE_SUB)
        self.release_channel = IpcChannel(IpcChannel.CHANNEL_TYPE_PUB)

        # Zero frames recevied counter
        self.frames_received = 0

        # Create the thread to handle frame processing
        self.frame_processor = threading.Thread(target=self.process_frames)
        self.frame_processor.daemon = True

        self._run = True

    def map_shared_buffer(self):

        success = False

        # Check if the current configuration object has a shared buffer name defined, otherwise request one from the
        # upstream frameReceiver
        if self.config.sharedbuf is None:
            if not self.request_shared_buffer_config():
                return success

        # Map the shared buffer manager
        try:
            self.shared_buffer_manager = SharedBufferManager(
                self.config.sharedbuf,
                boost_mmap_mode=self.config.boost_mmap_mode)
            success = True
        except SharedBufferManagerException as e:
            self.logger.error("Failed to create shared buffer manager: %s" %
                              str(e))

        return success

    def request_shared_buffer_config(self):

        success = False

        max_request_retries = 10
        max_reply_retries = 10

        request_retries = 0
        config_request = IpcMessage(msg_type='cmd',
                                    msg_val='request_buffer_config')

        while success is False and request_retries < max_request_retries:

            self.logger.debug(
                "Sending buffer config request {}".format(request_retries + 1))
            self.release_channel.send(config_request.encode())
            reply_retries = 0

            while success is False and reply_retries < max_reply_retries:
                if self.ready_channel.poll(100):
                    config_msg = self.ready_channel.recv()
                    config_decoded = IpcMessage(from_str=config_msg)
                    self.logger.debug(
                        'Got buffer configuration response with shared buffer name: {}'
                        .format(
                            config_decoded.get_param('shared_buffer_name')))
                    self.config.sharedbuf = config_decoded.get_param(
                        'shared_buffer_name')
                    success = True
                else:
                    reply_retries += 1

            request_retries += 1

        # temp hack
        if not success:
            self.logger.error("Failed to obtain shared buffer configuration")

        return success

    def run(self):

        self.logger.info("Frame processor starting up")

        # Connect the IPC channels
        self.ctrl_channel.connect(self.config.ctrl_endpoint)
        self.ready_channel.connect(self.config.ready_endpoint)
        self.release_channel.connect(self.config.release_endpoint)

        # Ready channel subscribes to all topics
        self.ready_channel.subscribe(b'')

        # Map the shared buffer manager - quit if this fails
        if not self.map_shared_buffer():
            self._run = False
            return

        self.logger.info(
            "Mapped shared buffer manager ID %d with %d buffers of size %d" %
            (self.shared_buffer_manager.get_manager_id(),
             self.shared_buffer_manager.get_num_buffers(),
             self.shared_buffer_manager.get_buffer_size()))

        if self.config.sensortype == 'percivalemulator':
            self.frame_decoder = PercivalEmulatorFrameDecoder(
                self.shared_buffer_manager)
            self.logger.debug(
                'Loaded frame decoder for PERCIVAL emulator sensor type')
        elif self.config.sensortype == 'excalibur':
            self.frame_decoder = ExcaliburFrameDecoder(
                self.shared_buffer_manager)
            self.logger.debug('Loaded frame decoder for EXCALIBUR sensor type')
        else:
            self.frame_decoder = None
            self.logger.error("Unrecognised sensor type specified: %s" %
                              self.config.sensortype)
            return

        # Launch the frame processing thread
        self.frame_processor.start()

        try:
            while self._run:

                if self.config.frames and self.frames_received >= self.config.frames:
                    self.logger.info(
                        "Specified number of frames (%d) received, terminating"
                        % self.config.frames)
                    self._run = False
                else:
                    msg = IpcMessage(msg_type='cmd', msg_val='status')
                    #self.logger.debug("Sending status command message")
                    self.ctrl_channel.send(msg.encode())

                    reply = self.ctrl_channel.recv()
                    reply_decoded = IpcMessage(from_str=reply)

                    #self.logger.debug("Got reply, msg_type = " + reply_decoded.get_msg_type() + " val = " + reply_decoded.get_msg_val())
                    time.sleep(1)

        except KeyboardInterrupt:

            self.logger.info("Got interrupt, terminating")
            self._run = False

        self.frame_processor.join()
        self.logger.info("Frame processor shutting down")

    def process_frames(self):

        self.frame_header = Struct('<LLQQL')

        while self._run:

            if (self.ready_channel.poll(100)):

                ready_msg = self.ready_channel.recv()
                ready_decoded = IpcMessage(from_str=ready_msg)

                if ready_decoded.get_msg_type(
                ) == 'notify' and ready_decoded.get_msg_val() == 'frame_ready':

                    frame_number = ready_decoded.get_param('frame')
                    buffer_id = ready_decoded.get_param('buffer_id')
                    self.logger.debug(
                        "Got frame ready notification for frame %d buffer ID %d"
                        % (frame_number, buffer_id))

                    if not self.config.bypass_mode:
                        self.handle_frame(frame_number, buffer_id)

                    release_msg = IpcMessage(msg_type='notify',
                                             msg_val='frame_release')
                    release_msg.set_param('frame', frame_number)
                    release_msg.set_param('buffer_id', buffer_id)
                    self.release_channel.send(release_msg.encode())

                    self.frames_received += 1

                elif ready_decoded.get_msg_type(
                ) == 'notify' and ready_decoded.get_msg_val(
                ) == 'buffer_config':

                    shared_buffer_name = ready_decoded.get_param(
                        'shared_buffer_name')
                    self.logger.debug(
                        'Got shared buffer config notification with name %s' %
                        (shared_buffer_name))

                else:

                    self.logger.error(
                        "Got unexpected message on ready notification channel: %s"
                        % (ready_decoded))

        self.logger.info("Frame processing thread interrupted, terminating")

    def handle_frame(self, frame_number, buffer_id):

        self.frame_decoder.decode_header(buffer_id)
        self.logger.debug(
            'Frame {:d} in buffer {:d} decoded header values:\n{:s}'.format(
                frame_number, buffer_id,
                self.frame_decoder.header_state_str()))

        if self.config.packet_state:
            self.logger.debug("Packet state : \n" +
                              self.frame_decoder.packet_state_str())

        self.frame_decoder.decode_data(buffer_id)
        self.logger.debug("Frame start: " + ' '.join(
            "0x{:04x}".format(val)
            for val in self.frame_decoder.data.pixels[:16]))
        self.logger.debug("Frame end  : " + ' '.join(
            "0x{:04x}".format(val)
            for val in self.frame_decoder.data.pixels[-16:]))
class LpdFemOdinDataReceiver():
    ''' This object is used when the data is coming from ODIN (as opposed to internally). This
        object is recycled throughout the lifecycle of the GUI session
    '''

    MESSAGE_ID_MAX = 2**32
    _msg_id = 0

    def __init__(self, run_status_signal, num_frames, app_main, live_view_signal):
        ''' This is executed on the first run of each GUI session - this object is retained
            throughout the lifecycle of the session.
        '''

        try:
            self.run_status_signal = run_status_signal
            self.num_frames = num_frames
            self.app_main = app_main
            self.live_view_signal = live_view_signal
            self.hdf_file_location = None

            print("Launching Frame Receiver and Frame Processor")
            # Getting location of FR & FP and paths of their config files
            fr_fp_location = self.app_main.getCachedParam("frameReceiverProcessorLocation")
            frame_receiver_config_path = self.app_main.getCachedParam("frameReceiverConfigFile")
            frame_processor_config_path = self.app_main.getCachedParam("frameProcessorConfigFile")

            # Using Subprocess to launch the frame receiver and frame processor
            self.frame_receiver = Popen(["./frameReceiver", "--logconfig",
                                         frame_receiver_config_path], cwd=fr_fp_location)
            self.frame_processor = Popen(["./frameProcessor", "--logconfig",
                                         frame_processor_config_path], cwd=fr_fp_location)

            # Create the appropriate IPC channels for receiver and proccessor
            print("Connecting to Frame Receiver's IPC Control Channel")
            self.fr_ctrl_channel = IpcChannel(IpcChannel.CHANNEL_TYPE_DEALER)
            self.fr_ctrl_channel.connect(self.app_main.getCachedParam('odinFrCtrlChannel'))

            print("Connecting to Frame Proccessor's IPC Control Channel")
            self.fp_ctrl_channel = IpcChannel(IpcChannel.CHANNEL_TYPE_DEALER)
            self.fp_ctrl_channel.connect(self.app_main.getCachedParam('odinFpCtrlChannel'))
        except Exception as e:
            print("LdpFemOdinDataReceiver got exception during initialisation: %s" % e)

    def configure(self, num_frames, num_images):
        ''' Executed at the start of every run to do a number of things including creating and
            starting the data monitor & live view threads
        '''
        try:
            self.num_frames = num_frames

            # Load Odin Data config
            print("Loading ODIN Data config from {}".format(self.app_main.getCachedParam('odinDataConfigFile')))
            with open(self.app_main.getCachedParam('odinDataConfigFile')) as configFile:
                self.odin_data_config = json.load(configFile)
                self.config_receiver = self.odin_data_config['receiver_default_config']
                self.config_processor = self.odin_data_config['processor_default_config']
                self.config_processor_plugins = self.odin_data_config['processor_plugins']

            # Set number of expected frames based on the master dataset (dataset containing the
            # data, as defined in odin_data_lpd_config.json)
            self.config_processor['hdf']['frames'] = (self.num_frames * num_images)

            # Set path and filename of output file
            print("Setting Path & Filename of Output File")
            file_path = self.app_main.getCachedParam('dataFilePath')
            self.config_processor['hdf']['file']['path'] = file_path

            # Set file name based on current run number
            run_number = self.app_main.getCachedParam('runNumber')            
            file_name = "lpdData-{:05d}.hdf5".format(run_number)
            self.config_processor['hdf']['file']['name'] = file_name

            # Set offset and divisor in config to be used in process plugin
            self.config_processor['lpd']['divisor'] = self.app_main.getCachedParam('liveViewDivisor')
            self.config_processor['lpd']['offset'] = self.app_main.getCachedParam('liveViewOffset')

            print("Setting debug level for frame receiver and frame processor")
            debug_level = self.app_main.getCachedParam('odinDataDebugLevel')
            self.config_receiver['debug_level'] = debug_level
            self.config_processor['debug_level'] = debug_level

            # Send Frame Receiver config
            print("Sending Frame Receiver Config")
            self.send_config_msg(self.fr_ctrl_channel, self.config_receiver)

            # Send Frame Receiver info to Frame Processor
            print("Sending Receiver connection info to Frame Processor")
            params = {'fr_setup': self.config_processor['fr_setup']}
            self.send_config_msg(self.fp_ctrl_channel, params)

            # Sending Processor plugin configs
            if len(self.config_processor_plugins):
                print("Sending Processor plugin chain config to Frame Processor")
                for plugin in self.config_processor_plugins:
                    self.send_config_msg(self.fp_ctrl_channel, plugin)

            # Send Frame Processor config
            print("Sending Frame Processor Config")
            self.send_config_msg(self.fp_ctrl_channel, self.config_processor)

            print("Waiting for Receiver/Processor handshake to complete")
            # Around 8 seconds before timeout
            timeout_attempts = 40
            request_number = 0

            # Checking status of FR/FP handshake
            while True:
                reply = self.send_status_cmd(self.fp_ctrl_channel)
                if reply is not None:
                    try:
                        shared_memory_buffer_status = reply.attrs['params']['shared_memory']['configured']
                        if shared_memory_buffer_status is True:
                            print("Shared memory is now configured in FP")
                            break
                    except KeyError as e:
                        print("LdpFemOdinDataReceiver got KeyError accessing shared buffer ",
                              "status parameter: %s" % e)

                request_number += 1
                if(request_number > timeout_attempts):
                    raise Exception("Handshake between frame receiver and frame processor has ",
                                    "timed out")
                    break
                time.sleep(0.2)

            print("Resetting statistics in LPD Process Plugin")
            self.reset_stats_cmd(self.fp_ctrl_channel)

            # Create data monitor object and thread then move object into thread
            print("Creating Data Monitor Thread")
            self.data_monitor = OdinDataMonitor(self)
            self.data_monitor_thread = QtCore.QThread()
            self.data_monitor.moveToThread(self.data_monitor_thread)

            # Connect monitor loop start signal to monitor loop function
            print("Starting Data Monitor Thread")
            self.data_monitor_thread.started.connect(self.data_monitor.monitorLoop)
            self.data_monitor_thread.start()

            if self.app_main.getCachedParam("liveViewEnable"):
                # Create live view receiver object and thread, then move object into thread
                print("Creating Live View Receiver Thread")
                self.live_view_receiver = LiveViewReceiver(self, self.live_view_signal, num_images)
                self.live_view_receiver_thread = QtCore.QThread()
                self.live_view_receiver.moveToThread(self.live_view_receiver_thread)

                # Start the thread and connect start signal to receive_data()
                print("Starting Live View Receiver Thread")
                self.live_view_receiver_thread.started.connect(self.live_view_receiver.receive_data)
                self.live_view_receiver_thread.start()

        except Exception as e:
            print("LdpFemOdinDataReceiver got exception during configuration: %s" % e)

    def awaitCompletion(self):

            print("Waiting for frame processing to complete")
            while (self.data_monitor.running) and (self.app_main.abort_run == False):
                time.sleep(0.1)

            if self.app_main.abort_run:
                print("Run aborted by user")
                self.data_monitor.abort_run()
            else:
                print("Frame processor handled all frames, terminating data monitor thread")

            # Set file writing and data now the run has finished
            self.set_file_writing(False)
            if self.app_main.getCachedParam("liveViewEnable"):
                # Subscriber in receive_data() will no longer look for data off the socket
                self.live_view_receiver.set_data_polling(False)

            # Get path of hdf file for the current run
            file_path = self.config_processor['hdf']['file']['path']
            file_name = self.config_processor['hdf']['file']['name']
            # Remove hardcoded file ending when feature disabled
            self.hdf_file_location = file_path + "/" + file_name + "_000001.h5"

            # Open hdf file for metadata to be written to it      
            try:
                self.hdf_file = h5py.File(self.hdf_file_location, 'r+')
            except IOError as e:
                print("Failed to open HDF file with error: %s" % e)
                raise(e)

            # Create metadata group and add datasets to it
            metadata_group = self.hdf_file.create_group('metadata')
            self.metadata_handler = MetadataWriter(self.app_main.cached_params)
            self.metadata_handler.write_metadata(metadata_group)

            # Close hdf file
            self.hdf_file.close()

            self.data_monitor_thread.quit()
            self.data_monitor_thread.wait()

    def await_response(self, channel, timeout_ms=1000):
        ''' Polls the passed channel for any data to be received, used in various functions below
        '''
        reply = None
        pollevts = channel.poll(timeout_ms)
        if pollevts == IpcChannel.POLLIN:
            reply = IpcMessage(from_str=channel.recv())
        return reply

    def _next_msg_id(self):
        ''' Increments message ID for the IPC messages
        '''
        self._msg_id = (self._msg_id + 1) % self.MESSAGE_ID_MAX
        return self._msg_id

    def send_config_msg(self, channel, config):
        ''' Sends a configuration command to the channel that's passed
        '''
        config_msg = IpcMessage('cmd', 'configure', id=self._next_msg_id())
        config_msg.attrs['params'] = config
        channel.send(config_msg.encode())
        reply = self.await_response(channel)

    def send_status_cmd(self, channel):
        ''' Sends a status command to the channel that's passed
        '''
        status_msg = IpcMessage('cmd', 'status', id=self._next_msg_id())
        channel.send(status_msg.encode())
        reply = self.await_response(channel)
        return reply

    def reset_stats_cmd(self, channel):
        ''' Used to reset statistics in LPD Process Plugin
        '''
        reset = IpcMessage('cmd', 'reset_statistics', id=self._next_msg_id())
        channel.send(reset.encode())
        reply = self.await_response(channel)
        return reply

    def do_shutdown_cmd(self, channel):
        ''' Sends a shutdown command to the channel that's passed
        '''
        shutdown_msg = IpcMessage('cmd', 'shutdown', id=self._next_msg_id())
        channel.send(shutdown_msg.encode())
        self.await_response(channel)

    def shutdown_frame_receiver_processor(self):
        ''' Used when GUI is closed to stop FR & FP processes
        '''
        print("Shutting down frame receiver and frame processor")
        self.do_shutdown_cmd(self.fr_ctrl_channel)
        self.do_shutdown_cmd(self.fp_ctrl_channel)

    def set_file_writing(self, enable):
        ''' Enables or disables file writing (typically once a run has finished)
        '''
        self.config_processor['hdf']['frames'] = self.num_frames
        self.config_processor['hdf']['write'] = enable

        config_msg = IpcMessage('cmd', 'configure', id=self._next_msg_id())
        config_msg.attrs['params'] = {'hdf': self.config_processor['hdf']}

        print('Sending file writing {} command to frame processor'.format(
              'enable' if enable else 'disable'))

        self.fp_ctrl_channel.send(config_msg.encode())
        self.await_response(self.fp_ctrl_channel)

    def last_data_file(self):
        '''Returns the location (path) of the last data file written.
        '''
        return self.hdf_file_location
Example #13
0
class IpcClient(object):

    ENDPOINT_TEMPLATE = "tcp://{IP}:{PORT}"

    MESSAGE_ID_MAX = 2**32

    def __init__(self, ip_address, port):
        self.logger = logging.getLogger(self.__class__.__name__)

        self._ip_address = ip_address
        self._port = port

        self.ctrl_endpoint = self.ENDPOINT_TEMPLATE.format(IP=ip_address,
                                                           PORT=port)
        self.logger.debug("Connecting to client at %s", self.ctrl_endpoint)
        self.ctrl_channel = IpcChannel(IpcChannel.CHANNEL_TYPE_DEALER)
        self.ctrl_channel.connect(self.ctrl_endpoint)

        self.message_id = 0

        self._lock = RLock()

    def _send_message(self, msg, timeout):
        msg.set_msg_id(self.message_id)
        self.message_id = (self.message_id + 1) % self.MESSAGE_ID_MAX
        self.logger.debug("Sending control message:\n%s", msg.encode())
        with self._lock:
            self.ctrl_channel.send(msg.encode())
            expected_id = msg.get_msg_id()
            id = None
            while not id == expected_id:
                pollevts = self.ctrl_channel.poll(timeout)

                if pollevts == zmq.POLLIN:
                    reply = IpcMessage(from_str=self.ctrl_channel.recv())
                    id = reply.get_msg_id()
                    if not id == expected_id:
                        self.logger.warn("Dropping reply message with id [" +
                                         str(id) + "] as was expecting [" +
                                         str(expected_id) + "]")
                        continue
                    if reply.is_valid() and reply.get_msg_type(
                    ) == IpcMessage.ACK:
                        self.logger.debug("Request successful: %s", reply)
                        return True, reply.attrs
                    else:
                        self.logger.debug("Request unsuccessful")
                        return False, reply.attrs
                else:
                    self.logger.warning("Received no response")
                    return False, None

    @staticmethod
    def _raise_reply_error(msg, reply):
        if reply is not None:
            raise IpcMessageException("Request\n%s\nunsuccessful."
                                      " Got invalid response: %s" %
                                      (msg, reply))
        else:
            raise IpcMessageException("Request\n%s\nunsuccessful."
                                      " Got no response." % msg)

    def send_request(self, value, timeout=1000):
        msg = IpcMessage("cmd", value)
        success, reply = self._send_message(msg, timeout)
        if success:
            return reply
        else:
            self._raise_reply_error(msg, reply)

    def send_configuration(self,
                           content,
                           target=None,
                           valid_error=None,
                           timeout=1000):
        msg = IpcMessage("cmd", "configure")

        if target is not None:
            msg.set_param(target, content)
        else:
            for parameter, value in content.items():
                msg.set_param(parameter, value)

        success, reply = self._send_message(msg, timeout)
        if not success and None not in [reply, valid_error]:
            if reply["params"]["error"] != valid_error:
                self._raise_reply_error(msg, reply)
            else:
                self.logger.debug("Got valid error for request %s: %s", msg,
                                  reply)
        return success, reply

    def _read_message(self, timeout):
        pollevts = self.ctrl_channel.poll(timeout)
        if pollevts == zmq.POLLIN:
            reply = IpcMessage(from_str=self.ctrl_channel.recv())
            return reply