def __init__(self): self.azimuth = simactuators.PointToPointActuator( min_position=-45, max_position=45, speed=10, start_position=0 ) self.elevation = simactuators.PointToPointActuator( min_position=-69, max_position=45, speed=10, start_position=0 ) self.focus = simactuators.PointToPointActuator( min_position=0, max_position=13000, speed=200, start_position=0 ) self.mask_select = simactuators.PointToPointActuator( min_position=1, max_position=5, speed=1, start_position=1 ) self.mask_rotate = simactuators.CircularPointToPointActuator(speed=10)
async def test_set_position(self): await self.check_set_position(start_position=3, end_position=4) await self.check_set_position(start_position=2, end_position=-5) # A move to the start position should have no effect. pos = 1 actuator = simactuators.PointToPointActuator( min_position=pos - 1, max_position=pos + 1, start_position=pos, speed=2, ) duration = actuator.set_position(pos) self.assertEqual(duration, 0) self.assertEqual(actuator.start_position, pos) self.assertEqual(actuator.end_position, pos) self.assertEqual(actuator.start_tai, actuator.end_tai) self.assertFalse(actuator.moving(actuator.start_tai)) self.assertEqual(actuator.velocity(actuator.start_tai), 0) self.assertEqual(actuator.direction, simactuators.Direction.POSITIVE) # Check specifying an explicit start_tai; # pick a value different than the existing start_tai # so we can tell the difference. start_tai = actuator.start_tai + 5 actuator.set_position(position=1, start_tai=start_tai) self.assertEqual(actuator.start_tai, start_tai)
async def test_stop_default_tai(self): min_position = -10 max_position = 10 start_position = 3 # Slow motion so plenty of time to stop speed = 0.1 actuator = simactuators.PointToPointActuator( min_position=min_position, max_position=max_position, start_position=start_position, speed=speed, ) end_position = 4 duration = actuator.set_position(end_position) self.assertEqual(actuator.end_position, end_position) self.assertTrue(actuator.moving(salobj.current_tai())) # Let the actuator move for some arbitrary time # that is less than the remaining time sleep_time = 0.21 self.assertGreater(duration, sleep_time) await asyncio.sleep(sleep_time) tai0 = salobj.current_tai() actuator.stop() time_slop = salobj.current_tai() - tai0 self.assertAlmostEqual(tai0, actuator.end_tai, delta=time_slop) position_slop = time_slop * speed self.assertAlmostEqual(actuator.end_position, actuator.position(tai0), delta=position_slop) move_time = actuator.end_tai - actuator.start_tai self.assertGreater(move_time, 0) predicted_end_position = start_position + speed * move_time self.assertAlmostEqual(predicted_end_position, actuator.end_position, places=6) self.assertFalse(actuator.moving(actuator.end_tai)) self.assertEqual(actuator.position(actuator.end_tai), actuator.end_position) self.assertEqual(actuator.start_position, start_position) self.assertEqual(actuator.position(actuator.start_tai), actuator.start_position) # Stopping a stopped actuator should have no effect # except updating end_tai. Sleep long enough # that we can see the end_tai change. await asyncio.sleep(0.1) old_start_tai = actuator.start_tai old_pos = actuator.end_position tai0 = salobj.current_tai() actuator.stop() time_slop = salobj.current_tai() self.assertEqual(actuator.start_tai, old_start_tai) self.assertEqual(actuator.start_position, start_position) self.assertEqual(actuator.end_position, old_pos) self.assertAlmostEqual(actuator.end_tai, tai0, delta=time_slop)
def __init__( self, initial_state, initial_elevation=0, elevation_velocity=3, azimuth_velocity=3, azimuth_acceleration=1, ): self.elevation_actuator = simactuators.PointToPointActuator( min_position=0, max_position=90, speed=elevation_velocity, start_position=initial_elevation, ) self.azimuth_actuator = simactuators.CircularTrackingActuator( max_velocity=azimuth_velocity, max_acceleration=azimuth_acceleration, dtmax_track=0, ) self.telemetry_interval = 0.2 # seconds self.telemetry_loop_task = utils.make_done_future() self.elevation_done_task = utils.make_done_future() self.azimuth_done_task = utils.make_done_future() # Add do methods for unsupported commands for name in ( "closeLouvers", "closeShutter", "crawlAz", "crawlEl", "exitFault", "openShutter", "park", "resetDrivesAz", "setLouvers", "setOperationalMode", "setTemperature", "setZeroAz", ): setattr(self, f"do_{name}", self._unsupportedCommand) super().__init__(name="MTDome", index=None, initial_state=initial_state)
def __init__(self, base_positions, mirror_positions, pivot, min_length, max_length, speed): if len(base_positions) != 6: raise ValueError( f"base_positions={base_positions} must have 6 elements") for pos in base_positions: if len(pos) != 3: raise ValueError( f"base_positions={base_positions}; each item must have 3 elements" ) if len(mirror_positions) != 6: raise ValueError( f"mirror_positions={mirror_positions} must have 6 elements") for pos in mirror_positions: if len(pos) != 3: raise ValueError( f"mirror_positions={mirror_positions}; each item must have 3 elements" ) if len(pivot) != 3: raise ValueError(f"pivot={pivot} must have 3 elements") self.base_positions = [np.array(pos) for pos in base_positions] # Positions of mirror actuator ends and pivot at orientation zero. self.neutral_mirror_positions = [ np.array(pos) for pos in mirror_positions ] self.neutral_pivot = np.array(pivot, dtype=float) # Information commanded by the `move` command. self.cmd_pos = np.zeros(3, dtype=float) self.cmd_xyzrot = np.zeros(3, dtype=float) self.cmd_mirror_positions = [np.array(pos) for pos in mirror_positions] self.neutral_actuator_lengths = self.compute_actuator_lengths( mirror_positions=mirror_positions, absolute=True) self.actuators = [ simactuators.PointToPointActuator( min_position=min_length, max_position=max_length, start_position=0, speed=speed, ) for actuator_length in self.neutral_actuator_lengths ]
async def test_stop_specified_tai(self): min_position = -10 max_position = 10 start_position = 3 # Slow motion so plenty of time to stop speed = 0.1 actuator = simactuators.PointToPointActuator( min_position=min_position, max_position=max_position, start_position=start_position, speed=speed, ) end_position = 4 start_tai = 12.1 # arbitrary duration = actuator.set_position(end_position, start_tai=start_tai) self.assertEqual(actuator.end_position, end_position) predicted_duration = (end_position - start_position) / speed self.assertAlmostEqual(duration, predicted_duration) end_tai = start_tai + duration self.assertAlmostEqual(actuator.end_tai, end_tai) self.assertFalse(actuator.moving(tai=start_tai - 0.001)) self.assertFalse(actuator.moving(tai=end_tai + 0.001)) self.assertTrue(actuator.moving(tai=start_tai + 0.001)) self.assertTrue(actuator.moving(tai=end_tai - 0.001)) # Let the actuator move for some arbitrary time # that is less than the remaining time stop_dtime = 0.21 stop_tai = stop_dtime + start_tai self.assertGreater(duration, stop_dtime) actuator.stop(tai=stop_tai) self.assertEqual(actuator.end_tai, stop_tai) self.assertFalse(actuator.moving(tai=start_tai - 0.001)) self.assertFalse(actuator.moving(tai=stop_tai + 0.001)) self.assertTrue(actuator.moving(tai=start_tai + 0.001)) self.assertTrue(actuator.moving(tai=stop_tai - 0.001)) self.assertEqual(actuator.end_tai, stop_tai) predicted_end_position = start_position + speed * stop_dtime self.assertAlmostEqual(actuator.end_position, predicted_end_position)
def __init__( self, port, door_time=1, az_vel=6, home_az=10, home_az_overshoot=1, home_az_vel=1, ): # If port == 0 then this will be updated to the actual port # in `start`, right after the TCP/IP server is created. self.port = port self.door_time = door_time self.az_vel = az_vel self.high_speed = 5 self.coast = 0.5 self.tolerance = 1.0 self.home_az = home_az self.home_az_overshoot = home_az_overshoot self.home_az_vel = home_az_vel self.encoder_counts_per_360 = 4018143232 self.az_actuator = simactuators.CircularPointToPointActuator( speed=az_vel) self.az_move_timeout = 120 self.watchdog_reset_time = 600 self.dropout_timer = 5 self.reverse_delay = 4 self.main_door_encoder_closed = 118449181478 self.main_door_encoder_opened = 8287616388 self.dropout_door_encoder_closed = 5669776578 self.dropout_door_encoder_opened = 5710996184 self.door_move_timeout = 360 self.door_actuators = { enum: simactuators.PointToPointActuator( min_position=0, max_position=100, start_position=0, speed=100 / door_time, ) for enum in Door } self._homing_task = salobj.make_done_future() self.rain_sensor_enabled = True self.rain_detected = False self.cloud_sensor_enabled = True self.clouds_detected = False self.scb_link_ok = True self.auto_shutdown_enabled = False self.estop_active = False # Name of a command to report as failed once, the next time it is seen, # or None if no failures. Used to test CSC handling of failed commands. self.fail_command = None self.last_rot_right = None self.log = logging.getLogger("MockDomeController") self.server = None # Dict of command: (has_argument, function). # The function is called with: # * No arguments, if `has_argument` False. # * The argument as a string, if `has_argument` is True. self.dispatch_dict = { "?": (False, self.do_short_status), "+": (False, self.do_full_status), "ST": (False, self.do_stop), "CL": (False, functools.partial(self.do_close_doors, Door.Main)), "OP": (False, functools.partial(self.do_open_doors, Door.Main)), "UP": (False, functools.partial(self.do_close_doors, Door.Dropout)), "DN": (False, functools.partial(self.do_open_doors, Door.Dropout)), "SC": ( False, functools.partial(self.do_close_doors, Door.Main | Door.Dropout), ), "SO": ( False, functools.partial(self.do_open_doors, Door.Main | Door.Dropout), ), "HM": (False, self.do_home), "MV": (True, self.do_set_cmd_az), }
def test_constructor(self): min_position = -1 max_position = 5 start_position = 3 speed = 1.5 for good_start_position in (start_position, min_position, max_position): with self.subTest(good_start_position=good_start_position): tai0 = salobj.current_tai() actuator = simactuators.PointToPointActuator( min_position=min_position, max_position=max_position, start_position=good_start_position, speed=speed, ) time_slop = salobj.current_tai() - tai0 self.assertAlmostEqual(actuator.start_tai, tai0, delta=time_slop) self.assertEqual(actuator.start_position, good_start_position) self.assertEqual(actuator.end_position, actuator.start_position) self.assertEqual(actuator.start_tai, actuator.end_tai) self.assertEqual(actuator.direction, simactuators.Direction.POSITIVE) for dt in (-1, 0, 1): tai = actuator.start_tai + dt self.assertEqual(actuator.position(tai), good_start_position) self.assertEqual(actuator.velocity(tai), 0) self.assertFalse(actuator.moving(tai)) predicted_remaining_time = 0 if dt >= 0 else -dt self.assertAlmostEqual(actuator.remaining_time(tai), predicted_remaining_time) self.assertEqual(actuator.position(), good_start_position) self.assertEqual(actuator.velocity(), 0) self.assertFalse(actuator.moving()) self.assertEqual(actuator.remaining_time(), 0) for bad_min_position in (max_position, max_position + 0.001): with self.subTest(bad_min_position=bad_min_position): with self.assertRaises(ValueError): simactuators.PointToPointActuator( min_position=bad_min_position, max_position=max_position, start_position=max_position, speed=speed, ) for bad_max_position in (min_position, min_position - 0.001): with self.subTest(bad_max_position=bad_max_position): with self.assertRaises(ValueError): simactuators.PointToPointActuator( min_position=min_position, max_position=bad_max_position, start_position=min_position, speed=speed, ) for bad_pos in (min_position - 0.001, max_position + 0.001): with self.subTest(bad_pos=bad_pos): with self.assertRaises(ValueError): simactuators.PointToPointActuator( min_position=min_position, max_position=max_position, start_position=bad_pos, speed=speed, ) for bad_speed in (0, -0.001): with self.subTest(bad_speed=bad_speed): with self.assertRaises(ValueError): simactuators.PointToPointActuator( min_position=min_position, max_position=max_position, start_position=start_position, speed=bad_speed, )
async def check_set_position(self, start_position, end_position): if start_position == end_position: raise ValueError("start_position must not equal end_position") min_position = min(start_position, end_position) - 1 max_position = max(start_position, end_position) + 1 # Make the move take a reasonable amount of time speed = 2.0 / abs(end_position - start_position) actuator = simactuators.PointToPointActuator( min_position=min_position, max_position=max_position, start_position=start_position, speed=speed, ) # Sleep a bit so actuator.start_tai will change by a noticeable amount # as a result of the set_position command. await asyncio.sleep(0.1) # Keep track of how long it takes to call `set_position` # so we know how picky to be when testing `start_tai`. tai0 = salobj.current_tai() duration = actuator.set_position(end_position) remaining_time = actuator.remaining_time() time_slop = salobj.current_tai() - tai0 predicted_duration = abs(end_position - start_position) / speed self.assertAlmostEqual(duration, predicted_duration) self.assertAlmostEqual(actuator.start_tai, tai0, delta=time_slop) self.assertAlmostEqual(remaining_time, duration, delta=time_slop) self.assertAlmostEqual(actuator.start_tai + predicted_duration, actuator.end_tai, places=6) self.assertEqual(actuator.end_position, end_position) predicted_direction = (simactuators.Direction.POSITIVE if end_position >= start_position else simactuators.Direction.NEGATIVE) self.assertEqual(actuator.direction, predicted_direction) predicted_speed = speed * predicted_direction # The actuator should not be moving before or after the move # (but remaining_time is > 0 before the move). for tai in ( actuator.start_tai - 1, actuator.start_tai, actuator.end_tai, actuator.end_tai + 1, ): self.assertFalse(actuator.moving(tai)) self.assertEqual(actuator.velocity(tai), 0) predicted_remaining_time = (actuator.end_tai - tai if actuator.end_tai > tai else 0) self.assertAlmostEqual(actuator.remaining_time(tai), predicted_remaining_time) # The actuator should be moving during the move. for tai in ( actuator.start_tai + 0.001, (actuator.start_tai + actuator.end_tai) / 2, actuator.end_tai - 0.001, ): self.assertTrue(actuator.moving(tai)) self.assertEqual(actuator.velocity(tai), predicted_speed) predicted_remaining_time = actuator.end_tai - tai self.assertAlmostEqual(actuator.remaining_time(tai), predicted_remaining_time) # Try moving out of bounds. for bad_end_position in ( min_position - 1, min_position - 0.0001, max_position + 0.0001, max_position + 1, ): with self.assertRaises(ValueError): actuator.set_position(bad_end_position)