def __init__(self, cmd, shell=False, window=None, initial_state=None, respawn=True, env=None): """ respawn handles whether or not the application shall be automatically respawned at all, default is True. """ self.cmd = cmd self.window = window self.lock = threading.Lock() self.proc = ProcController(cmd, spawn_hooks=[self._handle_respawn], shell=shell, respawn=respawn, env=env) self._respawn_handlers = [] self._state_handlers = [] if initial_state and is_valid_state(initial_state): rospy.logdebug('setting initial state to %s' % initial_state) self.state = initial_state self.set_state(self.state) else: self.state = ApplicationState.STOPPED self.post_init()
class TestCaptureViewport(unittest.TestCase): def setUp(self): display = CAPTURE_DISPLAY geometry = ManagedWindow.lookup_viewport_geometry(CAPTURE_VIEWPORT) geometry_str = '{}x{}x24'.format(CAPTURE_WIDTH, CAPTURE_HEIGHT) self.image_capture = TopicCapture() self.image_capture_sub = rospy.Subscriber(CAPTURE_TOPIC, CompressedImage, self.image_capture.handle_msg) # Run an Xvfb with the configured DISPLAY and geometry matching the # viewport exactly. self.xvfb = ProcController( ['Xvfb', display, '-noreset', '-screen', '0', geometry_str] ) self.xvfb.start() rospy.sleep(3.0) self.pub = rospy.Publisher('/director/scene', GenericMessage, queue_size=100) rospy.sleep(1.0) def tearDown(self): self.pub.publish(BLANK_SCENE) rospy.sleep(0.5) self.xvfb.stop() def test_capture_image_and_info(self): self.assertEqual(0, len(self.image_capture.msgs)) self.pub.publish(CAPTURE_SCENE) rospy.sleep(1.0) first_image = self.image_capture.msgs[0] first_image_data = StringIO(first_image.data) first_image_jpeg = Image.open(first_image_data) self.assertEqual(CAPTURE_WIDTH, first_image_jpeg.size[0]) self.assertEqual(CAPTURE_HEIGHT, first_image_jpeg.size[1]) # Now try a different viewport size. self.pub.publish(HALF_SCALE_SCENE) rospy.sleep(1.0) last_image = self.image_capture.msgs[-1] last_image_data = StringIO(last_image.data) last_image_jpeg = Image.open(last_image_data) self.assertEqual(CAPTURE_WIDTH / 2, last_image_jpeg.size[0]) self.assertEqual(CAPTURE_HEIGHT / 2, last_image_jpeg.size[1]) # We shouldn't get any more images after publishing blank scene. self.pub.publish(BLANK_SCENE) rospy.sleep(1.0) num_images = len(self.image_capture.msgs) rospy.sleep(1.0) self.assertEqual(num_images, len(self.image_capture.msgs))
class CaptureWebcam: def __init__(self, janus_host, janus_port, device, width, height, framerate, max_quantizer, target_bitrate, flip=False): self.janus_host = janus_host self.janus_port = janus_port self.device = device self.width = width self.height = height self.framerate = framerate self.max_quantizer = max_quantizer self.target_bitrate = target_bitrate self.proc = None self.lock = threading.Lock() if flip: self.flip_method = 'rotate-180' else: self.flip_method = 'none' self.cmd = self._get_cmd() def _get_cmd(self): caps = 'video/x-raw' if self.width is not None: caps += ',width={}'.format(self.width) if self.height is not None: caps += ',height={}'.format(self.height) if self.framerate is not None: caps += ',framerate={}/1'.format(self.framerate) args = map(lambda arg: arg.format( device=self.device, caps=caps, max_quantizer=self.max_quantizer, target_bitrate=self.target_bitrate, addr=self.janus_host, port=self.janus_port, flip_method=self.flip_method, ), CAPTURE_ARGS) cmd = [CAPTURE_CMD] cmd.extend(args) return cmd def start_capture(self): self.end_capture() self.proc = ProcController(self.cmd) self.proc.start() def end_capture(self): if self.proc is not None: self.proc.stop()
def test_respawn_flag_forward_to_runner(self): """ Test forwarding of the respawn flag setting and associated behaviour. Both for True (default value) and False from the higher-lever proc_controller POV. """ self.assertTrue(self.controller.respawn, "The 'respawn' flag is by default True.") self.controller.start() self.assertTrue(self.controller.watcher.respawn, "The 'respawn' flag is by default True.") self.controller.stop() time.sleep(0.1) self.controller = ProcController(TEST_CMD, respawn=False) self.assertFalse(self.controller.respawn, "The 'respawn' flag is False now.") self.controller.start() self.assertFalse(self.controller.watcher.respawn, "The 'respawn' flag is False now.") self.controller.stop() time.sleep(0.1)
def setUp(self): display = CAPTURE_DISPLAY geometry = ManagedWindow.lookup_viewport_geometry(CAPTURE_VIEWPORT) geometry_str = '{}x{}x24'.format(CAPTURE_WIDTH, CAPTURE_HEIGHT) self.image_capture = TopicCapture() self.image_capture_sub = rospy.Subscriber(CAPTURE_TOPIC, CompressedImage, self.image_capture.handle_msg) # Run an Xvfb with the configured DISPLAY and geometry matching the # viewport exactly. self.xvfb = ProcController( ['Xvfb', display, '-noreset', '-screen', '0', geometry_str] ) self.xvfb.start() rospy.sleep(3.0) self.pub = rospy.Publisher('/director/scene', GenericMessage, queue_size=100) rospy.sleep(1.0)
class ManagedApplication(object): def __init__(self, cmd, shell=False, window=None, initial_state=None, respawn=True, env=None): """ respawn handles whether or not the application shall be automatically respawned at all, default is True. """ self.cmd = cmd self.window = window self.lock = threading.Lock() self.proc = ProcController(cmd, spawn_hooks=[self._handle_respawn], shell=shell, respawn=respawn, env=env) self._respawn_handlers = [] self._state_handlers = [] if initial_state and is_valid_state(initial_state): rospy.logdebug('setting initial state to %s' % initial_state) self.state = initial_state self.set_state(self.state) else: self.state = ApplicationState.STOPPED self.post_init() def close(self): """ End this ManagedApplication immediately. After closing, the instance can no longer be used. """ self.set_state(ApplicationState.STOPPED) del self.lock del self.cmd del self.state del self.proc del self.window del self._respawn_handlers[:] del self._state_handlers[:] def post_init(self): pass def add_respawn_handler(self, handler): self._respawn_handlers.append(handler) def add_state_handler(self, handler): self._state_handlers.append(handler) def __str__(self): string_representation = "<ManagedApplication: state: %s, window: %s, cmd: %s, proc: %s" % (self.state, self.window, self.cmd, self.proc) return string_representation def __repr__(self): return self.__str__() def get_state(self): with self.lock: return self.state def set_state(self, state): with self.lock: state_changed = False if state != self.state: state_changed = True self.state = state if state == ApplicationState.STOPPED: rospy.logdebug("STOPPED") self.proc.stop() if self.window is not None: self.window.set_visibility(False) elif state == ApplicationState.SUSPENDED: rospy.logdebug("SUSPENDED") if self.window is not None: self.window.set_visibility(False) self.window.converge() self.proc.start() elif state == ApplicationState.HIDDEN: rospy.logdebug("HIDDEN") if self.window is not None: self.window.set_visibility(False) self.window.converge() else: rospy.logwarn( 'Tried to hide a ManagedApplication ' + 'without a ManagedWindow' ) self.proc.start() elif state == ApplicationState.STARTED: rospy.loginfo("STARTED") if self.window is not None: self.window.set_visibility(False) self.window.converge() self.proc.start() elif state == ApplicationState.VISIBLE: rospy.loginfo("VISIBLE") if self.window is not None: self.window.set_visibility(True) self.window.converge() self.proc.start() def run_handler(handler): try: handler(state) except Exception as e: rospy.logerr('caught an Exception in a state change handler') rospy.logerr(e.message) map(run_handler, self._state_handlers) def handle_state_msg(self, msg): rospy.logdebug('Got state message: {}'.format(msg)) self.set_state(msg.state) # TODO(mv): hook this up to ProcController def _handle_respawn(self): def run_handler(handler): try: handler() except Exception as e: rospy.logerr('caught an Exception in a respawn handler') rospy.logerr(e.message) map(run_handler, self._respawn_handlers) def handle_soft_relaunch(self, *args, **kwargs): rospy.logdebug('managed application relaunch...') self.proc.handle_soft_relaunch()
def start_capture(self): self.end_capture() self.proc = ProcController(self.cmd) self.proc.start()
#!/usr/bin/env python import rospy from appctl_support import ProcController from appctl_support import ModeHandler if __name__ == '__main__': rospy.init_node('spin_test_node', anonymous=True) modes = ['test_appctl'] cmd = ['/bin/sh', '-c', 'sleep 23023023'] proc_controller = ProcController(cmd) mode_handler = ModeHandler(modes, proc_controller) mode_handler.begin_handling_modes() rospy.spin() # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
def setUp(self): self.controller = ProcController(TEST_CMD)
class TestProcController(unittest.TestCase): def setUp(self): self.controller = ProcController(TEST_CMD) def tearDown(self): self.controller.stop() def test_start_and_stop(self): self.assertFalse(self.controller.started, 'Controller must not start on init') self.controller.start() self.assertTrue(self.controller.started, 'Controller must be started on start()') # Wait for the process to spin up time.sleep(0.1) watcher = self.controller.watcher self.assertTrue(watcher.is_alive(), 'Process watcher must be alive while started') self.controller.stop() self.assertFalse(self.controller.started, 'Controller must not be started after stop()') watcher.join() self.assertFalse(watcher.is_alive(), 'Process watcher must not be alive after joined') def test_concurrency(self): flippers = [StateFlipper(self.controller) for i in range(NUM_THREADS_TO_TEST)] map(lambda f: f.start(), flippers) map(lambda f: f.join(), flippers) self.assertFalse(any([f.failed for f in flippers])) def test_redundant_start(self): self.controller.start() watcher = self.controller.watcher self.controller.start() self.assertIs(watcher, self.controller.watcher, 'Stray process watcher on redundant start') def test_respawn_flag_forward_to_runner(self): """ Test forwarding of the respawn flag setting and associated behaviour. Both for True (default value) and False from the higher-lever proc_controller POV. """ self.assertTrue(self.controller.respawn, "The 'respawn' flag is by default True.") self.controller.start() self.assertTrue(self.controller.watcher.respawn, "The 'respawn' flag is by default True.") self.controller.stop() time.sleep(0.1) self.controller = ProcController(TEST_CMD, respawn=False) self.assertFalse(self.controller.respawn, "The 'respawn' flag is False now.") self.controller.start() self.assertFalse(self.controller.watcher.respawn, "The 'respawn' flag is False now.") self.controller.stop() time.sleep(0.1)
class TestCleanup(unittest.TestCase): def setUp(self): self.controller = ProcController(TEST_CMD) def test_cleanup_started(self): c_ref = weakref.ref(self.controller) self.controller.start() w_ref = weakref.ref(self.controller.watcher) self.controller = None gc.collect() self.assertIsNone(c_ref()) time.sleep(0.5) self.assertIsNone(w_ref()) def test_cleanup_stopped(self): c_ref = weakref.ref(self.controller) self.controller.start() w_ref = weakref.ref(self.controller.watcher) time.sleep(0.5) self.controller.stop() self.controller = None gc.collect() self.assertIsNone(c_ref()) time.sleep(0.5) self.assertIsNone(w_ref()) def test_close_started(self): c_ref = weakref.ref(self.controller) self.controller.start() w_ref = weakref.ref(self.controller.watcher) self.controller.close() gc.collect() time.sleep(0.5) self.assertIsNone(w_ref()) def test_close_stopped(self): c_ref = weakref.ref(self.controller) self.controller.start() w_ref = weakref.ref(self.controller.watcher) time.sleep(0.5) self.controller.stop() self.controller.close() gc.collect() time.sleep(0.5) self.assertIsNone(w_ref())
class ManagedApplication(object): def __init__(self, cmd, shell=False, window=None, initial_state=None, respawn=True, env=None): """ respawn handles whether or not the application shall be automatically respawned at all, default is True. """ self.cmd = cmd self.window = window self.lock = threading.Lock() self.proc = ProcController(cmd, spawn_hooks=[self._handle_respawn], shell=shell, respawn=respawn, env=env) self._respawn_handlers = [] self._state_handlers = [] if initial_state and is_valid_state(initial_state): rospy.logdebug('setting initial state to %s' % initial_state) self.state = initial_state self.set_state(self.state) else: self.state = ApplicationState.STOPPED self.post_init() def close(self): """ End this ManagedApplication immediately. After closing, the instance can no longer be used. """ self.set_state(ApplicationState.STOPPED) del self.lock del self.cmd del self.state del self.proc del self.window del self._respawn_handlers[:] del self._state_handlers[:] def post_init(self): pass def add_respawn_handler(self, handler): self._respawn_handlers.append(handler) def add_state_handler(self, handler): self._state_handlers.append(handler) def __str__(self): string_representation = "<ManagedApplication: state: %s, window: %s, cmd: %s, proc: %s" % ( self.state, self.window, self.cmd, self.proc) return string_representation def __repr__(self): return self.__str__() def get_state(self): with self.lock: return self.state def set_state(self, state): with self.lock: state_changed = False if state != self.state: state_changed = True self.state = state if state == ApplicationState.STOPPED: rospy.logdebug("STOPPED") self.proc.stop() if self.window is not None: self.window.set_visibility(False) elif state == ApplicationState.SUSPENDED: rospy.logdebug("SUSPENDED") if self.window is not None: self.window.set_visibility(False) self.window.converge() self.proc.start() elif state == ApplicationState.HIDDEN: rospy.logdebug("HIDDEN") if self.window is not None: self.window.set_visibility(False) self.window.converge() else: rospy.logwarn('Tried to hide a ManagedApplication ' + 'without a ManagedWindow') self.proc.start() elif state == ApplicationState.STARTED: rospy.loginfo("STARTED") if self.window is not None: self.window.set_visibility(False) self.window.converge() self.proc.start() elif state == ApplicationState.VISIBLE: rospy.loginfo("VISIBLE") if self.window is not None: self.window.set_visibility(True) self.window.converge() self.proc.start() def run_handler(handler): try: handler(state) except Exception as e: rospy.logerr( 'caught an Exception in a state change handler') rospy.logerr(e.message) map(run_handler, self._state_handlers) def handle_state_msg(self, msg): rospy.logdebug('Got state message: {}'.format(msg)) self.set_state(msg.state) # TODO(mv): hook this up to ProcController def _handle_respawn(self): def run_handler(handler): try: handler() except Exception as e: rospy.logerr('caught an Exception in a respawn handler') rospy.logerr(e.message) map(run_handler, self._respawn_handlers) def handle_soft_relaunch(self, *args, **kwargs): rospy.logdebug('managed application relaunch...') self.proc.handle_soft_relaunch()