def udp_echo(self, host, port, echo_delay, echo_wait, verbose=True): ser = self.myserial echo_host = '35.212.147.4' port = '3030' write_sock = '0' # Use socket 0 for sending if self.udp_listen(port, 0, verbose=verbose): # Open listen port aerisutils.print_log('Listening on port: ' + port) else: return False # Open UDP socket to the host for sending echo command rmutils.write(ser, 'AT+QICLOSE=0', delay=1, verbose=verbose) # Make sure no sockets open mycmd = 'AT+QIOPEN=1,0,\"UDP\",\"' + echo_host + '\",' + port + ',0,1' rmutils.write( ser, mycmd, delay=1, verbose=verbose) # Create UDP socket connection as a client sostate = rmutils.write(ser, 'AT+QISTATE=1,0', verbose=verbose) # Check socket state if "UDP" not in sostate: # Try one more time with a delay if not connected sostate = rmutils.write(ser, 'AT+QISTATE=1,0', delay=1, verbose=verbose) # Check socket state # Send data udppacket = str('{"delay":' + str(echo_delay * 1000) + ', "ip":"' + self.my_ip + '","port":' + str(port) + '}') #udppacket = str('Echo test!') # print('UDP packet: ' + udppacket) mycmd = 'AT+QISEND=0,' + str(len(udppacket)) rmutils.write(ser, mycmd, udppacket, delay=0, verbose=verbose) # Write udp packet rmutils.write(ser, 'AT+QISEND=0,0', verbose=verbose) # Check how much data sent aerisutils.print_log('Sent echo command: ' + udppacket) if echo_wait == 0: # True indicates we sent the echo return True else: echo_wait = round(echo_wait + echo_delay) vals = rmutils.wait_urc( ser, echo_wait, self.com_port, returnonreset=True, returnonvalue='OK' ) # Wait up to X seconds to confirm data sent #print('Return: ' + str(vals)) vals = rmutils.wait_urc( ser, echo_wait, self.com_port, returnonreset=True, returnonvalue='+QIURC:' ) # Wait up to X seconds for UDP data to come in vals = super().parse_response(vals, '+QIURC:') print('Return: ' + str(vals)) if len(vals) > 2 and int(vals[2]) == len(udppacket): return True else: return False
def udp_echo(self, host, port, echo_delay, echo_wait, verbose=True): echo_host = '35.212.147.4' echo_port = '3030' listen_port = '3032' ser = self.myserial # Create a packet session in case there is not one self.create_packet_session() # Close socket if open rmutils.write(ser, 'AT#SL=1,0,' + listen_port + ',0', delay=1) rmutils.write(ser, 'AT#SH=1', delay=1) # Create UDP socket for sending and receiving mycmd = 'AT#SD=1,1,' + echo_port + ',"' + echo_host + '",0,' + listen_port + ',1,0,1' rmutils.write(ser, mycmd, delay=1) # Send our UDP packet udppacket = str( '{"delay":' + str(echo_delay * 1000) + ', "ip":' + self.my_ip + ',"port":' + listen_port + '}' + chr(26)) rmutils.write(ser, 'AT#SSEND=1', udppacket, delay=1) # Sending packets to socket aerisutils.print_log('Sent Echo command to remote UDP server') # Wait for data if echo_wait > 0: echo_wait = round(echo_wait + echo_delay) # Wait for data to come in; handle case where we go to sleep rmutils.wait_urc(ser, echo_wait, self.com_port, returnonreset=True, returnonvalue='APP RDY') # Try to read data rmutils.write(ser, 'AT#SRECV=1,1500,1', delay=1)
def get_module_ip(self, response): if len(response) < len('+CGPADDR: 1,'): aerisutils.print_log('Module IP Not Found') else: values = response.split('\r\n') self.my_ip = values[1].split(',')[1] aerisutils.print_log('Module IP is ' + self.my_ip)
def enable_psm(self, tau_time, atime, verbose=True): ser = self.myserial super().enable_psm(tau_time, atime, verbose) rmutils.write(ser, 'AT+QCFG="psm/urc",1', verbose=verbose) # Enable urc for PSM aerisutils.print_log( 'PSM is enabled with TAU: {0} s and AT: {1} s'.format( str(tau_time), str(atime)))
def bytes_or_utf(b, want_bytes=False, verbose=False): aerisutils.print_log( f'Returning bytes: {want_bytes} from something that is bytes {isinstance(b, bytes)}', verbose) if want_bytes: return b else: return b.decode('utf-8')
def udp_listen(self, listen_port, listen_wait, verbose=True, returnbytes=False): '''Starts listening for UDP packets. Parameters ---------- listen_port : int The port on which to listen. listen_wait : int Greater than zero if this method should wait for that many seconds for received packets. If less than or equal to zero, this method will return a boolean type. verbose : bool, optional returnbytes : bool, optional If True, returns bytes, instead of a string. Returns ------- s : bool False if a packet data session was not active, or if setting up the UDP socket failed. True if the modem successfully started listening for packets. m : str or bytes Any URCs that arrived while listening for packets. ''' ser = self.myserial read_sock = '1' # Use socket 1 for listen if self.create_packet_session(verbose=verbose): aerisutils.print_log('Packet session active: ' + self.my_ip) else: return False # Open UDP socket for listen mycmd = 'AT+QIOPEN=1,' + read_sock + ',"UDP SERVICE","127.0.0.1",0,' + str( listen_port) + ',1' rmutils.write(ser, mycmd, delay=1, verbose=verbose) # Create UDP socket connection sostate = rmutils.write(ser, 'AT+QISTATE=1,' + read_sock, verbose=verbose) # Check socket state if "UDP" not in sostate: # Try one more time with a delay if not connected sostate = rmutils.write(ser, 'AT+QISTATE=1,' + read_sock, delay=1, verbose=verbose) # Check socket state if "UDP" not in sostate: return False # Wait for data if listen_wait > 0: return rmutils.wait_urc( ser, listen_wait, self.com_port, returnonreset=True, returnbytes=returnbytes ) # Wait up to X seconds for UDP data to come in return True
def udp_listen(self, listen_wait, verbose): ser = self.myserial read_sock = '1' # Use socket 1 for listen if self.create_packet_session(): aerisutils.print_log('Packet session active: ' + self.my_ip) else: return False # Open UDP socket for listen rmutils.write(ser, 'AT#SLUDP=1,1,3030', delay=1) # Starts listener rmutils.write(ser, 'AT#SS', delay=1) if listen_wait > 0: rmutils.wait_urc(ser, listen_wait, self.com_port, returnonreset=True) # Wait up to X seconds for UDP data to come in rmutils.write(ser, 'AT#SS', delay=1) return True
def udp_echo(self, host, port, echo_delay, echo_wait, verbose=True): ser = self.myserial echo_host = host listen_port = port udp_socket = 0 # echo_host = '195.34.89.241' # ublox echo server # port = '7' # ublox echo server port # Make sure we have a packet session self.create_packet_session(verbose=verbose) # Close our read socket self.close_socket(udp_socket, verbose) # Create a UDP socket mycmd = 'AT+USOCR=17,' + str(listen_port) socket_id = (super().get_values_for_cmd(mycmd,'+USOCR:'))[0] #print('Socket ID = ' + str(socket_id)) # Send data udppacket = str( '{"delay":' + str(echo_delay * 1000) + ', "ip":"' + self.my_ip + '","port":' + str(listen_port) + '}') mycmd = 'AT+USOST=' + str(socket_id) + ',"' + echo_host + '",' + str(port) + ',' + str(len(udppacket)) rmutils.write(ser, mycmd, udppacket, delay=0, verbose=verbose) # Write udp packet aerisutils.print_log('Sent echo command: ' + udppacket, verbose) # Always wait long enough to verify packet sent vals = rmutils.wait_urc(ser, 5, self.com_port, returnonvalue='OK', verbose=verbose) #print('Return: ' + str(vals)) if echo_wait == 0: # True indicates we sent the echo return True else: # Wait for data echo_wait = round(echo_wait + echo_delay) # vals = rmutils.wait_urc(ser, echo_wait, self.com_port, returnonreset=True, # returnonvalue='APP RDY') # Wait up to X seconds for UDP data to come in vals = rmutils.wait_urc(ser, echo_wait, self.com_port, returnonreset=True, returnonvalue='+UUSORF:', verbose=verbose) #print('Return: ' + str(vals)) mycmd = 'AT+USORF=0,' + str(len(udppacket)) #vals = rmutils.write(ser, mycmd, verbose=verbose) # Read from socket vals = (super().get_values_for_cmd(mycmd,'+USORF:')) #print('Return: ' + str(vals)) if len(vals) > 3 and int(vals[3]) == len(udppacket): return True else: return False
def udp_listen(self, listen_port, listen_wait, verbose=True): ser = self.myserial udp_socket = 0 if self.create_packet_session(verbose=verbose): aerisutils.print_log('Packet session active: ' + self.my_ip) else: return False # Close our read socket self.close_socket(udp_socket, verbose) # Open UDP socket socket_id = (super().get_values_for_cmd('AT+USOCR=17','+USOCR:'))[0] print('Socket ID = ' + str(socket_id)) # Listen on udp socket port mycmd = 'AT+USOLI=' + str(socket_id) + ',' + str(listen_port) val = rmutils.write(ser, mycmd, verbose=verbose) # Wait for data up to X seconds if listen_wait > 0: rmutils.wait_urc(ser, listen_wait, self.com_port, returnonreset=True) return True
def test(ctx, timeout, delay): """Send UDP echo and wait for response \f """ timeout = timeout * 60 echo_host = '35.212.147.4' echo_port = 3030 echo_delay = 1 echo_wait = 4 # Get ready to do some timing start_time = time.time() elapsed_time = 0 aerisutils.print_log('Starting test for {0} seconds'.format(timeout)) while elapsed_time < timeout: success = my_module.udp_echo(echo_host, echo_port, echo_delay, echo_wait, verbose=ctx.obj['verbose']) aerisutils.print_log('Success: ' + str(success)) if not success: success = my_module.udp_echo(echo_host, echo_port, echo_delay, echo_wait, verbose=ctx.obj['verbose']) aerisutils.print_log('Retry success: ' + str(success)) time.sleep(delay - echo_delay - echo_wait) elapsed_time = time.time() - start_time # Do some cleanup tasks aerisutils.print_log('Finished test')
def parse_shoulder_tap(packet, imsi, verbose=False): '''Parses a "packet" into a shoulder-tap. Parameters ---------- packet : bytes Binary representation of a single shoulder-tap packet. imsi : str The IMSI of this device. verbose : bool, optional True for verbose debugging output. Returns ------- shoulder_tap : BaseShoulderTap The parsed shoulder-tap. May be None if there was a problem.''' # the UDP0 scheme is: # one STX character # two characters representing the shoulder-tap-type: "01" is UDP0 # four characters representing the sequence number # two characters representing the length of the payload # X bytes of binary data # one ETX character aerisutils.print_log( 'The entire packet is: <' + aerisutils.bytes_to_utf_or_hex(packet) + '>', verbose) # STX is ASCII value 2 / Unicode code point U+0002 STX = 2 # parse first character: it should be STX if packet[0] != STX: aerisutils.print_log('Error: first character was not STX', verbose=True) return None # parse next two characters: they should be "01" message_type = packet[1:3] if message_type == b'01': return parse_udp0_packet(packet[3:], imsi, verbose=verbose) else: aerisutils.print_log( 'Error: message type was not 01 for Udp0; it was ' + aerisutils.bytes_to_utf_or_hex(message_type), verbose=True) return None
def test(ctx, timeout, delay): """Send http request and wait for response \f """ timeout = timeout * 60 #http_host = 'httpbin.org' http_host = '35.212.147.4' http_port = 80 # Get ready to do some timing start_time = time.time() elapsed_time = 0 aerisutils.print_log('Starting test for {0} seconds'.format(timeout)) while elapsed_time < timeout: response = my_module.http_get(http_host, http_port, verbose=ctx.obj['verbose']) if response: response = True aerisutils.print_log('Success: ' + str(response)) time.sleep(delay) elapsed_time = time.time() - start_time # Do some cleanup tasks aerisutils.print_log('Finished test')
def test(ctx, timeout, cycletime, delay): """Test eDRX mode \f """ timeout = timeout * 60 echo_host = '35.212.147.4' echo_port = 3030 echo_delay = int(cycletime / 2) echo_wait = int(cycletime / 2) + 4 # Enable eDRX my_module.enable_edrx(cycletime, verbose=ctx.obj['verbose']) # Get ready to do some timing start_time = time.time() elapsed_time = 0 aerisutils.print_log('Starting test for {0} seconds'.format(timeout)) while elapsed_time < timeout: success = my_module.udp_echo(echo_host, echo_port, echo_delay, echo_wait + cycletime, verbose=ctx.obj['verbose']) aerisutils.print_log('Success: ' + str(success)) time.sleep(delay - echo_delay - echo_wait) elapsed_time = time.time() - start_time # Do some cleanup tasks my_module.disable_psm(verbose=ctx.obj['verbose']) aerisutils.print_log('Finished test')
def disable_psm(self, verbose): ser = self.myserial super().disable_psm(verbose) rmutils.write(ser, 'AT+QCFG="psm/urc",0', verbose=verbose) # Disable urc for PSM aerisutils.print_log('PSM and PSM/URC disabled')
def test(ctx, timeout, psmtau, psmat, delay): """Test PSM mode \f """ echo_host = '35.212.147.4' echo_port = 3030 echo_delay = delay echo_wait = 4 # Enable PSM my_module.enable_psm(psmtau, psmat, verbose=ctx.obj['verbose']) time.sleep(1.0) # Sleep to allow enable to complete # Make sure network allowed the configuration we asked for psm_settings = my_module.get_psm_info(ctx.obj['verbose']) if 'tau_network' not in psm_settings: exit() tau_network = int(psm_settings['tau_network']) if tau_network - psmtau > 120: my_module.disable_psm(verbose=ctx.obj['verbose']) aerisutils.print_log('Network settings not within tolerance.') return False aerisutils.print_log('Network tau: ' + str(tau_network)) # Get ready to do some timing start_time = time.time() elapsed_time = 0 aerisutils.print_log('Starting test for {0} seconds'.format(timeout)) while elapsed_time < timeout: #my_module.udp_echo(delay, 4, verbose=ctx.obj['verbose']) success = my_module.udp_echo(echo_host, echo_port, echo_delay, echo_wait, verbose=ctx.obj['verbose']) aerisutils.print_log('Success: ' + str(success)) rmutils.wait_urc(my_module.myserial, timeout, my_module.com_port, returnonreset=True, returnonvalue='APP RDY', verbose=ctx.obj['verbose']) # Wait up to X seconds for app rdy time.sleep(5.0) # Sleep in case it helps telit be able to connect my_module.init_serial(ctx.obj['comPort'], ctx.obj['apn'], verbose=ctx.obj['verbose']) rmutils.write(my_module.myserial, 'ATE0', verbose=ctx.obj['verbose']) # Turn off echo aerisutils.print_log('Connection state: ' + str(my_module.get_packet_info(verbose=ctx.obj['verbose']))) elapsed_time = time.time() - start_time # Do some cleanup tasks my_module.disable_psm(verbose=ctx.obj['verbose']) aerisutils.print_log('Finished test')
def udp_urcs_to_payloads(self, urcs, verbose=False): '''Parses a string of URCs representing UDP packet deliveries into a list of payloads, one per packet. Parameters ---------- urcs : bytes The unsolicited result codes as output from e.g. udp_listen When delivered to a connectID that is serving a service of "UDP SERVICE," the Quectel BG96 outputs these URCs as "+QIURC: "recv",<connectID>,<currentrecvlength>,"<remote IP address>",<remoteport><CR><LF><data> verbose : bool, optional True to enable verbose/debugging output. Unrecognized URCs will be logged regardless of this value. Returns ------- list An iterable of payloads, each a bytes object. ''' # state machine: # (initial) -> (receive: +QIURC: "recv") -> (parse <connectID>,<currentrecvlength>,"remote ip",<remoteport><CR><LF>) -> read <currentrecvlength> bytes -> (initial) # (initial) -> (receive: +) -> (read rest of line, output as "unexpected URC") -> (initial) CHAR_CR = 13 CHAR_LF = 10 URC_HEAD = b'+QIURC: "recv",' urc_regex = re.compile( rb'\+QIURC: "recv",(?P<connectID>\d+),(?P<currentrecvlength>\d+),"(?P<remoteIP>[^"]+)",(?P<remotePort>\d+)' ) payloads = [] current_input = urcs while len(current_input) > 0: aerisutils.print_log( 'Remaining input: ' + aerisutils.bytes_to_utf_or_hex(current_input), verbose) head = current_input[:len(URC_HEAD)] if head == URC_HEAD: # find the next carriage return next_carriage_return_index = current_input.find(b'\x0D') if next_carriage_return_index == -1: aerisutils.print_log( 'Error: no carriage returns after an URC') parse_result = urc_regex.search( current_input[:next_carriage_return_index]) aerisutils.print_log( 'QIURC parse result: ' + str(parse_result), verbose) if not parse_result: aerisutils.print_log('Error: failed to parse QIURC', verbose=True) connection_id = parse_result.group('connectID') aerisutils.print_log( 'Found connection ID: ' + aerisutils.bytes_to_utf_or_hex(connection_id), verbose) length = parse_result.group('currentrecvlength') aerisutils.print_log( 'Found length of received data: ' + aerisutils.bytes_to_utf_or_hex(length), verbose) remote_ip = parse_result.group('remoteIP') aerisutils.print_log( 'Found remote IP: ' + aerisutils.bytes_to_utf_or_hex(remote_ip), verbose) remote_port = parse_result.group('remotePort') aerisutils.print_log( 'Found remote port: ' + aerisutils.bytes_to_utf_or_hex(remote_port), verbose) # advance to the carriage return current_input = current_input[next_carriage_return_index:] # consume the CRLF if not (current_input[0] == CHAR_CR and current_input[1] == CHAR_LF): aerisutils.print_log( 'Sanity: the two bytes after the length were not a CRLF' ) current_input = current_input[2:] # consume the next number of bytes the URC said we would get, and advance that many payload = current_input[:int(length)] extracted_payload_length = len(payload) current_input = current_input[int(length):] if extracted_payload_length != int(length): aerisutils.print_log( f'Sanity: the packet extracted from the buffer was not {length} bytes long, it was {extracted_payload_length} bytes long. Ignoring packet.' ) continue payloads.append(payload) aerisutils.print_log( 'Found packet: ' + aerisutils.bytes_to_utf_or_hex(payload), verbose) # consume the trailing CRLF if not (current_input[0] == CHAR_CR and current_input[1] == CHAR_LF): aerisutils.print_log( 'Sanity: the two characters after the payload were not a CRLF' ) current_input = current_input[2:] else: # this is not the URC we expected # consume to the next newline, output as a warning or whatever, and try again newline_index = current_input.find(b'\n') if newline_index == -1: aerisutils.print_log( 'Warning: no newline at end of unexpected URC: <<' + aerisutils.bytes_to_utf_or_hex(current_input) + '>>') # consume the rest of the input current_input = [] else: unexpected_urc = current_input[:newline_index] # this might be a blank line, i.e., just a CRLF if unexpected_urc != b'\x0D': aerisutils.print_log( 'Warning: found unexpected URC: <<' + aerisutils.bytes_to_utf_or_hex(unexpected_urc) + '>>', verbose=True) current_input = current_input[newline_index + 1:] return payloads
def parse_udp0_packet(packet, imsi, verbose=False): '''Parses (most of) a Udp0 shoulder-tap packet into a Udp0ShoulderTap. Parameters ---------- packet : bytes The portion of the packet after the first three bytes, i.e., starting at the sequence number. imsi : str The IMSI of this device. verbose : bool, optional True for verbose output. Returns ------- Udp0ShoulderTap or None if there was a problem.''' ETX = b'\x03' sequence_hex = packet[:4] # length check: sequence_hex should be 4 bytes long if len(sequence_hex) != 4: aerisutils.print_log( f'Error: did not get enough sequence number bytes; expected 4, got {len(sequence_hex)}', verbose=True) return None aerisutils.print_log(f'Sequence number binary: {sequence_hex}', verbose=verbose) try: sequence_decimal = int(sequence_hex, base=16) except ValueError: aerisutils.print_log('Error: Sequence number was not hexadecimal', verbose=True) return None payload_length_hex = packet[4:6] aerisutils.print_log(f'Payload length in hex: {payload_length_hex}', verbose=verbose) # Length check: payload_length_check should be 2 bytes if len(payload_length_hex) != 2: aerisutils.print_log( f'Error: did not get enough payload length bytes; expected 2, got {len(payload_length_hex)}', verbose=True) return None try: payload_length_decimal = int(payload_length_hex, base=16) except ValueError: aerisutils.print_log('Error: payload length was not hexadecimal', verbose=True) return None payload = packet[6:6 + payload_length_decimal] if len(payload) != payload_length_decimal: aerisutils.print_log( 'Error: extracted payload length was not expected.', True) return None if len(payload) == 0: payload = None final_character = packet[6 + payload_length_decimal:7 + payload_length_decimal] if final_character != ETX: aerisutils.print_log( f'Error: byte after the payload was not an ETX; it was (binary) {final_character}', verbose=True) return None return shoulder_tap.Udp0ShoulderTap(payload, sequence_decimal, imsi)
def wait_urc(ser, timeout, com_port, returnonreset=False, returnonvalue=False, verbose=True, returnbytes=False): '''Wait for unsolicited result codes from the module. Parameters ---------- ser : serial port object The serial port the module is communicating on. timeout : int How many seconds to wait com_port : int The com port associated with the module returnonreset : bool, optional If truthy, this method will return as soon as there is a problem. Default: False. returnonvalue : any, optional If truthy, this function will return once this value is found on a line of output. Default: False. verbose : bool, optional True to print verbose output. returnbytes : bool, optional True to return a bytes object. Otherwise, tries to return a string decoded from UTF-8. If decoding any URC to UTF-8 fails, the returned string will be all of the preivous, successfully-decoded URCs. Default: False. Returns ------ Either a bytes or a string, depending on the returnbytes parameter. ''' mybytes = bytearray() myfinalout = None if returnbytes: myfinalout = b'' else: myfinalout = '' start_time = time.time() elapsed_time = 0 aerisutils.print_log( 'Starting to wait up to {0}s for URC; returning bytes: {1}'.format( timeout, returnbytes), verbose) while elapsed_time < timeout: try: while ser.inWaiting() > 0: mybyte = ser.read()[0] mybytes.append(mybyte) if mybyte == 10: # Newline if returnbytes: oneline = mybytes myfinalout = myfinalout + mybytes aerisutils.print_log( "<< " + aerisutils.bytes_to_utf_or_hex(oneline.strip()), verbose) else: try: oneline = mybytes.decode("utf-8") except UnicodeDecodeError as e: aerisutils.print_log('Error in wait_urc') return myfinalout myfinalout = myfinalout + oneline aerisutils.print_log("<< " + oneline.strip(), verbose) if returnonvalue: if oneline.find(returnonvalue) > -1: return myfinalout mybytes = bytearray() except IOError: aerisutils.print_log('Exception while waiting for URC.', verbose) ser.close() find_serial(com_port, verbose=True, timeout=(timeout - elapsed_time)) if returnonreset: return myfinalout else: ser.open() time.sleep(0.5) elapsed_time = time.time() - start_time aerisutils.print_log('Finished waiting for URC.', verbose) return myfinalout