def inspect(self): timeout_manager = future_timeout_manager(self.sync_timeout) request_changes = yield self.inspect_requests(timeout=timeout_manager.remaining()) sensor_changes = yield self.inspect_sensors(timeout=timeout_manager.remaining()) model_changes = AttrDict() if request_changes: model_changes.requests = request_changes if sensor_changes: model_changes.sensors = sensor_changes if model_changes: raise Return(model_changes)
def inspect(self): """Inspect device requests and sensors, update model Returns ------- Tornado future that resolves with: model_changes : Nested AttrDict or None Contains sets of added/removed request/sensor names Example structure: {'requests': { 'added': set(['req1', 'req2']), 'removed': set(['req10', 'req20'])} 'sensors': { 'added': set(['sens1', 'sens2']), 'removed': set(['sens10', 'sens20'])} } If there are no changes keys may be omitted. If an item is in both the 'added' and 'removed' sets that means that it changed. If neither request not sensor changes are present, None is returned instead of a nested structure. """ timeout_manager = future_timeout_manager(self.sync_timeout) sensor_index_before = copy.copy(self._sensors_index) request_index_before = copy.copy(self._requests_index) try: request_changes = yield self.inspect_requests( timeout=timeout_manager.remaining()) sensor_changes = yield self.inspect_sensors( timeout=timeout_manager.remaining()) except Exception: # Ensure atomicity of sensor and request updates ; if the one # fails, the other should act as if it has failed too. self._sensors_index = sensor_index_before self._requests_index = request_index_before raise model_changes = AttrDict() if request_changes: model_changes.requests = request_changes if sensor_changes: model_changes.sensors = sensor_changes if model_changes: raise Return(model_changes)
def inspect_requests(self, name=None, timeout=None): """Inspect all or one requests on the device. Update requests index. Parameters ---------- name : str or None, optional Name of the sensor or None to get all requests. timeout : float or None, optional Timeout for request inspection, None for no timeout Returns ------- Tornado future that resolves with: changes : :class:`~katcp.core.AttrDict` AttrDict with keys ``added`` and ``removed`` (of type :class:`set`), listing the requests that have been added or removed respectively. Modified requests are listed in both. If there are no changes, returns ``None`` instead. Example structure: {'added': set(['req1', 'req2']), 'removed': set(['req10', 'req20'])} """ if name is None: msg = katcp.Message.request('help') else: msg = katcp.Message.request('help', name) reply, informs = yield self.katcp_client.future_request( msg, timeout=timeout) if not reply.reply_ok(): # If an unknown request is specified the desired result is to return # an empty list even though the request will fail if name is None or 'Unknown request' not in reply.arguments[1]: raise SyncError( 'Error reply during sync process for {}: {}' .format(self.bind_address_string, reply)) requests_old = set(self._requests_index.keys()) requests_updated = set() for msg in informs: req_name = msg.arguments[0] req = {'description': msg.arguments[1]} requests_updated.add(req_name) self._update_index(self._requests_index, req_name, req) added, removed = self._difference( requests_old, requests_updated, name, self._requests_index) if added or removed: raise Return(AttrDict(added=added, removed=removed))
def test_wrapping(self): test_dict = AttrDict(a=2, b=1) class TestWrapper(object): def __init__(self, wrappee): self.wrappee = wrappee def __eq__(self, other): return self.wrappee == other.wrappee wrapped_dict = resource_client.AttrMappingProxy(test_dict, TestWrapper) # Test keys self.assertEqual(wrapped_dict.keys(), test_dict.keys()) # Test key access: for key in test_dict: self.assertEqual(wrapped_dict[key].wrappee, test_dict[key]) # Test attribute access for key in test_dict: self.assertEqual(getattr(wrapped_dict, key).wrappee, getattr(test_dict, key)) # Test whole dict comparison self.assertEqual(wrapped_dict, {k : TestWrapper(v) for k, v in test_dict.items()})
def inspect(self): """Inspect device requests and sensors, update model""" timeout_manager = future_timeout_manager(self.sync_timeout) sensor_index_before = copy.copy(self._sensors_index) request_index_before = copy.copy(self._requests_index) try: request_changes = yield self.inspect_requests(timeout=timeout_manager.remaining()) sensor_changes = yield self.inspect_sensors(timeout=timeout_manager.remaining()) except Exception: # Ensure atomicity of sensor and request updates ; if the one # fails, the other should act as if it has failed too. self._sensors_index = sensor_index_before self._requests_index = request_index_before raise model_changes = AttrDict() if request_changes: model_changes.requests = request_changes if sensor_changes: model_changes.sensors = sensor_changes if model_changes: raise Return(model_changes)
def inspect_sensors(self, name=None, timeout=None): """Inspect all or one sensor on the device. Update sensors index. Parameters ---------- name : str or None, optional Name of the sensor or None to get all sensors. timeout : float or None, optional Timeout for sensors inspection, None for no timeout Returns ------- Tornado future that resolves with: changes : :class:`~katcp.core.AttrDict` AttrDict with keys ``added`` and ``removed`` (of type :class:`set`), listing the sensors that have been added or removed respectively. Modified sensors are listed in both. If there are no changes, returns ``None`` instead. Example structure: {'added': set(['sens1', 'sens2']), 'removed': set(['sens10', 'sens20'])} """ if name is None: msg = katcp.Message.request('sensor-list') else: msg = katcp.Message.request('sensor-list', name) reply, informs = yield self.katcp_client.future_request( msg, timeout=timeout) self._logger.debug( '{} received {} sensor-list informs, reply: {}'.format( self.bind_address_string, len(informs), reply)) if not reply.reply_ok(): # If an unknown sensor is specified the desired result is to return # an empty list, even though the request will fail if name is None or 'Unknown sensor' not in reply.arguments[1]: raise SyncError( 'Error reply during sync process: {}'.format(reply)) sensors_old = set(self._sensors_index.keys()) sensors_updated = set() for msg in informs: sen_name = msg.arguments[0] sensors_updated.add(sen_name) sen = { 'description': msg.arguments[1], 'units': msg.arguments[2], 'sensor_type': msg.arguments[3], 'params': msg.arguments[4:] } self._update_index(self._sensors_index, sen_name, sen) added, removed = self._difference(sensors_old, sensors_updated, name, self._sensors_index) for sensor_name in removed: if sensor_name in self._sensor_object_cache: del self._sensor_object_cache[sensor_name] if added or removed: raise Return(AttrDict(added=added, removed=removed))
def _connect(self): assert get_thread_ident() == self.ioloop_thread_id self._stream = AttrDict(error=None) self._connected.set() self.notify_connected(True) self.preset_protocol_flags(ProtocolFlags(5, 0, set('IM')))