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
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: