1. Interface with the Bundle Protocol (BP)¶
pyion provides two main abstractions to send/receive data through BP:
- BpProxy: Proxy to ION engine for a given node (identified by a node number). You should only ever have one proxy per node number. To ensure that this is the case, do not create Proxies manually. Instead, use
pyion.get_bp_proxy. - Endpoint: Endpoint to send/receive data using BP. Do not open/close/interrupt/delete endpoints manually, always do it through the proxy (note that all
Endpointsreference theProxythat create them).
While sending data through ION’s BP, several properties can be specified (e.g., time-to-live, required reports, reporting endpoint, etc). These can be defined as inherent to the endpoint (i.e., all bundles send through this endpoint will have a give TTL), in which case they must be specified while calling bp_open in the BpProxy object, or as one-of properties for a specific bundle (in which case they must be specified while calling bp_send in the Endpoint object).
Not all features available in ION are currently supported. For instance, while you can request BP reports and provide a reporting endpoint, reception of the reports is for now left in binary format and it is thus up to the user to decode them. Similarly, bundles cannot specify advanced class of service properties (ancillary data). Finally, pyion does not provide any flow control mechanisms when sending data over an endpoint. This means that if you overflow the SDR memory, a Python MemoryError exception will be raise and it is up to the user to handle it.
1.1. Endpoints as Python Context Managers¶
Endpoint objects can be used as context managers since they should always be openend and closes, just like file descriptors or sockets. This can be observed in the following very simple example:
Example 1.a: BP Transmitter
1 2 3 4 5 6 7 8 9 | import pyion # Create a proxy to node 1 and attach to ION proxy = pyion.get_bp_proxy(1) proxy.bp_attach() # Open endpoint 'ipn:1.1' and send data to 'ipn:2.1' with proxy.bp_open('ipn:1.1') as eid: eid.bp_send('ipn:2.1', b'hello') |
Example 1.b: BP Receiver
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | python import pyion # Create a proxy to node 2 and attach to it proxy = pyion.get_bp_proxy(2) proxy.bp_attach() # Listen to 'ipn:2.1' for incoming data with proxy.bp_open('ipn:2.1') as eid: while eid.is_open: try: # This is a blocking call. print('Received:', eid.bp_receive()) except InterruptedError: # User has triggered interruption with Ctrl+C break |
Note that the endpoint is opened from the proxy. The same is true for closing and interrupting and endpoint. Therefore, the only operations that can be triggered directly from the endpoint are send and receive (some additional convencience operations are also available, e.g., send a file).
1.2. Endpoints as Class Instances¶
Endpoints can also be used as class instances that are manually opened and closed by the user. The later is actually not necessary since endpoints will be closed automatically when they are garbage collected.
Here is a more complicated example of a transmitter and receiver that exchange data. At the receiver, the amount of data exchanged is measured, as well as the data rate at which the connection is running. Additionally, this example also demonstrates that typical bundle properties can be provided to an endpoint as a way to configure the BP operation.
Example 2.a: BP Transmitter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | from datetime import datetime from threading import Thread import time # Import module import pyion from pyion import BpCustodyEnum, BpPriorityEnum, BpReportsEnum # ================================================================= # === Define global variables # ================================================================= # ION node number node_nbr = 1 # Originating and destination endpoints orig_eid = 'ipn:1.1' dest_eid = 'ipn:2.1' rept_eid = 'ipn:1.2' # Define endpoint properties ept_props = { 'TTL': 3600, # [sec] 'custody': BpCustodyEnum.SOURCE_CUSTODY_REQUIRED, 'priority': BpPriorityEnum.BP_EXPEDITED_PRIORITY, 'report_eid': rept_eid, 'report_flags': BpReportsEnum.BP_RECEIVED_RPT #'report_flags': BpReportsEnum.BP_RECEIVED_RPT | BpReportsEnum.BP_CUSTODY_RPT, } # Create a proxy to ION proxy = pyion.get_bp_proxy(node_nbr) # Attach to ION proxy.bp_attach() # ================================================================= # === Acquire reports # ================================================================= # Open endpoint to get reports rpt_eid = proxy.bp_open(rept_eid) def print_reports(): while True: try: data = rpt_eid.bp_receive() print(data) except InterruptedError: break # Start monitoring thread th = Thread(target=print_reports, daemon=True) th.start() # ================================================================= # === MAIN # ================================================================= # Open a endpoint and set its properties. Then send file with proxy.bp_open(orig_eid, **ept_props) as eid: for i in range(50): eid.bp_send(dest_eid, str(datetime.now()) + ' - ' + 'a'*1000) # Sleep for a while and stop the monitoring thread time.sleep(2) proxy.bp_interrupt(rept_eid) th.join() |
Example 2.b: BP Receiver
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | from datetime import datetime import sys import time # Import module import pyion # ================================================================= # === Define global variables # ================================================================= # ION node number node_nbr = 2 # Endpoint to listen to EID = 'ipn:2.1' # ================================================================= # === MAIN # ================================================================= # Create a proxy to ION's BP proxy = pyion.get_bp_proxy(node_nbr) # Attach to ION proxy.bp_attach() # Open a proxy to receive data with proxy.bp_open(EID) as eid: # You are now ready to received print('{} ready to receive'.format(eid)) nbnd, nbytes, elapsed = 0, 0, 0 # Receive while eid.is_open: try: # This is a blocking call data = eid.bp_receive() try: data = data.decode('utf-8') if nbnd == 0: tic = time.time() # Separate timestamp and data ts, msg = data.split(' - ') # Convert timestamp ts = datetime.strptime(ts, '%Y-%m-%d %H:%M:%S.%f') # Get the time it took for data to arrive dt = (datetime.now()-ts).total_seconds() # Report statistics nbnd += 1 nbytes += sys.getsizeof(data) elapsed = (time.time() - tic) print('{}) Total bytes {} / {:.3f} seconds = {:.3f} bytes/sec'.format(nbnd, nbytes, elapsed, nbytes/elapsed)) except UnicodeDecodeError: print(data) except InterruptedError: break |