1# Copyright 2019 Oticon A/S 2# SPDX-License-Identifier: Apache-2.0 3 4#EDTT Transport driver for BabbleSim 5# Any EDTT Transport shall implement the following interface: 6# __init__(args) 7# connect() 8# send(idx, message) 9# recv(idx, number_bytes, timeout) 10# wait(time) 11# close() 12# get_time() 13# n_devices : Number of devices it is connected to 14 15import struct; 16import os; 17import signal; 18import stat; 19 20def create_dir(folderpath): 21 if not os.path.exists(folderpath): 22 if ( os.mkdir( folderpath , stat.S_IRWXG | stat.S_IRWXU ) != 0 ) \ 23 and ( os.access( folderpath, os.F_OK ) == False ): 24 raise Exception("Cannot create folder %s"% folderpath); 25 26def create_com_folder(sim_id): 27 import getpass 28 Com_path = "/tmp/bs_" + getpass.getuser(); 29 create_dir(Com_path); 30 Com_path = Com_path + "/" + sim_id; 31 create_dir(Com_path); 32 return Com_path; 33 34def Create_FIFO_if_not_there(FIFOName): 35 #we try to create a fifo which may already exist, and/or that some other 36 #program may be racing to create at the same time 37 if ( os.access( FIFOName, os.F_OK ) == False ): 38 try: 39 err = os.mkfifo(FIFOName, stat.S_IRWXG | stat.S_IRWXU); 40 except OSError as e: 41 if (e.errno == 17): #File already exists => we are done 42 return; 43 else: 44 raise Exception("Could not create FIFO %s", FIFOName); 45 46 if (err != 0) and ( os.access( FIFOName, os.F_OK ) == False ): 47 raise Exception("Could not create FIFO %s", FIFOName); 48 49 50class EDTTT: 51 COM_DISCONNECT = 0; 52 COM_WAIT = 1; 53 COM_SEND = 2; 54 COM_RCV = 3; 55 56 TO_EDTT = 0; 57 TO_BRIDGE =1; 58 FIFOs = [-1, -1]; 59 FIFOnames = [ "" , "" ]; 60 verbosity = 0; 61 last_t = 0; #last timestamp received from the bridge 62 Connected = False; 63 n_devices = 0; 64 65 def __init__(self, pending_args, TraceClass): 66 self.Trace = TraceClass; 67 import argparse 68 parser = argparse.ArgumentParser(prog="BabbleSim transport options:", add_help=False) 69 parser.add_argument("-s", "--sim_id", required=True, help="Simulation id"); 70 parser.add_argument("-d", "--bridge-device-nbr", required=True, help="Device number of the EDTT bridge"); 71 (args, discard) = parser.parse_known_args(pending_args) 72 73 self.device_nbr = args.bridge_device_nbr; 74 self.sim_id = args.sim_id; 75 76 def connect(self): 77 Com_path = create_com_folder(self.sim_id); 78 79 self.Trace.trace(3,"Connecting to EDTT bridge"); 80 81 #Note that python ignores SIGPIPE by default 82 83 self.FIFOnames[self.TO_EDTT] = \ 84 Com_path + "/Device" + str(self.device_nbr) + ".ToPTT"; 85 self.FIFOnames[self.TO_BRIDGE] = \ 86 Com_path + "/Device" + str(self.device_nbr) + ".ToBridge"; 87 88 Create_FIFO_if_not_there(self.FIFOnames[self.TO_EDTT]); 89 Create_FIFO_if_not_there(self.FIFOnames[self.TO_BRIDGE]); 90 91 self.FIFOs[self.TO_BRIDGE] = os.open(self.FIFOnames[self.TO_BRIDGE], os.O_WRONLY); 92 self.FIFOs[self.TO_EDTT] = os.open(self.FIFOnames[self.TO_EDTT], os.O_RDONLY); 93 94 if (self.FIFOs[self.TO_BRIDGE] == -1): 95 raise Exception("Could not open FIFO %s"% self.FIFOnames[self.TO_BRIDGE]); 96 97 if (self.FIFOs[self.TO_EDTT] == -1): 98 raise Exception("Could not open FIFO %s"% self.FIFOnames[self.TO_EDTT]); 99 100 self.Connected = True; 101 self.Trace.trace(4,"Connected to EDTT bridge"); 102 103 packet = self.read(2); 104 self.n_devices = struct.unpack('<H',packet[0:2])[0]; 105 106 def cleanup(self): 107 self.Trace.trace(4,"Cleaning up transport"); 108 try: 109 os.close(self.FIFOs[self.TO_BRIDGE]); 110 self.Trace.trace(9,"Closed FIFO to Bridge"); 111 os.close(self.FIFOs[self.TO_EDTT]); 112 self.Trace.trace(9,"Closed FIFO to EDTT"); 113 os.remove(self.FIFOnames[self.TO_BRIDGE]); 114 os.remove(self.FIFOnames[self.TO_EDTT]); 115 except OSError: 116 self.Trace.trace(9,"(minor) Error closing FIFO " 117 "(most likely either file does not exist yet)"); 118 119 def disconnect(self): 120 if self.Connected: 121 self.Trace.trace(4,"Disconnecting bridge") 122 self.ll_send(struct.pack('<B', self.COM_DISCONNECT)); 123 self.Connected = False; 124 125 def close(self): 126 self.disconnect(); 127 self.cleanup(); 128 129 def get_last_t(self): 130 return self.last_t 131 132 def ll_send(self, content): 133 try: 134 written = os.write(self.FIFOs[self.TO_BRIDGE], content); 135 if written != len(content): 136 raise; 137 except: 138 self.Trace.trace(4,"The EDTT bridge disappeared when trying to " 139 "write to it"); 140 self.cleanup(); 141 raise Exception("Abruptly disconnected from bridge"); 142 143 def ll_read(self, nbytes): 144 try: 145 pkt = os.read(self.FIFOs[self.TO_EDTT], nbytes); 146 if len(pkt) == 0: 147 raise; 148 return pkt; 149 except: 150 self.Trace.trace(4,"The EDTT bridge disapeared when trying to read " 151 "from it"); 152 self.cleanup(); 153 raise Exception("Abruptly disconnected from bridge"); 154 155 def send(self, idx, message): 156 if (idx > self.n_devices -1): 157 raise Exception("Trying to access unconnected device %i"%idx); 158 # build the packet 159 packet = struct.pack('<BBH', self.COM_SEND, idx, len(message)) + message 160 # send the packet 161 self.ll_send(packet) 162 # a send is immediate (no time advance) 163 164 def read(self, nbytes): 165 received_nbytes = 0; 166 packet = bytearray(); 167 #print "Will try to pick " + str(nbytes) + " bytes" 168 while ( len(packet) < nbytes): 169 packet += self.ll_read(nbytes - received_nbytes); 170 #print "Got so far " + str(len(packet)) + " bytes" 171 #print 'packet: "' + repr(packet) + '"' 172 return packet; 173 174 def recv(self, idx, number_bytes, to=None): 175 if (idx > self.n_devices -1): 176 raise Exception("Trying to access unconnected device %i"%idx); 177 178 #print ("EDTT: ("+str(idx)+") request rcv of "+ str(number_bytes) + " bytes; to = " + str(to)); 179 if to == None: 180 to = self.default_to 181 if ( number_bytes == 0 ): 182 return b"" 183 184 timeout = to*1000 + self.last_t; 185 # Poll the bridge for a response 186 #print ("EDTT: ("+str(idx)+") request rcv of "+ str(number_bytes) + " bytes; timeout = " + str(timeout)); 187 188 self.ll_send(struct.pack('<BBQH', self.COM_RCV, idx, timeout, number_bytes)); 189 190 header = self.read(9); 191 192 #print '"' + repr(header[1:])+ '"' 193 #print "length = " + str(len(header[1:])) 194 self.last_t = struct.unpack('<Q',header[1:])[0]; 195 #print "last_t updated to " + str(self.last_t) 196 197 packet=b"" 198 if header[0] == 0x00: #correct reception => packet follows 199 #print "correctly received " + str(number_bytes) + " bytes" 200 packet = self.read(number_bytes) 201 if (len(packet) != number_bytes): 202 raise Exception("very weird..") 203 #print "packet itself =" + repr(packet) 204 205 return packet 206 207 def wait(self, delay_in_ms): 208 # pack a message : delay 4 bytes 209 end_of_wait = delay_in_ms*1000 + self.last_t; 210 self.ll_send(struct.pack('<BQ', self.COM_WAIT, end_of_wait)); 211 self.last_t = end_of_wait; 212 213 def get_time(self): 214 return self.get_last_t()/1000; 215