def test_client_addresses(monkeypatch, protocol, default_port, env_auto, env_addr, expected): env = ca.get_environment_variables() # Easier to test without netifaces monkeypatch.setattr(ca._utils, 'netifaces', None) env[f'EPICS_{protocol}_ADDR_LIST'] = env_addr env[f'EPICS_{protocol}_AUTO_ADDR_LIST'] = env_auto if protocol == 'CA': env['EPICS_CA_SERVER_PORT'] = int(default_port) elif protocol == 'PVA': env['EPICS_PVA_BROADCAST_PORT'] = int(default_port) patch_env(monkeypatch, env) assert set(ca.get_client_address_list(protocol=protocol)) == set(expected)
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)
def send_search(message): bytes_to_send = broadcaster.send(message) for host, port in get_client_address_list(protocol='PVA'): udp_sock.sendto(bytes_to_send, (host, port)) logger.debug('Search request sent to %r.', (host, port)) logger.debug('%s', bytes_to_send)