コード例 #1
0
ファイル: test_hub.py プロジェクト: stuwilkins/caproto
def test_extract_address():
    old_style = ca.SearchResponse(port=6666, ip='1.2.3.4', cid=0, version=13)
    old_style.header.parameter1 = 0xffffffff
    old_style.sender_address = ('5.6.7.8', 6666)
    new_style = ca.SearchResponse(port=6666, ip='1.2.3.4', cid=0, version=13)
    ca.extract_address(old_style) == '1.2.3.4'
    ca.extract_address(new_style) == '5.6.7.8'
コード例 #2
0
ファイル: client.py プロジェクト: ZLLentz/caproto
    async def _broadcaster_queue_loop(self):
        await curio.spawn(self._broadcaster_recv_loop(), daemon=True)
        command = self.broadcaster.register('127.0.0.1')
        await self.send(ca.EPICS_CA2_PORT, command)

        while True:
            try:
                commands = await self.command_bundle_queue.get()
                self.broadcaster.process_commands(commands)
            except curio.TaskCancelled:
                break
            except Exception as ex:
                logger.error('Broadcaster command queue evaluation failed',
                             exc_info=ex)
                continue

            for command in commands:
                if isinstance(command, ca.RepeaterConfirmResponse):
                    self.registered = True
                if isinstance(command, ca.VersionResponse):
                    # Check that the server version is one we can talk to.
                    assert command.version > 11
                if isinstance(command, ca.SearchResponse):
                    name = self.unanswered_searches.pop(command.cid, None)
                    if name is not None:
                        self.search_results[name] = ca.extract_address(command)
                    else:
                        # This is a redundant response, which the spec tell us
                        # we must ignore.
                        pass

                async with self.broadcaster_command_condition:
                    await self.broadcaster_command_condition.notify_all()
コード例 #3
0
ファイル: client.py プロジェクト: teddyrendahl/caproto
    async def _broadcaster_queue_loop(self):
        await curio.spawn(self._broadcaster_recv_loop, daemon=True)

        while True:
            try:
                commands = await self.command_bundle_queue.get()
                self.broadcaster.process_commands(commands)
            except curio.TaskCancelled:
                break
            except Exception as ex:
                self.log.error('Broadcaster command queue evaluation failed',
                               exc_info=ex)
                continue

            for command in commands:
                if isinstance(command, ca.RepeaterConfirmResponse):
                    self.registered = True
                if isinstance(command, ca.VersionResponse):
                    # Check that the server version is one we can talk to.
                    if command.version <= 11:
                        self.log.debug('Old client on version %s',
                                       command.version)
                        continue
                if isinstance(command, ca.SearchResponse):
                    try:
                        name = self.unanswered_searches.pop(command.cid)
                    except KeyError:
                        # This is a redundant response, which the EPICS
                        # spec tells us to ignore. (The first responder
                        # to a given request wins.)
                        if name in self.search_results:
                            accepted_address = self.search_results[name]
                            new_address = ca.extract_address(command)
                            self.log.warning(
                                "PV found on multiple servers. "
                                "Accepted address is %s. "
                                "Also found on %s", accepted_address,
                                new_address)
                    else:
                        address = ca.extract_address(command)
                        self.log.debug('Found %s at %s', name, address)
                        self.search_results[name] = address

                async with self.broadcaster_command_condition:
                    await self.broadcaster_command_condition.notify_all()
コード例 #4
0
ファイル: client.py プロジェクト: stuwilkins/caproto
 def command_loop(self):
     while True:
         commands = self.command_bundle_queue.get()
         self.broadcaster.process_commands(commands)
         with self.command_cond:
             for command in commands:
                 if isinstance(command, ca.VersionResponse):
                     # Check that the server version is one we can talk to.
                     assert command.version > 11
                 if isinstance(command, ca.SearchResponse):
                     with self._search_lock:
                         name = self.unanswered_searches.get(
                             command.cid, None)
                         if name is not None:
                             self.search_results[name] = (
                                 ca.extract_address(command), time.time())
                             self.unanswered_searches.pop(command.cid)
                         else:
                             # This is a redundant response, which the spec
                             # tell us we must ignore.
                             pass
                 self.command_cond.notify_all()
コード例 #5
0
def test_nonet():
    # Register with the repeater.
    assert not cli_b._registered
    bytes_to_send = cli_b.send(ca.RepeaterRegisterRequest('0.0.0.0'))
    assert not cli_b._registered

    # Receive response
    data = bytes(ca.RepeaterConfirmResponse('127.0.0.1'))
    commands = cli_b.recv(data, cli_addr)
    cli_b.process_commands(commands)
    assert cli_b._registered

    # Search for pv1.
    # CA requires us to send a VersionRequest and a SearchRequest bundled into
    # one datagram.
    bytes_to_send = cli_b.send(ca.VersionRequest(0, ca.DEFAULT_PROTOCOL_VERSION),
                               ca.SearchRequest(pv1, 0,
                                                ca.DEFAULT_PROTOCOL_VERSION))

    commands = srv_b.recv(bytes_to_send, cli_addr)
    srv_b.process_commands(commands)
    ver_req, search_req = commands
    bytes_to_send = srv_b.send(
        ca.VersionResponse(ca.DEFAULT_PROTOCOL_VERSION),
        ca.SearchResponse(5064, None, search_req.cid, ca.DEFAULT_PROTOCOL_VERSION))

    # Receive a VersionResponse and SearchResponse.
    commands = iter(cli_b.recv(bytes_to_send, cli_addr))
    command = next(commands)
    assert type(command) is ca.VersionResponse
    command = next(commands)
    assert type(command) is ca.SearchResponse
    address = ca.extract_address(command)

    circuit = ca.VirtualCircuit(our_role=ca.CLIENT,
                                address=address,
                                priority=0)
    circuit.log.setLevel('DEBUG')
    chan1 = ca.ClientChannel(pv1, circuit)
    assert chan1.states[ca.CLIENT] is ca.SEND_CREATE_CHAN_REQUEST
    assert chan1.states[ca.SERVER] is ca.IDLE

    srv_circuit = ca.VirtualCircuit(our_role=ca.SERVER,
                                    address=address, priority=None)

    cli_send(chan1.circuit, ca.VersionRequest(priority=0,
                                              version=ca.DEFAULT_PROTOCOL_VERSION))

    srv_recv(srv_circuit)

    srv_send(srv_circuit, ca.VersionResponse(version=ca.DEFAULT_PROTOCOL_VERSION))
    cli_recv(chan1.circuit)
    cli_send(chan1.circuit, ca.HostNameRequest('localhost'))
    cli_send(chan1.circuit, ca.ClientNameRequest('username'))
    cli_send(chan1.circuit, ca.CreateChanRequest(name=pv1, cid=chan1.cid,
                                                 version=ca.DEFAULT_PROTOCOL_VERSION))
    assert chan1.states[ca.CLIENT] is ca.AWAIT_CREATE_CHAN_RESPONSE
    assert chan1.states[ca.SERVER] is ca.SEND_CREATE_CHAN_RESPONSE

    srv_recv(srv_circuit)
    assert chan1.states[ca.CLIENT] is ca.AWAIT_CREATE_CHAN_RESPONSE
    assert chan1.states[ca.SERVER] is ca.SEND_CREATE_CHAN_RESPONSE
    srv_chan1, = srv_circuit.channels.values()
    assert srv_chan1.states[ca.CLIENT] is ca.AWAIT_CREATE_CHAN_RESPONSE
    assert srv_chan1.states[ca.SERVER] is ca.SEND_CREATE_CHAN_RESPONSE

    srv_send(srv_circuit, ca.CreateChanResponse(cid=chan1.cid, sid=1,
                                                data_type=5, data_count=1))
    assert srv_chan1.states[ca.CLIENT] is ca.CONNECTED
    assert srv_chan1.states[ca.SERVER] is ca.CONNECTED

    # At this point the CLIENT is not aware that we are CONNECTED because it
    # has not yet received the CreateChanResponse. It should not be allowed to
    # read or write.
    assert chan1.states[ca.CLIENT] is ca.AWAIT_CREATE_CHAN_RESPONSE
    assert chan1.states[ca.SERVER] is ca.SEND_CREATE_CHAN_RESPONSE

    # Try sending a premature read request.
    read_req = ca.ReadNotifyRequest(sid=srv_chan1.sid,
                                    data_type=srv_chan1.native_data_type,
                                    data_count=srv_chan1.native_data_count,
                                    ioid=0)
    with pytest.raises(ca.LocalProtocolError):
        cli_send(chan1.circuit, read_req)

    # The above failed because the sid is not recognized. Remove that failure
    # by editing the sid cache, and check that it *still* fails, this time
    # because of the state machine prohibiting this command before the channel
    # is in a CONNECTED state.
    chan1.circuit.channels_sid[1] = chan1
    with pytest.raises(ca.LocalProtocolError):
        cli_send(chan1.circuit, read_req)

    cli_recv(chan1.circuit)
    assert chan1.states[ca.CLIENT] is ca.CONNECTED
    assert chan1.states[ca.SERVER] is ca.CONNECTED

    # Test subscriptions.
    assert chan1.native_data_type and chan1.native_data_count
    add_req = ca.EventAddRequest(data_type=chan1.native_data_type,
                                 data_count=chan1.native_data_count,
                                 sid=chan1.sid,
                                 subscriptionid=0,
                                 low=0, high=0, to=0, mask=1)
    cli_send(chan1.circuit, add_req)
    srv_recv(srv_circuit)
    add_res = ca.EventAddResponse(data=(3,),
                                  data_type=chan1.native_data_type,
                                  data_count=chan1.native_data_count,
                                  subscriptionid=0,
                                  status=1)

    srv_send(srv_circuit, add_res)
    cli_recv(chan1.circuit)

    cancel_req = ca.EventCancelRequest(data_type=add_req.data_type,
                                       sid=add_req.sid,
                                       subscriptionid=add_req.subscriptionid)

    cli_send(chan1.circuit, cancel_req)
    srv_recv(srv_circuit)

    # Test reading.
    cli_send(chan1.circuit, ca.ReadNotifyRequest(data_type=5, data_count=1,
                                                 sid=chan1.sid,
                                                 ioid=12))
    srv_recv(srv_circuit)
    srv_send(srv_circuit, ca.ReadNotifyResponse(data=(3,),
                                                data_type=5, data_count=1,
                                                ioid=12, status=1))
    cli_recv(chan1.circuit)

    # Test writing.
    request = ca.WriteNotifyRequest(data_type=2, data_count=1,
                                    sid=chan1.sid,
                                    ioid=13, data=(4,))

    cli_send(chan1.circuit, request)
    srv_recv(srv_circuit)
    srv_send(srv_circuit, ca.WriteNotifyResponse(data_type=5, data_count=1,
                                                 ioid=13, status=1))
    cli_recv(chan1.circuit)

    # Test "clearing" (closing) the channel.
    cli_send(chan1.circuit, ca.ClearChannelRequest(sid=chan1.sid, cid=chan1.cid))
    assert chan1.states[ca.CLIENT] is ca.MUST_CLOSE
    assert chan1.states[ca.SERVER] is ca.MUST_CLOSE

    srv_recv(srv_circuit)
    assert srv_chan1.states[ca.CLIENT] is ca.MUST_CLOSE
    assert srv_chan1.states[ca.SERVER] is ca.MUST_CLOSE

    srv_send(srv_circuit, ca.ClearChannelResponse(sid=chan1.sid, cid=chan1.cid))
    assert srv_chan1.states[ca.CLIENT] is ca.CLOSED
    assert srv_chan1.states[ca.SERVER] is ca.CLOSED
コード例 #6
0
def search(pv_name, udp_sock, timeout, *, max_retries=2):
    # Set Broadcaster log level to match our logger.
    b = ca.Broadcaster(our_role=ca.CLIENT)
    b.client_address = safe_getsockname(udp_sock)

    # Send registration request to the repeater
    logger.debug('Registering with the Channel Access repeater.')
    bytes_to_send = b.send(ca.RepeaterRegisterRequest())

    env = get_environment_variables()
    repeater_port = env['EPICS_CA_REPEATER_PORT']

    client_address_list = ca.get_client_address_list()
    local_address = ca.get_local_address()

    try:
        udp_sock.sendto(bytes_to_send, (local_address, repeater_port))
    except OSError as exc:
        raise ca.CaprotoNetworkError(
            f"Failed to send to {local_address}:{repeater_port}") from exc

    logger.debug("Searching for %r....", pv_name)
    commands = (ca.VersionRequest(0, ca.DEFAULT_PROTOCOL_VERSION),
                ca.SearchRequest(pv_name, 0, ca.DEFAULT_PROTOCOL_VERSION))
    bytes_to_send = b.send(*commands)
    tags = {
        'role': 'CLIENT',
        'our_address': b.client_address,
        'direction': '--->>>'
    }

    def send_search():
        for dest in client_address_list:
            tags['their_address'] = dest
            b.log.debug('%d commands %dB',
                        len(commands),
                        len(bytes_to_send),
                        extra=tags)
            try:
                udp_sock.sendto(bytes_to_send, dest)
            except OSError as exc:
                host, port = dest
                raise ca.CaprotoNetworkError(
                    f"Failed to send to {host}:{port}") from exc

    def check_timeout():
        nonlocal retry_at

        if time.monotonic() >= retry_at:
            send_search()
            retry_at = time.monotonic() + retry_timeout

        if time.monotonic() - t > timeout:
            raise CaprotoTimeoutError(
                f"Timed out while awaiting a response "
                f"from the search for {pv_name!r}. Search "
                f"requests were sent to this address list: "
                f"{ca.get_address_list()}.")

    # Initial search attempt
    send_search()

    # Await a search response, and keep track of registration status
    retry_timeout = timeout / max((max_retries, 1))
    t = time.monotonic()
    retry_at = t + retry_timeout

    try:
        orig_timeout = udp_sock.gettimeout()
        udp_sock.settimeout(retry_timeout)
        while True:
            try:
                bytes_received, address = udp_sock.recvfrom(ca.MAX_UDP_RECV)
            except socket.timeout:
                check_timeout()
                continue

            check_timeout()

            commands = b.recv(bytes_received, address)
            b.process_commands(commands)
            for command in commands:
                if isinstance(command, ca.SearchResponse) and command.cid == 0:
                    address = ca.extract_address(command)
                    logger.debug('Found %r at %s:%d', pv_name, *address)
                    return address
            else:
                # None of the commands we have seen are a reply to our request.
                # Receive more data.
                continue
    finally:
        udp_sock.settimeout(orig_timeout)
コード例 #7
0
def search(pv_name, udp_sock, timeout, *, max_retries=2):
    # Set Broadcaster log level to match our logger.
    b = ca.Broadcaster(our_role=ca.CLIENT)

    # Send registration request to the repeater
    logger.debug('Registering with the Channel Access repeater.')
    bytes_to_send = b.send(ca.RepeaterRegisterRequest())

    repeater_port = os.environ.get('EPICS_CA_REPEATER_PORT', 5065)
    for host in ca.get_address_list():
        udp_sock.sendto(bytes_to_send, (host, repeater_port))

    logger.debug("Searching for '%s'....", pv_name)
    bytes_to_send = b.send(
        ca.VersionRequest(0, ca.DEFAULT_PROTOCOL_VERSION),
        ca.SearchRequest(pv_name, 0, ca.DEFAULT_PROTOCOL_VERSION))

    def send_search():
        for host in ca.get_address_list():
            if ':' in host:
                host, _, specified_port = host.partition(':')
                dest = (host, int(specified_port))
            else:
                dest = (host, CA_SERVER_PORT)
            udp_sock.sendto(bytes_to_send, dest)
            logger.debug('Search request sent to %r.', dest)

    def check_timeout():
        nonlocal retry_at

        if time.monotonic() >= retry_at:
            send_search()
            retry_at = time.monotonic() + retry_timeout

        if time.monotonic() - t > timeout:
            raise TimeoutError(f"Timed out while awaiting a response "
                               f"from the search for {pv_name!r}. Search "
                               f"requests were sent to this address list: "
                               f"{ca.get_address_list()}.")

    # Initial search attempt
    send_search()

    # Await a search response, and keep track of registration status
    retry_timeout = timeout / max((max_retries, 1))
    t = time.monotonic()
    retry_at = t + retry_timeout

    try:
        orig_timeout = udp_sock.gettimeout()
        udp_sock.settimeout(retry_timeout)
        while True:
            try:
                bytes_received, address = udp_sock.recvfrom(ca.MAX_UDP_RECV)
            except socket.timeout:
                check_timeout()
                continue

            check_timeout()

            commands = b.recv(bytes_received, address)
            b.process_commands(commands)
            for command in commands:
                if isinstance(command, ca.SearchResponse) and command.cid == 0:
                    address = ca.extract_address(command)
                    logger.debug('Found %s at %s', pv_name, address)
                    return address
            else:
                # None of the commands we have seen are a reply to our request.
                # Receive more data.
                continue
    finally:
        udp_sock.settimeout(orig_timeout)
コード例 #8
0
ファイル: simple_client.py プロジェクト: stuwilkins/caproto
def main(*, skip_monitor_section=False):
    # A broadcast socket
    udp_sock = ca.bcast_socket()

    # Register with the repeater.
    bytes_to_send = b.send(ca.RepeaterRegisterRequest('0.0.0.0'))

    # TODO: for test environment with specific hosts listed in
    # EPICS_CA_ADDR_LIST
    if False:
        fake_reg = (('127.0.0.1', ca.EPICS_CA1_PORT),
                    [ca.RepeaterConfirmResponse(repeater_address='127.0.0.1')])
        b.command_queue.put(fake_reg)
    else:
        udp_sock.sendto(bytes_to_send, ('', CA_REPEATER_PORT))

        # Receive response
        data, address = udp_sock.recvfrom(1024)
        commands = b.recv(data, address)

    b.process_commands(commands)

    # Search for pv1.
    # CA requires us to send a VersionRequest and a SearchRequest bundled into
    # one datagram.
    bytes_to_send = b.send(ca.VersionRequest(0, 13),
                           ca.SearchRequest(pv1, 0, 13))
    for host in ca.get_address_list():
        if ':' in host:
            host, _, specified_port = host.partition(':')
            udp_sock.sendto(bytes_to_send, (host, int(specified_port)))
        else:
            udp_sock.sendto(bytes_to_send, (host, CA_SERVER_PORT))
    print('searching for %s' % pv1)
    # Receive a VersionResponse and SearchResponse.
    bytes_received, address = udp_sock.recvfrom(1024)
    commands = b.recv(bytes_received, address)
    b.process_commands(commands)
    c1, c2 = commands
    assert type(c1) is ca.VersionResponse
    assert type(c2) is ca.SearchResponse
    address = ca.extract_address(c2)

    circuit = ca.VirtualCircuit(our_role=ca.CLIENT,
                                address=address,
                                priority=0)
    circuit.log.setLevel('DEBUG')
    chan1 = ca.ClientChannel(pv1, circuit)
    sockets[chan1.circuit] = socket.create_connection(chan1.circuit.address)

    # Initialize our new TCP-based CA connection with a VersionRequest.
    send(chan1.circuit, ca.VersionRequest(priority=0, version=13))
    recv(chan1.circuit)
    # Send info about us.
    send(chan1.circuit, ca.HostNameRequest('localhost'))
    send(chan1.circuit, ca.ClientNameRequest('username'))
    send(chan1.circuit,
         ca.CreateChanRequest(name=pv1, cid=chan1.cid, version=13))
    commands = recv(chan1.circuit)

    # Test subscriptions.
    assert chan1.native_data_type and chan1.native_data_count
    add_req = ca.EventAddRequest(data_type=chan1.native_data_type,
                                 data_count=chan1.native_data_count,
                                 sid=chan1.sid,
                                 subscriptionid=0,
                                 low=0,
                                 high=0,
                                 to=0,
                                 mask=1)
    send(chan1.circuit, add_req)

    commands = recv(chan1.circuit)

    if not skip_monitor_section:
        try:
            print('Monitoring until Ctrl-C is hit. Meanwhile, use caput to '
                  'change the value and watch for commands to arrive here.')
            while True:
                commands = recv(chan1.circuit)
                if commands:
                    print(commands)
        except KeyboardInterrupt:
            pass

    cancel_req = ca.EventCancelRequest(data_type=add_req.data_type,
                                       sid=add_req.sid,
                                       subscriptionid=add_req.subscriptionid)

    send(chan1.circuit, cancel_req)
    commands, = recv(chan1.circuit)

    # Test reading.
    send(
        chan1.circuit,
        ca.ReadNotifyRequest(data_type=2, data_count=1, sid=chan1.sid,
                             ioid=12))
    commands, = recv(chan1.circuit)

    # Test writing.
    request = ca.WriteNotifyRequest(data_type=2,
                                    data_count=1,
                                    sid=chan1.sid,
                                    ioid=13,
                                    data=(4, ))

    send(chan1.circuit, request)
    recv(chan1.circuit)
    time.sleep(2)
    send(
        chan1.circuit,
        ca.ReadNotifyRequest(data_type=2, data_count=1, sid=chan1.sid,
                             ioid=14))
    recv(chan1.circuit)

    # Test "clearing" (closing) the channel.
    send(chan1.circuit, ca.ClearChannelRequest(chan1.sid, chan1.cid))
    recv(chan1.circuit)

    sockets.pop(chan1.circuit).close()
    udp_sock.close()