def _subscribe_to_block(self, block_name): self.client_comms = self.process.get_client_comms(block_name) assert self.client_comms, \ "Process doesn't know about block %s" % block_name request = Subscribe(None, self, [block_name], delta=True) request.set_id(self.BLOCK_ID) self.client_comms.q.put(request)
def subscribe(self, attr, callback, *args): """Subscribe to changes in a given attribute and call ``callback(value, *args)`` when it changes Returns: int: an id for the subscription """ assert isinstance(attr, Attribute), \ "Expected Attribute, got %r" % (attr,) endpoint = attr.process_path + ["value"] self.log_debug("Subscribing to %s", endpoint) request = Subscribe(None, self.q, endpoint, False) # If self is in args, then make weak version of it saved_args = [] for arg in args: if arg is self: saved_args.append(weakref.proxy(self)) else: saved_args.append(arg) new_id = self._get_next_id() self._subscriptions[new_id] = (endpoint, callback, args) request.set_id(new_id) self.process.q.put(request) return new_id
def test_unsubscribe(self): # Test that we remove the relevant subscription only and that # updates are no longer sent block = MagicMock(to_dict=MagicMock(return_value={ "attr": "0", "inner": { "attr2": "other" } })) p = Process("proc", MagicMock()) sub_1 = Subscribe(MagicMock(), MagicMock(), ["block"], False) sub_2 = Subscribe(MagicMock(), MagicMock(), ["block"], False) sub_1.set_id(1234) sub_2.set_id(4321) change_1 = BlockChanges([[["block", "attr"], "1"]]) change_2 = BlockChanges([[["block", "attr"], "2"]]) unsub_1 = Unsubscribe(MagicMock(), MagicMock()) unsub_1.set_id(1234) p.q.get = MagicMock(side_effect=[ sub_1, sub_2, change_1, unsub_1, change_2, PROCESS_STOP ]) p._handle_block_add(BlockAdd(block, "block")) p.recv_loop() self.assertEqual([sub_2], p._subscriptions) self.assertEquals(1, len(unsub_1.response_queue.put.call_args_list)) response = unsub_1.response_queue.put.call_args_list[0][0][0] self.assertIsNone(response.value) self.assertIs(unsub_1.context, response.context) sub_1_responses = sub_1.response_queue.put.call_args_list sub_2_responses = sub_2.response_queue.put.call_args_list self.assertEquals(2, len(sub_1_responses)) self.assertEquals(3, len(sub_2_responses))
def send_subscription(self): endpoints = [self._block] endpoints = endpoints + self.dict_to_path(self._request.toDict()) self.log_debug("Endpoints: %s", endpoints) msg = Subscribe(response_queue=self._server.q, endpoint=endpoints, delta=True) msg.set_id(self._id) self._server.send_to_process(msg)
def __init__(self, process, _=None): super(PvaServerComms, self).__init__(process) self.name = "PvaServerComms" self.set_logger_name(self.name) self._lock = RLock() self._current_id = 1 self._root_id = 0 self._blocklist = {} self._cache = Cache() self._server = None self._endpoints = {} self._cb = None self._rpcs = {} self._puts = {} self._dead_rpcs = [] # Create the V4 PVA server object self.create_pva_server() # Add a thread for executing the V4 PVA server self.add_spawn_function(self.start_pva_server) # Set up the subscription for everything (root down) request = Subscribe(None, self.q, [], True) request.set_id(self._root_id) self.process.q.put(request)
def do_initial_reset(self): self.state.set_value("Resetting") self.status.set_value("Waiting for connection...") self.busy.set_value(True) request = Subscribe(None, self, [self.process.name, "remoteBlocks", "value"]) request.set_id(self.REMOTE_BLOCKS_ID) self.process.q.put(request)
def subscribe(self, attr, callback, *args): """Subscribe to changes in a given attribute and call callback with (\*args) when it changes Returns: int: an id for the subscription """ endpoint = [attr.parent.name, attr.name] request = Subscribe(None, self.q, endpoint, False) new_id = self._save_subscription(endpoint, callback, *args) request.set_id(new_id) self.process.q.put(request) return new_id
def subscribe(self, attr, callback, *args): """Subscribe to changes in a given attribute and call ``callback(value, *args)`` when it changes Returns: int: an id for the subscription """ assert isinstance(attr, Attribute), \ "Expected Attribute, got %r" % (attr,) endpoint = attr.path_relative_to(self.process) + ["value"] self.log_debug("Subscribing to %s", endpoint) request = Subscribe(None, self.q, endpoint, False) new_id = self._save_subscription(endpoint, callback, *args) request.set_id(new_id) self.process.q.put(request) return new_id
def test_send_subscribe_to_server(self): self.PVA = PvaClientComms(self.p) self.PVA.send_to_caller = MagicMock() request = Subscribe(endpoint=["ep1", "ep2"]) request.set_id(1) self.PVA.send_to_server(request) pvaccess.Channel.assert_called_once() self.ch.subscribe.assert_called_once() self.ch.startMonitor.assert_called_once() mon = self.PVA._monitors[1] mon_val = MagicMock() mon_val.toDict = MagicMock(return_value={'typeid': 'malcolm:core/Error:1.0', 'message': 'test error'}) self.PVA.send_to_caller.reset_mock() mon.monitor_update(mon_val) self.PVA.send_to_caller.assert_called_once() self.PVA.send_to_caller.reset_mock() mon_val = MagicMock() mon_val.toDict = MagicMock(return_value={'typeid': 'malcolm:core/Update:1.0'}) mon.monitor_update(mon_val) self.PVA.send_to_caller.assert_called_once()
def __init__(self, process, _=None): super(PvaServerComms, self).__init__(process) self.name = "PvaServerComms" self.set_logger_name(self.name) self._lock = RLock() self._current_id = 1 self._root_id = 0 self._local_block_list = {} self._local_block_id = self._get_unique_id() self._remote_block_list = {} self._remote_block_id = self._get_unique_id() self._server = None self._endpoints = {} self._cb = None self._gets = {} self._rpcs = {} self._puts = {} self._monitors = {} self._dead_rpcs = [] # Create the V4 PVA server object self.create_pva_server() # Add a thread for executing the V4 PVA server self.add_spawn_function(self.start_pva_server) self.log_debug("Process name: %s", process.name) # Set up the subscription for local blocks request = Subscribe(None, self.q, [process.name, 'blocks', 'value'], False) request.set_id(self._local_block_id) self.process.q.put(request) # Set up the subscription for remote blocks request = Subscribe(None, self.q, [process.name, 'remoteBlocks', 'value'], False) request.set_id(self._remote_block_id) self.process.q.put(request)
def test_unsubscribe(self): # Test that we remove the relevant subscription only and that # updates are no longer sent block = MagicMock(to_dict=MagicMock(return_value={ "attr": "0", "inner": { "attr2": "other" } })) p = Process("proc", MagicMock()) sub_1 = Subscribe(MagicMock(), MagicMock(), ["block"], False) sub_1.response_queue.qsize.return_value = 0 sub_2 = Subscribe(MagicMock(), MagicMock(), ["block"], False) sub_2.response_queue.qsize.return_value = 0 sub_1.set_id(1234) sub_2.set_id(1234) change_1 = BlockChanges([[["block", "attr"], "1"]]) change_2 = BlockChanges([[["block", "attr"], "2"]]) unsub_1 = Unsubscribe(sub_1.context, sub_1.response_queue) unsub_1.set_id(sub_1.id) p.q.get = MagicMock(side_effect=[ sub_1, sub_2, change_1, unsub_1, change_2, PROCESS_STOP ]) p._handle_block_add(BlockAdd(block, "block", None)) p.recv_loop() self.assertEqual([sub_2], list(p._subscriptions.values())) sub_1_responses = sub_1.response_queue.put.call_args_list sub_2_responses = sub_2.response_queue.put.call_args_list self.assertEquals(3, len(sub_1_responses)) self.assertEquals(sub_1_responses[0][0][0].value["attr"], "0") self.assertEquals(sub_1_responses[1][0][0].value["attr"], "1") self.assertIsInstance(sub_1_responses[2][0][0], Return) self.assertEquals(3, len(sub_2_responses)) self.assertEquals(sub_2_responses[0][0][0].value["attr"], "0") self.assertEquals(sub_2_responses[1][0][0].value["attr"], "1") self.assertEquals(sub_2_responses[2][0][0].value["attr"], "2")
def test_unsubscribe(self): # Test that we remove the relevant subscription only and that # updates are no longer sent block = MagicMock( to_dict=MagicMock( return_value={"attr": "0", "inner": {"attr2": "other"}})) p = Process("proc", MagicMock()) sub_1 = Subscribe( MagicMock(), MagicMock(), ["block"], False) sub_1.response_queue.qsize.return_value = 0 sub_2 = Subscribe( MagicMock(), MagicMock(), ["block"], False) sub_2.response_queue.qsize.return_value = 0 sub_1.set_id(1234) sub_2.set_id(1234) change_1 = BlockChanges([[["block", "attr"], "1"]]) change_2 = BlockChanges([[["block", "attr"], "2"]]) unsub_1 = Unsubscribe(sub_1.context, sub_1.response_queue) unsub_1.set_id(sub_1.id) p.q.get = MagicMock(side_effect=[sub_1, sub_2, change_1, unsub_1, change_2, PROCESS_STOP]) p._handle_block_add(BlockAdd(block, "block", None)) p.recv_loop() self.assertEqual([sub_2], list(p._subscriptions.values())) sub_1_responses = sub_1.response_queue.put.call_args_list sub_2_responses = sub_2.response_queue.put.call_args_list self.assertEquals(3, len(sub_1_responses)) self.assertEquals(sub_1_responses[0][0][0].value["attr"], "0") self.assertEquals(sub_1_responses[1][0][0].value["attr"], "1") self.assertIsInstance(sub_1_responses[2][0][0], Return) self.assertEquals(3, len(sub_2_responses)) self.assertEquals(sub_2_responses[0][0][0].value["attr"], "0") self.assertEquals(sub_2_responses[1][0][0].value["attr"], "1") self.assertEquals(sub_2_responses[2][0][0].value["attr"], "2")
def test_send_subscribe_to_server(self): self.PVA = PvaClientComms(self.p) self.PVA.send_to_caller = MagicMock() request = Subscribe(endpoint=["ep1", "ep2"]) request.set_id(1) self.PVA.send_to_server(request) pvaccess.Channel.assert_called_once() self.ch.subscribe.assert_called_once() self.ch.startMonitor.assert_called_once() mon = self.PVA._monitors[1] mon_val = MagicMock() mon_val.toDict = MagicMock(return_value={ 'typeid': 'malcolm:core/Error:1.0', 'message': 'test error' }) self.PVA.send_to_caller.reset_mock() mon.monitor_update(mon_val) self.PVA.send_to_caller.assert_called_once() self.PVA.send_to_caller.reset_mock() mon_val = MagicMock() mon_val.toDict = MagicMock( return_value={'typeid': 'malcolm:core/Update:1.0'}) mon.monitor_update(mon_val) self.PVA.send_to_caller.assert_called_once()
class TestSubscribe(unittest.TestCase): def setUp(self): self.callback = MagicMock() self.path = ["BL18I:XSPRESS3"] self.delta = True self.o = Subscribe(11, self.path, self.delta, self.callback) def test_init(self): assert self.o.typeid == "malcolm:core/Subscribe:1.0" assert self.o.id == 11 assert self.o.callback == self.callback assert self.path == self.o.path assert self.delta == self.o.delta def test_respond_with_update(self): cb, response = self.o.update_response(value=5) assert cb == self.callback assert response == Update(id=11, value=5) def test_respond_with_delta(self): changes = [[["path"], "value"]] cb, response = self.o.delta_response(changes) assert cb == self.callback assert response == Delta(id=11, changes=changes) def test_setters(self): self.o.set_delta(False) assert not self.o.delta self.o.set_path(["BL18I:XSPRESS3", "state", "value"]) self.o.set_id(19) d = self.o.to_dict() del d["delta"] assert get_doc_json("subscribe_xspress3_state_value") == d def test_doc(self): assert get_doc_json("subscribe_xspress3") == self.o.to_dict()
def test_partial_structure_subscriptions(self): block_1 = MagicMock(to_dict=MagicMock(return_value={ "attr": "value", "inner": { "attr2": "value" } })) block_2 = MagicMock(to_dict=MagicMock(return_value={"attr": "value"})) sub_1 = Subscribe(None, MagicMock(), ["block_1", "inner"], delta=False) sub_1.set_id(1) sub_1.response_queue.qsize.return_value = 0 sub_2 = Subscribe(None, MagicMock(), ["block_1"], delta=True) sub_2.set_id(2) sub_2.response_queue.qsize.return_value = 0 sub_3 = Subscribe(None, MagicMock(), ["block_1", "inner", "attr2"], delta=False) sub_3.set_id(3) sub_3.response_queue.qsize.return_value = 0 changes_1 = [[["block_1", "inner", "attr2"], "new_value"], [["block_1", "attr"], "new_value"]] changes_2 = [[["block_2", "attr"], "block_2_value"]] request_1 = BlockChanges(changes_1) request_2 = BlockChanges(changes_2) p = Process("proc", MagicMock()) p.q.get = MagicMock(side_effect=[ sub_1, sub_2, sub_3, request_1, request_2, PROCESS_STOP ]) p._handle_block_add(BlockAdd(block_1, "block_1", None)) p._handle_block_add(BlockAdd(block_2, "block_2", None)) p.recv_loop() response_1 = sub_1.response_queue.put.call_args_list[1][0][0]["value"] self.assertEquals({"attr2": "new_value"}, response_1) response_2 = sub_2.response_queue.put.call_args_list[1][0][0][ "changes"] self.assertEquals( [[["inner", "attr2"], "new_value"], [["attr"], "new_value"]], response_2) response_3 = sub_3.response_queue.put.call_args_list[1][0][0]["value"] self.assertEquals("new_value", response_3)
def test_partial_structure_subscriptions(self): block_1 = MagicMock( to_dict=MagicMock( return_value={"attr": "value", "inner": {"attr2": "value"}})) block_2 = MagicMock( to_dict=MagicMock(return_value={"attr": "value"})) sub_1 = Subscribe(None, MagicMock(), ["block_1", "inner"], delta=False) sub_1.set_id(1) sub_1.response_queue.qsize.return_value = 0 sub_2 = Subscribe(None, MagicMock(), ["block_1"], delta=True) sub_2.set_id(2) sub_2.response_queue.qsize.return_value = 0 sub_3 = Subscribe(None, MagicMock(), ["block_1", "inner", "attr2"], delta=False) sub_3.set_id(3) sub_3.response_queue.qsize.return_value = 0 changes_1 = [[["block_1", "inner", "attr2"], "new_value"], [["block_1", "attr"], "new_value"]] changes_2 = [[["block_2", "attr"], "block_2_value"]] request_1 = BlockChanges(changes_1) request_2 = BlockChanges(changes_2) p = Process("proc", MagicMock()) p.q.get = MagicMock(side_effect=[ sub_1, sub_2, sub_3, request_1, request_2, PROCESS_STOP]) p._handle_block_add(BlockAdd(block_1, "block_1", None)) p._handle_block_add(BlockAdd(block_2, "block_2", None)) p.recv_loop() response_1 = sub_1.response_queue.put.call_args_list[1][0][0]["value"] self.assertEquals({"attr2": "new_value"}, response_1) response_2 = sub_2.response_queue.put.call_args_list[1][0][0]["changes"] self.assertEquals([[["inner", "attr2"], "new_value"], [["attr"], "new_value"]], response_2) response_3 = sub_3.response_queue.put.call_args_list[1][0][0]["value"] self.assertEquals("new_value", response_3)
def subscribe_server_blocks(self, _): """Subscribe to process blocks""" request = Subscribe(None, None, [".", "blocks", "value"]) request.set_id(self.SERVER_BLOCKS_ID) self.loop.add_callback(self.send_to_server, request)