예제 #1
0
 def test_parallel_with_repeated_drones_in_branches_should_give_error(self):
     drone_config_map = {
         "DRONE1": DefaultDroneConfig(),
         "DRONE2": DefaultDroneConfig(),
         "DRONE3": DefaultDroneConfig()
     }
     with self.assertRaises(CompileError) as context:
         generate_commands("""
             procedure foo() {
               DRONE1.takeoff();
               DRONE2.takeoff();
               DRONE1.land();
               DRONE2.land();
             }
             procedure bar() {
               DRONE2.takeoff();
               DRONE3.takeoff();
               DRONE2.land();
               DRONE3.land();
             }
             main() { 
               {foo();} || {bar();} || {DRONE3.takeoff(); DRONE3.land();}; 
             }
         """,
                           drone_config_map=drone_config_map)
     self.assertTrue(
         "Parallel branches should have exclusive drone names, " +
         "but {'DRONE2'} appeared in more than one branches" in str(
             context.exception))
예제 #2
0
 def test_parallel_with_different_drones_should_give_correct_commands(self):
     drone_config_map = {
         "DRONE1": DefaultDroneConfig(),
         "DRONE2": DefaultDroneConfig(),
         "DRONE3": DefaultDroneConfig()
     }
     actual = generate_commands("""
         main() { 
           {DRONE1.takeoff();} || {DRONE2.takeoff();} || {DRONE3.takeoff();}; 
           {
             DRONE1.land();
           } || {
             {DRONE2.land();} || {DRONE3.land();};
           };
         }
     """,
                                drone_config_map=drone_config_map)
     expected = [
         ParallelDroneCommands(
             [[SingleDroneCommand("DRONE1", Command.takeoff())],
              [SingleDroneCommand("DRONE2", Command.takeoff())],
              [SingleDroneCommand("DRONE3", Command.takeoff())]]),
         ParallelDroneCommands(
             [[SingleDroneCommand("DRONE1", Command.land())],
              [
                  ParallelDroneCommands(
                      [[SingleDroneCommand("DRONE2", Command.land())],
                       [SingleDroneCommand("DRONE3", Command.land())]])
              ]])
     ]
     self.assertEqual(expected, actual)
예제 #3
0
 def test_eq(self):
     expressions1 = [
         None,
         Expression(Type.int(), 1),
         Expression(Type.decimal(), 1.1),
         Expression(Type.boolean(), False),
         Expression(Type.vector(), [1.1, 2.2, -1.1]),
         Expression(Type.list_of(Type.int()), [1, 2, 3, 4]),
         Expression(Type.list_of(Type.int()), []),
         Expression(Type.list_of(Type.decimal()), [1.0, 2.0, 3.0, 4.0]),
         Expression(Type.list_of(Type.list_of(Type.vector())),
                    [[[1.1, 2.2, -1.1], [1, 2, -1]]]),
         Expression(Type.int(), 1, "a"),
         Expression(Type.decimal(), 1.1, "a"),
         Expression(Type.boolean(), False, "a"),
         Expression(Type.vector(), [1.1, 2.2, -1.1], "a"),
         Expression(Type.drone(), Drone("DRONE", DefaultDroneConfig()),
                    "a"),
         Expression(Type.list_of(Type.int()), [1, 2, 3, 4], "a"),
         Expression(Type.list_of(Type.int()), [], "a"),
         Expression(Type.list_of(Type.decimal()), [1.0, 2.0, 3.0, 4.0],
                    "a"),
         Expression(Type.list_of(Type.list_of(Type.vector())),
                    [[[1.1, 2.2, -1.1], [1, 2, -1]]], "a")
     ]
     expressions2 = [
         None,
         Expression(Type.int(), 1),
         Expression(Type.decimal(), 1.1),
         Expression(Type.boolean(), False),
         Expression(Type.vector(), [1.1, 2.2, -1.1]),
         Expression(Type.list_of(Type.int()), [1, 2, 3, 4]),
         Expression(Type.list_of(Type.int()), []),
         Expression(Type.list_of(Type.decimal()), [1.0, 2.0, 3.0, 4.0]),
         Expression(Type.list_of(Type.list_of(Type.vector())),
                    [[[1.1, 2.2, -1.1], [1, 2, -1]]]),
         Expression(Type.int(), 1, "a"),
         Expression(Type.decimal(), 1.1, "a"),
         Expression(Type.boolean(), False, "a"),
         Expression(Type.vector(), [1.1, 2.2, -1.1], "a"),
         Expression(Type.drone(), Drone("DRONE", DefaultDroneConfig()),
                    "a"),
         Expression(Type.list_of(Type.int()), [1, 2, 3, 4], "a"),
         Expression(Type.list_of(Type.int()), [], "a"),
         Expression(Type.list_of(Type.decimal()), [1.0, 2.0, 3.0, 4.0],
                    "a"),
         Expression(Type.list_of(Type.list_of(Type.vector())),
                    [[[1.1, 2.2, -1.1], [1, 2, -1]]], "a")
     ]
     for i in range(len(expressions1)):
         for j in range(len(expressions2)):
             if i == j:
                 self.assertEqual(expressions1[i], expressions2[j])
             else:
                 self.assertNotEqual(expressions1[i], expressions2[j])
 def test_declare_and_assign_drone_should_change_symbol_table(self):
     actual = SymbolTable()
     generate_commands("""
         main () { drone a <- DRONE1; }
         """,
                       drone_config_map={"DRONE1": DefaultDroneConfig()},
                       symbol_table=actual)
     expected = SymbolTable()
     expected.store(
         "a",
         Expression(Type.drone(),
                    Drone("DRONE1", DefaultDroneConfig()),
                    ident="a"))
     self.assertEqual(expected, actual)
예제 #5
0
 def test_movement_with_no_name_specified_if_multiple_drones_should_give_error(
         self):
     drone_config_map = {
         "DRONE1": DefaultDroneConfig(),
         "DRONE2": DefaultDroneConfig()
     }
     with self.assertRaises(CompileError) as context:
         generate_commands("""
             main() { takeoff(); land(); }
         """,
                           drone_config_map=drone_config_map)
     self.assertTrue(
         "Drone should be specified if there are multiple drones in config"
         in str(context.exception))
 def test_call_procedure_with_parameter_should_shadow_constant(self):
     commands = generate_commands("""
         procedure proc(drone DRONE2) {
           DRONE2.up(1);
         }
         main () {
           DRONE1.takeoff();
           proc(DRONE1);
           DRONE1.land();
         }
         """, drone_config_map={"DRONE1": DefaultDroneConfig(), "DRONE2": DefaultDroneConfig()})
     expected_commands = [SingleDroneCommand("DRONE1", Command.takeoff()),
                          SingleDroneCommand("DRONE1", Command.up(1)),
                          SingleDroneCommand("DRONE1", Command.land())]
     self.assertEqual(expected_commands, commands)
    def test_repeated_declare_and_assign_drone_constant_should_give_error(
            self):
        types = [
            Type.int(),
            Type.decimal(),
            Type.string(),
            Type.boolean(),
            Type.vector(),
            Type.drone(),
            Type.list_of(Type.int()),
            Type.list_of(Type.list_of(Type.int()))
        ]
        for type in types:
            with self.assertRaises(CompileError) as context:
                generate_commands(
                    """
                    main () {{
                     {} a;
                     {} DRONE <- a;
                    }}
                    """.format(type.type_name, type.type_name),
                    drone_config_map={"DRONE": DefaultDroneConfig()})

            self.assertTrue(
                "Identifier DRONE already declared" in str(context.exception))
예제 #8
0
 def test_to_expression_should_equal_self(self):
     expressions = [
         Expression(Type.int(), 1),
         Expression(Type.decimal(), 1.1),
         Expression(Type.boolean(), False),
         Expression(Type.vector(), [1.1, 2.2, -1.1]),
         Expression(Type.list_of(Type.int()), [1, 2, 3, 4]),
         Expression(Type.list_of(Type.int()), []),
         Expression(Type.list_of(Type.decimal()), [1.0, 2.0, 3.0, 4.0]),
         Expression(Type.list_of(Type.list_of(Type.vector())),
                    [[[1.1, 2.2, -1.1], [1, 2, -1]]]),
         Expression(Type.int(), 1, "a"),
         Expression(Type.decimal(), 1.1, "a"),
         Expression(Type.boolean(), False, "a"),
         Expression(Type.vector(), [1.1, 2.2, -1.1], "a"),
         Expression(Type.drone(), Drone("DRONE", DefaultDroneConfig()),
                    "a"),
         Expression(Type.list_of(Type.int()), [1, 2, 3, 4], "a"),
         Expression(Type.list_of(Type.int()), [], "a"),
         Expression(Type.list_of(Type.decimal()), [1.0, 2.0, 3.0, 4.0],
                    "a"),
         Expression(Type.list_of(Type.list_of(Type.vector())),
                    [[[1.1, 2.2, -1.1], [1, 2, -1]]], "a")
     ]
     for expression in expressions:
         self.assertEqual(expression, expression.to_expression())
예제 #9
0
 def test_parallel_return_with_value_in_branch_should_give_error(self):
     drone_config_map = {
         "DRONE1": DefaultDroneConfig(),
         "DRONE2": DefaultDroneConfig(),
         "DRONE3": DefaultDroneConfig()
     }
     with self.assertRaises(CompileError) as context:
         generate_commands("""
             main() { 
               {return 1; DRONE1.takeoff();} || {DRONE2.takeoff(); DRONE2.land();}; 
             }
         """,
                           drone_config_map=drone_config_map)
     self.assertTrue(
         "Parallel branch should not return anything, but {} is returned".
         format(Expression(Type.int(), 1)) in str(context.exception))
예제 #10
0
def generate_commands(program,
                      drone_config_map: Dict[str, DroneConfig] = None,
                      boundary_checker: BoundaryChecker = None,
                      collision_checker: CollisionChecker = None,
                      has_checks: bool = True,
                      symbol_table: SymbolTable = None,
                      function_table: FunctionTable = None):
    if drone_config_map is None:
        drone_config_map = {"DEFAULT": DefaultDroneConfig()}
    if boundary_checker is None:
        boundary_checker = BoundaryChecker(BoundaryConfig.no_limit())
    if collision_checker is None:
        collision_checker = CollisionChecker(drone_config_map,
                                             DefaultCollisionConfig())
    if symbol_table is None:
        symbol_table = SymbolTable()
    if function_table is None:
        function_table = FunctionTable()
    state_updater_map = {
        name: StateUpdater(config)
        for name, config in drone_config_map.items()
    }

    tree = _parse_program(program)

    drone_commands = Compiler(drone_config_map, symbol_table,
                              function_table).visit(tree)

    if has_checks:
        boundary_checker.check(drone_commands, state_updater_map)
        collision_checker.check(drone_commands, state_updater_map)

    return drone_commands
예제 #11
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)
예제 #12
0
 def test_movement_with_undefined_name_specified_should_give_error(self):
     drone_config_map = {"DRONE1": DefaultDroneConfig()}
     with self.assertRaises(CompileError) as context:
         generate_commands("""
             main() { DRONE2.takeoff(); DRONE2.land(); }
         """,
                           drone_config_map=drone_config_map)
     self.assertTrue("Identifier DRONE2 has not been declared" in str(
         context.exception))
예제 #13
0
 def test_backward_with_drone_name_should_return_correct_command(self):
     actual = generate_commands(
         """
         main() { DRONE1.takeoff(); DRONE1.backward(1); DRONE1.land(); }
     """,
         drone_config_map={"DRONE1": DefaultDroneConfig()})
     expected = [
         SingleDroneCommand("DRONE1", Command.takeoff()),
         SingleDroneCommand("DRONE1", Command.backward(1)),
         SingleDroneCommand("DRONE1", Command.land())
     ]
     self.assertEqual(expected, actual)
예제 #14
0
 def test_container_not_list_should_give_error(self):
     none_list_exprs = [
         Expression(Type.int(), 1, "a"),
         Expression(Type.decimal(), 1.0, "a"),
         Expression(Type.boolean(), True, "a"),
         Expression(Type.string(), "abc", "a"),
         Expression(Type.vector(), [1.0, 1.0, 1.0], "a"),
         Expression(Type.drone(), Drone("DRONE", DefaultDroneConfig()), "a")
     ]
     for none_list_expr in none_list_exprs:
         with self.assertRaises(AssertionError) as context:
             ListElem("a", none_list_expr, 0)
예제 #15
0
 def test_movement_with_no_name_specified_if_only_one_drone_should_use_that_drone(
         self):
     drone_config_map = {"THE_ONE": DefaultDroneConfig()}
     actual = generate_commands("""
         main() { takeoff(); land(); }
     """,
                                drone_config_map=drone_config_map)
     expected = [
         SingleDroneCommand("THE_ONE", Command.takeoff()),
         SingleDroneCommand("THE_ONE", Command.land())
     ]
     self.assertEqual(expected, actual)
예제 #16
0
 def test_parallel_return_in_branch_should_return_early(self):
     drone_config_map = {
         "DRONE1": DefaultDroneConfig(),
         "DRONE2": DefaultDroneConfig(),
         "DRONE3": DefaultDroneConfig()
     }
     actual = generate_commands("""
         main() { 
           {return; DRONE1.takeoff();} || {DRONE2.takeoff(); DRONE2.land();}; 
         }
     """,
                                drone_config_map=drone_config_map)
     expected = [
         ParallelDroneCommands(
             [[],
              [
                  SingleDroneCommand("DRONE2", Command.takeoff()),
                  SingleDroneCommand("DRONE2", Command.land())
              ]])
     ]
     self.assertEqual(expected, actual)
예제 #17
0
    def test_str(self):
        self.assertEqual("Expression: { type: int, value: 1, ident: None }",
                         str(Expression(Type.int(), 1)))
        self.assertEqual(
            "Expression: { type: decimal, value: 1.1, ident: None }",
            str(Expression(Type.decimal(), 1.1)))
        self.assertEqual(
            "Expression: { type: boolean, value: False, ident: None }",
            str(Expression(Type.boolean(), False)))
        self.assertEqual(
            "Expression: { type: vector, value: [1.1, 2.2, -1.1], ident: None }",
            str(Expression(Type.vector(), [1.1, 2.2, -1.1])))
        self.assertEqual(
            "Expression: { type: list[int], value: [1, 2, 3, 4], ident: None }",
            str(Expression(Type.list_of(Type.int()), [1, 2, 3, 4])))
        self.assertEqual(
            "Expression: { type: list[int], value: [], ident: None }",
            str(Expression(Type.list_of(Type.int()), [])))
        self.assertEqual(
            "Expression: { type: list[list[vector]], value: [[[1.1, 2.2, -1.1], [1, 2, -1]]], ident: None }",
            str(
                Expression(Type.list_of(Type.list_of(Type.vector())),
                           [[[1.1, 2.2, -1.1], [1, 2, -1]]])))

        self.assertEqual("Expression: { type: int, value: 1, ident: a }",
                         str(Expression(Type.int(), 1, "a")))
        self.assertEqual("Expression: { type: decimal, value: 1.1, ident: a }",
                         str(Expression(Type.decimal(), 1.1, "a")))
        self.assertEqual(
            "Expression: { type: boolean, value: False, ident: a }",
            str(Expression(Type.boolean(), False, "a")))
        self.assertEqual(
            "Expression: { type: vector, value: [1.1, 2.2, -1.1], ident: a }",
            str(Expression(Type.vector(), [1.1, 2.2, -1.1], "a")))
        self.assertEqual(
            "Expression: { type: drone, value: Drone: { name: DRONE, config: DroneConfig: "
            "{ init_position: (0, 0, 0), speed_mps: 1, rotate_speed_dps: 90, takeoff_height_meters: 1 } "
            "}, ident: a }",
            str(
                Expression(Type.drone(), Drone("DRONE", DefaultDroneConfig()),
                           "a")))
        self.assertEqual(
            "Expression: { type: list[int], value: [1, 2, 3, 4], ident: a }",
            str(Expression(Type.list_of(Type.int()), [1, 2, 3, 4], "a")))
        self.assertEqual(
            "Expression: { type: list[int], value: [], ident: a }",
            str(Expression(Type.list_of(Type.int()), [], "a")))
        self.assertEqual(
            "Expression: { type: list[list[vector]], value: [[[1.1, 2.2, -1.1], [1, 2, -1]]], ident: a }",
            str(
                Expression(Type.list_of(Type.list_of(Type.vector())),
                           [[[1.1, 2.2, -1.1], [1, 2, -1]]], "a")))
 def test_parse_if_not_provided_configs_should_use_default_configs(self):
     config = "{}"
     with self.assertLogs(logging.getLogger()) as log:
         actual_drone_configs, actual_boundary_config, acutal_collision_config = ConfigParser.parse(config)
     expected_log = ["WARNING:root:'drones' missing when parsing configs, using default drone_config. " +
                     "Position estimation may be inaccurate.",
                     "WARNING:root:'boundary_config' missing when parsing configs, using unlimited boundary_config. " +
                     "Time and position will be unlimited.",
                     "WARNING:root:'collision_config' missing when parsing configs, using default collision_config."]
     self.assertEqual(expected_log, log.output)
     self.assertEqual({"DEFAULT": DefaultDroneConfig()}, actual_drone_configs)
     self.assertEqual(BoundaryConfig.no_limit(), actual_boundary_config)
     self.assertEqual(DefaultCollisionConfig(), acutal_collision_config)
    def test_del_drone_constant_should_give_error(self):
        with self.assertRaises(CompileError) as context:
            generate_commands(
                """
                main () {
                 del DRONE1;
                }
            """,
                drone_config_map={"DRONE1": DefaultDroneConfig()})

        self.assertTrue(
            "Identifier DRONE1 is a drone constant, cannot be deleted" in str(
                context.exception))
예제 #20
0
 def test_to_expression_should_return_correct_value(self):
     expressions = [
         None,
         Expression(Type.int(), 1, "a"),
         Expression(Type.decimal(), 1.1, "a"),
         Expression(Type.boolean(), False, "a"),
         Expression(Type.vector(), [1.1, 2.2, -1.1], "a"),
         Expression(Type.drone(), Drone("DRONE", DefaultDroneConfig()),
                    "a"),
         Expression(Type.list_of(Type.int()), [1, 2, 3, 4], "a"),
         Expression(Type.list_of(Type.int()), [], "a"),
         Expression(Type.list_of(Type.decimal()), [1.0, 2.0, 3.0, 4.0],
                    "a"),
         Expression(Type.list_of(Type.list_of(Type.vector())),
                    [[[1.1, 2.2, -1.1], [1, 2, -1]]], "a")
     ]
     for expression in expressions:
         self.assertEqual(expression,
                          Identifier("a", expression).to_expression())
 def test_parse_if_configs_missing_fields_should_use_default_value(self):
     config = """
         {
           "drones": [],
           "boundary_config": {
           },
           "collision_config": {
           }
         }
         """
     with self.assertLogs(logging.getLogger()) as log:
         actual_drone_configs, actual_boundary_config, actual_collision_config = ConfigParser.parse(config)
     expected_log = ["WARNING:root:'drones' is empty when parsing configs, using default drone_config. " +
                     "Position estimation may be inaccurate.",
                     "WARNING:root:'max_seconds' missing when parsing 'boundary_config', " +
                     "using default value inf. There will be no limit on 'max_seconds'.",
                     "WARNING:root:'max_x_meters' missing when parsing 'boundary_config', " +
                     "using default value inf. There will be no limit on 'max_x_meters'.",
                     "WARNING:root:'max_y_meters' missing when parsing 'boundary_config', " +
                     "using default value inf. There will be no limit on 'max_y_meters'.",
                     "WARNING:root:'max_z_meters' missing when parsing 'boundary_config', " +
                     "using default value inf. There will be no limit on 'max_z_meters'.",
                     "WARNING:root:'min_x_meters' missing when parsing 'boundary_config', " +
                     "using default value -inf. There will be no limit on 'min_x_meters'.",
                     "WARNING:root:'min_y_meters' missing when parsing 'boundary_config', " +
                     "using default value -inf. There will be no limit on 'min_y_meters'.",
                     "WARNING:root:'min_z_meters' missing when parsing 'boundary_config', " +
                     "using default value -inf. There will be no limit on 'min_z_meters'.",
                     "WARNING:root:'collision_meters' missing when parsing 'collision_config', " +
                     "using default value 0. There will be no limit on 'collision_meters'.",
                     "WARNING:root:'time_interval_seconds' missing when parsing 'collision_config', " +
                     "using default value 0.1."]
     self.assertEqual(expected_log, log.output)
     expected_drone_configs = {"DEFAULT": DefaultDroneConfig()}
     expected_boundary_config = BoundaryConfig(max_seconds=float("inf"), max_x_meters=float("inf"),
                                               max_y_meters=float("inf"), max_z_meters=float("inf"),
                                               min_x_meters=float("-inf"), min_y_meters=float("-inf"),
                                               min_z_meters=float("-inf"))
     expected_collision_config = DefaultCollisionConfig()
     self.assertEqual(expected_drone_configs, actual_drone_configs)
     self.assertEqual(expected_boundary_config, actual_boundary_config)
     self.assertEqual(expected_collision_config, actual_collision_config)
예제 #22
0
 def __init__(self):
     super().__init__("null", DefaultDroneConfig())
    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
예제 #24
0
 def test_default_values(self):
     drone_config = DefaultDroneConfig()
     self.assertEqual(1, drone_config.speed_mps)
     self.assertEqual(90, drone_config.rotate_speed_dps)
     self.assertEqual(1, drone_config.takeoff_height_meters)