def __init__(self, params): # Layout options self.x = 0 self.y = 0 self.visible = None # {part_name: visible} saying whether part_name is visible self.part_visible = {} # {attr_name: attr_value} of last saved/loaded structure self.saved_structure = {} # {attr_name: modified_message} of current values self.modified_messages = {} # The controller hosting our child self.child_controller = None # {id: Subscribe} for subscriptions to config tagged fields self.config_subscriptions = {} # set(attribute_name) where the attribute is a config tagged field # we are modifying self.we_modified = set() # Update queue of modified alarms self.modified_update_queue = Queue() # Update queue of exportable fields self.exportable_update_queue = Queue() # {attr_name: PortInfo} self.port_infos = {} # Store params self.params = params super(ChildPart, self).__init__(params.name)
def testTwoMonitors(self): if PVAPY: # No need to do this test on the old server return assert "TESTCOUNTER" not in self.server._pvs # Make first monitor q1 = Queue() m1 = self.ctxt.monitor("TESTCOUNTER", q1.put) self.addCleanup(m1.close) counter = q1.get(timeout=1) self.assertStructureWithoutTsEqual(str(counter), str(counter_expected)) assert len(self.server._pvs["TESTCOUNTER"]) == 1 # Make a second monitor and check that also fires without making another # PV ctxt2 = self.make_pva_context() q2 = Queue() m2 = ctxt2.monitor("TESTCOUNTER", q2.put) self.addCleanup(m2.close) counter = q2.get(timeout=1) self.assertStructureWithoutTsEqual(str(counter), str(counter_expected)) assert len(self.server._pvs["TESTCOUNTER"]) == 1 # Check that a Put fires on both self.ctxt.put("TESTCOUNTER.counter", 5, "value") counter = q1.get(timeout=1) self.assertEqual(counter.counter.value, 5) counter = q2.get(timeout=1) self.assertEqual(counter.counter.value, 5)
def __init__(self, process, parts, params): self.params = params super(ProxyController, self).__init__(process, params.mri, parts) self.client_comms = process.get_controller(params.comms) self.update_health(self, Alarm.invalid("Uninitialized")) self._response_queue = Queue() self._notify_response = True self._first_response_queue = Queue()
def setUp(self): self.process = Process("proc") self.hello = call_with_params(hello_block, self.process, mri="hello") self.server = call_with_params( web_server_block, self.process, mri="server", port=self.socket) self.result = Queue() self.process.start()
def sync_proxy(self, mri, block): """Abstract method telling the ClientComms to sync this proxy Block with its remote counterpart. Should wait until it is connected Args: mri (str): The mri for the remote block block (BlockModel): The local proxy Block to keep in sync """ done_queue = Queue() self._queues[mri] = done_queue update_fields = set() def callback(value=None): if isinstance(value, Exception): # Disconnect or Cancelled or RemoteError if isinstance(value, Disconnected): # We will get a reconnect with a whole new structure update_fields.clear() block.health.set_value( value="pvAccess disconnected", alarm=Alarm.disconnected("pvAccess disconnected"), ) else: with block.notifier.changes_squashed: if not update_fields: self.log.debug("Regenerating from %s", list(value)) self._regenerate_block(block, value, update_fields) done_queue.put(None) else: self._update_block(block, value, update_fields) m = self._ctxt.monitor(mri, callback, notify_disconnect=True) self._monitors.add(m) done_queue.get(timeout=DEFAULT_TIMEOUT)
def test_block_fields_lut(self): fields = OrderedDict() block_data = BlockData(8, "Lut description", fields) fields["FUNC"] = FieldData("param", "lut", "Function", []) o = PandABlockController(self.client, "MRI", "LUT3", block_data, "/docs") self.process.add_controller(o) b = self.process.block_view("MRI:LUT3") func = b.func assert func.meta.writeable is True assert func.meta.typeid == StringMeta.typeid assert func.meta.tags == ["group:parameters", "widget:textinput", "config:1"] queue = Queue() subscribe = Subscribe(path=["MRI:LUT3"], delta=True) subscribe.set_callback(queue.put) o.handle_request(subscribe) delta = queue.get() assert delta.changes[0][1]["func"]["value"] == "" assert '<path id="OR"' in delta.changes[0][1]["icon"]["value"] # This is the correct FUNC.RAW value for !A&!B&!C&!D&!E self.client.get_field.return_value = "1" ts = TimeStamp() o.handle_changes({"FUNC": "!A&!B&!C&!D&!E"}, ts) self.client.get_field.assert_called_once_with("LUT3", "FUNC.RAW") delta = queue.get() assert delta.changes == [ [["func", "value"], "!A&!B&!C&!D&!E"], [["func", "timeStamp"], ts], [["icon", "value"], ANY], [["icon", "timeStamp"], ts], ] assert '<path id="OR"' not in delta.changes[2][1]
def test_pos_table_deltas(self): queue = Queue() subscribe = Subscribe(path=["P"], delta=True) subscribe.set_callback(queue.put) self.o.handle_request(subscribe) delta = queue.get() capture_enums = delta.changes[0][1]["positions"]["meta"]["elements"][ "capture"]["choices"] assert capture_enums[0] == PositionCapture.NO table = delta.changes[0][1]["positions"]["value"] assert table.name == ["COUNTER.OUT"] assert table.value == [0.0] assert table.scale == [1.0] assert table.offset == [0.0] assert table.capture == [PositionCapture.NO] self.o.handle_changes([("COUNTER.OUT", "20")]) delta = queue.get() assert delta.changes == [ [["positions", "value", "value"], [20.0]], [["positions", "timeStamp"], ANY], ] self.o.handle_changes([("COUNTER.OUT", "5"), ("COUNTER.OUT.SCALE", 0.5)]) delta = queue.get() assert delta.changes == [ [["positions", "value", "value"], [2.5]], [["positions", "value", "scale"], [0.5]], [["positions", "timeStamp"], ANY], ]
def initialize(self, registrar=None, validators=()): self._registrar = registrar # {id: mri} self._id_to_mri = {} self._validators = validators self._queue = Queue() self._counter = 0
def sync_proxy(self, mri, block): """Abstract method telling the ClientComms to sync this proxy Block with its remote counterpart. Should wait until it is connected Args: mri (str): The mri for the remote block block (BlockModel): The local proxy Block to keep in sync """ # Send a root Subscribe to the server subscribe = Subscribe(path=[mri], delta=True) done_queue = Queue() def handle_response(response): # Called from tornado if not isinstance(response, Delta): # Return or Error is the end of our subscription, log and ignore self.log.debug("Proxy got response %r", response) done_queue.put(None) else: cothread.Callback(self._handle_response, response, block, done_queue) subscribe.set_callback(handle_response) IOLoopHelper.call(self._send_request, subscribe) done_queue.get(timeout=DEFAULT_TIMEOUT)
def try_aborting_function(self, start_state, end_state, func, *args): try: # To make the running function fail we need to stop any running # contexts (if running a hook) or make transition() fail with # AbortedError. Both of these are accomplished here with self._lock: original_state = self.state.value self.abort_queue = Queue() self.transition(start_state) for context in self.part_contexts.values(): context.stop() if original_state not in (ss.READY, ss.ARMED, ss.PAUSED): # Something was running, let it finish aborting try: self.abort_queue.get(timeout=ABORT_TIMEOUT) except TimeoutError: self.log.warning("Timeout waiting while %s" % start_state) with self._lock: # Now we've waited for a while we can remove the error state # for transition in case a hook triggered it rather than a # transition self.part_contexts[self].ignore_stops_before_now() func(*args) self.abortable_transition(end_state) except AbortedError: self.abort_queue.put(None) raise except Exception as e: # pylint:disable=broad-except self.go_to_error_state(e) raise
def testMonitorEverythingInitial(self): q = Queue() m = self.ctxt.monitor("TESTCOUNTER", q.put) self.addCleanup(m.close) counter = q.get(timeout=1) self.assertStructureWithoutTsEqual(str(counter), str(counter_expected)) self.assertTrue(counter.changedSet().issuperset( {"meta.fields", "counter.value", "zero.meta.description"})) self.assertEqual(counter["counter.value"], 0) self.assertEqual(counter["zero.meta.description"], "Zero the counter attribute") self.ctxt.put("TESTCOUNTER.counter", 5, "value") counter = q.get(timeout=1) self.assertEqual(counter.counter.value, 5) self.assertEqual( counter.changedSet(), { "counter.value", "counter.timeStamp.userTag", "counter.timeStamp.secondsPastEpoch", "counter.timeStamp.nanoseconds", }, ) self.ctxt.put("TESTCOUNTER.counter", 0, "value") counter = q.get(timeout=1) self.assertStructureWithoutTsEqual(str(counter), str(counter_expected))
def setUp(self): self.process = Process("proc") self.hello = hello_block(mri="hello")[-1] self.process.add_controller(self.hello) self.server = web_server_block(mri="server", port=self.socket)[-1] self.process.add_controller(self.server) self.result = Queue() self.process.start()
def init(self, context): self.configure_args_update_queue = Queue() super(RunnableChildPart, self).init(context) # Monitor the child configure Method for changes self.serialized_configure = MethodModel().to_dict() subscription = Subscribe( path=[self.params.mri, "configure"], delta=True, callback=self.update_part_configure_args) # Wait for the first update to come in self.child_controller.handle_request(subscription).wait()
def start_poll_loop(self): # queue to listen for stop events if not self.client.started: self._stop_queue = Queue() if self.client.started: self.client.stop() self.client.start(self.process.spawn, socket) if not self._blocks_parts: self._make_blocks_parts() if self._poll_spawned is None: self._poll_spawned = self.process.spawn(self._poll_loop)
def start_poll_loop(self): # queue to listen for stop events if not self._client.started: self._stop_queue = Queue() if self._client.started: self._client.stop() self._client.start(self.process.spawn, socket) if not self._child_controllers: self._make_child_controllers() if self._poll_spawned is None: self._poll_spawned = self.process.spawn(self._poll_loop)
def test_hello_good_input(self): q = Queue() request = Post(id=44, path=["hello_block", "greet"], parameters=dict(name="thing")) request.set_callback(q.put) self.controller.handle_request(request) response = q.get(timeout=1.0) self.assertIsInstance(response, Return) assert response.id == 44 assert response.value == "Hello thing"
def do_configure(self, state: str, params: ConfigureParams) -> None: if state == ss.FINISHED: # If we were finished then do a reset before configuring self.run_hooks( builtin.hooks.ResetHook(p, c) for p, c in self.create_part_contexts().items() ) # Clear out any old part contexts now rather than letting gc do it for context in self.part_contexts.values(): context.unsubscribe_all() # These are the part tasks that abort() and pause() will operate on self.part_contexts = self.create_part_contexts() # So add one for ourself too so we can be aborted assert self.process, "No attached process" self.part_contexts[self] = Context(self.process) # Store the params for use in seek() self.configure_params = params # Tell everything to get into the right state to Configure self.run_hooks(PreConfigureHook(p, c) for p, c in self.part_contexts.items()) # This will calculate what we need from the generator, possibly a long # call params.generator.prepare() # Set the steps attributes that we will do across many run() calls self.total_steps.set_value(params.generator.size) self.completed_steps.set_value(0) self.configured_steps.set_value(0) # TODO: We can be cleverer about this and support a different number # of steps per run for each run by examining the generator structure self.steps_per_run = self.get_steps_per_run( params.generator, params.axesToMove, params.breakpoints ) # Get any status from all parts part_info = self.run_hooks( ReportStatusHook(p, c) for p, c in self.part_contexts.items() ) # Run the configure command on all parts, passing them info from # ReportStatus. Parts should return any reporting info for PostConfigure completed_steps = 0 self.breakpoint_index = 0 steps_to_do = self.steps_per_run[self.breakpoint_index] part_info = self.run_hooks( ConfigureHook(p, c, completed_steps, steps_to_do, part_info, **kw) for p, c, kw in self._part_params() ) # Take configuration info and reflect it as attribute updates self.run_hooks( PostConfigureHook(p, c, part_info) for p, c in self.part_contexts.items() ) # Update the completed and configured steps self.configured_steps.set_value(steps_to_do) self.completed_steps.meta.display.set_limitHigh(params.generator.size) # Reset the progress of all child parts self.progress_updates = {} self.resume_queue = Queue()
def _request_response(self, request_cls, path, **kwargs): queue = Queue() request = request_cls(path=[self._mri] + path, callback=queue.put, **kwargs) self._controller.handle_request(request) response = queue.get() if isinstance(response, Error): raise ResponseError(response.message) else: return response
def testMonitorSubfieldInitial(self): q = Queue() m = self.ctxt.monitor("TESTCOUNTER", q.put, "meta.fields") self.addCleanup(m.close) counter = q.get(timeout=1) self.assertEqual(counter.getID(), "structure") # P4P only says leaves have changed self.assertEqual(counter.changedSet(), {"meta.fields"}) self.assertEqual(counter.meta.fields, ["health", "counter", "delta", "zero", "increment"]) fields_code = dict(counter.meta.type().aspy()[2])["fields"] self.assertEqual(fields_code, "as")
def do_init(self): super(WebsocketClientComms, self).do_init() self.loop = IOLoop() self._request_lookup = {} self._subscription_keys = {} self._connected_queue = Queue() root_subscribe = Subscribe(id=0, path=[".", "blocks"], callback=self._update_remote_blocks) self._subscription_keys[root_subscribe.generate_key()] = root_subscribe self._request_lookup[0] = (root_subscribe, 0) self.start_io_loop()
def start_poll_loop(self): # queue to listen for stop events if not self.client.started: self._stop_queue = Queue() if self.client.started: self.client.stop() from socket import socket if self.use_cothread: cothread = maybe_import_cothread() if cothread: from cothread.cosocket import socket self.client.start(self.spawn, socket) if not self._blocks_parts: self._make_blocks_parts() if self._poll_spawned is None: self._poll_spawned = self.spawn(self._poll_loop)
def send_put(self, mri, attribute_name, value): """Abstract method to dispatch a Put to the server Args: mri (str): The mri of the Block attribute_name (str): The name of the Attribute within the Block value: The value to put """ q = Queue() request = Put(path=[mri, attribute_name, "value"], value=value) request.set_callback(q.put) IOLoopHelper.call(self._send_request, request) response = q.get() if isinstance(response, Error): raise response.message else: return response.value
def test_concurrent(self): q = Queue() request = Post(id=44, path=["hello_block", "greet"], parameters=dict(name="me", sleep=1)) request.set_callback(q.put) self.controller.handle_request(request) request = Post(id=45, path=["hello_block", "error"]) request.set_callback(q.put) self.controller.handle_request(request) response = q.get(timeout=1.0) self.assertIsInstance(response, Error) assert response.id == 45 response = q.get(timeout=3.0) self.assertIsInstance(response, Return) assert response.id == 44 assert response.value == "Hello me"
def test_concurrent(self): q = Queue() request = Subscribe(id=40, path=["hello_block", "greet"], delta=True) request.set_callback(q.put) self.controller.handle_request(request) # Get the initial subscribe value inital = q.get(timeout=0.1) self.assertIsInstance(inital, Delta) assert inital.changes[0][1]["took"]["value"] == dict(sleep=0, name="") assert inital.changes[0][1]["returned"]["value"] == {"return": ""} # Do a greet request = Post(id=44, path=["hello_block", "greet"], parameters=dict(name="me", sleep=1)) request.set_callback(q.put) self.controller.handle_request(request) # Then an error request = Post(id=45, path=["hello_block", "error"]) request.set_callback(q.put) self.controller.handle_request(request) # We should quickly get the error response first response = q.get(timeout=1.0) self.assertIsInstance(response, Error) assert response.id == 45 # Then the long running greet delta response = q.get(timeout=3.0) self.assertIsInstance(response, Delta) assert len(response.changes) == 2 assert response.changes[0][0] == ["took"] took = response.changes[0][1] assert took.value == dict(sleep=1, name="me") assert took.present == ["name", "sleep"] assert took.alarm == Alarm.ok assert response.changes[1][0] == ["returned"] returned = response.changes[1][1] assert returned.value == {"return": "Hello me"} assert returned.present == ["return"] assert returned.alarm == Alarm.ok # Check it took about 1s to run assert abs(1 - (returned.timeStamp.to_time() - took.timeStamp.to_time())) < 0.4 # And it's response response = q.get(timeout=1.0) self.assertIsInstance(response, Return) assert response.id == 44 assert response.value == "Hello me"
def test_table_deltas(self): queue = Queue() subscribe = Subscribe(path=["P"], delta=True) subscribe.set_callback(queue.put) self.o.handle_request(subscribe) delta = queue.get() table = delta.changes[0][1]["bits"]["value"] assert table.name == ["TTLIN1.VAL", "TTLIN2.VAL", "PCOMP.OUT"] assert table.value == [False, False, False] assert table.capture == [False, False, False] self.o.handle_changes([("TTLIN1.VAL", "1")]) delta = queue.get() assert delta.changes == [ [["bits", "value", "value"], [True, False, False]], [["bits", "timeStamp"], ANY], ]
def wait_for_good_status(self, deadline): q = Queue() m = util.catools.camonitor(self.status_pv, q.put, datatype=util.catools.DBR_STRING) status = None try: while True: try: status = q.get(deadline - time.time()) except TimeoutError: return status else: if status == self.good_status: return status finally: m.close()
def test_counter_subscribe(self): q = Queue() # Subscribe to the value sub = Subscribe(id=20, path=["counting", "counter"], delta=False) sub.set_callback(q.put) self.controller.handle_request(sub) # Check initial return response = q.get(timeout=1.0) self.assertIsInstance(response, Update) assert response.id == 20 assert response.value["typeid"] == "epics:nt/NTScalar:1.0" assert response.value["value"] == 0 # Post increment() post = Post(id=21, path=["counting", "increment"]) post.set_callback(q.put) self.controller.handle_request(post) # Check the value updates... response = q.get(timeout=1) self.assertIsInstance(response, Update) assert response.id == 20 assert response.value["value"] == 1 # ... then we get the return response = q.get(timeout=1) self.assertIsInstance(response, Return) assert response.id == 21 assert response.value is None # Check we can put too put = Put(id=22, path=["counting", "counter", "value"], value=31) put.set_callback(q.put) self.controller.handle_request(put) # Check the value updates... response = q.get(timeout=1) self.assertIsInstance(response, Update) assert response.id == 20 assert response.value["value"] == 31 # ... then we get the return response = q.get(timeout=1) self.assertIsInstance(response, Return) assert response.id == 22 assert response.value is None # And that there isn't anything else with self.assertRaises(TimeoutError): q.get(timeout=0.05)
def send_post(self, mri, method_name, **params): """Abstract method to dispatch a Post to the server Args: mri (str): The mri of the Block method_name (str): The name of the Method within the Block params: The parameters to send Returns: The return results from the server """ q = Queue() request = Post(path=[mri, method_name], parameters=params) request.set_callback(q.put) IOLoopHelper.call(self._send_request, request) response = q.get() if isinstance(response, Error): raise response.message else: return response.value
def __init__( self, mri: builtin.controllers.AMri, hostname: AHostname = "localhost", port: APort = 8008, connect_timeout: AConnectTimeout = DEFAULT_TIMEOUT, ) -> None: super().__init__(mri) self.hostname = hostname self.port = port self.connect_timeout = connect_timeout self._connected_queue = Queue() # {new_id: request} self._request_lookup: Dict[int, Request] = {} self._next_id = 1 self._conn: Optional[WebSocketClientConnection] = None # Create read-only attribute for the remotely reachable blocks self.remote_blocks = TableMeta.from_table( BlockTable, "Remotely reachable blocks").create_attribute_model() self.field_registry.add_attribute_model("remoteBlocks", self.remote_blocks)
def __init__(self): assert not self._instance, \ "Can't create more than one instance of Singleton. Use instance()" self.cothread = maybe_import_cothread() if self.cothread: # We can use it in this thread from cothread import catools self.in_cothread_thread = True else: # We need our own thread to run it in q = Queue() threading.Thread(target=_import_cothread, args=(q,)).start() self.cothread, catools = q.get() self.in_cothread_thread = False self.catools = catools self.DBR_STRING = catools.DBR_STRING self.DBR_LONG = catools.DBR_LONG self.DBR_DOUBLE = catools.DBR_DOUBLE self.FORMAT_CTRL = catools.FORMAT_CTRL self.FORMAT_TIME = catools.FORMAT_TIME self.DBR_ENUM = catools.DBR_ENUM self.DBR_CHAR_STR = catools.DBR_CHAR_STR