class TestAudioManager(unittest.TestCase): def setUp(self): self.tb = gr.top_block() self.p = AudioManager( graph=self.tb, audio_config=None, stereo=False) def test_smoke(self): rs = self.p.reconnecting() rs.input(ConnectionCanarySource(self.tb), 10000, 'client') rs.finish_bus_connections() self.tb.start() self.tb.stop() self.tb.wait() def test_wrong_dest_name(self): """ Shouldn't fail to construct a valid flow graph, despite the bad name. """ rs = self.p.reconnecting() rs.input(ConnectionCanarySource(self.tb), 10000, 'bogusname') rs.finish_bus_connections() self.tb.start() self.tb.stop() self.tb.wait()
def __init__(self, devices={}, audio_config=None, features=_STUB_FEATURES): # pylint: disable=dangerous-default-value if len(devices) <= 0: raise ValueError('Must have at least one RF device') gr.top_block.__init__(self, type(self).__name__) self.__running = False # duplicate of GR state we can't reach, see __start_or_stop self.__has_a_useful_receiver = False # Configuration # TODO: device refactoring: Remove vestigial 'accessories' self._sources = CellDict({k: d for k, d in devices.iteritems() if d.can_receive()}) self._accessories = accessories = {k: d for k, d in devices.iteritems() if not d.can_receive()} for key in self._sources: # arbitrary valid initial value self.source_name = key break self.__rx_device_type = EnumT({k: v.get_name() or k for (k, v) in self._sources.iteritems()}) # Audio early setup self.__audio_manager = AudioManager( # must be before contexts graph=self, audio_config=audio_config, stereo=features['stereo']) # Blocks etc. # TODO: device refactoring: remove 'source' concept (which is currently a device) # TODO: remove legacy no-underscore names, maybe get rid of self.source self.source = None self.__monitor_rx_driver = None self.monitor = MonitorSink( signal_type=SignalType(sample_rate=10000, kind='IQ'), # dummy value will be updated in _do_connect context=Context(self)) self.monitor.get_interested_cell().subscribe2(self.__start_or_stop_later, the_subscription_context) self.__clip_probe = MaxProbe() # Receiver blocks (multiple, eventually) self._receivers = CellDict(dynamic=True) self._receiver_valid = {} # collections # TODO: No longer necessary to have these non-underscore names self.sources = CollectionState(CellDict(self._sources)) self.receivers = ReceiverCollection(self._receivers, self) self.accessories = CollectionState(CellDict(accessories)) self.__telemetry_store = TelemetryStore() # Flags, other state self.__needs_reconnect = [u'initialization'] self.__in_reconnect = False self.receiver_key_counter = 0 self.receiver_default_state = {} # Initialization def hookup_vfo_callback(k, d): # function so as to not close over loop variable d.get_vfo_cell().subscribe2(lambda value: self.__device_vfo_callback(k), the_subscription_context) for k, d in devices.iteritems(): hookup_vfo_callback(k, d) self._do_connect()
class Top(gr.top_block, ExportedState, RecursiveLockBlockMixin): def __init__(self, devices={}, audio_config=None, features=_STUB_FEATURES): # pylint: disable=dangerous-default-value if len(devices) <= 0: raise ValueError('Must have at least one RF device') gr.top_block.__init__(self, type(self).__name__) self.__running = False # duplicate of GR state we can't reach, see __start_or_stop self.__has_a_useful_receiver = False # Configuration # TODO: device refactoring: Remove vestigial 'accessories' self._sources = CellDict({k: d for k, d in devices.iteritems() if d.can_receive()}) self._accessories = accessories = {k: d for k, d in devices.iteritems() if not d.can_receive()} for key in self._sources: # arbitrary valid initial value self.source_name = key break self.__rx_device_type = EnumT({k: v.get_name() or k for (k, v) in self._sources.iteritems()}) # Audio early setup self.__audio_manager = AudioManager( # must be before contexts graph=self, audio_config=audio_config, stereo=features['stereo']) # Blocks etc. # TODO: device refactoring: remove 'source' concept (which is currently a device) # TODO: remove legacy no-underscore names, maybe get rid of self.source self.source = None self.__monitor_rx_driver = None self.monitor = MonitorSink( signal_type=SignalType(sample_rate=10000, kind='IQ'), # dummy value will be updated in _do_connect context=Context(self)) self.monitor.get_interested_cell().subscribe2(self.__start_or_stop_later, the_subscription_context) self.__clip_probe = MaxProbe() # Receiver blocks (multiple, eventually) self._receivers = CellDict(dynamic=True) self._receiver_valid = {} # collections # TODO: No longer necessary to have these non-underscore names self.sources = CollectionState(CellDict(self._sources)) self.receivers = ReceiverCollection(self._receivers, self) self.accessories = CollectionState(CellDict(accessories)) self.__telemetry_store = TelemetryStore() # Flags, other state self.__needs_reconnect = [u'initialization'] self.__in_reconnect = False self.receiver_key_counter = 0 self.receiver_default_state = {} # Initialization def hookup_vfo_callback(k, d): # function so as to not close over loop variable d.get_vfo_cell().subscribe2(lambda value: self.__device_vfo_callback(k), the_subscription_context) for k, d in devices.iteritems(): hookup_vfo_callback(k, d) self._do_connect() def state_def(self): for d in super(Top, self).state_def(): yield d yield 'clip_warning', self.__clip_probe.state()['clip_warning'] def add_receiver(self, mode, key=None, state=None): if len(self._receivers) >= 100: # Prevent storage-usage DoS attack raise Exception('Refusing to create more than 100 receivers') if key is not None: assert key not in self._receivers else: while True: key = base26(self.receiver_key_counter) self.receiver_key_counter += 1 if key not in self._receivers: break if len(self._receivers) > 0: arbitrary = self._receivers.itervalues().next() defaults = arbitrary.state_to_json() else: defaults = self.receiver_default_state combined_state = defaults.copy() for do_not_use_default in ['device_name', 'freq_linked_to_device']: if do_not_use_default in combined_state: del combined_state[do_not_use_default] if state is not None: combined_state.update(state) facet = ContextForReceiver(self, key) receiver = unserialize_exported_state(Receiver, kwargs=dict( mode=mode, audio_channels=self.__audio_manager.get_channels(), device_name=self.source_name, audio_destination=self.__audio_manager.get_default_destination(), # TODO match others context=facet, ), state=combined_state) facet._receiver = receiver self._receivers[key] = receiver self._receiver_valid[key] = False self.__needs_reconnect.append(u'added receiver ' + key) self._do_connect() # until _enabled, the facet ignores any reconnect/rebuild-triggering callbacks facet._enabled = True return (key, receiver) def delete_receiver(self, key): assert key in self._receivers receiver = self._receivers[key] # save defaults for use if about to become empty if len(self._receivers) == 1: self.receiver_default_state = receiver.state_to_json() del self._receivers[key] del self._receiver_valid[key] self.__needs_reconnect.append(u'removed receiver ' + key) self._do_connect() # TODO move these methods to a facet of AudioManager def add_audio_queue(self, queue, queue_rate): self.__audio_manager.add_audio_queue(queue, queue_rate) self.__needs_reconnect.append(u'added audio queue') self._do_connect() self.__start_or_stop() def remove_audio_queue(self, queue): self.__audio_manager.remove_audio_queue(queue) self.__start_or_stop() self.__needs_reconnect.append(u'removed audio queue') self._do_connect() def get_audio_queue_channels(self): """ Return the number of channels (which will be 1 or 2) in audio queue outputs. """ return self.__audio_manager.get_channels() def _do_connect(self): """Do all reconfiguration operations in the proper order.""" if self.__in_reconnect: raise Exception('reentrant reconnect or _do_connect crashed') self.__in_reconnect = True t0 = time.time() if self.source is not self._sources[self.source_name]: log.msg('Flow graph: Switching RF device to %s' % (self.source_name)) self.__needs_reconnect.append(u'switched device') this_source = self._sources[self.source_name] self.source = this_source self.state_changed('source') self.__monitor_rx_driver = this_source.get_rx_driver() monitor_signal_type = self.__monitor_rx_driver.get_output_type() self.monitor.set_signal_type(monitor_signal_type) self.monitor.set_input_center_freq(this_source.get_freq()) self.__clip_probe.set_window_and_reconnect(0.5 * monitor_signal_type.get_sample_rate()) if self.__needs_reconnect: log.msg(u'Flow graph: Rebuilding connections because: %s' % (', '.join(self.__needs_reconnect),)) self.__needs_reconnect = [] self._recursive_lock() self.disconnect_all() self.connect( self.__monitor_rx_driver, self.monitor) self.connect( self.__monitor_rx_driver, self.__clip_probe) # Filter receivers audio_rs = self.__audio_manager.reconnecting() n_valid_receivers = 0 has_non_audio_receiver = False for key, receiver in self._receivers.iteritems(): self._receiver_valid[key] = receiver.get_is_valid() if not self._receiver_valid[key]: continue if not self.__audio_manager.validate_destination(receiver.get_audio_destination()): log.err('Flow graph: receiver audio destination %r is not available' % (receiver.get_audio_destination(),)) continue n_valid_receivers += 1 if n_valid_receivers > 6: # Sanity-check to avoid burning arbitrary resources # TODO: less arbitrary constant; communicate this restriction to client log.err('Flow graph: Refusing to connect more than 6 receivers') break self.connect(self._sources[receiver.get_device_name()].get_rx_driver(), receiver) receiver_output_type = receiver.get_output_type() if receiver_output_type.get_sample_rate() <= 0: # Demodulator has no output, but receiver has a dummy output, so connect it to something to satisfy flow graph structure. self.connect(receiver, blocks.null_sink(gr.sizeof_float * self.__audio_manager.get_channels())) # Note that we have a non-audio receiver which may be useful even if there is no audio output has_non_audio_receiver = True else: assert receiver_output_type.get_kind() == 'STEREO' audio_rs.input(receiver, receiver_output_type.get_sample_rate(), receiver.get_audio_destination()) self.__has_a_useful_receiver = audio_rs.finish_bus_connections() or \ has_non_audio_receiver self._recursive_unlock() # (this is in an if block but it can't not execute if anything else did) log.msg('Flow graph: ...done reconnecting (%i ms).' % ((time.time() - t0) * 1000,)) self.__start_or_stop_later() self.__in_reconnect = False def __device_vfo_callback(self, device_key): reactor.callLater( self._sources[device_key].get_rx_driver().get_tune_delay(), self.__device_vfo_changed, device_key) def __device_vfo_changed(self, device_key): device = self._sources[device_key] freq = device.get_freq() if self.source is device: self.monitor.set_input_center_freq(freq) for rec_key, receiver in self._receivers.iteritems(): if receiver.get_device_name() == device_key: receiver.changed_device_freq() self._update_receiver_validity(rec_key) # TODO: If multiple receivers change validity we'll do redundant reconnects in this loop; avoid that. def _update_receiver_validity(self, key): receiver = self._receivers[key] if receiver.get_is_valid() != self._receiver_valid[key]: self.__needs_reconnect.append(u'receiver %s validity changed' % (key,)) self._do_connect() @exported_value(type=ReferenceT(), changes='never') def get_monitor(self): return self.monitor @exported_value(type=ReferenceT(), persists=False, changes='never') def get_sources(self): return self.sources @exported_value(type=ReferenceT(), persists=False, changes='explicit') def get_source(self): return self.source # TODO no need for this now...? @exported_value(type=ReferenceT(), changes='never') def get_receivers(self): return self.receivers # TODO the concept of 'accessories' is old and needs to go away, but we don't have a flexible enough UI to replace it with just devices since only one device can be looked-at at a time so far. @exported_value(type=ReferenceT(), persists=False, changes='never') def get_accessories(self): return self.accessories @exported_value(type=ReferenceT(), changes='never', label='Telemetry') def get_telemetry_store(self): return self.__telemetry_store def start(self, **kwargs): # pylint: disable=arguments-differ # trigger reconnect/restart notification self._recursive_lock() self._recursive_unlock() super(Top, self).start(**kwargs) self.__running = True def stop(self): super(Top, self).stop() self.__running = False def __start_or_stop(self): # TODO: Improve start/stop conditions: # # * run if a client is watching an audio-having receiver's cell-based outputs (e.g. VOR) but not listening to audio # # * don't run if no client is watching a pure telemetry receiver # (maybe a user preference since having a history when you connect is useful) # # Both of these refinements require becoming aware of cell subscriptions. should_run = ( self.__has_a_useful_receiver or self.monitor.get_interested_cell().get()) if should_run != self.__running: if should_run: self.start() else: self.stop() self.wait() def __start_or_stop_later(self, unused_subscription_value=None): reactor.callLater(0, self.__start_or_stop) def close_all_devices(self): """Close all devices in preparation for a clean shutdown. Makes this top block unusable""" for device in self._sources.itervalues(): device.close() for device in self._accessories.itervalues(): device.close() self.stop() self.wait() @exported_value( type_fn=lambda self: self.__rx_device_type, changes='this_setter', label='RF source') def get_source_name(self): return self.source_name @setter def set_source_name(self, value): if value == self.source_name: return if value not in self._sources: raise ValueError('Source %r does not exist' % (value,)) self.source_name = value self._do_connect() def _get_rx_device_type(self): """for ContextForReceiver only""" return self.__rx_device_type def _get_audio_destination_type(self): """for ContextForReceiver only""" return self.__audio_manager.get_destination_type() def _trigger_reconnect(self, reason): self.__needs_reconnect.append(reason) self._do_connect() def _recursive_lock_hook(self): for source in self._sources.itervalues(): source.notify_reconnecting_or_restarting()
def __init__(self, devices={}, audio_config=None, features=_STUB_FEATURES): # pylint: disable=dangerous-default-value if len(devices) <= 0: raise ValueError('Must have at least one RF device') gr.top_block.__init__(self, type(self).__name__) self.__running = False # duplicate of GR state we can't reach, see __start_or_stop self.__has_a_useful_receiver = False # Configuration # TODO: device refactoring: Remove vestigial 'accessories' self._sources = CellDict({k: d for k, d in six.iteritems(devices) if d.can_receive()}) self._accessories = accessories = {k: d for k, d in six.iteritems(devices) if not d.can_receive()} for key in self._sources: # arbitrary valid initial value self.source_name = key break self.__rx_device_type = EnumT({k: v.get_name() or k for (k, v) in six.iteritems(self._sources)}) # Audio early setup self.__audio_manager = AudioManager( # must be before contexts graph=self, audio_config=audio_config, stereo=features['stereo']) # Blocks etc. # TODO: device refactoring: remove 'source' concept (which is currently a device) # TODO: remove legacy no-underscore names, maybe get rid of self.source self.source = None self.__monitor_rx_driver = None self.monitor = MonitorSink( signal_type=SignalType(sample_rate=10000, kind='IQ'), # dummy value will be updated in _do_connect context=Context(self)) self.monitor.get_interested_cell().subscribe2(self.__start_or_stop_later, the_subscription_context) self.__clip_probe = MaxProbe() # Receiver blocks (multiple, eventually) self._receivers = CellDict(dynamic=True) self._receiver_valid = {} # collections # TODO: No longer necessary to have these non-underscore names self.sources = CollectionState(CellDict(self._sources)) self.receivers = ReceiverCollection(self._receivers, self) self.accessories = CollectionState(CellDict(accessories)) self.__telemetry_store = TelemetryStore() # Flags, other state self.__needs_reconnect = [u'initialization'] self.__in_reconnect = False self.receiver_key_counter = 0 self.receiver_default_state = {} # Initialization def hookup_vfo_callback(k, d): # function so as to not close over loop variable d.get_vfo_cell().subscribe2(lambda value: self.__device_vfo_callback(k), the_subscription_context) for k, d in six.iteritems(devices): hookup_vfo_callback(k, d) device_context = DeviceContext(self.__telemetry_store.receive) for device in six.itervalues(devices): device.attach_context(device_context) self._do_connect()
class Top(gr.top_block, ExportedState, RecursiveLockBlockMixin): __log = Logger() def __init__(self, devices={}, audio_config=None, features=_STUB_FEATURES): # pylint: disable=dangerous-default-value if len(devices) <= 0: raise ValueError('Must have at least one RF device') gr.top_block.__init__(self, type(self).__name__) self.__running = False # duplicate of GR state we can't reach, see __start_or_stop self.__has_a_useful_receiver = False # Configuration # TODO: device refactoring: Remove vestigial 'accessories' self._sources = CellDict({k: d for k, d in six.iteritems(devices) if d.can_receive()}) self._accessories = accessories = {k: d for k, d in six.iteritems(devices) if not d.can_receive()} for key in self._sources: # arbitrary valid initial value self.source_name = key break self.__rx_device_type = EnumT({k: v.get_name() or k for (k, v) in six.iteritems(self._sources)}) # Audio early setup self.__audio_manager = AudioManager( # must be before contexts graph=self, audio_config=audio_config, stereo=features['stereo']) # Blocks etc. # TODO: device refactoring: remove 'source' concept (which is currently a device) # TODO: remove legacy no-underscore names, maybe get rid of self.source self.source = None self.__monitor_rx_driver = None self.monitor = MonitorSink( signal_type=SignalType(sample_rate=10000, kind='IQ'), # dummy value will be updated in _do_connect context=Context(self)) self.monitor.get_interested_cell().subscribe2(self.__start_or_stop_later, the_subscription_context) self.__clip_probe = MaxProbe() # Receiver blocks (multiple, eventually) self._receivers = CellDict(dynamic=True) self._receiver_valid = {} # collections # TODO: No longer necessary to have these non-underscore names self.sources = CollectionState(CellDict(self._sources)) self.receivers = ReceiverCollection(self._receivers, self) self.accessories = CollectionState(CellDict(accessories)) self.__telemetry_store = TelemetryStore() # Flags, other state self.__needs_reconnect = [u'initialization'] self.__in_reconnect = False self.receiver_key_counter = 0 self.receiver_default_state = {} # Initialization def hookup_vfo_callback(k, d): # function so as to not close over loop variable d.get_vfo_cell().subscribe2(lambda value: self.__device_vfo_callback(k), the_subscription_context) for k, d in six.iteritems(devices): hookup_vfo_callback(k, d) device_context = DeviceContext(self.__telemetry_store.receive) for device in six.itervalues(devices): device.attach_context(device_context) self._do_connect() def state_def(self): for d in super(Top, self).state_def(): yield d yield 'clip_warning', self.__clip_probe.state()['clip_warning'] def add_receiver(self, mode, key=None, state=None): if len(self._receivers) >= 100: # Prevent storage-usage DoS attack raise Exception('Refusing to create more than 100 receivers') if key is not None: assert key not in self._receivers else: while True: key = base26(self.receiver_key_counter) self.receiver_key_counter += 1 if key not in self._receivers: break if len(self._receivers) > 0: arbitrary = six.itervalues(self._receivers).next() defaults = arbitrary.state_to_json() else: defaults = self.receiver_default_state combined_state = defaults.copy() for do_not_use_default in ['device_name', 'freq_linked_to_device']: if do_not_use_default in combined_state: del combined_state[do_not_use_default] if state is not None: combined_state.update(state) facet = ContextForReceiver(self, key) receiver = unserialize_exported_state(Receiver, kwargs=dict( mode=mode, audio_channels=self.__audio_manager.get_channels(), device_name=self.source_name, audio_destination=self.__audio_manager.get_default_destination(), # TODO match others context=facet, ), state=combined_state) facet._receiver = receiver self._receivers[key] = receiver self._receiver_valid[key] = False self.__needs_reconnect.append(u'added receiver ' + key) self._do_connect() # until _enabled, the facet ignores any reconnect/rebuild-triggering callbacks facet._enabled = True return (key, receiver) def delete_receiver(self, key): assert key in self._receivers receiver = self._receivers[key] # save defaults for use if about to become empty if len(self._receivers) == 1: self.receiver_default_state = receiver.state_to_json() del self._receivers[key] del self._receiver_valid[key] self.__needs_reconnect.append(u'removed receiver ' + key) self._do_connect() # TODO move these methods to a facet of AudioManager def add_audio_callback(self, callback, sample_rate): self.__audio_manager.add_audio_callback(callback, sample_rate) self.__needs_reconnect.append(u'added audio callback') self._do_connect() self.__start_or_stop() def remove_audio_callback(self, callback): self.__audio_manager.remove_audio_callback(callback) self.__start_or_stop() self.__needs_reconnect.append(u'removed audio callback') self._do_connect() def get_audio_callback_channels(self): """ Return the number of channels (which will be 1 or 2) in audio callback outputs. """ return self.__audio_manager.get_channels() def _do_connect(self): """Do all reconfiguration operations in the proper order.""" if self.__in_reconnect: raise Exception('reentrant reconnect or _do_connect crashed') self.__in_reconnect = True t0 = time.time() if self.source is not self._sources[self.source_name]: self.__log.info('Flow graph: Switching RF device to {device_name}', device_name=self.source_name) self.__needs_reconnect.append(u'switched device') this_source = self._sources[self.source_name] self.source = this_source self.state_changed('source') self.__monitor_rx_driver = this_source.get_rx_driver() monitor_signal_type = self.__monitor_rx_driver.get_output_type() self.monitor.set_signal_type(monitor_signal_type) self.monitor.set_input_center_freq(this_source.get_freq()) self.__clip_probe.set_window_and_reconnect(0.5 * monitor_signal_type.get_sample_rate()) if self.__needs_reconnect: self.__log.info(u'Flow graph: Rebuilding connections because: {reasons}', reasons=', '.join(self.__needs_reconnect)) self.__needs_reconnect = [] self._recursive_lock() self.disconnect_all() self.connect( self.__monitor_rx_driver, self.monitor) self.connect( self.__monitor_rx_driver, self.__clip_probe) # Filter receivers audio_rs = self.__audio_manager.reconnecting() n_valid_receivers = 0 has_non_audio_receiver = False for key, receiver in six.iteritems(self._receivers): self._receiver_valid[key] = receiver.get_is_valid() if not self._receiver_valid[key]: continue if not self.__audio_manager.validate_destination(receiver.get_audio_destination()): self.__log.info('Flow graph: receiver audio destination {audio_destination} is not available', audio_destination=receiver.get_audio_destination()) continue n_valid_receivers += 1 if n_valid_receivers > 6: # Sanity-check to avoid burning arbitrary resources # TODO: less arbitrary constant; communicate this restriction to client self.__log.info('Flow graph: Refusing to connect more than 6 receivers') break self.connect(self._sources[receiver.get_device_name()].get_rx_driver(), receiver) receiver_output_type = receiver.get_output_type() if receiver_output_type.get_sample_rate() <= 0: # Demodulator has no output, but receiver has a dummy output, so connect it to something to satisfy flow graph structure. self.connect(receiver, blocks.null_sink(gr.sizeof_float * self.__audio_manager.get_channels())) # Note that we have a non-audio receiver which may be useful even if there is no audio output has_non_audio_receiver = True else: assert receiver_output_type.get_kind() == 'STEREO' audio_rs.input(receiver, receiver_output_type.get_sample_rate(), receiver.get_audio_destination()) self.__has_a_useful_receiver = audio_rs.finish_bus_connections() or \ has_non_audio_receiver self._recursive_unlock() # (this is in an if block but it can't not execute if anything else did) self.__log.info('Flow graph: ...done reconnecting ({time_ms} ms).', time_ms=(time.time() - t0) * 1000) self.__start_or_stop_later() self.__in_reconnect = False def __device_vfo_callback(self, device_key): reactor.callLater( self._sources[device_key].get_rx_driver().get_tune_delay(), self.__device_vfo_changed, device_key) def __device_vfo_changed(self, device_key): device = self._sources[device_key] freq = device.get_freq() if self.source is device: self.monitor.set_input_center_freq(freq) for rec_key, receiver in six.iteritems(self._receivers): if receiver.get_device_name() == device_key: receiver.changed_device_freq() self._update_receiver_validity(rec_key) # TODO: If multiple receivers change validity we'll do redundant reconnects in this loop; avoid that. def _update_receiver_validity(self, key): receiver = self._receivers[key] if receiver.get_is_valid() != self._receiver_valid[key]: self.__needs_reconnect.append(u'receiver %s validity changed' % (key,)) self._do_connect() @exported_value(type=ReferenceT(), changes='never') def get_monitor(self): return self.monitor @exported_value(type=ReferenceT(), persists=False, changes='never') def get_sources(self): return self.sources @exported_value(type=ReferenceT(), persists=False, changes='explicit') def get_source(self): return self.source # TODO no need for this now...? @exported_value(type=ReferenceT(), changes='never') def get_receivers(self): return self.receivers # TODO the concept of 'accessories' is old and needs to go away, but we don't have a flexible enough UI to replace it with just devices since only one device can be looked-at at a time so far. @exported_value(type=ReferenceT(), persists=False, changes='never') def get_accessories(self): return self.accessories @exported_value(type=ReferenceT(), changes='never', label='Telemetry') def get_telemetry_store(self): return self.__telemetry_store def start(self, **kwargs): # pylint: disable=arguments-differ # trigger reconnect/restart notification self._recursive_lock() self._recursive_unlock() super(Top, self).start(**kwargs) self.__running = True def stop(self): super(Top, self).stop() self.__running = False def __start_or_stop(self): # TODO: Improve start/stop conditions: # # * run if a client is watching an audio-having receiver's cell-based outputs (e.g. VOR) but not listening to audio # # * don't run if no client is watching a pure telemetry receiver # (maybe a user preference since having a history when you connect is useful) # # Both of these refinements require becoming aware of cell subscriptions. should_run = ( self.__has_a_useful_receiver or self.monitor.get_interested_cell().get()) if should_run != self.__running: if should_run: self.start() else: self.stop() self.wait() def __start_or_stop_later(self, unused_subscription_value=None): reactor.callLater(0, self.__start_or_stop) def close_all_devices(self): """Close all devices in preparation for a clean shutdown. Makes this top block unusable""" for device in six.itervalues(self._sources): device.close() for device in six.itervalues(self._accessories): device.close() self.stop() self.wait() @exported_value( type_fn=lambda self: self.__rx_device_type, changes='this_setter', label='RF source') def get_source_name(self): return self.source_name @setter def set_source_name(self, value): if value == self.source_name: return if value not in self._sources: raise ValueError('Source %r does not exist' % (value,)) self.source_name = value self._do_connect() def _get_rx_device_type(self): """for ContextForReceiver only""" return self.__rx_device_type def _get_audio_destination_type(self): """for ContextForReceiver only""" return self.__audio_manager.get_destination_type() def _trigger_reconnect(self, reason): self.__needs_reconnect.append(reason) self._do_connect() def _recursive_lock_hook(self): for source in self._sources.itervalues(): source.notify_reconnecting_or_restarting()
def setUp(self): self.tb = gr.top_block() self.p = AudioManager(graph=self.tb, audio_config=None, stereo=False)
def setUp(self): self.tb = gr.top_block() self.p = AudioManager( graph=self.tb, audio_config=None, stereo=False)