class RPCServer(object):
    def __init__(self,port,host=''):
        self.handler = RPCHandler(self)
        self.port = port
        self.host = host
        self.tcpserver = None
        self.serviceMap = {}

    def registerService(self, service):
        '''Register an RPC service.'''
        self.serviceMap[service.GetDescriptor().full_name] = service

    def run(self):
        log.info('Running server on port %d' % self.port)
        rpcprotocol = RPCProtocol()
        self.tcpserver = TCPServer(self.handler,rpcprotocol)
        self.tcpserver.bind(self.port, self.host)
        self.tcpserver.start(0)
        ioloop.IOLoop.instance().start()

    def stop(self):
        self.tcpserver.stop()
        ioloop.IOLoop.instance().stop()
Ejemplo n.º 2
0
class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        self.showMaximized()
        self.setWindowTitle("EVGP Race Control System")

        self.is_server_started = False

        teams_list_file_path = "racers_list.yaml"
        if not os.path.exists(teams_list_file_path):
            logging.warning("No racers_list.yaml found!")
            msgBox = QMessageBox()
            msgBox.setIcon(QMessageBox.Warning)
            msgBox.setText(
                "No racers_list.yaml found.\nPress Open and use the file explorer to select the racer list YAML file"
            )
            msgBox.setWindowTitle("No racers_list.yaml found!")
            msgBox.setStandardButtons(QMessageBox.Open)
            returnValue = msgBox.exec()
            if returnValue == QMessageBox.Open:
                teams_list_file_path = QFileDialog.getOpenFileName(
                    self, 'Open Racer List File', os.getcwd(),
                    "Yaml files (*.yaml)")[0]
        self.model = RCSModel(teams_list_file_path)

        layout = QGridLayout()
        layout.setColumnStretch(0, 10)
        layout.setColumnStretch(1, 10)

        #FILTERED ACTIVE RACE TABLE
        self.activeRaceProxyModel = RCSSortFilterProxyModel(True)
        self.activeRaceProxyModel.setDynamicSortFilter(True)
        self.activeRaceProxyModel.setSourceModel(self.model)

        self.activeRaceTable = QtWidgets.QTableView()
        self.activeRaceTable.setModel(self.activeRaceProxyModel)
        self.activeRaceTable.setSizeAdjustPolicy(
            QtWidgets.QAbstractScrollArea.AdjustToContents)
        self.activeRaceTable.horizontalHeader().setStretchLastSection(True)
        self.activeRaceTable.horizontalHeader().setSectionResizeMode(
            QtWidgets.QHeaderView.ResizeToContents)
        self.activeRaceTable.setSelectionBehavior(
            QtWidgets.QTableView.SelectRows)
        self.activeRaceTable.setSelectionMode(
            QtWidgets.QTableView.SingleSelection)
        layout.addWidget(self.activeRaceTable, 1, 0)

        self.standbyRaceProxyModel = RCSSortFilterProxyModel(False)
        self.standbyRaceProxyModel.setDynamicSortFilter(True)
        self.standbyRaceProxyModel.setSourceModel(self.model)

        self.standbyRaceTable = QtWidgets.QTableView()
        self.standbyRaceTable.setModel(self.standbyRaceProxyModel)
        self.standbyRaceTable.setSizeAdjustPolicy(
            QtWidgets.QAbstractScrollArea.AdjustToContents)
        self.standbyRaceTable.horizontalHeader().setSectionResizeMode(
            QtWidgets.QHeaderView.ResizeToContents)
        self.standbyRaceTable.horizontalHeader().setStretchLastSection(True)
        self.standbyRaceTable.setSelectionBehavior(
            QtWidgets.QTableView.SelectRows)
        self.standbyRaceTable.setSelectionMode(
            QtWidgets.QTableView.SingleSelection)
        layout.addWidget(self.standbyRaceTable, 1, 1)

        self.activeRaceTableLabel = QLabel("Active Race Table")
        self.activeRaceTableLabel.setAlignment(Qt.AlignCenter)
        self.standbyRaceTableLabel = QLabel("Other Teams Table")
        self.standbyRaceTableLabel.setAlignment(Qt.AlignCenter)
        layout.addWidget(self.activeRaceTableLabel, 0, 0)
        layout.addWidget(self.standbyRaceTableLabel, 0, 1)

        self.standbyRaceTable.setSortingEnabled(True)
        self.standbyRaceTable.sortByColumn(1, Qt.AscendingOrder)
        self.activeRaceTable.setSortingEnabled(True)
        self.activeRaceTable.sortByColumn(1, Qt.AscendingOrder)

        self.selectedIndex = None
        self.standbyRaceTable.selectionModel().selectionChanged.connect(
            self.standby_race_table_selection_handler)
        self.activeRaceTable.selectionModel().selectionChanged.connect(
            self.active_race_table_selection_handler)

        self.horizontalGroupBox = QGroupBox("")
        self.horizontalGroupBox.setLayout(layout)

        # All relevant buttons in sidebar
        self.button_sidebar_vBox = QVBoxLayout()
        layout.addLayout(self.button_sidebar_vBox, 1, 3)
        self.button_sidebar_vBox.setAlignment(Qt.AlignTop)

        # Move Racers Buttons
        self.button_container_stylesheet = "QWidget#ButtonContainer{background-color: rgb(200, 200, 200);\n border-radius: 5;\n}"
        move_racers_button_container = QWidget()
        move_racers_button_container.setObjectName("ButtonContainer")
        move_racers_button_container.setStyleSheet(
            self.button_container_stylesheet)
        move_racers_button_container.setSizePolicy(QSizePolicy.Preferred,
                                                   QSizePolicy.Maximum)
        move_racers_layout = QVBoxLayout(move_racers_button_container)
        move_racers_layout.addWidget(QLabel("ADMIN CONTROLS"))
        move_to_active_race_button = QPushButton("Move to Active Race")
        move_to_active_race_button.clicked.connect(self.move_to_active_race)
        move_racers_layout.addWidget(move_to_active_race_button)
        remove_from_active_race_button = QPushButton("Remove from Active Race")
        remove_from_active_race_button.clicked.connect(
            self.remove_from_active_race)
        move_racers_layout.addWidget(remove_from_active_race_button)

        self.button_sidebar_vBox.addWidget(move_racers_button_container)

        # Team State Control Buttons
        team_state_button_container = QWidget()
        team_state_button_container.setObjectName("ButtonContainer")
        team_state_button_container.setStyleSheet(
            self.button_container_stylesheet)
        team_state_button_container.setSizePolicy(QSizePolicy.Preferred,
                                                  QSizePolicy.Maximum)
        team_state_btns = self.create_team_state_buttons()
        team_state_layout = QVBoxLayout(team_state_button_container)
        team_state_layout.addWidget(QLabel("TEAM CONTROLS"))
        for btn in team_state_btns:
            team_state_layout.addWidget(btn)
        self.button_sidebar_vBox.addWidget(team_state_button_container)

        # Race State Control Buttons
        race_state_button_container = QWidget()
        race_state_button_container.setObjectName("ButtonContainer")
        race_state_button_container.setStyleSheet(
            self.button_container_stylesheet)
        race_state_button_container.setSizePolicy(QSizePolicy.Preferred,
                                                  QSizePolicy.Maximum)
        race_state_btns = self.create_race_state_buttons()
        race_state_layout = QVBoxLayout(race_state_button_container)
        race_state_layout.addWidget(QLabel("RACE CONTROLS"))
        for btn in race_state_btns:
            race_state_layout.addWidget(btn)
        self.button_sidebar_vBox.addWidget(race_state_button_container)

        self.info_group_box = QGroupBox("Race Status Information")
        self.race_state_label = QLabel("Race State: IN_GARAGE")
        self.info_label = QLabel("Race Status: No Race running.")
        info_layout = QVBoxLayout()
        info_layout.addWidget(self.race_state_label)
        info_layout.addWidget(self.info_label)
        self.info_group_box.setLayout(info_layout)
        layout.addWidget(self.info_group_box, 3, 0)

        self.buttonController = ButtonStateController(
            race_state_btns[0], race_state_btns[1], race_state_btns[2],
            race_state_btns[3], race_state_btns[4], team_state_btns[0],
            move_to_active_race_button, remove_from_active_race_button,
            self.race_state_label, self.info_label)
        self.model.race_state_change_signal.connect(
            self.buttonController.race_state_updated)

        verticalSpacer = QtWidgets.QSpacerItem(20, 40, QSizePolicy.Minimum,
                                               QSizePolicy.Expanding)
        layout.addItem(verticalSpacer, 4, 0, rowSpan=1, columnSpan=3)

        self.create_menu_bar()

        # wait for start of server
        self.server_wait_label = QLabel(
            "Waiting for TCP Server to start. Please hold on.")
        self.server_wait_label.setAlignment(Qt.AlignCenter)
        self.setCentralWidget(self.server_wait_label)
        self.start_server()

    # Make sure we stop the server on window close
    def closeEvent(self, event):
        self.stop_server()
        event.accept()

    def create_menu_bar(self):
        menuBar = QtWidgets.QMenuBar(self)
        self.setMenuBar(menuBar)
        helpMenu = menuBar.addMenu("&Help")
        self.reset_gui_buttons_actions = QtWidgets.QAction("Reset GUI buttons")
        self.reset_gui_buttons_actions.triggered.connect(
            self.buttonController.enable_all_buttons)
        helpMenu.addAction(self.reset_gui_buttons_actions)
        self.about_action = QtWidgets.QAction("About")
        self.about_action.triggered.connect(self.show_about_message)
        helpMenu.addAction(self.about_action)

    def show_about_message(self):
        text = "The Electric Vehicle GrandPrix Autonomous Race Control System " \
        "is brought to you by the RoboJackets at Georgia Tech.<br>" \
        "Contribute to the RCS at <a href='https://github.com/RoboJackets/evgp-rcs'>https://github.com/RoboJackets/evgp-rcs</a>"

        QMessageBox.question(self, 'About the EVGP Race Control System', text,
                             QMessageBox.Ok)

    def create_race_state_buttons(self):
        grid_active_race_button = QPushButton("GRID ACTIVE RACE")
        grid_active_race_button.clicked.connect(
            lambda: self.race_state_change_callback(RaceState.GRID_ACTIVE))
        start_race_button = QPushButton("START RACE")
        start_race_button.setEnabled(False)
        start_race_button.clicked.connect(
            lambda: self.race_state_change_callback(RaceState.GREEN_GREEN))
        red_flag_race_button = QPushButton("RED FLAG RACE")
        red_flag_race_button.clicked.connect(
            lambda: self.race_state_change_callback(RaceState.RED_FLAG))
        e_stop_race_button = QPushButton("E-STOP RACE")
        e_stop_race_button.clicked.connect(
            lambda: self.race_state_change_callback(RaceState.RED_RED))
        finish_race_button = QPushButton("FINISH RACE")
        finish_race_button.clicked.connect(
            lambda: self.race_state_change_callback(RaceState.IN_GARAGE))

        return [
            grid_active_race_button, start_race_button, red_flag_race_button,
            e_stop_race_button, finish_race_button
        ]

    def create_team_state_buttons(self):
        in_garage_team_button = QPushButton("IN GARAGE TEAM")
        in_garage_team_button.clicked.connect(
            lambda: self.team_state_change_callback(RaceState.IN_GARAGE))
        red_flag_team_button = QPushButton("RED FLAG TEAM")
        red_flag_team_button.clicked.connect(
            lambda: self.team_state_change_callback(RaceState.RED_FLAG))
        e_stop_team_button = QPushButton("E-STOP TEAM")
        e_stop_team_button.clicked.connect(
            lambda: self.team_state_change_callback(RaceState.RED_RED))

        return [
            in_garage_team_button, red_flag_team_button, e_stop_team_button
        ]

    def team_state_change_callback(self, state):
        if self.selectedIndex is not None:
            self.model.team_state_change(self.selectedIndex, state)

    def race_state_change_callback(self, state):
        self.model.race_state_change(state)

    def move_to_active_race(self):
        if self.selectedIndex is not None:
            changed = self.model.move_to_active_race(self.selectedIndex)
            if changed:
                self.clearAllSelections()

    def remove_from_active_race(self):
        if self.selectedIndex is not None:
            changed = self.model.move_to_standby_race(self.selectedIndex)
            if changed:
                self.clearAllSelections()

    def start_server(self):
        if not self.is_server_started:
            self.is_server_started = True
            port = 12017
            server_backlog = 10
            send_hz = 10
            ip_list = self.model.teams_list.keys()
            self.server = TCPServer(port,
                                    server_backlog,
                                    whitelist=ip_list,
                                    hz=send_hz)
            self.server.new_connection.connect(
                self.model.new_connection_handler)
            self.server.lost_connection.connect(
                self.model.lost_connection_handler)
            self.server.new_response.connect(self.model.new_response_handler)

            self.server.server_ready.connect(self.server_ready_handler)

            self.model.team_state_change_signal.connect(
                self.server.on_race_state_change)

            self.server_thread = QThread()
            self.server.moveToThread(self.server_thread)
            self.server_thread.started.connect(self.server.run_server)
            self.server_thread.start()

    def stop_server(self):
        if self.is_server_started:
            self.server.stop()
            self.is_server_started = False
            self.server_thread.quit()

    @QtCore.pyqtSlot(bool)
    def server_ready_handler(self, isReady):
        if isReady:
            self.setCentralWidget(self.horizontalGroupBox)
        if not isReady:
            self.server_wait_label.setText(
                "Server Error: Please restart program.")
            QMessageBox.question(
                self, 'Server Error',
                "Server failed to start.\nPress \"Close\" to quit program, then fix your network issues and restart this program.",
                QMessageBox.Close)
            self.close()

    @QtCore.pyqtSlot(QItemSelection, QItemSelection)
    def standby_race_table_selection_handler(self, filterTableSelection,
                                             filterTableDeselected):
        if filterTableSelection.indexes():
            self.activeRaceTable.selectionModel().clearSelection()
            self.selectedIndex = self.standbyRaceProxyModel.mapToSource(
                filterTableSelection.indexes()[0]).row()

    @QtCore.pyqtSlot(QItemSelection, QItemSelection)
    def active_race_table_selection_handler(self, tableSelection,
                                            tableDeselected):
        if tableSelection.indexes():
            self.standbyRaceTable.selectionModel().clearSelection()
            self.selectedIndex = self.activeRaceProxyModel.mapToSource(
                tableSelection.indexes()[0]).row()

    def clearAllSelections(self):
        self.activeRaceTable.selectionModel().clearSelection()
        self.standbyRaceTable.selectionModel().clearSelection()
        self.selectedIndex = None