class Dispatcher(Service): """Handles communication from drone and devices.""" ITEM_FPS = 'FPS' ITEM_MAKESPAN = '~Makespan' ITEM_THROUGHPUT = '~Throughput' def __init__(self, controller=None): Service.__init__(self, 'dispatcher') self.handlers[Message.TYPE_LOGIN] = self.handle_login self.handlers[Message.TYPE_IMAGE] = self.handle_image self.handlers[Message.TYPE_RESULT] = self.handle_result self.server = Server(self, cfg.SERVER_PORT) self.protocols = {} self.nodes = {} if cfg.DASHBOARD: self.dashboard = Dashboard() self.dashboard.start() else: self.dashboard = None measure_period = float(cfg.CONTROLLER_LOOP_TIME)/cfg.MEASURES_PER_LOOP self.monitor = Monitor(self.process_measurements, measure_period) self.monitor.register_item(self.ITEM_FPS, Monitor.ITEM_RATE) self.monitor.register_item(self.ITEM_MAKESPAN, Monitor.ITEM_AVG) self.monitor.register_item(self.ITEM_THROUGHPUT, Monitor.ITEM_RATE) self.controller = controller self.controller.dashboard = self.dashboard self.controller.dispatcher = self if self.dashboard: self.dashboard.controller = controller self.imagebuf = Queue.Queue() # Buffer of encoded images # (time stamp, image data) # Initialize probe to blank image self.probe_image = self.generate_probe_image() self.tokens = Queue.Queue() self.job_id = 0 self.job_image_cache = {} self.sub_loop = 0 return def handle_login(self, protocol, msg): """Handles device logins.""" name = msg.name self.log().info("'%s' login", name) protocol.name = name self.protocols[name] = protocol if not self.controller is None: self.controller.logon(name) if name != cfg.CAMERA_NAME: self.nodes[name] = Node(name) self.tokens.put(name) return def handle_image(self, protocol, msg): """Handles images.""" if self.monitor: self.monitor.update_item(self.ITEM_FPS, 1) if self.dashboard: image = cv2.imdecode(msg.data, cv2.IMREAD_UNCHANGED) self.dashboard.put_image(image) self.imagebuf.put((time.time(), msg.data)) self.probe_image = msg.data return def handle_result(self, protocol, msg): """Handles result.""" try: name = protocol.name except AttributeError: self.log().warning("result from protocol with no name") thread.interrupt_main() return job = msg.data job.end = time.time() if job.probe: prefix = 'probe ' else: prefix = '' # Update system stats makespan = job.end - job.start proc_time = job.left - job.arrived rtt = makespan - proc_time now = job.end suffix = '' # Ignore probe jobs if not job.probe: self.monitor.update_item(self.ITEM_MAKESPAN, makespan) if job.deadline >= now: self.monitor.update_item(self.ITEM_THROUGHPUT, 1) else: suffix = 'late' # Display result if image is cached if self.dashboard: if name in self.job_image_cache: cache = self.job_image_cache[name] if cache[0] == job.job_id: image = cv2.imdecode(cache[1], cv2.IMREAD_UNCHANGED) self.dashboard.put_result((image, job.data)) self.log().info("%sjob %d completed by '%s' %s", prefix, job.job_id, name, suffix) self.log().debug(job) self.log().debug('%sjob %d makespan: %0.6f', prefix, job.job_id, makespan) self.log().debug('%sjob %d proc_time: %0.6f', prefix, job.job_id, proc_time) self.log().debug('%sjob %d rtt: %0.6f', prefix, job.job_id, rtt) proc_rate = cfg.PIPELINES[job.pipeline][1][name[:3]]/proc_time # Update device stats node = self.nodes[name] with node.lock: node.processing_rate.add(proc_rate) node.rtt.add(rtt) self.log().debug('node updated %s', node) self.tokens.put(name) return def process_measurements(self, values): """Handles measurements from a Monitor.""" self.log().debug('measurements: %s', values) self.sub_loop += 1 self.sub_loop %= cfg.MEASURES_PER_LOOP if self.controller: self.controller.put_metrics(values) if self.sub_loop == 0: self.controller.loop() cvalues = self.controller.get_values() values.update(cvalues) values['nodes'] = self.nodes values['imagebuf'] = self.imagebuf.qsize() if self.dashboard: self.dashboard.put_values(values) return def start(self): """Starts the dispatcher server.""" self.log().info('starting dispatcher') if self.monitor: self.monitor.start() if self.controller: self.controller.start() self.server.run() # Blocking call return def stop(self): """Stops the dispatcher server.""" self.log().info('stopping dispatcher') if self.monitor: self.monitor.stop() if self.dashboard: self.dashboard.stop() if self.controller: self.controller.stop() self.server.stop() return def disconnected(self, protocol): """Handles device disconnections.""" try: name = protocol.name except AttributeError: self.log().warning("disconnected protocol has no name") return self.protocols.pop(name, None) self.log().info("'%s' disconnected", name) if not self.controller is None: self.controller.logoff(name) if name != cfg.CAMERA_NAME: del self.nodes[name] self.job_image_cache.pop(name, None) return def send_params(self, name, params): """Sends settings to client.""" protocol = self.protocols.get(name) if not protocol is None: msg = Message(self.name, Message.TYPE_PARAMS, params) protocol.send(msg) else: self.log().warn("send_params: protocol '%s' not found", name) return def next_job_id(self): """Returns next job id.""" job_id = self.job_id self.job_id += 1 return job_id def generate_probe_image(self): """Generate random blank image for probing.""" # Note: Due to the randomness, this will likely be larger in (encoded) # size than an image from the drone blank = np.random.randint(0, 256, (480, 640, 3), np.uint8) params = [cv2.IMWRITE_JPEG_QUALITY, 50] ret, image_data = cv2.imencode('.jpg', blank, params) if ret: return image_data else: thread.interrupt_main() def probe(self, name, pipeline): """Sends fake job to a device to probe.""" protocol = self.protocols.get(name) if protocol: job = Job(self.next_job_id(), pipeline, self.probe_image, True) msg = Message(self.name, Message.TYPE_JOB, job) self.log().info("sending job %d to '%s'", job.job_id, name) self.log().debug('%s', job) protocol.send(msg) else: self.log().warn("probe: protocol '%s' not found", name) return def send_job(self, name, pipeline, image, timestamp, deadline): """Sends jobs to devices.""" protocol = self.protocols.get(name) if protocol: job = Job(self.next_job_id(), pipeline, image) job.start = timestamp job.deadline = deadline msg = Message(self.name, Message.TYPE_JOB, job) self.log().info("sending job %d to '%s'", job.job_id, name) self.log().debug('%s', job) protocol.send(msg) # Cache images for results display if cfg.SHOW_RESULTS: self.job_image_cache[name] = (job.job_id, job.data) else: self.log().warn("send_job: protocol '%s' not found", name) return