def test_search(): # uses nonstandard array type, so custom code path from caproto.pva import SearchRequestLE addr = '127.0.0.1' pv = 'TST:image1:Array' channel1 = {'id': 0x01, 'channel_name': pv} req = SearchRequestLE( sequence_id=1, flags=(pva.SearchFlags.reply_required | pva.SearchFlags.unicast), response_address=(addr, 8080), protocols=['tcp'], channels=[channel1],) # NOTE: cache needed here to give interface for channels cache = pva.CacheContext() serialized = req.serialize(cache=cache) assert req.response_address == addr assert req.channel_count == 1 assert serialized == ( b'\x01\x00\x00\x00\x81\x00\x00\x00' b'\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'\x00\xff\xff\x7f\x00\x00\x01\x90\x1f' b'\x01\x03tcp\x01\x00\x01\x00\x00\x00' b'\x10TST:image1:Array' ) deserialized, buf, consumed = SearchRequestLE.deserialize( bytearray(serialized), cache=cache) assert consumed == len(serialized) assert deserialized.channel_count == 1 assert deserialized.channels == [channel1] assert deserialized.response_address == addr channel2 = {'id': 0x02, 'channel_name': pv + '2'} req.channels = [channel1, channel2] serialized = req.serialize(cache=cache) assert req.channel_count == 2 assert serialized == ( b'\x01\x00\x00\x00\x81\x00\x00\x00' b'\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'\x00\xff\xff\x7f\x00\x00\x01\x90\x1f' b'\x01\x03tcp' b'\x02\x00' b'\x01\x00\x00\x00' b'\x10TST:image1:Array' b'\x02\x00\x00\x00' b'\x11TST:image1:Array2' ) deserialized, buf, consumed = SearchRequestLE.deserialize( bytearray(serialized), cache=cache) assert consumed == len(serialized) assert deserialized.channel_count == 2 assert deserialized.channels == [channel1, channel2]
def test_serialize(struct, structured_data, expected_serialized, endian): field = struct._pva_struct_ print(field.summary()) cache = pva.CacheContext() serialized = pva.to_wire(field, value=structured_data, endian=endian) serialized = b''.join(serialized) assert serialized == expected_serialized result, buf, offset = pva.from_wire( field, serialized, cache=cache, endian=endian, bitset=None) for key, value in result.items(): if isinstance(value, array): result[key] = value.tolist() assert result == structured_data
def test_variant_types_and_serialization(field_type, value, array_type): fd = pva.FieldDesc(name='test', field_type=field_type, array_type=array_type, size=1) cache = pva.CacheContext() for endian in (pva.LITTLE_ENDIAN, pva.BIG_ENDIAN): serialized = pva.to_wire(fd, value=value, cache=cache, endian=endian) serialized = b''.join(serialized) print(field_type, value, '->', serialized) res = pva.from_wire(fd, data=serialized, cache=cache, endian=endian) deserialized, buf, offset = res assert len(buf) == 0 assert offset == len(serialized) if field_type.name == 'string': assert deserialized == value else: assert_array_almost_equal(deserialized, value)
def test_fielddesc_examples(data, expected): cache = pva.CacheContext() info, buf, offset = pva.FieldDesc.deserialize(data, endian='<', cache=cache) print(info.summary() == expected)
def search(*pvs): '''Search for a PV over the network by broadcasting over UDP Returns: (host, port) ''' udp_sock = bcast_socket() udp_sock.bind(('', 0)) port = udp_sock.getsockname()[1] seq_id = random.randint(1, 2 ** 31) search_ids = {pv: random.randint(1, 2 ** 31) for pv in pvs} search_req = pva.SearchRequestLE( sequence_id=seq_id, flags=(pva.SearchFlags.reply_required | pva.SearchFlags.broadcast), response_address='127.0.0.1', # TODO host ip response_port=port, protocols=['tcp'], channels=[{'id': search_ids[pv], 'channel_name': pv} for pv in pvs] ) # NOTE: cache needed here to give interface for channels cache = pva.CacheContext() payload = search_req.serialize(cache=cache) header = pva.MessageHeaderLE( flags=(pva.MessageFlags.APP_MESSAGE | pva.MessageFlags.FROM_CLIENT | pva.MessageFlags.LITTLE_ENDIAN), command=search_req.ID, payload_size=len(payload) ) for addr, bcast_addr in get_netifaces_addresses(): search_req.response_address = addr bytes_to_send = bytes(header) + search_req.serialize(cache=cache) dest = (addr, pva.PVA_BROADCAST_PORT) print('Sending SearchRequest to', bcast_addr, 'requesting response at {}:{}'.format(addr, port)) udp_sock.sendto(bytes_to_send, dest) response_data, addr = udp_sock.recvfrom(1024) response_data = bytearray(response_data) print('Received from', addr, ':', response_data) response_header, buf, offset = pva.MessageHeaderLE.deserialize( response_data, cache=pva.NullCache) assert response_header.valid msg_class = response_header.get_message( pva.MessageFlags.FROM_SERVER, use_fixed_byte_order=pva.LITTLE_ENDIAN) print('Response header:', response_header) print('Response msg class:', msg_class) msg, buf, off = msg_class.deserialize(buf, cache=pva.NullCache) offset += off print('Response message:', msg) assert offset == len(response_data) if msg.found: id_to_pv = {id_: pv for pv, id_ in search_ids.items()} found_pv = id_to_pv[msg.search_instance_ids[0]] print('Found {} on {}:{}!' ''.format(found_pv, msg.server_address, msg.server_port)) return (msg.server_address, msg.server_port) else: # TODO as a simple client, this only grabs the first response from # the quickest server, which is clearly not the right way to do it raise ValueError('PVs {} not found in brief search' ''.format(pvs))
def main(host, server_port, pv): 'Directly connect to a host that has a PV' # cache of types from server our_cache = {} # local copy of types cached on server their_cache = {} # locally-defined types # user_types = pva.basic_types.copy() cache = pva.CacheContext(ours=our_cache, theirs=their_cache, ioid_interfaces={}) sock = socket.create_connection((host, server_port)) buf = bytearray(sock.recv(4096)) # (1) print() print('- 1. initialization: byte order setting') msg, buf, offset = pva.SetByteOrder.deserialize(buf, cache=cache) print('<-', msg, msg.byte_order_setting, msg.byte_order) server_byte_order = msg.byte_order client_byte_order = server_byte_order cli_messages = pva.messages[(client_byte_order, pva.MessageFlags.FROM_CLIENT)] # srv_msgs = pva.messages[(server_byte_order, # pva.MessageFlags.FROM_SERVER)] if msg.byte_order_setting == pva.EndianSetting.use_server_byte_order: fixed_byte_order = server_byte_order print('\n* Using fixed byte order:', server_byte_order) else: fixed_byte_order = None print('\n* Using byte order from individual messages.') # convenience functions: def send(msg): return send_message(sock, client_byte_order, server_byte_order, msg, cache) def recv(buf, **kw): return recv_message(sock, fixed_byte_order, server_byte_order, cache, buf, **kw) # (2) print() print('- 2. Connection validation request from server') auth_request, buf, off = recv(buf) # (3) print() print('- 3. Connection validation response') auth_cls = cli_messages[pva.ApplicationCommand.CONNECTION_VALIDATION] auth_resp = auth_cls( client_buffer_size=auth_request.server_buffer_size, client_registry_size=auth_request.server_registry_size, connection_qos=auth_request.server_registry_size, auth_nz='anonymous', # TODO: 'ca' doesn't actually require a payload? ) send(auth_resp) auth_ack, buf, off = recv(buf) # (4) print() print('- 4. Create channel request') create_cls = cli_messages[pva.ApplicationCommand.CREATE_CHANNEL] create_req = create_cls(count=1, channels=[{'id': 0x01, 'channel_name': pv}]) send(create_req) create_reply, buf, off = recv(buf) print('\n<-', create_reply) assert create_reply.status_type == pva.StatusType.OK server_chid = create_reply.server_chid # (5) print() print('- 5. Get field interface request') if_cls = cli_messages[pva.ApplicationCommand.GET_FIELD] if_req = if_cls(server_chid=server_chid, ioid=1, sub_field_name='') send(if_req) if_reply, buf, off = recv(buf) print(if_reply.field_if.summary()) pv_interface = if_reply.field_if struct_name = pv_interface.struct_name print('Structure name is', struct_name) print() print('PV interface cache now contains:') # for i, (key, intf) in enumerate(cache.ours.items()): # print('{}).'.format(i), key, intf) print(', '.join('{} ({})'.format(intf.struct_name, key) for key, intf in cache.ours.items())) reverse_cache = dict((v.struct_name, k) for k, v in cache.ours.items()) print('id for structure {} is {}'.format(struct_name, reverse_cache[struct_name])) # (6) print() print('- 6. Initialize the channel get request') get_ioid = 2 get_cls = cli_messages[pva.ApplicationCommand.GET] get_init_req = get_cls(server_chid=server_chid, ioid=get_ioid, subcommand=pva.Subcommand.INIT, pv_request='field()', # or if field() then pv_request ignored ) send(get_init_req) get_init_reply, buf, off = recv(buf) print('init reply', get_init_reply) interface = get_init_reply.pv_structure_if cache.ioid_interfaces[get_ioid] = get_init_reply.pv_structure_if print() print('Field info according to init:') print(interface.summary()) print('bitset descendents') print('------------------') for idx, item in enumerate(interface.descendents, 1): print(idx, item) # (7) print() print('- 7. Perform an actual get request') get_cls = cli_messages[pva.ApplicationCommand.GET] get_req = get_cls(server_chid=server_chid, ioid=get_ioid, # <-- note same ioid subcommand=pva.Subcommand.GET, ) send(get_req) get_reply, buf, off = recv(buf) get_data = get_reply.pv_data print('The response structure') print('----------------------') print(get_data['field'].summary()) print() print('The structured data') print('-------------------') pprint.pprint(get_data['value']) assert len(buf) == 0 return get_data
def __init__(self, category, input_value, endian, cache=None): self.category = category self.input_value = input_value self.serialized = None self.endian = endian self.cache = cache or pva.CacheContext()