mirror of
https://github.com/CopterExpress/clever-show.git
synced 2026-06-04 19:19:33 +00:00
Merge with master
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import sys
|
||||
import re
|
||||
import math
|
||||
import configparser
|
||||
import collections
|
||||
import indexed
|
||||
from server import ConfigOption
|
||||
@@ -12,6 +13,9 @@ from PyQt5.QtCore import Qt as Qt
|
||||
ModelDataRole = 998
|
||||
ModelStateRole = 999
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
config.read("server_config.ini")
|
||||
|
||||
|
||||
class CopterData:
|
||||
class_basic_attrs = indexed.IndexedOrderedDict([('copter_id', None), ('git_ver', None), ('anim_id', None),
|
||||
@@ -49,6 +53,13 @@ class StatedCopterData(CopterData):
|
||||
try:
|
||||
self.states.__dict__[key] = \
|
||||
Checks.all_checks[self.attrs_dict.keys().index(key)](value)
|
||||
if key == 'start_pos':
|
||||
if (self.__dict__['position'] is not None) and (self.__dict__['start_pos'] is not None):
|
||||
current_pos = get_position(self.__dict__['position'])
|
||||
start_pos = get_position(self.__dict__['start_pos'])
|
||||
delta = get_position_delta(current_pos, start_pos)
|
||||
if delta != 'NO_POS':
|
||||
self.states.__dict__[key] = (delta < Checks.start_pos_delta_max)
|
||||
except KeyError: # No check present for that col
|
||||
pass
|
||||
else: # update selfchecked and takeoff_ready
|
||||
@@ -60,21 +71,32 @@ class StatedCopterData(CopterData):
|
||||
[self.states[i] for i in Checks.takeoff_checklist]
|
||||
)
|
||||
|
||||
def get_position(pos_string):
|
||||
pos = []
|
||||
pos_str = pos_string.split(' ')
|
||||
if pos_str[0] != 'nan' and pos_str[0] != 'NO_POS':
|
||||
for i in range(3):
|
||||
pos.append(float(pos_str[i]))
|
||||
else:
|
||||
pos = 'NO_POS'
|
||||
return pos
|
||||
|
||||
|
||||
def get_position_delta(pos1, pos2):
|
||||
if pos1 != 'NO_POS' and pos2 != 'NO_POS':
|
||||
delta_squared = 0
|
||||
for i in range(3):
|
||||
delta_squared += (pos1[i]-pos2[i])**2
|
||||
return math.sqrt(delta_squared)
|
||||
return 'NO_POS'
|
||||
|
||||
|
||||
class Checks:
|
||||
all_checks = {}
|
||||
takeoff_checklist = (3, 4, 6, 7, 8)
|
||||
current_position = 'NO_POS'
|
||||
start_position = 'NO_POS'
|
||||
|
||||
@classmethod
|
||||
def get_pos_delta(self):
|
||||
if self.current_position != 'NO_POS' and self.start_position != 'NO_POS':
|
||||
delta_squared = 0
|
||||
for i in range(3):
|
||||
delta_squared += (self.current_position[i]-self.start_position[i])**2
|
||||
return math.sqrt(delta_squared)
|
||||
return 'NO_POS'
|
||||
battery_min = config.getfloat('CHECKS', 'battery_percentage_min')
|
||||
start_pos_delta_max = config.getfloat('CHECKS', 'start_pos_delta_max')
|
||||
time_delta_max = config.getfloat('CHECKS', 'time_delta_max')
|
||||
|
||||
class CopterDataModel(QtCore.QAbstractTableModel):
|
||||
selected_ready_signal = QtCore.pyqtSignal(bool)
|
||||
@@ -290,7 +312,7 @@ def check_bat(item):
|
||||
if item == "NO_INFO":
|
||||
return False
|
||||
else:
|
||||
return float(item.split(' ')[1][:-1]) > 50
|
||||
return float(item.split(' ')[1][:-1]) > Checks.battery_min
|
||||
|
||||
@col_check(4)
|
||||
def check_sys_status(item):
|
||||
@@ -310,49 +332,31 @@ def check_mode(item):
|
||||
return None
|
||||
return (item != "NO_FCU") and not ("CMODE" in item)
|
||||
|
||||
|
||||
@col_check(7)
|
||||
def check_selfcheck(item):
|
||||
if not item:
|
||||
return None
|
||||
return item == "OK"
|
||||
|
||||
|
||||
@col_check(8)
|
||||
def check_pos_status(item):
|
||||
if not item:
|
||||
return None
|
||||
str_pos = item.split(' ')
|
||||
if str_pos[0] != 'nan' and str_pos[0] != 'NO_POS':
|
||||
Checks.current_position = []
|
||||
for i in range(3):
|
||||
Checks.current_position.append(float(str_pos[i]))
|
||||
return True
|
||||
Checks.current_position = 'NO_POS'
|
||||
return False
|
||||
return str_pos[0] != 'nan' and str_pos[0] != 'NO_POS'
|
||||
|
||||
@col_check(9)
|
||||
def check_start_pos_status(item):
|
||||
if not item:
|
||||
return None
|
||||
str_start_pos = item.split(' ')
|
||||
if str_start_pos[0] != 'nan' and str_start_pos[0] != 'NO_POS':
|
||||
Checks.start_position = []
|
||||
for i in range(3):
|
||||
Checks.start_position.append(float(str_start_pos[i]))
|
||||
delta = Checks.get_pos_delta()
|
||||
if delta == 'NO_POS':
|
||||
return False
|
||||
else:
|
||||
return delta < 1.
|
||||
return False
|
||||
|
||||
return str_start_pos[0] != 'nan' and str_start_pos[0] != 'NO_POS'
|
||||
|
||||
@col_check(10)
|
||||
def check_time_delta(item):
|
||||
if not item:
|
||||
return None
|
||||
return abs(float(item)) < 1
|
||||
return abs(float(item)) < Checks.time_delta_max
|
||||
|
||||
|
||||
def all_checks(copter_item):
|
||||
@@ -361,14 +365,12 @@ def all_checks(copter_item):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def takeoff_checks(copter_item):
|
||||
for col in Checks.takeoff_checklist:
|
||||
if not Checks.all_checks[col](copter_item[col]):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def flip_checks(copter_item):
|
||||
for col in Checks.takeoff_checklist:
|
||||
if col != 4 or col != 7:
|
||||
|
||||
@@ -3,6 +3,7 @@ import time
|
||||
import socket
|
||||
import random
|
||||
import logging
|
||||
import datetime
|
||||
import threading
|
||||
import selectors
|
||||
import collections
|
||||
@@ -17,11 +18,22 @@ import messaging_lib as messaging
|
||||
|
||||
random.seed()
|
||||
|
||||
now = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
|
||||
path = 'server_logs'
|
||||
if not os.path.exists(path):
|
||||
try:
|
||||
os.mkdir(path)
|
||||
except OSError:
|
||||
print("Creation of the directory %s failed" % path)
|
||||
else:
|
||||
print("Successfully created the directory %s " % path)
|
||||
|
||||
logging.basicConfig( # TODO all prints as logs
|
||||
level=logging.DEBUG,
|
||||
format="%(asctime)s [%(name)-7.7s] [%(threadName)-19.19s] [%(levelname)-7.7s] %(message)s",
|
||||
handlers=[
|
||||
logging.FileHandler("server_logs.log"),
|
||||
logging.FileHandler("server_logs/{}.log".format(now)),
|
||||
logging.StreamHandler()
|
||||
])
|
||||
|
||||
|
||||
@@ -12,3 +12,8 @@ broadcast_delay = 5
|
||||
use_ntp = False
|
||||
host = ntp1.stratum2.ru
|
||||
port = 123
|
||||
|
||||
[CHECKS]
|
||||
battery_percentage_min = 50
|
||||
start_pos_delta_max = 1
|
||||
time_delta_max = 1
|
||||
|
||||
@@ -238,8 +238,8 @@ class Ui_MainWindow(object):
|
||||
self.actionFill.setObjectName("actionFill")
|
||||
self.action_send_any_file = QtWidgets.QAction(MainWindow)
|
||||
self.action_send_any_file.setObjectName("action_send_any_file")
|
||||
self.actionSend_any_command = QtWidgets.QAction(MainWindow)
|
||||
self.actionSend_any_command.setObjectName("actionSend_any_command")
|
||||
self.action_send_any_command = QtWidgets.QAction(MainWindow)
|
||||
self.action_send_any_command.setObjectName("action_send_any_command")
|
||||
self.action_stop_music = QtWidgets.QAction(MainWindow)
|
||||
self.action_stop_music.setObjectName("action_stop_music")
|
||||
self.action_remove_row = QtWidgets.QAction(MainWindow)
|
||||
@@ -248,13 +248,18 @@ class Ui_MainWindow(object):
|
||||
self.action_send_calibrations.setObjectName("action_send_calibrations")
|
||||
self.action_reboot_all = QtWidgets.QAction(MainWindow)
|
||||
self.action_reboot_all.setObjectName("action_reboot_all")
|
||||
self.action_restart_chrony = QtWidgets.QAction(MainWindow)
|
||||
self.action_restart_chrony.setObjectName("action_restart_chrony")
|
||||
self.action_send_fcu_parameters = QtWidgets.QAction(MainWindow)
|
||||
self.action_send_fcu_parameters.setObjectName("action_send_fcu_parameters")
|
||||
self.menuDeveloper_mode.addAction(self.action_send_any_file)
|
||||
self.menuDeveloper_mode.addAction(self.actionSend_any_command)
|
||||
self.menuDeveloper_mode.addAction(self.action_send_any_command)
|
||||
self.menuOptions.addAction(self.action_send_animations)
|
||||
self.menuOptions.addAction(self.action_send_configurations)
|
||||
self.menuOptions.addAction(self.action_send_launch_file)
|
||||
self.menuOptions.addAction(self.action_send_Aruco_map)
|
||||
self.menuOptions.addAction(self.action_send_calibrations)
|
||||
self.menuOptions.addAction(self.action_send_fcu_parameters)
|
||||
self.menuOptions.addSeparator()
|
||||
self.menuOptions.addAction(self.menuDeveloper_mode.menuAction())
|
||||
self.menuOptions.addSeparator()
|
||||
@@ -267,10 +272,11 @@ class Ui_MainWindow(object):
|
||||
self.menuDeveloper_mode_2.addAction(self.action_reboot_all)
|
||||
self.menuDrone.addAction(self.action_set_z_offset_to_ground)
|
||||
self.menuDrone.addAction(self.action_reset_z_offset)
|
||||
self.menuDrone.addSeparator()
|
||||
self.menuDrone.addAction(self.action_restart_chrony)
|
||||
self.menuDrone.addAction(self.action_remove_row)
|
||||
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)
|
||||
@@ -332,8 +338,10 @@ class Ui_MainWindow(object):
|
||||
self.action_test_music_after.setText(_translate("MainWindow", "Test music after"))
|
||||
self.actionFill.setText(_translate("MainWindow", "fill"))
|
||||
self.action_send_any_file.setText(_translate("MainWindow", "Send any file"))
|
||||
self.actionSend_any_command.setText(_translate("MainWindow", "Send any command"))
|
||||
self.action_send_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"))
|
||||
self.action_send_calibrations.setText(_translate("MainWindow", "Send camera calibrations"))
|
||||
self.action_reboot_all.setText(_translate("MainWindow", "Reboot all"))
|
||||
self.action_restart_chrony.setText(_translate("MainWindow", "Restart chrony"))
|
||||
self.action_send_fcu_parameters.setText(_translate("MainWindow", "Send FCU parameters"))
|
||||
|
||||
@@ -391,13 +391,14 @@
|
||||
<string>Developer mode</string>
|
||||
</property>
|
||||
<addaction name="action_send_any_file"/>
|
||||
<addaction name="actionSend_any_command"/>
|
||||
<addaction name="action_send_any_command"/>
|
||||
</widget>
|
||||
<addaction name="action_send_animations"/>
|
||||
<addaction name="action_send_configurations"/>
|
||||
<addaction name="action_send_launch_file"/>
|
||||
<addaction name="action_send_Aruco_map"/>
|
||||
<addaction name="action_send_calibrations"/>
|
||||
<addaction name="action_send_fcu_parameters"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="menuDeveloper_mode"/>
|
||||
<addaction name="separator"/>
|
||||
@@ -425,10 +426,11 @@
|
||||
</widget>
|
||||
<addaction name="action_set_z_offset_to_ground"/>
|
||||
<addaction name="action_reset_z_offset"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_restart_chrony"/>
|
||||
<addaction name="action_remove_row"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="menuDeveloper_mode_2"/>
|
||||
<addaction name="action_remove_row"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuMusic">
|
||||
<property name="title">
|
||||
@@ -536,7 +538,7 @@
|
||||
<string>Send any file</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSend_any_command">
|
||||
<action name="action_send_any_command">
|
||||
<property name="text">
|
||||
<string>Send any command</string>
|
||||
</property>
|
||||
@@ -561,6 +563,16 @@
|
||||
<string>Reboot all</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_restart_chrony">
|
||||
<property name="text">
|
||||
<string>Restart chrony</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_send_fcu_parameters">
|
||||
<property name="text">
|
||||
<string>Send FCU parameters</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>start_delay_spin</tabstop>
|
||||
|
||||
@@ -9,7 +9,7 @@ from PyQt5 import QtWidgets, QtMultimedia
|
||||
from PyQt5.QtGui import QStandardItemModel, QStandardItem
|
||||
from PyQt5.QtCore import Qt, pyqtSlot, pyqtSignal, QObject, QUrl
|
||||
|
||||
from PyQt5.QtWidgets import QFileDialog, QMessageBox
|
||||
from PyQt5.QtWidgets import QFileDialog, QMessageBox, QApplication, QWidget, QInputDialog, QLineEdit
|
||||
from quamash import QEventLoop, QThreadExecutor
|
||||
|
||||
# Importing gui form
|
||||
@@ -152,6 +152,9 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
self.ui.action_send_configurations.triggered.connect(self.send_configurations)
|
||||
self.ui.action_send_Aruco_map.triggered.connect(self.send_aruco)
|
||||
self.ui.action_send_launch_file.triggered.connect(self.send_launch)
|
||||
self.ui.action_send_fcu_parameters.triggered.connect(self.send_fcu_parameters)
|
||||
self.ui.action_send_any_file.triggered.connect(self.send_any_file)
|
||||
self.ui.action_send_any_command.triggered.connect(self.send_any_command)
|
||||
self.ui.action_restart_clever.triggered.connect(self.restart_clever)
|
||||
self.ui.action_restart_clever_show.triggered.connect(self.restart_clever_show)
|
||||
self.ui.action_update_client_repo.triggered.connect(self.update_client_repo)
|
||||
@@ -160,6 +163,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
self.ui.action_reset_start.triggered.connect(self.reset_start)
|
||||
self.ui.action_set_z_offset_to_ground.triggered.connect(self.set_z_offset_to_ground)
|
||||
self.ui.action_reset_z_offset.triggered.connect(self.reset_z_offset)
|
||||
self.ui.action_restart_chrony.triggered.connect(self.restart_chrony)
|
||||
self.ui.action_select_music_file.triggered.connect(self.select_music_file)
|
||||
self.ui.action_play_music.triggered.connect(self.play_music)
|
||||
self.ui.action_stop_music.triggered.connect(self.stop_music)
|
||||
@@ -416,9 +420,38 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
for file in files:
|
||||
filename = os.path.basename(file)
|
||||
copter.client.send_file(file, "/home/pi/catkin_ws/src/clever/clever/launch/{}".format(filename))
|
||||
# copter.client.send_message("service_restart", {"name": "clever"})
|
||||
|
||||
@pyqtSlot()
|
||||
def send_fcu_parameters(self):
|
||||
path = QFileDialog.getOpenFileName(self, "Select px4 param file", filter="px4 params (*.params)")[0]
|
||||
if path:
|
||||
filename = os.path.basename(path)
|
||||
print("Selected file:", path, filename)
|
||||
for copter in self.model.user_selected():
|
||||
copter.client.send_file(path, "temp.params")
|
||||
copter.client.get_response("load_params", self._print_send_fcu_params_result, callback_args=(copter, ))
|
||||
|
||||
def _print_send_fcu_params_result(self, value, copter):
|
||||
logging.info("Send parameters to {} success: {}".format(copter.client.copter_id, value))
|
||||
|
||||
@pyqtSlot()
|
||||
def send_any_file(self):
|
||||
path = QFileDialog.getOpenFileName(self, "Select file")[0]
|
||||
if path:
|
||||
filename = os.path.basename(path)
|
||||
print("Selected file:", path, filename)
|
||||
text, okPressed = QInputDialog.getText(self, "Enter path to send on copter","Destination:", QLineEdit.Normal, "/home/pi/")
|
||||
if okPressed and text != '':
|
||||
for copter in self.model.user_selected():
|
||||
copter.client.send_file(path, text+'/'+filename)
|
||||
|
||||
@pyqtSlot()
|
||||
def send_any_command(self):
|
||||
text, okPressed = QInputDialog.getText(self, "Enter command to send on copter","Command:", QLineEdit.Normal, "")
|
||||
if okPressed and text != '':
|
||||
for copter in self.model.user_selected():
|
||||
copter.client.send_message("execute", {"command": text})
|
||||
@pyqtSlot()
|
||||
def restart_clever(self):
|
||||
for copter in self.model.user_selected():
|
||||
copter.client.send_message("service_restart", {"name": "clever"})
|
||||
@@ -458,6 +491,11 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
for copter in self.model.user_selected():
|
||||
copter.client.send_message("reset_z_offset")
|
||||
|
||||
@pyqtSlot()
|
||||
def restart_chrony(self):
|
||||
for copter in self.model.user_selected():
|
||||
copter.client.send_message("repair_chrony")
|
||||
|
||||
@pyqtSlot()
|
||||
def select_music_file(self):
|
||||
path = QFileDialog.getOpenFileName(self, "Select music file", filter="Music files (*.mp3 *.wav)")[0]
|
||||
|
||||
Reference in New Issue
Block a user