Exemplo n.º 1
0
def test_cothread_ioc(cothread_ioc):
    import cothread
    from cothread.catools import ca_nothing, caget, caput, camonitor

    pre = cothread_ioc.pv_prefix

    with Listener(ADDRESS) as listener, listener.accept() as conn:

        select_and_recv(conn, "R")  # "Ready"

        # Start
        assert caget(pre + ":UPTIME").startswith("00:00:0")
        # WAVEFORM
        caput(pre + ":SINN", 4, wait=True)
        q = cothread.EventQueue()
        m = camonitor(pre + ":SIN", q.Signal, notify_disconnect=True)
        assert len(q.Wait(1)) == 4
        # STRINGOUT
        assert caget(pre + ":STRINGOUT") == "watevah"
        caput(pre + ":STRINGOUT", "something", wait=True)
        assert caget(pre + ":STRINGOUT") == "something"
        # Check pvaccess works
        from p4p.client.cothread import Context
        with Context("pva") as ctx:
            assert ctx.get(pre + ":STRINGOUT") == "something"

        conn.send("D")  # "Done"

        select_and_recv(conn, "D")  # "Done"

        # Stop
        cothread_ioc.proc.send_signal(signal.SIGINT)
        # Disconnect
        assert isinstance(q.Wait(10), ca_nothing)
        m.close()

    # check closed and output
    out, err = cothread_ioc.proc.communicate()
    out = out.decode()
    err = err.decode()
    # check closed and output
    try:
        assert "%s:SINN.VAL 1024 -> 4" % pre in out
        assert 'update_sin_wf 4' in out
        assert "%s:STRINGOUT.VAL watevah -> something" % pre in out
        assert 'on_update \'something\'' in out
        assert 'Starting iocInit' in err
        assert 'iocRun: All initialization complete' in err
    except Exception:
        # Useful printing for when things go wrong!
        print("Out:", out)
        print("Err:", err)
        raise
Exemplo n.º 2
0
 def do_init(self):
     super().do_init()
     self._ctxt = Context("pva", unwrap=False)
     self._queues: Dict[str, Queue] = {}
     self._monitors: Set[Subscription] = set()
Exemplo n.º 3
0
class PvaClientComms(builtin.controllers.ClientComms):
    """A class for a client to communicate with the server"""

    _monitors = None
    _ctxt = None
    _queues: Dict[str, Queue] = {}

    def do_init(self):
        super().do_init()
        self._ctxt = Context("pva", unwrap=False)
        self._queues: Dict[str, Queue] = {}
        self._monitors: Set[Subscription] = set()

    def do_disable(self):
        super().do_disable()
        # Unsubscribe to all the monitors
        for m in self._monitors:
            m.close()
        self._ctxt.close()

    def _update_settable_fields(self, update_fields: Set[str],
                                dotted_path: str, ob: Any) -> None:
        if isinstance(ob, dict):
            model_children = all([isinstance(ob[k], Model) for k in ob])
        else:
            model_children = False

        if isinstance(ob, Model) or model_children:
            # Recurse down
            for k in ob:
                self._update_settable_fields(update_fields,
                                             f"{dotted_path}.{k}", ob[k])
        else:
            # This is a terminal field, add to the set
            update_fields.add(dotted_path)

    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 _regenerate_block(self, block: BlockModel, value: Value,
                          update_fields: Set[str]) -> None:
        # This is an initial update, generate the list of all fields
        # TODO: very similar to websocketclientcomms
        for field in list(block):
            if field not in ("health", "meta"):
                block.remove_endpoint(field)
        for k, v in value.items():
            if k == "health":
                # Update health attribute
                block.health.set_value(
                    value=v["value"],
                    alarm=convert_value_to_dict(v["alarm"]),
                    ts=convert_value_to_dict(v["timeStamp"]),
                )
            elif k == "meta":
                # Update BlockMeta
                meta: BlockMeta = block.meta
                for n in meta.call_types:
                    meta.apply_change([n], v[n])
            else:
                # Add new Attribute/Method
                v = convert_value_to_dict(v)
                block.set_endpoint_data(k, v)
            # Update the list of fields
            self._update_settable_fields(update_fields, k, block[k])

    def _update_block(self, block: BlockModel, value: Value,
                      update_fields: Set[str]) -> None:
        # This is a subsequent update
        changed = value.changedSet(parents=True, expand=False)
        for k in changed.intersection(update_fields):
            v = value[k]
            if isinstance(v, Value):
                v = convert_value_to_dict(v)
            block.apply_change(k.split("."), v)

    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
        """
        path = attribute_name + ".value"
        typ, value = convert_to_type_tuple_value(value)
        if isinstance(typ, tuple):
            # Structure, make into a Value
            _, typeid, fields = typ
            value = Value(Type(fields, typeid), value)
        try:
            self._ctxt.put(mri, {path: value}, path)
        except RemoteError:
            if attribute_name == "exports":
                # TODO: use a tag instead of a name
                # This will change the structure of the block
                # Wait for reconnect
                self._queues[mri].get(timeout=DEFAULT_TIMEOUT)
            else:
                # Not expected, raise
                raise

    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
        """
        typ, parameters = convert_to_type_tuple_value(params)
        uri = NTURI(typ[2])

        uri = uri.wrap(path=f"{mri}.{method_name}",
                       kws=parameters,
                       scheme="pva")
        value = self._ctxt.rpc(mri, uri, timeout=None)
        return convert_value_to_dict(value)
Exemplo n.º 4
0
    def test_configure(self):
        block = self.process2.block_view("TESTDET")
        self.check_blocks_equal()
        generator = self.make_generator()
        # an_empty_list = np.ndarray(shape=(0,), dtype=np.int64)
        # for some reason, the above is not equating to array([]) in Py3
        an_empty_list = pytest.approx([])
        validated = dict(
            generator=generator.to_dict(),
            fileDir=self.tmpdir,
            axesToMove=["x", "y"],
            fileTemplate="%s.h5",
            breakpoints=an_empty_list,
            formatName="det",
            exposure=0.0489975,
        )
        params = block.configure(generator, self.tmpdir, axesToMove=["x", "y"])

        assert validated == params
        # TODO: ordering is not maintained in PVA, so may need to wait before
        #       get
        # block._context.sleep(0.1)
        assert "Armed" == block.state.value
        assert block.configure.took.value == dict(
            generator=generator.to_dict(),
            axesToMove=["x", "y"],
            breakpoints=an_empty_list,
            exposure=0.0,
            fileDir=self.tmpdir,
            fileTemplate="",
            formatName="",
        )
        assert block.configure.took.present == [
            "generator", "fileDir", "axesToMove"
        ]
        assert block.configure.returned.value == validated
        assert block.configure.returned.present == [
            "generator",
            "fileDir",
            "axesToMove",
            "breakpoints",
            "exposure",
            "formatName",
            "fileTemplate",
        ]
        self.check_blocks_equal()
        # Check the NTTable
        from p4p.client.cothread import Context

        with Context("pva") as ctxt:
            table = ctxt.get("TESTDET.datasets")
            assert table.getID() == "epics:nt/NTTable:1.0"
            assert dict(table.value.items()) == dict(
                filename=["det.h5", "det.h5", "det.h5", "det.h5"],
                name=["det.data", "det.sum", "y.value_set", "x.value_set"],
                path=[
                    "/entry/data", "/entry/sum", "/entry/y_set", "/entry/x_set"
                ],
                rank=pytest.approx([4, 4, 1, 1]),
                type=["primary", "secondary", "position_set", "position_set"],
                uniqueid=["/entry/uid", "/entry/uid", "", ""],
            )
            labels = ["name", "filename", "type", "rank", "path", "uniqueid"]
            assert list(table.meta.elements) == labels
            assert table.labels == labels
Exemplo n.º 5
0
    def make_pva_context(self, *args, **kwargs):
        from p4p.client.cothread import Context

        ctxt = Context("pva", *args, **kwargs)
        self.addCleanup(ctxt.close)
        return ctxt
Exemplo n.º 6
0
 def do_init(self):
     super(PvaClientComms, self).do_init()
     self._ctxt = Context("pva", unwrap=False)
     self._queues = {}  # type: Dict[str, Queue]
     self._monitors = set()  # type: Set[Subscription]
from p4p.client.cothread import Context

ctx = Context("pva")
print(ctx.get("MY-DEVICE-PREFIX:AI"))
print(ctx.get("MY-DEVICE-PREFIX:AO"))
ctx.put("MY-DEVICE-PREFIX:AO", "999")
print(ctx.get("MY-DEVICE-PREFIX:AO"))