From 09992bd75131336364fd8340b8dc99142cea8df7 Mon Sep 17 00:00:00 2001 From: Artem30801 Date: Fri, 11 Oct 2019 22:43:34 +0300 Subject: [PATCH] Added client removing functionality --- Drone/client.py | 7 ++----- Server/copter_table_models.py | 13 +++++++++++++ Server/server.py | 27 ++++++++++++++++++++------- Server/server_gui.py | 18 ++++++++++++++++-- Server/server_gui.ui | 8 +++++++- Server/server_qt.py | 25 +++++++++++++++++++------ messaging_lib.py | 25 ++++++++++++++++++++++--- 7 files changed, 99 insertions(+), 24 deletions(-) diff --git a/Drone/client.py b/Drone/client.py index 2b6664b..b8e93dc 100644 --- a/Drone/client.py +++ b/Drone/client.py @@ -103,7 +103,7 @@ class Client(object): try: while True: self._reconnect() - #self._process_connections() + self._process_connections() except (KeyboardInterrupt, ): logger.critical("Caught interrupt, exiting!") @@ -138,15 +138,12 @@ class Client(object): self.broadcast_bind(timeout*2, attempt_limit) attempt_count = 0 - def _connect(self): self.connected = True self.client_socket.setblocking(False) events = selectors.EVENT_READ # | selectors.EVENT_WRITE self.selector.register(self.client_socket, events, data=self.server_connection) self.server_connection.connect(self.selector, self.client_socket, (self.server_host, self.server_port)) - self._process_connections() - def broadcast_bind(self, timeout=3.0, attempt_limit=5): broadcast_client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) @@ -200,7 +197,7 @@ class Client(object): logger.error( "Exception {} occurred for {}! Resetting connection!".format(error, connection.addr) ) - self.server_connection.close() + self.server_connection._close() self.connected = False if isinstance(error, OSError): diff --git a/Server/copter_table_models.py b/Server/copter_table_models.py index 51075a0..4bb2539 100644 --- a/Server/copter_table_models.py +++ b/Server/copter_table_models.py @@ -87,6 +87,14 @@ class CopterDataModel(QtCore.QAbstractTableModel): self.endInsertRows() + def removeRows(self, position, rows=1, index=QtCore.QModelIndex()): + self.beginRemoveRows(QtCore.QModelIndex(), position, position + rows - 1) + self.data_contents = self.data_contents[:position] + self.data_contents[position + rows:] + self.endRemoveRows() + print("removed") + + return True + def user_selected(self, contents=()): contents = contents or self.data_contents return filter(lambda x: x.states.checked == Qt.Checked, contents) @@ -205,6 +213,10 @@ class CopterDataModel(QtCore.QAbstractTableModel): def add_client(self, client): self.insertRows([client]) + @QtCore.pyqtSlot(int) + def remove_client(self, row): + self.removeRows(row) + def col_check(col): def inner(f): @@ -331,6 +343,7 @@ class CopterProxyModel(QtCore.QSortFilterProxyModel): class SignalManager(QtCore.QObject): update_data_signal = QtCore.pyqtSignal(int, int, QtCore.QVariant, QtCore.QVariant) add_client_signal = QtCore.pyqtSignal(object) + remove_client_signal = QtCore.pyqtSignal(int) if __name__ == '__main__': diff --git a/Server/server.py b/Server/server.py index e19ba2d..7bd067d 100644 --- a/Server/server.py +++ b/Server/server.py @@ -161,7 +161,7 @@ class Server: client.process_events(mask) except Exception as error: logging.error("Exception {} occurred for {}! Resetting connection!".format(error, client.addr)) - client.close() + client.close(True) else: # Notifier client.process_events(mask) @@ -301,16 +301,29 @@ class Client(messaging.ConnectionManager): def _got_id(self, value): logging.info("Got copter id: {} for client {}".format(value, self.addr)) self.copter_id = value - if Client.on_first_connect: - Client.on_first_connect(self) + if self.on_first_connect: + self.on_first_connect(self) - def close(self): + def close(self, inner=False): self.connected = False - if Client.on_disconnect: - Client.on_disconnect(self) + if self.on_disconnect: + self.on_disconnect(self) - super(Client, self).close() + if inner: + super(Client, self)._close() + else: + super(Client, self).close() + + logging.info("Connection to {} closed!".format(self.copter_id)) + + def remove(self): + if self.connected: + self.close() + + print("closed") + self.clients.pop(self.addr[0]) + logging.info("Client {} successfully removed!".format(self.copter_id)) @requires_connect def _send(self, data): diff --git a/Server/server_gui.py b/Server/server_gui.py index a64c38b..853bf96 100644 --- a/Server/server_gui.py +++ b/Server/server_gui.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'server_gui.ui' # -# Created by: PyQt5 UI code generator 5.13.1 +# Created by: PyQt5 UI code generator 5.13.0 # # WARNING! All changes made in this file will be lost! @@ -167,7 +167,7 @@ class Ui_MainWindow(object): self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 1220, 25)) + self.menubar.setGeometry(QtCore.QRect(0, 0, 1220, 26)) self.menubar.setObjectName("menubar") self.menuOptions = QtWidgets.QMenu(self.menubar) self.menuOptions.setObjectName("menuOptions") @@ -222,6 +222,8 @@ class Ui_MainWindow(object): self.actionSend_any_command.setObjectName("actionSend_any_command") self.action_stop_music = QtWidgets.QAction(MainWindow) self.action_stop_music.setObjectName("action_stop_music") + self.action_remove_row = QtWidgets.QAction(MainWindow) + self.action_remove_row.setObjectName("action_remove_row") self.menuDeveloper_mode.addAction(self.action_send_any_file) self.menuDeveloper_mode.addAction(self.actionSend_any_command) self.menuOptions.addAction(self.action_send_animations) @@ -241,6 +243,7 @@ class Ui_MainWindow(object): self.menuDrone.addAction(self.action_reset_z_offset) self.menuDrone.addSeparator() self.menuDrone.addAction(self.menuDeveloper_mode_2.menuAction()) + self.menuDrone.addAction(self.action_remove_row) self.menuMusic.addAction(self.action_select_music_file) self.menuMusic.addAction(self.action_play_music) self.menuMusic.addAction(self.action_stop_music) @@ -302,3 +305,14 @@ class Ui_MainWindow(object): self.action_send_any_file.setText(_translate("MainWindow", "Send any file")) self.actionSend_any_command.setText(_translate("MainWindow", "Send any command")) self.action_stop_music.setText(_translate("MainWindow", "Stop music")) + self.action_remove_row.setText(_translate("MainWindow", "Remove from table")) + + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + MainWindow = QtWidgets.QMainWindow() + ui = Ui_MainWindow() + ui.setupUi(MainWindow) + MainWindow.show() + sys.exit(app.exec_()) diff --git a/Server/server_gui.ui b/Server/server_gui.ui index b734dd4..41e46e3 100644 --- a/Server/server_gui.ui +++ b/Server/server_gui.ui @@ -328,7 +328,7 @@ 0 0 1220 - 25 + 26 @@ -374,6 +374,7 @@ + @@ -491,6 +492,11 @@ Stop music + + + Remove from table + + start_delay_spin diff --git a/Server/server_qt.py b/Server/server_qt.py index a754354..c2d42bd 100644 --- a/Server/server_qt.py +++ b/Server/server_qt.py @@ -68,9 +68,6 @@ class MainWindow(QtWidgets.QMainWindow): self.model = CopterDataModel() self.proxy_model = CopterProxyModel() self.signals = SignalManager() - self.gyro_calibrated = {} - self.level_calibrated = {} - self.first_col_is_checked = False self.player = QtMultimedia.QMediaPlayer() self.init_model() @@ -88,6 +85,8 @@ class MainWindow(QtWidgets.QMainWindow): # Connect signals to manipulate model from threads self.signals.update_data_signal.connect(self.model.update_item) self.signals.add_client_signal.connect(self.model.add_client) + self.signals.remove_client_signal.connect(self.model.remove_client) + # Connect model signals to UI self.model.selected_ready_signal.connect(self.ui.start_button.setEnabled) @@ -136,6 +135,8 @@ class MainWindow(QtWidgets.QMainWindow): self.ui.calibrate_gyro.clicked.connect(self.calibrate_gyro_selected) self.ui.calibrate_level.clicked.connect(self.calibrate_level_selected) + self.ui.action_remove_row.triggered.connect(self.remove_selected) + self.ui.action_send_animations.triggered.connect(self.send_animations) self.ui.action_send_configurations.triggered.connect(self.send_configurations) self.ui.action_send_Aruco_map.triggered.connect(self.send_aruco) @@ -203,6 +204,18 @@ class MainWindow(QtWidgets.QMainWindow): self.signals.update_data_signal.emit(row, col, data, Qt.EditRole) + @pyqtSlot() + def remove_selected(self): + for copter in self.model.user_selected(): + row_num = self.model.data_contents.index(copter) + + print(1) + copter.client.remove() + print(2) + + self.signals.remove_client_signal.emit(row_num) + print(3) + @pyqtSlot() @confirmation_required("This operation will takeoff selected copters with delay and start animation. Proceed?") def send_starttime_selected(self, **kwargs): @@ -284,7 +297,7 @@ class MainWindow(QtWidgets.QMainWindow): lambda x: x.copter_id == client.copter_id, self.model.data_contents))) col = 5 data = 'CALIBRATING' - self.signals.update_data_signal.emit(row, col, data) + self.signals.update_data_signal.emit(row, col, data, Qt.EditRole) # Send request client.get_response("calibrate_gyro", self._get_calibration_info, callback_args=(5, copter.copter_id)) @@ -297,7 +310,7 @@ class MainWindow(QtWidgets.QMainWindow): lambda x: x.copter_id == client.copter_id, self.model.data_contents))) col = 5 data = 'CALIBRATING' - self.signals.update_data_signal.emit(row, col, data) + self.signals.update_data_signal.emit(row, col, data, Qt.EditRole) # Send request client.get_response("calibrate_level", self._get_calibration_info, callback_args=(5, copter.copter_id)) @@ -305,7 +318,7 @@ class MainWindow(QtWidgets.QMainWindow): row = self.model.data_contents.index(next( filter(lambda x: x.copter_id == copter_id, self.model.data_contents))) data = str(value) - self.signals.update_data_signal.emit(row, col, data) + self.signals.update_data_signal.emit(row, col, data, Qt.EditRole) @pyqtSlot() def send_animations(self): diff --git a/messaging_lib.py b/messaging_lib.py index d2444fa..d1c9368 100644 --- a/messaging_lib.py +++ b/messaging_lib.py @@ -185,9 +185,7 @@ class ConnectionManager(object): self.socket = None self.addr = None - self.selector = None - self.socket = None - self.addr = None + self._should_close = False self._recv_buffer = b"" self._send_buffer = b"" @@ -198,6 +196,7 @@ class ConnectionManager(object): self._send_lock = threading.Lock() self._request_lock = threading.Lock() + self._close_lock = threading.Lock() self.BUFFER_SIZE = 1024 self.resume_queue = False @@ -225,8 +224,16 @@ class ConnectionManager(object): self._set_selector_events_mask('r') def close(self): + with self._close_lock: + self._should_close = True + + self._set_selector_events_mask('w') + NotifierSock().notify() + + def _close(self): logger.info("Closing connection to {}".format(self.addr)) try: + logger.info("Unregistering selector of {}".format(self.addr)) self.selector.unregister(self.socket) except AttributeError: pass @@ -236,6 +243,7 @@ class ConnectionManager(object): self.selector = None try: + logger.info("Closing socket of of {}".format(self.addr)) self.socket.close() except AttributeError: pass @@ -244,7 +252,18 @@ class ConnectionManager(object): finally: self.socket = None + with self._close_lock: + self._should_close = False + + logger.info("CLOSED connection to {}".format(self.addr)) + def process_events(self, mask): + with self._close_lock: + close = self._should_close + if close: + self._close() + return + if mask & selectors.EVENT_READ: self.read() if mask & selectors.EVENT_WRITE: