Merge with master

This commit is contained in:
Arthur Golubtsov
2019-11-28 16:04:54 +03:00
21 changed files with 836 additions and 171 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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