From 3cfb4a958743709779a935cc2354c92892686636 Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Fri, 22 Nov 2019 08:04:46 +0300 Subject: [PATCH 01/59] Animation update delay (#56) * Client: Make time_delay programmable from animation file * Client: Add yaw parameter in client_config --- Drone/animation_lib.py | 167 +++++++++++++++++++++++----------------- Drone/client_config.ini | 1 + Drone/copter_client.py | 9 ++- 3 files changed, 104 insertions(+), 73 deletions(-) diff --git a/Drone/animation_lib.py b/Drone/animation_lib.py index 6ee6d68..d238f2e 100644 --- a/Drone/animation_lib.py +++ b/Drone/animation_lib.py @@ -4,9 +4,16 @@ import copy import rospy import logging import threading +import ConfigParser -from FlightLib import FlightLib -from FlightLib import LedLib +try: + from FlightLib import FlightLib +except ImportError: + print("Can't import FlightLib") +try: + from FlightLib import LedLib +except ImportError: + print("Can't import LedLib") import tasking_lib as tasking @@ -14,6 +21,11 @@ logger = logging.getLogger(__name__) interrupt_event = threading.Event() +config = ConfigParser.ConfigParser() +config.read("client_config.ini") + +default_delay = config.getfloat('ANIMATION', 'frame_delay') + anim_id = "Empty id" # TODO refactor as class @@ -54,23 +66,20 @@ def get_start_xy(filepath="animation.csv", x_ratio=1, y_ratio=1): animation_file, delimiter=',', quotechar='|' ) try: - row_0 = csv_reader.next() + row_frame = csv_reader.next() + except: + return float('nan'), float('nan') + if len(row_frame) == 1: + anim_id = row_frame[0] + logger.debug("Got animation_id: {}".format(row_frame[0])) + row_frame = csv_reader.next() + if len(row_frame) == 2: + logger.debug("Got frame delay: {}".format(row_frame[1])) + row_frame - csv_reader.next() + try: + frame_number, x, y, z, yaw, red, green, blue = row_frame except: return float('nan'), float('nan') - if len(row_0) == 1: - anim_id = row_0[0] - logger.debug("Got animation_id: {}".format(anim_id)) - try: - frame_number, x, y, z, yaw, red, green, blue = csv_reader.next() - except: - return float('nan'), float('nan') - else: - anim_id = "Empty id" - logger.debug("No animation id in file") - try: - frame_number, x, y, z, yaw, red, green, blue = row_0 - except: - return float('nan'), float('nan') return float(x)*x_ratio, float(y)*y_ratio @@ -84,6 +93,7 @@ def load_animation(filepath="animation.csv", x0=0, y0=0, z0=0, x_ratio=1, y_rati anim_id = "No animation" else: with animation_file: + current_frame_delay = default_delay csv_reader = csv.reader( animation_file, delimiter=',', quotechar='|' ) @@ -91,6 +101,9 @@ def load_animation(filepath="animation.csv", x0=0, y0=0, z0=0, x_ratio=1, y_rati if len(row_0) == 1: anim_id = row_0[0] logger.debug("Got animation_id: {}".format(anim_id)) + elif len(row_0) == 2: + current_frame_delay = float(row_0[1]) + logger.debug("Got new frame delay: {}".format(current_frame_delay)) else: logger.debug("No animation id in file") frame_number, x, y, z, yaw, red, green, blue = row_0 @@ -103,25 +116,31 @@ def load_animation(filepath="animation.csv", x0=0, y0=0, z0=0, x_ratio=1, y_rati 'red': int(red), 'green': int(green), 'blue': int(blue), + 'delay': current_frame_delay }) for row in csv_reader: - frame_number, x, y, z, yaw, red, green, blue = row - imported_frames.append({ - 'number': int(frame_number), - 'x': x_ratio*float(x) + x0, - 'y': y_ratio*float(y) + y0, - 'z': z_ratio*float(z) + z0, - 'yaw': float(yaw), - 'red': int(red), - 'green': int(green), - 'blue': int(blue), - }) + if len(row) == 2: + current_frame_delay = float(row[1]) + else: + frame_number, x, y, z, yaw, red, green, blue = row + imported_frames.append({ + 'number': int(frame_number), + 'x': x_ratio*float(x) + x0, + 'y': y_ratio*float(y) + y0, + 'z': z_ratio*float(z) + z0, + 'yaw': float(yaw), + 'red': int(red), + 'green': int(green), + 'blue': int(blue), + 'delay': current_frame_delay + }) return imported_frames def correct_animation(frames, frame_delay=0.1, min_takeoff_height=0.5, move_delta=0.01, check_takeoff=True, check_land=True): corrected_frames = copy.deepcopy(frames) start_action = 'takeoff' frames_to_start = 0 + time_to_start = 0 if len(corrected_frames) == 0: raise Exception('Nothing to correct!') # Check takeoff @@ -133,9 +152,10 @@ def correct_animation(frames, frame_delay=0.1, min_takeoff_height=0.5, move_delt for i in range(len(corrected_frames)-1): if corrected_frames[i-frames_to_start+1]['z'] - corrected_frames[i-frames_to_start]['z'] > move_delta: break + time_to_start += corrected_frames[i-frames_to_start]['delay'] del corrected_frames[i-frames_to_start] frames_to_start += 1 - start_delay = frames_to_start*frame_delay + start_delay = time_to_start # Check Land # If copter lands in animation, landing points can be deleted if (corrected_frames[len(corrected_frames)-1]['z'] < min_takeoff_height) and check_land: @@ -156,60 +176,65 @@ def save_corrected_animation(frames, filename="corrected_animation.csv"): corrected_animation = open(filename, mode='w+') csv_writer = csv.writer(corrected_animation, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL) for frame in frames: - csv_writer.writerow([frame['number'],frame['x'], frame['y'], frame['z']]) + csv_writer.writerow([frame['number'],frame['x'], frame['y'], frame['z'], frame['delay']]) # print frame corrected_animation.close() def convert_frame(frame): return ((frame['x'], frame['y'], frame['z']), (frame['red'], frame['green'], frame['blue']), frame['yaw']) +try: + def execute_frame(point=(), color=(), yaw=float('Nan'), frame_id='aruco_map', use_leds=True, + flight_func=FlightLib.navto, auto_arm=False, flight_kwargs=None, interrupter=interrupt_event): + if flight_kwargs is None: + flight_kwargs = {} -def execute_frame(point=(), color=(), yaw=float('Nan'), frame_id='aruco_map', use_leds=True, - flight_func=FlightLib.navto, auto_arm=False, flight_kwargs=None, interrupter=interrupt_event): - if flight_kwargs is None: - flight_kwargs = {} - - flight_func(*point, yaw=yaw, frame_id=frame_id, auto_arm=auto_arm, interrupter=interrupt_event, **flight_kwargs) - if use_leds: - if color: - LedLib.fill(*color) + flight_func(*point, yaw=yaw, frame_id=frame_id, auto_arm=auto_arm, interrupter=interrupt_event, **flight_kwargs) + if use_leds: + if color: + LedLib.fill(*color) -def execute_animation(frames, frame_delay, frame_id='aruco_map', use_leds=True, flight_func=FlightLib.navto, - interrupter=interrupt_event): - next_frame_time = 0 - for frame in frames: + + + def execute_animation(frames, frame_delay, frame_id='aruco_map', use_leds=True, flight_func=FlightLib.navto, + interrupter=interrupt_event): + next_frame_time = 0 + for frame in frames: + if interrupter.is_set(): + logger.warning("Animation playing function interrupted!") + interrupter.clear() + return + execute_frame(*convert_frame(frame), frame_id=frame_id, use_leds=use_leds, flight_func=flight_func, + interrupter=interrupter) + + next_frame_time += frame_delay + tasking.wait(next_frame_time, interrupter) + + + def takeoff(z=1.5, safe_takeoff=True, frame_id='map', timeout=5.0, use_leds=True, + interrupter=interrupt_event): + if use_leds: + LedLib.wipe_to(255, 0, 0, interrupter=interrupter) if interrupter.is_set(): - logger.warning("Animation playing function interrupted!") - interrupter.clear() return - execute_frame(*convert_frame(frame), frame_id=frame_id, use_leds=use_leds, flight_func=flight_func, - interrupter=interrupter) - - next_frame_time += frame_delay - tasking.wait(next_frame_time, interrupter) + result = FlightLib.takeoff(height=z, timeout_takeoff=timeout, frame_id=frame_id, + emergency_land=safe_takeoff, interrupter=interrupter) + if result == 'not armed' or result == 'timeout': + raise Exception('STOP') # Raise exception to clear task_manager if copter can't arm + if interrupter.is_set(): + return + if use_leds: + LedLib.blink(0, 255, 0, wait=50, interrupter=interrupter) -def takeoff(z=1.5, safe_takeoff=True, frame_id='map', timeout=5.0, use_leds=True, + def land(z=1.5, descend=False, timeout=5.0, frame_id='aruco_map', use_leds=True, interrupter=interrupt_event): - if use_leds: - LedLib.wipe_to(255, 0, 0, interrupter=interrupter) - if interrupter.is_set(): - return - result = FlightLib.takeoff(height=z, timeout_takeoff=timeout, frame_id=frame_id, - emergency_land=safe_takeoff, interrupter=interrupter) - if result == 'not armed' or result == 'timeout': - raise Exception('STOP') # Raise exception to clear task_manager if copter can't arm - if interrupter.is_set(): - return - if use_leds: - LedLib.blink(0, 255, 0, wait=50, interrupter=interrupter) + if use_leds: + LedLib.blink(255, 0, 0, interrupter=interrupter) + FlightLib.land(z=z, descend=descend, timeout_land=timeout, frame_id_land=frame_id, interrupter=interrupter) + if use_leds: + LedLib.off() - -def land(z=1.5, descend=False, timeout=5.0, frame_id='aruco_map', use_leds=True, - interrupter=interrupt_event): - if use_leds: - LedLib.blink(255, 0, 0, interrupter=interrupter) - FlightLib.land(z=z, descend=descend, timeout_land=timeout, frame_id_land=frame_id, interrupter=interrupter) - if use_leds: - LedLib.off() +except NameError: + print("Can't create flying functions") diff --git a/Drone/client_config.ini b/Drone/client_config.ini index 568a8f6..e2e8249 100644 --- a/Drone/client_config.ini +++ b/Drone/client_config.ini @@ -44,6 +44,7 @@ land_time = 3.0 x0_common = 0 y0_common = 0 z0_common = 0 +yaw = 180 land_timeout = 6.0 [FLOOR FRAME] diff --git a/Drone/copter_client.py b/Drone/copter_client.py index 376cdd8..093ecdd 100644 --- a/Drone/copter_client.py +++ b/Drone/copter_client.py @@ -91,6 +91,7 @@ class CopterClient(client.Client): 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.YAW = self.config.get('COPTERS', 'yaw') 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') @@ -581,7 +582,6 @@ def _play_animation(*args, **kwargs): check_takeoff=client.active_client.TAKEOFF_CHECK, check_land=client.active_client.LAND_CHECK, ) - # Choose start action if start_action == 'takeoff': # Takeoff first @@ -635,16 +635,21 @@ def _play_animation(*args, **kwargs): # Play animation file for frame in corrected_frames: point, color, yaw = animation.convert_frame(frame) + if client.active_client.YAW == "animation": + yaw = frame["yaw"] + else: + yaw = math.radians(float(client.active_client.YAW)) task_manager.add_task(frame_time, 0, animation.execute_frame, task_kwargs={ "point": point, "color": color, + "yaw": yaw, "frame_id": client.active_client.FRAME_ID, "use_leds": client.active_client.USE_LEDS, "flight_func": FlightLib.navto, } ) - frame_time += client.active_client.FRAME_DELAY + frame_time += frame["delay"] # Calculate land_time land_time = frame_time + client.active_client.LAND_TIME From 2839ded41abb1d11caab79688ebb541bd86d985f Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Fri, 22 Nov 2019 11:44:54 +0300 Subject: [PATCH 02/59] tools: Update positions file --- tools/positions.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/positions.txt b/tools/positions.txt index 40e6cd8..6588876 100644 --- a/tools/positions.txt +++ b/tools/positions.txt @@ -1,2 +1,2 @@ -2 6 1 1 --0.6 0 1 +2 6 0.8 1 +-0.7 0 1 From eec82af58f9ea06f2d8eb9f36310a045d0e1edd9 Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Sun, 24 Nov 2019 07:55:58 +0000 Subject: [PATCH 03/59] Client: Add landing if position delta is bigger than value from client_config --- Drone/FlightLib/FlightLib.py | 14 +++++++++++++- Drone/client_config.ini | 7 ++++--- Drone/copter_client.py | 6 ++++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/Drone/FlightLib/FlightLib.py b/Drone/FlightLib/FlightLib.py index abf8e59..44f9094 100644 --- a/Drone/FlightLib/FlightLib.py +++ b/Drone/FlightLib/FlightLib.py @@ -44,6 +44,7 @@ FLIP_MIN_Z = 2.0 checklist = [] get_telemetry_lock = threading.Lock() +delta = 0.0 def get_telemetry_locked(*args, **kwargs): with get_telemetry_lock: @@ -174,12 +175,22 @@ def selfcheck(): return checks +def get_delta(): + global delta + return delta + +def reset_delta(): + global delta + delta = 0 def navto(x, y, z, yaw=float('nan'), frame_id=FRAME_ID, auto_arm=False, **kwargs): + global delta set_position(frame_id=frame_id, x=x, y=y, z=z, yaw=yaw, auto_arm=auto_arm) - #telemetry = get_telemetry_locked(frame_id=frame_id) + telemetry = get_telemetry_locked(frame_id=frame_id) + delta = get_distance3d(x, y, z, telemetry.x, telemetry.y, telemetry.z) logger.info('Going to: | x: {:.3f} y: {:.3f} z: {:.3f} yaw: {:.3f}'.format(x, y, z, yaw)) + #logger.info('Delta: {}'.format(delta)) #print('Going to: | x: {:.3f} y: {:.3f} z: {:.3f} yaw: {:.3f}'.format(x, y, z, yaw)) ##logger.info('Telemetry now: | z: {:.3f}'.format(telemetry.z)) #print('Telemetry now: | z: {:.3f}'.format(telemetry.z)) @@ -272,6 +283,7 @@ def stop(frame_id='body', hold_speed=SPEED): def land(descend=True, z=Z_DESCEND, frame_id_descend=FRAME_ID, frame_id_land=FRAME_ID, timeout_descend=TIMEOUT_DESCEND, timeout_land=TIMEOUT_LAND, freq=FREQUENCY, interrupter=INTERRUPTER): + reset_delta() if descend: logger.info("Descending to: | z: {:.3f}".format(z)) #print("Descending to: | z: {:.3f}".format(z)) diff --git a/Drone/client_config.ini b/Drone/client_config.ini index e2e8249..f95abda 100644 --- a/Drone/client_config.ini +++ b/Drone/client_config.ini @@ -24,6 +24,7 @@ timeout_to_disarm_after_watchdog_action = 10.0 frequency = 1 transmit = True clear_tasks_when_emergency = True +land_if_pos_delta_bigger_than = 3.0 log_cpu_and_memory = True [ANIMATION] @@ -40,18 +41,18 @@ takeoff_height = 1.0 takeoff_time = 5.0 safe_takeoff = False reach_first_point_time = 5.0 -land_time = 3.0 +land_time = 1.0 x0_common = 0 y0_common = 0 z0_common = 0 yaw = 180 -land_timeout = 6.0 +land_timeout = 10.0 [FLOOR FRAME] parent = aruco_map x = 2.4 y = 12.4 -z = 6.27 +z = 6.4 roll = 180 pitch = 0 yaw = -90 diff --git a/Drone/copter_client.py b/Drone/copter_client.py index 093ecdd..49422d0 100644 --- a/Drone/copter_client.py +++ b/Drone/copter_client.py @@ -80,6 +80,7 @@ class CopterClient(client.Client): self.TELEM_TRANSMIT = self.config.getboolean('TELEMETRY', 'transmit') self.CLEAR_TASKS_WHEN_EMERGENCY = self.config.getboolean('TELEMETRY', 'clear_tasks_when_emergency') self.LOG_CPU_AND_MEMORY = self.config.getboolean('TELEMETRY', 'log_cpu_and_memory') + self.LAND_POS_DELTA = self.config.getfloat('TELEMETRY', 'land_if_pos_delta_bigger_than') self.FRAME_ID = self.config.get('COPTERS', 'frame_id') self.FRAME_FLIPPED_HEIGHT = 0. self.TAKEOFF_HEIGHT = self.config.getfloat('COPTERS', 'takeoff_height') @@ -759,6 +760,7 @@ def telemetry_loop(): logger.info("Clear task manager because of {}".format(log_msg)) logger.info("Mode: {} | armed: {} | last task: {} ".format(mode, armed, last_task)) task_manager.reset() + FlightLib.reset_delta() tasks_cleared = True equal_state_counter = 0 else: @@ -780,6 +782,10 @@ def telemetry_loop(): else: cpu_temp_state = 'normal' logger.info("CPU usage: {} | Memory: {} % | T: {} ({}) | Power: {}".format(cpu_usage, mem_usage, cpu_temp, cpu_temp_state, power_state)) + delta = FlightLib.get_delta() + logger.info("Delta: {}".format(delta)) + if delta > client.active_client.LAND_POS_DELTA: + _command_land() rate.sleep() From fe3f5700a430a0afe193930f63b992d6f894a111 Mon Sep 17 00:00:00 2001 From: artem30801 <38689676+artem30801@users.noreply.github.com> Date: Sun, 24 Nov 2019 23:16:19 +0300 Subject: [PATCH 04/59] Removed unnecessary (and not working) yaw angle check --- Drone/FlightLib/FlightLib.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/Drone/FlightLib/FlightLib.py b/Drone/FlightLib/FlightLib.py index 44f9094..238d7c0 100644 --- a/Drone/FlightLib/FlightLib.py +++ b/Drone/FlightLib/FlightLib.py @@ -162,9 +162,6 @@ def check_angles(angle_limit=math.radians(5)): if abs(telemetry.roll) >= angle_limit: yield ("Roll estimation: {:.3f} rad;{:.3f} degrees".format(telemetry.roll, math.degrees(telemetry.roll))) - if abs(telemetry.yaw) >= angle_limit: - yield ("Yaw estimation: {:.3f} rad;{:.3f} degrees".format(telemetry.yaw, - math.degrees(telemetry.yaw))) def selfcheck(): From 2afa294cc4bfaa4fcfecc31b9d1f17d0452306dc Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Thu, 28 Nov 2019 15:22:10 +0300 Subject: [PATCH 05/59] Client: update default config --- Drone/client_config.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Drone/client_config.ini b/Drone/client_config.ini index f95abda..251c7ad 100644 --- a/Drone/client_config.ini +++ b/Drone/client_config.ini @@ -36,7 +36,7 @@ y_ratio = 1.0 z_ratio = 1.0 [COPTERS] -frame_id = floor +frame_id = map takeoff_height = 1.0 takeoff_time = 5.0 safe_takeoff = False @@ -60,7 +60,7 @@ yaw = -90 [PRIVATE] id = /hostname restart_dhcpcd = True -use_leds = False +use_leds = True led_pin = 21 x0 = 0 y0 = 0 From 3b2ef72ec9986c92ad2f723330f271d6f9ee24a9 Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Thu, 28 Nov 2019 15:24:44 +0300 Subject: [PATCH 06/59] builder: Add psutils module --- builder/image-software.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/builder/image-software.sh b/builder/image-software.sh index cc21aea..f456a11 100755 --- a/builder/image-software.sh +++ b/builder/image-software.sh @@ -61,6 +61,7 @@ chrony \ echo_stamp "Install python libs" my_travis_retry pip install selectors2 +my_travis_retry pip install psutil echo_stamp "Install catkin packages" cd /home/pi/catkin_ws/src From 9c518a81091b88f770b05449a0ac9a00001e3d91 Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Thu, 28 Nov 2019 15:40:07 +0300 Subject: [PATCH 07/59] Client: Add visual_pose_service setup --- Drone/client_setup.sh | 3 ++- builder/image-build.sh | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Drone/client_setup.sh b/Drone/client_setup.sh index bf5bd26..0905ab7 100755 --- a/Drone/client_setup.sh +++ b/Drone/client_setup.sh @@ -71,8 +71,9 @@ EOF # change server ip in client_config sed -i "0,/^host/s/\(^h.*\)/host = $4/" client_config.ini -# enable clever show service +# enable clever show service and visual_pose_watchdog service systemctl enable clever-show.service +systemctl enable visual_pose_watchdog.service # restart clever reboot \ No newline at end of file diff --git a/builder/image-build.sh b/builder/image-build.sh index aa802d2..fb0e137 100755 --- a/builder/image-build.sh +++ b/builder/image-build.sh @@ -116,8 +116,9 @@ img-chroot ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-software.sh' # Configure image img-chroot ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-configure.sh' -# Copy service file for clever show client +# Copy service files for clever show client and visual_pose_watchdog img-chroot ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/clever-show.service' '/lib/systemd/system/' +img-chroot ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/visual_pose_watchdog.service' '/lib/systemd/system/' # Copy config files for clever if [[ -d "${CONFIG_DIR}/launch" ]]; then img-chroot ${IMAGE_PATH} copy ${CONFIG_DIR}'/launch' '/home/pi/catkin_ws/src/clever/clever'; fi From 7e8e97f88c66dceeefe3f5ba7d6d3d4c1ba7dd86 Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Thu, 28 Nov 2019 17:50:35 +0300 Subject: [PATCH 08/59] Update client_setup to allow connection to hidden ssids --- Drone/client_setup.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/Drone/client_setup.sh b/Drone/client_setup.sh index 0905ab7..81f1496 100755 --- a/Drone/client_setup.sh +++ b/Drone/client_setup.sh @@ -38,6 +38,7 @@ country=GB network={ ssid="$1" psk="$2" + scan_ssid=1 } EOF From 53dad0e3fd9582febc76f1ec26660911ba9490a1 Mon Sep 17 00:00:00 2001 From: Oleg Kalachev Date: Tue, 3 Dec 2019 01:53:17 +0300 Subject: [PATCH 09/59] Add demo video to readme (#57) --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 7ec58c2..31f9d92 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,12 @@ Software for making the drone show controlled by Raspberry Pi and COEX [Clever]( [![Build Status](https://travis-ci.org/CopterExpress/clever-show.svg?branch=master)](https://travis-ci.org/CopterExpress/clever-show) +## Demo video + +[![Autonomous drone show in a theater](http://img.youtube.com/vi/HdHbZFz7nR0/0.jpg)](http://www.youtube.com/watch?v=HdHbZFz7nR0) + +12 drones perform in a show in Electrotheatre Stanislavsky, Moscow. + ## 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 From ce36c6f1e37be8044dfd2f8ff5a356221b7d6674 Mon Sep 17 00:00:00 2001 From: artem30801 <38689676+artem30801@users.noreply.github.com> Date: Thu, 5 Dec 2019 15:10:21 +0300 Subject: [PATCH 10/59] Feature branch: IMPORTANT connection+telemetry+table fixes and improvements (#55) * .client_connected > .new_client_connected * Fixed 'confirmation_required' wrapper * Logging impr * Changed and optimized a lot checks behaviour * Added indication of connected/disconnected copters * update_data_signal changed signature * Added client removing functionality * Option for automatically remove disconnected copters from table * Renaming copters from QT server table on the go + some improvements * Server: Check if self.clients list is not empty when trying to pop element from it * Probably fixes behaviour of non-immidiate data sending from server * Added changing hostname of copter * Updated config * Preview of selfchecheck results on double click * Delete doc_2019-10-16_17-57-17.bashrc * Update table data models for selfcheck * Server: modify set id request to message * Update client_config default file * Client: modify set new id function * Client: add avahi-daemon to restart when restarting network * Client: add new hostname to ssh motd message, do not change hostname if no network restart in config * Client: add newline to motd message * Optimized request behaviour * Client: fix service file and restart order * Client: Add SO_KEEPALIVE and TCP_NODELAY options to client socket * Modify to last tests with ping * Client: remove ping * Client: select reboot option when change id and add execute command * Server: Add SO_KEEPALIVE option to server socket * Server: Change removing copter * Request resending after disconnection * Resending improval (for furthrer functionality & fixes * Fix of client removing behaviour * Debugging * Revert dubug code; 'Remove' fix confirmed * do not clear requests queue * Update requirements.txt * Added namespace class to fix resend * Improvements and simplification of notifier + port to client * Refactor of telemetry thread * Simplify lambdas * Compress hostname check to single regex * Changes in telemetry * Refactored formatting of telemetry in table. NOT DONE * Fix * Git checkout. REVERT later! * Conection fix * Compability fixes * Update start position * Fix for reconnection with notifier socket * Added traceback for pyqt5 * Fixes in new telemetry display * Added lock to Telemetry * Fixes for table display * Fix of doubling line of client in table * Fix of mass-removing clients from table * Fix for clinet double-connection+removal * Fix lock in Telemetry * Changed signature of response callbacks for better syntax & fixes (all tested) * Revert "Git checkout. REVERT later!" This reverts commit 6122352380b8b973957640d3f4f9cfb113ffeee9. * Server: fix formatters * Client: Remove telemetry_loop, small refactor of Telemetry class * Server: Add formatters * Server: Very small refactor * Server: Fix checks and formatters * Client: Fix check_failsafe function, small code refactor * Client: update default config file --- Drone/client.py | 9 +- Drone/client_config.ini | 1 - Drone/copter_client.py | 363 ++++++++++++++++++++------------- Drone/mavros_mavlink.py | 4 +- Server/copter_table_models.py | 371 +++++++++++++++++++++------------- Server/server.py | 31 +-- Server/server_qt.py | 180 +++++++++-------- messaging_lib.py | 100 +++++---- requirements.txt | 1 + 9 files changed, 635 insertions(+), 425 deletions(-) diff --git a/Drone/client.py b/Drone/client.py index a52e704..5d37e16 100644 --- a/Drone/client.py +++ b/Drone/client.py @@ -105,6 +105,8 @@ class Client(object): def start(self): logger.info("Starting client") + messaging.NotifierSock().init(self.selector) + try: while True: self._reconnect() @@ -195,6 +197,7 @@ class Client(object): # self.server_connection.send_message("ping") # self._last_ping_time = time.time() # logging.debug("tick") + for key, mask in events: # TODO add notifier to client! connection = key.data if connection is None: @@ -214,8 +217,10 @@ class Client(object): if error.errno == errno.EINTR: raise KeyboardInterrupt - - if not self.selector.get_map(): + mapping = self.selector.get_map().values() + notifier_key = self.selector.get_key(messaging.NotifierSock().get_sock()) + notify_only= len(mapping) == 1 and notifier_key in mapping + if notify_only or not mapping: logger.warning("No active connections left!") return diff --git a/Drone/client_config.ini b/Drone/client_config.ini index 251c7ad..cc0e86f 100644 --- a/Drone/client_config.ini +++ b/Drone/client_config.ini @@ -23,7 +23,6 @@ timeout_to_disarm_after_watchdog_action = 10.0 [TELEMETRY] frequency = 1 transmit = True -clear_tasks_when_emergency = True land_if_pos_delta_bigger_than = 3.0 log_cpu_and_memory = True diff --git a/Drone/copter_client.py b/Drone/copter_client.py index 49422d0..148215a 100644 --- a/Drone/copter_client.py +++ b/Drone/copter_client.py @@ -29,11 +29,8 @@ from tf.transformations import quaternion_from_euler, euler_from_quaternion, qua import tf2_ros static_bloadcaster = tf2_ros.StaticTransformBroadcaster() -Telemetry = namedtuple("Telemetry", "git_version animation_id battery_v battery_p system_status calibration_status mode selfcheck current_position start_position armed") -telemetry = Telemetry('nan', 'No animation', 'nan', 'nan', 'NO_FCU', 'NO_FCU', 'NO_FCU', 'NO_FCU', 'NO_POS', 'NO_POS', False) -emergency = False -# get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry) +emergency = False logging.basicConfig( # TODO all prints as logs level=logging.DEBUG, # INFO @@ -78,7 +75,6 @@ class CopterClient(client.Client): super(CopterClient, self).load_config() self.TELEM_FREQ = self.config.getfloat('TELEMETRY', 'frequency') self.TELEM_TRANSMIT = self.config.getboolean('TELEMETRY', 'transmit') - self.CLEAR_TASKS_WHEN_EMERGENCY = self.config.getboolean('TELEMETRY', 'clear_tasks_when_emergency') self.LOG_CPU_AND_MEMORY = self.config.getboolean('TELEMETRY', 'log_cpu_and_memory') self.LAND_POS_DELTA = self.config.getfloat('TELEMETRY', 'land_if_pos_delta_bigger_than') self.FRAME_ID = self.config.get('COPTERS', 'frame_id') @@ -134,7 +130,8 @@ class CopterClient(client.Client): else: rospy.logerror("Can't make floor frame!") start_subscriber() - telemetry_thread.start() + + telemetry.start_loop() super(CopterClient, self).start() def start_floor_frame_broadcast(self): @@ -307,9 +304,10 @@ def _response_selfcheck(*args, **kwargs): stop_subscriber() return "NOT_CONNECTED_TO_FCU" + @messaging.request_callback("telemetry") def _response_telemetry(*args, **kwargs): - return create_telemetry_message(telemetry) + return telemetry.create_msg_contents() @messaging.request_callback("anim_id") @@ -663,148 +661,239 @@ def _play_animation(*args, **kwargs): }, ) -def telemetry_loop(): - global telemetry, emergency - last_state = [] - equal_state_counter = 0 - max_count = 2 - tasks_cleared = False - rate = rospy.Rate(client.active_client.TELEM_FREQ) - while not rospy.is_shutdown(): - telemetry = telemetry._replace(animation_id = animation.get_id()) - telemetry = telemetry._replace(git_version = subprocess.check_output("git log --pretty=format:'%h' -n 1", shell=True)) +class Telemetry: + params_default_dict = { + "git_version": None, + "animation_id": None, + "battery": None, + "armed": False, + "system_status": None, + "calibration_status": None, + "mode": None, + "selfcheck": None, + "current_position": None, + "start_position": None, + "time": None, + } + + def __init__(self): + self._lock = threading.Lock() + self._last_state = [] + self._interruption_counter = 0 + self._max_interruptions = 2 + self._tasks_cleared = False + + for key, value in self.params_default_dict.items(): + setattr(self, key, value) + + def __setattr__(self, key, value): + if key in self.params_default_dict: + with self.__dict__['_lock']: + self.__dict__[key] = value + else: + self.__dict__[key] = value + + def __getattr__(self, item): + if item in self.params_default_dict: + with self.__dict__['_lock']: + return self.__dict__[item] + + return self.__dict__[item] + + @classmethod + def get_git_version(cls): + return subprocess.check_output("git log --pretty=format:'%h' -n 1", shell=True) + + @classmethod + def get_start_position(cls): x_start, y_start = animation.get_start_xy(os.path.abspath("animation.csv"), - x_ratio=client.active_client.X_RATIO, - y_ratio=client.active_client.Y_RATIO, - ) + x_ratio=client.active_client.X_RATIO, + y_ratio=client.active_client.Y_RATIO, + ) x_delta = client.active_client.X0 + client.active_client.X0_COMMON y_delta = client.active_client.Y0 + client.active_client.Y0_COMMON z_delta = client.active_client.Z0 + client.active_client.Z0_COMMON - if not math.isnan(x_start): - telemetry = telemetry._replace(start_position = '{:.2f} {:.2f} {:.2f}'.format(x_start+x_delta, y_start+y_delta, z_delta)) - else: - telemetry = telemetry._replace(start_position = 'NO_POS') - services_unavailable = FlightLib.check_ros_services_unavailable() - if not services_unavailable: - try: - ros_telemetry = FlightLib.get_telemetry_locked(client.active_client.FRAME_ID) - if ros_telemetry.connected: - telemetry = telemetry._replace(armed = ros_telemetry.armed) - telemetry = telemetry._replace(battery_v = '{:.2f}'.format(ros_telemetry.voltage)) - batt_empty_param = get_param('BAT_V_EMPTY') - batt_charged_param = get_param('BAT_V_CHARGED') - batt_cells_param = get_param('BAT_N_CELLS') - if batt_empty_param.success and batt_charged_param.success and batt_cells_param.success: - batt_empty = batt_empty_param.value.real - batt_charged = batt_charged_param.value.real - batt_cells = batt_cells_param.value.integer - try: - telemetry = telemetry._replace(battery_p = '{}'.format(int(min((ros_telemetry.voltage/batt_cells - batt_empty)/(batt_charged - batt_empty)*100., 100)))) - except ValueError: - telemetry = telemetry._replace(battery_p = 'nan') - else: - telemetry = telemetry._replace(battery_p = 'nan') - telemetry = telemetry._replace(calibration_status = get_calibration_status()) - telemetry = telemetry._replace(system_status = get_sys_status()) - telemetry = telemetry._replace(mode = ros_telemetry.mode) - check = FlightLib.selfcheck() - if not check: - check = "OK" - telemetry = telemetry._replace(selfcheck = str(check)) - if not math.isnan(ros_telemetry.x): - telemetry = telemetry._replace(current_position = '{:.2f} {:.2f} {:.2f} {:.1f} {}'.format(ros_telemetry.x, ros_telemetry.y, ros_telemetry.z, - math.degrees(ros_telemetry.yaw), client.active_client.FRAME_ID)) - else: - telemetry = telemetry._replace(current_position = 'NO_POS in {}'.format(client.active_client.FRAME_ID)) - else: - telemetry = telemetry._replace(battery_v = 'nan') - telemetry = telemetry._replace(battery_p = 'nan') - telemetry = telemetry._replace(calibration_status = 'NO_FCU') - telemetry = telemetry._replace(system_status = 'NO_FCU') - telemetry = telemetry._replace(mode = 'NO_FCU') - telemetry = telemetry._replace(selfcheck = 'NO_FCU') - telemetry = telemetry._replace(current_position = 'NO_POS') - except rospy.ServiceException: - logger.debug("Some service is unavailable") - except AttributeError as e: - logger.debug(e) - except rospy.TransportException as e: - logger.debug(e) - else: - telemetry = telemetry._replace(selfcheck = 'WAIT_ROS') - if client.active_client.TELEM_TRANSMIT: - try: - client.active_client.server_connection.send_message('telem', args={'message':create_telemetry_message(telemetry)}) - except AttributeError as e: - logger.debug(e) - if client.active_client.CLEAR_TASKS_WHEN_EMERGENCY: - mode = telemetry.mode - armed = telemetry.armed - last_task = task_manager.get_last_task_name() - state = [mode, armed, last_task] - if state == last_state: - equal_state_counter += 1 - else: - equal_state_counter = 0 - external_interruption = (mode != "OFFBOARD" and armed == True and last_task not in [None, 'land']) - log_msg = '' - if emergency and external_interruption: - log_msg = "emergency and external interruption" - elif emergency: - log_msg = "emergency" - elif external_interruption: - log_msg = "external interruption" - logger.info("Possible expernal interruption, state_counter = {}".format(equal_state_counter)) - if emergency or (external_interruption and equal_state_counter >= max_count): - if not tasks_cleared: - logger.info("Clear task manager because of {}".format(log_msg)) - logger.info("Mode: {} | armed: {} | last task: {} ".format(mode, armed, last_task)) - task_manager.reset() - FlightLib.reset_delta() - tasks_cleared = True - equal_state_counter = 0 - else: - tasks_cleared = False - last_state = state - if client.active_client.LOG_CPU_AND_MEMORY: - cpu_usage = psutil.cpu_percent(interval=None, percpu=True) - mem_usage = psutil.virtual_memory().percent - cpu_temp_info = psutil.sensors_temperatures()['cpu-thermal'][0] - cpu_temp = cpu_temp_info.current - # https://github.com/raspberrypi/documentation/blob/JamesH65-patch-vcgencmd-vcdbg-docs/raspbian/applications/vcgencmd.md - throttled_hex = subprocess.check_output("vcgencmd get_throttled", shell=True).split('=')[1] - under_voltage = bool(int(bin(int(throttled_hex,16))[2:][-1])) - power_state = 'normal' if not under_voltage else 'under voltage!' - if cpu_temp_info.critical: - cpu_temp_state = 'critical' - elif cpu_temp_info.high: - cpu_temp_state = 'high' - else: - cpu_temp_state = 'normal' - logger.info("CPU usage: {} | Memory: {} % | T: {} ({}) | Power: {}".format(cpu_usage, mem_usage, cpu_temp, cpu_temp_state, power_state)) - delta = FlightLib.get_delta() - logger.info("Delta: {}".format(delta)) - if delta > client.active_client.LAND_POS_DELTA: - _command_land() - rate.sleep() + x = x_start + x_delta + y = y_start + y_delta + if not FlightLib._check_nans(x, y, z_delta): + return x, y, z_delta + return 'NO_POS' -def create_telemetry_message(telemetry): - msg = client.active_client.client_id + '`' - for key in telemetry.__dict__: - if key != 'armed': - msg += telemetry.__dict__[key] + '`' - msg += repr(time.time()) - return msg + @classmethod + def get_battery(cls, ros_telemetry): + battery_v = ros_telemetry.voltage + + batt_empty_param = get_param('BAT_V_EMPTY') + batt_charged_param = get_param('BAT_V_CHARGED') + batt_cells_param = get_param('BAT_N_CELLS') + + if batt_empty_param.success and batt_charged_param.success and batt_cells_param.success: + batt_empty = batt_empty_param.value.real + batt_charged = batt_charged_param.value.real + batt_cells = batt_cells_param.value.integer + + battery_p = (ros_telemetry.voltage / batt_cells - batt_empty) / (batt_charged - batt_empty) * 1. + battery_p = max(min(battery_p, 1.), 0.) + else: + battery_p = float('nan') + + return battery_v, battery_p + + @classmethod + def get_selfcheck(cls): + check = FlightLib.selfcheck() + if not check: + check = "OK" + return check + + @classmethod + def get_position(cls, ros_telemetry): + x, y, z = ros_telemetry.x, ros_telemetry.y, ros_telemetry.z + if not math.isnan(x): + return x, y, z, math.degrees(ros_telemetry.yaw), client.active_client.FRAME_ID + return 'NO_POS' + + def update_telemetry(self): + self.animation_id = animation.get_id() + self.git_version = self.get_git_version() + self.start_position = self.get_start_position() + try: + ros_telemetry = FlightLib.get_telemetry_locked(client.active_client.FRAME_ID) + if ros_telemetry.connected: + self.battery = self.get_battery(ros_telemetry) + self.armed = ros_telemetry.armed + self.calibration_status = get_calibration_status() + self.system_status = get_sys_status() + self.mode = ros_telemetry.mode + self.selfcheck = self.get_selfcheck() + self.current_position = self.get_position(ros_telemetry) + else: + self.reset_telemetry_values() + except rospy.ServiceException: + rospy.logdebug("Some service is unavailable") + self.selfcheck = ["WAIT_ROS"] + except AttributeError as e: + rospy.logdebug(e) + except rospy.TransportException as e: + rospy.logdebug(e) + self.time = time.time() + + def round_telemetry(self): + round_list = ["battery", "start_position", "current_position"] + for key in round_list: + if self.__dict__[key] not in [None, 'NO_POS', 'NO_FCU']: + self.__dict__[key] = [round(v,2) if type(v) == float else v for v in self.__dict__[key]] + + def reset_telemetry_values(self): + self.battery = float('nan'), float('nan') + self.calibration_status = 'NO_FCU' + self.system_status = 'NO_FCU' + self.mode = 'NO_FCU' + self.selfcheck = ['NO_FCU'] + self.current_position = 'NO_POS' + + def check_failsafe(self): + global emergency + # check current state + state = [self.mode, self.armed, task_manager.get_last_task_name()] + mode, armed, last_task = state + # check external interruption + external_interruption = (mode != "OFFBOARD" and armed == True and last_task not in [None, 'land']) + log_msg = '' + if emergency: + log_msg += 'emergency and ' + if external_interruption: + log_msg += 'external interruption and ' + # count interruptions to avoid px4 mode glitches + if state == self._last_state: + self._interruption_counter += 1 + else: + self._interruption_counter = 0 + logger.info("Possible expernal interruption, state_counter = {}".format(self._interruption_counter)) + # delete last ' end ' from log message + if len(log_msg) > 5: + log_msg = log_msg[:-5] + # clear task manager if emergency or external interruption + if emergency or (external_interruption and self._interruption_counter >= self._max_interruptions): + if not self._tasks_cleared: + logger.info("Clear task manager because of {}".format(log_msg)) + logger.info("Mode: {} | armed: {} | last task: {} ".format(mode, armed, last_task)) + task_manager.reset() + FlightLib.reset_delta() + self._tasks_cleared = True + self._interruption_counter = 0 + else: + self._tasks_cleared = False + self._last_state = state + # check position delta + if not emergency: + delta = FlightLib.get_delta() + if delta > client.active_client.LAND_POS_DELTA: + logger.info("Delta: {}".format(delta)) + _command_land() + + def transmit_message(self): + try: + client.active_client.server_connection.send_message('telemetry', args={'value': self.create_msg_contents()}) + except AttributeError as e: + logger.debug(e) + + @classmethod + def log_cpu_and_memory(cls): + cpu_usage = psutil.cpu_percent(interval=None, percpu=True) + mem_usage = psutil.virtual_memory().percent + cpu_temp_info = psutil.sensors_temperatures()['cpu-thermal'][0] + cpu_temp = cpu_temp_info.current + # https://github.com/raspberrypi/documentation/blob/JamesH65-patch-vcgencmd-vcdbg-docs/raspbian/applications/vcgencmd.md + throttled_hex = subprocess.check_output("vcgencmd get_throttled", shell=True).split('=')[1] + under_voltage = bool(int(bin(int(throttled_hex,16))[2:][-1])) + power_state = 'normal' if not under_voltage else 'under voltage!' + if cpu_temp_info.critical: + cpu_temp_state = 'critical' + elif cpu_temp_info.high: + cpu_temp_state = 'high' + else: + cpu_temp_state = 'normal' + logger.info("CPU usage: {} | Memory: {} % | T: {} ({}) | Power: {}".format( + cpu_usage, mem_usage, cpu_temp, cpu_temp_state, power_state)) + + def _update_loop(self, freq): # TODO extract? + rate = rospy.Rate(freq) + while not rospy.is_shutdown(): + + self.update_telemetry() + self.round_telemetry() + self.check_failsafe() + + if client.active_client.TELEM_TRANSMIT and client.active_client.connected: + self.transmit_message() + + if client.active_client.LOG_CPU_AND_MEMORY: + self.log_cpu_and_memory() + + rate.sleep() + + def start_loop(self): + if client.active_client.TELEM_FREQ > 0: + telemetry_thread = threading.Thread(target=self._update_loop, name="Telemetry getting thread", + args=(client.active_client.TELEM_FREQ,)) # TODO MOVE? Daemon? + telemetry_thread.start() + else: + logger.info("Don't create telemetry loop because of zero or negative telemetry frequency") + + def create_msg_contents(self, keys=None): # keys: set or list + if keys is None: + keys = self.params_default_dict.keys() + # return only existing keys from 'keys' + return {k: self.__dict__[k] for k in keys if k in self.params_default_dict} def emergency_callback(data): global emergency emergency = data.data -telemetry_thread = threading.Thread(target=telemetry_loop, name="Telemetry getting thread") - if __name__ == "__main__": - + telemetry = Telemetry() copter_client = CopterClient() task_manager = tasking.TaskManager() rospy.Subscriber('/emergency', Bool, emergency_callback) diff --git a/Drone/mavros_mavlink.py b/Drone/mavros_mavlink.py index 3b093da..7a6005e 100644 --- a/Drone/mavros_mavlink.py +++ b/Drone/mavros_mavlink.py @@ -108,9 +108,9 @@ def get_sys_status(): mavlink.MAV_STATE_EMERGENCY: "EMERGENCY", mavlink.MAV_STATE_POWEROFF: "POWEROFF", mavlink.MAV_STATE_FLIGHT_TERMINATION: "TERMINATION" - }.get(system_status, "NOT_CONNECTED_TO_FCU") + }.get(system_status, "NO_FCU") return status_text - return "NOT_CONNECTED_TO_FCU" + return "NO_FCU" def start_subscriber(): global heartbeat_sub, heartbeat_sub_status diff --git a/Server/copter_table_models.py b/Server/copter_table_models.py index 977cdfc..1d9e62e 100644 --- a/Server/copter_table_models.py +++ b/Server/copter_table_models.py @@ -1,10 +1,10 @@ -import sys import re +import sys +import time import math import configparser import collections import indexed -from server import ConfigOption from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtCore import Qt as Qt @@ -16,11 +16,100 @@ ModelStateRole = 999 config = configparser.ConfigParser() config.read("server_config.ini") +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 ModelChecks: + checks_dict = {} + takeoff_checklist = (3, 4, 6, 7, 8) + + @classmethod + def col_check(cls, col): + def inner(f): + def wrapper(item): + if item is not None: + return f(item) + return None + + cls.checks_dict[col] = wrapper + return wrapper + + return inner + + @classmethod + def all_checks(cls, copter_item): + for col, check in cls.checks_dict.items(): + if not check(copter_item[col]): + return False + return True + + @classmethod + def takeoff_checks(cls, copter_item): + for col in cls.takeoff_checklist: + if not cls.checks_dict[col](copter_item[col]): + return False + return True + + +@ModelChecks.col_check(1) +def check_ver(item): + return True # TODO git version! + + +@ModelChecks.col_check(2) +def check_anim(item): + return str(item) != 'No animation' + + +@ModelChecks.col_check(3) +def check_bat(item): + if item == "NO_INFO": + return False + return item[1]*100 > battery_min + + +@ModelChecks.col_check(4) +def check_sys_status(item): + return item == "STANDBY" + + +@ModelChecks.col_check(5) +def check_cal_status(item): + return item == "OK" + + +@ModelChecks.col_check(6) +def check_mode(item): + return (item != "NO_FCU") and not ("CMODE" in item) + + +@ModelChecks.col_check(7) +def check_selfcheck(item): + return item == "OK" + + +@ModelChecks.col_check(8) +def check_pos_status(item): + if item == 'NO_POS': + return False + return not math.isnan(item[0]) + + +@ModelChecks.col_check(9) +def check_start_pos_status(item): + return item != 'NO_POS' + + +@ModelChecks.col_check(10) +def check_time_delta(item): + return abs(item) < time_delta_max + class CopterData: class_basic_attrs = indexed.IndexedOrderedDict([('copter_id', None), ('git_ver', None), ('anim_id', None), ('battery', None), ('sys_status', None), ('cal_status', None), - ('mode', None), ('selfcheck', None), ('position', None), + ('mode', None), ('selfcheck', None), ('position', None), ('start_pos', None), ('time_delta', None), ('client', None)]) def __init__(self, **kwargs): @@ -41,8 +130,9 @@ class StatedCopterData(CopterData): class_basic_states = indexed.IndexedOrderedDict([("checked", 0), ("selfchecked", None), ("takeoff_ready", None), ("copter_id", True), ]) - def __init__(self, **kwargs): + def __init__(self, checks_class=ModelChecks, **kwargs): self.states = CopterData(**self.class_basic_states) + self.checks = ModelChecks super(StatedCopterData, self).__init__(**kwargs) @@ -52,31 +142,30 @@ class StatedCopterData(CopterData): if key in self.class_basic_attrs.keys(): try: self.states.__dict__[key] = \ - Checks.all_checks[self.attrs_dict.keys().index(key)](value) + ModelChecks.checks_dict[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) + self.states.__dict__[key] = (delta < start_pos_delta_max) except KeyError: # No check present for that col pass else: # update selfchecked and takeoff_ready self.states.__dict__["selfchecked"] = all( - [self.states[i] for i in Checks.all_checks.keys()] + [self.states[i] for i in ModelChecks.checks_dict.keys()] ) self.states.__dict__["takeoff_ready"] = all( - [self.states[i] for i in Checks.takeoff_checklist] + [self.states[i] for i in ModelChecks.takeoff_checklist] ) -def get_position(pos_string): - pos = [] - pos_str = pos_string.split(' ') - if pos_str[0] != 'nan' and pos_str[0] != 'NO_POS': +def get_position(pos_array): + if pos_array[0] != 'nan' and pos_array != 'NO_POS': + pos = [] for i in range(3): - pos.append(float(pos_str[i])) + pos.append(pos_array[i]) else: pos = 'NO_POS' return pos @@ -91,27 +180,120 @@ def get_position_delta(pos1, pos2): return 'NO_POS' -class Checks: - all_checks = {} - takeoff_checklist = (3, 4, 6, 7, 8) - 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 ModelFormatter: + view_formatters = {} + place_formatters = {} + VIEW_FORMATTER = False + PLACE_FORMATTER = True + + @classmethod + def format_view(cls, col, value): + if col in cls.view_formatters: + return cls.view_formatters[col](value) + return value + + @classmethod + def format_place(cls, col, value): + if col in cls.place_formatters: + return cls.place_formatters[col](value) + return value + + @classmethod + def col_format(cls, col, format_type): + def inner(f): + if format_type: + cls.place_formatters[col] = f + else: + cls.view_formatters[col] = f + + def wrapper(*args, **kwargs): + return f(*args, **kwargs) + + return wrapper + + return inner + + +@ModelFormatter.col_format(0, ModelFormatter.PLACE_FORMATTER) +def place_id(value): + value = value.stip() + # check user hostname spelling http://man7.org/linux/man-pages/man7/hostname.7.html + # '-' (hyphen) not first; latin letters/numbers/hyphens; length form 1 to 63 + # or matches command pattern + if re.match("^(?!-)[A-Za-z0-9-]{1,63}$", value) or re.match("^/[A-Za-z0-9]*$", value): + return value + else: + msgbox = QtWidgets.QMessageBox() + msgbox.setWindowTitle("Wrong input for the copter name!") + msgbox.setIcon(QtWidgets.QMessageBox.Critical) + msgbox.setText( + "Wrong input for the copter name!\n" + "Please use only A-Z, a-z, 0-9, and '-' chars.\n" + "Don't use '-' as first char.") + msgbox.exec_() + return None + + +@ModelFormatter.col_format(3, ModelFormatter.PLACE_FORMATTER) +def place_battery(value): + if isinstance(value, list): + battery_v, battery_p = value + if math.isnan(battery_v) or math.isnan(battery_p): + return "NO_INFO" + return value + + +@ModelFormatter.col_format(3, ModelFormatter.VIEW_FORMATTER) +def view_battery(value): + if isinstance(value, list): + battery_v, battery_p = value + return "{:.1f}V {:d}%".format(battery_v, int(battery_p*100)) + return value + +@ModelFormatter.col_format(7, ModelFormatter.VIEW_FORMATTER) +def view_selfcheck(value): + if isinstance(value, list): + return "ERROR" + return value + +@ModelFormatter.col_format(8, ModelFormatter.VIEW_FORMATTER) +def view_selfcheck(value): + if isinstance(value, list): + x, y, z, yaw, frame = value + return "{:.2f} {:.2f} {:.2f} {:d} {}".format(x, y, z, int(yaw), frame) + return value + +@ModelFormatter.col_format(9, ModelFormatter.VIEW_FORMATTER) +def view_selfcheck(value): + if isinstance(value, list): + x, y, z = value + return "{:.2f} {:.2f} {:.2f}".format(x, y, z) + return value + +@ModelFormatter.col_format(10, ModelFormatter.PLACE_FORMATTER) +def place_time_delta(value): + return abs(value - time.time()) + + +@ModelFormatter.col_format(10, ModelFormatter.VIEW_FORMATTER) +def view_time_delta(value): + return "{:.3f}".format(value) class CopterDataModel(QtCore.QAbstractTableModel): selected_ready_signal = QtCore.pyqtSignal(bool) selected_takeoff_ready_signal = QtCore.pyqtSignal(bool) - selected_flip_ready_signal = QtCore.pyqtSignal(bool) + selected_flip_ready_signal = QtCore.pyqtSignal(bool) # TODO fix this signals selected_calibrating_signal = QtCore.pyqtSignal(bool) selected_calibration_ready_signal = QtCore.pyqtSignal(bool) - def __init__(self, parent=None): + def __init__(self, checks=ModelChecks, formatter=ModelFormatter, parent=None): super(CopterDataModel, self).__init__(parent) self.headers = ('copter ID', 'version', ' animation ID ', ' battery ', ' system ', 'sensors', ' mode ', 'checks', 'current x y z yaw frame_id', ' start x y z ', 'dt') self.data_contents = [] - self.on_id_changed = None + self.checks = checks + self.formatter = formatter self.first_col_is_checked = False @@ -144,15 +326,15 @@ class CopterDataModel(QtCore.QAbstractTableModel): def flip_ready(self, contents=()): contents = contents or self.data_contents - return filter(lambda x: flip_checks(x), contents) # possibly change as takeoff checks + return filter(flip_checks, contents) # possibly change as takeoff checks def calibrating(self, contents=()): contents = contents or self.data_contents - return filter(lambda x: calibrating_check(x), contents) + return filter(calibrating_check, contents) def calibration_ready(self, contents=()): contents = contents or self.data_contents - return filter(lambda x: calibration_ready_check(x), contents) + return filter(calibration_ready_check, contents) def get_row_index(self, row_data): try: @@ -186,7 +368,7 @@ class CopterDataModel(QtCore.QAbstractTableModel): col = index.column() if role == Qt.DisplayRole or role == Qt.EditRole: # Separate editRole in case of editing non-text item = self.data_contents[row][col] - return str(item) if item is not None else "" + return str(self.formatter.format_view(col, item)) if item is not None else "" elif role == ModelDataRole: return self.data_contents[row][col] @@ -208,7 +390,7 @@ class CopterDataModel(QtCore.QAbstractTableModel): return self.data_contents[row].states.checked if role == QtCore.Qt.TextAlignmentRole and col != 0: - return QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter + return QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter def update_model(self, index=QtCore.QModelIndex(), role=QtCore.Qt.EditRole): selected = set(self.user_selected()) @@ -232,19 +414,14 @@ class CopterDataModel(QtCore.QAbstractTableModel): if role == Qt.CheckStateRole: self.data_contents[row].states.checked = value - elif role == Qt.EditRole: # For user actions with data - if col == 0: - # check user hostname spelling http://man7.org/linux/man-pages/man7/hostname.7.html - if value[0] != '-' and len(value) <= 63 and re.match("^[A-Za-z0-9-]*$", value): - self.data_contents[row].client.send_message("id", {"new_id": value}) + elif role == Qt.EditRole: # For user/outer actions with data, place modifiers applied + formatted_value = self.formatter.format_place(col, value) + if formatted_value is not None: # todo use new := syntax + self.data_contents[row][col] = formatted_value + + if col == 0: + self.data_contents[row].client.send_message("id", {"new_id": formatted_value}) self.data_contents[row].client.remove() - else: - msg = QtWidgets.QMessageBox() - msg.setIcon(QtWidgets.QMessageBox.Critical) - msg.setText("Wrong input for the copter name!\nPlease use only A-Z, a-z, 0-9, and '-' chars.\nDon't use '-' as first char.") - msg.exec_() - else: - self.data_contents[row][col] = value elif role == ModelDataRole: # For inner setting\editing of data self.data_contents[row][col] = value @@ -256,7 +433,7 @@ class CopterDataModel(QtCore.QAbstractTableModel): self.update_model(index, role) return True - def select_all(self): + def select_all(self): # probably NOT thread-safe! self.first_col_is_checked = not self.first_col_is_checked for row_num, copter in enumerate(self.data_contents): copter.states.checked = int(self.first_col_is_checked)*2 @@ -276,109 +453,23 @@ class CopterDataModel(QtCore.QAbstractTableModel): def add_client(self, client): self.insertRows([client]) - @QtCore.pyqtSlot(int) - def remove_client(self, row): + @QtCore.pyqtSlot(int) # Probably deprecated now + def remove_row(self, row): self.removeRows(row) - -def col_check(col): - def inner(f): - Checks.all_checks[col] = f - - def wrapper(*args, **kwargs): - return f(*args, **kwargs) - - return wrapper - - return inner - - -@col_check(1) -def check_ver(item): - if not item: - return None - return True - -@col_check(2) -def check_anim(item): - if not item: - return None - return str(item) != 'No animation' - -@col_check(3) -def check_bat(item): - if not item: - return None - if item == "NO_INFO": - return False - else: - return float(item.split(' ')[1][:-1]) > Checks.battery_min - -@col_check(4) -def check_sys_status(item): - if not item: - return None - return item == "STANDBY" - -@col_check(5) -def check_cal_status(item): - if not item: - return None - return item == "OK" - -@col_check(6) -def check_mode(item): - if not 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(' ') - 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(' ') - 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)) < Checks.time_delta_max - - -def all_checks(copter_item): - for col, check in Checks.all_checks.items(): - if not check(copter_item[col]): - 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 + @QtCore.pyqtSlot(object) + def remove_row_data(self, data): + row = self.get_row_index(data) + if row is not None: + self.removeRows(row) def flip_checks(copter_item): - for col in Checks.takeoff_checklist: + for col in ModelChecks.takeoff_checklist: if col != 4 or col != 7: - if not Checks.all_checks[col](copter_item[col]): - return False - else: - if copter_item[4] != "ACTIVE": + if not ModelChecks.checks_dict[col](copter_item[col]): return False + elif copter_item[4] != "ACTIVE": + return False return True @@ -387,7 +478,7 @@ def calibrating_check(copter_item): def calibration_ready_check(copter_item): - if not Checks.all_checks[4](copter_item[4]): + if not ModelChecks.checks_dict[4](copter_item[4]): return False return not calibrating_check(copter_item) @@ -414,7 +505,9 @@ class CopterProxyModel(QtCore.QSortFilterProxyModel): class SignalManager(QtCore.QObject): update_data_signal = QtCore.pyqtSignal(int, int, QtCore.QVariant, QtCore.QVariant) add_client_signal = QtCore.pyqtSignal(object) - remove_client_signal = QtCore.pyqtSignal(int) + remove_row_signal = QtCore.pyqtSignal(int) + remove_client_signal = QtCore.pyqtSignal(object) + if __name__ == '__main__': diff --git a/Server/server.py b/Server/server.py index 82157d9..b3b958e 100644 --- a/Server/server.py +++ b/Server/server.py @@ -146,12 +146,13 @@ class Server(messaging.Singleton): # noinspection PyArgumentList def _client_processor(self): logging.info("Client processor (selector) thread started!") + + messaging.NotifierSock().init(self.sel) + self.server_socket.listen() self.server_socket.setblocking(False) 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') @@ -177,15 +178,12 @@ class Server(messaging.Singleton): logging.info("Got connection from: {}".format(str(addr))) conn.setblocking(False) - 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()]): + if not any([client_addr == addr[0] for client_addr in Client.clients.keys()]): client = Client(addr[0]) logging.info("New client") else: client = Client.clients[addr[0]] + client.close(True) # to ensure in unregistering logging.info("Reconnected client") self.sel.register(conn, selectors.EVENT_READ, data=client) client.connect(self.sel, conn, addr) @@ -284,7 +282,7 @@ class Client(messaging.ConnectionManager): @staticmethod def get_by_id(copter_id): - for client in Client.clients.values(): + for client in Client.clients.values(): # TODO filter if client.copter_id == copter_id: return client @@ -303,10 +301,12 @@ class Client(messaging.ConnectionManager): if self.on_connect: self.on_connect(self) - def _got_id(self, value): + def _got_id(self, _client, value): logging.info("Got copter id: {} for client {}".format(value, self.addr)) + old_id = self.copter_id self.copter_id = value - if self.on_first_connect: + + if old_id is None and self.on_first_connect: self.on_first_connect(self) def close(self, inner=False): @@ -325,11 +325,12 @@ class Client(messaging.ConnectionManager): def remove(self): if self.connected: self.close() - if self.clients: - try: - self.clients.pop(self.addr[0]) - except Exception as e: - logging.error(e) + + try: + self.clients.pop(self.addr[0]) + except KeyError as e: + logging.error(e) + logging.info("Client {} successfully removed!".format(self.copter_id)) @requires_connect diff --git a/Server/server_qt.py b/Server/server_qt.py index 48aff7f..ef78935 100644 --- a/Server/server_qt.py +++ b/Server/server_qt.py @@ -37,7 +37,6 @@ def wait(end, interrupter=threading.Event(), maxsleep=0.1): def confirmation_required(text="Are you sure?", label="Confirm operation?"): def inner(f): - @functools.wraps(f) def wrapper(*args, **kwargs): reply = QMessageBox.question( @@ -55,6 +54,7 @@ def confirmation_required(text="Are you sure?", label="Confirm operation?"): return inner + # noinspection PyArgumentList,PyCallByClass class MainWindow(QtWidgets.QMainWindow): def __init__(self): @@ -70,9 +70,9 @@ class MainWindow(QtWidgets.QMainWindow): self.player = QtMultimedia.QMediaPlayer() self.init_model() - + self.show() - + def init_model(self): # self.model.on_id_changed = self.set_copter_id @@ -88,7 +88,8 @@ class MainWindow(QtWidgets.QMainWindow): # Connect signals to manipulate model from threads self.signals.update_data_signal.connect(self.model.update_item) self.signals.add_client_signal.connect(self.model.add_client) - self.signals.remove_client_signal.connect(self.model.remove_client) + self.signals.remove_row_signal.connect(self.model.remove_row) + self.signals.remove_client_signal.connect(self.model.remove_row_data) # Connect model signals to UI self.model.selected_ready_signal.connect(self.ui.start_button.setEnabled) @@ -110,20 +111,26 @@ class MainWindow(QtWidgets.QMainWindow): self.ui.action_select_all_rows.triggered.connect(self.model.select_all) def new_client_connected(self, client: Client): + logging.debug("Added client {}".format(client)) self.signals.add_client_signal.emit(StatedCopterData(copter_id=client.copter_id, client=client)) def client_connection_changed(self, client: Client): - logging.debug("Start remove {}".format(client.copter_id)) + logging.debug("Connection {} changed {}".format(client, client.connected), ) row_data = self.model.get_row_by_attr("client", client) - row_num = self.model.get_row_index(row_data) - logging.debug("Removing {}".format(client.copter_id)) - if row_num is not None: - if Server().remove_disconnected and (not client.connected): - client.remove() - self.signals.remove_client_signal.emit(row_num) - else: + + if row_data is None: + logging.error("No row for client presented") + return + + if Server().remove_disconnected and (not client.connected): + client.remove() + self.signals.remove_client_signal.emit(row_data) + logging.debug("Removing from table") + else: + row_num = self.model.get_row_index(row_data) + if row_num is not None: self.signals.update_data_signal.emit(row_num, 0, client.connected, ModelStateRole) - logging.debug("{} removed".format(client.copter_id)) + logging.debug("DATA: connected") def init_ui(self): # Connecting @@ -139,7 +146,7 @@ class MainWindow(QtWidgets.QMainWindow): self.ui.leds_button.clicked.connect(self.test_leds_selected) self.ui.takeoff_button.clicked.connect(self.takeoff_selected) self.ui.flip_button.clicked.connect(self.flip_selected) - self.ui.land_button.clicked.connect(self.land_selected) + self.ui.land_button.clicked.connect(self.land_selected) self.ui.reboot_fcu.clicked.connect(self.reboot_selected) self.ui.calibrate_gyro.clicked.connect(self.calibrate_gyro_selected) @@ -179,43 +186,36 @@ class MainWindow(QtWidgets.QMainWindow): client = copter_data_row.client client.get_response("telemetry", self.update_table_data) - @pyqtSlot(str) - def update_table_data(self, message): - fields = message.split('`') - # copter_id git_version animation_id battery_v battery_p system_status calibration_status mode selfcheck current_position start_position copter_time - copter_id = fields[0] - git_version = fields[1] - animation_id = fields[2] - battery_v = fields[3] - battery_p = fields[4] - if battery_v == 'nan' or battery_p == 'nan': - battery_info = "NO_INFO" - else: - battery_info = "{}V {}%".format(battery_v, battery_p) - sys_status = fields[5] - cal_status = fields[6] - mode = fields[7] - selfcheck = fields[8] - current_pos = fields[9] - start_pos = fields[10] - copter_time = fields[11] - time_delta = "{}".format(round(float(copter_time) - time.time(), 3)) - row = self.model.get_row_index(self.model.get_row_by_attr('copter_id', copter_id)) - self.signals.update_data_signal.emit(row, 1, git_version, ModelDataRole) - self.signals.update_data_signal.emit(row, 2, animation_id, ModelDataRole) - self.signals.update_data_signal.emit(row, 3, battery_info, ModelDataRole) - self.signals.update_data_signal.emit(row, 4, sys_status, ModelDataRole) - self.signals.update_data_signal.emit(row, 5, cal_status, ModelDataRole) - self.signals.update_data_signal.emit(row, 6, mode, ModelDataRole) - self.signals.update_data_signal.emit(row, 7, selfcheck, ModelDataRole) - self.signals.update_data_signal.emit(row, 8, current_pos, ModelDataRole) - self.signals.update_data_signal.emit(row, 9, start_pos, ModelDataRole) - self.signals.update_data_signal.emit(row, 10, time_delta, ModelDataRole) + @pyqtSlot(object, dict) + def update_table_data(self, client, telems: dict): + cols_dict = { + "git_version": 1, + "animation_id": 2, + "battery": 3, + "system_status": 4, + "calibration_status": 5, + "mode": 6, + "selfcheck": 7, + "current_position": 8, + "start_position": 9, + "time": 10, + } + + for key, value in telems.items(): + col = cols_dict.get(key, None) + if col is None: + logging.error("No column {} present!".format(key)) + continue + + row_data = self.model.get_row_by_attr("client", client) + row_num = self.model.get_row_index(row_data) + if row_num is not None: + self.signals.update_data_signal.emit(row_num, col, value, Qt.EditRole) @pyqtSlot(QtCore.QModelIndex) def selfcheck_info_dialog(self, index): col = index.column() - if col == 6: + if col == 7: data = self.proxy_model.data(index, role=ModelDataRole) if data and data != "OK": dialog = QMessageBox() @@ -226,7 +226,7 @@ class MainWindow(QtWidgets.QMainWindow): dialog.setDetailedText("\n".join(data)) dialog.exec() - def _selfcheck_shortener(self, data): + def _selfcheck_shortener(self, data): # TODO!!! shortened = [] for line in data: if len(line) > 89: @@ -236,16 +236,11 @@ class MainWindow(QtWidgets.QMainWindow): @pyqtSlot() def remove_selected(self): for copter in self.model.user_selected(): - row_num = self.model.get_row_index(copter) - if row_num is not None: - copter.client.remove() + copter.client.remove() - if not Server().remove_disconnected: - self.signals.remove_client_signal.emit(row_num) - - logging.info("Client removed from table!") - else: - logging.error("Client is not in table!") + if not Server().remove_disconnected: + self.signals.remove_client_signal.emit(copter) + logging.info("Client removed from table!") @pyqtSlot() @confirmation_required("This operation will takeoff selected copters with delay and start animation. Proceed?") @@ -255,12 +250,12 @@ class MainWindow(QtWidgets.QMainWindow): 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_at_time(music_dt+time_now), loop=loop) + asyncio.ensure_future(self.play_music_at_time(music_dt + time_now), loop=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): - server.send_starttime(copter.client, dt+time_now) + if self.model.checks.all_checks(copter): + server.send_starttime(copter.client, dt + time_now) @pyqtSlot() def pause_resume_selected(self): @@ -290,7 +285,7 @@ class MainWindow(QtWidgets.QMainWindow): def test_leds_selected(self): for copter in self.model.user_selected(): copter.client.send_message("led_test") - + @pyqtSlot() def disarm_all(self): Client.broadcast_message("disarm") @@ -299,9 +294,9 @@ class MainWindow(QtWidgets.QMainWindow): @confirmation_required("This operation will takeoff copters immediately. Proceed?") def takeoff_selected(self, **kwargs): for copter in self.model.user_selected(): - if takeoff_checks(copter): + if self.model.checks.takeoff_checks(copter): if self.ui.z_checkbox.isChecked(): - copter.client.send_message("takeoff_z", {"z":str(self.ui.z_spin.value())}) + copter.client.send_message("takeoff_z", {"z": str(self.ui.z_spin.value())}) # todo int else: copter.client.send_message("takeoff") @@ -320,7 +315,7 @@ class MainWindow(QtWidgets.QMainWindow): @pyqtSlot() def reboot_selected(self): for copter in self.model.user_selected(): - copter.client.send_message("reboot_fcu") + copter.client.send_message("reboot_fcu") @pyqtSlot() def calibrate_gyro_selected(self): @@ -332,7 +327,7 @@ class MainWindow(QtWidgets.QMainWindow): data = 'CALIBRATING' self.signals.update_data_signal.emit(row, col, data, ModelDataRole) # Send request - client.get_response("calibrate_gyro", self._get_calibration_info, callback_args=(copter_data_row, )) + client.get_response("calibrate_gyro", self._get_calibration_info) @pyqtSlot() def calibrate_level_selected(self): @@ -344,13 +339,15 @@ class MainWindow(QtWidgets.QMainWindow): data = 'CALIBRATING' self.signals.update_data_signal.emit(row, col, data, ModelDataRole) # Send request - client.get_response("calibrate_level", self._get_calibration_info, callback_args=(copter_data_row, )) + client.get_response("calibrate_level", self._get_calibration_info) - def _get_calibration_info(self, value, copter_data_row): + def _get_calibration_info(self, client, value): col = 5 - row = self.model.get_row_index(copter_data_row) - data = str(value) - self.signals.update_data_signal.emit(row, col, data, ModelDataRole) + row_data = self.model.get_row_by_attr("client", client) + row = self.model.get_row_index(row_data) + if row is not None: + data = str(value) + self.signals.update_data_signal.emit(row, col, data, ModelDataRole) @pyqtSlot() def send_animations(self): @@ -379,7 +376,8 @@ class MainWindow(QtWidgets.QMainWindow): for file, name in zip(files, names): for copter in self.model.user_selected(): if name == copter.copter_id: - copter.client.send_file(file, "/home/pi/catkin_ws/src/clever/clever/camera_info/calibration.yaml") + copter.client.send_file(file, + "/home/pi/catkin_ws/src/clever/clever/camera_info/calibration.yaml") else: logging.info("Filename has no matches with any drone selected") @@ -402,7 +400,8 @@ class MainWindow(QtWidgets.QMainWindow): @pyqtSlot() def send_aruco(self): - path = QFileDialog.getOpenFileName(self, "Select aruco map configuration file", filter="Aruco map files (*.txt)")[0] + path = \ + QFileDialog.getOpenFileName(self, "Select aruco map configuration file", filter="Aruco map files (*.txt)")[0] if path: filename = os.path.basename(path) print("Selected file:", path, filename) @@ -455,7 +454,7 @@ class MainWindow(QtWidgets.QMainWindow): def restart_clever(self): for copter in self.model.user_selected(): copter.client.send_message("service_restart", {"name": "clever"}) - + @pyqtSlot() def restart_clever_show(self): for copter in self.model.user_selected(): @@ -464,12 +463,12 @@ class MainWindow(QtWidgets.QMainWindow): @pyqtSlot() def update_client_repo(self): for copter in self.model.user_selected(): - copter.client.send_message("update_repo") + copter.client.send_message("update_repo") @pyqtSlot() def reboot_all_on_selected(self): for copter in self.model.user_selected(): - copter.client.send_message("reboot_all") + copter.client.send_message("reboot_all") @pyqtSlot() def update_start_to_current_position(self): @@ -501,7 +500,7 @@ class MainWindow(QtWidgets.QMainWindow): path = QFileDialog.getOpenFileName(self, "Select music file", filter="Music files (*.mp3 *.wav)")[0] if path: media = QUrl.fromLocalFile(path) - content = QtMultimedia.QMediaContent(media) + content = QtMultimedia.QMediaContent(media) self.player.setMedia(content) self.ui.action_select_music_file.setText(self.ui.action_select_music_file.text() + " (selected)") @@ -513,9 +512,9 @@ class MainWindow(QtWidgets.QMainWindow): if self.player.mediaStatus() == QtMultimedia.QMediaPlayer.NoMedia: logging.info("No media file") return - + if self.player.state() == QtMultimedia.QMediaPlayer.StoppedState or \ - self.player.state() == QtMultimedia.QMediaPlayer.PausedState: + self.player.state() == QtMultimedia.QMediaPlayer.PausedState: self.ui.action_play_music.setText("Pause music") self.player.play() else: @@ -552,16 +551,16 @@ class MainWindow(QtWidgets.QMainWindow): result = -1 while (result != 0) and (result != 3) and (result != 4): # light_green_red(min, max) - client_row_mid = int(math.ceil((client_row_max+client_row_min) / 2.0)) + client_row_mid = int(math.ceil((client_row_max + client_row_min) / 2.0)) print(client_row_min, client_row_mid, client_row_max) for row_num in range(client_row_min, client_row_mid): - self.model.data_contents[row_num].client\ + self.model.data_contents[row_num].client \ .send_message("led_fill", {"green": 255}) for row_num in range(client_row_mid, client_row_max + 1): self.model.data_contents[row_num].client \ .send_message("led_fill", {"red": 255}) - Dialog = QtWidgets.QDialog() + Dialog = QtWidgets.QDialog() ui = Ui_Dialog() ui.setupUi(Dialog) Dialog.show() @@ -574,7 +573,7 @@ class MainWindow(QtWidgets.QMainWindow): self.model.data_contents[row_num].client \ .send_message("led_fill") client_row_max = client_row_mid - 1 - + elif result == 2: for row_num in range(client_row_min, client_row_mid): self.model.data_contents[row_num].client \ @@ -592,18 +591,25 @@ class MainWindow(QtWidgets.QMainWindow): self.model.data_contents[row_num].client \ .send_message("disarm") -@messaging.message_callback("telem") -def get_telem_data(*args, **kwargs): - message = kwargs.get("message", None) - window.update_table_data(message) + +@messaging.message_callback("telemetry") +def get_telem_data(self, **kwargs): + message = kwargs.get("value") + window.update_table_data(self, message) + + +def except_hook(cls, exception, traceback): + sys.__excepthook__(cls, exception, traceback) if __name__ == "__main__": + sys.excepthook = except_hook # for debugging (exceptions traceback) + app = QtWidgets.QApplication(sys.argv) loop = QEventLoop(app) asyncio.set_event_loop(loop) - #app.exec_() + # app.exec_() with loop: window = MainWindow() diff --git a/messaging_lib.py b/messaging_lib.py index 9b1074b..855e1ef 100644 --- a/messaging_lib.py +++ b/messaging_lib.py @@ -17,12 +17,24 @@ try: except ImportError: import selectors2 as selectors + # import logging_lib -PendingRequest = collections.namedtuple("PendingRequest", ["value", "requested_value", # "expires_on", - "callback", "callback_args", "callback_kwargs", - "request_args", "resend", - ]) + +class Namespace: + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + def __getitem__(self, key): + return self.__dict__[key] + + def __setitem__(self, key, value): + self.__dict__[key] = value + + +class PendingRequest(Namespace): pass + + logger = logging.getLogger(__name__) @@ -239,10 +251,19 @@ class ConnectionManager(object): self.socket = client_socket self.addr = client_addr + self._clear() + self._set_selector_events_mask('r') if self.resend_requests: self._resend_requests() + def _clear(self): + if not self.resume_queue: # maybe needs locks + self._recv_buffer = b'' + self._send_buffer = b'' + self._received_queue.clear() + self._send_queue.clear() + def close(self): with self._close_lock: self._should_close = True @@ -253,11 +274,6 @@ class ConnectionManager(object): def _close(self): logger.info("Closing connection to {}".format(self.addr)) - if not self.resume_queue: - self._recv_buffer = b'' - self._send_buffer = b'' - self._received_queue.clear() # - try: logger.info("Unregistering selector of {}".format(self.addr)) self.selector.unregister(self.socket) @@ -281,6 +297,7 @@ class ConnectionManager(object): with self._close_lock: self._should_close = False + self._clear() logger.info("CLOSED connection to {}".format(self.addr)) def process_events(self, mask): @@ -379,7 +396,7 @@ class ConnectionManager(object): ) f = request.callback - f(value, *request.callback_args, **request.callback_kwargs) + f(self, value, *request.callback_args, **request.callback_kwargs) else: logger.warning("Unexpected response!") @@ -417,9 +434,6 @@ class ConnectionManager(object): 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'' - raise error else: logger.debug("Sent {} to {}".format(self._send_buffer[:sent], self.addr)) @@ -456,14 +470,12 @@ class ConnectionManager(object): def _resend_requests(self): with self._request_lock: - for request_id, request in self._request_queue.items(): + for request_id, request in self._request_queue.items(): #TODO filter if request.resend: self._send(MessageManager.create_request( request.requested_value, request_id, request.request_args.update(resend=request.resend)) ) - #request.resend = False - - # self._request_queue.clear() + request.resend = False def send_message(self, command, args=None): self._send(MessageManager.create_simple_message(command, args)) @@ -484,41 +496,45 @@ class ConnectionManager(object): )) -class NotifierSock(Singleton): #TODO remake as connecting ONLY to self socket and selector +class NotifierSock(Singleton): def __init__(self): - self.receive_socket = None - self.addr = None + self._server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self._server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self._server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) + self._server_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) - self._notify_socket = None - self._notify_lock = threading.Lock() + self._sending_sock = socket.socket() + self._send_lock = threading.Lock() - def bind(self, server_addr): - self._notify_socket = socket.socket() - self._notify_socket.connect(server_addr) - logger.info("Notify socket: bind") + self._receiving_sock = None - def connect(self, _, client_socket, client_addr): - self.receive_socket = client_socket - self.addr = client_addr + def init(self, selector, port=26000): + port += random.randint(0, 100) # local testing fix + self._server_socket.bind(('', port)) + self._server_socket.listen(1) + self._sending_sock.connect(('127.0.0.1', port)) + self._receiving_sock, _ = self._server_socket.accept() logger.info("Notify socket: connected") + selector.register(self._receiving_sock, selectors.EVENT_READ, data=self) + logger.info("Notify socket: selector registered") + + def get_sock(self): + return self._receiving_sock + def notify(self): - with self._notify_lock: - if self.addr is not None: - self._notify_socket.sendall(bytes(1)) + with self._send_lock: + if self._receiving_sock is not None: + self._sending_sock.sendall(bytes(1)) logger.debug("Notify socket: notified") def process_events(self, mask): - if mask & selectors.EVENT_READ: + if mask & selectors.EVENT_READ and self._receiving_sock is not None: try: - data = self.receive_socket.recv(1024) - except Exception: # TODO remove + self._receiving_sock.recv(1024) + logger.debug("Notify socket: received") + except io.BlockingIOError: 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)) - + except Exception as e: + print(e) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 728753f..a196866 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ numpy==1.16.4 PyQt5==5.13.0 PyQt5-sip==4.19.18 selectors2==2.0.1 +Quamash==0.6.1 \ No newline at end of file From f9a6cca6fcdbafc475415eb338fdbd126ee14281 Mon Sep 17 00:00:00 2001 From: artem30801 <38689676+artem30801@users.noreply.github.com> Date: Mon, 9 Dec 2019 11:49:00 +0300 Subject: [PATCH 11/59] Add remove_disconnected to docks --- docs/ru/server.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/ru/server.md b/docs/ru/server.md index 09d44ee..2ff1dec 100644 --- a/docs/ru/server.md +++ b/docs/ru/server.md @@ -4,7 +4,8 @@ ```ini [SERVER] port = 25000 -buffer_size = 1024 +buffer_size = 1024 +remove_disconnected = False [BROADCAST] use_broadcast = True @@ -15,6 +16,11 @@ 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 ``` Конфигурация по умолчанию является полностью работоспособной и не требует изменений для быстрого начала работы системы. ### Раздел 'Server' @@ -22,6 +28,7 @@ port = 123 * `port` - TCP порт, на котором будут приниматься входящие соединения от клиентов (коптеров). При использовании broadcast данный порт будет сконфигурирован у клиента автоматически. *Рекомендуется изменить значение по умолчанию в целях безопасности* (любое пятизначное и более число, если другое ПО не использует выбранный порт). * `buffer_size` - размер буфера при приёме и передаче данных. *Не рекомендуется изменять. Рекомендуется использовать единое значение у сервера и клиентов.* + * `remove_disconnected` - Определяет поведение при разрыве связи с клиентом. При значении `True` вся информация о клиенте *будет удалена* как из внутренней памяти, так и *из таблицы*. *Это может привести к 'скачкам' таблицы при отключении клиентов.* При значении `False` отключённые клиенты *не будут* удалены из таблицы, но будут отображены с подсвечиванием ячейки `copter ID` красным цветом. Все данные будут сохранены. При переподключении клиента, он будет ассоциирован с той же строкой таблицы, а ячейка со значением `copter ID` вновь станет зелёного цвета. ### Раздел 'Broadcast' Сервер использует UDP broadcast (на адрес 255.255.255.255 с выбранным портом), чтобы передавать клиентам (коптерам) актуальную информацию о конфигурации сервера и собственном адресе сервера для подключения (IP адрес и порт сервера). Таким образом, обеспечивается автоматическое подключение клиентов к серверу без необходимости дополнительной ручной конфигурации. В данном разделе задаются параметры этого механизма. @@ -96,8 +103,3 @@ port = 123 * Кнопка `Disarm` - все коптеры в выбранной ('зелёной') группе *немедленно отключают моторы (disarm).* (аналогично кнопке `Disarm` в панели инструментов) ==Это может привести к падению и повреждению коптеров==. ### Алгоритм использования * ... - \ No newline at end of file From 181086b2b5fce9a3aa3f80523b608eb8c1fad088 Mon Sep 17 00:00:00 2001 From: artem30801 <38689676+artem30801@users.noreply.github.com> Date: Mon, 9 Dec 2019 15:23:38 +0300 Subject: [PATCH 12/59] Added 'Checks' section to docs --- docs/ru/server.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/ru/server.md b/docs/ru/server.md index 2ff1dec..53ea575 100644 --- a/docs/ru/server.md +++ b/docs/ru/server.md @@ -29,6 +29,11 @@ time_delta_max = 1 * `port` - TCP порт, на котором будут приниматься входящие соединения от клиентов (коптеров). При использовании broadcast данный порт будет сконфигурирован у клиента автоматически. *Рекомендуется изменить значение по умолчанию в целях безопасности* (любое пятизначное и более число, если другое ПО не использует выбранный порт). * `buffer_size` - размер буфера при приёме и передаче данных. *Не рекомендуется изменять. Рекомендуется использовать единое значение у сервера и клиентов.* * `remove_disconnected` - Определяет поведение при разрыве связи с клиентом. При значении `True` вся информация о клиенте *будет удалена* как из внутренней памяти, так и *из таблицы*. *Это может привести к 'скачкам' таблицы при отключении клиентов.* При значении `False` отключённые клиенты *не будут* удалены из таблицы, но будут отображены с подсвечиванием ячейки `copter ID` красным цветом. Все данные будут сохранены. При переподключении клиента, он будет ассоциирован с той же строкой таблицы, а ячейка со значением `copter ID` вновь станет зелёного цвета. + +### Раздел 'Checks' +* `battery_percentage_min` - Минимальный заряд батарии коптера *в процентах* (дробное значение от 0 до 100), допустимый для взлёта. Значение меньше указанного будет отмечено как неудовлетворительное (см. раздел таблицы). +* `start_pos_delta_max` - Максимальное расстояние *в метрах* (дробное значение от 0 до 'inf') от текущего положения коптера до его точки взлёта в файле анимации, допустимое для взлёта. Допустимо использование строки 'inf' для любого допустимого расстояния. Значение больше указанного будет отмечено как неудовлетворительное (см. раздел таблицы). +* `time_delta_max` - Максимальная разница (абсолютное значение) *в секундах* (дробное значение от 0 до 'inf') между временем сервера и клиента (включая сетевую задержку), допустимая для взлёта. Допустимо использование строки 'inf' для любой допустимой разницы. Значение больше указанного будет отмечено как неудовлетворительное (см. раздел таблицы). ### Раздел 'Broadcast' Сервер использует UDP broadcast (на адрес 255.255.255.255 с выбранным портом), чтобы передавать клиентам (коптерам) актуальную информацию о конфигурации сервера и собственном адресе сервера для подключения (IP адрес и порт сервера). Таким образом, обеспечивается автоматическое подключение клиентов к серверу без необходимости дополнительной ручной конфигурации. В данном разделе задаются параметры этого механизма. @@ -75,7 +80,7 @@ time_delta_max = 1 * Кнопка `Land` - ВСЕ коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно *переходят в режим посадки.* **Используйте в экстренных случаях как одно из средств перехвата.** * Кнопка `Diarm` - ВСЕ коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно *отключают моторы (disarm).* ==Это может привести к падению и повреждению коптеров== **Используйте в крайних случаях как последнее из средств перехвата.** -## Таблица состояния коптеров (клиентов) +## Таблица состояния коптеров (клиентов) При первом подключении клиента к серверу в таблицу добавляется строка для отображения состояния клиента, содержащая начальные данные, переданные клиентом при подключении (`Copter ID`). Строки НЕ удаляются после зарегистрированного отключения клиента. Строки можно сортировать по возрастанию \ убыванию значений любого из столбцов (кликнув по заголовку столбца). Ячейки таблицы подсвечиваются: From 21df7e32a319c6824dfea0dc623094a8009b7c8a Mon Sep 17 00:00:00 2001 From: artem30801 <38689676+artem30801@users.noreply.github.com> Date: Mon, 9 Dec 2019 15:48:12 +0300 Subject: [PATCH 13/59] Added images for docs --- docs/ru/img/server-animation.png | Bin 0 -> 5022 bytes docs/ru/img/server-drone.png | Bin 0 -> 15438 bytes docs/ru/img/server-music.png | Bin 0 -> 3482 bytes docs/ru/img/server-server.png | Bin 0 -> 16430 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/ru/img/server-animation.png create mode 100644 docs/ru/img/server-drone.png create mode 100644 docs/ru/img/server-music.png create mode 100644 docs/ru/img/server-server.png diff --git a/docs/ru/img/server-animation.png b/docs/ru/img/server-animation.png new file mode 100644 index 0000000000000000000000000000000000000000..99229cb015946ba18c1d3392c95f0c996a266db7 GIT binary patch literal 5022 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 literal 0 HcmV?d00001 From 04912fe11108fa9636d312b9e25137d35017df40 Mon Sep 17 00:00:00 2001 From: artem30801 <38689676+artem30801@users.noreply.github.com> Date: Thu, 12 Dec 2019 15:58:03 +0300 Subject: [PATCH 14/59] Updated docs of submenu 'server' --- docs/ru/server.md | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/docs/ru/server.md b/docs/ru/server.md index 53ea575..e20d9a9 100644 --- a/docs/ru/server.md +++ b/docs/ru/server.md @@ -10,7 +10,7 @@ remove_disconnected = False [BROADCAST] use_broadcast = True broadcast_port = 8181 -broadcast_delay = 5 +broadcast_delay = 5.0 [NTP] use_ntp = False @@ -18,10 +18,10 @@ host = ntp1.stratum2.ru port = 123 [CHECKS] -battery_percentage_min = 50 -start_pos_delta_max = 1 -time_delta_max = 1 -``` +battery_percentage_min = 50.0 +start_pos_delta_max = 1.0 +time_delta_max = 1.0 +``` Конфигурация по умолчанию является полностью работоспособной и не требует изменений для быстрого начала работы системы. ### Раздел 'Server' В этом разделе задаются параметры сетевого взаимодействия сервера, доступны следующие параметры: @@ -29,7 +29,7 @@ time_delta_max = 1 * `port` - TCP порт, на котором будут приниматься входящие соединения от клиентов (коптеров). При использовании broadcast данный порт будет сконфигурирован у клиента автоматически. *Рекомендуется изменить значение по умолчанию в целях безопасности* (любое пятизначное и более число, если другое ПО не использует выбранный порт). * `buffer_size` - размер буфера при приёме и передаче данных. *Не рекомендуется изменять. Рекомендуется использовать единое значение у сервера и клиентов.* * `remove_disconnected` - Определяет поведение при разрыве связи с клиентом. При значении `True` вся информация о клиенте *будет удалена* как из внутренней памяти, так и *из таблицы*. *Это может привести к 'скачкам' таблицы при отключении клиентов.* При значении `False` отключённые клиенты *не будут* удалены из таблицы, но будут отображены с подсвечиванием ячейки `copter ID` красным цветом. Все данные будут сохранены. При переподключении клиента, он будет ассоциирован с той же строкой таблицы, а ячейка со значением `copter ID` вновь станет зелёного цвета. - + ### Раздел 'Checks' * `battery_percentage_min` - Минимальный заряд батарии коптера *в процентах* (дробное значение от 0 до 100), допустимый для взлёта. Значение меньше указанного будет отмечено как неудовлетворительное (см. раздел таблицы). * `start_pos_delta_max` - Максимальное расстояние *в метрах* (дробное значение от 0 до 'inf') от текущего положения коптера до его точки взлёта в файле анимации, допустимое для взлёта. Допустимо использование строки 'inf' для любого допустимого расстояния. Значение больше указанного будет отмечено как неудовлетворительное (см. раздел таблицы). @@ -56,13 +56,23 @@ time_delta_max = 1 НЕ прошедший предполётную проверку (`Preflight check`) (не имеющий её результатов в таблице - жёлтые ячейки) или же имеющий НЕудовлетворительные результаты (красные ячейки в таблице). Учитываются проверки аккумулятора и сообщения selfcheck. ## Меню -### Раздел 'Actions' +### Раздел 'Server' + +![Скриншот меню](img\server-server.png) + Данный раздел содержит несколько утилит по отправке различных данных на *выбранные* клиенты. **Внимание!** Не пытайтесь использовать данные команды во время полёта коптеров! * `Send Animations` - отправка файлов анимации (экспортированных аддоном к Blender) на выбранные клиенты (коптеры). В диалоговом окне необходимо выбрать *папку*, содержащую файлы анимации (автоматически создается аддоном). Каждый файл анимации будет отправлен на клиент с именем (copter ID), соответствующим имени файла без расширения. * `Send Configurations` - отправка *единого* файла конфигурации клиента на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл конфигурации в установленном формате. Файл конфигурации может быть неполным, в таком случае будут перезаписаны лишь указанные в файле параметры. *Не рекомендуется использовать данное действие для массовой перезаписи `Copter ID`, кроме значения `/hostname`.* **Внимание!** НЕ отправляйте на клиенты файл конфигурации сервера. -* `Send Aruco map` - отправка *единого* файла карты aruco маркеров на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл карты в установленном формате. Файл на клиенте будет перезаписан. После получения и записи файла клиент автоматически перезапустит сервис `clever`. Для работоспособности полётных функция *необходимо подождать* некоторое время до полного запуска сервиса. +* `Send Launch files ` - отправка launch-файлов конфигурации сервиса `clever`. . В диалоговом окне необходимо выбрать *папку*, содержащую файлы конфигурации с расширением `.launch`. Все файлы с таким расширением будут отправлены *на каждый* из клиентов. **Внимание!** Существующие файлы конфигурации на коптерах будут перезаписаны, однако файлы, не отправленные сервером, не будут удалены или модифицированы. +* `Send Aruco map` - отправка *единого* файла карты aruco маркеров на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл карты в установленном формате. Файл на клиенте будет перезаписан. После получения и записи файла клиент автоматически перезапустит сервис `clever`. Для работоспособности полётных функций *необходимо подождать* некоторое время до полного запуска сервиса. +* `Send Camera Calibrations` - отправка yaml-файлов калибрации камеры для сервиса `clever`. В диалоговом окне необходимо выбрать *папку*, содержащую файлы конфигурации с расширением `.yaml`. Каждый файл калибрации будет отправлен на клиент с именем (copter ID), соответствующим имени файла без расширения. **Внимание!** Существующий файл калибрации на коптере будет перезаписан. +* `Send FCU parameters` - отправка и запись *единого* файла конфигураций полётного контроллера (FCU) на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл параметров в установленном формате. Параметры на полётном контроллере будут перезаписаны. После получения и записи файла клиент автоматически. +* `Developer mode`: **Внимание!** Используйте данные действия с большой осторожностью + * `Send any file` - отправка *одного* любого файла на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл. Далее, необходимо указать путь, по которому данный файл будет записан на клиенты (не включая имя файла) + * `Send any command` - отправка и выполнение любой команды терминала на все выбранные клиенты. В диалоговом окне необходимо ввести требуемую команду. Команды *могут* использовать `sudo`-права. +* `Select all drones` (`Ctrl+A`) Выделяет все коптеры в таблице. При следующем вызове команды, выделение всех коптеров будет отменено. -## Боковая панель инструментов (команд) +## Боковая панель инструментов (команд) ### Управление Данный раздел команд предназначен для выскоуровневого управления роем дронов. * Кнопка `Preflight check` - Все выбранные клиенты выполняют самодиагностику и предполётную проверку. Результаты, вместе с другими параметрами клиента, будут отображены в таблице по мере поступления данных. From c8585be3b088c201f912877119d4ccaf5c574246 Mon Sep 17 00:00:00 2001 From: artem30801 <38689676+artem30801@users.noreply.github.com> Date: Thu, 12 Dec 2019 15:59:23 +0300 Subject: [PATCH 15/59] Fixed image --- docs/ru/server.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ru/server.md b/docs/ru/server.md index e20d9a9..5408dd1 100644 --- a/docs/ru/server.md +++ b/docs/ru/server.md @@ -58,7 +58,7 @@ time_delta_max = 1.0 ## Меню ### Раздел 'Server' -![Скриншот меню](img\server-server.png) +![Скриншот меню](img/server-server.png) Данный раздел содержит несколько утилит по отправке различных данных на *выбранные* клиенты. **Внимание!** Не пытайтесь использовать данные команды во время полёта коптеров! * `Send Animations` - отправка файлов анимации (экспортированных аддоном к Blender) на выбранные клиенты (коптеры). В диалоговом окне необходимо выбрать *папку*, содержащую файлы анимации (автоматически создается аддоном). Каждый файл анимации будет отправлен на клиент с именем (copter ID), соответствующим имени файла без расширения. From 01a3ee9cfdde87dbbba69d3c75b8338746e8fcea Mon Sep 17 00:00:00 2001 From: artem30801 <38689676+artem30801@users.noreply.github.com> Date: Thu, 12 Dec 2019 16:56:56 +0300 Subject: [PATCH 16/59] Added 'drone' menu section to docs --- docs/ru/server.md | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/docs/ru/server.md b/docs/ru/server.md index e20d9a9..4c7a78a 100644 --- a/docs/ru/server.md +++ b/docs/ru/server.md @@ -39,7 +39,7 @@ time_delta_max = 1.0 Сервер использует UDP broadcast (на адрес 255.255.255.255 с выбранным портом), чтобы передавать клиентам (коптерам) актуальную информацию о конфигурации сервера и собственном адресе сервера для подключения (IP адрес и порт сервера). Таким образом, обеспечивается автоматическое подключение клиентов к серверу без необходимости дополнительной ручной конфигурации. В данном разделе задаются параметры этого механизма. * `use_broadcast` - будут ли использованы broadcast'ы для передачи данных (при значении `False` broadcast'ы НЕ будут отправляться). Используйте `False` в случае повышенных требований безопасности, перегруженности сети или невозможности передачи по широковещательному каналу (из-за конфигурации брандмауэра или сети) * `broadcast_port` - UDP порт, по которому будет осуществляться отправка сообщений. *Рекомендуется изменить значение по умолчанию в целях безопасности.* **Внимание!** При изменении этого параметра клиенты НЕ смогут принимать сообщения автоконфигурации до изменения (вручную) соответствующего параметра в конфигурации клиента на равное значение. - * `broadcast_delay` - Периодичность (в секундах, целочисленное значение), с которой будет происходить отправка broadcast сообщений. Увеличьте задержку для уменьшения нагрузки на сеть. *ИЛИ* Уменьшите задержку для уменьшения времени отклика и подключения при первом запуске клиентов. + * `broadcast_delay` - периодичность (в секундах, целочисленное значение), с которой будет происходить отправка broadcast сообщений. Увеличьте задержку для уменьшения нагрузки на сеть. *ИЛИ* Уменьшите задержку для уменьшения времени отклика и подключения при первом запуске клиентов. ### Раздел 'NTP' Помимо синхронизации времени (с миллисекундной точностью) с помощью пакета chrony, предоставляется альтернатива - возможность использования внешних (при наличии соединения локальной сети с интернетом) или внутрисетевых NTP-серверов. **Внимание!** Для корректной работы системы, и сервер, *и* клиенты должны использовать единый способ синхронизации времени (набор параметров в этом разделе). Данный раздел полностью унифицирован и для сервера, и для клиентов. * `use_ntp` - Определяет, будет ли использоваться синхронизация времени с помощью NTP. (при значении `False` будет использовано локальное время ОС (синхронизируется автоматически при использовании chrony). *Рекомендуется использование crhony, а не NTP* @@ -64,13 +64,27 @@ time_delta_max = 1.0 * `Send Animations` - отправка файлов анимации (экспортированных аддоном к Blender) на выбранные клиенты (коптеры). В диалоговом окне необходимо выбрать *папку*, содержащую файлы анимации (автоматически создается аддоном). Каждый файл анимации будет отправлен на клиент с именем (copter ID), соответствующим имени файла без расширения. * `Send Configurations` - отправка *единого* файла конфигурации клиента на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл конфигурации в установленном формате. Файл конфигурации может быть неполным, в таком случае будут перезаписаны лишь указанные в файле параметры. *Не рекомендуется использовать данное действие для массовой перезаписи `Copter ID`, кроме значения `/hostname`.* **Внимание!** НЕ отправляйте на клиенты файл конфигурации сервера. * `Send Launch files ` - отправка launch-файлов конфигурации сервиса `clever`. . В диалоговом окне необходимо выбрать *папку*, содержащую файлы конфигурации с расширением `.launch`. Все файлы с таким расширением будут отправлены *на каждый* из клиентов. **Внимание!** Существующие файлы конфигурации на коптерах будут перезаписаны, однако файлы, не отправленные сервером, не будут удалены или модифицированы. -* `Send Aruco map` - отправка *единого* файла карты aruco маркеров на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл карты в установленном формате. Файл на клиенте будет перезаписан. После получения и записи файла клиент автоматически перезапустит сервис `clever`. Для работоспособности полётных функций *необходимо подождать* некоторое время до полного запуска сервиса. +* `Send Aruco map` - отправка *единого* файла карты aruco маркеров на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл карты в установленном формате. Файл на клиенте будет перезаписан. После получения и записи файла клиент автоматически перезапустит сервис `clever`. Для возобновления работоспособности полётных функций и получения некоторых значений телеметрии *необходимо подождать* некоторое время до полного запуска сервиса. * `Send Camera Calibrations` - отправка yaml-файлов калибрации камеры для сервиса `clever`. В диалоговом окне необходимо выбрать *папку*, содержащую файлы конфигурации с расширением `.yaml`. Каждый файл калибрации будет отправлен на клиент с именем (copter ID), соответствующим имени файла без расширения. **Внимание!** Существующий файл калибрации на коптере будет перезаписан. * `Send FCU parameters` - отправка и запись *единого* файла конфигураций полётного контроллера (FCU) на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл параметров в установленном формате. Параметры на полётном контроллере будут перезаписаны. После получения и записи файла клиент автоматически. * `Developer mode`: **Внимание!** Используйте данные действия с большой осторожностью * `Send any file` - отправка *одного* любого файла на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл. Далее, необходимо указать путь, по которому данный файл будет записан на клиенты (не включая имя файла) * `Send any command` - отправка и выполнение любой команды терминала на все выбранные клиенты. В диалоговом окне необходимо ввести требуемую команду. Команды *могут* использовать `sudo`-права. -* `Select all drones` (`Ctrl+A`) Выделяет все коптеры в таблице. При следующем вызове команды, выделение всех коптеров будет отменено. +* `Select all drones` (`Ctrl+A`) Выделяет все коптеры в таблице. При следующем вызове команды, выделение всех коптеров будет отменено. + +### Раздел 'Drone' + +![Скриншот раздела](img\server-drone.png) + +* `Set Z offfset to ground` - Устанавливает собственный offset (отступ) каждого из выбранных клиентов на значение, равное текущему положению по координате Z +* `Reset Z offfset` - Устанавливает собственный offset (отступ) каждого из выбранных клиентов на значение `0` +* `Restart chrony` - Перезапускает сервис синхронизации времени `chrony` на выбранных клиентах. +* `Remove from table` - Удаляет выбранные коптеры из таблицы. **Внимание!** В случае, если клиент был подключен, будет произведено отключение. В случае если удалённый таким образом клиент исправно функционировал, он переподключится в кратчайшие сроки. +* `Developer mode`: **Внимание!** Используйте данные действия с большой осторожностью + * `Restart clever service` - Перезапускает сервис `clever` на выбранных клиентах. Для возобновления работоспособности полётных функций и получения некоторых значений телеметрии *необходимо подождать* некоторое время до полного запуска сервиса. + * `Restart clever-show service` - Перезапускает сервис шоу коптеров (клиента) `clever-show` на выбранных клиентах. Во время перезапуска клиенты будет отключены. + * `Update clever-show git` - Обновляет папку репозитория `clever-show` на выбранных клиентах. Файлы конфигурации клиента *не будут* перезаписаны. **Внимание!** Для того, чтобы изменения вступили в силу, *необходимо* перезапустить сервис `clever-show`. + * `Reboot all` - Полностью перезагружает систему выбранных клиентов. Во время перезапуска клиенты будет отключены. ## Боковая панель инструментов (команд) ### Управление From d88064afafc097943b4b81a7797df82036af5811 Mon Sep 17 00:00:00 2001 From: artem30801 <38689676+artem30801@users.noreply.github.com> Date: Thu, 12 Dec 2019 17:01:15 +0300 Subject: [PATCH 17/59] Fixed image --- docs/ru/server.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ru/server.md b/docs/ru/server.md index 7970f03..d997562 100644 --- a/docs/ru/server.md +++ b/docs/ru/server.md @@ -74,7 +74,7 @@ time_delta_max = 1.0 ### Раздел 'Drone' -![Скриншот раздела](img\server-drone.png) +![Скриншот раздела](img/server-drone.png) * `Set Z offfset to ground` - Устанавливает собственный offset (отступ) каждого из выбранных клиентов на значение, равное текущему положению по координате Z * `Reset Z offfset` - Устанавливает собственный offset (отступ) каждого из выбранных клиентов на значение `0` From f20299e78ff2407e94c45e0ce1f55320fcd07d3e Mon Sep 17 00:00:00 2001 From: artem30801 <38689676+artem30801@users.noreply.github.com> Date: Thu, 12 Dec 2019 18:41:32 +0300 Subject: [PATCH 18/59] Added 'Animation' and 'Music' menus to docs --- docs/ru/server.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/ru/server.md b/docs/ru/server.md index 7970f03..263080c 100644 --- a/docs/ru/server.md +++ b/docs/ru/server.md @@ -86,6 +86,24 @@ time_delta_max = 1.0 * `Update clever-show git` - Обновляет папку репозитория `clever-show` на выбранных клиентах. Файлы конфигурации клиента *не будут* перезаписаны. **Внимание!** Для того, чтобы изменения вступили в силу, *необходимо* перезапустить сервис `clever-show`. * `Reboot all` - Полностью перезагружает систему выбранных клиентов. Во время перезапуска клиенты будет отключены. +### Раздел 'Animation' + +![Скриншот раздела](img/server-animation.png) + +- `Set start X Y to current position` - Устанавливает точку старта анимации у выбранных клиентов на значения текущей позиции по X Y +- `Reset start position` - Устанавливает точку старта анимации у выбранных клиентов на значения `0.0`, `0.0` + +### Раздел 'Music' + +![Скриншот раздела](img/server-music.png) + +- `Select music file` - Загружает выбранный музыкальный файл (поддерживаемые расширения: `.mp3`, `.wav`) для дальнейшего воспроизведения вручную или +- `Play music` - Воспроизводит загруженную музыку + +- `Stop music` - Останавливает воспроизведение проигрываемой музыки (запущенной вручную или автоматически) + + + ## Боковая панель инструментов (команд) ### Управление Данный раздел команд предназначен для выскоуровневого управления роем дронов. From 819af47c4a9b6f0c71332387fddeb19f4589d39c Mon Sep 17 00:00:00 2001 From: artem30801 <38689676+artem30801@users.noreply.github.com> Date: Thu, 12 Dec 2019 18:45:49 +0300 Subject: [PATCH 19/59] Added gui screenshot to docs --- docs/ru/img/server_gui.png | Bin 0 -> 148646 bytes docs/ru/server.md | 3 +++ 2 files changed, 3 insertions(+) create mode 100644 docs/ru/img/server_gui.png diff --git a/docs/ru/img/server_gui.png b/docs/ru/img/server_gui.png new file mode 100644 index 0000000000000000000000000000000000000000..4b0bf2077c6ce31fb75ecb10bac09f833f04fe00 GIT binary patch literal 148646 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%0A10F} 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%0AfK@{tx|0$MCSauTUP#J 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 literal 0 HcmV?d00001 diff --git a/docs/ru/server.md b/docs/ru/server.md index e628141..cdd9b13 100644 --- a/docs/ru/server.md +++ b/docs/ru/server.md @@ -48,6 +48,9 @@ time_delta_max = 1.0 # Интерфейс сервера Сервер имеет визуальный графический интерфейс для удобства взаимодействия. + +![Интерфейс сервера](img/server_gui.png) + ## Глоссарий Некоторые термины, используемые для краткости записи: * Готовый [к полёту] коптер: From 2a07b43c887a9ff411461a08b703bc37da988173 Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Sat, 7 Dec 2019 04:02:47 +0300 Subject: [PATCH 20/59] docs: Fix typo --- docs/ru/start-tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ru/start-tutorial.md b/docs/ru/start-tutorial.md index 4f565fe..7aaba1f 100644 --- a/docs/ru/start-tutorial.md +++ b/docs/ru/start-tutorial.md @@ -31,7 +31,7 @@ cd ~/clever-show/Drone sudo ./client_setup.sh ``` -* Теперь при запуске серверного приложения настроенные коптеры будут отображаться в виде таблицы. Также можно подключаться к Raspberry Pi на коптере по его имени через `ssh` в указанной при настройке wifi сети, например `ssh pi@clever-1`, пароль `cleverwifi`. +* Теперь при запуске серверного приложения настроенные коптеры будут отображаться в виде таблицы. Также можно подключаться к Raspberry Pi на коптере по его имени через `ssh` в указанной при настройке wifi сети, например `ssh pi@clever-1`, пароль `raspberry`. Документация по клиентской части находится [здесь](client.md). From 16da689b967f17cbb360922da84bcebd782a27ef Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Fri, 13 Dec 2019 07:25:16 +0000 Subject: [PATCH 21/59] Client: remove unnecessary interruption returns from takeoff --- Drone/animation_lib.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Drone/animation_lib.py b/Drone/animation_lib.py index d238f2e..be51fd9 100644 --- a/Drone/animation_lib.py +++ b/Drone/animation_lib.py @@ -216,14 +216,10 @@ try: interrupter=interrupt_event): if use_leds: LedLib.wipe_to(255, 0, 0, interrupter=interrupter) - if interrupter.is_set(): - return result = FlightLib.takeoff(height=z, timeout_takeoff=timeout, frame_id=frame_id, emergency_land=safe_takeoff, interrupter=interrupter) if result == 'not armed' or result == 'timeout': raise Exception('STOP') # Raise exception to clear task_manager if copter can't arm - if interrupter.is_set(): - return if use_leds: LedLib.blink(0, 255, 0, wait=50, interrupter=interrupter) From 3b35ff8b6676bba7c1a7868f13980034782a025f Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Tue, 17 Dec 2019 11:38:22 +0300 Subject: [PATCH 22/59] Server: Write short check statuses directly --- Server/copter_table_models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Server/copter_table_models.py b/Server/copter_table_models.py index 1d9e62e..a75faac 100644 --- a/Server/copter_table_models.py +++ b/Server/copter_table_models.py @@ -253,6 +253,8 @@ def view_battery(value): @ModelFormatter.col_format(7, ModelFormatter.VIEW_FORMATTER) def view_selfcheck(value): if isinstance(value, list): + if len(value)==1: + return value[0] return "ERROR" return value @@ -289,7 +291,7 @@ class CopterDataModel(QtCore.QAbstractTableModel): def __init__(self, checks=ModelChecks, formatter=ModelFormatter, parent=None): super(CopterDataModel, self).__init__(parent) self.headers = ('copter ID', 'version', ' animation ID ', ' battery ', ' system ', 'sensors', - ' mode ', 'checks', 'current x y z yaw frame_id', ' start x y z ', 'dt') + ' mode ', ' checks ', 'current x y z yaw frame_id', ' start x y z ', 'dt') self.data_contents = [] self.checks = checks From f07fb205a5d0c0c2b4066eb21ddecc26e84f3bde Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Tue, 17 Dec 2019 08:57:01 +0000 Subject: [PATCH 23/59] Client: Add try/except dummy for getting map --- Drone/client.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Drone/client.py b/Drone/client.py index 5d37e16..f004e2d 100644 --- a/Drone/client.py +++ b/Drone/client.py @@ -216,13 +216,15 @@ class Client(object): if isinstance(error, OSError): if error.errno == errno.EINTR: raise KeyboardInterrupt - - mapping = self.selector.get_map().values() - notifier_key = self.selector.get_key(messaging.NotifierSock().get_sock()) - notify_only= len(mapping) == 1 and notifier_key in mapping - if notify_only or not mapping: - logger.warning("No active connections left!") - return + try: + mapping = self.selector.get_map().values() + notifier_key = self.selector.get_key(messaging.NotifierSock().get_sock()) + notify_only= len(mapping) == 1 and notifier_key in mapping + if notify_only or not mapping: + logger.warning("No active connections left!") + return + except (RuntimeError, KeyError) as e: + logger.error("Exception {} occured when getting net map!".format(e)) @messaging.message_callback("config_write") From fab15a72001bb2fd9e20ac515ff66fe3eed08307 Mon Sep 17 00:00:00 2001 From: artem30801 <38689676+artem30801@users.noreply.github.com> Date: Thu, 19 Dec 2019 17:56:38 +0300 Subject: [PATCH 24/59] Updated sideenu and table docs --- docs/ru/img/server_sidemenu.png | Bin 0 -> 10048 bytes docs/ru/server.md | 29 +++++++++++++++++++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) create mode 100644 docs/ru/img/server_sidemenu.png diff --git a/docs/ru/img/server_sidemenu.png b/docs/ru/img/server_sidemenu.png new file mode 100644 index 0000000000000000000000000000000000000000..ad0a1e61a2c62f93ac01465fddf8e86ea5d9130b GIT binary patch literal 10048 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 секунд).* + * Спинбокс `Music after N seconds` - Задаёт время задержки до запуска музыки при запуске анимации. Задержка *не складывается с задержкой запуска коптеров* (`Start after N seconds`). + * Чекбокс `Play music ` - Определяет, будет ли воспроизведена музыка при запуске анимации. + * Кнопка `Preflight check` - Все выбранные клиенты выполняют самодиагностику и предполётную проверку. Результаты, вместе с другими параметрами клиента, будут отображены в таблице по мере поступления данных. * Кнопка `Start animation` - По истечению заданного в `Start after` времени, все выбранные коптеры совершат **взлёт**ные процедуры, перелетают на стартовые точки своих анимаций и *синхронно* выполнят полётное задание (анимацию). По окончанию анимации все коптеры выполнят посадку *на месте окончания своей анимации*. Кнопка деактивирована по умолчанию и если среди выбранных коптеров есть *не готовые коптеры* (все выбранные коптеры должны быть готовыми). *При нажатии запрашивается дополнительное предупреждение!* - * Кнопка `Pause` - Ставит на 'паузу' все выбранные коптеры (их очередь заданий): приостанавливается выполнение любого полётного задания. *Используйте в чрезвычайных случаях.* **Внимание!** Данная команда НЕ прерывает полёт коптера в уже указанную точку (например: элементы взлёта, посадки; следование до начальной точки анимации и т.д.) - * Кнопка `Resume` - Все выбранные коптеры *синхронизированно* продолжат выполнение своих очередей заданий (например: исполнение анимации) - * Кнопка `Stop` - Прерывает выполнение полётных заданий *ВСЕХ* подключенных коптеров. Сбрасывает очередь заданий - *действие необратимо*. **Используйте в экстренных случаях как одно из средств перехвата.** **Внимание!** Данная команда НЕ прерывает полёт коптера в уже указанную точку (например: элементы взлёта, посадки; следование до начальной точки анимации и т.д.) + * `Pause/Resume` - Позволяет ставить на паузу и возобновлять выполнение полётных задач. После каждого нажатия кнопка меняет состояние на обратное. + * Состояние`Pause` - Ставит на 'паузу' все выбранные коптеры (их очередь заданий): приостанавливается выполнение любого полётного задания. *Используйте в чрезвычайных случаях.* **Внимание!** Данная команда НЕ прерывает полёт коптера в уже указанную точку (например: элементы взлёта, посадки; следование до начальной точки анимации и т.д.) + * Состояние `Resume` - Все выбранные коптеры *синхронизированно* продолжат выполнение своих очередей заданий (например: исполнение анимации) + * Кнопка `Stop and land alll` - Прерывает выполнение полётных заданий *ВСЕХ* подключенных коптеров. Сбрасывает очередь заданий - *действие необратимо*. Выполняет полную остановку и немедленную посадку коптеров. **Используйте в экстренных случаях как одно из средств перехвата.** * Кнопка `Emergency land` - Открывает диалоговое окно дополнительного модуля быстрого выбора коптера и его последующей экстренной посадки \ дизарма. *Смотреть далее.* + * Кнопка `Disarm selected` - все выбранные коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно *отключают моторы (disarm).* Это может привести к падению и повреждению коптеров + * Кнопка `Disarm ALL` - ВСЕ коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно *отключают моторы (disarm).* Это может привести к падению и повреждению коптеров **Используйте в крайних случаях как последнее из средств перехвата.** ### Полётные функции (команды) В данном разделе находятся команды, позволяющие напрямую управлять коптером(ами). * Кнопка `Test leds` - Все выбранные коптеры выполняют двухсекундную анимацию (бегущие точки) светодиодной лентой (белым цветом). Команда *безопасна* и может быть использована для проверки работы светодиодных лент \ качества и задержки подключения к серверу \ определения соответствия коптера и его `Copter ID` в таблице. * Кнопка `Takeoff` - Все выбранные коптеры **совершают взлёт**, после чего зависают над точкой взлёта. Аналогично `Start animation`, кнопка активна, *только* если все выбранные коптеры готовы. **Внимание!** Используйте осторожно, соблюдайте технику безопасности. Не применяйте во время выполнения других полётных функций! + * Чекбокс `Z` - Если чекбокс активен, то будет и, будет использовано значение спинбокса `Z` (см. далее) при выполнении взлёта. Иначе коптерами будут использоваться значения по умолчанию, указанные в их конфигурациях. + * Спинбокс`Z` - Задаёт значение целевой высоты взлёта коптеров в метрах. * Кнопка `Flip` - Все выбранные коптеры **совершают флип (flip)** - переворот на 360 градусов вокруг одной из *горизонтальных* осей. **Внимание!** Используйте осторожно, соблюдайте технику безопасности. *Для исполнения флипа коптер должен иметь минимальную высоту >2м.* Не применяйте во время выполнения других полётных функций! * Кнопка `Land` - ВСЕ коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно *переходят в режим посадки.* **Используйте в экстренных случаях как одно из средств перехвата.** - * Кнопка `Diarm` - ВСЕ коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно *отключают моторы (disarm).* ==Это может привести к падению и повреждению коптеров== **Используйте в крайних случаях как последнее из средств перехвата.** + +### Системные команды + +- Кнопка `Reboot FCU` - перезагружает полётные контроллеры всех выбранных коптеров. ля возобновления работоспособности полётных функций и получения некоторых значений телеметрии *необходимо подождать* некоторое время до перезагрузки полётного контроллера. +- Кнопка `Calibrate gyro` - переводит полётные контроллеры всех выбранных коптеров в режим калибровки гироскопа. Перед калибровки необходимо убедится, что все коптеры неподвижно стоят на ровном полу параллельно полу (и будут в течение всего периода калибровки). +- Кнопка `Calibrate level` - переводит полётные контроллеры всех выбранных коптеров в режим калибровки уровня горизонта. Перед калибровки необходимо убедится, что все коптеры неподвижно стоят на ровном полу параллельно полу (и будут в течение всего периода калибровки). ## Таблица состояния коптеров (клиентов) При первом подключении клиента к серверу в таблицу добавляется строка для отображения состояния клиента, содержащая начальные данные, переданные клиентом при подключении (`Copter ID`). Строки НЕ удаляются после зарегистрированного отключения клиента. Строки можно сортировать по возрастанию \ убыванию значений любого из столбцов (кликнув по заголовку столбца). @@ -134,10 +150,11 @@ time_delta_max = 1.0 * зелёным, если значение (состояние) ячейки удовлетворительно (согласно внутренним проверкам) ### Столбцы таблицы * `copter ID` - имя (идентификатор) клиента. Может быт сконфигурирован на стороне клиента. Отображается сразу при подключении клиента. Рядом с каждым ID коптера расположен чекбокс - коптеры, чей ID отмечен чекбоксом положительно (галочка), считаются *выбранными*. +* `version` - хеш-код текущей git версии клиента. * `animation ID` - внутреннее название файла анимации, подгруженного клиентом. Отображается после выполнения `selfcheck`. *Проверьте соответствие названий файлов анимаций у коптеров* * `battery V` - абсолютное значение напряжения на аккумуляторе коптера (в Вольтах, по данным полётного контроллера). *Убедитесь, что напряжение не ниже порогового для вашего аккумулятора.* **При критически низком значении коптер считается не готовым** - блокируется возможность взлёта и старта анимации. * `battery %` - относительное значение напряжения на аккумуляторе коптера. Значение рассчитывается по среднему напряжению (по данным полётного контроллера) на ячейку аккумулятора (банку). *Убедитесь, что уровень заряда перед вылетом не менее 30%* **При критически низком значении коптер считается не готовым** - блокируется возможность взлёта и старта анимации. -* `selfcheck` - Все дополнительные сообщения и ошибки при самодиагностике (*Смотреть далее.*). При успешном прохождении самодиагностики без ошибок выводится значение `OK`, ячейка подсвечивается зелёным цветом. **При наличии ошибок коптер считается не готовым** - блокируется возможность взлёта и старта анимации. +* `checks` - Все дополнительные сообщения и ошибки при самодиагностике (*Смотреть далее.*). При успешном прохождении самодиагностики без ошибок выводится значение `OK`, ячейка подсвечивается зелёным цветом. **При наличии ошибок коптер считается не готовым** - блокируется возможность взлёта и старта анимации. * `time delta` - Разница между временем на сервере и клиенте (в секундах). *При слишком больших значениях сигнализирует об отсутствии синхронизации времени между коптером и клиентом!* В это значение так же входит сетевая задержка. # Дополнительные операции From 4da5a91e538e613097c6b6ba130105775f6506cd Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Tue, 24 Dec 2019 19:44:25 +0300 Subject: [PATCH 25/59] Server: change config order accordingly to the docs --- Server/server_config.ini | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Server/server_config.ini b/Server/server_config.ini index 6dcbc87..1f6da13 100644 --- a/Server/server_config.ini +++ b/Server/server_config.ini @@ -3,6 +3,11 @@ port = 25000 buffer_size = 1024 remove_disconnected = False +[CHECKS] +battery_percentage_min = 50 +start_pos_delta_max = 1 +time_delta_max = 1 + [BROADCAST] use_broadcast = True broadcast_port = 8181 @@ -12,8 +17,3 @@ 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 From bb8a90bc4074643f086b031e6c7beb0cb942b165 Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Tue, 24 Dec 2019 19:45:25 +0300 Subject: [PATCH 26/59] docs: Review article about server --- docs/ru/server.md | 131 ++++++++++++++++++++++++++-------------------- 1 file changed, 75 insertions(+), 56 deletions(-) diff --git a/docs/ru/server.md b/docs/ru/server.md index 79031a7..691de8e 100644 --- a/docs/ru/server.md +++ b/docs/ru/server.md @@ -1,11 +1,19 @@ # Настройка сервера + ## Файл конфигурации -Конфигурация сервера задаётся в файле Server/server_config.ini, имеющем вид (по умолчанию): + +Конфигурация сервера задаётся в файле [server_config.ini](../../Server/server_config.ini), имеющем следующий вид по умолчанию: + ```ini [SERVER] port = 25000 buffer_size = 1024 remove_disconnected = False + +[CHECKS] +battery_percentage_min = 50.0 +start_pos_delta_max = 1.0 +time_delta_max = 1.0 [BROADCAST] use_broadcast = True @@ -16,62 +24,70 @@ broadcast_delay = 5.0 use_ntp = False host = ntp1.stratum2.ru port = 123 - -[CHECKS] -battery_percentage_min = 50.0 -start_pos_delta_max = 1.0 -time_delta_max = 1.0 ``` + Конфигурация по умолчанию является полностью работоспособной и не требует изменений для быстрого начала работы системы. -### Раздел 'Server' -В этом разделе задаются параметры сетевого взаимодействия сервера, доступны следующие параметры: + +### Раздел [SERVER] + +В этом разделе задаются параметры сетевого взаимодействия сервера. Доступны следующие параметры: * `port` - TCP порт, на котором будут приниматься входящие соединения от клиентов (коптеров). При использовании broadcast данный порт будет сконфигурирован у клиента автоматически. *Рекомендуется изменить значение по умолчанию в целях безопасности* (любое пятизначное и более число, если другое ПО не использует выбранный порт). * `buffer_size` - размер буфера при приёме и передаче данных. *Не рекомендуется изменять. Рекомендуется использовать единое значение у сервера и клиентов.* - * `remove_disconnected` - Определяет поведение при разрыве связи с клиентом. При значении `True` вся информация о клиенте *будет удалена* как из внутренней памяти, так и *из таблицы*. *Это может привести к 'скачкам' таблицы при отключении клиентов.* При значении `False` отключённые клиенты *не будут* удалены из таблицы, но будут отображены с подсвечиванием ячейки `copter ID` красным цветом. Все данные будут сохранены. При переподключении клиента, он будет ассоциирован с той же строкой таблицы, а ячейка со значением `copter ID` вновь станет зелёного цвета. + * `remove_disconnected` - Определяет поведение при разрыве связи с клиентом. При значении `True` вся информация о клиенте *будет удалена* как из внутренней памяти, так и *из таблицы*. *Это может привести к 'скачкам' таблицы при отключении клиентов.* При значении `False` отключённые клиенты *не будут* удалены из таблицы, но будут отображены с подсвечиванием ячейки в столбце `copter ID` красным цветом. Все данные будут сохранены. При переподключении клиента, он будет ассоциирован с той же строкой таблицы, а ячейка со значением `copter ID` вновь станет зелёного цвета. -### Раздел 'Checks' -* `battery_percentage_min` - Минимальный заряд батарии коптера *в процентах* (дробное значение от 0 до 100), допустимый для взлёта. Значение меньше указанного будет отмечено как неудовлетворительное (см. раздел таблицы). -* `start_pos_delta_max` - Максимальное расстояние *в метрах* (дробное значение от 0 до 'inf') от текущего положения коптера до его точки взлёта в файле анимации, допустимое для взлёта. Допустимо использование строки 'inf' для любого допустимого расстояния. Значение больше указанного будет отмечено как неудовлетворительное (см. раздел таблицы). -* `time_delta_max` - Максимальная разница (абсолютное значение) *в секундах* (дробное значение от 0 до 'inf') между временем сервера и клиента (включая сетевую задержку), допустимая для взлёта. Допустимо использование строки 'inf' для любой допустимой разницы. Значение больше указанного будет отмечено как неудовлетворительное (см. раздел таблицы). +### Раздел [CHECKS] + +В этом разделе задаются параметры проверок коптера, которые регулируются на стороне сервера. Доступны следующие параметры: + +* `battery_percentage_min` - Минимальный заряд батарии коптера, допустимый для взлёта. Указывается *в процентах* (дробное значение от 0 до 100). Значение меньше указанного будет отмечено в столбце `battery` как неудовлетворительное. +* `start_pos_delta_max` - Максимальное расстояние от текущего положения коптера до его точки взлёта в файле анимации, допустимое для взлёта. Указывается *в метрах* (дробное значение от 0 до 'inf'). Значение больше указанного будет отмечено в столбце `start x y z` как неудовлетворительное. Допустимо использование строки 'inf' для любого допустимого расстояния. +* `time_delta_max` - Максимальная разница (абсолютное значение) между временем сервера и клиента (включая сетевую задержку), допустимая для взлёта. Указывается *в секундах* (дробное значение от 0 до 'inf'). Значение больше указанного будет отмечено в столбце `dt` как неудовлетворительное. + +### Раздел [BROADCAST] + +Сервер может использовать UDP broadcast, чтобы передавать клиентам актуальную информацию о конфигурации сервера. Таким образом становится возможным автоматическое подключение клиентов к серверу без необходимости дополнительной ручной конфигурации. В данном разделе задаются параметры этого механизма: -### Раздел 'Broadcast' -Сервер использует UDP broadcast (на адрес 255.255.255.255 с выбранным портом), чтобы передавать клиентам (коптерам) актуальную информацию о конфигурации сервера и собственном адресе сервера для подключения (IP адрес и порт сервера). Таким образом, обеспечивается автоматическое подключение клиентов к серверу без необходимости дополнительной ручной конфигурации. В данном разделе задаются параметры этого механизма. * `use_broadcast` - будут ли использованы broadcast'ы для передачи данных (при значении `False` broadcast'ы НЕ будут отправляться). Используйте `False` в случае повышенных требований безопасности, перегруженности сети или невозможности передачи по широковещательному каналу (из-за конфигурации брандмауэра или сети) * `broadcast_port` - UDP порт, по которому будет осуществляться отправка сообщений. *Рекомендуется изменить значение по умолчанию в целях безопасности.* **Внимание!** При изменении этого параметра клиенты НЕ смогут принимать сообщения автоконфигурации до изменения (вручную) соответствующего параметра в конфигурации клиента на равное значение. - * `broadcast_delay` - периодичность (в секундах, целочисленное значение), с которой будет происходить отправка broadcast сообщений. Увеличьте задержку для уменьшения нагрузки на сеть. *ИЛИ* Уменьшите задержку для уменьшения времени отклика и подключения при первом запуске клиентов. - ### Раздел 'NTP' + * `broadcast_delay` - периодичность (в секундах, целочисленное значение), с которой будет происходить отправка broadcast сообщений. Увеличьте задержку для уменьшения нагрузки на сеть. Уменьшите задержку для уменьшения времени отклика и подключения при первом запуске клиентов. + + ### Раздел [NTP] + Помимо синхронизации времени (с миллисекундной точностью) с помощью пакета chrony, предоставляется альтернатива - возможность использования внешних (при наличии соединения локальной сети с интернетом) или внутрисетевых NTP-серверов. **Внимание!** Для корректной работы системы, и сервер, *и* клиенты должны использовать единый способ синхронизации времени (набор параметров в этом разделе). Данный раздел полностью унифицирован и для сервера, и для клиентов. - * `use_ntp` - Определяет, будет ли использоваться синхронизация времени с помощью NTP. (при значении `False` будет использовано локальное время ОС (синхронизируется автоматически при использовании chrony). *Рекомендуется использование crhony, а не NTP* + * `use_ntp` - Определяет, будет ли использоваться синхронизация времени с помощью NTP. (при значении `False` будет использовано локальное время ОС (синхронизируется автоматически при использовании chrony). *Рекомендуется использование chrony, а не NTP* * `host` - имя хоста или IP адрес NTP сервера (локального или удаленного) * `port` - порт, используемый NTP сервером # Интерфейс сервера + Сервер имеет визуальный графический интерфейс для удобства взаимодействия. ![Интерфейс сервера](img/server_gui.png) ## Глоссарий + Некоторые термины, используемые для краткости записи: -* Готовый [к полёту] коптер: - Прошедший предполётную проверку (`Preflight check`) и имеющий удовлетворительные результаты по всем необходимым столбцам (зелёные ячейки в таблице). Учитываются проверки аккумулятора и сообщения selfcheck. -* Не готовый [к полёту] коптер: -НЕ прошедший предполётную проверку (`Preflight check`) (не имеющий её результатов в таблице - жёлтые ячейки) или же имеющий НЕудовлетворительные результаты (красные ячейки в таблице). Учитываются проверки аккумулятора и сообщения selfcheck. +* Удовлетворительный - прошедший проверку определённых условий. В таблице отмечается зелёным цветом. +* Готовый к полёту: прошедший предполётную проверку и имеющий удовлетворительные результаты по всем столбцам, кроме `animation ID` и `dt`. +* Готовый к воспроизведению анимации: прошедший предполётную проверку и имеющий удовлетворительные результаты по всем столбцам. +* FCU - Flight Controller Unit, полётный контроллер. ## Меню + ### Раздел 'Server' ![Скриншот меню](img/server-server.png) Данный раздел содержит несколько утилит по отправке различных данных на *выбранные* клиенты. **Внимание!** Не пытайтесь использовать данные команды во время полёта коптеров! * `Send Animations` - отправка файлов анимации (экспортированных аддоном к Blender) на выбранные клиенты (коптеры). В диалоговом окне необходимо выбрать *папку*, содержащую файлы анимации (автоматически создается аддоном). Каждый файл анимации будет отправлен на клиент с именем (copter ID), соответствующим имени файла без расширения. -* `Send Configurations` - отправка *единого* файла конфигурации клиента на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл конфигурации в установленном формате. Файл конфигурации может быть неполным, в таком случае будут перезаписаны лишь указанные в файле параметры. *Не рекомендуется использовать данное действие для массовой перезаписи `Copter ID`, кроме значения `/hostname`.* **Внимание!** НЕ отправляйте на клиенты файл конфигурации сервера. -* `Send Launch files ` - отправка launch-файлов конфигурации сервиса `clever`. . В диалоговом окне необходимо выбрать *папку*, содержащую файлы конфигурации с расширением `.launch`. Все файлы с таким расширением будут отправлены *на каждый* из клиентов. **Внимание!** Существующие файлы конфигурации на коптерах будут перезаписаны, однако файлы, не отправленные сервером, не будут удалены или модифицированы. +* `Send Configurations` - отправка *единого* файла конфигурации клиента на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл конфигурации в установленном формате. Файл конфигурации может быть неполным, в таком случае будут перезаписаны лишь указанные в файле параметры. *Не рекомендуется использовать данное действие для массовой перезаписи* `Copter ID` *, кроме значения `/hostname`.* **Внимание!** НЕ отправляйте на клиенты файл конфигурации сервера. +* `Send Launch files ` - отправка launch-файлов конфигурации сервиса `clever`. В диалоговом окне необходимо выбрать *папку*, содержащую файлы конфигурации с сширением `.launch`. Все файлы с таким расширением будут отправлены *на каждый* из клиентов. **Внимание!** Существующие файлы конфигурации на коптерах будут перезаписаны, однако файлы, не отправленные сервером, не будут удалены или модифицированы. * `Send Aruco map` - отправка *единого* файла карты aruco маркеров на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл карты в установленном формате. Файл на клиенте будет перезаписан. После получения и записи файла клиент автоматически перезапустит сервис `clever`. Для возобновления работоспособности полётных функций и получения некоторых значений телеметрии *необходимо подождать* некоторое время до полного запуска сервиса. -* `Send Camera Calibrations` - отправка yaml-файлов калибрации камеры для сервиса `clever`. В диалоговом окне необходимо выбрать *папку*, содержащую файлы конфигурации с расширением `.yaml`. Каждый файл калибрации будет отправлен на клиент с именем (copter ID), соответствующим имени файла без расширения. **Внимание!** Существующий файл калибрации на коптере будет перезаписан. -* `Send FCU parameters` - отправка и запись *единого* файла конфигураций полётного контроллера (FCU) на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл параметров в установленном формате. Параметры на полётном контроллере будут перезаписаны. После получения и записи файла клиент автоматически. -* `Developer mode`: **Внимание!** Используйте данные действия с большой осторожностью - * `Send any file` - отправка *одного* любого файла на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл. Далее, необходимо указать путь, по которому данный файл будет записан на клиенты (не включая имя файла) +* `Send Camera Calibrations` - отправка yaml-файлов калибровки камеры для сервиса `clever`. В диалоговом окне необходимо выбрать *папку*, содержащую файлы конфигурации с расширением `.yaml`. Каждый файл калибровки будет отправлен на клиент с именем (copter ID), соответствующим имени файла без расширения. **Внимание!** Существующий файл калибровки на коптере будет перезаписан. +* `Send FCU parameters` - отправка и запись *единого* файла конфигураций полётного контроллера (FCU) на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл параметров в установленном формате. Параметры на полётном контроллере будут перезаписаны. +* `Developer mode`: **Внимание!** Используйте данные действия с большой осторожностью. + * `Send any file` - отправка *одного* любого файла на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл. Далее, необходимо указать путь, по которому данный файл будет записан на клиенты (не включая имя файла). * `Send any command` - отправка и выполнение любой команды терминала на все выбранные клиенты. В диалоговом окне необходимо ввести требуемую команду. Команды *могут* использовать `sudo`-права. * `Select all drones` (`Ctrl+A`) Выделяет все коптеры в таблице. При следующем вызове команды, выделение всех коптеров будет отменено. @@ -93,17 +109,17 @@ time_delta_max = 1.0 ![Скриншот раздела](img/server-animation.png) -- `Set start X Y to current position` - Устанавливает точку старта анимации у выбранных клиентов на значения текущей позиции по X Y -- `Reset start position` - Устанавливает точку старта анимации у выбранных клиентов на значения `0.0`, `0.0` +- `Set start X Y to current position` - Устанавливает точку старта анимации у выбранных клиентов на значения текущей позиции по X Y. +- `Reset start position` - Устанавливает точку старта анимации у выбранных клиентов на значения `0.0`, `0.0`. ### Раздел 'Music' ![Скриншот раздела](img/server-music.png) -- `Select music file` - Загружает выбранный музыкальный файл (поддерживаемые расширения: `.mp3`, `.wav`) для дальнейшего воспроизведения вручную или -- `Play music` - Воспроизводит загруженную музыку +- `Select music file` - Загружает выбранный музыкальный файл (поддерживаемые расширения: `.mp3`, `.wav`) для дальнейшего воспроизведения вручную или через определённое время после старта анимации. +- `Play music` - Воспроизводит загруженную музыку. -- `Stop music` - Останавливает воспроизведение проигрываемой музыки (запущенной вручную или автоматически) +- `Stop music` - Останавливает воспроизведение проигрываемой музыки. @@ -114,54 +130,57 @@ time_delta_max = 1.0 Данный раздел команд предназначен для выскоуровневого управления роем дронов. - * Спинбокс `Start after N seconds` - Задаёт время задержки до синхронного запуска выполнения анимаций коптерами. *Не рекомендуется использовать `0` (нулевую задержку). Для загруженных\подверженных помехам\имеющих большой пинг сетей рекомендуется использовать бо́льшие значения (>5 секунд).* - * Спинбокс `Music after N seconds` - Задаёт время задержки до запуска музыки при запуске анимации. Задержка *не складывается с задержкой запуска коптеров* (`Start after N seconds`). + * Спинбокс `Start after` - Задаёт время задержки синхронного запуска выполнения анимаций коптерами после нажатия на кнопку `Start animation`. Для загруженных\подверженных помехам\имеющих большой пинг сетей рекомендуется использовать значения больше нуля. + * Спинбокс `Music after` - Задаёт время задержки запуска музыки после нажатия на кнопку `Start animation`. * Чекбокс `Play music ` - Определяет, будет ли воспроизведена музыка при запуске анимации. - * Кнопка `Preflight check` - Все выбранные клиенты выполняют самодиагностику и предполётную проверку. Результаты, вместе с другими параметрами клиента, будут отображены в таблице по мере поступления данных. - * Кнопка `Start animation` - По истечению заданного в `Start after` времени, все выбранные коптеры совершат **взлёт**ные процедуры, перелетают на стартовые точки своих анимаций и *синхронно* выполнят полётное задание (анимацию). По окончанию анимации все коптеры выполнят посадку *на месте окончания своей анимации*. Кнопка деактивирована по умолчанию и если среди выбранных коптеров есть *не готовые коптеры* (все выбранные коптеры должны быть готовыми). *При нажатии запрашивается дополнительное предупреждение!* + * Кнопка `Preflight check` - Все выбранные клиенты выполняют самодиагностику и предполётную проверку. Результаты, вместе с другими параметрами клиента, будут отображены в таблице по мере поступления данных. Необходима в том случае, если на клиенте не настроена автоматическая передача телеметрии. + * Кнопка `Start animation` - По истечению заданного в спинбоксе `Start after` времени, все выбранные коптеры начинают синхронное воспроизведение анимации. По окончанию анимации все коптеры выполнят посадку *на месте окончания своей анимации*. Кнопка активна только в том случае, если все коптеры готовы к воспроизведению анимации. При нажатии запрашивается дополнительное предупреждение. * `Pause/Resume` - Позволяет ставить на паузу и возобновлять выполнение полётных задач. После каждого нажатия кнопка меняет состояние на обратное. - * Состояние`Pause` - Ставит на 'паузу' все выбранные коптеры (их очередь заданий): приостанавливается выполнение любого полётного задания. *Используйте в чрезвычайных случаях.* **Внимание!** Данная команда НЕ прерывает полёт коптера в уже указанную точку (например: элементы взлёта, посадки; следование до начальной точки анимации и т.д.) - * Состояние `Resume` - Все выбранные коптеры *синхронизированно* продолжат выполнение своих очередей заданий (например: исполнение анимации) - * Кнопка `Stop and land alll` - Прерывает выполнение полётных заданий *ВСЕХ* подключенных коптеров. Сбрасывает очередь заданий - *действие необратимо*. Выполняет полную остановку и немедленную посадку коптеров. **Используйте в экстренных случаях как одно из средств перехвата.** - * Кнопка `Emergency land` - Открывает диалоговое окно дополнительного модуля быстрого выбора коптера и его последующей экстренной посадки \ дизарма. *Смотреть далее.* - * Кнопка `Disarm selected` - все выбранные коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно *отключают моторы (disarm).* Это может привести к падению и повреждению коптеров + * Состояние`Pause` - Ставит на паузу очередь заданий всех выбранных коптеров: приостанавливается выполнение любого полётного задания. Рекомендуется использовать в чрезвычайных ситуациях для определения неисправного коптера. **Внимание!** Данная команда НЕ прерывает полёт коптера в уже указанную точку (например: элементы взлёта, посадки; следование до начальной точки анимации и т.д.) + * Состояние `Resume` - Все выбранные коптеры *синхронизированно* продолжат выполнение своих очередей заданий (например исполнение анимации) + * Кнопка `Stop and land all` - Прерывает выполнение полётных заданий *ВСЕХ* подключенных коптеров. Сбрасывает очередь заданий - *действие необратимо*. Выполняет полную остановку и немедленную посадку коптеров. **Используйте в экстренных случаях как одно из средств перехвата.** + * Кнопка `Emergency land` - Открывает диалоговое окно дополнительного модуля быстрого выбора коптера и его последующей экстренной посадки \ дизарма. *Полное описание в конце статьи.* + * Кнопка `Disarm selected` - все выбранные коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно *отключают моторы (disarm).* Это может привести к падению и повреждению коптеров. * Кнопка `Disarm ALL` - ВСЕ коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно *отключают моторы (disarm).* Это может привести к падению и повреждению коптеров **Используйте в крайних случаях как последнее из средств перехвата.** + ### Полётные функции (команды) + В данном разделе находятся команды, позволяющие напрямую управлять коптером(ами). * Кнопка `Test leds` - Все выбранные коптеры выполняют двухсекундную анимацию (бегущие точки) светодиодной лентой (белым цветом). Команда *безопасна* и может быть использована для проверки работы светодиодных лент \ качества и задержки подключения к серверу \ определения соответствия коптера и его `Copter ID` в таблице. - * Кнопка `Takeoff` - Все выбранные коптеры **совершают взлёт**, после чего зависают над точкой взлёта. Аналогично `Start animation`, кнопка активна, *только* если все выбранные коптеры готовы. **Внимание!** Используйте осторожно, соблюдайте технику безопасности. Не применяйте во время выполнения других полётных функций! - * Чекбокс `Z` - Если чекбокс активен, то будет и, будет использовано значение спинбокса `Z` (см. далее) при выполнении взлёта. Иначе коптерами будут использоваться значения по умолчанию, указанные в их конфигурациях. - * Спинбокс`Z` - Задаёт значение целевой высоты взлёта коптеров в метрах. + * Кнопка `Takeoff` - Все выбранные коптеры **совершают вертикальный взлёт**, после чего зависают над точкой взлёта. Кнопка активна, *только* если все выбранные коптеры готовы к полёту. **Внимание!** Используйте осторожно, соблюдайте технику безопасности. Не применяйте во время выполнения других полётных функций! + * Чекбокс `Z` - Если чекбокс активен, коптер взлетит в указанное значение по `z`. Иначе коптерами будут использоваться значения по умолчанию, указанные в их конфигурациях, а взлёт будет производиться относительно текущей высоты. + * Спинбокс`Z` - Задаёт значение координаты `z` взлёта коптеров в метрах. * Кнопка `Flip` - Все выбранные коптеры **совершают флип (flip)** - переворот на 360 градусов вокруг одной из *горизонтальных* осей. **Внимание!** Используйте осторожно, соблюдайте технику безопасности. *Для исполнения флипа коптер должен иметь минимальную высоту >2м.* Не применяйте во время выполнения других полётных функций! - * Кнопка `Land` - ВСЕ коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно *переходят в режим посадки.* **Используйте в экстренных случаях как одно из средств перехвата.** + * Кнопка `Land` - Все выбранные коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно *переходят в режим посадки.* **Используйте в экстренных случаях как одно из средств перехвата.** ### Системные команды -- Кнопка `Reboot FCU` - перезагружает полётные контроллеры всех выбранных коптеров. ля возобновления работоспособности полётных функций и получения некоторых значений телеметрии *необходимо подождать* некоторое время до перезагрузки полётного контроллера. -- Кнопка `Calibrate gyro` - переводит полётные контроллеры всех выбранных коптеров в режим калибровки гироскопа. Перед калибровки необходимо убедится, что все коптеры неподвижно стоят на ровном полу параллельно полу (и будут в течение всего периода калибровки). -- Кнопка `Calibrate level` - переводит полётные контроллеры всех выбранных коптеров в режим калибровки уровня горизонта. Перед калибровки необходимо убедится, что все коптеры неподвижно стоят на ровном полу параллельно полу (и будут в течение всего периода калибровки). +- Кнопка `Reboot FCU` - перезагружает полётные контроллеры всех выбранных коптеров. Для возобновления работоспособности полётных функций и получения некоторых значений телеметрии *необходимо подождать* некоторое время до перезагрузки полётного контроллера. +- Кнопка `Calibrate gyro` - переводит полётные контроллеры всех выбранных коптеров в режим калибровки гироскопа. Коптеры должны быть неподвижны в течение калибровки. +- Кнопка `Calibrate level` - переводит полётные контроллеры всех выбранных коптеров в режим калибровки уровня горизонта. Коптеры должны быть неподвижны в течение калибровки. ## Таблица состояния коптеров (клиентов) -При первом подключении клиента к серверу в таблицу добавляется строка для отображения состояния клиента, содержащая начальные данные, переданные клиентом при подключении (`Copter ID`). Строки НЕ удаляются после зарегистрированного отключения клиента. Строки можно сортировать по возрастанию \ убыванию значений любого из столбцов (кликнув по заголовку столбца). +При первом подключении клиента к серверу в таблицу добавляется строка для отображения состояния клиента, содержащая начальные данные, переданные клиентом при подключении (`Copter ID`). Строки можно сортировать по возрастанию \ убыванию значений любого из столбцов (кликнув по заголовку столбца). Ячейки таблицы подсвечиваются: * жёлтым, если необходимое значение отсутствует * красным, если значение (состояние) ячейки неудовлетворительно (согласно внутренним проверкам) * зелёным, если значение (состояние) ячейки удовлетворительно (согласно внутренним проверкам) + ### Столбцы таблицы -* `copter ID` - имя (идентификатор) клиента. Может быт сконфигурирован на стороне клиента. Отображается сразу при подключении клиента. Рядом с каждым ID коптера расположен чекбокс - коптеры, чей ID отмечен чекбоксом положительно (галочка), считаются *выбранными*. +* `copter ID` - идентификатор клиента. Может быть сконфигурирован на стороне клиента. Отображается сразу при подключении клиента. Рядом с каждым ID коптера расположен чекбокс - коптеры, чей ID отмечен чекбоксом положительно (галочка), считаются *выбранными*. * `version` - хеш-код текущей git версии клиента. -* `animation ID` - внутреннее название файла анимации, подгруженного клиентом. Отображается после выполнения `selfcheck`. *Проверьте соответствие названий файлов анимаций у коптеров* -* `battery V` - абсолютное значение напряжения на аккумуляторе коптера (в Вольтах, по данным полётного контроллера). *Убедитесь, что напряжение не ниже порогового для вашего аккумулятора.* **При критически низком значении коптер считается не готовым** - блокируется возможность взлёта и старта анимации. -* `battery %` - относительное значение напряжения на аккумуляторе коптера. Значение рассчитывается по среднему напряжению (по данным полётного контроллера) на ячейку аккумулятора (банку). *Убедитесь, что уровень заряда перед вылетом не менее 30%* **При критически низком значении коптер считается не готовым** - блокируется возможность взлёта и старта анимации. +* `animation ID` - внутреннее название файла анимации, подгруженного клиентом. Отображается после выполнения `selfcheck`. *Проверьте соответствие названий файлов анимаций у коптеров перед запуском.* +* `battery V` - абсолютное значение напряжения на аккумуляторе коптера в вольтах по данным полётного контроллера. *Убедитесь, что напряжение не ниже порогового для вашего аккумулятора.* **При критически низком значении коптер считается не готовым** - блокируется возможность взлёта и старта анимации. +* `battery %` - относительное значение напряжения на аккумуляторе коптера. Значение рассчитывается по среднему напряжению (по данным полётного контроллера) на ячейку аккумулятора (банку). *Убедитесь, что уровень заряда перед вылетом не менее установленного в настройках сервера.* **При критически низком значении коптер считается не готовым** - блокируется возможность взлёта и старта анимации. * `checks` - Все дополнительные сообщения и ошибки при самодиагностике (*Смотреть далее.*). При успешном прохождении самодиагностики без ошибок выводится значение `OK`, ячейка подсвечивается зелёным цветом. **При наличии ошибок коптер считается не готовым** - блокируется возможность взлёта и старта анимации. * `time delta` - Разница между временем на сервере и клиенте (в секундах). *При слишком больших значениях сигнализирует об отсутствии синхронизации времени между коптером и клиентом!* В это значение так же входит сетевая задержка. -# Дополнительные операции +# Дополнительные операции [TODO] ## Selfcheck .. -## Emergency land +## Emergency land {: #emergency-land } Модуль экстренной посадки/дизарма, предназначенный для быстрого поиска оператором визуально неисправного коптера методом бинарного поиска ### Интерфейс * Зелёная кнопка `1` - ... From 6bcf7afdae72c5aea470950fc05837d319e4d389 Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Wed, 25 Dec 2019 19:10:46 +0300 Subject: [PATCH 27/59] docs: Next review of the article about server --- docs/ru/img/server-led-emergency-land.png | Bin 0 -> 16961 bytes docs/ru/server.md | 275 +++++++++++----------- 2 files changed, 144 insertions(+), 131 deletions(-) create mode 100644 docs/ru/img/server-led-emergency-land.png diff --git a/docs/ru/img/server-led-emergency-land.png b/docs/ru/img/server-led-emergency-land.png new file mode 100644 index 0000000000000000000000000000000000000000..8105c1e0cca0f58f2e5c9c169c2b10c2d3d90c0a GIT binary patch literal 16961 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 X`P}d-aV_D63q(qCYO*D-%|HAP06-d! literal 0 HcmV?d00001 diff --git a/docs/ru/server.md b/docs/ru/server.md index 691de8e..d7a1717 100644 --- a/docs/ru/server.md +++ b/docs/ru/server.md @@ -1,3 +1,131 @@ +# Сервер + +* [Интерфейс](#интерфейс-сервера) +* [Настройка](#настройка-сервера) +* [Дополнительные операции](#дополнительные-операции) + +# Интерфейс сервера + +Сервер имеет визуальный графический интерфейс для удобства взаимодействия. + +![Интерфейс сервера](img/server_gui.png) + +## Таблица состояния коптеров + +При первом подключении клиента к серверу в таблицу добавляется строка для отображения состояния клиента, содержащая только имя клиента (`copter ID`). Если на клиентах настроена автоматическая передача телеметрии, данные в таблице будут обновляться автоматически. Строки можно сортировать по возрастанию или убыванию значений любого из столбцов, кликнув по его заголовку. + +Ячейки таблицы подсвечиваются: + +* жёлтым, если необходимое значение отсутствует +* красным, если данные в ячейке не прошли проверку +* зелёным, если данные в ячейке прошли проверку + +Коптер считается **готовым к воспроизведению анимации**, если все ячейки в строке прошли проверку и подсвечены зелёным. + +Коптер считается **готовым к полёту**, если все ячейки в строке, кроме `animation ID` и `dt`, прошли проверку и подсвечены зелёным. + +### Столбцы таблицы + +* `copter ID` - имя клиента. Может быть сконфигурирован на стороне клиента. Отображается сразу при подключении клиента. Рядом с каждым ID коптера расположен чекбокс - коптеры, чей ID отмечен чекбоксом положительно (галочка), считаются *выбранными*. Ячейки в этом столбце всегда проходят проверку. +* `version` - хеш-код текущей git версии клиента. Ячейки в этом столбце всегда проходят проверку. +* `animation ID` - внутреннее название файла анимации, подгруженного клиентом. Ячейка в данном столбце не проходит проверку, если анимация отсутствует (значение `No animation`). В остальных случаях, если ячейка не пустая, она проходит проверку. **Внимание!** Проверьте соответствие названий файлов анимаций у коптеров перед запуском. +* `battery` - значение напряжения на аккумуляторе коптера в вольтах и заряд в процентах по данным полётного контроллера. Ячейка в данном столбце проходит проверку, если значение заряда батареи выше значения [battery_percentage_min](###раздел-checks), задаваемого в настройках сервера. В остальных случаях, если ячейка не пустая, она не проходит проверку. +* `system` - состояние полётного контроллера. Ячейка в данном столбце проходит проверку, если её значение `STANDBY`. В остальных случаях, если ячейка не пустая, она не проходит проверку. +* `sensors` - состояние калибровки компаса, акселлерометра и гироскопа полётного контроллера. Ячейка в данном столбце проходит проверку, если её значение `OK`. В остальных случаях, если ячейка не пустая, она не проходит проверку. +* `mode` - режим полётного контроллера. Ячейка в данном столбце не проходит проверку, если её значение `NO_FCU` или содержит `CMODE`. В остальных случаях, если ячейка не пустая, она проходит проверку. +* `checks` - состояние самодиагностики коптера. Ячейка в данном столбце проходит проверку, если её значение `OK`. В остальных случаях, если ячейка не пустая, она не проходит проверку. +* `current x y z yaw frame_id` - текущее положение коптера с указанием названия системы координат. Ячейка в данном столбце не проходит проверку, если её значение `NO_POS` или содержит `nan`. В остальных случаях, если ячейка не пустая, она проходит проверку. +* `start x y z` - стартовое положение коптера для воспроизведения анимации. Ячейка в данном столбце не проходит проверку, если её значение `NO_POS` или разница между текущим и стартовым положением коптера больше значения [start_pos_delta_max](###раздел-checks). В остальных случаях, если ячейка не пустая, она проходит проверку. +* `dt` - разница между временем на сервере и клиенте в секундах, включая сетевую задержку. Ячейка в данном столбце проходит проверку, если её значение меньше значения [time_delta_max](###раздел-checks), задаваемого в настройках сервера. В остальных случаях, если ячейка не пустая, она не проходит проверку. При слишком больших значениях сигнализирует об отсутствии синхронизации времени между коптером и клиентом. + +## Меню + +### Раздел 'Server' + +![Скриншот раздела Server](img/server-server.png) + +Данный раздел содержит несколько утилит по отправке различных данных на *выбранные* клиенты. **Внимание!** Не используйте данные команды во время полёта коптеров! +* `Send Animations` - отправка файлов анимации, экспортированных аддоном к Blender, на выбранные коптеры. В диалоговом окне необходимо выбрать *папку*, содержащую файлы анимации. Каждый файл анимации будет отправлен на клиент с именем, соответствующим имени файла без расширения. +* `Send Configurations` - отправка *единого* файла конфигурации клиента на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл конфигурации в установленном формате. Файл конфигурации может быть неполным, в таком случае будут перезаписаны лишь указанные в файле параметры. **Внимание!** Не рекомендуется использовать данное действие для массовой перезаписи `copter ID`, кроме значения `/hostname`. **Внимание!** НЕ отправляйте на клиенты файл конфигурации сервера. +* `Send Launch files ` - отправка launch-файлов конфигурации сервиса `clever`. В диалоговом окне необходимо выбрать *папку*, содержащую файлы конфигурации с сширением `.launch`. Все файлы с таким расширением будут отправлены *на каждый* из клиентов. **Внимание!** Существующие файлы конфигурации на коптерах будут перезаписаны, однако файлы, не отправленные сервером, не будут удалены или модифицированы. +* `Send Aruco map` - отправка *единого* файла карты aruco маркеров на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл карты в установленном формате. Файл на клиенте будет перезаписан. После получения и записи файла клиент автоматически перезапустит сервис `clever`. Для возобновления работоспособности полётных функций и получения некоторых значений телеметрии *необходимо подождать* некоторое время до полного запуска сервиса. +* `Send Camera Calibrations` - отправка yaml-файлов калибровки камеры для сервиса `clever`. В диалоговом окне необходимо выбрать *папку*, содержащую файлы конфигурации с расширением `.yaml`. Каждый файл калибровки будет отправлен на клиент с именем (copter ID), соответствующим имени файла без расширения. **Внимание!** Существующий файл калибровки на коптере будет перезаписан. +* `Send FCU parameters` - отправка и запись *единого* файла конфигураций полётного контроллера (FCU) на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл параметров в установленном формате. Параметры на полётном контроллере будут перезаписаны. +* `Developer mode`: **Внимание!** Используйте данные действия с большой осторожностью. + * `Send any file` - отправка *одного* любого файла на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл. Далее, необходимо указать путь, по которому данный файл будет записан на клиенты (не включая имя файла). + * `Send any command` - отправка и выполнение любой команды терминала на все выбранные клиенты. В диалоговом окне необходимо ввести требуемую команду. Команды *могут* использовать `sudo`-права. +* `Select all drones` (`Ctrl+A`) - выделяет все коптеры в таблице. При следующем вызове команды, выделение всех коптеров будет отменено. + +### Раздел 'Drone' + +![Скриншот раздела Drone](img/server-drone.png) + +* `Set Z offfset to ground` - устанавливает собственный отступ по Z каждого из выбранных клиентов в значение, равное текущему положению по координате Z. Можно применять для выравнимания общей высоты полёта коптеров. +* `Reset Z offfset` - устанавливает собственный отступ по Z каждого из выбранных клиентов в значение `0` +* `Restart chrony` - перезапускает сервис синхронизации времени `chrony` на выбранных клиентах. Используйте для ручной синхронизации в случаях, если время между сервером и клиентами не синхронихированно. +* `Remove from table` - удаляет выбранные коптеры из таблицы. **Внимание!** В случае, если клиент был подключен, будет произведено отключение. В случае если удалённый таким образом клиент исправно функционировал, он переподключится в кратчайшие сроки. +* `Developer mode`: **Внимание!** Используйте данные действия с большой осторожностью. + * `Restart clever service` - перезапускает сервис `clever` на выбранных клиентах. Для возобновления работоспособности полётных функций и получения некоторых значений телеметрии *необходимо подождать* некоторое время до полного запуска сервиса. + * `Restart clever-show service` - перезапускает сервис шоу коптеров `clever-show` на выбранных клиентах. Во время перезапуска клиенты будут отключены. + * `Update clever-show git` - обновляет папку репозитория `clever-show` на выбранных клиентах. Файлы конфигурации клиента *не будут* перезаписаны. **Внимание!** Для того, чтобы изменения вступили в силу, *необходимо* перезапустить сервис `clever-show`. + * `Reboot all` - полностью перезагружает полётный контроллер и компьютер на выбранных коптерах. Во время перезапуска клиенты будут отключены. + +### Раздел 'Animation' + +![Скриншот раздела Animation](img/server-animation.png) + +- `Set start X Y to current position` - устанавливает точку старта анимации у выбранных клиентов в значения текущей позиции по X Y. +- `Reset start position` - устанавливает точку старта анимации у выбранных клиентов в значения `0.0`, `0.0`. + +### Раздел 'Music' + +![Скриншот раздела Music](img/server-music.png) + +- `Select music file` - загружает выбранный музыкальный файл для дальнейшего воспроизведения вручную или через определённое время после старта анимации. Поддерживаемые расширения: `.mp3` или `.wav`. +- `Play music` - воспроизводит загруженную музыку. + +- `Stop music` - останавливает воспроизведение проигрываемой музыки. + +## Боковая панель команд + +![Скриншот боковой панели](img/server_sidemenu.png) + +### Управление + +Данный раздел команд предназначен для выскоуровневого управления роем дронов. + + * Спинбокс `Start after` - задаёт время задержки синхронного запуска выполнения анимаций коптерами после нажатия на кнопку `Start animation`. Для загруженных, подверженных помехам или имеющих большой пинг сетей рекомендуется использовать значения больше нуля. + * Спинбокс `Music after` - задаёт время задержки запуска музыки после нажатия на кнопку `Start animation`. + * Чекбокс `Play music ` - определяет, будет ли воспроизведена музыка при запуске анимации. + * Кнопка `Preflight check` - все выбранные клиенты выполняют самодиагностику и предполётную проверку. Результаты, вместе с другими параметрами клиента, будут отображены в таблице по мере поступления данных. Необходима в том случае, если на клиенте не настроена автоматическая передача телеметрии. + * Кнопка `Start animation` - посылает время старта анимации на все выбранные коптеры с учётом заданного в спинбоксе `Start after` времени. Все выбранные коптеры начинают синхронное воспроизведение анимации после нажатия на данную кнопку и через время, заданное в спинбоксе `Start after`. По окончанию анимации все коптеры выполнят посадку на месте окончания своей анимации. Кнопка активна только в том случае, если все коптеры готовы к воспроизведению анимации. При нажатии запрашивается дополнительное предупреждение. + * `Pause/Resume` - ставит на паузу и возобновляет выполнение полётных задач. После каждого нажатия кнопка меняет состояние на обратное. + * Состояние`Pause` - ставит на паузу очередь заданий всех выбранных коптеров: приостанавливается выполнение любого полётного задания. Рекомендуется использовать в чрезвычайных ситуациях для определения неисправного коптера. **Внимание!** Данная команда НЕ прерывает полёт коптера в уже указанную точку (например: элементы взлёта, посадки; следование до начальной точки анимации и т.д.) + * Состояние `Resume` - все выбранные коптеры *синхронизированно* продолжат выполнение своих очередей заданий (например исполнение анимации) + * Кнопка `Stop and land all` - прерывает выполнение полётных заданий *ВСЕХ* подключенных коптеров. Сбрасывает очередь заданий - *действие необратимо*. Выполняет полную остановку и немедленную посадку коптеров. **Используйте в экстренных случаях как одно из средств перехвата.** + * Кнопка `Emergency land` - открывает диалоговое окно дополнительного модуля быстрого выбора коптера и его последующей экстренной посадки или дизарма. *Полное описание в конце статьи.* + * Кнопка `Disarm selected` - все выбранные коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно отключают моторы. Это может привести к падению и повреждению коптеров. + * Кнопка `Disarm ALL` - ВСЕ коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно отключают моторы. Это может привести к падению и повреждению коптеров **Используйте в крайних случаях как последнее из средств перехвата.** + +### Тестовые команды + +В данном разделе находятся команды, позволяющие напрямую управлять коптерами для их проверки. + + * Кнопка `Test leds` - все выбранные коптеры выполняют двухсекундную анимацию (бегущие точки) светодиодной лентой (белым цветом). Команда *безопасна* и может быть использована для проверки работы светодиодных лент, качества и задержки подключения к серверу или определения соответствия коптера и его `copter ID` в таблице. + * Кнопка `Takeoff` - все выбранные коптеры совершают вертикальный взлёт, после чего зависают над точкой взлёта. Кнопка активна, *только* если все выбранные коптеры готовы к полёту. **Внимание!** Используйте осторожно, соблюдайте технику безопасности. Не применяйте во время выполнения других полётных функций! + * Чекбокс `Z` - если чекбокс активен, коптер взлетит в указанное значение по `z`. Иначе коптерами будут использоваться значения по умолчанию, указанные в их конфигурациях, а взлёт будет производиться относительно текущей высоты. + * Спинбокс `Z` - задаёт значение координаты `z` взлёта коптеров в метрах. + * Кнопка `Flip` - все выбранные коптеры **совершают флип (flip)** - переворот на 360 градусов вокруг одной из *горизонтальных* осей. **Внимание!** Используйте осторожно, соблюдайте технику безопасности. **Внимание!** Для исполнения флипа коптер должен иметь минимальную высоту больше 2м. **Внимание!** Не применяйте во время выполнения других полётных функций! + * Кнопка `Land` - все выбранные коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно переходят в режим посадки. **Используйте в экстренных случаях как одно из средств перехвата.** + +### Системные команды + +В данном разделе находятся команды, исполняемые непосредственно на полётном контроллере коптера. + +- Кнопка `Reboot FCU` - перезагружает полётные контроллеры всех выбранных коптеров. Можно использовать для обновления поворота коптера при его определении только с помощью инерциальной системы коптера, например при полёте по системе позиционирования Pozyx или с помощью Optical Flow. Для возобновления работоспособности полётных функций и получения некоторых значений телеметрии *необходимо подождать* некоторое время до перезагрузки полётного контроллера. +- Кнопка `Calibrate gyro` - переводит полётные контроллеры всех выбранных коптеров в режим калибровки гироскопа. **Внимание!** Коптеры должны быть неподвижны в течение калибровки. +- Кнопка `Calibrate level` - переводит полётные контроллеры всех выбранных коптеров в режим калибровки уровня горизонта. **Внимание!** Коптеры должны быть неподвижны в течение калибровки. + # Настройка сервера ## Файл конфигурации @@ -28,7 +156,7 @@ port = 123 Конфигурация по умолчанию является полностью работоспособной и не требует изменений для быстрого начала работы системы. -### Раздел [SERVER] +### Раздел SERVER В этом разделе задаются параметры сетевого взаимодействия сервера. Доступны следующие параметры: @@ -36,7 +164,7 @@ port = 123 * `buffer_size` - размер буфера при приёме и передаче данных. *Не рекомендуется изменять. Рекомендуется использовать единое значение у сервера и клиентов.* * `remove_disconnected` - Определяет поведение при разрыве связи с клиентом. При значении `True` вся информация о клиенте *будет удалена* как из внутренней памяти, так и *из таблицы*. *Это может привести к 'скачкам' таблицы при отключении клиентов.* При значении `False` отключённые клиенты *не будут* удалены из таблицы, но будут отображены с подсвечиванием ячейки в столбце `copter ID` красным цветом. Все данные будут сохранены. При переподключении клиента, он будет ассоциирован с той же строкой таблицы, а ячейка со значением `copter ID` вновь станет зелёного цвета. -### Раздел [CHECKS] +### Раздел CHECKS В этом разделе задаются параметры проверок коптера, которые регулируются на стороне сервера. Доступны следующие параметры: @@ -44,7 +172,7 @@ port = 123 * `start_pos_delta_max` - Максимальное расстояние от текущего положения коптера до его точки взлёта в файле анимации, допустимое для взлёта. Указывается *в метрах* (дробное значение от 0 до 'inf'). Значение больше указанного будет отмечено в столбце `start x y z` как неудовлетворительное. Допустимо использование строки 'inf' для любого допустимого расстояния. * `time_delta_max` - Максимальная разница (абсолютное значение) между временем сервера и клиента (включая сетевую задержку), допустимая для взлёта. Указывается *в секундах* (дробное значение от 0 до 'inf'). Значение больше указанного будет отмечено в столбце `dt` как неудовлетворительное. -### Раздел [BROADCAST] +### Раздел BROADCAST Сервер может использовать UDP broadcast, чтобы передавать клиентам актуальную информацию о конфигурации сервера. Таким образом становится возможным автоматическое подключение клиентов к серверу без необходимости дополнительной ручной конфигурации. В данном разделе задаются параметры этого механизма: @@ -52,140 +180,25 @@ port = 123 * `broadcast_port` - UDP порт, по которому будет осуществляться отправка сообщений. *Рекомендуется изменить значение по умолчанию в целях безопасности.* **Внимание!** При изменении этого параметра клиенты НЕ смогут принимать сообщения автоконфигурации до изменения (вручную) соответствующего параметра в конфигурации клиента на равное значение. * `broadcast_delay` - периодичность (в секундах, целочисленное значение), с которой будет происходить отправка broadcast сообщений. Увеличьте задержку для уменьшения нагрузки на сеть. Уменьшите задержку для уменьшения времени отклика и подключения при первом запуске клиентов. - ### Раздел [NTP] + ### Раздел NTP - Помимо синхронизации времени (с миллисекундной точностью) с помощью пакета chrony, предоставляется альтернатива - возможность использования внешних (при наличии соединения локальной сети с интернетом) или внутрисетевых NTP-серверов. **Внимание!** Для корректной работы системы, и сервер, *и* клиенты должны использовать единый способ синхронизации времени (набор параметров в этом разделе). Данный раздел полностью унифицирован и для сервера, и для клиентов. + Помимо синхронизации времени (с миллисекундной точностью) с помощью пакета chrony, предоставляется альтернатива - возможность использования внешних (при наличии соединения локальной сети с интернетом) или внутрисетевых NTP-серверов. **Внимание!** Для корректной работы системы, **и сервер, и клиенты** должны использовать единый способ синхронизации времени (набор параметров в этом разделе). Данный раздел полностью унифицирован и для сервера, и для клиентов. * `use_ntp` - Определяет, будет ли использоваться синхронизация времени с помощью NTP. (при значении `False` будет использовано локальное время ОС (синхронизируется автоматически при использовании chrony). *Рекомендуется использование chrony, а не NTP* * `host` - имя хоста или IP адрес NTP сервера (локального или удаленного) * `port` - порт, используемый NTP сервером -# Интерфейс сервера +# Дополнительные операции -Сервер имеет визуальный графический интерфейс для удобства взаимодействия. +## Emergency land -![Интерфейс сервера](img/server_gui.png) +Модуль визуальной экстренной посадки, предназначенный для быстрого поиска оператором визуально неисправного коптера методом бинарного поиска. Для успешного применения на всех коптерах должна быть установлена светодиодная лента. -## Глоссарий - -Некоторые термины, используемые для краткости записи: -* Удовлетворительный - прошедший проверку определённых условий. В таблице отмечается зелёным цветом. -* Готовый к полёту: прошедший предполётную проверку и имеющий удовлетворительные результаты по всем столбцам, кроме `animation ID` и `dt`. -* Готовый к воспроизведению анимации: прошедший предполётную проверку и имеющий удовлетворительные результаты по всем столбцам. -* FCU - Flight Controller Unit, полётный контроллер. - -## Меню - -### Раздел 'Server' - -![Скриншот меню](img/server-server.png) - -Данный раздел содержит несколько утилит по отправке различных данных на *выбранные* клиенты. **Внимание!** Не пытайтесь использовать данные команды во время полёта коптеров! -* `Send Animations` - отправка файлов анимации (экспортированных аддоном к Blender) на выбранные клиенты (коптеры). В диалоговом окне необходимо выбрать *папку*, содержащую файлы анимации (автоматически создается аддоном). Каждый файл анимации будет отправлен на клиент с именем (copter ID), соответствующим имени файла без расширения. -* `Send Configurations` - отправка *единого* файла конфигурации клиента на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл конфигурации в установленном формате. Файл конфигурации может быть неполным, в таком случае будут перезаписаны лишь указанные в файле параметры. *Не рекомендуется использовать данное действие для массовой перезаписи* `Copter ID` *, кроме значения `/hostname`.* **Внимание!** НЕ отправляйте на клиенты файл конфигурации сервера. -* `Send Launch files ` - отправка launch-файлов конфигурации сервиса `clever`. В диалоговом окне необходимо выбрать *папку*, содержащую файлы конфигурации с сширением `.launch`. Все файлы с таким расширением будут отправлены *на каждый* из клиентов. **Внимание!** Существующие файлы конфигурации на коптерах будут перезаписаны, однако файлы, не отправленные сервером, не будут удалены или модифицированы. -* `Send Aruco map` - отправка *единого* файла карты aruco маркеров на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл карты в установленном формате. Файл на клиенте будет перезаписан. После получения и записи файла клиент автоматически перезапустит сервис `clever`. Для возобновления работоспособности полётных функций и получения некоторых значений телеметрии *необходимо подождать* некоторое время до полного запуска сервиса. -* `Send Camera Calibrations` - отправка yaml-файлов калибровки камеры для сервиса `clever`. В диалоговом окне необходимо выбрать *папку*, содержащую файлы конфигурации с расширением `.yaml`. Каждый файл калибровки будет отправлен на клиент с именем (copter ID), соответствующим имени файла без расширения. **Внимание!** Существующий файл калибровки на коптере будет перезаписан. -* `Send FCU parameters` - отправка и запись *единого* файла конфигураций полётного контроллера (FCU) на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл параметров в установленном формате. Параметры на полётном контроллере будут перезаписаны. -* `Developer mode`: **Внимание!** Используйте данные действия с большой осторожностью. - * `Send any file` - отправка *одного* любого файла на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл. Далее, необходимо указать путь, по которому данный файл будет записан на клиенты (не включая имя файла). - * `Send any command` - отправка и выполнение любой команды терминала на все выбранные клиенты. В диалоговом окне необходимо ввести требуемую команду. Команды *могут* использовать `sudo`-права. -* `Select all drones` (`Ctrl+A`) Выделяет все коптеры в таблице. При следующем вызове команды, выделение всех коптеров будет отменено. - -### Раздел 'Drone' - -![Скриншот раздела](img/server-drone.png) - -* `Set Z offfset to ground` - Устанавливает собственный offset (отступ) каждого из выбранных клиентов на значение, равное текущему положению по координате Z -* `Reset Z offfset` - Устанавливает собственный offset (отступ) каждого из выбранных клиентов на значение `0` -* `Restart chrony` - Перезапускает сервис синхронизации времени `chrony` на выбранных клиентах. -* `Remove from table` - Удаляет выбранные коптеры из таблицы. **Внимание!** В случае, если клиент был подключен, будет произведено отключение. В случае если удалённый таким образом клиент исправно функционировал, он переподключится в кратчайшие сроки. -* `Developer mode`: **Внимание!** Используйте данные действия с большой осторожностью - * `Restart clever service` - Перезапускает сервис `clever` на выбранных клиентах. Для возобновления работоспособности полётных функций и получения некоторых значений телеметрии *необходимо подождать* некоторое время до полного запуска сервиса. - * `Restart clever-show service` - Перезапускает сервис шоу коптеров (клиента) `clever-show` на выбранных клиентах. Во время перезапуска клиенты будет отключены. - * `Update clever-show git` - Обновляет папку репозитория `clever-show` на выбранных клиентах. Файлы конфигурации клиента *не будут* перезаписаны. **Внимание!** Для того, чтобы изменения вступили в силу, *необходимо* перезапустить сервис `clever-show`. - * `Reboot all` - Полностью перезагружает систему выбранных клиентов. Во время перезапуска клиенты будет отключены. - -### Раздел 'Animation' - -![Скриншот раздела](img/server-animation.png) - -- `Set start X Y to current position` - Устанавливает точку старта анимации у выбранных клиентов на значения текущей позиции по X Y. -- `Reset start position` - Устанавливает точку старта анимации у выбранных клиентов на значения `0.0`, `0.0`. - -### Раздел 'Music' - -![Скриншот раздела](img/server-music.png) - -- `Select music file` - Загружает выбранный музыкальный файл (поддерживаемые расширения: `.mp3`, `.wav`) для дальнейшего воспроизведения вручную или через определённое время после старта анимации. -- `Play music` - Воспроизводит загруженную музыку. - -- `Stop music` - Останавливает воспроизведение проигрываемой музыки. - - - -## Боковая панель инструментов (команд) -![](img/server_sidemenu.png) - -### Управление - -Данный раздел команд предназначен для выскоуровневого управления роем дронов. - - * Спинбокс `Start after` - Задаёт время задержки синхронного запуска выполнения анимаций коптерами после нажатия на кнопку `Start animation`. Для загруженных\подверженных помехам\имеющих большой пинг сетей рекомендуется использовать значения больше нуля. - * Спинбокс `Music after` - Задаёт время задержки запуска музыки после нажатия на кнопку `Start animation`. - * Чекбокс `Play music ` - Определяет, будет ли воспроизведена музыка при запуске анимации. - * Кнопка `Preflight check` - Все выбранные клиенты выполняют самодиагностику и предполётную проверку. Результаты, вместе с другими параметрами клиента, будут отображены в таблице по мере поступления данных. Необходима в том случае, если на клиенте не настроена автоматическая передача телеметрии. - * Кнопка `Start animation` - По истечению заданного в спинбоксе `Start after` времени, все выбранные коптеры начинают синхронное воспроизведение анимации. По окончанию анимации все коптеры выполнят посадку *на месте окончания своей анимации*. Кнопка активна только в том случае, если все коптеры готовы к воспроизведению анимации. При нажатии запрашивается дополнительное предупреждение. - * `Pause/Resume` - Позволяет ставить на паузу и возобновлять выполнение полётных задач. После каждого нажатия кнопка меняет состояние на обратное. - * Состояние`Pause` - Ставит на паузу очередь заданий всех выбранных коптеров: приостанавливается выполнение любого полётного задания. Рекомендуется использовать в чрезвычайных ситуациях для определения неисправного коптера. **Внимание!** Данная команда НЕ прерывает полёт коптера в уже указанную точку (например: элементы взлёта, посадки; следование до начальной точки анимации и т.д.) - * Состояние `Resume` - Все выбранные коптеры *синхронизированно* продолжат выполнение своих очередей заданий (например исполнение анимации) - * Кнопка `Stop and land all` - Прерывает выполнение полётных заданий *ВСЕХ* подключенных коптеров. Сбрасывает очередь заданий - *действие необратимо*. Выполняет полную остановку и немедленную посадку коптеров. **Используйте в экстренных случаях как одно из средств перехвата.** - * Кнопка `Emergency land` - Открывает диалоговое окно дополнительного модуля быстрого выбора коптера и его последующей экстренной посадки \ дизарма. *Полное описание в конце статьи.* - * Кнопка `Disarm selected` - все выбранные коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно *отключают моторы (disarm).* Это может привести к падению и повреждению коптеров. - * Кнопка `Disarm ALL` - ВСЕ коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно *отключают моторы (disarm).* Это может привести к падению и повреждению коптеров **Используйте в крайних случаях как последнее из средств перехвата.** - -### Полётные функции (команды) - -В данном разделе находятся команды, позволяющие напрямую управлять коптером(ами). - * Кнопка `Test leds` - Все выбранные коптеры выполняют двухсекундную анимацию (бегущие точки) светодиодной лентой (белым цветом). Команда *безопасна* и может быть использована для проверки работы светодиодных лент \ качества и задержки подключения к серверу \ определения соответствия коптера и его `Copter ID` в таблице. - * Кнопка `Takeoff` - Все выбранные коптеры **совершают вертикальный взлёт**, после чего зависают над точкой взлёта. Кнопка активна, *только* если все выбранные коптеры готовы к полёту. **Внимание!** Используйте осторожно, соблюдайте технику безопасности. Не применяйте во время выполнения других полётных функций! - * Чекбокс `Z` - Если чекбокс активен, коптер взлетит в указанное значение по `z`. Иначе коптерами будут использоваться значения по умолчанию, указанные в их конфигурациях, а взлёт будет производиться относительно текущей высоты. - * Спинбокс`Z` - Задаёт значение координаты `z` взлёта коптеров в метрах. - * Кнопка `Flip` - Все выбранные коптеры **совершают флип (flip)** - переворот на 360 градусов вокруг одной из *горизонтальных* осей. **Внимание!** Используйте осторожно, соблюдайте технику безопасности. *Для исполнения флипа коптер должен иметь минимальную высоту >2м.* Не применяйте во время выполнения других полётных функций! - * Кнопка `Land` - Все выбранные коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно *переходят в режим посадки.* **Используйте в экстренных случаях как одно из средств перехвата.** - -### Системные команды - -- Кнопка `Reboot FCU` - перезагружает полётные контроллеры всех выбранных коптеров. Для возобновления работоспособности полётных функций и получения некоторых значений телеметрии *необходимо подождать* некоторое время до перезагрузки полётного контроллера. -- Кнопка `Calibrate gyro` - переводит полётные контроллеры всех выбранных коптеров в режим калибровки гироскопа. Коптеры должны быть неподвижны в течение калибровки. -- Кнопка `Calibrate level` - переводит полётные контроллеры всех выбранных коптеров в режим калибровки уровня горизонта. Коптеры должны быть неподвижны в течение калибровки. - -## Таблица состояния коптеров (клиентов) -При первом подключении клиента к серверу в таблицу добавляется строка для отображения состояния клиента, содержащая начальные данные, переданные клиентом при подключении (`Copter ID`). Строки можно сортировать по возрастанию \ убыванию значений любого из столбцов (кликнув по заголовку столбца). - -Ячейки таблицы подсвечиваются: -* жёлтым, если необходимое значение отсутствует -* красным, если значение (состояние) ячейки неудовлетворительно (согласно внутренним проверкам) -* зелёным, если значение (состояние) ячейки удовлетворительно (согласно внутренним проверкам) - -### Столбцы таблицы -* `copter ID` - идентификатор клиента. Может быть сконфигурирован на стороне клиента. Отображается сразу при подключении клиента. Рядом с каждым ID коптера расположен чекбокс - коптеры, чей ID отмечен чекбоксом положительно (галочка), считаются *выбранными*. -* `version` - хеш-код текущей git версии клиента. -* `animation ID` - внутреннее название файла анимации, подгруженного клиентом. Отображается после выполнения `selfcheck`. *Проверьте соответствие названий файлов анимаций у коптеров перед запуском.* -* `battery V` - абсолютное значение напряжения на аккумуляторе коптера в вольтах по данным полётного контроллера. *Убедитесь, что напряжение не ниже порогового для вашего аккумулятора.* **При критически низком значении коптер считается не готовым** - блокируется возможность взлёта и старта анимации. -* `battery %` - относительное значение напряжения на аккумуляторе коптера. Значение рассчитывается по среднему напряжению (по данным полётного контроллера) на ячейку аккумулятора (банку). *Убедитесь, что уровень заряда перед вылетом не менее установленного в настройках сервера.* **При критически низком значении коптер считается не готовым** - блокируется возможность взлёта и старта анимации. -* `checks` - Все дополнительные сообщения и ошибки при самодиагностике (*Смотреть далее.*). При успешном прохождении самодиагностики без ошибок выводится значение `OK`, ячейка подсвечивается зелёным цветом. **При наличии ошибок коптер считается не готовым** - блокируется возможность взлёта и старта анимации. -* `time delta` - Разница между временем на сервере и клиенте (в секундах). *При слишком больших значениях сигнализирует об отсутствии синхронизации времени между коптером и клиентом!* В это значение так же входит сетевая задержка. - -# Дополнительные операции [TODO] -## Selfcheck -.. - -## Emergency land {: #emergency-land } -Модуль экстренной посадки/дизарма, предназначенный для быстрого поиска оператором визуально неисправного коптера методом бинарного поиска ### Интерфейс -* Зелёная кнопка `1` - ... -* Красная кнопка `2` - ... -* Кнопка `Land` - все коптеры в выбранной ('зелёной') группе совершат процедуру экстренной посадки (аналогично кнопке `Land` в панели инструментов). -* Кнопка `Disarm` - все коптеры в выбранной ('зелёной') группе *немедленно отключают моторы (disarm).* (аналогично кнопке `Disarm` в панели инструментов) ==Это может привести к падению и повреждению коптеров==. -### Алгоритм использования -* ... + +![LED Emergency Land](img/server-led-emergency-land.png) + +При нажатии на кнопку `Emergency land` все коптеры делятся на 2 равные группы по порядку расположения в таблице. Первая половина коптеров зажигает светодиодную ленту зелёным цветом, вторая - красным. При нажатии на зелёную или красную кнопку происходит выбор группы, соответствующей цвету нажатой кнопки. Коптеры выбранного цвета снова делятся на две половины и каждая половина зажигает светодиодную ленту зелёным и красным цветом соответственно. Остальные коптеры выключают светодиодную ленту. + +Нажимая на кнопки, соответствующие цвету группы, в которой находится неисправный коптер, можно определить его номер и выполнить экстренную посадку за логорифмическое количество шагов от количества коптеров, т.е. гораздо быстрее, чем перебирая коптеры по одному. + +На любом шаге можно произвести посадку или выключение моторов всех коптеров, на которых включена светодиодная лента, нажав кнопку `Land` или `Disarm`. From 3fa8fd155e6cd52f319dc101685cdb1ff9348280 Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Wed, 25 Dec 2019 19:16:42 +0300 Subject: [PATCH 28/59] docs: Move img folder to assets --- docs/{ru/img => assets}/server-animation.png | Bin docs/{ru/img => assets}/server-drone.png | Bin .../img/server_gui.png => assets/server-gui.png} | Bin .../img => assets}/server-led-emergency-land.png | Bin docs/{ru/img => assets}/server-music.png | Bin docs/{ru/img => assets}/server-server.png | Bin .../server-sidemenu.png} | Bin docs/ru/server.md | 14 +++++++------- 8 files changed, 7 insertions(+), 7 deletions(-) rename docs/{ru/img => assets}/server-animation.png (100%) rename docs/{ru/img => assets}/server-drone.png (100%) rename docs/{ru/img/server_gui.png => assets/server-gui.png} (100%) rename docs/{ru/img => assets}/server-led-emergency-land.png (100%) rename docs/{ru/img => assets}/server-music.png (100%) rename docs/{ru/img => assets}/server-server.png (100%) rename docs/{ru/img/server_sidemenu.png => assets/server-sidemenu.png} (100%) diff --git a/docs/ru/img/server-animation.png b/docs/assets/server-animation.png similarity index 100% rename from docs/ru/img/server-animation.png rename to docs/assets/server-animation.png diff --git a/docs/ru/img/server-drone.png b/docs/assets/server-drone.png similarity index 100% rename from docs/ru/img/server-drone.png rename to docs/assets/server-drone.png diff --git a/docs/ru/img/server_gui.png b/docs/assets/server-gui.png similarity index 100% rename from docs/ru/img/server_gui.png rename to docs/assets/server-gui.png diff --git a/docs/ru/img/server-led-emergency-land.png b/docs/assets/server-led-emergency-land.png similarity index 100% rename from docs/ru/img/server-led-emergency-land.png rename to docs/assets/server-led-emergency-land.png diff --git a/docs/ru/img/server-music.png b/docs/assets/server-music.png similarity index 100% rename from docs/ru/img/server-music.png rename to docs/assets/server-music.png diff --git a/docs/ru/img/server-server.png b/docs/assets/server-server.png similarity index 100% rename from docs/ru/img/server-server.png rename to docs/assets/server-server.png diff --git a/docs/ru/img/server_sidemenu.png b/docs/assets/server-sidemenu.png similarity index 100% rename from docs/ru/img/server_sidemenu.png rename to docs/assets/server-sidemenu.png diff --git a/docs/ru/server.md b/docs/ru/server.md index d7a1717..82d1e29 100644 --- a/docs/ru/server.md +++ b/docs/ru/server.md @@ -8,7 +8,7 @@ Сервер имеет визуальный графический интерфейс для удобства взаимодействия. -![Интерфейс сервера](img/server_gui.png) +![Интерфейс сервера](../assets/server-gui.png) ## Таблица состояния коптеров @@ -42,7 +42,7 @@ ### Раздел 'Server' -![Скриншот раздела Server](img/server-server.png) +![Скриншот раздела Server](../assets/server-server.png) Данный раздел содержит несколько утилит по отправке различных данных на *выбранные* клиенты. **Внимание!** Не используйте данные команды во время полёта коптеров! * `Send Animations` - отправка файлов анимации, экспортированных аддоном к Blender, на выбранные коптеры. В диалоговом окне необходимо выбрать *папку*, содержащую файлы анимации. Каждый файл анимации будет отправлен на клиент с именем, соответствующим имени файла без расширения. @@ -58,7 +58,7 @@ ### Раздел 'Drone' -![Скриншот раздела Drone](img/server-drone.png) +![Скриншот раздела Drone](../assets/server-drone.png) * `Set Z offfset to ground` - устанавливает собственный отступ по Z каждого из выбранных клиентов в значение, равное текущему положению по координате Z. Можно применять для выравнимания общей высоты полёта коптеров. * `Reset Z offfset` - устанавливает собственный отступ по Z каждого из выбранных клиентов в значение `0` @@ -72,14 +72,14 @@ ### Раздел 'Animation' -![Скриншот раздела Animation](img/server-animation.png) +![Скриншот раздела Animation](../assets/server-animation.png) - `Set start X Y to current position` - устанавливает точку старта анимации у выбранных клиентов в значения текущей позиции по X Y. - `Reset start position` - устанавливает точку старта анимации у выбранных клиентов в значения `0.0`, `0.0`. ### Раздел 'Music' -![Скриншот раздела Music](img/server-music.png) +![Скриншот раздела Music](../assets/server-music.png) - `Select music file` - загружает выбранный музыкальный файл для дальнейшего воспроизведения вручную или через определённое время после старта анимации. Поддерживаемые расширения: `.mp3` или `.wav`. - `Play music` - воспроизводит загруженную музыку. @@ -88,7 +88,7 @@ ## Боковая панель команд -![Скриншот боковой панели](img/server_sidemenu.png) +![Скриншот боковой панели](../assets/server-sidemenu.png) ### Управление @@ -195,7 +195,7 @@ port = 123 ### Интерфейс -![LED Emergency Land](img/server-led-emergency-land.png) +![LED Emergency Land](../assets/server-led-emergency-land.png) При нажатии на кнопку `Emergency land` все коптеры делятся на 2 равные группы по порядку расположения в таблице. Первая половина коптеров зажигает светодиодную ленту зелёным цветом, вторая - красным. При нажатии на зелёную или красную кнопку происходит выбор группы, соответствующей цвету нажатой кнопки. Коптеры выбранного цвета снова делятся на две половины и каждая половина зажигает светодиодную ленту зелёным и красным цветом соответственно. Остальные коптеры выключают светодиодную ленту. From 3a1ef1400371828409ff1918f1d9226399e719ba Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Wed, 25 Dec 2019 19:21:43 +0300 Subject: [PATCH 29/59] docs: Fix links --- docs/ru/server.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/ru/server.md b/docs/ru/server.md index 82d1e29..4e2d344 100644 --- a/docs/ru/server.md +++ b/docs/ru/server.md @@ -29,14 +29,14 @@ * `copter ID` - имя клиента. Может быть сконфигурирован на стороне клиента. Отображается сразу при подключении клиента. Рядом с каждым ID коптера расположен чекбокс - коптеры, чей ID отмечен чекбоксом положительно (галочка), считаются *выбранными*. Ячейки в этом столбце всегда проходят проверку. * `version` - хеш-код текущей git версии клиента. Ячейки в этом столбце всегда проходят проверку. * `animation ID` - внутреннее название файла анимации, подгруженного клиентом. Ячейка в данном столбце не проходит проверку, если анимация отсутствует (значение `No animation`). В остальных случаях, если ячейка не пустая, она проходит проверку. **Внимание!** Проверьте соответствие названий файлов анимаций у коптеров перед запуском. -* `battery` - значение напряжения на аккумуляторе коптера в вольтах и заряд в процентах по данным полётного контроллера. Ячейка в данном столбце проходит проверку, если значение заряда батареи выше значения [battery_percentage_min](###раздел-checks), задаваемого в настройках сервера. В остальных случаях, если ячейка не пустая, она не проходит проверку. +* `battery` - значение напряжения на аккумуляторе коптера в вольтах и заряд в процентах по данным полётного контроллера. Ячейка в данном столбце проходит проверку, если значение заряда батареи выше значения [battery_percentage_min](#раздел-checks), задаваемого в настройках сервера. В остальных случаях, если ячейка не пустая, она не проходит проверку. * `system` - состояние полётного контроллера. Ячейка в данном столбце проходит проверку, если её значение `STANDBY`. В остальных случаях, если ячейка не пустая, она не проходит проверку. * `sensors` - состояние калибровки компаса, акселлерометра и гироскопа полётного контроллера. Ячейка в данном столбце проходит проверку, если её значение `OK`. В остальных случаях, если ячейка не пустая, она не проходит проверку. * `mode` - режим полётного контроллера. Ячейка в данном столбце не проходит проверку, если её значение `NO_FCU` или содержит `CMODE`. В остальных случаях, если ячейка не пустая, она проходит проверку. * `checks` - состояние самодиагностики коптера. Ячейка в данном столбце проходит проверку, если её значение `OK`. В остальных случаях, если ячейка не пустая, она не проходит проверку. * `current x y z yaw frame_id` - текущее положение коптера с указанием названия системы координат. Ячейка в данном столбце не проходит проверку, если её значение `NO_POS` или содержит `nan`. В остальных случаях, если ячейка не пустая, она проходит проверку. -* `start x y z` - стартовое положение коптера для воспроизведения анимации. Ячейка в данном столбце не проходит проверку, если её значение `NO_POS` или разница между текущим и стартовым положением коптера больше значения [start_pos_delta_max](###раздел-checks). В остальных случаях, если ячейка не пустая, она проходит проверку. -* `dt` - разница между временем на сервере и клиенте в секундах, включая сетевую задержку. Ячейка в данном столбце проходит проверку, если её значение меньше значения [time_delta_max](###раздел-checks), задаваемого в настройках сервера. В остальных случаях, если ячейка не пустая, она не проходит проверку. При слишком больших значениях сигнализирует об отсутствии синхронизации времени между коптером и клиентом. +* `start x y z` - стартовое положение коптера для воспроизведения анимации. Ячейка в данном столбце не проходит проверку, если её значение `NO_POS` или разница между текущим и стартовым положением коптера больше значения [start_pos_delta_max](#раздел-checks). В остальных случаях, если ячейка не пустая, она проходит проверку. +* `dt` - разница между временем на сервере и клиенте в секундах, включая сетевую задержку. Ячейка в данном столбце проходит проверку, если её значение меньше значения [time_delta_max](#раздел-checks), задаваемого в настройках сервера. В остальных случаях, если ячейка не пустая, она не проходит проверку. При слишком больших значениях сигнализирует об отсутствии синхронизации времени между коптером и клиентом. ## Меню @@ -103,7 +103,7 @@ * Состояние`Pause` - ставит на паузу очередь заданий всех выбранных коптеров: приостанавливается выполнение любого полётного задания. Рекомендуется использовать в чрезвычайных ситуациях для определения неисправного коптера. **Внимание!** Данная команда НЕ прерывает полёт коптера в уже указанную точку (например: элементы взлёта, посадки; следование до начальной точки анимации и т.д.) * Состояние `Resume` - все выбранные коптеры *синхронизированно* продолжат выполнение своих очередей заданий (например исполнение анимации) * Кнопка `Stop and land all` - прерывает выполнение полётных заданий *ВСЕХ* подключенных коптеров. Сбрасывает очередь заданий - *действие необратимо*. Выполняет полную остановку и немедленную посадку коптеров. **Используйте в экстренных случаях как одно из средств перехвата.** - * Кнопка `Emergency land` - открывает диалоговое окно дополнительного модуля быстрого выбора коптера и его последующей экстренной посадки или дизарма. *Полное описание в конце статьи.* + * Кнопка `Emergency land` - открывает диалоговое окно модуля визуальной посадки неисправного коптера. Полное описание находится в [конце статьи](#emergency-land). * Кнопка `Disarm selected` - все выбранные коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно отключают моторы. Это может привести к падению и повреждению коптеров. * Кнопка `Disarm ALL` - ВСЕ коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно отключают моторы. Это может привести к падению и повреждению коптеров **Используйте в крайних случаях как последнее из средств перехвата.** From 88f4c1b093932957bb500cecd5e761e0a3985e39 Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Wed, 25 Dec 2019 20:09:27 +0300 Subject: [PATCH 30/59] docs: Fix headings in start-tutorial --- docs/ru/start-tutorial.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/ru/start-tutorial.md b/docs/ru/start-tutorial.md index 7aaba1f..795a6af 100644 --- a/docs/ru/start-tutorial.md +++ b/docs/ru/start-tutorial.md @@ -1,20 +1,25 @@ # Инструкция по настройке и запуску клиента и сервера ## Список оборудования + Данное ПО предназначено для управления несколькими квадракоптерами с компьютера-сервера. Для полноценной работы необходимо следующее оборудование: + * Один или несколько квадрокоптеров, работающих на базе ПО [Клевер](https://github.com/copterexpress/clever). * Компьютер с операционной системой Linux. * Wifi роутер, работающий на частоте 2.4 ГГц, либо 5.8 ГГц, если эту частоту поддерживают wifi модули коптеров и компьютера. ## Подготовка ПО + Скачайте на компьютер последний образ (clever-show_XXX.img.zip) и исходный код (Source code) из последнего [релиза](https://github.com/copterexpress/clever-show/releases/latest). Разархивируйте исходный код в удобную директорию. ## Настройка роутера -Для управления одним или несколькими коптерами требуется подключение коптеров и сервера к одной сети. Для этого требуется отдельный wifi роутер с известным SSID и паролем. + +Для управления одним или несколькими коптерами требуется подключение коптеров и сервера к одной сети. Для этого требуется отдельный wifi роутер с известным SSID и паролем. Подключите компьютер, который будет использоваться в качестве сервера, к сети роутера и узнайте его ip адрес - он понадобится для дальнейшей настройки. -## Настройка и запуск клиента +## Установка и запуск клиента + * Запишите образ на microSD карту, используя [Etcher](https://www.balena.io/etcher/). * Вставьте флешку в Raspberry Pi, включите коптер. Дождитесь появления сети `CLEVERSHOW-XXXX`. * Подключитесь к сети коптера, используя пароль `cleverwifi`. @@ -35,7 +40,8 @@ sudo ./client_setup.sh Документация по клиентской части находится [здесь](client.md). -## Настройка и запуск сервера +## Установка и запуск сервера + * Установите [chrony](https://chrony.tuxfamily.org/index.html), [samba](https://help.ubuntu.ru/wiki/samba) и Python 3 на ваш компьютер: ```bash From 8f7a6ce9dece58db82cde994f119c7d6950d0245 Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Wed, 25 Dec 2019 20:10:01 +0300 Subject: [PATCH 31/59] docs: Add description and installation link to server --- docs/ru/server.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/ru/server.md b/docs/ru/server.md index 4e2d344..a4454c2 100644 --- a/docs/ru/server.md +++ b/docs/ru/server.md @@ -1,5 +1,8 @@ # Сервер +Приложение для создания и запуска шоу, настройки дронов, анимации и музыки. + +* [Установка и запуск](start-tutorial.md#установка-и-запуск-сервера) * [Интерфейс](#интерфейс-сервера) * [Настройка](#настройка-сервера) * [Дополнительные операции](#дополнительные-операции) From 2dad557de6b477774536698f8eb1f6d0c1456bdc Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Wed, 25 Dec 2019 20:37:22 +0300 Subject: [PATCH 32/59] docs: Fix md syntax in server article --- docs/ru/server.md | 130 +++++++++++++++++++++++----------------------- 1 file changed, 66 insertions(+), 64 deletions(-) diff --git a/docs/ru/server.md b/docs/ru/server.md index a4454c2..38a4497 100644 --- a/docs/ru/server.md +++ b/docs/ru/server.md @@ -7,13 +7,13 @@ * [Настройка](#настройка-сервера) * [Дополнительные операции](#дополнительные-операции) -# Интерфейс сервера +## Интерфейс сервера Сервер имеет визуальный графический интерфейс для удобства взаимодействия. ![Интерфейс сервера](../assets/server-gui.png) -## Таблица состояния коптеров +### Таблица состояния коптеров При первом подключении клиента к серверу в таблицу добавляется строка для отображения состояния клиента, содержащая только имя клиента (`copter ID`). Если на клиентах настроена автоматическая передача телеметрии, данные в таблице будут обновляться автоматически. Строки можно сортировать по возрастанию или убыванию значений любого из столбцов, кликнув по его заголовку. @@ -25,9 +25,9 @@ Коптер считается **готовым к воспроизведению анимации**, если все ячейки в строке прошли проверку и подсвечены зелёным. -Коптер считается **готовым к полёту**, если все ячейки в строке, кроме `animation ID` и `dt`, прошли проверку и подсвечены зелёным. +Коптер считается **готовым к полёту**, если все ячейки в строке, кроме `animation ID` и `dt`, прошли проверку и подсвечены зелёным. -### Столбцы таблицы +#### Столбцы таблицы * `copter ID` - имя клиента. Может быть сконфигурирован на стороне клиента. Отображается сразу при подключении клиента. Рядом с каждым ID коптера расположен чекбокс - коптеры, чей ID отмечен чекбоксом положительно (галочка), считаются *выбранными*. Ячейки в этом столбце всегда проходят проверку. * `version` - хеш-код текущей git версии клиента. Ячейки в этом столбце всегда проходят проверку. @@ -35,31 +35,32 @@ * `battery` - значение напряжения на аккумуляторе коптера в вольтах и заряд в процентах по данным полётного контроллера. Ячейка в данном столбце проходит проверку, если значение заряда батареи выше значения [battery_percentage_min](#раздел-checks), задаваемого в настройках сервера. В остальных случаях, если ячейка не пустая, она не проходит проверку. * `system` - состояние полётного контроллера. Ячейка в данном столбце проходит проверку, если её значение `STANDBY`. В остальных случаях, если ячейка не пустая, она не проходит проверку. * `sensors` - состояние калибровки компаса, акселлерометра и гироскопа полётного контроллера. Ячейка в данном столбце проходит проверку, если её значение `OK`. В остальных случаях, если ячейка не пустая, она не проходит проверку. -* `mode` - режим полётного контроллера. Ячейка в данном столбце не проходит проверку, если её значение `NO_FCU` или содержит `CMODE`. В остальных случаях, если ячейка не пустая, она проходит проверку. +* `mode` - режим полётного контроллера. Ячейка в данном столбце не проходит проверку, если её значение `NO_FCU` или содержит `CMODE`. В остальных случаях, если ячейка не пустая, она проходит проверку. * `checks` - состояние самодиагностики коптера. Ячейка в данном столбце проходит проверку, если её значение `OK`. В остальных случаях, если ячейка не пустая, она не проходит проверку. * `current x y z yaw frame_id` - текущее положение коптера с указанием названия системы координат. Ячейка в данном столбце не проходит проверку, если её значение `NO_POS` или содержит `nan`. В остальных случаях, если ячейка не пустая, она проходит проверку. * `start x y z` - стартовое положение коптера для воспроизведения анимации. Ячейка в данном столбце не проходит проверку, если её значение `NO_POS` или разница между текущим и стартовым положением коптера больше значения [start_pos_delta_max](#раздел-checks). В остальных случаях, если ячейка не пустая, она проходит проверку. * `dt` - разница между временем на сервере и клиенте в секундах, включая сетевую задержку. Ячейка в данном столбце проходит проверку, если её значение меньше значения [time_delta_max](#раздел-checks), задаваемого в настройках сервера. В остальных случаях, если ячейка не пустая, она не проходит проверку. При слишком больших значениях сигнализирует об отсутствии синхронизации времени между коптером и клиентом. -## Меню +### Меню -### Раздел 'Server' +#### Раздел Server ![Скриншот раздела Server](../assets/server-server.png) Данный раздел содержит несколько утилит по отправке различных данных на *выбранные* клиенты. **Внимание!** Не используйте данные команды во время полёта коптеров! + * `Send Animations` - отправка файлов анимации, экспортированных аддоном к Blender, на выбранные коптеры. В диалоговом окне необходимо выбрать *папку*, содержащую файлы анимации. Каждый файл анимации будет отправлен на клиент с именем, соответствующим имени файла без расширения. * `Send Configurations` - отправка *единого* файла конфигурации клиента на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл конфигурации в установленном формате. Файл конфигурации может быть неполным, в таком случае будут перезаписаны лишь указанные в файле параметры. **Внимание!** Не рекомендуется использовать данное действие для массовой перезаписи `copter ID`, кроме значения `/hostname`. **Внимание!** НЕ отправляйте на клиенты файл конфигурации сервера. -* `Send Launch files ` - отправка launch-файлов конфигурации сервиса `clever`. В диалоговом окне необходимо выбрать *папку*, содержащую файлы конфигурации с сширением `.launch`. Все файлы с таким расширением будут отправлены *на каждый* из клиентов. **Внимание!** Существующие файлы конфигурации на коптерах будут перезаписаны, однако файлы, не отправленные сервером, не будут удалены или модифицированы. +* `Send Launch files` - отправка launch-файлов конфигурации сервиса `clever`. В диалоговом окне необходимо выбрать *папку*, содержащую файлы конфигурации с сширением `.launch`. Все файлы с таким расширением будут отправлены *на каждый* из клиентов. **Внимание!** Существующие файлы конфигурации на коптерах будут перезаписаны, однако файлы, не отправленные сервером, не будут удалены или модифицированы. * `Send Aruco map` - отправка *единого* файла карты aruco маркеров на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл карты в установленном формате. Файл на клиенте будет перезаписан. После получения и записи файла клиент автоматически перезапустит сервис `clever`. Для возобновления работоспособности полётных функций и получения некоторых значений телеметрии *необходимо подождать* некоторое время до полного запуска сервиса. * `Send Camera Calibrations` - отправка yaml-файлов калибровки камеры для сервиса `clever`. В диалоговом окне необходимо выбрать *папку*, содержащую файлы конфигурации с расширением `.yaml`. Каждый файл калибровки будет отправлен на клиент с именем (copter ID), соответствующим имени файла без расширения. **Внимание!** Существующий файл калибровки на коптере будет перезаписан. * `Send FCU parameters` - отправка и запись *единого* файла конфигураций полётного контроллера (FCU) на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл параметров в установленном формате. Параметры на полётном контроллере будут перезаписаны. * `Developer mode`: **Внимание!** Используйте данные действия с большой осторожностью. * `Send any file` - отправка *одного* любого файла на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл. Далее, необходимо указать путь, по которому данный файл будет записан на клиенты (не включая имя файла). * `Send any command` - отправка и выполнение любой команды терминала на все выбранные клиенты. В диалоговом окне необходимо ввести требуемую команду. Команды *могут* использовать `sudo`-права. -* `Select all drones` (`Ctrl+A`) - выделяет все коптеры в таблице. При следующем вызове команды, выделение всех коптеров будет отменено. +* `Select all drones` (`Ctrl+A`) - выделяет все коптеры в таблице. При следующем вызове команды, выделение всех коптеров будет отменено. -### Раздел 'Drone' +#### Раздел Drone ![Скриншот раздела Drone](../assets/server-drone.png) @@ -69,69 +70,69 @@ * `Remove from table` - удаляет выбранные коптеры из таблицы. **Внимание!** В случае, если клиент был подключен, будет произведено отключение. В случае если удалённый таким образом клиент исправно функционировал, он переподключится в кратчайшие сроки. * `Developer mode`: **Внимание!** Используйте данные действия с большой осторожностью. * `Restart clever service` - перезапускает сервис `clever` на выбранных клиентах. Для возобновления работоспособности полётных функций и получения некоторых значений телеметрии *необходимо подождать* некоторое время до полного запуска сервиса. - * `Restart clever-show service` - перезапускает сервис шоу коптеров `clever-show` на выбранных клиентах. Во время перезапуска клиенты будут отключены. + * `Restart clever-show service` - перезапускает сервис шоу коптеров `clever-show` на выбранных клиентах. Во время перезапуска клиенты будут отключены. * `Update clever-show git` - обновляет папку репозитория `clever-show` на выбранных клиентах. Файлы конфигурации клиента *не будут* перезаписаны. **Внимание!** Для того, чтобы изменения вступили в силу, *необходимо* перезапустить сервис `clever-show`. - * `Reboot all` - полностью перезагружает полётный контроллер и компьютер на выбранных коптерах. Во время перезапуска клиенты будут отключены. + * `Reboot all` - полностью перезагружает полётный контроллер и компьютер на выбранных коптерах. Во время перезапуска клиенты будут отключены. -### Раздел 'Animation' +#### Раздел Animation ![Скриншот раздела Animation](../assets/server-animation.png) -- `Set start X Y to current position` - устанавливает точку старта анимации у выбранных клиентов в значения текущей позиции по X Y. -- `Reset start position` - устанавливает точку старта анимации у выбранных клиентов в значения `0.0`, `0.0`. +* `Set start X Y to current position` - устанавливает точку старта анимации у выбранных клиентов в значения текущей позиции по X Y. +* `Reset start position` - устанавливает точку старта анимации у выбранных клиентов в значения `0.0`, `0.0`. -### Раздел 'Music' +#### Раздел Music ![Скриншот раздела Music](../assets/server-music.png) -- `Select music file` - загружает выбранный музыкальный файл для дальнейшего воспроизведения вручную или через определённое время после старта анимации. Поддерживаемые расширения: `.mp3` или `.wav`. -- `Play music` - воспроизводит загруженную музыку. +* `Select music file` - загружает выбранный музыкальный файл для дальнейшего воспроизведения вручную или через определённое время после старта анимации. Поддерживаемые расширения: `.mp3` или `.wav`. +* `Play music` - воспроизводит загруженную музыку. -- `Stop music` - останавливает воспроизведение проигрываемой музыки. +* `Stop music` - останавливает воспроизведение проигрываемой музыки. -## Боковая панель команд +### Боковая панель команд ![Скриншот боковой панели](../assets/server-sidemenu.png) -### Управление +#### Управление Данный раздел команд предназначен для выскоуровневого управления роем дронов. - * Спинбокс `Start after` - задаёт время задержки синхронного запуска выполнения анимаций коптерами после нажатия на кнопку `Start animation`. Для загруженных, подверженных помехам или имеющих большой пинг сетей рекомендуется использовать значения больше нуля. - * Спинбокс `Music after` - задаёт время задержки запуска музыки после нажатия на кнопку `Start animation`. - * Чекбокс `Play music ` - определяет, будет ли воспроизведена музыка при запуске анимации. - * Кнопка `Preflight check` - все выбранные клиенты выполняют самодиагностику и предполётную проверку. Результаты, вместе с другими параметрами клиента, будут отображены в таблице по мере поступления данных. Необходима в том случае, если на клиенте не настроена автоматическая передача телеметрии. - * Кнопка `Start animation` - посылает время старта анимации на все выбранные коптеры с учётом заданного в спинбоксе `Start after` времени. Все выбранные коптеры начинают синхронное воспроизведение анимации после нажатия на данную кнопку и через время, заданное в спинбоксе `Start after`. По окончанию анимации все коптеры выполнят посадку на месте окончания своей анимации. Кнопка активна только в том случае, если все коптеры готовы к воспроизведению анимации. При нажатии запрашивается дополнительное предупреждение. - * `Pause/Resume` - ставит на паузу и возобновляет выполнение полётных задач. После каждого нажатия кнопка меняет состояние на обратное. - * Состояние`Pause` - ставит на паузу очередь заданий всех выбранных коптеров: приостанавливается выполнение любого полётного задания. Рекомендуется использовать в чрезвычайных ситуациях для определения неисправного коптера. **Внимание!** Данная команда НЕ прерывает полёт коптера в уже указанную точку (например: элементы взлёта, посадки; следование до начальной точки анимации и т.д.) - * Состояние `Resume` - все выбранные коптеры *синхронизированно* продолжат выполнение своих очередей заданий (например исполнение анимации) - * Кнопка `Stop and land all` - прерывает выполнение полётных заданий *ВСЕХ* подключенных коптеров. Сбрасывает очередь заданий - *действие необратимо*. Выполняет полную остановку и немедленную посадку коптеров. **Используйте в экстренных случаях как одно из средств перехвата.** - * Кнопка `Emergency land` - открывает диалоговое окно модуля визуальной посадки неисправного коптера. Полное описание находится в [конце статьи](#emergency-land). - * Кнопка `Disarm selected` - все выбранные коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно отключают моторы. Это может привести к падению и повреждению коптеров. - * Кнопка `Disarm ALL` - ВСЕ коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно отключают моторы. Это может привести к падению и повреждению коптеров **Используйте в крайних случаях как последнее из средств перехвата.** +* Спинбокс `Start after` - задаёт время задержки синхронного запуска выполнения анимаций коптерами после нажатия на кнопку `Start animation`. Для загруженных, подверженных помехам или имеющих большой пинг сетей рекомендуется использовать значения больше нуля. +* Спинбокс `Music after` - задаёт время задержки запуска музыки после нажатия на кнопку `Start animation`. +* Чекбокс `Play music` - определяет, будет ли воспроизведена музыка при запуске анимации. +* Кнопка `Preflight check` - все выбранные клиенты выполняют самодиагностику и предполётную проверку. Результаты, вместе с другими параметрами клиента, будут отображены в таблице по мере поступления данных. Необходима в том случае, если на клиенте не настроена автоматическая передача телеметрии. +* Кнопка `Start animation` - посылает время старта анимации на все выбранные коптеры с учётом заданного в спинбоксе `Start after` времени. Все выбранные коптеры начинают синхронное воспроизведение анимации после нажатия на данную кнопку и через время, заданное в спинбоксе `Start after`. По окончанию анимации все коптеры выполнят посадку на месте окончания своей анимации. Кнопка активна только в том случае, если все коптеры готовы к воспроизведению анимации. При нажатии запрашивается дополнительное предупреждение. +* Кнопка `Pause/Resume` - ставит на паузу и возобновляет выполнение полётных задач. После каждого нажатия кнопка меняет состояние на обратное. + * Состояние`Pause` - ставит на паузу очередь заданий всех выбранных коптеров: приостанавливается выполнение любого полётного задания. Рекомендуется использовать в чрезвычайных ситуациях для определения неисправного коптера. **Внимание!** Данная команда НЕ прерывает полёт коптера в уже указанную точку (например: элементы взлёта, посадки; следование до начальной точки анимации и т.д.) + * Состояние `Resume` - все выбранные коптеры *синхронизированно* продолжат выполнение своих очередей заданий (например исполнение анимации) +* Кнопка `Stop and land all` - прерывает выполнение полётных заданий *ВСЕХ* подключенных коптеров. Сбрасывает очередь заданий - *действие необратимо*. Выполняет полную остановку и немедленную посадку коптеров. **Используйте в экстренных случаях как одно из средств перехвата.** +* Кнопка `Emergency land` - открывает диалоговое окно модуля визуальной посадки неисправного коптера. Полное описание находится в [конце статьи](#emergency-land). +* Кнопка `Disarm selected` - все выбранные коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно отключают моторы. Это может привести к падению и повреждению коптеров. +* Кнопка `Disarm ALL` - ВСЕ коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно отключают моторы. Это может привести к падению и повреждению коптеров **Используйте в крайних случаях как последнее из средств перехвата.** -### Тестовые команды +#### Тестовые команды В данном разделе находятся команды, позволяющие напрямую управлять коптерами для их проверки. - * Кнопка `Test leds` - все выбранные коптеры выполняют двухсекундную анимацию (бегущие точки) светодиодной лентой (белым цветом). Команда *безопасна* и может быть использована для проверки работы светодиодных лент, качества и задержки подключения к серверу или определения соответствия коптера и его `copter ID` в таблице. - * Кнопка `Takeoff` - все выбранные коптеры совершают вертикальный взлёт, после чего зависают над точкой взлёта. Кнопка активна, *только* если все выбранные коптеры готовы к полёту. **Внимание!** Используйте осторожно, соблюдайте технику безопасности. Не применяйте во время выполнения других полётных функций! - * Чекбокс `Z` - если чекбокс активен, коптер взлетит в указанное значение по `z`. Иначе коптерами будут использоваться значения по умолчанию, указанные в их конфигурациях, а взлёт будет производиться относительно текущей высоты. - * Спинбокс `Z` - задаёт значение координаты `z` взлёта коптеров в метрах. - * Кнопка `Flip` - все выбранные коптеры **совершают флип (flip)** - переворот на 360 градусов вокруг одной из *горизонтальных* осей. **Внимание!** Используйте осторожно, соблюдайте технику безопасности. **Внимание!** Для исполнения флипа коптер должен иметь минимальную высоту больше 2м. **Внимание!** Не применяйте во время выполнения других полётных функций! - * Кнопка `Land` - все выбранные коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно переходят в режим посадки. **Используйте в экстренных случаях как одно из средств перехвата.** +* Кнопка `Test leds` - все выбранные коптеры выполняют двухсекундную анимацию (бегущие точки) светодиодной лентой (белым цветом). Команда *безопасна* и может быть использована для проверки работы светодиодных лент, качества и задержки подключения к серверу или определения соответствия коптера и его `copter ID` в таблице. +* Кнопка `Takeoff` - все выбранные коптеры совершают вертикальный взлёт, после чего зависают над точкой взлёта. Кнопка активна, *только* если все выбранные коптеры готовы к полёту. **Внимание!** Используйте осторожно, соблюдайте технику безопасности. Не применяйте во время выполнения других полётных функций! + * Чекбокс `Z` - если чекбокс активен, коптер взлетит в указанное значение по `z`. Иначе коптерами будут использоваться значения по умолчанию, указанные в их конфигурациях, а взлёт будет производиться относительно текущей высоты. + * Спинбокс `Z` - задаёт значение координаты `z` взлёта коптеров в метрах. +* Кнопка `Flip` - все выбранные коптеры **совершают флип (flip)** - переворот на 360 градусов вокруг одной из *горизонтальных* осей. **Внимание!** Используйте осторожно, соблюдайте технику безопасности. **Внимание!** Для исполнения флипа коптер должен иметь минимальную высоту больше 2м. **Внимание!** Не применяйте во время выполнения других полётных функций! +* Кнопка `Land` - все выбранные коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно переходят в режим посадки. **Используйте в экстренных случаях как одно из средств перехвата.** -### Системные команды +#### Системные команды В данном разделе находятся команды, исполняемые непосредственно на полётном контроллере коптера. -- Кнопка `Reboot FCU` - перезагружает полётные контроллеры всех выбранных коптеров. Можно использовать для обновления поворота коптера при его определении только с помощью инерциальной системы коптера, например при полёте по системе позиционирования Pozyx или с помощью Optical Flow. Для возобновления работоспособности полётных функций и получения некоторых значений телеметрии *необходимо подождать* некоторое время до перезагрузки полётного контроллера. -- Кнопка `Calibrate gyro` - переводит полётные контроллеры всех выбранных коптеров в режим калибровки гироскопа. **Внимание!** Коптеры должны быть неподвижны в течение калибровки. -- Кнопка `Calibrate level` - переводит полётные контроллеры всех выбранных коптеров в режим калибровки уровня горизонта. **Внимание!** Коптеры должны быть неподвижны в течение калибровки. +* Кнопка `Reboot FCU` - перезагружает полётные контроллеры всех выбранных коптеров. Можно использовать для обновления поворота коптера при его определении только с помощью инерциальной системы коптера, например при полёте по системе позиционирования Pozyx или с помощью Optical Flow. Для возобновления работоспособности полётных функций и получения некоторых значений телеметрии *необходимо подождать* некоторое время до перезагрузки полётного контроллера. +* Кнопка `Calibrate gyro` - переводит полётные контроллеры всех выбранных коптеров в режим калибровки гироскопа. **Внимание!** Коптеры должны быть неподвижны в течение калибровки. +* Кнопка `Calibrate level` - переводит полётные контроллеры всех выбранных коптеров в режим калибровки уровня горизонта. **Внимание!** Коптеры должны быть неподвижны в течение калибровки. -# Настройка сервера +## Настройка сервера -## Файл конфигурации +### Файл конфигурации Конфигурация сервера задаётся в файле [server_config.ini](../../Server/server_config.ini), имеющем следующий вид по умолчанию: @@ -159,15 +160,15 @@ port = 123 Конфигурация по умолчанию является полностью работоспособной и не требует изменений для быстрого начала работы системы. -### Раздел SERVER +#### Раздел SERVER В этом разделе задаются параметры сетевого взаимодействия сервера. Доступны следующие параметры: - * `port` - TCP порт, на котором будут приниматься входящие соединения от клиентов (коптеров). При использовании broadcast данный порт будет сконфигурирован у клиента автоматически. *Рекомендуется изменить значение по умолчанию в целях безопасности* (любое пятизначное и более число, если другое ПО не использует выбранный порт). - * `buffer_size` - размер буфера при приёме и передаче данных. *Не рекомендуется изменять. Рекомендуется использовать единое значение у сервера и клиентов.* - * `remove_disconnected` - Определяет поведение при разрыве связи с клиентом. При значении `True` вся информация о клиенте *будет удалена* как из внутренней памяти, так и *из таблицы*. *Это может привести к 'скачкам' таблицы при отключении клиентов.* При значении `False` отключённые клиенты *не будут* удалены из таблицы, но будут отображены с подсвечиванием ячейки в столбце `copter ID` красным цветом. Все данные будут сохранены. При переподключении клиента, он будет ассоциирован с той же строкой таблицы, а ячейка со значением `copter ID` вновь станет зелёного цвета. +* `port` - TCP порт, на котором будут приниматься входящие соединения от клиентов (коптеров). При использовании broadcast данный порт будет сконфигурирован у клиента автоматически. *Рекомендуется изменить значение по умолчанию в целях безопасности* (любое пятизначное и более число, если другое ПО не использует выбранный порт). +* `buffer_size` - размер буфера при приёме и передаче данных. *Не рекомендуется изменять. Рекомендуется использовать единое значение у сервера и клиентов.* +* `remove_disconnected` - Определяет поведение при разрыве связи с клиентом. При значении `True` вся информация о клиенте *будет удалена* как из внутренней памяти, так и *из таблицы*. *Это может привести к 'скачкам' таблицы при отключении клиентов.* При значении `False` отключённые клиенты *не будут* удалены из таблицы, но будут отображены с подсвечиванием ячейки в столбце `copter ID` красным цветом. Все данные будут сохранены. При переподключении клиента, он будет ассоциирован с той же строкой таблицы, а ячейка со значением `copter ID` вновь станет зелёного цвета. -### Раздел CHECKS +#### Раздел CHECKS В этом разделе задаются параметры проверок коптера, которые регулируются на стороне сервера. Доступны следующие параметры: @@ -175,28 +176,29 @@ port = 123 * `start_pos_delta_max` - Максимальное расстояние от текущего положения коптера до его точки взлёта в файле анимации, допустимое для взлёта. Указывается *в метрах* (дробное значение от 0 до 'inf'). Значение больше указанного будет отмечено в столбце `start x y z` как неудовлетворительное. Допустимо использование строки 'inf' для любого допустимого расстояния. * `time_delta_max` - Максимальная разница (абсолютное значение) между временем сервера и клиента (включая сетевую задержку), допустимая для взлёта. Указывается *в секундах* (дробное значение от 0 до 'inf'). Значение больше указанного будет отмечено в столбце `dt` как неудовлетворительное. -### Раздел BROADCAST +#### Раздел BROADCAST Сервер может использовать UDP broadcast, чтобы передавать клиентам актуальную информацию о конфигурации сервера. Таким образом становится возможным автоматическое подключение клиентов к серверу без необходимости дополнительной ручной конфигурации. В данном разделе задаются параметры этого механизма: - * `use_broadcast` - будут ли использованы broadcast'ы для передачи данных (при значении `False` broadcast'ы НЕ будут отправляться). Используйте `False` в случае повышенных требований безопасности, перегруженности сети или невозможности передачи по широковещательному каналу (из-за конфигурации брандмауэра или сети) - * `broadcast_port` - UDP порт, по которому будет осуществляться отправка сообщений. *Рекомендуется изменить значение по умолчанию в целях безопасности.* **Внимание!** При изменении этого параметра клиенты НЕ смогут принимать сообщения автоконфигурации до изменения (вручную) соответствующего параметра в конфигурации клиента на равное значение. - * `broadcast_delay` - периодичность (в секундах, целочисленное значение), с которой будет происходить отправка broadcast сообщений. Увеличьте задержку для уменьшения нагрузки на сеть. Уменьшите задержку для уменьшения времени отклика и подключения при первом запуске клиентов. - - ### Раздел NTP +* `use_broadcast` - будут ли использованы broadcast'ы для передачи данных (при значении `False` broadcast'ы НЕ будут отправляться). Используйте `False` в случае повышенных требований безопасности, перегруженности сети или невозможности передачи по широковещательному каналу (из-за конфигурации брандмауэра или сети) +* `broadcast_port` - UDP порт, по которому будет осуществляться отправка сообщений. *Рекомендуется изменить значение по умолчанию в целях безопасности.* **Внимание!** При изменении этого параметра клиенты НЕ смогут принимать сообщения автоконфигурации до изменения (вручную) соответствующего параметра в конфигурации клиента на равное значение. +* `broadcast_delay` - периодичность (в секундах, целочисленное значение), с которой будет происходить отправка broadcast сообщений. Увеличьте задержку для уменьшения нагрузки на сеть. Уменьшите задержку для уменьшения времени отклика и подключения при первом запуске клиентов. - Помимо синхронизации времени (с миллисекундной точностью) с помощью пакета chrony, предоставляется альтернатива - возможность использования внешних (при наличии соединения локальной сети с интернетом) или внутрисетевых NTP-серверов. **Внимание!** Для корректной работы системы, **и сервер, и клиенты** должны использовать единый способ синхронизации времени (набор параметров в этом разделе). Данный раздел полностью унифицирован и для сервера, и для клиентов. - * `use_ntp` - Определяет, будет ли использоваться синхронизация времени с помощью NTP. (при значении `False` будет использовано локальное время ОС (синхронизируется автоматически при использовании chrony). *Рекомендуется использование chrony, а не NTP* - * `host` - имя хоста или IP адрес NTP сервера (локального или удаленного) - * `port` - порт, используемый NTP сервером +#### Раздел NTP -# Дополнительные операции +Помимо синхронизации времени (с миллисекундной точностью) с помощью пакета chrony, предоставляется альтернатива - возможность использования внешних (при наличии соединения локальной сети с интернетом) или внутрисетевых NTP-серверов. **Внимание!** Для корректной работы системы, **и сервер, и клиенты** должны использовать единый способ синхронизации времени (набор параметров в этом разделе). Данный раздел полностью унифицирован и для сервера, и для клиентов. -## Emergency land +* `use_ntp` - Определяет, будет ли использоваться синхронизация времени с помощью NTP. (при значении `False` будет использовано локальное время ОС (синхронизируется автоматически при использовании chrony). *Рекомендуется использование chrony, а не NTP* +* `host` - имя хоста или IP адрес NTP сервера (локального или удаленного) +* `port` - порт, используемый NTP сервером + +## Дополнительные операции + +### Emergency land Модуль визуальной экстренной посадки, предназначенный для быстрого поиска оператором визуально неисправного коптера методом бинарного поиска. Для успешного применения на всех коптерах должна быть установлена светодиодная лента. -### Интерфейс +#### Интерфейс ![LED Emergency Land](../assets/server-led-emergency-land.png) From 9647548ac12661b35f82733b12dd3c1256b6d433 Mon Sep 17 00:00:00 2001 From: artem30801 <38689676+artem30801@users.noreply.github.com> Date: Wed, 25 Dec 2019 22:12:21 +0300 Subject: [PATCH 33/59] Typo fix and slight additions to server docs --- docs/ru/server.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/ru/server.md b/docs/ru/server.md index 38a4497..18bf67d 100644 --- a/docs/ru/server.md +++ b/docs/ru/server.md @@ -15,7 +15,7 @@ ### Таблица состояния коптеров -При первом подключении клиента к серверу в таблицу добавляется строка для отображения состояния клиента, содержащая только имя клиента (`copter ID`). Если на клиентах настроена автоматическая передача телеметрии, данные в таблице будут обновляться автоматически. Строки можно сортировать по возрастанию или убыванию значений любого из столбцов, кликнув по его заголовку. +При первом подключении клиента к серверу в таблицу добавляется строка для отображения состояния клиента, содержащая только имя клиента (`copter ID`). Если на клиентах настроена автоматическая передача телеметрии, данные в таблице будут обновляться автоматически. Так же возможно запросить телеметрию выбранных клиентов с помощью кнопки [`Preflight check`](#управление) Строки можно сортировать по возрастанию или убыванию значений любого из столбцов, кликнув по его заголовку. Ячейки таблицы подсвечиваются: @@ -51,7 +51,7 @@ * `Send Animations` - отправка файлов анимации, экспортированных аддоном к Blender, на выбранные коптеры. В диалоговом окне необходимо выбрать *папку*, содержащую файлы анимации. Каждый файл анимации будет отправлен на клиент с именем, соответствующим имени файла без расширения. * `Send Configurations` - отправка *единого* файла конфигурации клиента на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл конфигурации в установленном формате. Файл конфигурации может быть неполным, в таком случае будут перезаписаны лишь указанные в файле параметры. **Внимание!** Не рекомендуется использовать данное действие для массовой перезаписи `copter ID`, кроме значения `/hostname`. **Внимание!** НЕ отправляйте на клиенты файл конфигурации сервера. -* `Send Launch files` - отправка launch-файлов конфигурации сервиса `clever`. В диалоговом окне необходимо выбрать *папку*, содержащую файлы конфигурации с сширением `.launch`. Все файлы с таким расширением будут отправлены *на каждый* из клиентов. **Внимание!** Существующие файлы конфигурации на коптерах будут перезаписаны, однако файлы, не отправленные сервером, не будут удалены или модифицированы. +* `Send Launch files` - отправка launch-файлов конфигурации сервиса `clever`. В диалоговом окне необходимо выбрать *папку*, содержащую файлы конфигурации с расширением `.launch`. Все файлы с таким расширением будут отправлены *на каждый* из клиентов. **Внимание!** Существующие файлы конфигурации на коптерах будут перезаписаны, однако файлы, не отправленные сервером, не будут удалены или модифицированы. * `Send Aruco map` - отправка *единого* файла карты aruco маркеров на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл карты в установленном формате. Файл на клиенте будет перезаписан. После получения и записи файла клиент автоматически перезапустит сервис `clever`. Для возобновления работоспособности полётных функций и получения некоторых значений телеметрии *необходимо подождать* некоторое время до полного запуска сервиса. * `Send Camera Calibrations` - отправка yaml-файлов калибровки камеры для сервиса `clever`. В диалоговом окне необходимо выбрать *папку*, содержащую файлы конфигурации с расширением `.yaml`. Каждый файл калибровки будет отправлен на клиент с именем (copter ID), соответствующим имени файла без расширения. **Внимание!** Существующий файл калибровки на коптере будет перезаписан. * `Send FCU parameters` - отправка и запись *единого* файла конфигураций полётного контроллера (FCU) на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл параметров в установленном формате. Параметры на полётном контроллере будут перезаписаны. @@ -87,7 +87,6 @@ * `Select music file` - загружает выбранный музыкальный файл для дальнейшего воспроизведения вручную или через определённое время после старта анимации. Поддерживаемые расширения: `.mp3` или `.wav`. * `Play music` - воспроизводит загруженную музыку. - * `Stop music` - останавливает воспроизведение проигрываемой музыки. ### Боковая панель команд From a85af14b3a98cbcc009746a8ccc3e0ee8630e4a1 Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Thu, 26 Dec 2019 00:53:57 +0300 Subject: [PATCH 34/59] Server: Update emergency dialog --- Server/{emergency.py => visual_land.py} | 62 +++++-------------------- Server/{emergency.ui => visual_land.ui} | 23 +++++---- 2 files changed, 25 insertions(+), 60 deletions(-) rename Server/{emergency.py => visual_land.py} (58%) rename Server/{emergency.ui => visual_land.ui} (87%) diff --git a/Server/emergency.py b/Server/visual_land.py similarity index 58% rename from Server/emergency.py rename to Server/visual_land.py index 85dbef9..60b9b71 100644 --- a/Server/emergency.py +++ b/Server/visual_land.py @@ -1,36 +1,20 @@ # -*- coding: utf-8 -*- -# Form implementation generated from reading ui file 'emergency.ui' +# Form implementation generated from reading ui file 'visual_land.ui' # -# Created by: PyQt5 UI code generator 5.11.3 +# Created by: PyQt5 UI code generator 5.13.0 # # WARNING! All changes made in this file will be lost! + from PyQt5 import QtCore, QtGui, QtWidgets -import os -import glob -from PyQt5 import QtWidgets -from PyQt5.QtGui import QStandardItemModel, QStandardItem -from PyQt5.QtCore import Qt, pyqtSlot, pyqtSignal, QObject - -from PyQt5.QtWidgets import QDialog - -# Importing gui form -from server_qt import * -from server import * class Ui_Dialog(object): - - def __init__(self): - self.Dialog = None def setupUi(self, Dialog): - self.Dialog = Dialog Dialog.setObjectName("Dialog") Dialog.resize(746, 620) - Dialog.setStyleSheet("QDialog{\n" -"background-color: #fffdd0;\n" -"}") + Dialog.setStyleSheet("") self.two_button = QtWidgets.QPushButton(Dialog) self.two_button.setGeometry(QtCore.QRect(420, 120, 231, 171)) self.two_button.setSizeIncrement(QtCore.QSize(16, 16)) @@ -42,10 +26,12 @@ class Ui_Dialog(object): "}") self.two_button.setObjectName("two_button") self.label = QtWidgets.QLabel(Dialog) - self.label.setGeometry(QtCore.QRect(90, 30, 561, 51)) + self.label.setGeometry(QtCore.QRect(60, 30, 631, 51)) font = QtGui.QFont() - font.setPointSize(16) + font.setPointSize(20) self.label.setFont(font) + self.label.setLayoutDirection(QtCore.Qt.LeftToRight) + self.label.setAlignment(QtCore.Qt.AlignCenter) self.label.setObjectName("label") self.one_button = QtWidgets.QPushButton(Dialog) self.one_button.setGeometry(QtCore.QRect(90, 120, 231, 171)) @@ -54,7 +40,7 @@ class Ui_Dialog(object): "color: white;\n" "font-weight: 600;\n" "font-size: 25pt;\n" -"background-color: RGB(118, 255, 122);\n" +"background-color: green;\n" "}") self.one_button.setObjectName("one_button") self.land_emergency_button = QtWidgets.QPushButton(Dialog) @@ -73,39 +59,15 @@ class Ui_Dialog(object): "background-color: white;\n" "}") self.disarm_emergency_button.setObjectName("disarm_emergency_button") - self.one_button.clicked.connect(self.one_button_click) - self.two_button.clicked.connect(self.two_button_click) - self.land_emergency_button.clicked.connect(self.land_emergency_click) - self.disarm_emergency_button.clicked.connect(self.disarm_emergency_click) self.retranslateUi(Dialog) QtCore.QMetaObject.connectSlotsByName(Dialog) def retranslateUi(self, Dialog): _translate = QtCore.QCoreApplication.translate - Dialog.setWindowTitle(_translate("Dialog", "Dialog")) + Dialog.setWindowTitle(_translate("Dialog", "Visual land")) self.two_button.setText(_translate("Dialog", "2")) - self.label.setText(_translate("Dialog", "\n" -"Select a group in which the drone does not work correctly")) + self.label.setText(_translate("Dialog", "Select the group with the defective copter")) self.one_button.setText(_translate("Dialog", "1")) self.land_emergency_button.setText(_translate("Dialog", "Land")) - self.disarm_emergency_button.setText(_translate("Dialog", "Disarm")) - def one_button_click(self): - self.Dialog.done(1) - def two_button_click(self): - self.Dialog.done(2) - def land_emergency_click(self): - self.Dialog.done(3) - def disarm_emergency_click(self): - self.Dialog.done(4) - - -if __name__ == "__main__": - import sys - app = QtWidgets.QApplication(sys.argv) - Dialog = QtWidgets.QDialog() - ui = Ui_Dialog() - ui.setupUi(Dialog) - Dialog.show() - sys.exit(app.exec_()) - + self.disarm_emergency_button.setText(_translate("Dialog", "Disarm")) diff --git a/Server/emergency.ui b/Server/visual_land.ui similarity index 87% rename from Server/emergency.ui rename to Server/visual_land.ui index df5c698..212f2bb 100644 --- a/Server/emergency.ui +++ b/Server/visual_land.ui @@ -11,12 +11,10 @@ - Dialog + Visual land - QDialog{ -background-color: #fffdd0; -} + @@ -48,20 +46,25 @@ background-color: red; - 90 + 60 30 - 561 + 631 51 - 16 + 20 + + Qt::LeftToRight + - -Select a group in which the drone does not work correctly + Select the group with the defective copter + + + Qt::AlignCenter @@ -84,7 +87,7 @@ Select a group in which the drone does not work correctly color: white; font-weight: 600; font-size: 25pt; -background-color: RGB(118, 255, 122); +background-color: green; } From fd763feeed2e57e7ef83a7ef0bd001b32e85cdac Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Thu, 26 Dec 2019 00:55:09 +0300 Subject: [PATCH 35/59] Server: Update GUI --- Server/server_gui.py | 125 ++++++++++++++++----------- Server/server_gui.ui | 198 +++++++++++++++++++++++++++---------------- Server/server_qt.py | 142 ++++++++++++++++--------------- 3 files changed, 280 insertions(+), 185 deletions(-) diff --git a/Server/server_gui.py b/Server/server_gui.py index 5e9536a..b448385 100644 --- a/Server/server_gui.py +++ b/Server/server_gui.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'server_gui.ui' # -# Created by: PyQt5 UI code generator 5.13.1 +# Created by: PyQt5 UI code generator 5.13.0 # # WARNING! All changes made in this file will be lost! @@ -44,9 +44,17 @@ class Ui_MainWindow(object): self.verticalLayout.setSizeConstraint(QtWidgets.QLayout.SetMaximumSize) self.verticalLayout.setObjectName("verticalLayout") self.formLayout = QtWidgets.QFormLayout() - self.formLayout.setLabelAlignment(QtCore.Qt.AlignCenter) - self.formLayout.setFormAlignment(QtCore.Qt.AlignCenter) + self.formLayout.setLabelAlignment(QtCore.Qt.AlignHCenter|QtCore.Qt.AlignTop) + self.formLayout.setFormAlignment(QtCore.Qt.AlignHCenter|QtCore.Qt.AlignTop) self.formLayout.setObjectName("formLayout") + 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.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.music_text = QtWidgets.QLabel(self.centralwidget) self.music_text.setLayoutDirection(QtCore.Qt.RightToLeft) self.music_text.setObjectName("music_text") @@ -56,6 +64,10 @@ class Ui_MainWindow(object): self.music_delay_spin.setMaximum(1000.0) self.music_delay_spin.setObjectName("music_delay_spin") self.formLayout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.music_delay_spin) + 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.music_checkbox = QtWidgets.QCheckBox(self.centralwidget) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) @@ -70,18 +82,6 @@ class Ui_MainWindow(object): 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_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) @@ -89,42 +89,71 @@ class Ui_MainWindow(object): self.line.setObjectName("line") self.verticalLayout.addWidget(self.line) self.formLayout_2 = QtWidgets.QFormLayout() - self.formLayout_2.setFormAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.formLayout_2.setLabelAlignment(QtCore.Qt.AlignCenter) + self.formLayout_2.setFormAlignment(QtCore.Qt.AlignCenter) self.formLayout_2.setObjectName("formLayout_2") - 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.check_button = QtWidgets.QPushButton(self.centralwidget) + self.check_button.setEnabled(True) + self.check_button.setObjectName("check_button") + self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.check_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.formLayout_2.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.start_button) + self.pause_button = QtWidgets.QPushButton(self.centralwidget) + self.pause_button.setObjectName("pause_button") + self.formLayout_2.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.pause_button) self.verticalLayout.addLayout(self.formLayout_2) + self.line_5 = QtWidgets.QFrame(self.centralwidget) + self.line_5.setFrameShape(QtWidgets.QFrame.HLine) + self.line_5.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_5.setObjectName("line_5") + self.verticalLayout.addWidget(self.line_5) + self.formLayout_5 = QtWidgets.QFormLayout() + self.formLayout_5.setLabelAlignment(QtCore.Qt.AlignCenter) + self.formLayout_5.setFormAlignment(QtCore.Qt.AlignCenter) + self.formLayout_5.setContentsMargins(-1, 0, -1, -1) + self.formLayout_5.setObjectName("formLayout_5") + self.land_selected_button = QtWidgets.QPushButton(self.centralwidget) + self.land_selected_button.setObjectName("land_selected_button") + self.formLayout_5.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.land_selected_button) + self.land_all_button = QtWidgets.QPushButton(self.centralwidget) + self.land_all_button.setObjectName("land_all_button") + self.formLayout_5.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.land_all_button) + self.verticalLayout.addLayout(self.formLayout_5) + self.line_6 = QtWidgets.QFrame(self.centralwidget) + self.line_6.setFrameShape(QtWidgets.QFrame.HLine) + self.line_6.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line_6.setObjectName("line_6") + self.verticalLayout.addWidget(self.line_6) + self.formLayout_7 = QtWidgets.QFormLayout() + self.formLayout_7.setFormAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.formLayout_7.setContentsMargins(-1, 0, -1, -1) + self.formLayout_7.setObjectName("formLayout_7") + self.visual_land_button = QtWidgets.QPushButton(self.centralwidget) + self.visual_land_button.setObjectName("visual_land_button") + self.formLayout_7.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.visual_land_button) + self.emergency_land_button = QtWidgets.QPushButton(self.centralwidget) + self.emergency_land_button.setObjectName("emergency_land_button") + self.formLayout_7.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.emergency_land_button) + self.verticalLayout.addLayout(self.formLayout_7) self.line_2 = QtWidgets.QFrame(self.centralwidget) self.line_2.setFrameShape(QtWidgets.QFrame.HLine) self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken) self.line_2.setObjectName("line_2") self.verticalLayout.addWidget(self.line_2) self.formLayout_3 = QtWidgets.QFormLayout() - self.formLayout_3.setFormAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.formLayout_3.setLabelAlignment(QtCore.Qt.AlignCenter) + self.formLayout_3.setFormAlignment(QtCore.Qt.AlignCenter) self.formLayout_3.setVerticalSpacing(6) self.formLayout_3.setObjectName("formLayout_3") 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.disarm_selected_button = QtWidgets.QPushButton(self.centralwidget) + self.disarm_selected_button.setObjectName("disarm_selected_button") + self.formLayout_3.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.disarm_selected_button) self.verticalLayout.addLayout(self.formLayout_3) self.line_3 = QtWidgets.QFrame(self.centralwidget) self.line_3.setFrameShape(QtWidgets.QFrame.HLine) @@ -132,11 +161,9 @@ 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.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) + self.formLayout_4.setLabelAlignment(QtCore.Qt.AlignCenter) + self.formLayout_4.setFormAlignment(QtCore.Qt.AlignCenter) self.formLayout_4.setObjectName("formLayout_4") - self.land_button = QtWidgets.QPushButton(self.centralwidget) - self.land_button.setObjectName("land_button") - self.formLayout_4.setWidget(8, QtWidgets.QFormLayout.FieldRole, self.land_button) self.flip_button = QtWidgets.QPushButton(self.centralwidget) self.flip_button.setObjectName("flip_button") self.formLayout_4.setWidget(7, QtWidgets.QFormLayout.FieldRole, self.flip_button) @@ -170,7 +197,8 @@ class Ui_MainWindow(object): 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.setLabelAlignment(QtCore.Qt.AlignCenter) + self.formLayout_6.setFormAlignment(QtCore.Qt.AlignBottom|QtCore.Qt.AlignHCenter) self.formLayout_6.setObjectName("formLayout_6") self.reboot_fcu = QtWidgets.QPushButton(self.centralwidget) self.reboot_fcu.setObjectName("reboot_fcu") @@ -187,7 +215,7 @@ class Ui_MainWindow(object): self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 1360, 25)) + self.menubar.setGeometry(QtCore.QRect(0, 0, 1360, 22)) self.menubar.setObjectName("menubar") self.menuOptions = QtWidgets.QMenu(self.menubar) self.menuOptions.setObjectName("menuOptions") @@ -292,19 +320,20 @@ class Ui_MainWindow(object): def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "Clever Drone Show")) + self.start_text.setText(_translate("MainWindow", " Start after")) + self.start_delay_spin.setSuffix(_translate("MainWindow", " s")) 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.start_button.setText(_translate("MainWindow", "Start animation")) + self.pause_button.setText(_translate("MainWindow", "Pause")) + self.land_selected_button.setText(_translate("MainWindow", "Land selected")) + self.land_all_button.setText(_translate("MainWindow", "Land ALL")) + self.visual_land_button.setText(_translate("MainWindow", "Visual land")) + self.emergency_land_button.setText(_translate("MainWindow", "Emergency land")) 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.land_button.setText(_translate("MainWindow", "Land")) + self.disarm_selected_button.setText(_translate("MainWindow", "Disarm selected")) self.flip_button.setText(_translate("MainWindow", "Flip")) self.takeoff_button.setText(_translate("MainWindow", "Takeoff")) self.leds_button.setText(_translate("MainWindow", "Test leds")) diff --git a/Server/server_gui.ui b/Server/server_gui.ui index e39eefe..59e73ad 100644 --- a/Server/server_gui.ui +++ b/Server/server_gui.ui @@ -71,11 +71,31 @@ - Qt::AlignCenter + Qt::AlignHCenter|Qt::AlignTop - Qt::AlignCenter + Qt::AlignHCenter|Qt::AlignTop + + + + Qt::RightToLeft + + + Start after + + + Qt::AlignCenter + + + + + + + s + + + @@ -99,6 +119,16 @@ + + + + Qt::RightToLeft + + + Play music + + + @@ -127,36 +157,6 @@ - - - - Qt::RightToLeft - - - Play music - - - - - - - s - - - - - - - Qt::RightToLeft - - - Start after - - - Qt::AlignCenter - - - @@ -168,24 +168,23 @@ - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + Qt::AlignCenter - - + + Qt::AlignCenter + + + + + true + - Stop and land all + Preflight check - - - - Pause - - - - + true @@ -198,13 +197,75 @@ - - - - true - + + - Preflight check + Pause + + + + + + + + + Qt::Horizontal + + + + + + + Qt::AlignCenter + + + Qt::AlignCenter + + + 0 + + + + + Land selected + + + + + + + Land ALL + + + + + + + + + Qt::Horizontal + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + 0 + + + + + Visual land + + + + + + + Emergency land @@ -219,8 +280,11 @@ + + Qt::AlignCenter + - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + Qt::AlignCenter 6 @@ -233,19 +297,12 @@ - + Disarm selected - - - - Emergency land - - - @@ -257,16 +314,12 @@ - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + Qt::AlignCenter + + + Qt::AlignCenter - - - - Land - - - @@ -341,8 +394,11 @@ + + Qt::AlignCenter + - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + Qt::AlignBottom|Qt::AlignHCenter @@ -379,7 +435,7 @@ 0 0 1360 - 25 + 22 diff --git a/Server/server_qt.py b/Server/server_qt.py index ef78935..d645cf2 100644 --- a/Server/server_qt.py +++ b/Server/server_qt.py @@ -18,7 +18,7 @@ from server_gui import Ui_MainWindow from server import * import messaging_lib as messaging from copter_table_models import * -from emergency import * +from visual_land import * import threading @@ -98,12 +98,13 @@ class MainWindow(QtWidgets.QMainWindow): # Connect calibrating signal (testing) self.model.selected_calibrating_signal.connect(self.ui.check_button.setDisabled) self.model.selected_calibrating_signal.connect(self.ui.pause_button.setDisabled) - self.model.selected_calibrating_signal.connect(self.ui.stop_button.setDisabled) - self.model.selected_calibrating_signal.connect(self.ui.emergency_button.setDisabled) - self.model.selected_calibrating_signal.connect(self.ui.disarm_button.setDisabled) + self.model.selected_calibrating_signal.connect(self.ui.land_selected_button.setDisabled) + self.model.selected_calibrating_signal.connect(self.ui.land_all_button.setDisabled) + self.model.selected_calibrating_signal.connect(self.ui.visual_land_button.setDisabled) + self.model.selected_calibrating_signal.connect(self.ui.emergency_land_button.setDisabled) + self.model.selected_calibrating_signal.connect(self.ui.disarm_selected_button.setDisabled) self.model.selected_calibrating_signal.connect(self.ui.disarm_all_button.setDisabled) self.model.selected_calibrating_signal.connect(self.ui.leds_button.setDisabled) - self.model.selected_calibrating_signal.connect(self.ui.land_button.setDisabled) self.model.selected_calibrating_signal.connect(self.ui.reboot_fcu.setDisabled) self.model.selected_calibration_ready_signal.connect(self.ui.calibrate_gyro.setEnabled) self.model.selected_calibration_ready_signal.connect(self.ui.calibrate_level.setEnabled) @@ -137,16 +138,19 @@ class MainWindow(QtWidgets.QMainWindow): self.ui.check_button.clicked.connect(self.selfcheck_selected) self.ui.start_button.clicked.connect(self.send_starttime_selected) self.ui.pause_button.clicked.connect(self.pause_resume_selected) - self.ui.stop_button.clicked.connect(self.land_all) - self.ui.emergency_button.clicked.connect(self.emergency) - self.ui.disarm_button.clicked.connect(self.disarm_selected) + self.ui.land_selected_button.clicked.connect(self.land_selected) + self.ui.land_all_button.clicked.connect(self.land_all) + + self.ui.visual_land_button.clicked.connect(self.visual_land) + self.ui.emergency_land_button.clicked.connect(self.emergency_land_selected) + + self.ui.disarm_selected_button.clicked.connect(self.disarm_selected) self.ui.disarm_all_button.clicked.connect(self.disarm_all) self.ui.leds_button.clicked.connect(self.test_leds_selected) self.ui.takeoff_button.clicked.connect(self.takeoff_selected) self.ui.flip_button.clicked.connect(self.flip_selected) - self.ui.land_button.clicked.connect(self.land_selected) self.ui.reboot_fcu.clicked.connect(self.reboot_selected) self.ui.calibrate_gyro.clicked.connect(self.calibrate_gyro_selected) @@ -272,24 +276,83 @@ class MainWindow(QtWidgets.QMainWindow): copter.client.send_message('resume', {"time": server.time_now() + time_gap}) self.ui.pause_button.setText('Pause') + + @pyqtSlot() + def land_selected(self): + for copter in self.model.user_selected(): + copter.client.send_message("land") + @pyqtSlot() def land_all(self): Client.broadcast_message("land") + @pyqtSlot() + def visual_land(self): + client_row_min = 0 + client_row_max = self.model.rowCount() - 1 + result = -1 + while (result != 0) and (result != 3) and (result != 4): + # light_green_red(min, max) + client_row_mid = int(math.ceil((client_row_max + client_row_min) / 2.0)) + print(client_row_min, client_row_mid, client_row_max) + for row_num in range(client_row_min, client_row_mid): + self.model.data_contents[row_num].client \ + .send_message("led_fill", {"green": 255}) + for row_num in range(client_row_mid, client_row_max + 1): + self.model.data_contents[row_num].client \ + .send_message("led_fill", {"red": 255}) + + Dialog = QtWidgets.QDialog() + ui = Ui_Dialog() + ui.setupUi(Dialog) + Dialog.show() + result = Dialog.exec() + print("Dialog result: {}".format(result)) + + if client_row_max != client_row_min: + if result == 1: + for row_num in range(client_row_mid, client_row_max + 1): + self.model.data_contents[row_num].client \ + .send_message("led_fill") + client_row_max = client_row_mid - 1 + + elif result == 2: + for row_num in range(client_row_min, client_row_mid): + self.model.data_contents[row_num].client \ + .send_message("led_fill") + client_row_min = client_row_mid + + if result == 0: + Client.broadcast_message("led_fill") + elif result == 3: + for row_num in range(client_row_min, client_row_max + 1): + self.model.data_contents[row_num].client \ + .send_message("land") + elif result == 4: + for row_num in range(client_row_min, client_row_max + 1): + self.model.data_contents[row_num].client \ + .send_message("disarm") + + @pyqtSlot() + def emergency_land_selected(self): + for copter in self.model.user_selected(): + copter.client.send_message("emergency_land") + @pyqtSlot() def disarm_selected(self): for copter in self.model.user_selected(): copter.client.send_message("disarm") + + @pyqtSlot() + def disarm_all(self): + Client.broadcast_message("disarm") + @pyqtSlot() def test_leds_selected(self): for copter in self.model.user_selected(): copter.client.send_message("led_test") - @pyqtSlot() - def disarm_all(self): - Client.broadcast_message("disarm") - @pyqtSlot() @confirmation_required("This operation will takeoff copters immediately. Proceed?") def takeoff_selected(self, **kwargs): @@ -307,11 +370,6 @@ class MainWindow(QtWidgets.QMainWindow): if flip_checks(copter): copter.client.send_message("flip") - @pyqtSlot() - def land_selected(self): - for copter in self.model.user_selected(): - copter.client.send_message("land") - @pyqtSlot() def reboot_selected(self): for copter in self.model.user_selected(): @@ -544,60 +602,12 @@ class MainWindow(QtWidgets.QMainWindow): logging.info("Playing music") self.player.play() - @pyqtSlot() - def emergency(self): - client_row_min = 0 - client_row_max = self.model.rowCount() - 1 - result = -1 - while (result != 0) and (result != 3) and (result != 4): - # light_green_red(min, max) - client_row_mid = int(math.ceil((client_row_max + client_row_min) / 2.0)) - print(client_row_min, client_row_mid, client_row_max) - for row_num in range(client_row_min, client_row_mid): - self.model.data_contents[row_num].client \ - .send_message("led_fill", {"green": 255}) - for row_num in range(client_row_mid, client_row_max + 1): - self.model.data_contents[row_num].client \ - .send_message("led_fill", {"red": 255}) - - Dialog = QtWidgets.QDialog() - ui = Ui_Dialog() - ui.setupUi(Dialog) - Dialog.show() - result = Dialog.exec() - print("Dialog result: {}".format(result)) - - if client_row_max != client_row_min: - if result == 1: - for row_num in range(client_row_mid, client_row_max + 1): - self.model.data_contents[row_num].client \ - .send_message("led_fill") - client_row_max = client_row_mid - 1 - - elif result == 2: - for row_num in range(client_row_min, client_row_mid): - self.model.data_contents[row_num].client \ - .send_message("led_fill") - client_row_min = client_row_mid - - if result == 0: - Client.broadcast_message("led_fill") - elif result == 3: - for row_num in range(client_row_min, client_row_max + 1): - self.model.data_contents[row_num].client \ - .send_message("land") - elif result == 4: - for row_num in range(client_row_min, client_row_max + 1): - self.model.data_contents[row_num].client \ - .send_message("disarm") - @messaging.message_callback("telemetry") def get_telem_data(self, **kwargs): message = kwargs.get("value") window.update_table_data(self, message) - def except_hook(cls, exception, traceback): sys.__excepthook__(cls, exception, traceback) From bcc7a0cf881bd4f5482afa51d2557354d3bfc977 Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Thu, 26 Dec 2019 02:35:35 +0300 Subject: [PATCH 36/59] Client: Update client_config according to client's documentation --- Drone/client_config.ini | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/Drone/client_config.ini b/Drone/client_config.ini index cc0e86f..1da8803 100644 --- a/Drone/client_config.ini +++ b/Drone/client_config.ini @@ -4,15 +4,6 @@ broadcast_port = 8181 host = 192.168.1.101 buffer_size = 1024 -[FILETRANSFER] -files_directory = animation -animation_file = animation.csv - -[NTP] -use_ntp = False -host = ntp1.stratum2.ru -port = 123 - [VISUAL_POSE_WATCHDOG] timeout = 1.0 action = emergency_land @@ -26,14 +17,6 @@ transmit = True land_if_pos_delta_bigger_than = 3.0 log_cpu_and_memory = True -[ANIMATION] -takeoff_animation_check = True -land_animation_check = True -frame_delay = 0.1 -x_ratio = 1.0 -y_ratio = 1.0 -z_ratio = 1.0 - [COPTERS] frame_id = map takeoff_height = 1.0 @@ -56,6 +39,14 @@ roll = 180 pitch = 0 yaw = -90 +[ANIMATION] +takeoff_animation_check = True +land_animation_check = True +frame_delay = 0.1 +x_ratio = 1.0 +y_ratio = 1.0 +z_ratio = 1.0 + [PRIVATE] id = /hostname restart_dhcpcd = True @@ -65,3 +56,11 @@ x0 = 0 y0 = 0 z0 = 0 +[FILETRANSFER] +files_directory = animation +animation_file = animation.csv + +[NTP] +use_ntp = False +host = ntp1.stratum2.ru +port = 123 From f07804b97d6a0ee38dd789ea432c25c02563d4bf Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Thu, 26 Dec 2019 02:37:00 +0300 Subject: [PATCH 37/59] docs: Small fixes to server article --- docs/ru/client.md | 176 ++++++++++++++++++++++++++++++++++++++++++++++ docs/ru/server.md | 16 ++--- 2 files changed, 184 insertions(+), 8 deletions(-) diff --git a/docs/ru/client.md b/docs/ru/client.md index e69de29..f407971 100644 --- a/docs/ru/client.md +++ b/docs/ru/client.md @@ -0,0 +1,176 @@ +# Клиент + +Приложение для удаленного синхронизированного управления дронами в шоу. + +* [Установка и запуск](start-tutorial.md#установка-и-запуск-клиента) +* [Настройка клиента](#настройка-клиента) + +## Настройка клиента + +### Файл конфигурации + +Конфигурация клиента задаётся в файле [client_config.ini](../../Drone/client_config.ini), имеющем следующий вид по умолчанию: + +```ini +[SERVER] +port = 25000 +broadcast_port = 8181 +host = 192.168.1.101 +buffer_size = 1024 + +[VISUAL_POSE_WATCHDOG] +timeout = 1.0 +action = emergency_land +emergency_land_thrust = 0.45 +emergency_land_decrease_thrust_after = 5.0 +timeout_to_disarm_after_watchdog_action = 10.0 + +[TELEMETRY] +frequency = 1 +transmit = True +land_if_pos_delta_bigger_than = 3.0 +log_cpu_and_memory = True + +[COPTERS] +frame_id = map +takeoff_height = 1.0 +takeoff_time = 5.0 +safe_takeoff = False +reach_first_point_time = 5.0 +land_time = 1.0 +x0_common = 0 +y0_common = 0 +z0_common = 0 +yaw = 180 +land_timeout = 10.0 + +[FLOOR FRAME] +parent = aruco_map +x = 2.4 +y = 12.4 +z = 6.4 +roll = 180 +pitch = 0 +yaw = -90 + +[ANIMATION] +takeoff_animation_check = True +land_animation_check = True +frame_delay = 0.1 +x_ratio = 1.0 +y_ratio = 1.0 +z_ratio = 1.0 + +[PRIVATE] +id = /hostname +restart_dhcpcd = True +use_leds = True +led_pin = 21 +x0 = 0 +y0 = 0 +z0 = 0 + +[FILETRANSFER] +files_directory = animation +animation_file = animation.csv + +[NTP] +use_ntp = False +host = ntp1.stratum2.ru +port = 123 + +``` + +Конфигурация по умолчанию является полностью работоспособной и не требует изменений для быстрого старта клиента. + +Для централизованной загрузки конфигурации на все коптеры нужно использовать пункт меню `Send configurations` на [сервере](server.md#раздел-server). Допускается загрузка неполного файла параметров конфигурации, с отсутствующими разделами или параметрами относительно конфигурации по умолчанию. + +#### Раздел SERVER + +В этом разделе задаются параметры сетевого взаимодействия клиента с сервером. Доступны следующие параметры: + +* `port` - TCP порт, на который будут приниматься входящие соединения от сервера. При использовании настройки [use_broadcast](server.md#раздел-broadcast) на сервере, данный порт будет сконфигурирован у клиента автоматически. *Рекомендуется изменить значение по умолчанию в целях безопасности* (любое пятизначное и более число, если другое ПО не использует выбранный порт). +* `broadcast_port` - UDP порт, на который по широковещательному каналу сервер передаёт свои настройки. С помощью данного механизма возможно автоматическое подключение клиента к серверу. +* `host` - IP адрес сервера. +* `buffer_size` - размер буфера при приёме и передаче данных. *Не рекомендуется изменять. Рекомендуется использовать единое значение у сервера и клиентов.* + +#### Раздел VISUAL_POSE_WATCHDOG + +В данном разделе настраивается программа экстренной защиты коптера от потери позиции или столкновения с объектом. + +* `timeout` - время срабатывания экстренной защиты после потери визуальной позиции, в секундах. +* `action` - действие при срабатывании экстренной защиты. Доступные варианты: `land` - посадка коптера в режиме полётного контроллера AUTO.LAND, `emergency_land` - посадка коптера с постепенным уменьшением мощности моторов, `disarm`- выключение моторов. +* `emergency_land_thrust` - начальная мощность, подаваемая на моторы в случае выбора действия `emergency_land` при срабатывании экстренной защиты, в процентах. +* `emergency_land_decrease_thrust_after` - время, через которое мощность на моторах плавно начинает уменьшаться в случае выбора действия `emergency_land` при срабатывании экстренной защиты, в процентах. +* `timeout_to_disarm_after_watchdog_action` - время, через которое коптер безусловно выключает моторы после срабатывания экстренной защиты, в секундах. + +#### Раздел TELEMETRY + +В данном разделе настраивается поток передачи телеметрии на сервер. + +* `frequency` - частота передачи данных на сервер, целочисленное значение, количество раз в секунду. +* `transmit` - логическое значение, определяет, нужно ли передавать данные на сервер. +* `land_if_pos_delta_bigger_than` - проверка на столкновение коптера с объектом. Работает в цикле телеметрии. Если расстояние между текущим положением коптера и положением, в котором он должен сейчас находиться, больше этого числа (в метрах), коптер очищает очередь задач и переходит в режим AUTO.LAND. +* `log_cpu_and_memory` - логическое значение, которое определяет, будет ли записываться в лог сервиса клиента clever-show состояние процессора и памяти. + +#### Раздел COPTERS + +* `frame_id` - название системы координат, в которой настроен коптер на удержание позиции. Если значение `floor` - клиент публикует статическую систему координат с названием `floor` и настройками из раздела [FLOOR_FRAME](#раздел-floor_frame). +* `takeoff_height` - высота взлёта коптера, в метрах. Используется в начале анимации или при тестировании коптера с сервера. +* `takeoff_time` - максимальное время взлёта коптера, в секундах. +* `safe_takeoff` - логическое значение, определяет, нужно ли производить посадку в безопасном режиме. +* `reach_first_point_time` - максимальное время полёта к первой точке анимации, в секундах. +* `land_time` - время зависания в конечной точке анимации перед посадкой, в секундах. +* `x0_common` - смещение по оси x, общее для всех коптеров, в метрах. +* `y0_common` - смещение по оси y, общее для всех коптеров, в метрах. +* `z0_common` - смещение по оси z, общее для всех коптеров, в метрах. +* `yaw` - поворот коптера при полёте по точкам, в градусах. Если значение `nan` - коптер сохраняет изначальную ориентацию в полёте. +* `land_timeout` - время таймаута посадки, после которого происходит выключение моторов коптера, в секундах. + +#### Раздел FLOOR_FRAME + +* `parent` - название опорной системы координат, относительно которой будет располагаться система координат `floor`. +* `x` - смещение системы координат `floor` по оси x относительно системы координат `parent`, в метрах. +* `y` - смещение системы координат `floor` по оси y относительно системы координат `parent`, в метрах. +* `z` - смещение системы координат `floor` по оси z относительно системы координат `parent`, в метрах. +* `roll` - поворот системы координат `floor` вокруг оси x относительно системы координат `parent`, в градусах. +* `pitch` - поворот системы координат `floor` вокруг оси y относительно системы координат `parent`, в градусах. +* `yaw` - поворот системы координат `floor` вокруг оси z относительно системы координат `parent`, в градусах. + +**Внимание!** Повороты `roll`, `pitch`, `yaw` производятся последовательно в указанном порядке. + +#### Раздел ANIMATION + +В данном разделе настраивается обработка анимации. + +* `takeoff_animation_check` - логическое значение, определяет, будет ли производиться автоматическая обработка старта анимации. **Если значение True**, при загрузке анимации проверяется взлёт коптеров. Если в файле анимации коптер взлетает с земли, при старте анимации будет применена *логика немедленного воспроизведения*: коптер сразу начинает следовать точкам, указанным в анимации. Если в файле анимации коптер начинает полёт в воздухе, при старте анимации будет применена *логика полёта к первой точке*: коптер в начале взлетает на высоту `takeoff_height` за время `takeoff_time`, затем перемещается к первой точке за время `reach_first_point_time`, и затем начинает следовать точкам, указанным в анимации. **Если значение False**, при загрузке анимации не проверяется взлёт коптеров, а при старте анимации действует *логика полёта к первой точке*. +* `land_animation_check` - логическое значение, определяет, будет ли производиться автоматическая обработка завершения анимации. **Если значение True**, при загрузке анимации проверяется посадка коптеров. Если в файле анимации коптер садится на землю и стоит до завершения анимации, проверка удалит все точки в анимации после начала посадки коптера. Таким образом, коптер в конце анимации зависнет над точкой посадки на время `land_time`, сядет автоматически и выключит моторы. **Если значение False**, при загрузке анимации не проверяется посадка коптеров и точкой посадки считается последняя точка в анимации. Например, если анимация посадки нарисована полностью и коптер стоит после посадки на земле некоторое время, а значение данного параметра **False**, всё это время у коптера будут включены моторы и он будет пытаться удержать указанную позицию посадки вплоть до завершении файла анимации, затем через время `land_time` перейдёт в редим посадки. +* `frame_delay` - время воспроизведения одного кадра в секундах. +* `x_ratio` - масштаб анимации по оси x +* `y_ratio` - масштаб анимации по оси y +* `z_ratio` - масштаб анимации по оси z + +#### Раздел PRIVATE + +* `id` - имя коптера, отображаемое в таблице. Если значение `/hostname` - имя определяется из файла `/etc/hostname`. +* `restart_dhcpcd` - логический параметр, определяет, требуется ли перезагрузка коптера при переименовании его `id` удалённо с сервера. +* `use_leds` - логический параметр, определяет, использует ли коптер светодиодную ленту. +* `led_pin` - номер пина GPIO на Raspberry Pi, к которому подключена светодиодная лента. +* `x0` - смещение по оси x, только для данного коптера. +* `y0` - смещение по оси y, только для данного коптера. +* `z0` - смещение по оси z, только для данного коптера. + +#### Раздел FILETRANSFER + +В этом разделе задаются параметры передачи файлов. + +* `files_directory` - директория для хранения файлов анимации (сейчас не используется). +* `animation_file` - название файла анимации, сохраняемое в клиенте. + +#### Раздел NTP + +Помимо синхронизации времени (с миллисекундной точностью) с помощью пакета chrony, предоставляется альтернатива - возможность использования внешних (при наличии соединения локальной сети с интернетом) или внутрисетевых NTP-серверов. **Внимание!** Для корректной работы системы, **и сервер, и клиенты** должны использовать единый способ синхронизации времени (набор параметров в этом разделе). Данный раздел полностью унифицирован и для сервера, и для клиентов. + +* `use_ntp` - определяет, будет ли использоваться синхронизация времени с помощью NTP. (при значении `False` будет использовано локальное время ОС (синхронизируется автоматически при использовании chrony). *Рекомендуется использование chrony, а не NTP* +* `host` - имя хоста или IP адрес NTP сервера (локального или удаленного) +* `port` - порт, используемый NTP сервером diff --git a/docs/ru/server.md b/docs/ru/server.md index 18bf67d..72a54b7 100644 --- a/docs/ru/server.md +++ b/docs/ru/server.md @@ -49,11 +49,11 @@ Данный раздел содержит несколько утилит по отправке различных данных на *выбранные* клиенты. **Внимание!** Не используйте данные команды во время полёта коптеров! -* `Send Animations` - отправка файлов анимации, экспортированных аддоном к Blender, на выбранные коптеры. В диалоговом окне необходимо выбрать *папку*, содержащую файлы анимации. Каждый файл анимации будет отправлен на клиент с именем, соответствующим имени файла без расширения. -* `Send Configurations` - отправка *единого* файла конфигурации клиента на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл конфигурации в установленном формате. Файл конфигурации может быть неполным, в таком случае будут перезаписаны лишь указанные в файле параметры. **Внимание!** Не рекомендуется использовать данное действие для массовой перезаписи `copter ID`, кроме значения `/hostname`. **Внимание!** НЕ отправляйте на клиенты файл конфигурации сервера. -* `Send Launch files` - отправка launch-файлов конфигурации сервиса `clever`. В диалоговом окне необходимо выбрать *папку*, содержащую файлы конфигурации с расширением `.launch`. Все файлы с таким расширением будут отправлены *на каждый* из клиентов. **Внимание!** Существующие файлы конфигурации на коптерах будут перезаписаны, однако файлы, не отправленные сервером, не будут удалены или модифицированы. -* `Send Aruco map` - отправка *единого* файла карты aruco маркеров на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл карты в установленном формате. Файл на клиенте будет перезаписан. После получения и записи файла клиент автоматически перезапустит сервис `clever`. Для возобновления работоспособности полётных функций и получения некоторых значений телеметрии *необходимо подождать* некоторое время до полного запуска сервиса. -* `Send Camera Calibrations` - отправка yaml-файлов калибровки камеры для сервиса `clever`. В диалоговом окне необходимо выбрать *папку*, содержащую файлы конфигурации с расширением `.yaml`. Каждый файл калибровки будет отправлен на клиент с именем (copter ID), соответствующим имени файла без расширения. **Внимание!** Существующий файл калибровки на коптере будет перезаписан. +* `Send animations` - отправка файлов анимации, экспортированных аддоном к Blender, на выбранные коптеры. В диалоговом окне необходимо выбрать *папку*, содержащую файлы анимации. Каждый файл анимации будет отправлен на клиент с именем, соответствующим имени файла без расширения. +* `Send configurations` - отправка *единого* файла конфигурации клиента на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл конфигурации в установленном формате. Файл конфигурации может быть неполным, в таком случае будут перезаписаны лишь указанные в файле параметры. **Внимание!** Не рекомендуется использовать данное действие для массовой перезаписи `copter ID`, кроме значения `/hostname`. **Внимание!** НЕ отправляйте на клиенты файл конфигурации сервера. +* `Send launch files` - отправка launch-файлов конфигурации сервиса `clever`. В диалоговом окне необходимо выбрать *папку*, содержащую файлы конфигурации с сширением `.launch`. Все файлы с таким расширением будут отправлены *на каждый* из клиентов. **Внимание!** Существующие файлы конфигурации на коптерах будут перезаписаны, однако файлы, не отправленные сервером, не будут удалены или модифицированы. +* `Send aruco map` - отправка *единого* файла карты aruco маркеров на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл карты в установленном формате. Файл на клиенте будет перезаписан. После получения и записи файла клиент автоматически перезапустит сервис `clever`. Для возобновления работоспособности полётных функций и получения некоторых значений телеметрии *необходимо подождать* некоторое время до полного запуска сервиса. +* `Send camera calibrations` - отправка yaml-файлов калибровки камеры для сервиса `clever`. В диалоговом окне необходимо выбрать *папку*, содержащую файлы конфигурации с расширением `.yaml`. Каждый файл калибровки будет отправлен на клиент с именем (copter ID), соответствующим имени файла без расширения. **Внимание!** Существующий файл калибровки на коптере будет перезаписан. * `Send FCU parameters` - отправка и запись *единого* файла конфигураций полётного контроллера (FCU) на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл параметров в установленном формате. Параметры на полётном контроллере будут перезаписаны. * `Developer mode`: **Внимание!** Используйте данные действия с большой осторожностью. * `Send any file` - отправка *одного* любого файла на все выбранные клиенты. В диалоговом окне необходимо выбрать *один* файл. Далее, необходимо указать путь, по которому данный файл будет записан на клиенты (не включая имя файла). @@ -65,7 +65,7 @@ ![Скриншот раздела Drone](../assets/server-drone.png) * `Set Z offfset to ground` - устанавливает собственный отступ по Z каждого из выбранных клиентов в значение, равное текущему положению по координате Z. Можно применять для выравнимания общей высоты полёта коптеров. -* `Reset Z offfset` - устанавливает собственный отступ по Z каждого из выбранных клиентов в значение `0` +* `Reset Z offfset` - устанавливает собственный отступ по Z каждого из выбранных клиентов в значение `0`. * `Restart chrony` - перезапускает сервис синхронизации времени `chrony` на выбранных клиентах. Используйте для ручной синхронизации в случаях, если время между сервером и клиентами не синхронихированно. * `Remove from table` - удаляет выбранные коптеры из таблицы. **Внимание!** В случае, если клиент был подключен, будет произведено отключение. В случае если удалённый таким образом клиент исправно функционировал, он переподключится в кратчайшие сроки. * `Developer mode`: **Внимание!** Используйте данные действия с большой осторожностью. @@ -163,7 +163,7 @@ port = 123 В этом разделе задаются параметры сетевого взаимодействия сервера. Доступны следующие параметры: -* `port` - TCP порт, на котором будут приниматься входящие соединения от клиентов (коптеров). При использовании broadcast данный порт будет сконфигурирован у клиента автоматически. *Рекомендуется изменить значение по умолчанию в целях безопасности* (любое пятизначное и более число, если другое ПО не использует выбранный порт). +* `port` - TCP порт, на который будут приниматься входящие соединения от клиентов. При использовании broadcast данный порт будет сконфигурирован у клиента автоматически. *Рекомендуется изменить значение по умолчанию в целях безопасности* (любое пятизначное и более число, если другое ПО не использует выбранный порт). * `buffer_size` - размер буфера при приёме и передаче данных. *Не рекомендуется изменять. Рекомендуется использовать единое значение у сервера и клиентов.* * `remove_disconnected` - Определяет поведение при разрыве связи с клиентом. При значении `True` вся информация о клиенте *будет удалена* как из внутренней памяти, так и *из таблицы*. *Это может привести к 'скачкам' таблицы при отключении клиентов.* При значении `False` отключённые клиенты *не будут* удалены из таблицы, но будут отображены с подсвечиванием ячейки в столбце `copter ID` красным цветом. Все данные будут сохранены. При переподключении клиента, он будет ассоциирован с той же строкой таблицы, а ячейка со значением `copter ID` вновь станет зелёного цвета. @@ -187,7 +187,7 @@ port = 123 Помимо синхронизации времени (с миллисекундной точностью) с помощью пакета chrony, предоставляется альтернатива - возможность использования внешних (при наличии соединения локальной сети с интернетом) или внутрисетевых NTP-серверов. **Внимание!** Для корректной работы системы, **и сервер, и клиенты** должны использовать единый способ синхронизации времени (набор параметров в этом разделе). Данный раздел полностью унифицирован и для сервера, и для клиентов. -* `use_ntp` - Определяет, будет ли использоваться синхронизация времени с помощью NTP. (при значении `False` будет использовано локальное время ОС (синхронизируется автоматически при использовании chrony). *Рекомендуется использование chrony, а не NTP* +* `use_ntp` - определяет, будет ли использоваться синхронизация времени с помощью NTP. (при значении `False` будет использовано локальное время ОС (синхронизируется автоматически при использовании chrony). *Рекомендуется использование chrony, а не NTP* * `host` - имя хоста или IP адрес NTP сервера (локального или удаленного) * `port` - порт, используемый NTP сервером From 12b9f6650a9f3d05a5ad4f1f8f6457c89692e0ab Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Thu, 26 Dec 2019 03:31:15 +0300 Subject: [PATCH 38/59] blender-addon: Update repo and docs links --- blender-addon/addon.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blender-addon/addon.py b/blender-addon/addon.py index 1a9d527..0fea471 100644 --- a/blender-addon/addon.py +++ b/blender-addon/addon.py @@ -16,8 +16,8 @@ bl_info = { "location": "File > Export > CSV Drone Swarm Animation Exporter (.csv)", "description": "Export > CSV Drone Swarm Animation Exporter (.csv)", "warning": "", - "wiki_url": "https://github.com/artem30801/blender-csv-animation/blob/master/README.md", - "tracker_url": "https://github.com/artem30801/blender-csv-animation/issues", + "wiki_url": "https://github.com/CopterExpress/clever-show/blob/master/docs/ru/blender-addon.md", + "tracker_url": "https://github.com/CopterExpress/clever-show/issues", "category": "Import-Export" } From 443b5ac2de5adc4347c30d5a1ad3f6d738125ef3 Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Thu, 26 Dec 2019 03:31:41 +0300 Subject: [PATCH 39/59] docs: Update blender-addon article --- docs/ru/blender-addon.md | 59 +++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/docs/ru/blender-addon.md b/docs/ru/blender-addon.md index 6af6eb8..35b9496 100644 --- a/docs/ru/blender-addon.md +++ b/docs/ru/blender-addon.md @@ -1,30 +1,33 @@ -# Установка и настройка аддона -## Установка -1. Скачайте [аддон](https://github.com/artem30801/blender-csv-animation) для экспорта анимации из Blender в полётные пути для коптеров. -2. Скачайте и установите согласно инструкциям последнюю версию Blender 2.8 (beta) с [оффициального сайта](https://builder.blender.org/download/) или при использовании OS Linux через команду терминала: -```bash -snap install blender --channel=beta --classic -``` -3. Откройте Blender, в верхнем меню выберите `Edit > Preferences`. В открывшемся окне настроек в боковой панели выберите пункт `Add-ons`. Нажмите на кнопку `Install...` в верхнем правом углу окна. В диалоговом окне откройте путь к папке со склонированным репозиторием проекта и выберите файл `addon.py` по пути [`blender-csv-animation/addon.py`](https://github.com/artem30801/blender-csv-animation/blob/master/addon.py). Нажмите `Install Add-on from file...`. Аддон установлен. -## Активация -В выпадающем списке `All` выберите пункт `User`. Поставьте "галочку" напротив аддона `Import-Export: Export > CSV Drone Swarm Animation Exporter` для активации аддона. Аддон активирован и готов к работе. Выполнение этих операций не понадобится при дальнейших запусках Blender. -## Дополнительно -Для деактивации аддона уберите "галочку" напротив имени аддона, как описано в предыдущем пункте. Для получения дополнительных сведений (версия, путь к файлу...) нажмите знак стрелочки слева от поля активации. В развернувшемся блоке так же есть кнопки: `Documentation` - ведет на страницу документации аддона (вы тут); `Report a bug` - ведет на страницу багтрекера на репозитории аддона; `Remove` - удалят (деинсталлирует) аддон (перед установокой новой версии рекомендуется удалить старую). -# Подготовка и создание анимации дронов -## Взлёт -... -## Посадка -**Внимание!** В анимации НЕ нужно отображать процесс посадки коптера до момента касания с полом. Снижение (с любой высоты) и посадка будут совершены автоматически из последней точки анимации данного дрона. +# Аддон для экспорта анимации из Blender + +Аддон для Blender предназначен для преобразования анимации полёта коптеров, нарисованной в Blender, в полётные пути для каждого коптера анимации, с учётом цвета объектов в каждый момент времени. + +## Установка и настройка + +* Скачайте и установите согласно инструкциям последнюю версию Blender 2.81 с [оффициального сайта](https://www.blender.org/download/). +* Откройте Blender, в верхнем меню выберите `Edit > Preferences`. В открывшемся окне настроек в боковой панели выберите пункт `Add-ons`. Нажмите на кнопку `Install...` в верхнем правом углу окна. В диалоговом окне откройте путь к папке с аддоном [clever-show/blender-addon](../../blender-addon/) и выберите файл `addon.py`. Нажмите `Install Add-on from file...`. Аддон установлен. +* После установки аддона поставьте "галочку" напротив аддона `Import-Export: Export > CSV Drone Swarm Animation Exporter` для активации аддона. + +Аддон активирован и готов к работе. Выполнение этих операций не понадобится при дальнейших запусках Blender. + +## Экпорт при помощи аддона -[Пример](https://github.com/artem30801/blender-csv-animation/blob/master/Examples/copter_base_animation.blend) можно использовать в качестве шаблона. -# Экпорт при помощи аддона Для вызова диалогового окна экспорта нажмите в верхнем меню `File > Export > CSV Drone Swarm Animation Exporter`. В открывшемся окне экспорта необходимо выбрать целевой путь экспорта и название папки, которую создаст аддон в процессе экспорта. В боковом меню доступна панель параметров экспорта: -* `Use name filter for objects` - при отключении этого параметра будут экспортированы _все видимые объекты_ -* `Name identifier` -* `Show detailed animation warnings` - -* `Speed limit` - при нарушении указанного ограничения по скорости передвижения дронов будут выведены предупреждения -* `Distance limit` - при нарушении указанной минимальной дистанции между дронами будут выведены предупреждения -После настройки (при необходимости) нужных параметров нажмите кнопку `Export Drone Swarm animation` - \ No newline at end of file + +* `Use name filter for objects` - чекбокс, определяет, будет ли использован фильтр объектов при сохранении их путей. При отключении этого параметра будут экспортированы пути всех видимых объектов. +* `Name identifier` - фильтр имён объектов. Если активен чекбокс `Use name filter for objects`, будут сохранены только пути объектов, содержащих данное значение в названии. +* `Show detailed animation warnings` - чекбокс, определяет, будут ли показаны предупреждения по превышению скоростей и расстояний в анимации. +* `Speed limit` - при нарушении указанного ограничения по скорости передвижения дронов будут выведены предупреждения. +* `Distance limit` - при нарушении указанной минимальной дистанции между дронами будут выведены предупреждения. + +После настройки нужных параметров нажмите кнопку `Export Drone Swarm animation`. В указанную папку экспортируются анимации указанных объектов из проекта Blender в формате `.csv`. + +## Деактивация и удаление + +Для деактивации аддона уберите "галочку" напротив имени аддона, как описано [выше](#установка-и-настройка). + +Для получения дополнительных сведений нажмите знак стрелочки слева от поля активации. В развернувшемся блоке так же есть кнопки: + +* `Documentation` - ведет на страницу документации аддона +* `Report a bug` - ведет на страницу issues репозитория clever-show +* `Remove` - удаляет аддон (перед установкой новой версии рекомендуется удалить старую) From a32de98d683f57aa9e2241b8637a62bd3ab544bb Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Thu, 26 Dec 2019 03:37:59 +0300 Subject: [PATCH 40/59] docs: Move blender-addon.md to blender-addon/README.md --- blender-addon/README.md | 52 +++++++++++++++++++++++----------------- blender-addon/addon.py | 2 +- docs/ru/blender-addon.md | 33 ------------------------- 3 files changed, 31 insertions(+), 56 deletions(-) delete mode 100644 docs/ru/blender-addon.md diff --git a/blender-addon/README.md b/blender-addon/README.md index 50a5992..35b9496 100644 --- a/blender-addon/README.md +++ b/blender-addon/README.md @@ -1,25 +1,33 @@ -# blender-csv-animation -A Blender extension that export paths of objects in blender animation to a csv files +# Аддон для экспорта анимации из Blender -## CSV file format -First row is the animation filename. -Every next row of the file contains following information about an object: -- frame number, -- x coordinate, -- y coordinate, -- z coordinate, -- rotaion around z-axis angle (yaw for copter), -- rgb. +Аддон для Blender предназначен для преобразования анимации полёта коптеров, нарисованной в Blender, в полётные пути для каждого коптера анимации, с учётом цвета объектов в каждый момент времени. -## How to use it -Clone or download this repository -```bash -git clone https://github.com/artem30801/CleverSwarm.git -``` -Open Blender and install the addon: -1) Open User Prerences windows using main menu or shortcut (Ctrl + Alt + U): Files - User Preferences -2) Under Add-ons tab click Install Add-on from File... -3) Choose addon.py file from the directory of this repository -4) Enable the Add-on +## Установка и настройка -Use [official docs](https://docs.blender.org/manual/en/latest/preferences/addons.html) for getting additional information +* Скачайте и установите согласно инструкциям последнюю версию Blender 2.81 с [оффициального сайта](https://www.blender.org/download/). +* Откройте Blender, в верхнем меню выберите `Edit > Preferences`. В открывшемся окне настроек в боковой панели выберите пункт `Add-ons`. Нажмите на кнопку `Install...` в верхнем правом углу окна. В диалоговом окне откройте путь к папке с аддоном [clever-show/blender-addon](../../blender-addon/) и выберите файл `addon.py`. Нажмите `Install Add-on from file...`. Аддон установлен. +* После установки аддона поставьте "галочку" напротив аддона `Import-Export: Export > CSV Drone Swarm Animation Exporter` для активации аддона. + +Аддон активирован и готов к работе. Выполнение этих операций не понадобится при дальнейших запусках Blender. + +## Экпорт при помощи аддона + +Для вызова диалогового окна экспорта нажмите в верхнем меню `File > Export > CSV Drone Swarm Animation Exporter`. В открывшемся окне экспорта необходимо выбрать целевой путь экспорта и название папки, которую создаст аддон в процессе экспорта. В боковом меню доступна панель параметров экспорта: + +* `Use name filter for objects` - чекбокс, определяет, будет ли использован фильтр объектов при сохранении их путей. При отключении этого параметра будут экспортированы пути всех видимых объектов. +* `Name identifier` - фильтр имён объектов. Если активен чекбокс `Use name filter for objects`, будут сохранены только пути объектов, содержащих данное значение в названии. +* `Show detailed animation warnings` - чекбокс, определяет, будут ли показаны предупреждения по превышению скоростей и расстояний в анимации. +* `Speed limit` - при нарушении указанного ограничения по скорости передвижения дронов будут выведены предупреждения. +* `Distance limit` - при нарушении указанной минимальной дистанции между дронами будут выведены предупреждения. + +После настройки нужных параметров нажмите кнопку `Export Drone Swarm animation`. В указанную папку экспортируются анимации указанных объектов из проекта Blender в формате `.csv`. + +## Деактивация и удаление + +Для деактивации аддона уберите "галочку" напротив имени аддона, как описано [выше](#установка-и-настройка). + +Для получения дополнительных сведений нажмите знак стрелочки слева от поля активации. В развернувшемся блоке так же есть кнопки: + +* `Documentation` - ведет на страницу документации аддона +* `Report a bug` - ведет на страницу issues репозитория clever-show +* `Remove` - удаляет аддон (перед установкой новой версии рекомендуется удалить старую) diff --git a/blender-addon/addon.py b/blender-addon/addon.py index 0fea471..221e6cf 100644 --- a/blender-addon/addon.py +++ b/blender-addon/addon.py @@ -16,7 +16,7 @@ bl_info = { "location": "File > Export > CSV Drone Swarm Animation Exporter (.csv)", "description": "Export > CSV Drone Swarm Animation Exporter (.csv)", "warning": "", - "wiki_url": "https://github.com/CopterExpress/clever-show/blob/master/docs/ru/blender-addon.md", + "wiki_url": "https://github.com/CopterExpress/clever-show/blob/master/blender-addon/README.md", "tracker_url": "https://github.com/CopterExpress/clever-show/issues", "category": "Import-Export" } diff --git a/docs/ru/blender-addon.md b/docs/ru/blender-addon.md deleted file mode 100644 index 35b9496..0000000 --- a/docs/ru/blender-addon.md +++ /dev/null @@ -1,33 +0,0 @@ -# Аддон для экспорта анимации из Blender - -Аддон для Blender предназначен для преобразования анимации полёта коптеров, нарисованной в Blender, в полётные пути для каждого коптера анимации, с учётом цвета объектов в каждый момент времени. - -## Установка и настройка - -* Скачайте и установите согласно инструкциям последнюю версию Blender 2.81 с [оффициального сайта](https://www.blender.org/download/). -* Откройте Blender, в верхнем меню выберите `Edit > Preferences`. В открывшемся окне настроек в боковой панели выберите пункт `Add-ons`. Нажмите на кнопку `Install...` в верхнем правом углу окна. В диалоговом окне откройте путь к папке с аддоном [clever-show/blender-addon](../../blender-addon/) и выберите файл `addon.py`. Нажмите `Install Add-on from file...`. Аддон установлен. -* После установки аддона поставьте "галочку" напротив аддона `Import-Export: Export > CSV Drone Swarm Animation Exporter` для активации аддона. - -Аддон активирован и готов к работе. Выполнение этих операций не понадобится при дальнейших запусках Blender. - -## Экпорт при помощи аддона - -Для вызова диалогового окна экспорта нажмите в верхнем меню `File > Export > CSV Drone Swarm Animation Exporter`. В открывшемся окне экспорта необходимо выбрать целевой путь экспорта и название папки, которую создаст аддон в процессе экспорта. В боковом меню доступна панель параметров экспорта: - -* `Use name filter for objects` - чекбокс, определяет, будет ли использован фильтр объектов при сохранении их путей. При отключении этого параметра будут экспортированы пути всех видимых объектов. -* `Name identifier` - фильтр имён объектов. Если активен чекбокс `Use name filter for objects`, будут сохранены только пути объектов, содержащих данное значение в названии. -* `Show detailed animation warnings` - чекбокс, определяет, будут ли показаны предупреждения по превышению скоростей и расстояний в анимации. -* `Speed limit` - при нарушении указанного ограничения по скорости передвижения дронов будут выведены предупреждения. -* `Distance limit` - при нарушении указанной минимальной дистанции между дронами будут выведены предупреждения. - -После настройки нужных параметров нажмите кнопку `Export Drone Swarm animation`. В указанную папку экспортируются анимации указанных объектов из проекта Blender в формате `.csv`. - -## Деактивация и удаление - -Для деактивации аддона уберите "галочку" напротив имени аддона, как описано [выше](#установка-и-настройка). - -Для получения дополнительных сведений нажмите знак стрелочки слева от поля активации. В развернувшемся блоке так же есть кнопки: - -* `Documentation` - ведет на страницу документации аддона -* `Report a bug` - ведет на страницу issues репозитория clever-show -* `Remove` - удаляет аддон (перед установкой новой версии рекомендуется удалить старую) From 92793ba9d53d32d482abce954b6d7c0d4c91b527 Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Thu, 26 Dec 2019 04:00:45 +0300 Subject: [PATCH 41/59] docs: Modify blender addon readme and article --- blender-addon/README.md | 38 +++++++++----------------------------- docs/ru/blender-addon.md | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 29 deletions(-) create mode 100644 docs/ru/blender-addon.md diff --git a/blender-addon/README.md b/blender-addon/README.md index 35b9496..9fb1d70 100644 --- a/blender-addon/README.md +++ b/blender-addon/README.md @@ -1,33 +1,13 @@ -# Аддон для экспорта анимации из Blender +# Blender animation export add-on -Аддон для Blender предназначен для преобразования анимации полёта коптеров, нарисованной в Blender, в полётные пути для каждого коптера анимации, с учётом цвета объектов в каждый момент времени. +The add-on for Blender is designed to convert the flight animation of copters drawn in Blender into flight paths for each copter, taking into account the color of objects at each given time. -## Установка и настройка +Export result is a folder with .csv files where each line in file represents a sequence with comma delimiter: -* Скачайте и установите согласно инструкциям последнюю версию Blender 2.81 с [оффициального сайта](https://www.blender.org/download/). -* Откройте Blender, в верхнем меню выберите `Edit > Preferences`. В открывшемся окне настроек в боковой панели выберите пункт `Add-ons`. Нажмите на кнопку `Install...` в верхнем правом углу окна. В диалоговом окне откройте путь к папке с аддоном [clever-show/blender-addon](../../blender-addon/) и выберите файл `addon.py`. Нажмите `Install Add-on from file...`. Аддон установлен. -* После установки аддона поставьте "галочку" напротив аддона `Import-Export: Export > CSV Drone Swarm Animation Exporter` для активации аддона. +* `x, y, z` coordinates of an object in meters +* `yaw` of an object in radians +* `red, green, blue` values of the color of an object, each is integer from 0 to 255 -Аддон активирован и готов к работе. Выполнение этих операций не понадобится при дальнейших запусках Blender. - -## Экпорт при помощи аддона - -Для вызова диалогового окна экспорта нажмите в верхнем меню `File > Export > CSV Drone Swarm Animation Exporter`. В открывшемся окне экспорта необходимо выбрать целевой путь экспорта и название папки, которую создаст аддон в процессе экспорта. В боковом меню доступна панель параметров экспорта: - -* `Use name filter for objects` - чекбокс, определяет, будет ли использован фильтр объектов при сохранении их путей. При отключении этого параметра будут экспортированы пути всех видимых объектов. -* `Name identifier` - фильтр имён объектов. Если активен чекбокс `Use name filter for objects`, будут сохранены только пути объектов, содержащих данное значение в названии. -* `Show detailed animation warnings` - чекбокс, определяет, будут ли показаны предупреждения по превышению скоростей и расстояний в анимации. -* `Speed limit` - при нарушении указанного ограничения по скорости передвижения дронов будут выведены предупреждения. -* `Distance limit` - при нарушении указанной минимальной дистанции между дронами будут выведены предупреждения. - -После настройки нужных параметров нажмите кнопку `Export Drone Swarm animation`. В указанную папку экспортируются анимации указанных объектов из проекта Blender в формате `.csv`. - -## Деактивация и удаление - -Для деактивации аддона уберите "галочку" напротив имени аддона, как описано [выше](#установка-и-настройка). - -Для получения дополнительных сведений нажмите знак стрелочки слева от поля активации. В развернувшемся блоке так же есть кнопки: - -* `Documentation` - ведет на страницу документации аддона -* `Report a bug` - ведет на страницу issues репозитория clever-show -* `Remove` - удаляет аддон (перед установкой новой версии рекомендуется удалить старую) +Documentation is located here: +* English +* [Russian](../docs/ru/blender-addon.md) diff --git a/docs/ru/blender-addon.md b/docs/ru/blender-addon.md new file mode 100644 index 0000000..35b9496 --- /dev/null +++ b/docs/ru/blender-addon.md @@ -0,0 +1,33 @@ +# Аддон для экспорта анимации из Blender + +Аддон для Blender предназначен для преобразования анимации полёта коптеров, нарисованной в Blender, в полётные пути для каждого коптера анимации, с учётом цвета объектов в каждый момент времени. + +## Установка и настройка + +* Скачайте и установите согласно инструкциям последнюю версию Blender 2.81 с [оффициального сайта](https://www.blender.org/download/). +* Откройте Blender, в верхнем меню выберите `Edit > Preferences`. В открывшемся окне настроек в боковой панели выберите пункт `Add-ons`. Нажмите на кнопку `Install...` в верхнем правом углу окна. В диалоговом окне откройте путь к папке с аддоном [clever-show/blender-addon](../../blender-addon/) и выберите файл `addon.py`. Нажмите `Install Add-on from file...`. Аддон установлен. +* После установки аддона поставьте "галочку" напротив аддона `Import-Export: Export > CSV Drone Swarm Animation Exporter` для активации аддона. + +Аддон активирован и готов к работе. Выполнение этих операций не понадобится при дальнейших запусках Blender. + +## Экпорт при помощи аддона + +Для вызова диалогового окна экспорта нажмите в верхнем меню `File > Export > CSV Drone Swarm Animation Exporter`. В открывшемся окне экспорта необходимо выбрать целевой путь экспорта и название папки, которую создаст аддон в процессе экспорта. В боковом меню доступна панель параметров экспорта: + +* `Use name filter for objects` - чекбокс, определяет, будет ли использован фильтр объектов при сохранении их путей. При отключении этого параметра будут экспортированы пути всех видимых объектов. +* `Name identifier` - фильтр имён объектов. Если активен чекбокс `Use name filter for objects`, будут сохранены только пути объектов, содержащих данное значение в названии. +* `Show detailed animation warnings` - чекбокс, определяет, будут ли показаны предупреждения по превышению скоростей и расстояний в анимации. +* `Speed limit` - при нарушении указанного ограничения по скорости передвижения дронов будут выведены предупреждения. +* `Distance limit` - при нарушении указанной минимальной дистанции между дронами будут выведены предупреждения. + +После настройки нужных параметров нажмите кнопку `Export Drone Swarm animation`. В указанную папку экспортируются анимации указанных объектов из проекта Blender в формате `.csv`. + +## Деактивация и удаление + +Для деактивации аддона уберите "галочку" напротив имени аддона, как описано [выше](#установка-и-настройка). + +Для получения дополнительных сведений нажмите знак стрелочки слева от поля активации. В развернувшемся блоке так же есть кнопки: + +* `Documentation` - ведет на страницу документации аддона +* `Report a bug` - ведет на страницу issues репозитория clever-show +* `Remove` - удаляет аддон (перед установкой новой версии рекомендуется удалить старую) From 009edea40315bbc6027f24a69a1a4e041cc1cd03 Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Thu, 26 Dec 2019 04:04:41 +0300 Subject: [PATCH 42/59] docs: Update image-building article --- docs/ru/image-building.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ru/image-building.md b/docs/ru/image-building.md index 855f3a9..6f7cd24 100644 --- a/docs/ru/image-building.md +++ b/docs/ru/image-building.md @@ -10,7 +10,7 @@ sudo apt install docker.io ## Локальная сборка с изменением настроек Клевера -* Замените файлы настроек Клевера (launch файлы и карту) в [папке](../builder/clever-config) `builder/clever-config` в директории с исходным кодом CleverSwarm. +* Замените файлы настроек Клевера (launch файлы и карту) в [папке](../builder/clever-config) `builder/clever-config` в директории с исходным кодом clever-show. * Соберите свой образ с помощью docker: ```bash cd source-dir From 5a893361ba5f9fe047667c92dd80ba5776e008db Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Thu, 26 Dec 2019 04:08:34 +0300 Subject: [PATCH 43/59] Delete unnecessary logging modules --- Drone/ros_logging.py | 29 ----------------------------- logging_lib.py | 44 -------------------------------------------- 2 files changed, 73 deletions(-) delete mode 100644 Drone/ros_logging.py delete mode 100644 logging_lib.py diff --git a/Drone/ros_logging.py b/Drone/ros_logging.py deleted file mode 100644 index c395f3c..0000000 --- a/Drone/ros_logging.py +++ /dev/null @@ -1,29 +0,0 @@ -import logging -import rospy - - -class RosHandler(logging.Handler): - - level_map = { - logging.DEBUG: rospy.logdebug, - logging.INFO: rospy.loginfo, - logging.WARNING: rospy.logwarn, - logging.ERROR: rospy.logerr, - logging.CRITICAL: rospy.logfatal - } - - def emit(self, record): - print(record.levelno, record.name, record.msg) - if "rosout" not in record.msg: - try: - pass - #self.level_map[record.levelno]("%s: %s" % (record.name, record.msg)) - except KeyError: - rospy.logerr("unknown log level %s LOG: %s: %s" % (record.levelno, record.name, record.msg)) - - -def route_logger_to_ros(logger_name=None): - if logger_name is not None: - logging.getLogger(logger_name).addHandler(RosHandler()) - else: - logging.getLogger().addHandler(RosHandler()) diff --git a/logging_lib.py b/logging_lib.py deleted file mode 100644 index d53100a..0000000 --- a/logging_lib.py +++ /dev/null @@ -1,44 +0,0 @@ -import logging - -try: - import rospy -except ImportError: - ros = False -else: - ros = True - - -class Logger: - def __init__(self, logger=logging.getLogger(), use_ros=False): - self.ros = True if use_ros and ros else False - self.logger = logger - - def info(self, msg): - self.logger.info(msg) - - if self.ros: - rospy.loginfo(msg) - - def debug(self, msg): - self.logger.debug(msg) - - if self.ros: - rospy.logdebug(msg) - - def warning(self, msg): - self.logger.warning(msg) - - if self.ros: - rospy.logwarn(msg) - - def error(self, msg): - self.logger.error(msg) - - if self.ros: - rospy.logerr(msg) - - def critical(self, msg): - self.logger.critical(msg) - - if self.ros: - rospy.logfatal(msg) From f95b2a87132a4c7bf1b20b330376409c44968472 Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Thu, 26 Dec 2019 11:30:02 +0300 Subject: [PATCH 44/59] docs: Fix image-building article --- docs/ru/image-building.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/docs/ru/image-building.md b/docs/ru/image-building.md index 6f7cd24..5c436da 100644 --- a/docs/ru/image-building.md +++ b/docs/ru/image-building.md @@ -3,15 +3,21 @@ Иногда возникает необходимость собрать образ с настройками коптера, отличными от релизной версии образа. Есть несколько способов это сделать. ## Подготовка к сборке + Установите [docker](https://www.docker.com): + ```bash sudo apt install docker.io ``` ## Локальная сборка с изменением настроек Клевера -* Замените файлы настроек Клевера (launch файлы и карту) в [папке](../builder/clever-config) `builder/clever-config` в директории с исходным кодом clever-show. +* Поместите папки с файлами настроек Клевера (`launch`, `map` и `camera_info`) в [папку](../../builder/clever-config) `builder/clever-config` в директории с исходным кодом clever-show. + * Все файлы из папки `launch` будут скопированы в директорию `/home/pi/catkin_ws/src/clever/clever/launch` в собранном образе. + * Все файлы из папки `map` будут скопированы в директорию `/home/pi/catkin_ws/src/clever/aruco_pose/map` в собранном образе. + * Все файлы из папки `camera_info` будут скопированы в директорию `/home/pi/catkin_ws/src/clever/clever/camera_info` в собранном образе. * Соберите свой образ с помощью docker: + ```bash cd source-dir sudo docker run --privileged -it --rm -v /dev:/dev -v $(pwd):/mnt goldarte/img-tool:v0.5 @@ -20,26 +26,36 @@ sudo docker run --privileged -it --rm -v /dev:/dev -v $(pwd):/mnt goldarte/img-t ## Ручная настройка образа * Разархивируйте файл со скачанным образом, перейдите в директорию с этим образом, и войдите в консоль сборщика образа с помощью команды: + ```bash cd image-dir sudo docker run --privileged -it --rm -v /dev:/dev -v $(pwd):/mnt goldarte/img-tool:v0.5 img-chroot /mnt/ ``` + где `` - имя файла образа. В открывшемся терминале с помощью стандартных программ (nano, git, cp, apt-get) вы можете донастроить образ. + * Внешние файлы вы можете перенести в образ с помощью команды: + ```bash sudo docker run --privileged -it --rm -v /dev:/dev -v $(pwd):/mnt goldarte/img-tool:v0.5 img-chroot /mnt/ copy /mnt/ ``` + где `` - файл, который нужно перенести в образ (расположение относительно папки с образом, например `../builder/assets/clever-show.service`), а `` - путь в образе, куда нужно переместить файл. + * Если в образе не хватает места для всех необходимых файлов, можно расширить образ с помощью команды: + ```bash sudo docker run --privileged -it --rm -v /dev:/dev -v $(pwd):/mnt goldarte/img-tool:v0.5 img-resize /mnt/ max ``` + где `` - размер в байтах. Например 5G будет означать 5GB, а 5M - 5MB. + * После расширения образа его можно сжать до минимального размера + 10МB командой + ```bash sudo docker run --privileged -it --rm -v /dev:/dev -v $(pwd):/mnt goldarte/img-tool:v0.5 img-resize /mnt/ min ``` ## Изменение скриптов сборки -Статья по изменению скриптов сборки образа и создания кастомной сборки написана [здесь](https://clever.copterexpress.com/ru/image_building.html) \ No newline at end of file +Статья по изменению скриптов сборки образа и создания кастомной сборки написана [здесь](https://clever.copterexpress.com/ru/image_building.html) From b253a0cf8eee3255ae2900e59c77dc09e9314cde Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Thu, 26 Dec 2019 11:30:31 +0300 Subject: [PATCH 45/59] builder: Add README file --- builder/README.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 builder/README.md diff --git a/builder/README.md b/builder/README.md new file mode 100644 index 0000000..76b5ab1 --- /dev/null +++ b/builder/README.md @@ -0,0 +1,28 @@ +# Scripts for building Raspberry Pi image + +This directory contains scripts for automated image building via travis-ci.org. + +You can place the folders with the `clever` settings files (`launch`,`map` and `camera_info`) to the folder `clever-config` located in this directory. Then you can build your image with custom drone settings locally. + +* All files from the `launch` folder will be copied to the `/home/pi/catkin_ws/src/clever/clever/launch` directory in the assembled image. +* All files from the `map` folder will be copied to the `/home/pi/catkin_ws/src/clever/aruco_pose/map` directory in the assembled image. +* All files from the `camera_info` folder will be copied to the `/home/pi/catkin_ws/src/clever/clever/camera_info` directory in the assembled image. + +Install docker if needed: + +```bash +sudo apt install docker.io +``` + +Build your custom image with docker: + +```bash +cd source-dir +sudo docker run --privileged -it --rm -v /dev:/dev -v $(pwd):/mnt goldarte/img-tool:v0.5 +``` + +The image will be located in `images` directory in the clever-show source code directory. + +Article about building custom image is located here: +* English +* [Russian](../docs/ru/image-building.md) From 853cc0d3fa42e58041a3650e6e066e9892884420 Mon Sep 17 00:00:00 2001 From: artem30801 <38689676+artem30801@users.noreply.github.com> Date: Thu, 26 Dec 2019 18:23:10 +0300 Subject: [PATCH 46/59] File sending fix (#61) * Changed sending buffering * Connected config to buffer size in clients * Update data logging for file transmit * Fix logging when receive data Co-authored-by: Arthur Golubtsov --- Drone/client_config.ini | 3 ++- Server/server.py | 3 ++- Server/server_config.ini | 2 +- messaging_lib.py | 14 ++++++++------ 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Drone/client_config.ini b/Drone/client_config.ini index 1da8803..1710d60 100644 --- a/Drone/client_config.ini +++ b/Drone/client_config.ini @@ -2,7 +2,7 @@ port = 25000 broadcast_port = 8181 host = 192.168.1.101 -buffer_size = 1024 +buffer_size = 10000 [VISUAL_POSE_WATCHDOG] timeout = 1.0 @@ -64,3 +64,4 @@ animation_file = animation.csv use_ntp = False host = ntp1.stratum2.ru port = 123 + diff --git a/Server/server.py b/Server/server.py index b3b958e..6b9af0f 100644 --- a/Server/server.py +++ b/Server/server.py @@ -180,6 +180,7 @@ class Server(messaging.Singleton): if not any([client_addr == addr[0] for client_addr in Client.clients.keys()]): client = Client(addr[0]) + client.buffer_size = self.BUFFER_SIZE logging.info("New client") else: client = Client.clients[addr[0]] @@ -336,7 +337,7 @@ class Client(messaging.ConnectionManager): @requires_connect def _send(self, data): super(Client, self)._send(data) - logging.debug("Queued data to send: {}".format(data)) + logging.debug("Queued data to send (first 256 bytes): {}".format(data[:256])) def send_config_options(self, *options: ConfigOption, reload_config=True): logging.info("Sending config options: {} to {}".format(options, self.addr)) diff --git a/Server/server_config.ini b/Server/server_config.ini index 1f6da13..653133d 100644 --- a/Server/server_config.ini +++ b/Server/server_config.ini @@ -1,6 +1,6 @@ [SERVER] port = 25000 -buffer_size = 1024 +buffer_size = 10000 remove_disconnected = False [CHECKS] diff --git a/messaging_lib.py b/messaging_lib.py index 855e1ef..4591842 100644 --- a/messaging_lib.py +++ b/messaging_lib.py @@ -207,7 +207,7 @@ class ConnectionManager(object): messages_callbacks = {} requests_callbacks = {} - def __init__(self, whoami = "computer"): + def __init__(self, whoami="computer"): self.selector = None self.socket = None self.addr = None @@ -227,7 +227,7 @@ class ConnectionManager(object): self._request_lock = threading.Lock() self._close_lock = threading.Lock() - self.BUFFER_SIZE = 1024 + self.buffer_size = 1024 self.resume_queue = False self.resend_requests = True @@ -333,14 +333,14 @@ class ConnectionManager(object): def _read(self): try: - data = self.socket.recv(self.BUFFER_SIZE) + data = self.socket.recv(self.buffer_size) except io.BlockingIOError: # Resource temporarily unavailable (errno EWOULDBLOCK) pass else: if data: self._recv_buffer += data - logger.debug("Received {} from {}".format(data, self.addr)) + logger.debug("Received {} bytes from {}".format(len(data), self.addr)) else: logger.warning("Connection to {} lost!".format(self.addr)) @@ -426,7 +426,7 @@ class ConnectionManager(object): def _write(self): try: - sent = self.socket.send(self._send_buffer) + sent = self.socket.send(self._send_buffer[:self.buffer_size]) except io.BlockingIOError: # Resource temporarily unavailable (errno EWOULDBLOCK) pass @@ -436,8 +436,10 @@ class ConnectionManager(object): raise error else: - logger.debug("Sent {} to {}".format(self._send_buffer[:sent], self.addr)) self._send_buffer = self._send_buffer[sent:] + left = len(self._send_buffer) + logger.debug("Sent message to {}: sent {} bytes, {} bytes left.".format(self.addr, sent, left))#, self._send_buffer[:sent],)) + def _send(self, data): with self._send_lock: From 5435a014f2d87b9cab0ac706f390b87f41dcb36d Mon Sep 17 00:00:00 2001 From: artem30801 <38689676+artem30801@users.noreply.github.com> Date: Thu, 26 Dec 2019 18:58:04 +0300 Subject: [PATCH 47/59] Client mapping fix (#58) * Fix of idle error in clients * Working fix of mapping get --- Drone/client.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/Drone/client.py b/Drone/client.py index f004e2d..d3762e8 100644 --- a/Drone/client.py +++ b/Drone/client.py @@ -198,7 +198,7 @@ class Client(object): # self._last_ping_time = time.time() # logging.debug("tick") - for key, mask in events: # TODO add notifier to client! + for key, mask in events: connection = key.data if connection is None: pass @@ -217,14 +217,16 @@ class Client(object): if error.errno == errno.EINTR: raise KeyboardInterrupt try: - mapping = self.selector.get_map().values() - notifier_key = self.selector.get_key(messaging.NotifierSock().get_sock()) - notify_only= len(mapping) == 1 and notifier_key in mapping - if notify_only or not mapping: + mapping_fds = self.selector.get_map().keys() # file descriptors + notifier_fd = messaging.NotifierSock().get_sock().fileno() + except (KeyError, RuntimeError) as e: + logger.error("Exception {} occurred when getting connections map!".format(e)) + logger.error("Connections changed during getting connections map, passing") + else: + notify_only= len(mapping_fds) == 1 and notifier_fd in mapping_fds + if notify_only or not mapping_fds: logger.warning("No active connections left!") return - except (RuntimeError, KeyError) as e: - logger.error("Exception {} occured when getting net map!".format(e)) @messaging.message_callback("config_write") @@ -250,5 +252,6 @@ def _response_time(*args, **kwargs): if __name__ == "__main__": + logging.basicConfig(level=logging.DEBUG) client = Client() client.start() From 5b145e2d3c0c871485024796a152b9baac3bb1ad Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Fri, 27 Dec 2019 14:38:35 +0000 Subject: [PATCH 48/59] Remove unnecessary install requirements scripts --- install_requirements.bash | 2 -- install_requirements.bat | 2 -- 2 files changed, 4 deletions(-) delete mode 100644 install_requirements.bash delete mode 100644 install_requirements.bat diff --git a/install_requirements.bash b/install_requirements.bash deleted file mode 100644 index ddc36f6..0000000 --- a/install_requirements.bash +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env bash -pip3 install -r requirements.txt \ No newline at end of file diff --git a/install_requirements.bat b/install_requirements.bat deleted file mode 100644 index db13ba8..0000000 --- a/install_requirements.bat +++ /dev/null @@ -1,2 +0,0 @@ -pip3 install -r requirements.txt -pause \ No newline at end of file From c91bb949a95e2f8215d3ea59a9a2c397ace27505 Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Fri, 27 Dec 2019 21:54:11 +0000 Subject: [PATCH 49/59] Client: move landing when pos delta is big to visual_pose_watchdog --- Drone/visual_pose_watchdog.py | 221 ++++++++++++++++++++++++++-------- 1 file changed, 168 insertions(+), 53 deletions(-) diff --git a/Drone/visual_pose_watchdog.py b/Drone/visual_pose_watchdog.py index 663f028..1978071 100644 --- a/Drone/visual_pose_watchdog.py +++ b/Drone/visual_pose_watchdog.py @@ -1,23 +1,27 @@ import rospy import sys import time +import math import logging +import threading import ConfigParser from clever.srv import SetAttitude from sensor_msgs.msg import Range -from mavros_msgs.msg import State +from mavros_msgs.msg import State, PositionTarget from mavros_msgs.srv import SetMode, CommandBool from std_msgs.msg import Bool +from std_srvs.srv import Trigger, TriggerResponse from geometry_msgs.msg import PoseStamped config = ConfigParser.ConfigParser() config.read("client_config.ini") visual_pose_timeout = config.getfloat('VISUAL_POSE_WATCHDOG', 'timeout') +pos_delta_max = config.getfloat('VISUAL_POSE_WATCHDOG', 'pos_delta_max') timeout_action = config.get('VISUAL_POSE_WATCHDOG', 'action') emergency_land_thrust = config.getfloat('VISUAL_POSE_WATCHDOG', 'emergency_land_thrust') emergency_land_decrease_thrust_after = config.getfloat('VISUAL_POSE_WATCHDOG', 'emergency_land_decrease_thrust_after') -timeout_to_disarm_after_watchdog_action = config.getfloat('VISUAL_POSE_WATCHDOG', 'timeout_to_disarm_after_watchdog_action') +timeout_to_disarm = config.getfloat('VISUAL_POSE_WATCHDOG', 'timeout_to_disarm') logging.basicConfig( # TODO all prints as logs level=logging.DEBUG, # INFO @@ -33,7 +37,7 @@ formatter = logging.Formatter("%(asctime)s [%(name)-7.7s] [%(threadName)-12.12s] handler.setFormatter(formatter) logger = logging.getLogger(__name__) -logger.setLevel(logging.INFO) +logger.setLevel(logging.DEBUG) logger.addHandler(handler) set_mode = rospy.ServiceProxy('/mavros/set_mode', SetMode) @@ -46,19 +50,73 @@ mode = '' laser_range = 10 emergency = False +local_pose = None +setpoint_raw = None +setpoint_position = None +setpoint_pose = None + +offboard_start_time = None +offboard_disarmed_timeout = 3. + +emergency_land_called = False + rospy.init_node('visual_pose_watchdog') logger.info('visual_pose_watchdog inited') logger.info('timeout = {} | timeout_action = {}'.format(visual_pose_timeout, timeout_action)) -logger.info('timeout_to_disarm_after_watchdog_action = {}'.format(timeout_to_disarm_after_watchdog_action)) +logger.info('timeout_to_disarm = {}'.format(timeout_to_disarm)) if timeout_action == 'emergency_land': logger.info('emergency_land_thrust: {}'.format(emergency_land_thrust)) rate = rospy.Rate(10) +def get_distance(x1, y1, z1, x2, y2, z2): + return math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2 + (z1 - z2) ** 2) + +def get_pos_delta(PoseStamped1, PoseStamped2): + if PoseStamped1 is None or PoseStamped2 is None: + return float('nan') + pos1 = PoseStamped1.pose.position + pos2 = PoseStamped2.pose.position + return get_distance(pos1.x, pos1.y, pos1.z, pos2.x, pos2.y, pos2.z) + +def get_time_delta(PoseStamped1, PoseStamped2): + if PoseStamped1 is None or PoseStamped2 is None: + return float('nan') + time1 = PoseStamped1.header.stamp.to_sec() + time2 = PoseStamped2.header.stamp.to_sec() + return time1 - time2 + def visual_pose_callback(data): global visual_pose_last_timestamp visual_pose_last_timestamp = data.header.stamp.to_sec() +def local_pose_callback(data): + global local_pose + local_pose = data + +def setpoint_raw_callback(data): + global setpoint_raw, setpoint_position, setpoint_pose + setpoint_raw_pose = PoseStamped() + setpoint_raw_pose.header = data.header + setpoint_raw_pose.pose.position = data.position + setpoint_raw = setpoint_raw_pose + setpoint_pose = get_current_setpoint_pose(setpoint_raw, setpoint_position) + +def setpoint_position_callback(data): + global setpoint_raw, setpoint_position, setpoint_pose + setpoint_position = data + setpoint_pose = get_current_setpoint_pose(setpoint_raw, setpoint_position) + +def get_current_setpoint_pose(_setpoint_raw, _setpoint_position): + if _setpoint_position is None and _setpoint_raw is None: + return None + elif _setpoint_position is not None and _setpoint_raw is None: + return _setpoint_position + elif _setpoint_raw is not None and _setpoint_position is None: + return _setpoint_raw + else: + return _setpoint_raw if _setpoint_raw.header.stamp > _setpoint_position.header.stamp else _setpoint_position + def state_callback(data): global armed, mode armed = data.armed @@ -68,72 +126,130 @@ def laser_callback(data): global laser_range laser_range = data.range +def emergency_land(disarm_if_timeout = True): + global emergency_land_thrust, laser_range + current_thrust = emergency_land_thrust + action_timestamp = time.time() + while armed: + logger.debug("Emergency land | range: {:.2f} | thrust: {:.2f}".format(laser_range, current_thrust)) + if current_thrust >= 0.03: + try: + set_attitude(thrust = current_thrust, yaw = 0, frame_id = 'body', auto_arm = True) + except rospy.ServiceException as e: + logger.info(e) + delta = time.time() - action_timestamp + if delta > timeout_to_disarm and disarm_if_timeout: + try: + arming(False) + except rospy.ServiceException as e: + logger.info(e) + if (laser_range < 0.1 or delta > emergency_land_decrease_thrust_after) and current_thrust >= 0.: + current_thrust -= 0.02 + if current_thrust <= 0.03: + current_thrust = 0 + try: + arming(False) + except rospy.ServiceException as e: + logger.info(e) + rate.sleep() + +def emergency_land_service(request): + global emergency_land_called, armed + responce = TriggerResponse() + if armed: + responce.success = True + responce.message = "Start emergency landing" + emergency_land_called = True + else: + responce.success = False + responce.message = "Copter is disarmed, no need for emergency landing!" + emergency_land_called = False + return responce + def watchdog_callback(event): - global visual_pose_last_timestamp, armed, mode, timeout_action, laser_range, emergency - logger.debug("armed: {} | mode: {} | delta: {} | action: {} | range: {}".format(armed, mode, abs(time.time() - visual_pose_last_timestamp), timeout_action, laser_range)) - if abs(time.time() - visual_pose_last_timestamp) > visual_pose_timeout: + global visual_pose_last_timestamp, armed, mode, timeout_action, laser_range, emergency, local_pose, setpoint_pose, offboard_start_time, emergency_land_called + pos_delta = get_pos_delta(local_pose, setpoint_pose) + pos_dt = get_time_delta(local_pose, setpoint_pose) + logger.debug("armed: {} | mode: {} | viz_dt: {:.2f} | pos_delta: {:.2f} | pos_dt: {:.2f} | action: {} | range: {:.2f}".format( + armed, mode, abs(time.time() - visual_pose_last_timestamp), pos_delta, pos_dt, timeout_action, laser_range)) + if mode == 'OFFBOARD': + if offboard_start_time is None: + offboard_start_time = time.time() if armed: - if timeout_action in ['land', 'emergency_land', 'disarm']: - emergency = True - if timeout_action == 'land': - logger.info('Visual pose data is too old, copter is armed, landing...') - while mode != "AUTO.LAND": - try: - set_mode(custom_mode='AUTO.LAND') - except rospy.ServiceException as e: - logger.info(e) - rate.sleep() - logger.info('Land mode is set') + visual_pose_dt = abs(time.time() - visual_pose_last_timestamp) + if visual_pose_dt > visual_pose_timeout or pos_delta > pos_delta_max: action_timestamp = time.time() - while armed: - if time.time() - action_timestamp > timeout_to_disarm_after_watchdog_action: + if timeout_action in ['land', 'emergency_land', 'disarm']: + emergency = True + if timeout_action == 'land': + logger.info('Visual pose data is too old, copter is armed, landing...') + while mode != "AUTO.LAND": + try: + set_mode(custom_mode='AUTO.LAND') + except rospy.ServiceException as e: + logger.info(e) + if time.time() - action_timestamp > timeout_to_disarm: + break + rate.sleep() + else: + logger.info('Land mode is set') + while armed: + if time.time() - action_timestamp > timeout_to_disarm: + try: + arming(False) + except rospy.ServiceException as e: + logger.info(e) + rate.sleep() + elif timeout_action == 'disarm': + logger.info('Visual pose data is too old, copter is armed, disarming...') + while armed: try: arming(False) except rospy.ServiceException as e: logger.info(e) - rate.sleep() - elif timeout_action == 'disarm': - logger.info('Visual pose data is too old, copter is armed, disarming...') - while armed: - try: - arming(False) - except rospy.ServiceException as e: - logger.info(e) - rate.sleep() - elif timeout_action == 'emergency_land': - logger.info('Visual pose data is too old, copter is armed, emergency landing...') - action_timestamp = time.time() - current_thrust = emergency_land_thrust - while armed: - logger.debug("Emergency land | range: {} | thrust: {}".format(laser_range, current_thrust)) - try: - set_attitude(thrust = current_thrust, yaw = 0, frame_id = 'body') - except rospy.ServiceException as e: - logger.info(e) - delta = time.time() - action_timestamp - if delta > timeout_to_disarm_after_watchdog_action: - try: - arming(False) - except rospy.ServiceException as e: - logger.info(e) - if (laser_range < 0.1 or delta > emergency_land_decrease_thrust_after) and current_thrust > 0.: - current_thrust -= 0.02 - if current_thrust < 0: - current_thrust = 0 - rate.sleep() - logger.info('Disarmed') - emergency = False + rate.sleep() + elif timeout_action == 'emergency_land': + if visual_pose_dt > visual_pose_timeout: + logger.info('Visual pose data is too old, copter is armed, emergency landing...') + if pos_delta > pos_delta_max: + logger.info('Position delta is {} m, copter is armed, emergency landing...'.format(pos_delta)) + emergency_land() + logger.info('Disarmed') + emergency = False + elif emergency_land_called: + emergency = True + logger.info('/emergency_land service was called, start emergency landing...') + emergency_land() + logger.info('Disarmed') + emergency = False + emergency_land_called = False else: + if time.time() - offboard_start_time > offboard_disarmed_timeout: + try: + set_mode(custom_mode='AUTO.LAND') + except rospy.ServiceException as e: + logger.info(e) + else: + offboard_start_time = None + if abs(time.time() - visual_pose_last_timestamp) > visual_pose_timeout: logger.info('Visual pose data is too old') rospy.Subscriber('/mavros/vision_pose/pose', PoseStamped, visual_pose_callback) +rospy.Subscriber('/mavros/local_position/pose', PoseStamped, local_pose_callback) + +rospy.Subscriber('/mavros/setpoint_position/local', PoseStamped, setpoint_position_callback) + +rospy.Subscriber('/mavros/setpoint_raw/local', PositionTarget, setpoint_raw_callback) + rospy.Subscriber('/mavros/state', State, state_callback) rospy.Subscriber('/mavros/distance_sensor/rangefinder', Range, laser_callback) emergency_pub = rospy.Publisher('/emergency', Bool, queue_size=10) +rospy.Service('emergency_land', Trigger, emergency_land_service) + rospy.Timer(rospy.Duration(0.5), watchdog_callback) while not rospy.is_shutdown(): @@ -141,4 +257,3 @@ while not rospy.is_shutdown(): emergency_msg.data = emergency emergency_pub.publish(emergency_msg) rate.sleep() - From f22b362da0b98052d34cf22a75f04a2fd30bcc06 Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Fri, 27 Dec 2019 21:55:13 +0000 Subject: [PATCH 50/59] Client: Add support for emergency land --- Drone/FlightLib/FlightLib.py | 1 + Drone/client.py | 2 -- Drone/copter_client.py | 69 ++++++++++++++++++++++-------------- 3 files changed, 44 insertions(+), 28 deletions(-) diff --git a/Drone/FlightLib/FlightLib.py b/Drone/FlightLib/FlightLib.py index 238d7c0..8c70b09 100644 --- a/Drone/FlightLib/FlightLib.py +++ b/Drone/FlightLib/FlightLib.py @@ -21,6 +21,7 @@ set_mode = rospy.ServiceProxy('/mavros/set_mode', SetMode) get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry) arming = rospy.ServiceProxy('/mavros/cmd/arming', CommandBool) landing = rospy.ServiceProxy('/land', Trigger) +emergency_land = rospy.ServiceProxy('/emergency_land', Trigger) services_list = ['/navigate', '/set_position', '/set_rates', '/mavros/set_mode', '/get_telemetry', '/mavros/cmd/arming', '/land', '/mavros/param/get'] diff --git a/Drone/client.py b/Drone/client.py index d3762e8..6003ef9 100644 --- a/Drone/client.py +++ b/Drone/client.py @@ -60,8 +60,6 @@ class Client(object): self.NTP_HOST = self.config.get('NTP', 'host') self.NTP_PORT = self.config.getint('NTP', 'port') - self.files_directory = self.config.get('FILETRANSFER', 'files_directory') # not used?! - self.client_id = self.config.get('PRIVATE', 'id') if self.client_id == '/default': self.client_id = 'copter' + str(random.randrange(9999)).zfill(4) diff --git a/Drone/copter_client.py b/Drone/copter_client.py index 148215a..7328f4c 100644 --- a/Drone/copter_client.py +++ b/Drone/copter_client.py @@ -76,7 +76,6 @@ class CopterClient(client.Client): self.TELEM_FREQ = self.config.getfloat('TELEMETRY', 'frequency') self.TELEM_TRANSMIT = self.config.getboolean('TELEMETRY', 'transmit') self.LOG_CPU_AND_MEMORY = self.config.getboolean('TELEMETRY', 'log_cpu_and_memory') - self.LAND_POS_DELTA = self.config.getfloat('TELEMETRY', 'land_if_pos_delta_bigger_than') self.FRAME_ID = self.config.get('COPTERS', 'frame_id') self.FRAME_FLIPPED_HEIGHT = 0. self.TAKEOFF_HEIGHT = self.config.getfloat('COPTERS', 'takeoff_height') @@ -112,7 +111,7 @@ class CopterClient(client.Client): except ConfigParser.Error: rospy.logerror("No floor frame!") self.FLOOR_FRAME_EXISTS = False - self.RESTART_DHCPCD = self.config.getboolean('PRIVATE', 'restart_dhcpcd') + self.RESTART_AFTER_RENAME = self.config.getboolean('PRIVATE', 'restart_after_rename') def on_broadcast_bind(self): configure_chrony_ip(self.server_host) @@ -279,7 +278,7 @@ def _response_id(*args, **kwargs): cfg = client.ConfigOption("PRIVATE", "id", new_id) client.active_client.write_config(True, cfg) if new_id != '/hostname': - if client.active_client.RESTART_DHCPCD: + if client.active_client.RESTART_AFTER_RENAME: hostname = client.active_client.client_id configure_hostname(hostname) configure_hosts(hostname) @@ -307,6 +306,7 @@ def _response_selfcheck(*args, **kwargs): @messaging.request_callback("telemetry") def _response_telemetry(*args, **kwargs): + telemetry.update() return telemetry.create_msg_contents() @@ -531,6 +531,11 @@ def _command_land(*args, **kwargs): ) +@messaging.message_callback("emergency_land") +def _emergency_land(*args, **kwargs): + logger.info(FlightLib.emergency_land().message) + + @messaging.message_callback("disarm") def _command_disarm(*args, **kwargs): task_manager.reset() @@ -682,6 +687,7 @@ class Telemetry: self._interruption_counter = 0 self._max_interruptions = 2 self._tasks_cleared = False + self.ros_telemetry = None for key, value in self.params_default_dict.items(): setattr(self, key, value) @@ -722,6 +728,9 @@ class Telemetry: @classmethod def get_battery(cls, ros_telemetry): + if ros_telemetry is None: + return float(nan), float(nan) + battery_v = ros_telemetry.voltage batt_empty_param = get_param('BAT_V_EMPTY') @@ -754,20 +763,15 @@ class Telemetry: return x, y, z, math.degrees(ros_telemetry.yaw), client.active_client.FRAME_ID return 'NO_POS' - def update_telemetry(self): - self.animation_id = animation.get_id() - self.git_version = self.get_git_version() + def update_telemetry_fast(self): self.start_position = self.get_start_position() try: - ros_telemetry = FlightLib.get_telemetry_locked(client.active_client.FRAME_ID) - if ros_telemetry.connected: - self.battery = self.get_battery(ros_telemetry) - self.armed = ros_telemetry.armed - self.calibration_status = get_calibration_status() - self.system_status = get_sys_status() - self.mode = ros_telemetry.mode + self.ros_telemetry = FlightLib.get_telemetry_locked(client.active_client.FRAME_ID) + if self.ros_telemetry.connected: + self.armed = self.ros_telemetry.armed + self.mode = self.ros_telemetry.mode self.selfcheck = self.get_selfcheck() - self.current_position = self.get_position(ros_telemetry) + self.current_position = self.get_position(self.ros_telemetry) else: self.reset_telemetry_values() except rospy.ServiceException: @@ -778,6 +782,18 @@ class Telemetry: except rospy.TransportException as e: rospy.logdebug(e) self.time = time.time() + self.round_telemetry() + + def update_telemetry_slow(self): + self.animation_id = animation.get_id() + self.git_version = self.get_git_version() + self.calibration_status = get_calibration_status() + self.system_status = get_sys_status() + self.battery = self.get_battery(self.ros_telemetry) + + def update(self): + self.update_telemetry_fast() + self.update_telemetry_slow() def round_telemetry(self): round_list = ["battery", "start_position", "current_position"] @@ -793,7 +809,7 @@ class Telemetry: self.selfcheck = ['NO_FCU'] self.current_position = 'NO_POS' - def check_failsafe(self): + def check_failsafe_and_interruption(self): global emergency # check current state state = [self.mode, self.armed, task_manager.get_last_task_name()] @@ -826,12 +842,6 @@ class Telemetry: else: self._tasks_cleared = False self._last_state = state - # check position delta - if not emergency: - delta = FlightLib.get_delta() - if delta > client.active_client.LAND_POS_DELTA: - logger.info("Delta: {}".format(delta)) - _command_land() def transmit_message(self): try: @@ -862,25 +872,32 @@ class Telemetry: rate = rospy.Rate(freq) while not rospy.is_shutdown(): - self.update_telemetry() - self.round_telemetry() - self.check_failsafe() + self.update_telemetry_fast() + self.check_failsafe_and_interruption() if client.active_client.TELEM_TRANSMIT and client.active_client.connected: self.transmit_message() if client.active_client.LOG_CPU_AND_MEMORY: self.log_cpu_and_memory() - + + rate.sleep() + + def _slow_update_loop(self): + rate = rospy.Rate(1) + while not rospy.is_shutdown(): + self.update_telemetry_slow() rate.sleep() def start_loop(self): if client.active_client.TELEM_FREQ > 0: telemetry_thread = threading.Thread(target=self._update_loop, name="Telemetry getting thread", args=(client.active_client.TELEM_FREQ,)) # TODO MOVE? Daemon? + slow_telemetry_thread = threading.Thread(target=self._slow_update_loop, name="Slow telemetry getting thread") + slow_telemetry_thread.start() telemetry_thread.start() else: - logger.info("Don't create telemetry loop because of zero or negative telemetry frequency") + logger.info("Telemetry loop is not created because of zero or negative telemetry frequency") def create_msg_contents(self, keys=None): # keys: set or list if keys is None: From 2337985b969805ab3fe849cafa8dfaeda5713092 Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Fri, 27 Dec 2019 21:55:36 +0000 Subject: [PATCH 51/59] Client: Update client_config --- Drone/client_config.ini | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Drone/client_config.ini b/Drone/client_config.ini index 1710d60..0f4deb2 100644 --- a/Drone/client_config.ini +++ b/Drone/client_config.ini @@ -6,15 +6,15 @@ buffer_size = 10000 [VISUAL_POSE_WATCHDOG] timeout = 1.0 +pos_delta_max = 3.0 action = emergency_land emergency_land_thrust = 0.45 emergency_land_decrease_thrust_after = 5.0 -timeout_to_disarm_after_watchdog_action = 10.0 +timeout_to_disarm = 10.0 [TELEMETRY] frequency = 1 transmit = True -land_if_pos_delta_bigger_than = 3.0 log_cpu_and_memory = True [COPTERS] @@ -49,17 +49,13 @@ z_ratio = 1.0 [PRIVATE] id = /hostname -restart_dhcpcd = True +restart_after_rename = True use_leds = True led_pin = 21 x0 = 0 y0 = 0 z0 = 0 -[FILETRANSFER] -files_directory = animation -animation_file = animation.csv - [NTP] use_ntp = False host = ntp1.stratum2.ru From d70e4f011991e2457f67f177b8c870362e8c253c Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Sun, 29 Dec 2019 15:59:19 +0300 Subject: [PATCH 52/59] Update and clear .gitignore --- .gitignore | 121 +++++++---------------------------------------------- 1 file changed, 14 insertions(+), 107 deletions(-) diff --git a/.gitignore b/.gitignore index 8761dcb..436c09f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,123 +1,30 @@ # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] *$py.class -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: +# Logs *.log -local_settings.py -db.sqlite3 -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy +# IDE .mypy_cache/ .vscode/settings.json +.vscode/ +\.idea/ + +# Development +images/ Server/tests.py Server/convert_ui.sh +Server/server_logs +Server/testj\.ipynb +Server/tst_client\.py +Server/tst\.py Drone/test_animation/ Drone/animation.csv Drone/client_logs -images/ -.vscode/ -\.idea/ -builder/clever-config Drone/_copter_client_old_\.py - Drone/test_cl\.py - -Server/testj\.ipynb - -Server/tst_client\.py - -Server/tst\.py From ec9e0cba30bea8e51730929f5900524c8b24ff89 Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Sun, 29 Dec 2019 15:59:52 +0300 Subject: [PATCH 53/59] builder: Add default clever-config folder --- .../camera_info/calibration.yaml | 45 ++++++++ builder/clever-config/launch/aruco.launch | 41 +++++++ builder/clever-config/launch/clever.launch | 73 +++++++++++++ .../clever-config/launch/main_camera.launch | 38 +++++++ builder/clever-config/map/animation_map.txt | 100 ++++++++++++++++++ 5 files changed, 297 insertions(+) create mode 100644 builder/clever-config/camera_info/calibration.yaml create mode 100644 builder/clever-config/launch/aruco.launch create mode 100644 builder/clever-config/launch/clever.launch create mode 100644 builder/clever-config/launch/main_camera.launch create mode 100644 builder/clever-config/map/animation_map.txt diff --git a/builder/clever-config/camera_info/calibration.yaml b/builder/clever-config/camera_info/calibration.yaml new file mode 100644 index 0000000..76fcd8c --- /dev/null +++ b/builder/clever-config/camera_info/calibration.yaml @@ -0,0 +1,45 @@ +image_width: 320 +image_height: 240 +distortion_model: plumb_bob +camera_name: raspicam +camera_matrix: + rows: 3 + cols: 3 + data: + - 166.23942373073172 + - 0. + - 162.19011246829268 + - 0. + - 166.5880923974026 + - 109.82227735714285 + - 0. + - 0. + - 1. +distortion_coefficients: + rows: 1 + cols: 8 + data: [ 2.15356885e-01, -1.17472846e-01, -3.06197672e-04, + -1.09444025e-04, -4.53657258e-03, 5.73090623e-01, + -1.27574577e-01, -2.86125589e-02, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 0.00000000e+00] +rectification_matrix: + rows: 3 + cols: 3 + data: [1, 0, 0, 0, 1, 0, 0, 0, 1] +projection_matrix: + rows: 3 + cols: 4 + data: + - 166.23942373073172 + - 0. + - 162.19011246829268 + - 0. + - 0. + - 166.5880923974026 + - 109.82227735714285 + - 0. + - 0. + - 0. + - 1. + - 0. diff --git a/builder/clever-config/launch/aruco.launch b/builder/clever-config/launch/aruco.launch new file mode 100644 index 0000000..d1c6e05 --- /dev/null +++ b/builder/clever-config/launch/aruco.launch @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/builder/clever-config/launch/clever.launch b/builder/clever-config/launch/clever.launch new file mode 100644 index 0000000..97ad8de --- /dev/null +++ b/builder/clever-config/launch/clever.launch @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/builder/clever-config/launch/main_camera.launch b/builder/clever-config/launch/main_camera.launch new file mode 100644 index 0000000..76ab4bd --- /dev/null +++ b/builder/clever-config/launch/main_camera.launch @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/builder/clever-config/map/animation_map.txt b/builder/clever-config/map/animation_map.txt new file mode 100644 index 0000000..dfe273d --- /dev/null +++ b/builder/clever-config/map/animation_map.txt @@ -0,0 +1,100 @@ +0 0.33 0.0 9.0 0 0 0 0 +1 0.33 1.0 9.0 0 0 0 0 +2 0.33 2.0 9.0 0 0 0 0 +3 0.33 3.0 9.0 0 0 0 0 +4 0.33 4.0 9.0 0 0 0 0 +5 0.33 5.0 9.0 0 0 0 0 +6 0.33 6.0 9.0 0 0 0 0 +7 0.33 7.0 9.0 0 0 0 0 +8 0.33 8.0 9.0 0 0 0 0 +9 0.33 9.0 9.0 0 0 0 0 +10 0.33 0.0 8.0 0 0 0 0 +11 0.33 1.0 8.0 0 0 0 0 +12 0.33 2.0 8.0 0 0 0 0 +13 0.33 3.0 8.0 0 0 0 0 +14 0.33 4.0 8.0 0 0 0 0 +15 0.33 5.0 8.0 0 0 0 0 +16 0.33 6.0 8.0 0 0 0 0 +#17 0.33 7.0 8.0 0 0 0 0 +18 0.33 8.0 8.0 0 0 0 0 +19 0.33 9.0 8.0 0 0 0 0 +20 0.33 0.0 7.0 0 0 0 0 +21 0.33 1.0 7.0 0 0 0 0 +22 0.33 2.0 7.0 0 0 0 0 +23 0.33 3.0 7.0 0 0 0 0 +24 0.33 4.0 7.0 0 0 0 0 +25 0.33 5.0 7.0 0 0 0 0 +26 0.33 6.0 7.0 0 0 0 0 +27 0.33 7.0 7.0 0 0 0 0 +28 0.33 8.0 7.0 0 0 0 0 +29 0.33 9.0 7.0 0 0 0 0 +30 0.33 0.0 6.0 0 0 0 0 +31 0.33 1.0 6.0 0 0 0 0 +32 0.33 2.0 6.0 0 0 0 0 +33 0.33 3.0 6.0 0 0 0 0 +34 0.33 4.0 6.0 0 0 0 0 +35 0.33 5.0 6.0 0 0 0 0 +36 0.33 6.0 6.0 0 0 0 0 +37 0.33 7.0 6.0 0 0 0 0 +38 0.33 8.0 6.0 0 0 0 0 +39 0.33 9.0 6.0 0 0 0 0 +40 0.33 0.0 5.0 0 0 0 0 +41 0.33 1.0 5.0 0 0 0 0 +42 0.33 2.0 5.0 0 0 0 0 +43 0.33 3.0 5.0 0 0 0 0 +44 0.33 4.0 5.0 0 0 0 0 +45 0.33 5.0 5.0 0 0 0 0 +46 0.33 6.0 5.0 0 0 0 0 +47 0.33 7.0 5.0 0 0 0 0 +48 0.33 8.0 5.0 0 0 0 0 +49 0.33 9.0 5.0 0 0 0 0 +50 0.33 0.0 4.0 0 0 0 0 +51 0.33 1.0 4.0 0 0 0 0 +52 0.33 2.0 4.0 0 0 0 0 +53 0.33 3.0 4.0 0 0 0 0 +54 0.33 4.0 4.0 0 0 0 0 +55 0.33 5.0 4.0 0 0 0 0 +56 0.33 6.0 4.0 0 0 0 0 +57 0.33 7.0 4.0 0 0 0 0 +58 0.33 8.0 4.0 0 0 0 0 +59 0.33 9.0 4.0 0 0 0 0 +60 0.33 0.0 3.0 0 0 0 0 +61 0.33 1.0 3.0 0 0 0 0 +62 0.33 2.0 3.0 0 0 0 0 +63 0.33 3.0 3.0 0 0 0 0 +64 0.33 4.0 3.0 0 0 0 0 +65 0.33 5.0 3.0 0 0 0 0 +66 0.33 6.0 3.0 0 0 0 0 +67 0.33 7.0 3.0 0 0 0 0 +68 0.33 8.0 3.0 0 0 0 0 +69 0.33 9.0 3.0 0 0 0 0 +70 0.33 0.0 2.0 0 0 0 0 +71 0.33 1.0 2.0 0 0 0 0 +72 0.33 2.0 2.0 0 0 0 0 +73 0.33 3.0 2.0 0 0 0 0 +74 0.33 4.0 2.0 0 0 0 0 +75 0.33 5.0 2.0 0 0 0 0 +76 0.33 6.0 2.0 0 0 0 0 +77 0.33 7.0 2.0 0 0 0 0 +78 0.33 8.0 2.0 0 0 0 0 +79 0.33 9.0 2.0 0 0 0 0 +80 0.33 0.0 1.0 0 0 0 0 +81 0.33 1.0 1.0 0 0 0 0 +82 0.33 2.0 1.0 0 0 0 0 +83 0.33 3.0 1.0 0 0 0 0 +84 0.33 4.0 1.0 0 0 0 0 +85 0.33 5.0 1.0 0 0 0 0 +86 0.33 6.0 1.0 0 0 0 0 +87 0.33 7.0 1.0 0 0 0 0 +88 0.33 8.0 1.0 0 0 0 0 +89 0.33 9.0 1.0 0 0 0 0 +90 0.33 0.0 0.0 0 0 0 0 +91 0.33 1.0 0.0 0 0 0 0 +92 0.33 2.0 0.0 0 0 0 0 +93 0.33 3.0 0.0 0 0 0 0 +94 0.33 4.0 0.0 0 0 0 0 +95 0.33 5.0 0.0 0 0 0 0 +96 0.33 6.0 0.0 0 0 0 0 +97 0.33 7.0 0.0 0 0 0 0 +98 0.33 8.0 0.0 0 0 0 0 +99 0.33 9.0 0.0 0 0 0 0 From ccb3039dd3c9f27c08cf14b08f87779b18dc0e67 Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Sun, 29 Dec 2019 13:48:44 +0000 Subject: [PATCH 54/59] Client: Fix bugs --- Drone/copter_client.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Drone/copter_client.py b/Drone/copter_client.py index 7328f4c..0e20082 100644 --- a/Drone/copter_client.py +++ b/Drone/copter_client.py @@ -729,7 +729,7 @@ class Telemetry: @classmethod def get_battery(cls, ros_telemetry): if ros_telemetry is None: - return float(nan), float(nan) + return float('nan'), float('nan') battery_v = ros_telemetry.voltage @@ -787,8 +787,16 @@ class Telemetry: def update_telemetry_slow(self): self.animation_id = animation.get_id() self.git_version = self.get_git_version() - self.calibration_status = get_calibration_status() - self.system_status = get_sys_status() + try: + self.calibration_status = get_calibration_status() + self.system_status = get_sys_status() + except rospy.ServiceException: + rospy.logdebug("Some service is unavailable") + self.selfcheck = ["WAIT_ROS"] + except AttributeError as e: + rospy.logdebug(e) + except rospy.TransportException as e: + rospy.logdebug(e) self.battery = self.get_battery(self.ros_telemetry) def update(self): From 80f15a3b067fe2915a7e722f3e4c4476436177a5 Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Sun, 29 Dec 2019 20:14:48 +0300 Subject: [PATCH 55/59] Client: Update client_config according to article about client --- Drone/client_config.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Drone/client_config.ini b/Drone/client_config.ini index 0f4deb2..6fe44a4 100644 --- a/Drone/client_config.ini +++ b/Drone/client_config.ini @@ -13,8 +13,8 @@ emergency_land_decrease_thrust_after = 5.0 timeout_to_disarm = 10.0 [TELEMETRY] -frequency = 1 transmit = True +frequency = 1 log_cpu_and_memory = True [COPTERS] From f36d6ed81ba39cca0e5f27da1b0aeba4d58cd5bb Mon Sep 17 00:00:00 2001 From: Arthur Golubtsov Date: Sun, 29 Dec 2019 20:15:09 +0300 Subject: [PATCH 56/59] docs: Update article about client --- docs/ru/client.md | 49 +++++++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/docs/ru/client.md b/docs/ru/client.md index f407971..2e166f9 100644 --- a/docs/ru/client.md +++ b/docs/ru/client.md @@ -1,6 +1,6 @@ -# Клиент +# Клиент clever-show -Приложение для удаленного синхронизированного управления дронами в шоу. +Приложение для удаленного синхронизированного управления дронами в шоу и модуль экстренной защиты дронов. * [Установка и запуск](start-tutorial.md#установка-и-запуск-клиента) * [Настройка клиента](#настройка-клиента) @@ -16,19 +16,19 @@ port = 25000 broadcast_port = 8181 host = 192.168.1.101 -buffer_size = 1024 +buffer_size = 10000 [VISUAL_POSE_WATCHDOG] timeout = 1.0 +pos_delta_max = 3.0 action = emergency_land emergency_land_thrust = 0.45 emergency_land_decrease_thrust_after = 5.0 -timeout_to_disarm_after_watchdog_action = 10.0 +timeout_to_disarm = 10.0 [TELEMETRY] -frequency = 1 transmit = True -land_if_pos_delta_bigger_than = 3.0 +frequency = 1 log_cpu_and_memory = True [COPTERS] @@ -63,17 +63,13 @@ z_ratio = 1.0 [PRIVATE] id = /hostname -restart_dhcpcd = True +restart_after_rename = True use_leds = True led_pin = 21 x0 = 0 y0 = 0 z0 = 0 -[FILETRANSFER] -files_directory = animation -animation_file = animation.csv - [NTP] use_ntp = False host = ntp1.stratum2.ru @@ -99,24 +95,26 @@ port = 123 В данном разделе настраивается программа экстренной защиты коптера от потери позиции или столкновения с объектом. * `timeout` - время срабатывания экстренной защиты после потери визуальной позиции, в секундах. -* `action` - действие при срабатывании экстренной защиты. Доступные варианты: `land` - посадка коптера в режиме полётного контроллера AUTO.LAND, `emergency_land` - посадка коптера с постепенным уменьшением мощности моторов, `disarm`- выключение моторов. -* `emergency_land_thrust` - начальная мощность, подаваемая на моторы в случае выбора действия `emergency_land` при срабатывании экстренной защиты, в процентах. -* `emergency_land_decrease_thrust_after` - время, через которое мощность на моторах плавно начинает уменьшаться в случае выбора действия `emergency_land` при срабатывании экстренной защиты, в процентах. -* `timeout_to_disarm_after_watchdog_action` - время, через которое коптер безусловно выключает моторы после срабатывания экстренной защиты, в секундах. +* `pos_delta_max` - максимальная разница между текущим положением и точкой, в которой сейчас должен находиться коптер, в метрах. Требуется для проверки на столкновение коптера с объектом. Если расстояние между текущим положением коптера и положением, в котором он должен сейчас находиться, больше этого числа (в метрах), срабатывает экстренная защита. +* `action` - действие при срабатывании экстренной защиты. Доступные варианты: `land` - посадка коптера в режиме полётного контроллера AUTO.LAND, `emergency_land` - посадка коптера с постепенным уменьшением мощности моторов, `disarm`- выключение моторов. **Внимание!** Не рекомендуется использовать режим AUTO.LAND с выключенным барометром - при потере источника высоты в полёте, например показаний лазера или визуальной позиции, режим AUTO.LAND не гарантирует посадку коптера, так как ориентируется на показания высоты. Для посадки коптера при его позиционировании с использованием визуальной позиции или лазера и возможности потери данных с этих систем рекомендуется использовать режим `emergency_land`. +* `emergency_land_thrust` - начальная мощность, подаваемая на моторы в случае выбора действия `emergency_land` при срабатывании экстренной защиты. Безразмерная величина, от 0 (отсутствие мошности) до 1 (полная мощность). Для гарантированной посадки рекомендуется устанавливать в значение, меньшее по величине на 5-10 процентов, чем газ висения (параметр `MPC_THR_HOVER` в px4). +* `emergency_land_decrease_thrust_after` - время, через которое мощность на моторах плавно начинает уменьшаться в случае выбора действия `emergency_land` при срабатывании экстренной защиты, в секундах. +* `timeout_to_disarm` - время, через которое коптер безусловно выключает моторы после срабатывания экстренной защиты, в секундах. #### Раздел TELEMETRY В данном разделе настраивается поток передачи телеметрии на сервер. -* `frequency` - частота передачи данных на сервер, целочисленное значение, количество раз в секунду. * `transmit` - логическое значение, определяет, нужно ли передавать данные на сервер. -* `land_if_pos_delta_bigger_than` - проверка на столкновение коптера с объектом. Работает в цикле телеметрии. Если расстояние между текущим положением коптера и положением, в котором он должен сейчас находиться, больше этого числа (в метрах), коптер очищает очередь задач и переходит в режим AUTO.LAND. -* `log_cpu_and_memory` - логическое значение, которое определяет, будет ли записываться в лог сервиса клиента clever-show состояние процессора и памяти. +* `frequency` - частота передачи данных на сервер, целочисленное значение, количество раз в секунду. +* `log_cpu_and_memory` - логическое значение, определяет, будет ли записываться в лог сервиса клиента clever-show состояние процессора и памяти. #### Раздел COPTERS -* `frame_id` - название системы координат, в которой настроен коптер на удержание позиции. Если значение `floor` - клиент публикует статическую систему координат с названием `floor` и настройками из раздела [FLOOR_FRAME](#раздел-floor_frame). -* `takeoff_height` - высота взлёта коптера, в метрах. Используется в начале анимации или при тестировании коптера с сервера. +В данном разделе находятся настройки, влияющие на процесс полёта коптера. + +* `frame_id` - название системы координат, относительно которой будут публиковаться координаты точек для воспроизведения анимации. Если значение `floor` - клиент публикует статическую систему координат с названием `floor` и настройками из раздела [FLOOR_FRAME](#раздел-floor_frame). **Внимание!** Убедитесь, что коптер удерживает позицию в данной системе координат. Для этого вы можете воспользоваться командой [Takeoff](server.md#тестовые-команды) из серверного приложения. Коптер взлетит на высоту `takeoff_height` относительно текущей. +* `takeoff_height` - высота взлёта коптера, в метрах. Используется в начале воспроизведения анимации или при тестировании коптера с сервера. * `takeoff_time` - максимальное время взлёта коптера, в секундах. * `safe_takeoff` - логическое значение, определяет, нужно ли производить посадку в безопасном режиме. * `reach_first_point_time` - максимальное время полёта к первой точке анимации, в секундах. @@ -129,6 +127,8 @@ port = 123 #### Раздел FLOOR_FRAME +Данный раздел описывает смещение системы координат с названием `floor` и используется только при указании параметра `frame_id` как `floor` в разделе [COPTERS](#раздел-copters). + * `parent` - название опорной системы координат, относительно которой будет располагаться система координат `floor`. * `x` - смещение системы координат `floor` по оси x относительно системы координат `parent`, в метрах. * `y` - смещение системы координат `floor` по оси y относительно системы координат `parent`, в метрах. @@ -152,6 +152,8 @@ port = 123 #### Раздел PRIVATE +В данном разделе находятся параметры, специфичные для конкретного коптера. + * `id` - имя коптера, отображаемое в таблице. Если значение `/hostname` - имя определяется из файла `/etc/hostname`. * `restart_dhcpcd` - логический параметр, определяет, требуется ли перезагрузка коптера при переименовании его `id` удалённо с сервера. * `use_leds` - логический параметр, определяет, использует ли коптер светодиодную ленту. @@ -160,13 +162,6 @@ port = 123 * `y0` - смещение по оси y, только для данного коптера. * `z0` - смещение по оси z, только для данного коптера. -#### Раздел FILETRANSFER - -В этом разделе задаются параметры передачи файлов. - -* `files_directory` - директория для хранения файлов анимации (сейчас не используется). -* `animation_file` - название файла анимации, сохраняемое в клиенте. - #### Раздел NTP Помимо синхронизации времени (с миллисекундной точностью) с помощью пакета chrony, предоставляется альтернатива - возможность использования внешних (при наличии соединения локальной сети с интернетом) или внутрисетевых NTP-серверов. **Внимание!** Для корректной работы системы, **и сервер, и клиенты** должны использовать единый способ синхронизации времени (набор параметров в этом разделе). Данный раздел полностью унифицирован и для сервера, и для клиентов. From 7e2066d0dd7d04c3f5bd7861c9168b260161ce3c Mon Sep 17 00:00:00 2001 From: Artem30801 Date: Mon, 30 Dec 2019 15:15:47 +0300 Subject: [PATCH 57/59] Update server-gui.png --- docs/assets/server-gui.png | Bin 148646 -> 132844 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/assets/server-gui.png b/docs/assets/server-gui.png index 4b0bf2077c6ce31fb75ecb10bac09f833f04fe00..fa75efaac7b00603fd1d37862a4eace91348cede 100644 GIT binary patch literal 132844 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%0A10F} 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%0AfK@{tx|0$MCSauTUP#J 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 From 4af17cbcd83d0ff5ae7d3d9834e3bd9ea31bc88b Mon Sep 17 00:00:00 2001 From: artem30801 <38689676+artem30801@users.noreply.github.com> Date: Mon, 30 Dec 2019 16:50:32 +0300 Subject: [PATCH 58/59] Update server.md --- docs/ru/server.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/docs/ru/server.md b/docs/ru/server.md index 72a54b7..187358c 100644 --- a/docs/ru/server.md +++ b/docs/ru/server.md @@ -105,8 +105,15 @@ * Кнопка `Pause/Resume` - ставит на паузу и возобновляет выполнение полётных задач. После каждого нажатия кнопка меняет состояние на обратное. * Состояние`Pause` - ставит на паузу очередь заданий всех выбранных коптеров: приостанавливается выполнение любого полётного задания. Рекомендуется использовать в чрезвычайных ситуациях для определения неисправного коптера. **Внимание!** Данная команда НЕ прерывает полёт коптера в уже указанную точку (например: элементы взлёта, посадки; следование до начальной точки анимации и т.д.) * Состояние `Resume` - все выбранные коптеры *синхронизированно* продолжат выполнение своих очередей заданий (например исполнение анимации) -* Кнопка `Stop and land all` - прерывает выполнение полётных заданий *ВСЕХ* подключенных коптеров. Сбрасывает очередь заданий - *действие необратимо*. Выполняет полную остановку и немедленную посадку коптеров. **Используйте в экстренных случаях как одно из средств перехвата.** -* Кнопка `Emergency land` - открывает диалоговое окно модуля визуальной посадки неисправного коптера. Полное описание находится в [конце статьи](#emergency-land). +#### Средства перехвата в экстренных ситуациях + +* Кнопка `Land selected` - все выбранные коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно переходят в режим посадки. **Используйте в экстренных случаях как одно из средств перехвата.** +* Кнопка `Land ALL` - ВСЕ коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно переходят в режим посадки. **Используйте в экстренных случаях как одно из средств перехвата.** + + +* Кнопка `Emergency land` - прерывает выполнение полётных заданий *ВСЕХ* подключенных коптеров. Сбрасывает очередь заданий - *действие необратимо*. Выполняет полную остановку и немедленную посадку коптеров. **Используйте в экстренных случаях как одно из средств перехвата.** +* Кнопка `Visual land` - открывает диалоговое окно модуля визуальной посадки неисправного коптера. Полное описание находится в [конце статьи](#visual-land). + * Кнопка `Disarm selected` - все выбранные коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно отключают моторы. Это может привести к падению и повреждению коптеров. * Кнопка `Disarm ALL` - ВСЕ коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно отключают моторы. Это может привести к падению и повреждению коптеров **Используйте в крайних случаях как последнее из средств перехвата.** @@ -119,7 +126,6 @@ * Чекбокс `Z` - если чекбокс активен, коптер взлетит в указанное значение по `z`. Иначе коптерами будут использоваться значения по умолчанию, указанные в их конфигурациях, а взлёт будет производиться относительно текущей высоты. * Спинбокс `Z` - задаёт значение координаты `z` взлёта коптеров в метрах. * Кнопка `Flip` - все выбранные коптеры **совершают флип (flip)** - переворот на 360 градусов вокруг одной из *горизонтальных* осей. **Внимание!** Используйте осторожно, соблюдайте технику безопасности. **Внимание!** Для исполнения флипа коптер должен иметь минимальную высоту больше 2м. **Внимание!** Не применяйте во время выполнения других полётных функций! -* Кнопка `Land` - все выбранные коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно переходят в режим посадки. **Используйте в экстренных случаях как одно из средств перехвата.** #### Системные команды @@ -193,15 +199,15 @@ port = 123 ## Дополнительные операции -### Emergency land +### Visual land Модуль визуальной экстренной посадки, предназначенный для быстрого поиска оператором визуально неисправного коптера методом бинарного поиска. Для успешного применения на всех коптерах должна быть установлена светодиодная лента. #### Интерфейс -![LED Emergency Land](../assets/server-led-emergency-land.png) +![LED Visual Land](../assets/server-led-emergency-land.png) -При нажатии на кнопку `Emergency land` все коптеры делятся на 2 равные группы по порядку расположения в таблице. Первая половина коптеров зажигает светодиодную ленту зелёным цветом, вторая - красным. При нажатии на зелёную или красную кнопку происходит выбор группы, соответствующей цвету нажатой кнопки. Коптеры выбранного цвета снова делятся на две половины и каждая половина зажигает светодиодную ленту зелёным и красным цветом соответственно. Остальные коптеры выключают светодиодную ленту. +При нажатии на кнопку `Visual land` все коптеры делятся на 2 равные группы по порядку расположения в таблице. Первая половина коптеров зажигает светодиодную ленту зелёным цветом, вторая - красным. При нажатии на зелёную или красную кнопку происходит выбор группы, соответствующей цвету нажатой кнопки. Коптеры выбранного цвета снова делятся на две половины и каждая половина зажигает светодиодную ленту зелёным и красным цветом соответственно. Остальные коптеры выключают светодиодную ленту. Нажимая на кнопки, соответствующие цвету группы, в которой находится неисправный коптер, можно определить его номер и выполнить экстренную посадку за логорифмическое количество шагов от количества коптеров, т.е. гораздо быстрее, чем перебирая коптеры по одному. From 95e35603922ff4119e50c705d943cecf21a7e08a Mon Sep 17 00:00:00 2001 From: Artem30801 Date: Mon, 30 Dec 2019 16:54:41 +0300 Subject: [PATCH 59/59] Update server-sidemenu.png --- docs/assets/server-sidemenu.png | Bin 10048 -> 101621 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/assets/server-sidemenu.png b/docs/assets/server-sidemenu.png index ad0a1e61a2c62f93ac01465fddf8e86ea5d9130b..c42d1fcf28407b3c63ee3f050e393d87ec31bac6 100644 GIT binary patch literal 101621 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 z