예제 #1
0
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        PyQt4.uic.loadUi('forms/main_window.ui', self)

        # Scene working rectangle.
        self.scene_rect = QRectF(
            -config.scene_width / 2, -config.scene_height / 2,
            config.scene_width, config.scene_height)

        self.scene = QGraphicsScene()
        self.graphicsView = GraphicsView()
        self.setCentralWidget(self.graphicsView)
        self.graphicsView.setScene(self.scene)
        self.scene.setSceneRect(self.scene_rect)

        # Disable spatial indexing since all objects will be moving.
        self.scene.setItemIndexMethod(QGraphicsScene.NoIndex)

        self.scene_rect_item = self.scene.addRect(self.scene.sceneRect())

        self.name_it = itertools.count(0)
        self.routers = []
        self.visible_routers = 0

        # Symmetrical matrix of links between routers.
        self.links = [
            [None for c in xrange(config.max_routers_num)]
                for r in xrange(config.max_routers_num)]
        # List of all links.
        self.links_list = []

        # Routers
        self._dt = 1 / 20.0
        self._refresh_timer_id = self.startTimer(int(1000 * self._dt))

        link_update_rate = 5 # times per second
        self._update_link_timer_id = \
            self.startTimer(int(1000 / link_update_rate))

        transmitting_image_refresh_rate = 5 # times per second
        self._update_transmitting_image_timer_id = \
            self.startTimer(int(1000 / transmitting_image_refresh_rate))

        statistics_refresh_rate = 1 # times per second
        self._statistics_timer_id = \
            self.startTimer(int(1000 / statistics_refresh_rate))

        self.generate_routers()

        # Statistics widget.
        self.statistics = StatisticsWidget(self)
        # TODO: Better if it will be statically floatable.
        self.addDockWidget(Qt.RightDockWidgetArea, self.statistics)

        # Main panel.
        # TODO: Rename to `control_panel'.
        self.panel = MainDockableWidget(self)
        self.addDockWidget(Qt.RightDockWidgetArea, self.panel)
        self.panel.nRoutersSlider.valueChanged.connect(
            self.on_routers_num_changed)
        self.panel.shakeRoutersButton.clicked.connect(
            self.shake_routers)
        self.panel.stopRoutersButton.clicked.connect(
            self.stop_routers)
        self.panel.renderNodesRangeCheckBox.stateChanged.connect(
            self.on_display_router_connection_range_changed)
        self.panel.connectionDistSlider.valueChanged.connect(
            self.on_display_router_connection_range_changed)
        self.panel.disconnectionDistSlider.valueChanged.connect(
            self.on_display_router_connection_range_changed)

        # Transmission widget.
        self.transmission = TransmissionWidget()
        self.addDockWidget(Qt.RightDockWidgetArea, self.transmission)
        self.transmission.restartTransmissionButton.clicked.connect(
            self.on_restart_transmission)
        self.transmission.load_image("images/forest.jpg")
        self.transmission.openImageButton.clicked.connect(self.on_new_image)

        self._transmitted_parts = 0
        self._topology_change_times = collections.deque(
            maxlen=config.num_of_topology_changes_in_stat)
        self._invalid_routes_stat = collections.deque(
            maxlen=config.num_of_invalid_routes_in_stat)
        self._not_optimal_routes_stat = collections.deque(
            maxlen=config.num_of_not_optimal_routes_in_stat)

        # TODO: Tabs order.
        self.tabifyDockWidget(self.transmission, self.panel)

        # If working thread will be able to acquire the lock, then it should
        # terminate himself.
        self._exit_lock = threading.RLock()
        self._exit_lock.acquire()

        self._working_thread = threading.Thread(target=self._work)
        self._working_thread.start()
예제 #2
0
class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        PyQt4.uic.loadUi('forms/main_window.ui', self)

        # Scene working rectangle.
        self.scene_rect = QRectF(
            -config.scene_width / 2, -config.scene_height / 2,
            config.scene_width, config.scene_height)

        self.scene = QGraphicsScene()
        self.graphicsView = GraphicsView()
        self.setCentralWidget(self.graphicsView)
        self.graphicsView.setScene(self.scene)
        self.scene.setSceneRect(self.scene_rect)

        # Disable spatial indexing since all objects will be moving.
        self.scene.setItemIndexMethod(QGraphicsScene.NoIndex)

        self.scene_rect_item = self.scene.addRect(self.scene.sceneRect())

        self.name_it = itertools.count(0)
        self.routers = []
        self.visible_routers = 0

        # Symmetrical matrix of links between routers.
        self.links = [
            [None for c in xrange(config.max_routers_num)]
                for r in xrange(config.max_routers_num)]
        # List of all links.
        self.links_list = []

        # Routers
        self._dt = 1 / 20.0
        self._refresh_timer_id = self.startTimer(int(1000 * self._dt))

        link_update_rate = 5 # times per second
        self._update_link_timer_id = \
            self.startTimer(int(1000 / link_update_rate))

        transmitting_image_refresh_rate = 5 # times per second
        self._update_transmitting_image_timer_id = \
            self.startTimer(int(1000 / transmitting_image_refresh_rate))

        statistics_refresh_rate = 1 # times per second
        self._statistics_timer_id = \
            self.startTimer(int(1000 / statistics_refresh_rate))

        self.generate_routers()

        # Statistics widget.
        self.statistics = StatisticsWidget(self)
        # TODO: Better if it will be statically floatable.
        self.addDockWidget(Qt.RightDockWidgetArea, self.statistics)

        # Main panel.
        # TODO: Rename to `control_panel'.
        self.panel = MainDockableWidget(self)
        self.addDockWidget(Qt.RightDockWidgetArea, self.panel)
        self.panel.nRoutersSlider.valueChanged.connect(
            self.on_routers_num_changed)
        self.panel.shakeRoutersButton.clicked.connect(
            self.shake_routers)
        self.panel.stopRoutersButton.clicked.connect(
            self.stop_routers)
        self.panel.renderNodesRangeCheckBox.stateChanged.connect(
            self.on_display_router_connection_range_changed)
        self.panel.connectionDistSlider.valueChanged.connect(
            self.on_display_router_connection_range_changed)
        self.panel.disconnectionDistSlider.valueChanged.connect(
            self.on_display_router_connection_range_changed)

        # Transmission widget.
        self.transmission = TransmissionWidget()
        self.addDockWidget(Qt.RightDockWidgetArea, self.transmission)
        self.transmission.restartTransmissionButton.clicked.connect(
            self.on_restart_transmission)
        self.transmission.load_image("images/forest.jpg")
        self.transmission.openImageButton.clicked.connect(self.on_new_image)

        self._transmitted_parts = 0
        self._topology_change_times = collections.deque(
            maxlen=config.num_of_topology_changes_in_stat)
        self._invalid_routes_stat = collections.deque(
            maxlen=config.num_of_invalid_routes_in_stat)
        self._not_optimal_routes_stat = collections.deque(
            maxlen=config.num_of_not_optimal_routes_in_stat)

        # TODO: Tabs order.
        self.tabifyDockWidget(self.transmission, self.panel)

        # If working thread will be able to acquire the lock, then it should
        # terminate himself.
        self._exit_lock = threading.RLock()
        self._exit_lock.acquire()

        self._working_thread = threading.Thread(target=self._work)
        self._working_thread.start()

    def __del__(self):
        #super(MainWindow, self).__del__()
        print self, "__del__" # DEBUG

        for l in self.links_list:
#            l.enabled = False
            assert l is not None
            l.terminate()

        for r in self.routers:
            # TODO
            r._stop_networking()

        #import sliding_window
        #print sliding_window.worker._frame_transmitters

    def closeEvent(self, event):
        # TODO: It is possible to hit close button twice. Must do
        # deinitialization on destroy event.

        # Release exit lock and wait until working thread will not terminate.
        self._exit_lock.release()
        self._working_thread.join()
        super(MainWindow, self).closeEvent(event)

    def timerEvent(self, event):
        if event.timerId() == self._refresh_timer_id:
            for router in self.routers[:self.visible_routers]:
                router.advance(self._dt)

        elif event.timerId() == self._update_link_timer_id:
            self._first_update_link_timer_id = \
                not hasattr(self, "_first_update_link_timer_id")

            # Disconnect routers that are far away each other.
            for r1_idx in xrange(self.visible_routers):
                for r2_idx in xrange(r1_idx + 1, self.visible_routers):
                    r1 = self.routers[r1_idx]
                    r2 = self.routers[r2_idx]
                    link = self.links[r1_idx][r2_idx]
                    if link.enabled and \
                            r1.distance(r2) >= config.disconnection_distance:
                        link.enabled = False

                        # TODO: Misses case when number of routers changed
                        # dynamically.
                        if not self._first_update_link_timer_id:
                            self._topology_change_times.append(time.time())

            # Connect close routers.
            for r1_idx in xrange(self.visible_routers):
                for r2_idx in xrange(r1_idx + 1, self.visible_routers):
                    r1 = self.routers[r1_idx]
                    r2 = self.routers[r2_idx]
                    link = self.links[r1_idx][r2_idx]
                    
                    if not link.enabled and \
                            r1.distance(r2) <= config.connection_distance:
                        link.enabled = True

                        if not self._first_update_link_timer_id:
                            self._topology_change_times.append(time.time())

            self._first_update_link_timer_id = False

        elif event.timerId() == self._update_transmitting_image_timer_id:
            self._update_transmitting_image()

        elif event.timerId() == self._statistics_timer_id:
            self._update_statistics()

    def _update_statistics(self):
        # Update topology changes frequency.
        if self._topology_change_times:
            average_time = (time.time() - self._topology_change_times[0]) / \
                len(self._topology_change_times)

            self.statistics.timeBetweenChangesLabel.setText(
                unicode(self.tr("{0:.2f} s")).format(1.0 / average_time))

        # Obtain network topology.
        g = networkx.Graph()
        g.add_nodes_from(xrange(self.visible_routers))
        for r_name, r in enumerate(self.routers[:self.visible_routers]):
            for adj_r_name in r.link_manager.connected_routers():
                g.add_edge(r_name, adj_r_name)

        # DEBUG
        if False:
            import matplotlib.pyplot as plt
            plt.clf()
            networkx.draw(g)
            plt.savefig("network.png")

        valid_routes = 0       # valid and optimal
        invalid_routes = 0
        not_optimal_routes = 0
        for color in xrange(self.visible_routers):
            # Build RIP network topology for paths to router `color'.
            r_g = networkx.DiGraph()
            r_g.add_nodes_from(xrange(self.visible_routers))
            for r_name, r in enumerate(self.routers[:self.visible_routers]):
                r_next = \
                    r.rip_service.dynamic_routing_table().next_router(color)

                if r_next is not None and r_next != r_name:
                    r_g.add_edge(r_next, r_name)

            # DEBUG
            if False:
                import matplotlib.pyplot as plt
                plt.clf()
                networkx.draw(r_g)
                plt.savefig("rip_{0}.png".format(color))

            cur_valid_routes = 0
            cur_invalid_routes = 0
            cur_not_optimal_routes = 0
            #counted = set() # count by routers from which RIP shows direction.

            # Detect not updated disconnected links.
            invalid_edges = set(r_g.edges()) - set(g.edges()) - \
                    set([(e[1], e[0]) for e in g.edges()])
            #print "invalid_edges:", invalid_edges
            for e in invalid_edges:
                #cur_invalid_routes += 1
                #assert e[1] not in counted
                #counted.add(e[1])
                r_g.remove_edge(*e)

            paths = single_source_dijkstra_path_length(g, color)
            rip_paths = single_source_dijkstra_path_length(r_g, color)

            accessible_routers = set(paths.keys())

            # DEBUG
            #print accessible_routers
            #print paths
            #print rip_paths

            # Not accessible by RIP but accessible by Dijkstra --- router has
            # invalid route.
            #print "cur_invalid_routes 0: ", cur_invalid_routes
            cur_invalid_routes += \
                len(accessible_routers - set(rip_paths.keys()))
            #print "cur_invalid_routes 1: ", cur_invalid_routes

            # Compare path lengths in accessible routes, if RIP path is longer
            # then route is not optimal.
            for r_name, length in rip_paths.iteritems():
                assert r_name in paths
                if length > paths[r_name]:
                    cur_not_optimal_routes += 1
                else:
                    cur_valid_routes += 1

            # If not accessible router has link --- invalid route.
            for r_name in set(g.nodes()) - accessible_routers:
                #print "r_g[r_name] =",r_g[r_name] # DEBUG
                if len(r_g[r_name]) > 0:
                    cur_invalid_routes += 1
                else:
                    cur_valid_routes += 1

            # DEBUG
            #print cur_valid_routes, cur_not_optimal_routes, cur_invalid_routes
            assert cur_valid_routes + cur_not_optimal_routes + \
                cur_invalid_routes == self.visible_routers

            valid_routes += cur_valid_routes
            not_optimal_routes += cur_not_optimal_routes
            invalid_routes += cur_invalid_routes

        assert valid_routes + not_optimal_routes + invalid_routes == \
                self.visible_routers**2

        self._invalid_routes_stat.append(invalid_routes)
        self._not_optimal_routes_stat.append(not_optimal_routes)

        self.statistics.incorrectRoutesRatioLabel.setText(
                str(self.tr("{0:.2f} %")).format(
                    100.0 * invalid_routes / self.visible_routers**2))
        self.statistics.notoptimalRoutesRatioLabel.setText(
                str(self.tr("{0:.2f} %")).format(
                    100.0 * not_optimal_routes / self.visible_routers**2))

        if self._invalid_routes_stat:
            avg = sum(self._invalid_routes_stat) / \
                len(self._invalid_routes_stat)

            self.statistics.avgIncorrectRoutesRatioLabel.setText(
                str(self.tr("{0:.2f} %")).format(
                    100.0 * avg / self.visible_routers**2))

        if self._not_optimal_routes_stat:
            avg = sum(self._not_optimal_routes_stat) / \
                len(self._not_optimal_routes_stat)

            self.statistics.avgNotoptimalRoutesRatioLabel.setText(
                str(self.tr("{0:.2f} %")).format(
                    100.0 * avg / self.visible_routers**2))

    def _update_transmitting_image(self):
        new_positions = []
        while True:
            data = self.receive_image_router.receive()

            if data is None:
                break

            #print "Receive!!" # DEBUG
            #print data # DEBUG

            new_positions.append(data)
            self._transmitted_parts += 1

        if new_positions:
            w = self.transmission.transmitted_pixmap.width()
            h = self.transmission.transmitted_pixmap.height()
            dw = w / config.image_cut_columns
            dh = h / config.image_cut_rows

            painter = QPainter(self.transmission.transmitted_pixmap)
            for x, y in new_positions:
                painter.drawImage(x * dw, y * dh,
                    self.transmission.source_pixmap.toImage(),
                    x * dw, y * dh, dw, dh)
            painter.end()
            del painter

            self.transmission.transmitted_image_item.setPixmap(
                self.transmission.transmitted_pixmap)

        self.transmission.successRatioLabel.setText("{0:2.1f} %".format(
            100.0 * self._transmitted_parts /
                (config.image_cut_columns * config.image_cut_rows)))

    def generate_routers(self):
        def send_wrapper(name, **kwargs):
            return SendImageRouterItem(name, 1, **kwargs)

        self._generate_router(router_class=send_wrapper)
        self._generate_router(router_class=ReceiveImageRouterItem)
        self.send_image_router = self.routers[0]
        self.receive_image_router = self.routers[1]

        for i in xrange(2, config.max_routers_num):
            self._generate_router()

    def _generate_router(self, pos=None, router_class=RouterItem):
        name = self.name_it.next()

        if pos is None:
            scene_rect = self.scene.sceneRect()
            router_pos = QPointF(
                random.uniform(
                    scene_rect.left(), scene_rect.left() + scene_rect.width()),
                random.uniform(
                    scene_rect.top(),  scene_rect.top()  + scene_rect.height()))
        else:
            router_pos = pos

        router = router_class(name, enabled=False)
        self.scene.addItem(router)
        router.setPos(router_pos)

        r_idx = len(self.routers)
        for r2_idx in xrange(len(self.routers)):
            r2 = self.routers[r2_idx]

            link = LinkItem(router, r2, enabled=False)
            self.scene.addItem(link)
            self.links_list.append(link)
            self.links[r_idx][r2_idx] = link
            self.links[r2_idx][r_idx] = link

        self.routers.append(router)

        return name

    def add_router(self):
        if self.visible_routers < config.max_routers_num:
            r = self.routers[self.visible_routers]
            r.enabled = True
            #for r2_idx in xrange(self.visible_routers):
            #    self.links[self.visible_routers][r2_idx].enabled = True
            self.visible_routers += 1

    def remove_router(self):
        if self.visible_routers > 2: # 2 --- number of predefined routers
            r = self.routers[self.visible_routers - 1]
            r.enabled = False
            for r2_idx in xrange(self.visible_routers - 1):
                self.links[self.visible_routers - 1][r2_idx].enabled = False
            self.visible_routers -= 1

    @pyqtSlot()
    def stop_routers(self):
        for router in self.routers:
            router.velocity = QPointF()

    @pyqtSlot()
    def shake_routers(self):
        for router in self.routers:
            router.velocity = random_velocity()

    @pyqtSlot(int)
    def on_routers_num_changed(self, nRouters):
        while nRouters > self.visible_routers:
            self.add_router()
        while nRouters < self.visible_routers:
            self.remove_router()

    @pyqtSlot()
    def on_restart_transmission(self):
        self._transmitted_parts = 0

        self.transmission.reload_image()
        
        data = [(x, y) for y in xrange(config.image_cut_rows)
                            for x in xrange(config.image_cut_columns)]
        # TODO: Race condition.
        session_key = self.send_image_router.send_data(data)
        self.receive_image_router.set_active_session(session_key)

    @pyqtSlot()
    def on_new_image(self):
        self._transmitted_parts = 0

        # TODO
        self.receive_image_router.set_active_session(-1)
        self.send_image_router.stop_data_transfer()

    @pyqtSlot(int)
    def on_display_router_connection_range_changed(self, value):
        self._update_routers()

    def _update_routers(self):
        for router in self.routers:
            router.update()

    def _work(self):
        # TODO: move to __init__()
        logger = logging.getLogger("{0}._work".format(self))

        logger.info("Working thread started")

        while True:
            if self._exit_lock.acquire(False):
                # Obtained exit lock. Terminate.

                self._exit_lock.release()
                logger.info("Exit working thread")
                return

            time.sleep(config.thread_sleep_time)