def test_load_robot_config(self): with patch('ev3dev2simulator.state.RobotState.get_robot_config' ) as get_robot_config_mock: get_robot_config_mock.return_value = { 'parts': [{ 'name': 'brick-left', 'type': 'brick', 'brick': '0', 'x_offset': '-39', 'y_offset': '-22.5' }] } state = RobotState(self.default_config()) self.assertEqual( len(state.parts), 4 + 1 + 2 + 1) # 4 sensors/actuators + 1 brick + 2 leds + 1 speaker config = { 'center_x': 0, 'center_y': 0, 'orientation': 180, 'name': 'test_bot', 'type': 'test_robot' } state = RobotState(config) self.assertEqual(4, len(state.parts)) # only a brick
def test_process_data_request(self): robot_state = RobotState() robot_state.values['right_brick:ev3-ports:in4'] = 10 robot_state.locks['right_brick:ev3-ports:in4'] = threading.Lock() message_processor = MessageProcessor('right_brick', robot_state) value = message_processor.process_data_request(DataRequest('ev3-ports:in4')) self.assertEqual(value, 10)
def test_add_robot(self): conf = TestRobotState.default_config() state = RobotState(conf) sidebar = Sidebar(100, 150, 200, 300) sidebar_sprite_mock = MagicMock() sidebar.init_robot(state.name, state.sensors, state.get_bricks(), [sidebar_sprite_mock]) self.assertEqual(len(sidebar.robot_info), 1)
def test_setters_getters(self): state = RobotState(self.default_config()) state.setup_pymunk_shapes(1) self.assertEqual(len(state.get_shapes()), 4 + 1 + 2 + 1) self.assertEqual( state.get_sensor((0, 'ev3-ports:in4')).get_ev3type(), 'ultrasonic_sensor') self.assertEqual(state.get_bricks()[0].name, 'brick-left') self.assertEqual(len(state.get_sensors()), 1) self.assertEqual(state.get_anchor().name, 'brick-left') del state.bricks[0] self.assertEqual(state.get_anchor(), None)
def test_create_jobs_right(self): robot_state = RobotState() message_processor = MessageProcessor('left_brick', robot_state) message_processor.process_rotate_command(RotateCommand('ev3-ports:outD', 10, 100, 'hold')) for i in range(300): c, l, r = robot_state.next_motor_jobs() self.assertIsNone(c) self.assertIsNone(l) self.assertAlmostEqual(r, 0.104, 3) self.assertEqual((None, None, None), robot_state.next_motor_jobs())
def test_create_jobs_center(self): robot_state = RobotState() message_processor = MessageProcessor('left_brick', robot_state) message_processor.process_rotate_command(RotateCommand('ev3-ports:outB', 20, 100, 'hold')) for i in range(150): c, l, r = robot_state.next_motor_jobs() self.assertAlmostEqual(c, -0.667, 3) self.assertIsNone(l) self.assertIsNone(r) self.assertEqual((None, None, None), robot_state.next_motor_jobs())
def test_get_value_and_locks(self): conf = TestRobotState.default_config() state = RobotState(conf) state.setup_pymunk_shapes(1) for arm in state.side_bar_sprites: arm.rotate_x = 0 # this is set in setup visuals arm.rotate_y = 0 # this is set in setup visuals sim = RobotSimulator(state) sim._sync_physics_sprites = MagicMock() val = sim.get_value((0, 'ev3-ports:in4')) self.assertEqual(val, 2550) self.assertEqual(sim.locks[(0, 'ev3-ports:in4')].locked(), True) sim.update() self.assertEqual(sim.locks[(0, 'ev3-ports:in4')].locked(), False)
def test_process_data_request(self): d = { 'type': 'DataRequest', 'address': 'ev3-ports:in4', } robot_state = RobotState() robot_state.values['left_brick:ev3-ports:in4'] = 10 robot_state.locks['left_brick:ev3-ports:in4'] = threading.Lock() server = ClientSocketHandler(robot_state, None, 'left_brick') data = server._process_data_request(d) val = self._deserialize(data) self.assertEqual(val, 10)
def test_process_sound_command(self): robot_state = RobotState() frames_per_second = get_config().get_data()['exec_settings']['frames_per_second'] frames = int(round((32 / 2.5) * frames_per_second)) message_processor = MessageProcessor('left_brick', robot_state) message_processor.process_sound_command(SoundCommand('A test is running at the moment!')) for i in range(frames - 1): self.assertIsNotNone(robot_state.next_sound_job()) message = robot_state.next_sound_job() self.assertEqual(message, 'A test is \nrunning at\n the momen\nt!') self.assertIsNone(robot_state.next_sound_job())
def test_constructor_and_reset(self): conf = TestRobotState.default_config() state = RobotState(conf) state.setup_pymunk_shapes(1) for arm in state.side_bar_sprites: arm.rotate_x = 0 # this is set in setup visuals arm.rotate_y = 0 # this is set in setup visuals sim = RobotSimulator(state) sim._sync_physics_sprites = MagicMock() sim.update() sim._sync_physics_sprites.assert_called_once() sim.should_reset = True self.assertEqual(sim.should_reset, True) sim.reset() self.assertEqual(sim.should_reset, False) sim.release_locks()
def test_add_robot_info(self): conf = TestRobotState.default_config() state = RobotState(conf) sidebar = Sidebar(100, 150, 200, 300) sidebar_sprite_mock = MagicMock() state.values[(0, 'ev3-ports:in4')] = 5 state.sounds[(0, 'speaker')] = 'test_sound' sidebar.init_robot(state.name, state.sensors, state.get_bricks(), [sidebar_sprite_mock]) sidebar.add_robot_info(state.name, state.values, state.sounds) self.assertEqual( sidebar.robot_info[state.name][(0, 'ev3-ports:in4')]['value'], 5) self.assertEqual( sidebar.robot_info[state.name][(0, 'speaker')]['value'], 'test_sound')
def test_set_obstacles(self): config = self.default_config().copy() config['parts'].append({ 'name': 'color_test_sensor', 'type': 'color_sensor', 'x_offset': 0, 'y_offset': 81, 'brick': 0, 'port': 'ev3-ports:in2' }) config['parts'].append({ 'name': 'touch_test_sensor', 'type': 'touch_sensor', 'side': 'left', 'x_offset': 0, 'y_offset': -81, 'brick': 0, 'port': 'ev3-ports:in3' }) state = RobotState(config) state.setup_pymunk_shapes(1) for arm in state.side_bar_sprites: arm.rotate_x = 0 # this is set in setup visuals color_obstacle_mock = MagicMock() rock_mock = MagicMock() lake_mock = MagicMock() state.set_color_obstacles([color_obstacle_mock]) state.set_touch_obstacles([rock_mock]) state.set_falling_obstacles([lake_mock]) self.assertEqual( state.sensors[(0, 'ev3-ports:in3')].sensible_obstacles, [rock_mock]) self.assertEqual( state.sensors[(0, 'ev3-ports:in2')].sensible_obstacles, [color_obstacle_mock]) self.assertEqual( state.sensors[(0, 'ev3-ports:in4')].sensible_obstacles, [lake_mock]) self.assertEqual( state.actuators[(0, 'ev3-ports:outA')].sensible_obstacles, [lake_mock])
def test_process_sound_command(self): d = { 'type': 'SoundCommand', 'message': 'A test is running at the moment!', } frames_per_second = get_config().get_data( )['exec_settings']['frames_per_second'] frames = int(round((32 / 2.5) * frames_per_second)) robot_state = RobotState() server = ClientSocketHandler(robot_state, None, 'left_brick') server._process_sound_command(d) for i in range(frames): self.assertIsNotNone(robot_state.next_sound_job()) self.assertIsNone(robot_state.next_sound_job())
def test_create_jobs_coast_center(self): coasting_sub = get_config().get_data()['motor_settings']['degree_coasting_subtraction'] robot_state = RobotState() message_processor = MessageProcessor('left_brick', robot_state) message_processor.process_rotate_command(RotateCommand('ev3-ports:outB', 80, 200, 'coast')) for i in range(75): c, l, r = robot_state.next_motor_jobs() self.assertAlmostEqual(c, -2.667, 3) self.assertIsNone(l) self.assertIsNone(r) ppf = 2.667 - coasting_sub for i in range(2): c, l, r = robot_state.next_motor_jobs() self.assertAlmostEqual(c, -ppf, 3) self.assertIsNone(l) self.assertIsNone(r) ppf = max(ppf - coasting_sub, 0) self.assertEqual((None, None, None), robot_state.next_motor_jobs())
def test_create_jobs_coast_left(self): coasting_sub = apply_scaling(get_config().get_data()['motor_settings']['pixel_coasting_subtraction']) robot_state = RobotState() message_processor = MessageProcessor('left_brick', robot_state) message_processor.process_rotate_command(RotateCommand('ev3-ports:outA', 80, 200, 'coast')) for i in range(75): c, l, r = robot_state.next_motor_jobs() self.assertIsNone(c) self.assertAlmostEqual(l, 0.833, 3) self.assertIsNone(r) ppf = 0.833 - coasting_sub for i in range(2): c, l, r = robot_state.next_motor_jobs() self.assertIsNone(c) self.assertAlmostEqual(l, ppf, 3) self.assertIsNone(r) ppf = max(ppf - coasting_sub, 0) self.assertEqual((None, None, None), robot_state.next_motor_jobs())
def test_determine_port(self): conf = TestRobotState.default_config() state = RobotState(conf) sim = RobotSimulator(state) # nothing given kwargs = {} port = sim.determine_port(0, kwargs, '') self.assertEqual(port, 'dev_not_connected') # no class given kwargs = {'address': 'ev3-ports:in4'} port = sim.determine_port(0, kwargs, '') self.assertEqual(port, 'dev_not_connected') # single class and address given kwargs = {'address': 'ev3-ports:in4', 'driver_name': 'lego-ev3-us'} port = sim.determine_port(0, kwargs, '') self.assertEqual(port, 'ev3-ports:in4') # single class and None address given kwargs = {'address': None, 'driver_name': 'lego-ev3-us'} port = sim.determine_port(0, kwargs, '') self.assertEqual(port, 'ev3-ports:in4') # multiple classes and address given kwargs = { 'address': 'ev3-ports:outA', 'driver_name': ['lego-ev3-l-motor', 'lego-nxt-motor'] } port = sim.determine_port(0, kwargs, '') self.assertEqual(port, 'ev3-ports:outA') # single class and invalid address given kwargs = {'address': 'ev3-ports:in3', 'driver_name': 'lego-ev3-us'} port = sim.determine_port(0, kwargs, '') self.assertEqual(port, 'dev_not_connected') # multiple classes and invalid address given kwargs = { 'address': 'ev3-ports:outC', 'driver_name': ['lego-ev3-l-motor', 'lego-nxt-motor'] } port = sim.determine_port(0, kwargs, '') self.assertEqual(port, 'dev_not_connected') # leds kwargs = {'address': None, 'driver_name': None} port = sim.determine_port(0, kwargs, 'leds') self.assertEqual(port, 'leds_addr')
def test_process_stop_command(self): d = { 'type': 'StopCommand', 'address': 'ev3-ports:outD', 'speed': 100, 'stop_action': 'coast' } robot_state = RobotState() server = ClientSocketHandler(robot_state, None, 'left_brick') data = server._process_stop_command(d) val = self._deserialize(data) self.assertAlmostEqual(0.0667, val, 3)
def test_reset(self): state = RobotState(self.default_config()) state.setup_pymunk_shapes(1) for arm in state.side_bar_sprites: arm.rotate_x = 0 # this is set in setup visuals arm.rotate_y = 0 # this is set in setup visuals x = state.x * state.scale y = state.y * state.scale state.body.position = Vec2d(5, 5) # 5 per sec state._move_position(Vec2d(5, 5)) self.assertEqual(state.body.position, Vec2d(5, 5)) self.assertEqual(state.body.velocity, Vec2d(5 * 30, 5 * 30)) state.reset() self.assertEqual(state.body.position, Vec2d(x, y)) self.assertEqual(state.body.velocity, Vec2d(0, 0))
def test_process_drive_command_pixels(self): d = { 'type': 'RotateCommand', 'address': 'ev3-ports:outB', 'speed': 10, 'distance': 100, 'stop_action': 'hold' } robot_state = RobotState() server = ClientSocketHandler(robot_state, None, 'left_brick') data = server._process_drive_command(d) val = self._deserialize(data) self.assertEqual(10, val)
def test_execute_movement(self): state = RobotState(self.default_config()) state.setup_pymunk_shapes(1) for arm in state.side_bar_sprites: arm.rotate_x = 0 # this is set in setup visuals arm.rotate_y = 0 # this is set in setup visuals state.execute_movement(5, 5) self.assertAlmostEqual(tuple(state.body.velocity)[0], 0.0, 3) # no x movement self.assertAlmostEqual(tuple(state.body.velocity)[1], -5.0 * 30, 3) # -150 y distance per second self.assertEqual(state.body.angle, pi)
def test_process_right_led_command(self): robot_state = RobotState() message_processor = MessageProcessor('right_brick', robot_state) command1 = LedCommand('led0:red:brick-status', 1) command2 = LedCommand('led0:green:brick-status', 1) message_processor.process_led_command(command1) message_processor.process_led_command(command2) self.assertEqual(robot_state.right_brick_left_led_color, 0) self.assertEqual(robot_state.right_brick_right_led_color, 1) command3 = LedCommand('led1:red:brick-status', 1) command4 = LedCommand('led1:green:brick-status', 0.5) message_processor.process_led_command(command3) message_processor.process_led_command(command4) self.assertEqual(robot_state.right_brick_left_led_color, 0) self.assertEqual(robot_state.right_brick_right_led_color, 4)
def test_setup_pymunk_shapes(self): state = RobotState(self.default_config()) state.setup_pymunk_shapes(1) self.assertEqual(state.wheel_distance, 120) self.assertEqual( len(state.shapes), 4 + 1 + 2 + 1) # 4 sensors/actuators + 1 brick + 2 leds + 1 speaker self.assertEqual(state.body.angle, pi) config_with_one_wheel = self.default_config().copy() del config_with_one_wheel['parts'][1] config_with_one_wheel['parts'].append({ 'name': 'invalid_sensor', 'type': 'type_that_does_not_exist', }) state = RobotState(config_with_one_wheel) self.assertRaises(RuntimeError, state.setup_pymunk_shapes, 1)
def test_arm_movement(self): config = self.default_config().copy() config['parts'].append({ 'name': 'measurement-probe', 'type': 'arm', 'x_offset': 15, 'y_offset': 102, 'brick': 0, 'port': 'ev3-ports:outB' }) with patch('ev3dev2simulator.robotpart.Arm.ArmLarge') as ArmLargeMock: arm_instance = ArmLargeMock.return_value state = RobotState(self.default_config()) state.setup_pymunk_shapes(1) state.execute_arm_movement((0, 'ev3-ports:outB'), 15) state.actuators[(0, 'ev3-ports:outB')].side_bar_arm.degrees = 15 self.assertEqual(len(arm_instance.mock_calls), 3) # first two have to do with a sprite list fn_name, args, kwargs = arm_instance.mock_calls[2] self.assertEqual(fn_name, 'rotate') self.assertEqual(args, (15, ))
def main(): """ Spawns the user thread and creates and starts the simulation. """ parser = argparse.ArgumentParser() parser.add_argument("-V", "--version", action='store_true', help="Show version info") parser.add_argument("-s", "--window_scaling", default=0.65, help="Scaling of the screen, default is 0.65", required=False, type=validate_scale) parser.add_argument( "-t", "--simulation_type", choices=['small', 'large'], default='small', help="Type of the simulation (small or large). Default is small", required=False, type=str) parser.add_argument( "-x", "--robot_position_x", default=200, help="Starting position x-coordinate of the robot, default is 200", required=False, type=validate_xy) parser.add_argument( "-y", "--robot_position_y", default=300, help="Starting position y-coordinate of the robot, default is 300", required=False, type=validate_xy) parser.add_argument("-o", "--robot_orientation", default=0, help="Starting orientation the robot, default is 0", required=False, type=int) parser.add_argument( "-c", "--connection_order_first", choices=['left', 'right'], default='left', help= "Side of the first brick to connect to the simulator, default is 'left'", required=False, type=str) parser.add_argument( "-2", "--show-on-second-monitor", action='store_true', help= "Show simulator window on second monitor instead, default is first monitor" ) parser.add_argument("-f", "--fullscreen", action='store_true', help="Show simulator fullscreen") parser.add_argument("-m", "--maximized", action='store_true', help="Show simulator maximized") args = vars(parser.parse_args()) if args['version'] == True: from ev3dev2 import version as apiversion from ev3dev2simulator import version as simversion print("version ev3dev2 : " + apiversion.__version__) print("version ev3dev2simulator : " + simversion.__version__) sys.exit(0) config = get_config() s = args['window_scaling'] config.write_scale(s) t = args['simulation_type'] config.write_sim_type(t) x = apply_scaling(args['robot_position_x']) y = apply_scaling(args['robot_position_y']) o = args['robot_orientation'] c = args['connection_order_first'] use_second_screen_to_show_simulator = args['show_on_second_monitor'] show_fullscreen = args['fullscreen'] show_maximized = args['maximized'] robot_state = RobotState() robot_pos = (x, y, o) sim = Simulator(robot_state, robot_pos, show_fullscreen, show_maximized, use_second_screen_to_show_simulator) #sim.setup() server_thread = ServerSocketDouble( robot_state, c) if t == 'large' else ServerSocketSingle(robot_state) server_thread.setDaemon(True) server_thread.start() arcade.run()