def _on_response(self, response: Response) -> None:
     # called from tornado thread
     message = json_encode(response)
     try:
         self.write_message(message)
     except WebSocketError:
         # The websocket is dead. If the response was a Delta or Update, then
         # unsubscribe so the local controller doesn't keep on trying to
         # respond
         if isinstance(response, (Delta, Update)):
             # Websocket is dead so we can clear the subscription key.
             # Subsequent updates may come in before the unsubscribe, but
             # ignore them as we can't do anything about it
             mri = self._id_to_mri.pop(response.id, None)
             if mri:
                 log.info("WebSocket Error: unsubscribing from stale handle")
                 unsubscribe = Unsubscribe(response.id)
                 unsubscribe.set_callback(self.on_response)
                 if self._registrar:
                     self._registrar.report(
                         builtin.infos.RequestInfo(unsubscribe, mri)
                     )
     finally:
         assert self._queue, "No queue"
         cothread.Callback(self._queue.put, None)
示例#2
0
 def onLastDisconnect(self, pv: SharedPV) -> None:
     assert self.pv, "onFirstConnect not called yet"
     # No-one listening, unsubscribe
     with self._lock:
         self.pv.close()
         self.pv = None
         self.value = None
     request = Unsubscribe()
     request.set_callback(self.handle)
     self.controller.handle_request(request).get(timeout=1)
示例#3
0
    def update_part_exportable(self, response):
        # Get a child context to check if we have a config field
        child = self.child_controller.block_view()
        spawned = []
        if response.value:
            new_fields = response.value
        else:
            new_fields = []

        # Remove any existing subscription that is not in the new fields
        for subscribe in self.config_subscriptions.values():
            attr_name = subscribe.path[-2]
            if attr_name not in new_fields:
                unsubscribe = Unsubscribe(subscribe.id, subscribe.callback)
                spawned.append(
                    self.child_controller.handle_request(unsubscribe))
                self.port_infos.pop(attr_name, None)

        # Add a subscription to any new field
        existing_fields = set(s.path[-2]
                              for s in self.config_subscriptions.values())
        for field in set(new_fields) - existing_fields:
            attr = getattr(child, field)
            if isinstance(attr, Attribute):
                for tag in attr.meta.tags:
                    match = port_tag_re.match(tag)
                    if match:
                        d, type, extra = match.groups()
                        self.port_infos[field] = PortInfo(name=field,
                                                          value=attr.value,
                                                          direction=d,
                                                          type=type,
                                                          extra=extra)
            if isinstance(attr, Attribute) and config() in attr.meta.tags:
                if self.config_subscriptions:
                    new_id = max(self.config_subscriptions) + 1
                else:
                    new_id = 1
                subscribe = Subscribe(id=new_id,
                                      path=[self.params.mri, field, "value"],
                                      callback=self.update_part_modified)
                self.config_subscriptions[new_id] = subscribe
                # Signal that any change we get is a difference
                if field not in self.saved_structure:
                    self.saved_structure[field] = None
                spawned.append(self.child_controller.handle_request(subscribe))

        # Wait for the first update to come in
        for s in spawned:
            s.wait()

        # Put data on the queue, so if spawns are handled out of order we
        # still get the most up to date data
        port_infos = [
            self.port_infos[f] for f in new_fields if f in self.port_infos
        ]
        self.exportable_update_queue.put((new_fields, port_infos))
        self.spawn(self._update_part_exportable).wait()
示例#4
0
 def on_response(self, response, write_message):
     # called from tornado thread
     message = json_encode(response)
     try:
         write_message(message)
     except WebSocketError:
         if isinstance(response, (Delta, Update)):
             request = self._subscription_keys[response.id]
             unsubscribe = Unsubscribe(request.id)
             controller = self.process.get_controller(request.path[0])
             controller.handle_request(unsubscribe)
示例#5
0
    def _get_current_part_fields(self):
        # Clear out the current subscriptions
        for subscription in self._subscriptions:
            controller = self.process.get_controller(subscription.path[0])
            unsubscribe = Unsubscribe(subscription.id)
            unsubscribe.set_callback(subscription.callback)
            controller.handle_request(unsubscribe)
        self._subscriptions = []

        # Find the mris of parts
        mris = {}
        invisible = set()
        for part_name, mri, visible in zip(self.layout.value.name,
                                           self.layout.value.mri,
                                           self.layout.value.visible):
            if visible:
                mris[part_name] = mri
            else:
                invisible.add(part_name)

        # Add fields from parts that aren't invisible
        for part_name, part in self.parts.items():
            if part_name not in invisible:
                for data in self.field_registry.fields.get(part, []):
                    yield data

        # Add exported fields from visible parts
        for source, export_name in self.exports.value.rows():
            part_name, attr_name = source.rsplit(".", 1)
            part = self.parts[part_name]
            # If part is visible, get its mri
            mri = mris.get(part_name, None)
            if mri and attr_name in self.part_exportable.get(part, []):
                if not export_name:
                    export_name = attr_name
                export, setter = self._make_export_field(
                    mri, attr_name, export_name)
                yield export_name, export, setter, False
示例#6
0
 def halt(self, context):
     unsubscribe = Unsubscribe(callback=self.update_part_exportable)
     self.child_controller.handle_request(unsubscribe)
示例#7
0
    def update_part_exportable(self, response: Response) -> None:
        # Get a child context to check if we have a config field
        assert self.child_controller, "No child controller"
        child = self.child_controller.block_view()
        spawned = []
        if isinstance(response, Update):
            new_fields = response.value
            assert isinstance(new_fields, Sequence), f"Bad field list {new_fields}"
        elif isinstance(response, Return):
            # We got a return with None, so clear out all of the
            # config_subscriptions
            new_fields = []
        else:
            self.log.warning("Got unexpected response {response}")
            return

        # Remove any existing subscription that is not in the new fields
        for subscribe in self.config_subscriptions.values():
            attr_name = subscribe.path[-2]
            if attr_name not in new_fields:
                unsubscribe = Unsubscribe(subscribe.id)
                unsubscribe.set_callback(subscribe.callback)
                spawned.append(self.child_controller.handle_request(unsubscribe))
                self.port_infos.pop(attr_name, None)

        # Add a subscription to any new field
        existing_fields = set(s.path[-2] for s in self.config_subscriptions.values())
        for field in set(new_fields) - existing_fields:
            attr = getattr(child, field)
            if isinstance(attr, Attribute):
                # Cache tags here
                tags = attr.meta.tags
                # Check if the attribute has any port tags, and store for
                # when we are asked for LayoutInfo
                port_info = Port.port_tag_details(tags)
                info: PortInfo
                if port_info:
                    is_source, port, extra = port_info
                    if is_source:
                        info = SourcePortInfo(
                            name=field, port=port, connected_value=extra
                        )
                    else:
                        info = SinkPortInfo(
                            name=field,
                            port=port,
                            disconnected_value=extra,
                            value=attr.value,
                        )
                    self.port_infos[field] = info
                # If we are config tagged then subscribe so we can calculate
                # if we are modified
                if self._unmanaged_attr(field) and get_config_tag(tags):
                    if self.config_subscriptions:
                        new_id = max(self.config_subscriptions) + 1
                    else:
                        new_id = 1
                    subscribe = Subscribe(id=new_id, path=[self.mri, field, "value"])
                    subscribe.set_callback(self.update_part_modified)
                    self.config_subscriptions[new_id] = subscribe
                    spawned.append(self.child_controller.handle_request(subscribe))

        # Wait for the first update to come in for every subscription
        for s in spawned:
            s.wait()
        port_infos = [self.port_infos[f] for f in new_fields if f in self.port_infos]
        assert self.registrar, "No registrar assigned"
        self.registrar.report(PartExportableInfo(new_fields, port_infos))
示例#8
0
 def on_halt(self) -> None:
     unsubscribe = Unsubscribe()
     unsubscribe.set_callback(self.update_part_exportable)
     assert self.child_controller, "No child controller"
     self.child_controller.handle_request(unsubscribe)
示例#9
0
 def halt(self, context):
     super(RunnableChildPart, self).halt(context)
     unsubscribe = Unsubscribe(callback=self.update_part_configure_args)
     self.child_controller.handle_request(unsubscribe)
示例#10
0
    def test_handle_request(self):
        q = Queue()

        request = Get(id=41, path=["mri", "myAttribute"])
        request.set_callback(q.put)
        self.o.handle_request(request)
        response = q.get(timeout=0.1)
        self.assertIsInstance(response, Return)
        assert response.id == 41
        assert response.value["value"] == "hello_block"
        self.part.my_attribute.meta.writeable = False
        request = Put(
            id=42, path=["mri", "myAttribute"], value="hello_block2", get=True
        )
        request.set_callback(q.put)
        self.o.handle_request(request)
        response = q.get(timeout=0.1)
        self.assertIsInstance(response, Error)  # not writeable
        assert response.id == 42

        self.part.my_attribute.meta.writeable = True
        self.o.handle_request(request)
        response = q.get(timeout=0.1)
        self.assertIsInstance(response, Return)
        assert response.id == 42
        assert response.value == "hello_block2"

        request = Post(id=43, path=["mri", "method"])
        request.set_callback(q.put)
        self.o.handle_request(request)
        response = q.get(timeout=0.1)
        self.assertIsInstance(response, Return)
        assert response.id == 43
        assert response.value == "world"

        # cover the controller._handle_post path for parameters
        request = Post(id=43, path=["mri", "method"], parameters={"dummy": 1})
        request.set_callback(q.put)
        self.o.handle_request(request)
        response = q.get(timeout=0.1)
        self.assertIsInstance(response, Error)
        assert response.id == 43
        assert (
            str(response.message)
            == "Given keys ['dummy'], some of which aren't in allowed keys []"
        )

        request = Subscribe(id=44, path=["mri", "myAttribute"], delta=False)
        request.set_callback(q.put)
        self.o.handle_request(request)
        response = q.get(timeout=0.1)
        self.assertIsInstance(response, Update)
        assert response.id == 44
        assert response.value["typeid"] == "epics:nt/NTScalar:1.0"
        assert response.value["value"] == "hello_block2"

        request = Unsubscribe(id=44)
        request.set_callback(q.put)
        self.o.handle_request(request)
        response = q.get(timeout=0.1)
        self.assertIsInstance(response, Return)
        assert response.id == 44
示例#11
0
 def halt(self):
     # type: () -> None
     unsubscribe = Unsubscribe()
     unsubscribe.set_callback(self.update_part_exportable)
     self.child_controller.handle_request(unsubscribe)
示例#12
0
 def halt(self):
     unsubscribe = Unsubscribe(callback=self.handle_response)
     self.client_comms.send_to_server(unsubscribe)
示例#13
0
 def halt(self):
     # type: () -> None
     super(RunnableChildPart, self).halt()
     unsubscribe = Unsubscribe()
     unsubscribe.set_callback(self.update_part_configure_args)
     self.child_controller.handle_request(unsubscribe)