mirror of
https://github.com/CopterExpress/clever-show.git
synced 2026-05-26 23:19:33 +00:00
Merge master branch
This commit is contained in:
6
Drone/FCU/clever_lpe_flow.params
Normal file
6
Drone/FCU/clever_lpe_flow.params
Normal file
@@ -0,0 +1,6 @@
|
||||
1 1 LPE_FLW_OFF_Z 0.000000000000000000 9
|
||||
1 1 LPE_FLW_QMIN 60 6
|
||||
1 1 LPE_FLW_R 0.200000002980232239 9
|
||||
1 1 LPE_FLW_RR 0.000000000000000000 9
|
||||
1 1 LPE_FLW_SCALE 1.000000000000000000 9
|
||||
1 1 LPE_FUSION 86 6
|
||||
4
Drone/FCU/clever_lpe_gps.params
Normal file
4
Drone/FCU/clever_lpe_gps.params
Normal file
@@ -0,0 +1,4 @@
|
||||
1 1 ATT_EXT_HDG_M 0 6
|
||||
1 1 ATT_W_EXT_HDG 0.000000000000000000 9
|
||||
1 1 ATT_W_MAG 0.100000001490116119 9
|
||||
1 1 LPE_FUSION 145 6
|
||||
3
Drone/FCU/clever_lpe_no_ext_hdg.params
Normal file
3
Drone/FCU/clever_lpe_no_ext_hdg.params
Normal file
@@ -0,0 +1,3 @@
|
||||
1 1 ATT_EXT_HDG_M 0 6
|
||||
1 1 ATT_W_EXT_HDG 0.000000000000000000 9
|
||||
1 1 ATT_W_MAG 0.000000000000000000 9
|
||||
4
Drone/FCU/clever_lpe_viz.params
Normal file
4
Drone/FCU/clever_lpe_viz.params
Normal file
@@ -0,0 +1,4 @@
|
||||
1 1 ATT_EXT_HDG_M 1 6
|
||||
1 1 ATT_W_EXT_HDG 0.500000000000000000 9
|
||||
1 1 ATT_W_MAG 0.000000000000000000 9
|
||||
1 1 LPE_FUSION 84 6
|
||||
58
Drone/FCU/clever_mc_mpc.params
Normal file
58
Drone/FCU/clever_mc_mpc.params
Normal file
@@ -0,0 +1,58 @@
|
||||
1 1 MC_PITCHRATE_D 0.003000000026077032 9
|
||||
1 1 MC_PITCHRATE_FF 0.000000000000000000 9
|
||||
1 1 MC_PITCHRATE_I 0.019999999552965164 9
|
||||
1 1 MC_PITCHRATE_MAX 220.000000000000000000 9
|
||||
1 1 MC_PITCHRATE_P 0.100000001490116119 9
|
||||
1 1 MC_PITCH_P 6.500000000000000000 9
|
||||
1 1 MC_PR_INT_LIM 0.300000011920928955 9
|
||||
1 1 MC_RATT_TH 0.800000011920928955 9
|
||||
1 1 MC_ROLLRATE_D 0.003000000026077032 9
|
||||
1 1 MC_ROLLRATE_FF 0.000000000000000000 9
|
||||
1 1 MC_ROLLRATE_I 0.019999999552965164 9
|
||||
1 1 MC_ROLLRATE_MAX 220.000000000000000000 9
|
||||
1 1 MC_ROLLRATE_P 0.100000001490116119 9
|
||||
1 1 MC_ROLL_P 6.500000000000000000 9
|
||||
1 1 MPC_ACC_DOWN_MAX 10.000000000000000000 9
|
||||
1 1 MPC_ACC_HOR 5.000000000000000000 9
|
||||
1 1 MPC_ACC_HOR_ESTM 0.500000000000000000 9
|
||||
1 1 MPC_ACC_HOR_MAX 10.000000000000000000 9
|
||||
1 1 MPC_ACC_UP_MAX 10.000000000000000000 9
|
||||
1 1 MPC_ALT_MODE 0 6
|
||||
1 1 MPC_CRUISE_90 3.000000000000000000 9
|
||||
1 1 MPC_DEC_HOR_SLOW 5.000000000000000000 9
|
||||
1 1 MPC_FLT_TSK 0 6
|
||||
1 1 MPC_HOLD_DZ 0.100000001490116119 9
|
||||
1 1 MPC_HOLD_MAX_XY 0.800000011920928955 9
|
||||
1 1 MPC_HOLD_MAX_Z 0.600000023841857910 9
|
||||
1 1 MPC_JERK_MAX 0.000000000000000000 9
|
||||
1 1 MPC_JERK_MIN 1.000000000000000000 9
|
||||
1 1 MPC_LAND_ALT1 10.000000000000000000 9
|
||||
1 1 MPC_LAND_ALT2 5.000000000000000000 9
|
||||
1 1 MPC_LAND_SPEED 0.699999988079071045 9
|
||||
1 1 MPC_MANTHR_MAX 1.000000000000000000 9
|
||||
1 1 MPC_MANTHR_MIN 0.079999998211860657 9
|
||||
1 1 MPC_MAN_TILT_MAX 35.000000000000000000 9
|
||||
1 1 MPC_MAN_Y_MAX 200.000000000000000000 9
|
||||
1 1 MPC_THR_HOVER 0.550000011920928955 9
|
||||
1 1 MPC_THR_MAX 1.000000000000000000 9
|
||||
1 1 MPC_THR_MIN 0.119999997317790985 9
|
||||
1 1 MPC_TILTMAX_AIR 45.000000000000000000 9
|
||||
1 1 MPC_TILTMAX_LND 12.000000000000000000 9
|
||||
1 1 MPC_TKO_RAMP_T 0.400000005960464478 9
|
||||
1 1 MPC_TKO_SPEED 1.000000000000000000 9
|
||||
1 1 MPC_VELD_LP 5.000000000000000000 9
|
||||
1 1 MPC_VEL_MANUAL 10.000000000000000000 9
|
||||
1 1 MPC_XY_CRUISE 5.000000000000000000 9
|
||||
1 1 MPC_XY_MAN_EXPO 0.000000000000000000 9
|
||||
1 1 MPC_XY_P 1.000000000000000000 9
|
||||
1 1 MPC_XY_VEL_D 0.006000000052154064 9
|
||||
1 1 MPC_XY_VEL_I 0.004999999888241291 9
|
||||
1 1 MPC_XY_VEL_MAX 12.000000000000000000 9
|
||||
1 1 MPC_XY_VEL_P 0.090000003576278687 9
|
||||
1 1 MPC_Z_MAN_EXPO 0.000000000000000000 9
|
||||
1 1 MPC_Z_P 0.949999988079071045 9
|
||||
1 1 MPC_Z_VEL_D 0.000000000000000000 9
|
||||
1 1 MPC_Z_VEL_I 0.014999999664723873 9
|
||||
1 1 MPC_Z_VEL_MAX_DN 1.000000000000000000 9
|
||||
1 1 MPC_Z_VEL_MAX_UP 3.000000000000000000 9
|
||||
1 1 MPC_Z_VEL_P 0.250000000000000000 9
|
||||
@@ -1,53 +0,0 @@
|
||||
# Onboard parameters for LPE setup
|
||||
#
|
||||
# Stack: PX4 Pro
|
||||
# Vehicle: Multi-Rotor
|
||||
# Version: 1.8.2
|
||||
#
|
||||
|
||||
# Vehicle-Id Component-Id Name Value Type
|
||||
1 1 ATT_EXT_HDG_M 1 6
|
||||
1 1 ATT_W_EXT_HDG 0.500000000000000000 9
|
||||
1 1 ATT_W_MAG 0.000000000000000000 9
|
||||
1 1 CBRK_IO_SAFETY 22027 6
|
||||
1 1 CBRK_USB_CHK 197848 6
|
||||
1 1 COM_ARM_MAG 1.000000000000000000 9
|
||||
1 1 COM_DISARM_LAND 1 6
|
||||
1 1 COM_FLTMODE1 8 6
|
||||
1 1 COM_FLTMODE4 2 6
|
||||
1 1 COM_FLTMODE6 2 6
|
||||
1 1 COM_LOW_BAT_ACT 2 6
|
||||
1 1 COM_RC_LOSS_T 2.000000000000000000 9
|
||||
1 1 LNDMC_ROT_MAX 45.000000000000000000 9
|
||||
1 1 LNDMC_THR_RANGE 0.800000000000000000 9
|
||||
1 1 LNDMC_Z_VEL_MAX 1.000000000000000000 9
|
||||
1 1 LPE_FLW_QMIN 10 6
|
||||
1 1 LPE_FLW_R 0.200000001490116119 9
|
||||
1 1 LPE_FLW_RR 0.000000000000000000 9
|
||||
1 1 LPE_FLW_SCALE 1.000000000000000000 9
|
||||
1 1 LPE_FUSION 86 6
|
||||
1 1 LPE_VIS_DELAY 0.000000000000000000 9
|
||||
1 1 LPE_VIS_Z 0.10000005960464478 9
|
||||
1 1 MC_PITCHRATE_D 0.001200000056996942 9
|
||||
1 1 MC_PITCHRATE_P 0.119999997317790985 9
|
||||
1 1 MC_PITCH_P 4.500000000000000000 9
|
||||
1 1 MC_ROLLRATE_D 0.001200000056996942 9
|
||||
1 1 MC_ROLLRATE_P 0.119999997317790985 9
|
||||
1 1 MC_ROLL_P 4.500000000000000000 9
|
||||
1 1 MC_YAW_P 2.500000000000000000 9
|
||||
1 1 MPC_ACC_DOWN_MAX 2.000000000000000000 9
|
||||
1 1 MPC_ACC_UP_MAX 2.000000000000000000 9
|
||||
1 1 MPC_MANTHR_MAX 0.899999976158142090 9
|
||||
1 1 MPC_THR_HOVER 0.550011920928955 9
|
||||
1 1 MPC_THR_MAX 0.899999976158142090 9
|
||||
1 1 MPC_XY_P 1.400000023841857910 9
|
||||
1 1 MPC_XY_VEL_P 0.100000003576278687 9
|
||||
1 1 MPC_Z_VEL_MAX_UP 1.000000000000000000 9
|
||||
1 1 MPC_Z_VEL_P 0.300000011920928955 9
|
||||
1 1 RC_MAP_FLTMODE 5 6
|
||||
1 1 SENS_FLOW_MAXHGT 4.000000000000000000 9
|
||||
1 1 SENS_FLOW_MAXR 2.500000000000000000 9
|
||||
1 1 SENS_FLOW_MINHGT 0.01 9
|
||||
1 1 SENS_FLOW_ROT 0 6
|
||||
1 1 SYS_HAS_BARO 0 6
|
||||
1 1 SYS_MC_EST_GROUP 1 6
|
||||
@@ -40,7 +40,7 @@ def get_id(filepath="animation.csv"):
|
||||
print("No animation id in file")
|
||||
return anim_id
|
||||
|
||||
def load_animation(filepath="animation.csv", x0=0, y0=0, z0=0):
|
||||
def load_animation(filepath="animation.csv", x0=0, y0=0, z0=0, ratio=1):
|
||||
imported_frames = []
|
||||
global anim_id
|
||||
try:
|
||||
@@ -62,9 +62,9 @@ def load_animation(filepath="animation.csv", x0=0, y0=0, z0=0):
|
||||
frame_number, x, y, z, yaw, red, green, blue = row_0
|
||||
imported_frames.append({
|
||||
'number': int(frame_number),
|
||||
'x': float(x) + x0,
|
||||
'y': float(y) + y0,
|
||||
'z': float(z) + z0,
|
||||
'x': ratio*float(x) + x0,
|
||||
'y': ratio*float(y) + y0,
|
||||
'z': ratio*float(z) + z0,
|
||||
'yaw': float(yaw),
|
||||
'red': int(red),
|
||||
'green': int(green),
|
||||
@@ -74,9 +74,9 @@ def load_animation(filepath="animation.csv", x0=0, y0=0, z0=0):
|
||||
frame_number, x, y, z, yaw, red, green, blue = row
|
||||
imported_frames.append({
|
||||
'number': int(frame_number),
|
||||
'x': float(x) + x0,
|
||||
'y': float(y) + y0,
|
||||
'z': float(z) + z0,
|
||||
'x': ratio*float(x) + x0,
|
||||
'y': ratio*float(y) + y0,
|
||||
'z': ratio*float(z) + z0,
|
||||
'yaw': float(yaw),
|
||||
'red': int(red),
|
||||
'green': int(green),
|
||||
|
||||
@@ -7,6 +7,7 @@ import logging
|
||||
import collections
|
||||
import ConfigParser
|
||||
import selectors2 as selectors
|
||||
import threading
|
||||
|
||||
from contextlib import closing
|
||||
|
||||
@@ -15,7 +16,7 @@ current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentfra
|
||||
parent_dir = os.path.dirname(current_dir)
|
||||
sys.path.insert(0, parent_dir)
|
||||
|
||||
#logging.basicConfig(level=logging.INFO)
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
import messaging_lib as messaging
|
||||
@@ -102,7 +103,7 @@ class Client(object):
|
||||
try:
|
||||
while True:
|
||||
self._reconnect()
|
||||
self._process_connections()
|
||||
#self._process_connections()
|
||||
|
||||
except (KeyboardInterrupt, ):
|
||||
logger.critical("Caught interrupt, exiting!")
|
||||
@@ -141,7 +142,7 @@ class Client(object):
|
||||
def _connect(self):
|
||||
self.connected = True
|
||||
self.client_socket.setblocking(False)
|
||||
events = selectors.EVENT_READ | selectors.EVENT_WRITE
|
||||
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()
|
||||
@@ -186,25 +187,26 @@ class Client(object):
|
||||
def _process_connections(self):
|
||||
while True:
|
||||
events = self.selector.select(timeout=1)
|
||||
if events:
|
||||
for key, mask in events:
|
||||
if key.data is None:
|
||||
pass
|
||||
else:
|
||||
connection = key.data
|
||||
try:
|
||||
connection.process_events(mask)
|
||||
# logging.debug("tick")
|
||||
for key, mask in events: # TODO add notifier to client!
|
||||
connection = key.data
|
||||
if connection is None:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
connection.process_events(mask)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
"Exception {} occurred for {}! Resetting connection!".format(error, connection.addr)
|
||||
)
|
||||
self.server_connection.close()
|
||||
self.connected = False
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
"Exception {} occurred for {}! Resetting connection!".format(error, connection.addr)
|
||||
)
|
||||
self.server_connection.close()
|
||||
self.connected = False
|
||||
|
||||
if isinstance(error, OSError):
|
||||
if error.errno == errno.EINTR:
|
||||
raise KeyboardInterrupt
|
||||
|
||||
if isinstance(error, OSError):
|
||||
if error.errno == errno.EINTR:
|
||||
raise KeyboardInterrupt
|
||||
|
||||
if not self.selector.get_map():
|
||||
logger.warning("No active connections left!")
|
||||
|
||||
@@ -16,12 +16,11 @@ port = 123
|
||||
[ANIMATION]
|
||||
takeoff_animation_check = True
|
||||
land_animation_check = True
|
||||
frame_delay = 0.1
|
||||
frame_delay = 0.15
|
||||
ratio = 0.8
|
||||
|
||||
[COPTERS]
|
||||
frame_id = aruco_map_flipped
|
||||
frame_flipped_width = 1.5
|
||||
frame_flipped_height = 6.5
|
||||
frame_id = map
|
||||
takeoff_height = 2.0
|
||||
takeoff_time = 8.0
|
||||
safe_takeoff = False
|
||||
@@ -29,11 +28,22 @@ reach_first_point_time = 8.0
|
||||
land_time = 3.0
|
||||
x0_common = 0
|
||||
y0_common = 0
|
||||
z0_common = 0
|
||||
|
||||
[FLOOR FRAME]
|
||||
parent = aruco_map
|
||||
x = 0.0
|
||||
y = 0.0
|
||||
z = 6.5
|
||||
roll = 180
|
||||
pitch = 0
|
||||
yaw = -90
|
||||
|
||||
[PRIVATE]
|
||||
id = /hostname
|
||||
use_leds = True
|
||||
led_pin = 18
|
||||
led_pin = 21
|
||||
x0 = 0
|
||||
y0 = 0
|
||||
z0 = 0
|
||||
|
||||
|
||||
@@ -52,7 +52,6 @@ sed -i "/127.0.1.1/c 127.0.1.1 $3" /etc/hosts
|
||||
|
||||
# set hostname for ROS
|
||||
sed -i "/ROS_HOSTNAME/c ROS_HOSTNAME=\'$3\'" /home/pi/.bashrc
|
||||
sed -i "/ROS_HOSTNAME/c ROS_HOSTNAME=$3" /lib/systemd/system/roscore.env
|
||||
|
||||
# set ssh message
|
||||
cat << EOF | tee /etc/motd
|
||||
@@ -65,7 +64,7 @@ EOF
|
||||
cat << EOF | tee /etc/chrony/chrony.conf
|
||||
server $4 iburst
|
||||
driftfile /var/lib/chrony/drift
|
||||
makestep 1.0 3
|
||||
makestep 1.0 -1
|
||||
rtcsync
|
||||
EOF
|
||||
|
||||
|
||||
@@ -45,11 +45,14 @@ class CopterClient(client.Client):
|
||||
self.LAND_TIME = self.config.getfloat('COPTERS', 'land_time')
|
||||
self.X0_COMMON = self.config.getfloat('COPTERS', 'x0_common')
|
||||
self.Y0_COMMON = self.config.getfloat('COPTERS', 'y0_common')
|
||||
self.Z0_COMMON = self.config.getfloat('COPTERS', 'z0_common')
|
||||
self.TAKEOFF_CHECK = self.config.getboolean('ANIMATION', 'takeoff_animation_check')
|
||||
self.LAND_CHECK = self.config.getboolean('ANIMATION', 'land_animation_check')
|
||||
self.FRAME_DELAY = self.config.getfloat('ANIMATION', 'frame_delay')
|
||||
self.RATIO = self.config.getfloat('ANIMATION', 'ratio')
|
||||
self.X0 = self.config.getfloat('PRIVATE', 'x0')
|
||||
self.Y0 = self.config.getfloat('PRIVATE', 'y0')
|
||||
self.Z0 = self.config.getfloat('PRIVATE', 'z0')
|
||||
self.USE_LEDS = self.config.getboolean('PRIVATE', 'use_leds')
|
||||
self.LED_PIN = self.config.getint('PRIVATE', 'led_pin')
|
||||
|
||||
@@ -63,20 +66,28 @@ class CopterClient(client.Client):
|
||||
if self.USE_LEDS:
|
||||
LedLib.init_led(self.LED_PIN)
|
||||
task_manager_instance.start()
|
||||
if self.FRAME_ID == "aruco_map_flipped":
|
||||
if self.FRAME_ID == "floor":
|
||||
try:
|
||||
self.FRAME_FLIPPED_HEIGHT = self.config.getfloat('COPTERS', 'frame_flipped_height')
|
||||
self.FRAME_FLIPPED_WIDTH = self.config.getfloat('COPTERS', 'frame_flipped_width')
|
||||
self.FLOOR_DX = self.config.getfloat('FLOOR FRAME', 'x')
|
||||
self.FLOOR_DY = self.config.getfloat('FLOOR FRAME', 'y')
|
||||
self.FLOOR_DZ = self.config.getfloat('FLOOR FRAME', 'z')
|
||||
self.FLOOR_ROLL = self.config.getfloat('FLOOR FRAME', 'roll')
|
||||
self.FLOOR_PITCH = self.config.getfloat('FLOOR FRAME', 'pitch')
|
||||
self.FLOOR_YAW = self.config.getfloat('FLOOR FRAME', 'yaw')
|
||||
self.FLOOR_PARENT = self.config.get('FLOOR FRAME', 'parent')
|
||||
except Exception as e:
|
||||
pass
|
||||
raise Exception("Can't make floor frame!")
|
||||
quit()
|
||||
else:
|
||||
trans = TransformStamped()
|
||||
trans.transform.translation.x = 0.
|
||||
trans.transform.translation.y = self.FRAME_FLIPPED_WIDTH
|
||||
trans.transform.translation.z = self.FRAME_FLIPPED_HEIGHT
|
||||
trans.transform.rotation = Quaternion(*quaternion_from_euler(math.pi,0,0))
|
||||
trans.header.frame_id = "aruco_map"
|
||||
trans.child_frame_id = "aruco_map_flipped"
|
||||
trans.transform.translation.x = self.FLOOR_DX
|
||||
trans.transform.translation.y = self.FLOOR_DY
|
||||
trans.transform.translation.z = self.FLOOR_DZ
|
||||
trans.transform.rotation = Quaternion(*quaternion_from_euler(math.radians(self.FLOOR_ROLL),
|
||||
math.radians(self.FLOOR_PITCH),
|
||||
math.radians(self.FLOOR_YAW)))
|
||||
trans.header.frame_id = self.FLOOR_PARENT
|
||||
trans.child_frame_id = self.FRAME_ID
|
||||
static_bloadcaster.sendTransform(trans)
|
||||
start_subscriber()
|
||||
# print(check_state_topic())
|
||||
@@ -138,6 +149,8 @@ def _response_animation_id():
|
||||
frames = animation.load_animation(os.path.abspath("animation.csv"),
|
||||
x0=client.active_client.X0 + client.active_client.X0_COMMON,
|
||||
y0=client.active_client.Y0 + client.active_client.Y0_COMMON,
|
||||
z0=client.active_client.Z0 + client.active_client.Z0_COMMON,
|
||||
ratio=client.active_client.RATIO,
|
||||
)
|
||||
# Correct start and land frames in animation
|
||||
corrected_frames, start_action, start_delay = animation.correct_animation(frames,
|
||||
@@ -202,6 +215,7 @@ def _command_move_start_to_current_position(**kwargs):
|
||||
frames = animation.load_animation(os.path.abspath("animation.csv"),
|
||||
x0=client.active_client.X0_COMMON,
|
||||
y0=client.active_client.Y0_COMMON,
|
||||
ratio=client.active_client.RATIO,
|
||||
)
|
||||
# Correct start and land frames in animation
|
||||
corrected_frames, start_action, start_delay = animation.correct_animation(frames,
|
||||
@@ -225,6 +239,21 @@ def _command_reset_start(**kwargs):
|
||||
client.active_client.load_config()
|
||||
print ("Reset start to {:.2f} {:.2f}".format(client.active_client.X0, client.active_client.Y0))
|
||||
|
||||
@messaging.message_callback("set_z_to_ground")
|
||||
def _command_set_z(**kwargs):
|
||||
telem = FlightLib.get_telemetry(client.active_client.FRAME_ID)
|
||||
client.active_client.config.set('PRIVATE', 'z0', telem.z)
|
||||
client.active_client.rewrite_config()
|
||||
client.active_client.load_config()
|
||||
print ("Set z offset to {:.2f}".format(client.active_client.Z0))
|
||||
|
||||
@messaging.message_callback("reset_z_offset")
|
||||
def _command_reset_z(**kwargs):
|
||||
client.active_client.config.set('PRIVATE', 'z0', 0)
|
||||
client.active_client.rewrite_config()
|
||||
client.active_client.load_config()
|
||||
print ("Reset z offset to {:.2f}".format(client.active_client.Z0))
|
||||
|
||||
|
||||
@messaging.message_callback("update_repo")
|
||||
def _command_update_repo(**kwargs):
|
||||
@@ -333,6 +362,8 @@ def _play_animation(**kwargs):
|
||||
frames = animation.load_animation(os.path.abspath("animation.csv"),
|
||||
x0=client.active_client.X0 + client.active_client.X0_COMMON,
|
||||
y0=client.active_client.Y0 + client.active_client.Y0_COMMON,
|
||||
z0=client.active_client.Z0 + client.active_client.Z0_COMMON,
|
||||
ratio=client.active_client.RATIO,
|
||||
)
|
||||
# Correct start and land frames in animation
|
||||
corrected_frames, start_action, start_delay = animation.correct_animation(frames,
|
||||
|
||||
25
README.md
25
README.md
@@ -1,16 +1,19 @@
|
||||
# clever-show
|
||||
[](https://travis-ci.org/artem30801/CleverSwarm)
|
||||
[](https://www.codefactor.io/repository/github/artem30801/cleverswarm)
|
||||
[Русская версия](README_RU.md)
|
||||
|
||||
Програмное обеспечение для запуска шоу дронов под управлением Raspberry Pi с пакетом COEX [Clever](https://github.com/copterexpress/clever).
|
||||
Software for making the drone show controlled by Raspberry Pi and COEX [Clever](https://github.com/CopterExpress/clever) package.
|
||||
|
||||
### Пакет включает в себя:
|
||||
* [Набор ПО для дрона](https://github.com/artem30801/CleverSwarm/tree/master/Drone), включащее в себя библиотеку для автономного полёта, модуль для воспроизведения анимаций и клиентское приложение для удаленного синхронизированного управления
|
||||
* [Серверное приложение](https://github.com/artem30801/CleverSwarm/tree/master/Server) для удаленного синхронизированного управления дронами и удобной настройки системы для воспроизведения анимации
|
||||
* [Аддон для Blender 2.8](https://github.com/artem30801/CleverSwarm/tree/master/blender-addon) для преобразования анимации полёта коптеров, созданной в Blender, в файлы полётов для каждого коптера
|
||||
* [Образ для Raspberry Pi](https://github.com/artem30801/CleverSwarm/releases/latest) для быстрого запуска ПО на коптере
|
||||
[](https://travis-ci.org/CopterExpress/clever-show)
|
||||
|
||||
## This software includes
|
||||
* [Drone side](https://github.com/CopterExpress/clever-show/tree/master/Drone) with autonomous flight module, animation player module and client application for remote synchronized control of drones
|
||||
* [Server side](https://github.com/CopterExpress/clever-show/tree/master/Server) for making the drone show with ability of tuning drones, animation and music
|
||||
* [Blender 2.8 addon](https://github.com/CopterExpress/clever-show/tree/master/blender-addon) for animation export to drone paths
|
||||
* [Raspberry Pi image](https://github.com/CopterExpress/clever-show/releases/latest) for quick launch software on the drones
|
||||
|
||||
## Documentation
|
||||
Start tutorial is located [here](docs/start-tutorial.md).
|
||||
|
||||
Detailed documentation is located in the [docs](https://github.com/CopterExpress/clever-show/tree/master/docs) folder.
|
||||
|
||||
## Документация
|
||||
Инструкция по запуску ПО находится [здесь](docs/start-tutorial.md).
|
||||
|
||||
Подробная документация расположена в папке [docs](https://github.com/artem30801/CleverSwarm/tree/master/docs).
|
||||
|
||||
19
README_RU.md
Normal file
19
README_RU.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# clever-show
|
||||
[English version](README.md)
|
||||
|
||||
Програмное обеспечение для запуска шоу дронов под управлением Raspberry Pi с пакетом COEX [Clever](https://github.com/CopterExpress/clever).
|
||||
|
||||
[](https://travis-ci.org/CopterExpress/clever-show)
|
||||
|
||||
## Пакет включает в себя
|
||||
* [Набор ПО для дрона](https://github.com/CopterExpress/clever-show/tree/master/Drone) с библиотекой для автономного полёта, модулем воспроизведения анимаций и клиентским приложением для удаленного синхронизированного управления дронами
|
||||
* [Серверное приложение](https://github.com/CopterExpress/clever-show/tree/master/Server) для создания шоу и настройки дронов, анимации и музыки
|
||||
* [Аддон для Blender 2.8](https://github.com/CopterExpress/clever-show/tree/master/blender-addon) для преобразования анимации полёта коптеров, созданной в Blender, в файлы полётов для каждого коптера
|
||||
* [Образ для Raspberry Pi](https://github.com/CopterExpress/clever-show/releases/latest) для быстрого запуска ПО на коптере
|
||||
|
||||
## Документация
|
||||
Инструкция по запуску ПО находится [здесь](docs/start-tutorial.md).
|
||||
|
||||
Подробная документация расположена в папке [docs](https://github.com/CopterExpress/clever-show/tree/master/docs).
|
||||
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
server master iburst
|
||||
pool 0.ru.pool.ntp.org iburst minpoll 10
|
||||
pool 1.ru.pool.ntp.org iburst minpoll 10
|
||||
pool 2.ru.pool.ntp.org iburst minpoll 10
|
||||
pool 3.ru.pool.ntp.org iburst minpoll 10
|
||||
driftfile /var/lib/chrony/drift
|
||||
local stratum 8
|
||||
allow 192.168.0.0/16
|
||||
makestep 1.0 3
|
||||
rtcsync
|
||||
smoothtime 50000 0.01
|
||||
rtcsync
|
||||
|
||||
|
||||
@@ -170,74 +170,49 @@ def col_check(col):
|
||||
def check_anim(item):
|
||||
if not item:
|
||||
return None
|
||||
if str(item) == 'No animation':
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
return str(item) != 'No animation'
|
||||
|
||||
@col_check(2)
|
||||
def check_bat_v(item):
|
||||
if not item:
|
||||
return None
|
||||
if float(item) > 3.2: # todo config
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
return float(item) > 3.2
|
||||
|
||||
@col_check(3)
|
||||
def check_bat_p(item):
|
||||
if not item:
|
||||
return None
|
||||
if float(item) > 30: # todo config
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
#return True #For testing
|
||||
return float(item) > 30
|
||||
|
||||
@col_check(4)
|
||||
def check_sys_status(item):
|
||||
if not item:
|
||||
return None
|
||||
if item == "STANDBY":
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
return item == "STANDBY"
|
||||
|
||||
@col_check(5)
|
||||
def check_cal_status(item):
|
||||
if not item:
|
||||
return None
|
||||
if item == "OK":
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
return item == "OK"
|
||||
|
||||
@col_check(6)
|
||||
def check_selfcheck(item):
|
||||
if not item:
|
||||
return None
|
||||
if item == "OK":
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
return item == "OK"
|
||||
|
||||
@col_check(7)
|
||||
def check_cal_status(item):
|
||||
if not item:
|
||||
return None
|
||||
else:
|
||||
return True
|
||||
return True
|
||||
|
||||
@col_check(8)
|
||||
def check_time_delta(item):
|
||||
if not item:
|
||||
return None
|
||||
if abs(float(item)) < 1:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
return abs(float(item)) < 1
|
||||
|
||||
|
||||
def all_checks(copter_item):
|
||||
@@ -263,9 +238,7 @@ def flip_checks(copter_item):
|
||||
return True
|
||||
|
||||
def calibrating_check(copter_item):
|
||||
if copter_item[5] == "CALIBRATING":
|
||||
return True
|
||||
return False
|
||||
return copter_item[5] == "CALIBRATING"
|
||||
|
||||
def calibration_ready_check(copter_item):
|
||||
if not CopterDataModel.checks[4](copter_item[4]):
|
||||
|
||||
@@ -143,20 +143,27 @@ class Server:
|
||||
logging.info("Client processor (selector) thread started!")
|
||||
self.server_socket.listen()
|
||||
self.server_socket.setblocking(False)
|
||||
self.sel.register(self.server_socket, selectors.EVENT_READ | selectors.EVENT_WRITE, data=None)
|
||||
self.sel.register(self.server_socket, selectors.EVENT_READ, data=None) #| selectors.EVENT_WRITE
|
||||
|
||||
messaging.NotifierSock().bind((self.ip, self.port))
|
||||
|
||||
while self.client_processor_thread_running.is_set():
|
||||
events = self.sel.select()
|
||||
logging.error('tick')
|
||||
for key, mask in events:
|
||||
if key.data is None:
|
||||
# logging.error(mask)
|
||||
# logging.error(str(key.data))
|
||||
client = key.data
|
||||
if client is None:
|
||||
self._connect_client(key.fileobj)
|
||||
else:
|
||||
client = key.data
|
||||
elif isinstance(client, messaging.ConnectionManager):
|
||||
try:
|
||||
client.process_events(mask)
|
||||
except Exception as error:
|
||||
logging.error("Exception {} occurred for {}! Resetting connection!".format(error, client.addr))
|
||||
client.close()
|
||||
else: # Notifier
|
||||
client.process_events(mask)
|
||||
|
||||
logging.info("Client autoconnect thread stopped!")
|
||||
|
||||
@@ -165,7 +172,11 @@ class Server:
|
||||
logging.info("Got connection from: {}".format(str(addr)))
|
||||
conn.setblocking(False)
|
||||
|
||||
if not any([client_addr == addr[0] for client_addr in Client.clients.keys()]):
|
||||
if addr[0] == self.ip and messaging.NotifierSock().addr is None:
|
||||
client = messaging.NotifierSock()
|
||||
logging.info("Notifier sock client")
|
||||
|
||||
elif not any([client_addr == addr[0] for client_addr in Client.clients.keys()]):
|
||||
client = Client(addr[0])
|
||||
logging.info("New client")
|
||||
else:
|
||||
|
||||
@@ -13,7 +13,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
class Ui_MainWindow(object):
|
||||
def setupUi(self, MainWindow):
|
||||
MainWindow.setObjectName("MainWindow")
|
||||
MainWindow.resize(1220, 613)
|
||||
MainWindow.resize(1220, 750)
|
||||
self.centralwidget = QtWidgets.QWidget(MainWindow)
|
||||
self.centralwidget.setEnabled(True)
|
||||
self.centralwidget.setObjectName("centralwidget")
|
||||
@@ -43,31 +43,41 @@ class Ui_MainWindow(object):
|
||||
self.verticalLayout.setSizeConstraint(QtWidgets.QLayout.SetMaximumSize)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.formLayout = QtWidgets.QFormLayout()
|
||||
self.formLayout.setLabelAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
|
||||
self.formLayout.setFormAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop)
|
||||
self.formLayout.setLabelAlignment(QtCore.Qt.AlignCenter)
|
||||
self.formLayout.setFormAlignment(QtCore.Qt.AlignCenter)
|
||||
self.formLayout.setObjectName("formLayout")
|
||||
self.check_button = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.check_button.setEnabled(True)
|
||||
self.check_button.setObjectName("check_button")
|
||||
self.formLayout.setWidget(1, QtWidgets.QFormLayout.SpanningRole, self.check_button)
|
||||
self.start_text = QtWidgets.QLabel(self.centralwidget)
|
||||
self.start_text.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.start_text.setObjectName("start_text")
|
||||
self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.start_text)
|
||||
self.music_text = QtWidgets.QLabel(self.centralwidget)
|
||||
self.music_text.setLayoutDirection(QtCore.Qt.RightToLeft)
|
||||
self.music_text.setObjectName("music_text")
|
||||
self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.music_text)
|
||||
self.music_delay_spin = QtWidgets.QDoubleSpinBox(self.centralwidget)
|
||||
self.music_delay_spin.setDecimals(1)
|
||||
self.music_delay_spin.setObjectName("music_delay_spin")
|
||||
self.formLayout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.music_delay_spin)
|
||||
self.music_checkbox = QtWidgets.QCheckBox(self.centralwidget)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.music_checkbox.sizePolicy().hasHeightForWidth())
|
||||
self.music_checkbox.setSizePolicy(sizePolicy)
|
||||
self.music_checkbox.setLayoutDirection(QtCore.Qt.RightToLeft)
|
||||
self.music_checkbox.setAutoFillBackground(True)
|
||||
self.music_checkbox.setText("")
|
||||
self.music_checkbox.setChecked(False)
|
||||
self.music_checkbox.setObjectName("music_checkbox")
|
||||
self.formLayout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.music_checkbox)
|
||||
self.music_play_text = QtWidgets.QLabel(self.centralwidget)
|
||||
self.music_play_text.setLayoutDirection(QtCore.Qt.RightToLeft)
|
||||
self.music_play_text.setObjectName("music_play_text")
|
||||
self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.music_play_text)
|
||||
self.start_delay_spin = QtWidgets.QSpinBox(self.centralwidget)
|
||||
self.start_delay_spin.setObjectName("start_delay_spin")
|
||||
self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.start_delay_spin)
|
||||
self.start_button = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.start_button.setEnabled(True)
|
||||
self.start_button.setFlat(False)
|
||||
self.start_button.setObjectName("start_button")
|
||||
self.formLayout.setWidget(3, QtWidgets.QFormLayout.SpanningRole, self.start_button)
|
||||
self.pause_button = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.pause_button.setObjectName("pause_button")
|
||||
self.formLayout.setWidget(4, QtWidgets.QFormLayout.SpanningRole, self.pause_button)
|
||||
self.stop_button = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.stop_button.setObjectName("stop_button")
|
||||
self.formLayout.setWidget(5, QtWidgets.QFormLayout.SpanningRole, self.stop_button)
|
||||
self.start_text = QtWidgets.QLabel(self.centralwidget)
|
||||
self.start_text.setLayoutDirection(QtCore.Qt.RightToLeft)
|
||||
self.start_text.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.start_text.setObjectName("start_text")
|
||||
self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.start_text)
|
||||
self.verticalLayout.addLayout(self.formLayout)
|
||||
self.line = QtWidgets.QFrame(self.centralwidget)
|
||||
self.line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
@@ -77,15 +87,21 @@ class Ui_MainWindow(object):
|
||||
self.formLayout_2 = QtWidgets.QFormLayout()
|
||||
self.formLayout_2.setFormAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
|
||||
self.formLayout_2.setObjectName("formLayout_2")
|
||||
self.disarm_button = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.disarm_button.setObjectName("disarm_button")
|
||||
self.formLayout_2.setWidget(7, QtWidgets.QFormLayout.SpanningRole, self.disarm_button)
|
||||
self.emergency_button = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.emergency_button.setObjectName("emergency_button")
|
||||
self.formLayout_2.setWidget(6, QtWidgets.QFormLayout.SpanningRole, self.emergency_button)
|
||||
self.disarm_all_button = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.disarm_all_button.setObjectName("disarm_all_button")
|
||||
self.formLayout_2.setWidget(8, QtWidgets.QFormLayout.SpanningRole, self.disarm_all_button)
|
||||
self.stop_button = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.stop_button.setObjectName("stop_button")
|
||||
self.formLayout_2.setWidget(10, QtWidgets.QFormLayout.FieldRole, self.stop_button)
|
||||
self.pause_button = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.pause_button.setObjectName("pause_button")
|
||||
self.formLayout_2.setWidget(9, QtWidgets.QFormLayout.FieldRole, self.pause_button)
|
||||
self.start_button = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.start_button.setEnabled(True)
|
||||
self.start_button.setFlat(False)
|
||||
self.start_button.setObjectName("start_button")
|
||||
self.formLayout_2.setWidget(8, QtWidgets.QFormLayout.FieldRole, self.start_button)
|
||||
self.check_button = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.check_button.setEnabled(True)
|
||||
self.check_button.setObjectName("check_button")
|
||||
self.formLayout_2.setWidget(7, QtWidgets.QFormLayout.FieldRole, self.check_button)
|
||||
self.verticalLayout.addLayout(self.formLayout_2)
|
||||
self.line_2 = QtWidgets.QFrame(self.centralwidget)
|
||||
self.line_2.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
@@ -96,19 +112,15 @@ class Ui_MainWindow(object):
|
||||
self.formLayout_3.setFormAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
|
||||
self.formLayout_3.setVerticalSpacing(6)
|
||||
self.formLayout_3.setObjectName("formLayout_3")
|
||||
self.leds_button = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.leds_button.setObjectName("leds_button")
|
||||
self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.SpanningRole, self.leds_button)
|
||||
self.takeoff_button = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.takeoff_button.setEnabled(True)
|
||||
self.takeoff_button.setObjectName("takeoff_button")
|
||||
self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.SpanningRole, self.takeoff_button)
|
||||
self.flip_button = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.flip_button.setObjectName("flip_button")
|
||||
self.formLayout_3.setWidget(2, QtWidgets.QFormLayout.SpanningRole, self.flip_button)
|
||||
self.land_button = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.land_button.setObjectName("land_button")
|
||||
self.formLayout_3.setWidget(3, QtWidgets.QFormLayout.SpanningRole, self.land_button)
|
||||
self.disarm_all_button = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.disarm_all_button.setObjectName("disarm_all_button")
|
||||
self.formLayout_3.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.disarm_all_button)
|
||||
self.disarm_button = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.disarm_button.setObjectName("disarm_button")
|
||||
self.formLayout_3.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.disarm_button)
|
||||
self.emergency_button = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.emergency_button.setObjectName("emergency_button")
|
||||
self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.emergency_button)
|
||||
self.verticalLayout.addLayout(self.formLayout_3)
|
||||
self.line_3 = QtWidgets.QFrame(self.centralwidget)
|
||||
self.line_3.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
@@ -116,18 +128,40 @@ class Ui_MainWindow(object):
|
||||
self.line_3.setObjectName("line_3")
|
||||
self.verticalLayout.addWidget(self.line_3)
|
||||
self.formLayout_4 = QtWidgets.QFormLayout()
|
||||
self.formLayout_4.setFormAlignment(QtCore.Qt.AlignBottom|QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft)
|
||||
self.formLayout_4.setFormAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
|
||||
self.formLayout_4.setObjectName("formLayout_4")
|
||||
self.land_button = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.land_button.setObjectName("land_button")
|
||||
self.formLayout_4.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.land_button)
|
||||
self.flip_button = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.flip_button.setObjectName("flip_button")
|
||||
self.formLayout_4.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.flip_button)
|
||||
self.takeoff_button = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.takeoff_button.setEnabled(True)
|
||||
self.takeoff_button.setObjectName("takeoff_button")
|
||||
self.formLayout_4.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.takeoff_button)
|
||||
self.leds_button = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.leds_button.setObjectName("leds_button")
|
||||
self.formLayout_4.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.leds_button)
|
||||
self.verticalLayout.addLayout(self.formLayout_4)
|
||||
self.line_4 = QtWidgets.QFrame(self.centralwidget)
|
||||
self.line_4.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
self.line_4.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
self.line_4.setObjectName("line_4")
|
||||
self.verticalLayout.addWidget(self.line_4)
|
||||
self.formLayout_6 = QtWidgets.QFormLayout()
|
||||
self.formLayout_6.setFormAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
|
||||
self.formLayout_6.setObjectName("formLayout_6")
|
||||
self.reboot_fcu = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.reboot_fcu.setObjectName("reboot_fcu")
|
||||
self.formLayout_4.setWidget(0, QtWidgets.QFormLayout.SpanningRole, self.reboot_fcu)
|
||||
self.formLayout_6.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.reboot_fcu)
|
||||
self.calibrate_gyro = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.calibrate_gyro.setObjectName("calibrate_gyro")
|
||||
self.formLayout_4.setWidget(1, QtWidgets.QFormLayout.SpanningRole, self.calibrate_gyro)
|
||||
self.formLayout_6.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.calibrate_gyro)
|
||||
self.calibrate_level = QtWidgets.QPushButton(self.centralwidget)
|
||||
self.calibrate_level.setObjectName("calibrate_level")
|
||||
self.formLayout_4.setWidget(2, QtWidgets.QFormLayout.SpanningRole, self.calibrate_level)
|
||||
self.verticalLayout.addLayout(self.formLayout_4)
|
||||
self.formLayout_6.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.calibrate_level)
|
||||
self.verticalLayout.addLayout(self.formLayout_6)
|
||||
self.horizontalLayout.addLayout(self.verticalLayout)
|
||||
self.horizontalLayout.setStretch(0, 1)
|
||||
self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1)
|
||||
@@ -143,6 +177,10 @@ class Ui_MainWindow(object):
|
||||
self.menuTable.setObjectName("menuTable")
|
||||
self.menuAnimation = QtWidgets.QMenu(self.menubar)
|
||||
self.menuAnimation.setObjectName("menuAnimation")
|
||||
self.menuDrone = QtWidgets.QMenu(self.menubar)
|
||||
self.menuDrone.setObjectName("menuDrone")
|
||||
self.menuMusic = QtWidgets.QMenu(self.menubar)
|
||||
self.menuMusic.setObjectName("menuMusic")
|
||||
MainWindow.setMenuBar(self.menubar)
|
||||
self.action_send_animations = QtWidgets.QAction(MainWindow)
|
||||
self.action_send_animations.setObjectName("action_send_animations")
|
||||
@@ -166,6 +204,16 @@ class Ui_MainWindow(object):
|
||||
self.action_set_start_to_current_position.setObjectName("action_set_start_to_current_position")
|
||||
self.action_reset_start = QtWidgets.QAction(MainWindow)
|
||||
self.action_reset_start.setObjectName("action_reset_start")
|
||||
self.action_set_z_offset_to_ground = QtWidgets.QAction(MainWindow)
|
||||
self.action_set_z_offset_to_ground.setObjectName("action_set_z_offset_to_ground")
|
||||
self.action_reset_z_offset = QtWidgets.QAction(MainWindow)
|
||||
self.action_reset_z_offset.setObjectName("action_reset_z_offset")
|
||||
self.action_select_music_file = QtWidgets.QAction(MainWindow)
|
||||
self.action_select_music_file.setObjectName("action_select_music_file")
|
||||
self.action_play_music = QtWidgets.QAction(MainWindow)
|
||||
self.action_play_music.setObjectName("action_play_music")
|
||||
self.action_test_music_after = QtWidgets.QAction(MainWindow)
|
||||
self.action_test_music_after.setObjectName("action_test_music_after")
|
||||
self.menuDeveloper_mode.addAction(self.action_send_launch_file)
|
||||
self.menuDeveloper_mode.addAction(self.action_restart_clever)
|
||||
self.menuDeveloper_mode.addAction(self.action_restart_clever_show)
|
||||
@@ -178,35 +226,40 @@ class Ui_MainWindow(object):
|
||||
self.menuTable.addAction(self.action_select_all_rows)
|
||||
self.menuAnimation.addAction(self.action_set_start_to_current_position)
|
||||
self.menuAnimation.addAction(self.action_reset_start)
|
||||
self.menuDrone.addAction(self.action_set_z_offset_to_ground)
|
||||
self.menuDrone.addAction(self.action_reset_z_offset)
|
||||
self.menuMusic.addAction(self.action_select_music_file)
|
||||
self.menuMusic.addAction(self.action_play_music)
|
||||
self.menuMusic.addAction(self.action_test_music_after)
|
||||
self.menubar.addAction(self.menuOptions.menuAction())
|
||||
self.menubar.addAction(self.menuAnimation.menuAction())
|
||||
self.menubar.addAction(self.menuDrone.menuAction())
|
||||
self.menubar.addAction(self.menuMusic.menuAction())
|
||||
self.menubar.addAction(self.menuTable.menuAction())
|
||||
|
||||
self.retranslateUi(MainWindow)
|
||||
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
||||
MainWindow.setTabOrder(self.check_button, self.start_delay_spin)
|
||||
MainWindow.setTabOrder(self.start_delay_spin, self.start_button)
|
||||
MainWindow.setTabOrder(self.start_button, self.pause_button)
|
||||
MainWindow.setTabOrder(self.pause_button, self.stop_button)
|
||||
MainWindow.setTabOrder(self.stop_button, self.disarm_button)
|
||||
MainWindow.setTabOrder(self.disarm_button, self.tableView)
|
||||
MainWindow.setTabOrder(self.start_delay_spin, self.tableView)
|
||||
|
||||
def retranslateUi(self, MainWindow):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
MainWindow.setWindowTitle(_translate("MainWindow", "Clever Drone Animation Player"))
|
||||
self.check_button.setText(_translate("MainWindow", "Preflight check"))
|
||||
self.start_text.setText(_translate("MainWindow", "Start after"))
|
||||
self.start_delay_spin.setSuffix(_translate("MainWindow", " seconds"))
|
||||
self.start_button.setText(_translate("MainWindow", "Start animation"))
|
||||
self.pause_button.setText(_translate("MainWindow", "Pause"))
|
||||
self.music_text.setText(_translate("MainWindow", " Music after"))
|
||||
self.music_delay_spin.setSuffix(_translate("MainWindow", " s"))
|
||||
self.music_play_text.setText(_translate("MainWindow", " Play music"))
|
||||
self.start_delay_spin.setSuffix(_translate("MainWindow", " s"))
|
||||
self.start_text.setText(_translate("MainWindow", " Start after"))
|
||||
self.stop_button.setText(_translate("MainWindow", "Stop and land all"))
|
||||
self.pause_button.setText(_translate("MainWindow", "Pause"))
|
||||
self.start_button.setText(_translate("MainWindow", "Start animation"))
|
||||
self.check_button.setText(_translate("MainWindow", "Preflight check"))
|
||||
self.disarm_all_button.setText(_translate("MainWindow", "Disarm ALL"))
|
||||
self.disarm_button.setText(_translate("MainWindow", "Disarm selected"))
|
||||
self.emergency_button.setText(_translate("MainWindow", "Emergency land"))
|
||||
self.disarm_all_button.setText(_translate("MainWindow", "Disarm ALL"))
|
||||
self.leds_button.setText(_translate("MainWindow", "Test leds"))
|
||||
self.takeoff_button.setText(_translate("MainWindow", "Takeoff"))
|
||||
self.flip_button.setText(_translate("MainWindow", "Flip"))
|
||||
self.land_button.setText(_translate("MainWindow", "Land"))
|
||||
self.flip_button.setText(_translate("MainWindow", "Flip"))
|
||||
self.takeoff_button.setText(_translate("MainWindow", "Takeoff"))
|
||||
self.leds_button.setText(_translate("MainWindow", "Test leds"))
|
||||
self.reboot_fcu.setText(_translate("MainWindow", "Reboot FCU"))
|
||||
self.calibrate_gyro.setText(_translate("MainWindow", "Calibrate gyro"))
|
||||
self.calibrate_level.setText(_translate("MainWindow", "Calibrate level"))
|
||||
@@ -214,6 +267,8 @@ class Ui_MainWindow(object):
|
||||
self.menuDeveloper_mode.setTitle(_translate("MainWindow", "Developer mode"))
|
||||
self.menuTable.setTitle(_translate("MainWindow", "Table"))
|
||||
self.menuAnimation.setTitle(_translate("MainWindow", "Animation"))
|
||||
self.menuDrone.setTitle(_translate("MainWindow", "Drone"))
|
||||
self.menuMusic.setTitle(_translate("MainWindow", "Music"))
|
||||
self.action_send_animations.setText(_translate("MainWindow", "Send Animations"))
|
||||
self.action_send_configurations.setText(_translate("MainWindow", "Send Configurations"))
|
||||
self.action_send_Aruco_map.setText(_translate("MainWindow", "Send Aruco map"))
|
||||
@@ -224,5 +279,10 @@ class Ui_MainWindow(object):
|
||||
self.action_restart_clever_show.setText(_translate("MainWindow", "Restart clever-show service"))
|
||||
self.action_select_all_rows.setText(_translate("MainWindow", "Select All"))
|
||||
self.action_select_all_rows.setShortcut(_translate("MainWindow", "Ctrl+A"))
|
||||
self.action_set_start_to_current_position.setText(_translate("MainWindow", "Set start to current position"))
|
||||
self.action_set_start_to_current_position.setText(_translate("MainWindow", "Set start X Y to current position"))
|
||||
self.action_reset_start.setText(_translate("MainWindow", "Reset start position"))
|
||||
self.action_set_z_offset_to_ground.setText(_translate("MainWindow", "Set Z offset to ground"))
|
||||
self.action_reset_z_offset.setText(_translate("MainWindow", "Reset Z offset"))
|
||||
self.action_select_music_file.setText(_translate("MainWindow", "Select music file"))
|
||||
self.action_play_music.setText(_translate("MainWindow", "Play music"))
|
||||
self.action_test_music_after.setText(_translate("MainWindow", "Test music after"))
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1220</width>
|
||||
<height>613</height>
|
||||
<height>750</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -68,62 +68,80 @@
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="labelAlignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="formAlignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="check_button">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="music_text">
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::RightToLeft</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Preflight check</string>
|
||||
<string> Music after</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="start_text">
|
||||
<property name="text">
|
||||
<string>Start after</string>
|
||||
<item row="3" column="1">
|
||||
<widget class="QDoubleSpinBox" name="music_delay_spin">
|
||||
<property name="suffix">
|
||||
<string> s</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
<property name="decimals">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QCheckBox" name="music_checkbox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::RightToLeft</enum>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="music_play_text">
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::RightToLeft</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string> Play music</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QSpinBox" name="start_delay_spin">
|
||||
<property name="suffix">
|
||||
<string> seconds</string>
|
||||
<string> s</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="start_button">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="start_text">
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::RightToLeft</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Start animation</string>
|
||||
<string> Start after</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="pause_button">
|
||||
<property name="text">
|
||||
<string>Pause</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="stop_button">
|
||||
<property name="text">
|
||||
<string>Stop and land all</string>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -141,24 +159,40 @@
|
||||
<property name="formAlignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<item row="7" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="disarm_button">
|
||||
<item row="10" column="1">
|
||||
<widget class="QPushButton" name="stop_button">
|
||||
<property name="text">
|
||||
<string>Disarm selected</string>
|
||||
<string>Stop and land all</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="emergency_button">
|
||||
<item row="9" column="1">
|
||||
<widget class="QPushButton" name="pause_button">
|
||||
<property name="text">
|
||||
<string>Emergency land</string>
|
||||
<string>Pause</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="disarm_all_button">
|
||||
<item row="8" column="1">
|
||||
<widget class="QPushButton" name="start_button">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Disarm ALL</string>
|
||||
<string>Start animation</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QPushButton" name="check_button">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Preflight check</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -179,34 +213,24 @@
|
||||
<property name="verticalSpacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="leds_button">
|
||||
<item row="3" column="1">
|
||||
<widget class="QPushButton" name="disarm_all_button">
|
||||
<property name="text">
|
||||
<string>Test leds</string>
|
||||
<string>Disarm ALL</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="takeoff_button">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<item row="2" column="1">
|
||||
<widget class="QPushButton" name="disarm_button">
|
||||
<property name="text">
|
||||
<string>Takeoff</string>
|
||||
<string>Disarm selected</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="flip_button">
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="emergency_button">
|
||||
<property name="text">
|
||||
<string>Flip</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="land_button">
|
||||
<property name="text">
|
||||
<string>Land</string>
|
||||
<string>Emergency land</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -222,23 +246,68 @@
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout_4">
|
||||
<property name="formAlignment">
|
||||
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<item row="3" column="1">
|
||||
<widget class="QPushButton" name="land_button">
|
||||
<property name="text">
|
||||
<string>Land</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QPushButton" name="flip_button">
|
||||
<property name="text">
|
||||
<string>Flip</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="takeoff_button">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Takeoff</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="leds_button">
|
||||
<property name="text">
|
||||
<string>Test leds</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout_6">
|
||||
<property name="formAlignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="reboot_fcu">
|
||||
<property name="text">
|
||||
<string>Reboot FCU</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="calibrate_gyro">
|
||||
<property name="text">
|
||||
<string>Calibrate gyro</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<item row="2" column="1">
|
||||
<widget class="QPushButton" name="calibrate_level">
|
||||
<property name="text">
|
||||
<string>Calibrate level</string>
|
||||
@@ -294,8 +363,25 @@
|
||||
<addaction name="action_set_start_to_current_position"/>
|
||||
<addaction name="action_reset_start"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuDrone">
|
||||
<property name="title">
|
||||
<string>Drone</string>
|
||||
</property>
|
||||
<addaction name="action_set_z_offset_to_ground"/>
|
||||
<addaction name="action_reset_z_offset"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuMusic">
|
||||
<property name="title">
|
||||
<string>Music</string>
|
||||
</property>
|
||||
<addaction name="action_select_music_file"/>
|
||||
<addaction name="action_play_music"/>
|
||||
<addaction name="action_test_music_after"/>
|
||||
</widget>
|
||||
<addaction name="menuOptions"/>
|
||||
<addaction name="menuAnimation"/>
|
||||
<addaction name="menuDrone"/>
|
||||
<addaction name="menuMusic"/>
|
||||
<addaction name="menuTable"/>
|
||||
</widget>
|
||||
<action name="action_send_animations">
|
||||
@@ -348,7 +434,7 @@
|
||||
</action>
|
||||
<action name="action_set_start_to_current_position">
|
||||
<property name="text">
|
||||
<string>Set start to current position</string>
|
||||
<string>Set start X Y to current position</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_reset_start">
|
||||
@@ -356,14 +442,34 @@
|
||||
<string>Reset start position</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_set_z_offset_to_ground">
|
||||
<property name="text">
|
||||
<string>Set Z offset to ground</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_reset_z_offset">
|
||||
<property name="text">
|
||||
<string>Reset Z offset</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_select_music_file">
|
||||
<property name="text">
|
||||
<string>Select music file</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_play_music">
|
||||
<property name="text">
|
||||
<string>Play music</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_test_music_after">
|
||||
<property name="text">
|
||||
<string>Test music after</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>check_button</tabstop>
|
||||
<tabstop>start_delay_spin</tabstop>
|
||||
<tabstop>start_button</tabstop>
|
||||
<tabstop>pause_button</tabstop>
|
||||
<tabstop>stop_button</tabstop>
|
||||
<tabstop>disarm_button</tabstop>
|
||||
<tabstop>tableView</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import os
|
||||
import glob
|
||||
import math
|
||||
import asyncio
|
||||
|
||||
from PyQt5 import QtWidgets
|
||||
from PyQt5 import QtWidgets, QtMultimedia
|
||||
from PyQt5.QtGui import QStandardItemModel, QStandardItem
|
||||
from PyQt5.QtCore import Qt, pyqtSlot, pyqtSignal, QObject
|
||||
from PyQt5.QtCore import Qt, pyqtSlot, pyqtSignal, QObject, QUrl
|
||||
|
||||
from PyQt5.QtWidgets import QFileDialog, QMessageBox
|
||||
from quamash import QEventLoop
|
||||
|
||||
# Importing gui form
|
||||
from server_gui import Ui_MainWindow
|
||||
@@ -18,6 +20,18 @@ from emergency import *
|
||||
import threading
|
||||
|
||||
|
||||
def wait(end, interrupter=threading.Event(), maxsleep=0.1):
|
||||
# Added features to interrupter sleep and set max sleeping interval
|
||||
|
||||
while not interrupter.is_set(): # Basic implementation of pause module until()
|
||||
now = time.time()
|
||||
diff = min(end - now, maxsleep)
|
||||
if diff <= 0:
|
||||
break
|
||||
else:
|
||||
time.sleep(diff / 2)
|
||||
|
||||
|
||||
def confirmation_required(text="Are you sure?", label="Confirm operation?"):
|
||||
def inner(f):
|
||||
|
||||
@@ -41,7 +55,7 @@ def confirmation_required(text="Are you sure?", label="Confirm operation?"):
|
||||
|
||||
# noinspection PyArgumentList,PyCallByClass
|
||||
class MainWindow(QtWidgets.QMainWindow):
|
||||
def __init__(self):
|
||||
def __init__(self, loop):
|
||||
super(MainWindow, self).__init__()
|
||||
|
||||
self.ui = Ui_MainWindow()
|
||||
@@ -54,6 +68,8 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
self.gyro_calibrated = {}
|
||||
self.level_calibrated = {}
|
||||
self.first_col_is_checked = False
|
||||
self.player = QtMultimedia.QMediaPlayer()
|
||||
self.loop = loop
|
||||
|
||||
self.init_model()
|
||||
|
||||
@@ -123,6 +139,11 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
self.ui.action_update_client_repo.triggered.connect(self.update_client_repo)
|
||||
self.ui.action_set_start_to_current_position.triggered.connect(self.update_start_to_current_position)
|
||||
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_select_music_file.triggered.connect(self.select_music_file)
|
||||
self.ui.action_play_music.triggered.connect(self.play_music)
|
||||
self.ui.action_test_music_after.triggered.connect(self.test_music_after)
|
||||
|
||||
# Set most safety-important buttons disabled
|
||||
self.ui.start_button.setEnabled(False)
|
||||
@@ -178,6 +199,11 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
@pyqtSlot()
|
||||
def send_starttime_selected(self, **kwargs):
|
||||
dt = self.ui.start_delay_spin.value()
|
||||
logging.info('Wait {} seconds to start animation'.format(dt))
|
||||
if self.ui.music_checkbox.isChecked():
|
||||
music_dt = self.ui.music_delay_spin.value()
|
||||
asyncio.ensure_future(self.play_music_after(music_dt), loop=self.loop)
|
||||
logging.info('Wait {} seconds to play music'.format(music_dt))
|
||||
self.selfcheck_selected()
|
||||
for copter in self.model.user_selected():
|
||||
if all_checks(copter):
|
||||
@@ -351,6 +377,59 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
for copter in self.model.user_selected():
|
||||
copter.client.send_message("reset_start")
|
||||
|
||||
@pyqtSlot()
|
||||
def set_z_offset_to_ground(self):
|
||||
for copter in self.model.user_selected():
|
||||
copter.client.send_message("set_z_to_ground")
|
||||
|
||||
@pyqtSlot()
|
||||
def reset_z_offset(self):
|
||||
for copter in self.model.user_selected():
|
||||
copter.client.send_message("reset_z_offset")
|
||||
|
||||
@pyqtSlot()
|
||||
def select_music_file(self):
|
||||
path = QFileDialog.getOpenFileName(self, "Select music file", filter="Music files (*.mp3)")[0]
|
||||
if path:
|
||||
media = QUrl.fromLocalFile(path)
|
||||
content = QtMultimedia.QMediaContent(media)
|
||||
self.player.setMedia(content)
|
||||
|
||||
@pyqtSlot()
|
||||
def play_music(self):
|
||||
if self.player.mediaStatus() == QtMultimedia.QMediaPlayer.InvalidMedia:
|
||||
logger.info("Can't play media")
|
||||
return
|
||||
if self.player.mediaStatus() == QtMultimedia.QMediaPlayer.NoMedia:
|
||||
logger.info("No media file")
|
||||
return
|
||||
|
||||
if self.player.state() == QtMultimedia.QMediaPlayer.StoppedState or \
|
||||
self.player.state() == QtMultimedia.QMediaPlayer.PausedState:
|
||||
self.player.play()
|
||||
else:
|
||||
self.player.pause()
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def play_music_after(self, delay=0.):
|
||||
if self.player.mediaStatus() == QtMultimedia.QMediaPlayer.InvalidMedia:
|
||||
logger.info("Can't play media")
|
||||
return
|
||||
if self.player.mediaStatus() == QtMultimedia.QMediaPlayer.NoMedia:
|
||||
logger.info("No media file")
|
||||
return
|
||||
self.player.stop()
|
||||
wait(time.time()+delay)
|
||||
logging.info("Play music")
|
||||
self.player.play()
|
||||
|
||||
@pyqtSlot()
|
||||
def test_music_after(self):
|
||||
dt = self.ui.music_delay_spin.value()
|
||||
asyncio.ensure_future(self.play_music_after(dt), loop=self.loop)
|
||||
logging.info('Wait {} seconds to play music'.format(dt))
|
||||
|
||||
@pyqtSlot()
|
||||
def emergency(self):
|
||||
client_row_min = 0
|
||||
@@ -401,13 +480,18 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
window = MainWindow()
|
||||
loop = QEventLoop(app)
|
||||
asyncio.set_event_loop(loop)
|
||||
window = MainWindow(loop)
|
||||
|
||||
Client.on_first_connect = window.client_connected
|
||||
|
||||
server = Server(on_stop=app.quit)
|
||||
server.start()
|
||||
|
||||
app.exec_()
|
||||
#app.exec_()
|
||||
with loop:
|
||||
loop.run_forever()
|
||||
|
||||
server.stop()
|
||||
sys.exit()
|
||||
|
||||
@@ -3,9 +3,9 @@ Description=Clever Show Client Service
|
||||
After=clever.service
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/home/pi/CleverSwarm/Drone
|
||||
EnvironmentFile=/lib/systemd/system/roscore.env
|
||||
ExecStart=/usr/bin/python /home/pi/CleverSwarm/Drone/copter_client.py
|
||||
WorkingDirectory=/home/pi/clever-show/Drone
|
||||
ExecStart=/bin/bash -c ". /home/pi/catkin_ws/devel/setup.sh; \
|
||||
/usr/bin/python /home/pi/clever-show/Drone/copter_client.py"
|
||||
KillSignal=SIGKILL
|
||||
Restart=on-failure
|
||||
RestartSec=3
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
set -e # Exit immidiately on non-zero result
|
||||
|
||||
SOURCE_IMAGE="https://github.com/CopterExpress/clever/releases/download/v0.17/clever_v0.17.img.zip"
|
||||
SOURCE_IMAGE="https://github.com/CopterExpress/clever/releases/download/v0.18/clever_v0.18.img.zip"
|
||||
|
||||
export DEBIAN_FRONTEND=${DEBIAN_FRONTEND:='noninteractive'}
|
||||
export LANG=${LANG:='C.UTF-8'}
|
||||
@@ -41,6 +41,7 @@ if [[ -z ${TRAVIS_TAG} ]]; then IMAGE_VERSION="$(cd ${REPO_DIR}; git log --forma
|
||||
# IMAGE_VERSION="${TRAVIS_TAG:=$(cd ${REPO_DIR}; git log --format=%h -1)}"
|
||||
REPO_URL="$(cd ${REPO_DIR}; git remote --verbose | grep origin | grep fetch | cut -f2 | cut -d' ' -f1 | sed 's/git@github\.com\:/https\:\/\/github.com\//')"
|
||||
REPO_NAME="$(basename -s '.git' ${REPO_URL})"
|
||||
echo_stamp "REPO_NAME=${REPO_NAME}" "INFO"
|
||||
IMAGE_NAME="${REPO_NAME}_${IMAGE_VERSION}.img"
|
||||
echo_stamp "IMAGE_NAME=${IMAGE_NAME}" "INFO"
|
||||
IMAGE_PATH="${IMAGES_DIR}/${IMAGE_NAME}"
|
||||
@@ -99,10 +100,10 @@ echo_stamp "Mount dirs ${MOUNT_POINT} & ${MOUNT_POINT}/boot"
|
||||
mount "${DEV_IMAGE}p2" ${MOUNT_POINT}
|
||||
mount "${DEV_IMAGE}p1" ${MOUNT_POINT}/boot
|
||||
|
||||
mkdir -p ${MOUNT_POINT}'/home/pi/CleverSwarm/'
|
||||
mkdir -p ${MOUNT_POINT}'/home/pi/clever-show/'
|
||||
for dir in ${REPO_DIR}/*; do
|
||||
if [[ $dir != *"images" && $dir != *"imgcache" ]]; then
|
||||
cp -r $dir ${MOUNT_POINT}'/home/pi/CleverSwarm/'$(basename $dir)
|
||||
cp -r $dir ${MOUNT_POINT}'/home/pi/clever-show/'$(basename $dir)
|
||||
fi;
|
||||
done
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ my_travis_retry() {
|
||||
}
|
||||
|
||||
echo_stamp "Change repo owner to pi"
|
||||
chown -Rf pi:pi /home/pi/CleverSwarm/
|
||||
chown -Rf pi:pi /home/pi/clever-show/
|
||||
|
||||
echo_stamp "Update apt cache"
|
||||
apt-get update -qq
|
||||
@@ -65,8 +65,13 @@ chrony \
|
||||
echo_stamp "Install python libs"
|
||||
my_travis_retry pip install selectors2
|
||||
|
||||
cd /home/pi/catkin_ws
|
||||
echo_stamp "Install catkin packages"
|
||||
cd /home/pi/catkin_ws/src
|
||||
git clone https://github.com/CopterExpress/clever_tools.git
|
||||
cd ..
|
||||
source devel/setup.bash
|
||||
catkin_make --pkg clever_flight_routines
|
||||
catkin_make aruco_pose
|
||||
source devel/setup.bash
|
||||
|
||||
echo_stamp "End of software installation"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import io
|
||||
import sys
|
||||
import json
|
||||
import socket
|
||||
import struct
|
||||
import random
|
||||
import logging
|
||||
@@ -12,13 +13,28 @@ try:
|
||||
except ImportError:
|
||||
import selectors2 as selectors
|
||||
|
||||
#import logging_lib
|
||||
# import logging_lib
|
||||
|
||||
PendingRequest = collections.namedtuple("PendingRequest", ["value", "requested_value", # "expires_on",
|
||||
"callback", "callback_args", "callback_kwargs",
|
||||
])
|
||||
logger = logging.getLogger(__name__)
|
||||
#logger = logging_lib.Logger(_logger, True)
|
||||
|
||||
|
||||
# logger = logging_lib.Logger(_logger, True)
|
||||
|
||||
|
||||
class _Singleton(type):
|
||||
""" A metaclass that creates a Singleton base class when called. """
|
||||
_instances = {}
|
||||
|
||||
def __call__(cls, *args, **kwargs):
|
||||
if cls not in cls._instances:
|
||||
cls._instances[cls] = super(_Singleton, cls).__call__(*args, **kwargs)
|
||||
return cls._instances[cls]
|
||||
|
||||
|
||||
class Singleton(_Singleton('SingletonMeta', (object,), {})): pass
|
||||
|
||||
|
||||
class MessageManager:
|
||||
@@ -196,14 +212,17 @@ class ConnectionManager(object):
|
||||
events = selectors.EVENT_READ | selectors.EVENT_WRITE
|
||||
else:
|
||||
raise ValueError("Invalid events mask mode {}.".format(mode))
|
||||
self.selector.modify(self.socket, events, data=self)
|
||||
|
||||
key = self.selector.modify(self.socket, events, data=self)
|
||||
logging.debug("Switched selector of {} to mode {}".format(self.addr, key.events))
|
||||
return key
|
||||
|
||||
def connect(self, client_selector, client_socket, client_addr):
|
||||
self.selector = client_selector
|
||||
self.socket = client_socket
|
||||
self.addr = client_addr
|
||||
|
||||
self._set_selector_events_mask('rw')
|
||||
self._set_selector_events_mask('r')
|
||||
|
||||
def close(self):
|
||||
logger.info("Closing connection to {}".format(self.addr))
|
||||
@@ -269,7 +288,8 @@ class ConnectionManager(object):
|
||||
|
||||
def process_received(self, income_message):
|
||||
message_type = income_message.jsonheader["message-type"]
|
||||
logger.debug("Received message! Header: {}, content: {}".format(income_message.jsonheader, income_message.content))
|
||||
logger.debug(
|
||||
"Received message! Header: {}, content: {}".format(income_message.jsonheader, income_message.content))
|
||||
|
||||
if message_type == "message":
|
||||
self._process_message(income_message)
|
||||
@@ -339,6 +359,8 @@ class ConnectionManager(object):
|
||||
self._send_buffer += message
|
||||
if self._send_buffer:
|
||||
self._write()
|
||||
else:
|
||||
self._set_selector_events_mask('r') # we're done writing
|
||||
|
||||
def _write(self):
|
||||
try:
|
||||
@@ -347,7 +369,8 @@ class ConnectionManager(object):
|
||||
# Resource temporarily unavailable (errno EWOULDBLOCK)
|
||||
pass
|
||||
except Exception as error:
|
||||
logger.warning("Attempt to send message {} to {} failed due error: {}".format(self._send_buffer, self.addr, error))
|
||||
logger.warning(
|
||||
"Attempt to send message {} to {} failed due error: {}".format(self._send_buffer, self.addr, error))
|
||||
|
||||
if not self.resume_queue:
|
||||
self._send_buffer = b''
|
||||
@@ -361,6 +384,10 @@ class ConnectionManager(object):
|
||||
with self._send_lock:
|
||||
self._send_queue.append(data)
|
||||
|
||||
if self.selector.get_key(self.socket).events != selectors.EVENT_WRITE:
|
||||
self._set_selector_events_mask('w')
|
||||
NotifierSock().notify()
|
||||
|
||||
def get_response(self, requested_value, callback, request_args=None, # timeout=30,
|
||||
callback_args=(), callback_kwargs=None):
|
||||
if request_args is None:
|
||||
@@ -397,3 +424,43 @@ class ConnectionManager(object):
|
||||
self._send(MessageManager.create_message(
|
||||
data, "binary", "filetransfer", "binary", {"filepath": dest_filepath}
|
||||
))
|
||||
|
||||
|
||||
class NotifierSock(Singleton):
|
||||
def __init__(self):
|
||||
self.receive_socket = None
|
||||
self.addr = None
|
||||
|
||||
self._notify_socket = None
|
||||
self._notify_lock = threading.Lock()
|
||||
|
||||
def bind(self, server_addr):
|
||||
self._notify_socket = socket.socket()
|
||||
self._notify_socket.connect(server_addr)
|
||||
logger.info("Notify socket: bind")
|
||||
|
||||
def connect(self, _, client_socket, client_addr):
|
||||
self.receive_socket = client_socket
|
||||
self.addr = client_addr
|
||||
|
||||
logger.info("Notify socket: connected")
|
||||
|
||||
def notify(self):
|
||||
with self._notify_lock:
|
||||
if self.addr is not None:
|
||||
self._notify_socket.sendall(bytes(1))
|
||||
logger.debug("Notify socket: notified")
|
||||
|
||||
def process_events(self, mask):
|
||||
if mask & selectors.EVENT_READ:
|
||||
try:
|
||||
data = self.receive_socket.recv(1024)
|
||||
except Exception: # TODO remove
|
||||
pass
|
||||
else:
|
||||
if data:
|
||||
logger.debug("Notifier received {} from {}".format(data, self.addr))
|
||||
else:
|
||||
self.addr = None
|
||||
logger.warning("Notifier: connection to {} lost!".format(self.addr))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user