Added client removing functionality

This commit is contained in:
Artem30801
2019-10-11 22:43:34 +03:00
parent edfe808bd5
commit 09992bd751
7 changed files with 99 additions and 24 deletions

View File

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

View File

@@ -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__':

View File

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

View File

@@ -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_())

View File

@@ -328,7 +328,7 @@
<x>0</x>
<y>0</y>
<width>1220</width>
<height>25</height>
<height>26</height>
</rect>
</property>
<widget class="QMenu" name="menuOptions">
@@ -374,6 +374,7 @@
<addaction name="action_reset_z_offset"/>
<addaction name="separator"/>
<addaction name="menuDeveloper_mode_2"/>
<addaction name="action_remove_row"/>
</widget>
<widget class="QMenu" name="menuMusic">
<property name="title">
@@ -491,6 +492,11 @@
<string>Stop music</string>
</property>
</action>
<action name="action_remove_row">
<property name="text">
<string>Remove from table</string>
</property>
</action>
</widget>
<tabstops>
<tabstop>start_delay_spin</tabstop>

View File

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

View File

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