def setUp(self) -> None:
        self.collision_config = CollisionConfig(collision_meters=0.3, time_interval_seconds=0.1)
        self.drone_config_map = {"DRONE1": DroneConfig(init_position=(0, 0, 0), speed_mps=1,
                                                       rotate_speed_dps=90, takeoff_height_meters=1),
                                 "DRONE2": DroneConfig(init_position=(1, 0, 0), speed_mps=2,
                                                       rotate_speed_dps=180, takeoff_height_meters=1)}
        self.state_updater_map = {"DRONE1": StateUpdater(DroneConfig(init_position=(0, 0, 0), speed_mps=1,
                                                                     rotate_speed_dps=90, takeoff_height_meters=1)),
                                  "DRONE2": StateUpdater(DroneConfig(init_position=(1, 0, 0), speed_mps=2,
                                                                     rotate_speed_dps=180, takeoff_height_meters=1))}

        self.collision_checker = CollisionChecker(self.drone_config_map, self.collision_config)
Beispiel #2
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)
Beispiel #3
0
 def test_str(self):
     drone_config = DroneConfig(init_position=(0, 0, 0),
                                speed_mps=0.5,
                                rotate_speed_dps=45,
                                takeoff_height_meters=1)
     self.assertEqual(
         "DroneConfig: { init_position: (0, 0, 0), speed_mps: 0.5, " +
         "rotate_speed_dps: 45, takeoff_height_meters: 1 }",
         str(drone_config))
Beispiel #4
0
 def test_property(self):
     drone_config = DroneConfig(init_position=(0, 0, 0),
                                speed_mps=0.5,
                                rotate_speed_dps=45,
                                takeoff_height_meters=1)
     self.assertEqual((0, 0, 0), drone_config.init_position)
     self.assertEqual(0.5, drone_config.speed_mps)
     self.assertEqual(45, drone_config.rotate_speed_dps)
     self.assertEqual(1, drone_config.takeoff_height_meters)
 def test_get_init_state(self):
     self.assertEqual(State(x_meters=0, y_meters=0, z_meters=0),
                      self.state_updater.get_init_state())
     drone_config = DroneConfig(init_position=(1, 2, 3),
                                speed_mps=0.5,
                                rotate_speed_dps=45,
                                takeoff_height_meters=1)
     state_updater = StateUpdater(drone_config=drone_config)
     self.assertEqual(State(x_meters=1, y_meters=2, z_meters=3),
                      state_updater.get_init_state())
 def test_parse_if_provided_configs_should_parse_all_configs(self):
     config = """
         {
           "drones": [{
             "name": "DRONE1",
             "init_position": {"x": 1, "y": 2, "z": 3},
             "speed_mps": 2,
             "rotate_speed_dps": 180,
             "takeoff_height_meters": 2
           },{
             "name": "DRONE2",
             "init_position": {"x": 4, "y": 5, "z": 6},
             "speed_mps": 1,
             "rotate_speed_dps": 90,
             "takeoff_height_meters": 1
           }],
           "boundary_config": {
             "max_seconds": 100,
             "max_x_meters": 10,
             "max_y_meters": 20,
             "max_z_meters": 30,
             "min_x_meters": -10,
             "min_y_meters": -20,
             "min_z_meters": -30
           },
           "collision_config": {
             "collision_meters": 0.3,
             "time_interval_seconds": 0.5
           }
         }
         """
     actual_drone_config, actual_boundary_config, acutal_collision_config = ConfigParser.parse(config)
     expected_drone_configs = {"DRONE1": DroneConfig(init_position=(1, 2, 3), speed_mps=2,
                                                     rotate_speed_dps=180, takeoff_height_meters=2),
                               "DRONE2": DroneConfig(init_position=(4, 5, 6), speed_mps=1,
                                                     rotate_speed_dps=90, takeoff_height_meters=1)}
     expected_boundary_config = BoundaryConfig(max_seconds=100, max_x_meters=10, max_y_meters=20, max_z_meters=30,
                                               min_x_meters=-10, min_y_meters=-20, min_z_meters=-30)
     expected_collision_config = CollisionConfig(collision_meters=0.3, time_interval_seconds=0.5)
     self.assertEqual(expected_drone_configs, actual_drone_config)
     self.assertEqual(expected_boundary_config, actual_boundary_config)
     self.assertEqual(expected_collision_config, acutal_collision_config)
Beispiel #7
0
 def setUp(self) -> None:
     self.boundary_config = BoundaryConfig(max_seconds=10,
                                           max_x_meters=5,
                                           max_y_meters=5,
                                           max_z_meters=5,
                                           min_x_meters=-5,
                                           min_y_meters=-5,
                                           min_z_meters=-5)
     self.state_updater_map = {
         "DRONE1":
         StateUpdater(
             DroneConfig(init_position=(0, 0, 0),
                         speed_mps=1,
                         rotate_speed_dps=90,
                         takeoff_height_meters=1)),
         "DRONE2":
         StateUpdater(
             DroneConfig(init_position=(1, 0, 0),
                         speed_mps=2,
                         rotate_speed_dps=180,
                         takeoff_height_meters=2))
     }
Beispiel #8
0
    def test_init_with_wrong_parameter_should_give_error(self):
        for invalid_value in [-10, -1, 0]:
            with self.assertRaises(ValueError) as context:
                DroneConfig(init_position=(0, 0, 0),
                            speed_mps=invalid_value,
                            rotate_speed_dps=45,
                            takeoff_height_meters=1)
            self.assertTrue("speed_mps should > 0" in str(context.exception))

            with self.assertRaises(ValueError) as context:
                DroneConfig(init_position=(0, 0, 0),
                            speed_mps=0.5,
                            rotate_speed_dps=invalid_value,
                            takeoff_height_meters=1)
            self.assertTrue(
                "rotate_speed_dps should > 0" in str(context.exception))

            with self.assertRaises(ValueError) as context:
                DroneConfig(init_position=(0, 0, 0),
                            speed_mps=0.5,
                            rotate_speed_dps=45,
                            takeoff_height_meters=invalid_value)
            self.assertTrue(
                "takeoff_height_meters should > 0" in str(context.exception))
Beispiel #9
0
 def test_if_given_state_updater_should_use_it_to_update_state(self):
     commands = "main() {takeoff(); land();}"
     with self.assertRaises(SafetyCheckError) as context:
         generate_commands(commands,
                           drone_config_map={
                               "DEFAULT":
                               DroneConfig(init_position=(0, 0, 0),
                                           speed_mps=1,
                                           rotate_speed_dps=90,
                                           takeoff_height_meters=10)
                           },
                           boundary_checker=BoundaryChecker(
                               BoundaryConfig(max_seconds=10,
                                              max_z_meters=1)))
     self.assertTrue(
         "Drone 'DEFAULT': the z coordinate 10 will go beyond its upper limit 1"
         in str(context.exception))
    def test_parse_if_name_repeated_should_ignore_second_appearance(self):
        config = """
            {
              "drones": [{
                "name": "DRONE1",
                "init_position": {"x": 1, "y": 2, "z": 3},
                "speed_mps": 2,
                "rotate_speed_dps": 180,
                "takeoff_height_meters": 2
              },{
                "name": "DRONE1",
                "init_position": {"x": 4, "y": 5, "z": 6},
                "speed_mps": 1,
                "rotate_speed_dps": 90,
                "takeoff_height_meters": 1
              }],
              "boundary_config": {
                "max_seconds": 100,
                "max_x_meters": 10,
                "max_y_meters": 20,
                "max_z_meters": 30,
                "min_x_meters": -10,
                "min_y_meters": -20,
                "min_z_meters": -30
              },
              "collision_config": {
                "collision_meters": 0.3,
                "time_interval_seconds": 0.5
              }
            }
            """

        with self.assertLogs(logging.getLogger()) as log:
            actual_drone_configs, actual_boundary_config, actual_collision_config = ConfigParser.parse(config)
        expected_log = ["WARNING:root:Drone name 'DRONE1' appeared more than ones in 'drones', ignored."]
        self.assertEqual(expected_log, log.output)
        expected_drone_configs = {"DRONE1": DroneConfig(init_position=(1, 2, 3), speed_mps=2,
                                                        rotate_speed_dps=180, takeoff_height_meters=2)}
        expected_boundary_config = BoundaryConfig(max_seconds=100, max_x_meters=10, max_y_meters=20, max_z_meters=30,
                                                  min_x_meters=-10, min_y_meters=-20, min_z_meters=-30)
        expected_collision_config = CollisionConfig(collision_meters=0.3, time_interval_seconds=0.5)
        self.assertEqual(expected_drone_configs, actual_drone_configs)
        self.assertEqual(expected_boundary_config, actual_boundary_config)
        self.assertEqual(expected_collision_config, actual_collision_config)
    def test_parse_if_init_position_missing_fields_should_use_default_value(self):
        config = """
            {
              "drones": [{
                "name": "DRONE1",
                "init_position": {},
                "speed_mps": 2,
                "rotate_speed_dps": 180,
                "takeoff_height_meters": 2
              }],
              "boundary_config": {
                "max_seconds": 100,
                "max_x_meters": 10,
                "max_y_meters": 20,
                "max_z_meters": 30,
                "min_x_meters": -10,
                "min_y_meters": -20,
                "min_z_meters": -30
              },
              "collision_config": {
                "collision_meters": 0.3,
                "time_interval_seconds": 0.5
              }
            }
            """

        with self.assertLogs(logging.getLogger()) as log:
            actual_drone_configs, actual_boundary_config, actual_collision_config = ConfigParser.parse(config)
        expected_log = ["WARNING:root:'x' missing when parsing drone 'DRONE1', using default value 0. " +
                        "Position estimation may be inaccurate.",
                        "WARNING:root:'y' missing when parsing drone 'DRONE1', using default value 0. " +
                        "Position estimation may be inaccurate.",
                        "WARNING:root:'z' missing when parsing drone 'DRONE1', using default value 0. " +
                        "Position estimation may be inaccurate."]
        self.assertEqual(expected_log, log.output)
        expected_drone_configs = {"DRONE1": DroneConfig(init_position=(0, 0, 0), speed_mps=2,
                                                        rotate_speed_dps=180, takeoff_height_meters=2)}
        expected_boundary_config = BoundaryConfig(max_seconds=100, max_x_meters=10, max_y_meters=20, max_z_meters=30,
                                                  min_x_meters=-10, min_y_meters=-20, min_z_meters=-30)
        expected_collision_config = CollisionConfig(collision_meters=0.3, time_interval_seconds=0.5)
        self.assertEqual(expected_drone_configs, actual_drone_configs)
        self.assertEqual(expected_boundary_config, actual_boundary_config)
        self.assertEqual(expected_collision_config, actual_collision_config)
 def test_parse_if_drone_object_missing_fields_should_use_default_value(self):
     config = """
         {
           "drones": [{}],
           "boundary_config": {
             "max_seconds": 100,
             "max_x_meters": 10,
             "max_y_meters": 20,
             "max_z_meters": 30,
             "min_x_meters": -10,
             "min_y_meters": -20,
             "min_z_meters": -30
           },
           "collision_config": {
             "collision_meters": 0.3,
             "time_interval_seconds": 0.5
           }
         }
         """
     with self.assertLogs(logging.getLogger()) as log:
         actual_drone_configs, actual_boundary_config, actual_collision_config = ConfigParser.parse(config)
     expected_log = ["WARNING:root:'name' missing when parsing object in 'drones', using default value 'DEFAULT'.",
                     "WARNING:root:'init_position' missing when parsing drone 'DEFAULT', " +
                     "using default value (0, 0, 0). Position estimation may be inaccurate.",
                     "WARNING:root:'speed_mps' missing when parsing drone 'DEFAULT', " +
                     "using default value 1. Position estimation may be inaccurate.",
                     "WARNING:root:'rotate_speed_dps' missing when parsing drone 'DEFAULT', " +
                     "using default value 90. Position estimation may be inaccurate.",
                     "WARNING:root:'takeoff_height_meters' missing when parsing drone 'DEFAULT', " +
                     "using default value 1. Position estimation may be inaccurate."]
     self.assertEqual(expected_log, log.output)
     expected_drone_configs = {"DEFAULT": DroneConfig(init_position=(0, 0, 0), speed_mps=1,
                                                      rotate_speed_dps=90, takeoff_height_meters=1)}
     expected_boundary_config = BoundaryConfig(max_seconds=100, max_x_meters=10, max_y_meters=20, max_z_meters=30,
                                               min_x_meters=-10, min_y_meters=-20, min_z_meters=-30)
     expected_collision_config = CollisionConfig(collision_meters=0.3, time_interval_seconds=0.5)
     self.assertEqual(expected_drone_configs, actual_drone_configs)
     self.assertEqual(expected_boundary_config, actual_boundary_config)
     self.assertEqual(expected_collision_config, actual_collision_config)
    def test_parse_if_name_is_empty_string_should_use_default_value(self):
        config = """
            {
              "drones": [{
                "name": "",
                "init_position": {"x": 1, "y": 2, "z": 3},
                "speed_mps": 2,
                "rotate_speed_dps": 180,
                "takeoff_height_meters": 2
              }],
              "boundary_config": {
                "max_seconds": 100,
                "max_x_meters": 10,
                "max_y_meters": 20,
                "max_z_meters": 30,
                "min_x_meters": -10,
                "min_y_meters": -20,
                "min_z_meters": -30
              },
              "collision_config": {
                "collision_meters": 0.3,
                "time_interval_seconds": 0.5
              }
            }
            """

        with self.assertLogs(logging.getLogger()) as log:
            actual_drone_configs, actual_boundary_config, actual_collision_config = ConfigParser.parse(config)
        expected_log = ["WARNING:root:'name' cannot be an empty string, using default value 'DEFAULT' instead."]
        self.assertEqual(expected_log, log.output)
        expected_drone_configs = {"DEFAULT": DroneConfig(init_position=(1, 2, 3), speed_mps=2,
                                                         rotate_speed_dps=180, takeoff_height_meters=2)}
        expected_boundary_config = BoundaryConfig(max_seconds=100, max_x_meters=10, max_y_meters=20, max_z_meters=30,
                                                  min_x_meters=-10, min_y_meters=-20, min_z_meters=-30)
        expected_collision_config = CollisionConfig(collision_meters=0.3, time_interval_seconds=0.5)
        self.assertEqual(expected_drone_configs, actual_drone_configs)
        self.assertEqual(expected_boundary_config, actual_boundary_config)
        self.assertEqual(expected_collision_config, actual_collision_config)
    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))))
Beispiel #15
0
 def test_eq(self):
     self.assertEqual(
         DroneConfig(init_position=(0, 0, 0),
                     speed_mps=0.5,
                     rotate_speed_dps=45,
                     takeoff_height_meters=1),
         DroneConfig(init_position=(0, 0, 0),
                     speed_mps=0.5,
                     rotate_speed_dps=45,
                     takeoff_height_meters=1))
     self.assertNotEqual(
         DroneConfig(init_position=(0, 0, 0),
                     speed_mps=0.5,
                     rotate_speed_dps=45,
                     takeoff_height_meters=1),
         DroneConfig(init_position=(1, 0, 0),
                     speed_mps=0.5,
                     rotate_speed_dps=45,
                     takeoff_height_meters=1))
     self.assertNotEqual(
         DroneConfig(init_position=(0, 0, 0),
                     speed_mps=0.5,
                     rotate_speed_dps=45,
                     takeoff_height_meters=1),
         DroneConfig(init_position=(0, 0, 0),
                     speed_mps=1,
                     rotate_speed_dps=45,
                     takeoff_height_meters=1))
     self.assertNotEqual(
         DroneConfig(init_position=(0, 0, 0),
                     speed_mps=0.5,
                     rotate_speed_dps=45,
                     takeoff_height_meters=1),
         DroneConfig(init_position=(0, 0, 0),
                     speed_mps=0.5,
                     rotate_speed_dps=90,
                     takeoff_height_meters=1))
     self.assertNotEqual(
         DroneConfig(init_position=(0, 0, 0),
                     speed_mps=0.5,
                     rotate_speed_dps=45,
                     takeoff_height_meters=1),
         DroneConfig(init_position=(0, 0, 0),
                     speed_mps=0.5,
                     rotate_speed_dps=45,
                     takeoff_height_meters=2))
     self.assertNotEqual(
         None,
         DroneConfig(init_position=(0, 0, 0),
                     speed_mps=0.5,
                     rotate_speed_dps=45,
                     takeoff_height_meters=2))
 def setUp(self) -> None:
     self.drone_config = DroneConfig(init_position=(0, 0, 0),
                                     speed_mps=0.5,
                                     rotate_speed_dps=45,
                                     takeoff_height_meters=1)
     self.state_updater = StateUpdater(drone_config=self.drone_config)
    def _parse_drone_config(data: dict) -> Dict[str, DroneConfig]:
        drone_config_map = {}
        if "drones" in data:
            if len(data["drones"]) == 0:
                warning(
                    "'drones' is empty when parsing configs, using default drone_config. "
                    + "Position estimation may be inaccurate.")
                return {"DEFAULT": DefaultDroneConfig()}
            for drone in data["drones"]:
                if "name" in drone:
                    name = drone["name"]
                    if name == "":
                        warning(
                            "'name' cannot be an empty string, using default value 'DEFAULT' instead."
                        )
                        name = "DEFAULT"
                else:
                    warning(
                        "'name' missing when parsing object in 'drones', using default value 'DEFAULT'."
                    )
                    name = "DEFAULT"

                if name in drone_config_map:
                    warning(
                        "Drone name '{}' appeared more than ones in 'drones', ignored."
                        .format(name))
                    continue

                if "init_position" in drone:
                    position = drone["init_position"]
                    init_position = []
                    for dim in ["x", "y", "z"]:
                        if dim in position:
                            init_position.append(position[dim])
                        else:
                            warning(
                                "'{}' missing when parsing drone '{}', using default value 0. "
                                .format(dim, name) +
                                "Position estimation may be inaccurate.")
                            init_position.append(0)
                    init_position = tuple(init_position)
                else:
                    warning("'init_position' missing when parsing drone '{}', "
                            .format(name) + "using default value (0, 0, 0). " +
                            "Position estimation may be inaccurate.")
                    init_position = (0, 0, 0)
                if "speed_mps" in drone:
                    speed_mps = drone["speed_mps"]
                else:
                    warning("'speed_mps' missing when parsing drone '{}', ".
                            format(name) + "using default value 1. " +
                            "Position estimation may be inaccurate.")
                    speed_mps = 1
                if "rotate_speed_dps" in drone:
                    rotate_speed_dps = drone["rotate_speed_dps"]
                else:
                    warning(
                        "'rotate_speed_dps' missing when parsing drone '{}', ".
                        format(name) + "using default value 90. " +
                        "Position estimation may be inaccurate.")
                    rotate_speed_dps = 90
                if "takeoff_height_meters" in drone:
                    takeoff_height_meters = drone["takeoff_height_meters"]
                else:
                    warning(
                        "'takeoff_height_meters' missing when parsing drone '{}', "
                        .format(name) + "using default value 1. " +
                        "Position estimation may be inaccurate.")
                    takeoff_height_meters = 1

                drone_config = DroneConfig(init_position, speed_mps,
                                           rotate_speed_dps,
                                           takeoff_height_meters)
                drone_config_map[name] = drone_config
        else:
            warning(
                "'drones' missing when parsing configs, using default drone_config. "
                + "Position estimation may be inaccurate.")
            return {"DEFAULT": DefaultDroneConfig()}
        return drone_config_map