コード例 #1
0
 def test_check_parallel_drone_commands_nested_should_update_states_correctly(
         self):
     drone_commands = \
         [
             ParallelDroneCommands([
                 [SingleDroneCommand("DRONE1", Command.wait(5))],
                 [SingleDroneCommand("DRONE2", Command.wait(3)),
                  ParallelDroneCommands([
                      [SingleDroneCommand("DRONE3", Command.wait(4))],
                      [SingleDroneCommand("DRONE4", Command.wait(1))]
                  ])]
             ]),
             SingleDroneCommand("DRONE1", Command.wait(1))
         ]
     drones_involved = {"DRONE1", "DRONE2", "DRONE3", "DRONE4", "DRONE5"}
     state_updaters = {
         name: StateUpdater(DroneConfig((1, 0, 0), 2, 180, 2))
         for name in drones_involved
     }
     drone_state_map = {name: State() for name in drones_involved}
     BoundaryChecker(
         self.boundary_config)._update_states_and_check_drone_commands(
             drone_commands, state_updaters, drone_state_map,
             drones_involved)
     expected = {
         name: State(time_used_seconds=8)
         for name in drones_involved
     }
     self.assertEqual(expected, drone_state_map)
コード例 #2
0
 def _update_states_to_wait_for_slowest_branch(
         self, parallel_drone_commands: ParallelDroneCommands,
         state_updaters: Dict[str, StateUpdater],
         drone_trajectory_map: Dict[str, List[State]],
         time_used_in_branches: List[float],
         drones_involved: Set[str]) -> float:
     longest_time_used = max(time_used_in_branches)
     # for each branch, let drones involved in the branch wait until longest_time_used
     for i, time_used in enumerate(time_used_in_branches):
         for name in parallel_drone_commands.drones_involved_each_branch[i]:
             wait_command = Command.wait(longest_time_used - time_used)
             self._update_states_for_single_drone_command(
                 SingleDroneCommand(name, wait_command),
                 state_updaters,
                 drone_trajectory_map,
                 drones_involved={name})
     # let drones not involved in any branch wait for longest_time_used
     for name in drones_involved.difference(
             parallel_drone_commands.get_drones_involved()):
         wait_command = Command.wait(longest_time_used)
         self._update_states_for_single_drone_command(
             SingleDroneCommand(name, wait_command),
             state_updaters,
             drone_trajectory_map,
             drones_involved={name})
     return longest_time_used
コード例 #3
0
 def test_eq(self):
     commands1 = [
         None,
         Command.takeoff(),
         Command.land(),
         Command.up(1),
         Command.down(1),
         Command.left(1),
         Command.right(1),
         Command.forward(1),
         Command.backward(1),
         Command.rotate_left(1),
         Command.rotate_right(1),
         Command.wait(1),
         Command.up(2),
         Command.down(2),
         Command.left(2),
         Command.right(2),
         Command.forward(2),
         Command.backward(2),
         Command.rotate_left(2),
         Command.rotate_right(2),
         Command.wait(2)
     ]
     commands2 = [
         None,
         Command.takeoff(),
         Command.land(),
         Command.up(1),
         Command.down(1),
         Command.left(1),
         Command.right(1),
         Command.forward(1),
         Command.backward(1),
         Command.rotate_left(1),
         Command.rotate_right(1),
         Command.wait(1),
         Command.up(2),
         Command.down(2),
         Command.left(2),
         Command.right(2),
         Command.forward(2),
         Command.backward(2),
         Command.rotate_left(2),
         Command.rotate_right(2),
         Command.wait(2)
     ]
     for i in range(len(commands1)):
         for j in range(len(commands2)):
             if i == j:
                 self.assertEqual(commands1[i], commands2[j])
             else:
                 self.assertNotEqual(commands1[i], commands2[j])
コード例 #4
0
 def test_multiple_commands_should_return_correct_command(self):
     actual = generate_commands("""
         main() {
          takeoff();
          up(1);
          down(2);
          left(3);
          right(4);
          forward(5);
          backward(6);
          rotate_left(7);
          rotate_right(8);
          wait(9);
          land(); 
         }
     """)
     expected = [
         SingleDroneCommand("DEFAULT", Command.takeoff()),
         SingleDroneCommand("DEFAULT", Command.up(1)),
         SingleDroneCommand("DEFAULT", Command.down(2)),
         SingleDroneCommand("DEFAULT", Command.left(3)),
         SingleDroneCommand("DEFAULT", Command.right(4)),
         SingleDroneCommand("DEFAULT", Command.forward(5)),
         SingleDroneCommand("DEFAULT", Command.backward(6)),
         SingleDroneCommand("DEFAULT", Command.rotate_left(7)),
         SingleDroneCommand("DEFAULT", Command.rotate_right(8)),
         SingleDroneCommand("DEFAULT", Command.wait(9)),
         SingleDroneCommand("DEFAULT", Command.land())
     ]
     self.assertEqual(expected, actual)
コード例 #5
0
 def test_wait_with_drone_name_should_return_correct_command(self):
     actual = generate_commands(
         """
         main() { DRONE1.wait(1); }
     """,
         drone_config_map={"DRONE1": DefaultDroneConfig()})
     expected = [SingleDroneCommand("DRONE1", Command.wait(1))]
     self.assertEqual(expected, actual)
コード例 #6
0
 def test_check_single_drone_command_wait_when_taken_off_should_not_give_error(
         self):
     drone_commands = [
         SingleDroneCommand("DRONE1", Command.takeoff()),
         SingleDroneCommand("DRONE1", Command.wait(1)),
         SingleDroneCommand("DRONE1", Command.land())
     ]
     BoundaryChecker(self.boundary_config).check(drone_commands,
                                                 self.state_updater_map)
コード例 #7
0
 def visitWait(self, ctx: xDroneParser.WaitContext) -> None:
     exprs = [self.visit(expr) for expr in ctx.expr()]
     if ctx.DOT():
         drone_expr, expr = exprs
     else:
         drone_expr, expr = None, exprs[0]
     if expr.type != Type.int() and expr.type != Type.decimal():
         raise CompileError(
             "Expression {} should have type int or decimal, but is {}".
             format(expr, expr.type))
     drone_name = self._get_drone_name(drone_expr)
     self._get_latest_commands().append(
         SingleDroneCommand(drone_name, Command.wait(expr.value)))
コード例 #8
0
 def test_update_wait_should_update_state(self):
     state = State(has_taken_off=True,
                   x_meters=1,
                   y_meters=2,
                   z_meters=3,
                   orientation_degrees=60)
     actual = self.state_updater.update(Command.wait(5), state)
     expected = State(has_taken_off=True,
                      time_used_seconds=5,
                      x_meters=1,
                      y_meters=2,
                      z_meters=3,
                      orientation_degrees=60)
     self.assertEqual(expected, actual)
コード例 #9
0
    def test_check_single_drone_command_should_detect_collision_and_give_error(self):
        drone_commands = [SingleDroneCommand("DRONE1", Command.takeoff()),
                          SingleDroneCommand("DRONE2", Command.takeoff()),
                          SingleDroneCommand("DRONE1", Command.right(1)),
                          SingleDroneCommand("DRONE2", Command.wait(1)),
                          SingleDroneCommand("DRONE2", Command.rotate_left(90)),
                          SingleDroneCommand("DRONE2", Command.forward(1)),
                          SingleDroneCommand("DRONE1", Command.land()),
                          SingleDroneCommand("DRONE2", Command.land())]

        with self.assertRaises(SafetyCheckError) as context:
            self.collision_checker.check(drone_commands, self.state_updater_map)
        self.assertTrue("Collisions might happen!\nCollision might happen between DRONE1 and DRONE2"
                        in str(context.exception))
コード例 #10
0
 def test_check_parallel_drone_commands_should_let_drone_not_involved_wait(
         self):
     drone_commands = [
         ParallelDroneCommands(
             [[], [SingleDroneCommand("DRONE2", Command.wait(5))]])
     ]
     drone_state_map = {"DRONE1": State(), "DRONE2": State(x_meters=1)}
     drones_involved = {"DRONE1", "DRONE2"}
     BoundaryChecker(
         self.boundary_config)._update_states_and_check_drone_commands(
             drone_commands, self.state_updater_map, drone_state_map,
             drones_involved)
     expected = {
         "DRONE1": State(time_used_seconds=5),
         "DRONE2": State(time_used_seconds=5, x_meters=1)
     }
     self.assertEqual(expected, drone_state_map)
コード例 #11
0
 def test_execute_single_drone_command(self):
     single_commands = [
         SingleDroneCommand("name1", Command.takeoff()),
         SingleDroneCommand("name1", Command.land()),
         SingleDroneCommand("name2", Command.up(1.1)),
         SingleDroneCommand("name2", Command.down(1.2)),
         SingleDroneCommand("name2", Command.left(1.3)),
         SingleDroneCommand("name2", Command.right(1.4)),
         SingleDroneCommand("name2", Command.forward(1.5)),
         SingleDroneCommand("name2", Command.backward(1.6)),
         SingleDroneCommand("name2", Command.rotate_left(91)),
         SingleDroneCommand("name2", Command.rotate_right(92)),
         SingleDroneCommand("name2", Command.wait(1))
     ]
     with patch(
             'xdrone.command_converters.dji_tello_edu_drone_executor.FlyTello',
             return_value=self.fly_tello):
         executor = DJITelloEduExecutor(self.name_id_map)
         executor.execute_drone_commands(single_commands)
         calls = [
             call.wait_sync(),
             call.takeoff(tello=1),
             call.wait_sync(),
             call.land(tello=1),
             call.wait_sync(),
             call.up(110, tello=2),
             call.wait_sync(),
             call.down(120, tello=2),
             call.wait_sync(),
             call.left(130, tello=2),
             call.wait_sync(),
             call.right(140, tello=2),
             call.wait_sync(),
             call.forward(150, tello=2),
             call.wait_sync(),
             call.back(160, tello=2),
             call.wait_sync(),
             call.rotate_ccw(91, tello=2),
             call.wait_sync(),
             call.rotate_cw(92, tello=2),
             call.wait_sync(),
             call.pause(1)
         ]
         self.fly.assert_has_calls(calls)
コード例 #12
0
 def test_check_parallel_drone_commands_should_wait_for_slower_branch(self):
     drone_commands = [
         ParallelDroneCommands(
             [[SingleDroneCommand("DRONE1", Command.takeoff())],
              [SingleDroneCommand("DRONE2", Command.wait(5))]])
     ]
     drone_state_map = {"DRONE1": State(), "DRONE2": State(x_meters=1)}
     drones_involved = {"DRONE1", "DRONE2"}
     BoundaryChecker(
         self.boundary_config)._update_states_and_check_drone_commands(
             drone_commands, self.state_updater_map, drone_state_map,
             drones_involved)
     expected = {
         "DRONE1": State(has_taken_off=True,
                         time_used_seconds=5,
                         z_meters=1),
         "DRONE2": State(time_used_seconds=5, x_meters=1)
     }
     self.assertEqual(expected, drone_state_map)
コード例 #13
0
    def test_convert_command(self):
        commands = [
            SingleDroneCommand("DRONE1", Command.takeoff()),
            SingleDroneCommand("DRONE1", Command.up(1)),
            SingleDroneCommand("DRONE1", Command.down(1)),
            SingleDroneCommand("DRONE1", Command.left(1)),
            SingleDroneCommand("DRONE1", Command.right(1)),
            SingleDroneCommand("DRONE1", Command.forward(1)),
            SingleDroneCommand("DRONE1", Command.backward(1)),
            SingleDroneCommand("DRONE1", Command.rotate_left(1)),
            SingleDroneCommand("DRONE1", Command.rotate_right(1)),
            ParallelDroneCommands(
                [[
                    ParallelDroneCommands(
                        [[], [SingleDroneCommand("DRONE1", Command.up(1))]])
                ],
                 [
                     SingleDroneCommand("DRONE2", Command.takeoff()),
                     SingleDroneCommand("DRONE2", Command.land())
                 ]]),
            SingleDroneCommand("DRONE1", Command.land()),
            SingleDroneCommand("DRONE1", Command.wait(1))
        ]
        drone_config_map = {
            "DRONE1": DroneConfig((1, 2, 3), 1, 90, 2),
            "DRONE2": DroneConfig((0, 0, 0), 1, 90, 1)
        }
        expected = {
            "config": [{
                "name": "DRONE1",
                "init_pos": [1, 2, 3],
                "speed": 1,
                "rotate_speed": 90,
                "takeoff_height": 2
            }, {
                "name": "DRONE2",
                "init_pos": [0, 0, 0],
                "speed": 1,
                "rotate_speed": 90,
                "takeoff_height": 1
            }],
            "commands": [{
                "type": "single",
                "name": "DRONE1",
                "action": "takeoff",
                "values": []
            }, {
                "type": "single",
                "name": "DRONE1",
                "action": "up",
                "values": [1]
            }, {
                "type": "single",
                "name": "DRONE1",
                "action": "down",
                "values": [1]
            }, {
                "type": "single",
                "name": "DRONE1",
                "action": "left",
                "values": [1]
            }, {
                "type": "single",
                "name": "DRONE1",
                "action": "right",
                "values": [1]
            }, {
                "type": "single",
                "name": "DRONE1",
                "action": "forward",
                "values": [1]
            }, {
                "type": "single",
                "name": "DRONE1",
                "action": "backward",
                "values": [1]
            }, {
                "type": "single",
                "name": "DRONE1",
                "action": "rotate_left",
                "values": [1]
            }, {
                "type": "single",
                "name": "DRONE1",
                "action": "rotate_right",
                "values": [1]
            }, {
                "type":
                "parallel",
                "branches": [[{
                    "type":
                    "parallel",
                    "branches": [[],
                                 [{
                                     "type": "single",
                                     "name": "DRONE1",
                                     "action": "up",
                                     "values": [1]
                                 }]]
                }],
                             [{
                                 "type": "single",
                                 "name": "DRONE2",
                                 "action": "takeoff",
                                 "values": []
                             }, {
                                 "type": "single",
                                 "name": "DRONE2",
                                 "action": "land",
                                 "values": []
                             }]]
            }, {
                "type": "single",
                "name": "DRONE1",
                "action": "land",
                "values": []
            }, {
                "type": "single",
                "name": "DRONE1",
                "action": "wait",
                "values": [1]
            }]
        }
        actual = SimulationConverter().convert(commands, drone_config_map)

        self.assertEqual(expected,
                         json.loads(zlib.decompress(b64decode(actual))))
コード例 #14
0
 def _update_states(self, single_drone_command: SingleDroneCommand,
                    state_updaters: Dict[str, StateUpdater],
                    drone_trajectory_map: Dict[str, List[State]],
                    drones_involved: Set[str]) -> float:
     time_interval = self.collision_config.time_interval_seconds
     drone_name = single_drone_command.drone_name
     command = single_drone_command.command
     if command.opcode == "takeoff":
         takeoff_distance = self.drone_config_map[
             drone_name].takeoff_height_meters
         new_drone_command = SingleDroneCommand(
             drone_name, Command.up(takeoff_distance))
         return self._update_states(new_drone_command, state_updaters,
                                    drone_trajectory_map, drones_involved)
     if command.opcode == "land":
         old_state = drone_trajectory_map[drone_name][-1]
         land_distance = old_state.z_meters
         new_drone_command = SingleDroneCommand(drone_name,
                                                Command.down(land_distance))
         return self._update_states(new_drone_command, state_updaters,
                                    drone_trajectory_map, drones_involved)
     if command.opcode == "wait":
         seconds, = command.operands
         old_state = drone_trajectory_map[drone_name][-1]
         if seconds <= time_interval:
             new_state = state_updaters[drone_name].update(
                 Command.wait(seconds), old_state)
             drone_trajectory_map[drone_name].append(new_state)
             for name in drones_involved.difference({drone_name}):
                 state = drone_trajectory_map[name][-1]
                 drone_trajectory_map[name].append(
                     state.copy_and_set_time_used_seconds(
                         state.time_used_seconds + seconds))
             return seconds
         else:
             new_state = state_updaters[drone_name].update(
                 Command.wait(time_interval), old_state)
             drone_trajectory_map[drone_name].append(new_state)
             for name in drones_involved.difference({drone_name}):
                 state = drone_trajectory_map[name][-1]
                 drone_trajectory_map[name].append(
                     state.copy_and_set_time_used_seconds(
                         state.time_used_seconds + time_interval))
             new_drone_command = SingleDroneCommand(
                 drone_name, Command.wait(seconds - time_interval))
             return time_interval + self._update_states(
                 new_drone_command, state_updaters, drone_trajectory_map,
                 drones_involved)
     if command.opcode in ["rotate_left", "rotate_right"]:
         degrees, = command.operands
         old_state = drone_trajectory_map[drone_name][-1]
         rotate_speed = self.drone_config_map[drone_name].rotate_speed_dps
         seconds = degrees / rotate_speed
         if seconds <= time_interval:
             new_state = state_updaters[drone_name].update(
                 Command(command.opcode, [rotate_speed * seconds]),
                 old_state)
             drone_trajectory_map[drone_name].append(new_state)
             for name in drones_involved.difference({drone_name}):
                 state = drone_trajectory_map[name][-1]
                 drone_trajectory_map[name].append(
                     state.copy_and_set_time_used_seconds(
                         state.time_used_seconds + seconds))
             return seconds
         else:
             new_state = state_updaters[drone_name].update(
                 Command(command.opcode, [rotate_speed * time_interval]),
                 old_state)
             drone_trajectory_map[drone_name].append(new_state)
             for name in drones_involved.difference({drone_name}):
                 state = drone_trajectory_map[name][-1]
                 drone_trajectory_map[name].append(
                     state.copy_and_set_time_used_seconds(
                         state.time_used_seconds + time_interval))
             new_drone_command = SingleDroneCommand(
                 drone_name,
                 Command(command.opcode,
                         [degrees - rotate_speed * time_interval]))
             return time_interval + self._update_states(
                 new_drone_command, state_updaters, drone_trajectory_map,
                 drones_involved)
     if command.opcode in [
             "up", "down", "left", "right", "forward", "backward"
     ]:
         meters, = command.operands
         old_state = drone_trajectory_map[drone_name][-1]
         speed = self.drone_config_map[drone_name].speed_mps
         seconds = meters / speed
         if seconds <= time_interval:
             new_state = state_updaters[drone_name].update(
                 Command(command.opcode, [speed * seconds]), old_state)
             drone_trajectory_map[drone_name].append(new_state)
             for name in drones_involved.difference({drone_name}):
                 state = drone_trajectory_map[name][-1]
                 drone_trajectory_map[name].append(
                     state.copy_and_set_time_used_seconds(
                         state.time_used_seconds + seconds))
             return seconds
         else:
             new_state = state_updaters[drone_name].update(
                 Command(command.opcode, [speed * time_interval]),
                 old_state)
             drone_trajectory_map[drone_name].append(new_state)
             for name in drones_involved.difference({drone_name}):
                 state = drone_trajectory_map[name][-1]
                 drone_trajectory_map[name].append(
                     state.copy_and_set_time_used_seconds(
                         state.time_used_seconds + time_interval))
             new_drone_command = SingleDroneCommand(
                 drone_name,
                 Command(command.opcode, [meters - speed * time_interval]))
             return time_interval + self._update_states(
                 new_drone_command, state_updaters, drone_trajectory_map,
                 drones_involved)
コード例 #15
0
 def test_wait(self):
     self.assertEqual(Command.wait(1), Command.wait(1))
     self.assertEqual("wait", Command.wait(1).opcode)
     self.assertEqual([1], Command.wait(1).operands)
     self.assertEqual("Command: { opcode: wait, operands: [1] }",
                      str(Command.wait(1)))
コード例 #16
0
 def test_dji_tello_executor_should_send_correct_message(self) -> None:
     mocked_socket = Mock()
     mocked_socket.recvfrom = Mock(return_value=(b'ok', ('192.168.10.1',
                                                         8889)))
     mocked_socket.sendto = Mock()
     commands = [
         Command.takeoff(),
         Command.up(1),
         Command.down(1),
         Command.left(1),
         Command.right(1),
         Command.forward(1),
         Command.backward(1),
         Command.rotate_left(90),
         Command.rotate_right(90),
         Command.wait(0),
         Command.land()
     ]
     executor = DJITelloExecutor()
     executor.sock.close()
     executor.sock = mocked_socket
     with self.assertLogs(logging.getLogger()) as log:
         executor.execute_commands(commands)
     calls = [
         call(b"command", ('192.168.10.1', 8889)),
         call(b"takeoff", ('192.168.10.1', 8889)),
         call(b"up 100", ('192.168.10.1', 8889)),
         call(b"down 100", ('192.168.10.1', 8889)),
         call(b"left 100", ('192.168.10.1', 8889)),
         call(b"right 100", ('192.168.10.1', 8889)),
         call(b"forward 100", ('192.168.10.1', 8889)),
         call(b"back 100", ('192.168.10.1', 8889)),
         call(b"ccw 90", ('192.168.10.1', 8889)),
         call(b"cw 90", ('192.168.10.1', 8889)),
         call(b"land", ('192.168.10.1', 8889))
     ]
     mocked_socket.sendto.assert_has_calls(calls)
     expected_log = [
         "INFO:root:sent message: b'command'",
         "INFO:root:received response: ok",
         "INFO:root:sent message: b'takeoff'",
         "INFO:root:received response: ok",
         "INFO:root:sent message: b'up 100'",
         "INFO:root:received response: ok",
         "INFO:root:sent message: b'down 100'",
         "INFO:root:received response: ok",
         "INFO:root:sent message: b'left 100'",
         "INFO:root:received response: ok",
         "INFO:root:sent message: b'right 100'",
         "INFO:root:received response: ok",
         "INFO:root:sent message: b'forward 100'",
         "INFO:root:received response: ok",
         "INFO:root:sent message: b'back 100'",
         "INFO:root:received response: ok",
         "INFO:root:sent message: b'ccw 90'",
         "INFO:root:received response: ok",
         "INFO:root:sent message: b'cw 90'",
         "INFO:root:received response: ok",
         "INFO:root:sent message: b'land'",
         "INFO:root:received response: ok"
     ]
     self.assertEqual(expected_log, log.output)
コード例 #17
0
 def test_wait_with_decimal_parameter_should_return_correct_command(self):
     actual = generate_commands("""
         main() { wait(1.0); }
     """)
     expected = [SingleDroneCommand("DEFAULT", Command.wait(1.0))]
     self.assertEqual(expected, actual)