class Application(cmd.Cmd): def __init__(self, transport=None, stdout=None): # cmd Initialization and configuration cmd.Cmd.__init__(self, stdout=stdout) self.intro = 'pytelemetry terminal started.' \ + ' (type help for a list of commands.)' self.prompt = ':> ' self.file = None # pytelemetry setup if not transport: self.transport = transports.SerialTransport() else: self.transport = transport self.telemetry = Pytelemetry(self.transport) self.topics = Topics() self.plots = [] self.plotsLock = Lock() self.runner = Runner(self.transport, self.telemetry, self.plots, self.plotsLock, self.topics) self.telemetry.subscribe(None, self.topics.process) self.types_lookup = { '--s': 'string', '--u8': 'uint8', '--u16': 'uint16', '--u32': 'uint32', '--i8': 'int8', '--i16': 'int16', '--i32': 'int32', '--f32': 'float32' } logger.info("Module path : %s" % os.path.dirname(os.path.realpath(__file__))) try: logger.info("Module version : %s" % __version__) except: logger.warning("Module version : not found.") def emptyline(self): pass # Override default behavior to repeat last command if empty input @docopt_cmd def do_serial(self, arg): """ List serial ports or connect to one of them. Usage: serial ((-l | --list) | <port> [options]) Options: -b X, --bauds X Connection speed in bauds [default: 9600] """ if arg['--list'] or arg['-l']: self.stdout.write("Available COM ports:\n") for port, desc, hid in list_ports.comports(): self.stdout.write("%s \t %s\n" % (port, desc)) return try: self.runner.disconnect() logger.warn("User requested connect without desconnecting first.") except (IOError, AttributeError) as e: logger.warn( "Already disconnected. Continuing happily. E : {0}".format(e)) pass self.topics.clear() logger.info("Cleared all topics for new session.") self.transport.resetStats(averaging_window=10) self.runner.resetStats() self.telemetry.resetStats() logger.info("Cleared all stats for new session.") try: b = int(arg['--bauds']) self.runner.connect(arg['<port>'], b) except IOError as e: self.stdout.write( "Failed to connect to {0} at {1} (bauds).\n".format( arg['<port>'], b)) logger.warn( "Failed to connect to {0} at {1} (bauds). E : \n".format( arg['<port>'], b, e)) else: s = "Connected to {0} at {1} (bauds).\n".format(arg['<port>'], b) self.stdout.write(s) logger.info(s) @docopt_cmd def do_print(self, arg): """ Prints X last received samples from <topic>. Usage: print <topic> [options] Options: -a, --all Display all received samples under <topic> -l X, --limit X Display X last received samples under <topic> [default: 1] """ topic = arg['<topic>'] if not self.topics.exists(topic): s = "Topic '{0}' unknown. Type 'ls' to list all available topics.\n".format( topic) self.stdout.write(s) logger.warn(s) return try: if arg['--all']: amount = 0 # 0 is understood as 'return all samples' by self.topics.samples() else: amount = int(arg['--limit']) except: s = "Could not cast --limit = '{0}' to integer. Using 1.\n".format( arg['--limit']) self.stdout.write(s) logger.warn(s) amount = 1 s = self.topics.samples(topic, amount) if s is not None: for i in s: self.stdout.write("{0}\n".format(i)) else: logger.error( "Could not retrieve {0} sample(s) under topic '{1}'.\n".format( amount, topic)) @docopt_cmd def do_ls(self, arg): """ Prints available topics. Topics are basically labels under which data is available (for display, plot, etc). Data can come from remote source (a connected embedded device) or the command-line interface itself (reception speed, etc). Without flags, prints a list of remote topics. Usage: ls [options] Options: -c, --cli Prints all CLI topics. Use this to display topics for monitoring reception speed, errors amount, etc. """ if arg['--cli']: for topic in self.topics.ls(source='cli'): self.stdout.write("%s\n" % topic) return for topic in self.topics.ls(source='remote'): self.stdout.write("%s\n" % topic) @docopt_cmd def do_plot(self, arg): """ Plots <topic> in a graph window. Usage: plot <topic> """ topic = arg['<topic>'] if not self.topics.exists(topic): s = "Topic '{0}' unknown. Type 'ls' to list all available topics.\n".format( topic) self.stdout.write(s) logger.warn(s) return if self.topics.intransfer(topic): s = "Topic '{0}' already plotting.\n".format(topic) self.stdout.write(s) logger.warn(s) return has_indexes = self.topics.has_indexed_data(arg['<topic>']) if has_indexes: plotType = PlotType.indexed transferType = "indexed" else: plotType = PlotType.linear transferType = "linear" p = Superplot(topic, plotType) q, ctrl = p.start() # Protect self.plots from modifications from the runner thread self.plotsLock.acquire() self.plots.append({ 'topic': topic, 'plot': p, # Plot handler 'queue': q, # Data queue 'ctrl': ctrl # Plot control pipe }) self.plotsLock.release() self.topics.transfer(topic, q, transfer_type=transferType) s = "Plotting '{0}' in mode [{1}].\n".format(topic, transferType) logger.info(s) self.stdout.write(s) @docopt_cmd def do_pub(self, arg): """ Publishes a (value | string) on <topic>. Usage: pub (--u8 | --u16 | --u32 | --i8 | --i16 | --i32 | --f32 | --s) <topic> <value> """ if arg['--f32']: arg['<value>'] = float(arg['<value>']) elif not arg['--s']: try: arg['<value>'] = int(arg['<value>']) except: # User most likely entered a float with an integer flag inter = float(arg['<value>']) rounded = int(inter) if isclose(inter, rounded): arg['<value>'] = rounded else: s = "Aborted : Wrote decimal number ({0}) with integer flag.".format( arg['<value>']) self.stdout.write(s + "\n") logger.warning(s) return subset = { k: arg[k] for k in ("--u8", "--u16", "--u32", "--i8", "--i16", "--i32", "--f32", "--s") } valtype = None for i, k in subset.items(): if k: valtype = self.types_lookup[i] if not valtype: logger.error("Payload type [{0}] unkown.".format(arg)) return try: self.telemetry.publish(arg['<topic>'], arg['<value>'], valtype) except SerialTimeoutException as e: self.stdout.write("Pub failed. Connection most likely terminated.") logger.error( "Pub failed. Connection most likely terminated. exception : %s" % e) return except AttributeError as e: self.stdout.write( "Pub failed because you are not connected to any device. Connect first using `serial` command." ) logger.warning( "Trying to publish while not connected. exception : %s" % e) return s = "Published on topic '{0}' : {1} [{2}]".format( arg['<topic>'], arg['<value>'], valtype) self.stdout.write(s + "\n") logger.info(s) @docopt_cmd def do_count(self, arg): """ Prints a count of received samples for each topic. Usage: count """ for topic in self.topics.ls(): self.stdout.write("{0} : {1}\n".format(topic, self.topics.count(topic))) @docopt_cmd def do_disconnect(self, arg): """ Disconnects from any open connection. Usage: disconnect """ try: self.runner.disconnect() self.stdout.write("Disconnected.\n") logger.info("Disconnected.") measures = self.transport.stats() for key, item in measures.items(): logger.info("Raw IO : %s : %s" % (key, item)) measures = self.runner.stats() for key, item in measures.items(): logger.info("IO speeds : %s : %s" % (key, item)) measures = self.telemetry.stats() for key, item in measures['framing'].items(): logger.info("Framing : %s : %s" % (key, item)) for key, item in measures['protocol'].items(): logger.info("Protocol : %s : %s" % (key, item)) logger.info("Logged session statistics.") except: logger.warn("Already disconnected. Continuing happily.") @docopt_cmd def do_info(self, arg): """ Prints out cli.py full path, module version. Usage: info """ self.stdout.write("- CLI path : %s\n" % os.path.dirname(os.path.realpath(__file__))) try: self.stdout.write("- version : %s\n" % __version__) except: self.stdout.write("- version : not found.\n") @docopt_cmd def do_stats(self, arg): """ Displays different metrics about the active transport (ex : serial port). This allows you to know if for instance corrupted frames are received, what fraction of the maximum baudrate is being used, etc. Usage: stats """ measures = self.transport.stats() self.stdout.write("Raw IO:\n") for key, item in measures.items(): self.stdout.write("\t%s : %s\n" % (key, item)) measures = self.runner.stats() self.stdout.write("IO speeds:\n") for key, item in measures.items(): self.stdout.write("\t%s : %s\n" % (key, item)) measures = self.telemetry.stats() self.stdout.write("Framing:\n") for key, item in measures['framing'].items(): self.stdout.write("\t%s : %s\n" % (key, item)) self.stdout.write("Protocol:\n") for key, item in measures['protocol'].items(): self.stdout.write("\t%s : %s\n" % (key, item)) def do_quit(self, arg): """ Exits the terminal application. Usage: quit """ self.runner.terminate() self.do_disconnect("") self.stdout.write("Good Bye!\n") logger.info("Application quit.") exit()
def test_protocol_stats(): t = transportMock() p = Pytelemetry(t) measures = p.api.stats() assert measures["rx_decoded_frames"] == 0 assert measures["rx_corrupted_crc"] == 0 assert measures["rx_corrupted_header"] == 0 assert measures["rx_corrupted_eol"] == 0 assert measures["rx_corrupted_topic"] == 0 assert measures["rx_corrupted_payload"] == 0 assert measures["tx_encoded_frames"] == 0 # Add a frame inside the transport queue t.write(bytearray.fromhex("f70700666f6f0062617247027f")) # update to read the new frame p.update() # get measurements measures = p.api.stats() assert measures["rx_decoded_frames"] == 1 assert measures["rx_corrupted_crc"] == 0 assert measures["rx_corrupted_header"] == 0 assert measures["rx_corrupted_eol"] == 0 assert measures["rx_corrupted_topic"] == 0 assert measures["rx_corrupted_payload"] == 0 assert measures["tx_encoded_frames"] == 0 # replaced CRC '4702' by '4701' to corrupt crc t.write(bytearray.fromhex("f70700666f6f0062617247017f")) # update to read the new frame p.update() # get measurements measures = p.api.stats() assert measures["rx_decoded_frames"] == 1 assert measures["rx_corrupted_crc"] == 1 assert measures["rx_corrupted_header"] == 0 assert measures["rx_corrupted_eol"] == 0 assert measures["rx_corrupted_topic"] == 0 assert measures["rx_corrupted_payload"] == 0 assert measures["tx_encoded_frames"] == 0 # replaced byte n3 '00' by '10' to corrupt crc t.write(bytearray.fromhex("f70710666f6f0062617247027f")) # update to read the new frame p.update() # get measurements measures = p.api.stats() assert measures["rx_decoded_frames"] == 1 assert measures["rx_corrupted_crc"] == 2 assert measures["rx_corrupted_header"] == 0 assert measures["rx_corrupted_eol"] == 0 assert measures["rx_corrupted_topic"] == 0 assert measures["rx_corrupted_payload"] == 0 assert measures["tx_encoded_frames"] == 0 #crc = crc16(bytearray.fromhex("0900666f6f00626172")) #print(crc) #crc = struct.pack("<H",crc) #print(crc.hex()) # Replaced header from 0700 to 0900 to corrupt it. Crc is valid to not discard t.write(bytearray.fromhex("f70900666f6f006261725fc57f")) # update to read the new frame p.update() # get measurements measures = p.api.stats() assert measures["rx_decoded_frames"] == 1 assert measures["rx_corrupted_crc"] == 2 assert measures["rx_corrupted_header"] == 1 assert measures["rx_corrupted_eol"] == 0 assert measures["rx_corrupted_topic"] == 0 assert measures["rx_corrupted_payload"] == 0 assert measures["tx_encoded_frames"] == 0 #crc = crc16(bytearray.fromhex("0700666f6f01626172")) #print(crc) #crc = struct.pack("<H",crc) #print(crc.hex()) # Removed EOL. Crc is valid to not discard t.write(bytearray.fromhex("f70700666f6f0162617224417f")) # update to read the new frame p.update() # get measurements measures = p.api.stats() assert measures["rx_decoded_frames"] == 1 assert measures["rx_corrupted_crc"] == 2 assert measures["rx_corrupted_header"] == 1 assert measures["rx_corrupted_eol"] == 1 assert measures["rx_corrupted_topic"] == 0 assert measures["rx_corrupted_payload"] == 0 assert measures["tx_encoded_frames"] == 0 # Impossible to detect corrupted payload of type string because length is unkown. One more reason to store framesize inside frame # Use a frame of type u32 instead #crc = crc16(bytearray.fromhex("03006b6c6d6f707100ffffff")) #crc = struct.pack("<H",crc) #print(crc.hex()) # Corruped payload by removing third & fourth hex number from end. Crc will pass # t.write(bytearray.fromhex("f703006b6c6d6f707100fffffff2dd7f")) # update to read the new frame p.update() # get measurements measures = p.api.stats() assert measures["rx_decoded_frames"] == 1 assert measures["rx_corrupted_crc"] == 2 assert measures["rx_corrupted_header"] == 1 assert measures["rx_corrupted_eol"] == 1 assert measures["rx_corrupted_topic"] == 0 assert measures["rx_corrupted_payload"] == 1 assert measures["tx_encoded_frames"] == 0 topmeasures = p.stats() assert measures["rx_decoded_frames"] == topmeasures['protocol'][ "rx_decoded_frames"] assert measures["rx_corrupted_crc"] == topmeasures['protocol'][ "rx_corrupted_crc"] assert measures["rx_corrupted_header"] == topmeasures['protocol'][ "rx_corrupted_header"] assert measures["rx_corrupted_eol"] == topmeasures['protocol'][ "rx_corrupted_eol"] assert measures["rx_corrupted_topic"] == topmeasures['protocol'][ "rx_corrupted_topic"] assert measures["rx_corrupted_payload"] == topmeasures['protocol'][ "rx_corrupted_payload"] assert measures["tx_encoded_frames"] == topmeasures['protocol'][ "tx_encoded_frames"] p.publish("boo", 123, "uint8") # get measurements measures = p.stats() assert measures['protocol']["rx_decoded_frames"] == 1 assert measures['protocol']["rx_corrupted_crc"] == 2 assert measures['protocol']["rx_corrupted_header"] == 1 assert measures['protocol']["rx_corrupted_eol"] == 1 assert measures['protocol']["rx_corrupted_topic"] == 0 assert measures['protocol']["rx_corrupted_payload"] == 1 assert measures['protocol']["tx_encoded_frames"] == 1 measures = p.api.delimiter.stats() assert measures["rx_processed_bytes"] > 0 assert measures["rx_discarded_bytes"] == 0 assert measures["rx_escaped_bytes"] == 0 assert measures["rx_complete_frames"] > 0 assert measures["rx_uncomplete_frames"] == 0 assert measures["tx_processed_bytes"] > 0 assert measures["tx_encoded_frames"] > 0 assert measures["tx_escaped_bytes"] == 0 p.resetStats() measures = p.stats() assert measures['protocol']["rx_decoded_frames"] == 0 assert measures['protocol']["rx_corrupted_crc"] == 0 assert measures['protocol']["rx_corrupted_header"] == 0 assert measures['protocol']["rx_corrupted_eol"] == 0 assert measures['protocol']["rx_corrupted_topic"] == 0 assert measures['protocol']["rx_corrupted_payload"] == 0 assert measures['protocol']["tx_encoded_frames"] == 0 assert measures['framing']["rx_processed_bytes"] == 0 assert measures['framing']["rx_discarded_bytes"] == 0 assert measures['framing']["rx_escaped_bytes"] == 0 assert measures['framing']["rx_complete_frames"] == 0 assert measures['framing']["rx_uncomplete_frames"] == 0 assert measures['framing']["tx_processed_bytes"] == 0 assert measures['framing']["tx_encoded_frames"] == 0 assert measures['framing']["tx_escaped_bytes"] == 0
class Application (cmd.Cmd): def __init__(self, transport=None, stdout=None): # cmd Initialization and configuration cmd.Cmd.__init__(self,stdout=stdout) self.intro = 'pytelemetry terminal started.' \ + ' (type help for a list of commands.)' self.prompt = ':> ' self.file = None # pytelemetry setup if not transport: self.transport = transports.SerialTransport() else: self.transport = transport self.telemetry = Pytelemetry(self.transport) self.topics = Topics() self.plots = [] self.plotsLock = Lock() self.runner = Runner(self.transport, self.telemetry, self.plots, self.plotsLock, self.topics) self.telemetry.subscribe(None,self.topics.process) self.types_lookup = {'--s' : 'string', '--u8' : 'uint8', '--u16' : 'uint16', '--u32' : 'uint32', '--i8' : 'int8', '--i16' : 'int16', '--i32' : 'int32', '--f32' : 'float32'} logger.info("Module path : %s" % os.path.dirname(os.path.realpath(__file__))) try: logger.info("Module version : %s" % __version__) except: logger.warning("Module version : not found.") def emptyline(self): pass # Override default behavior to repeat last command if empty input @docopt_cmd def do_serial(self, arg): """ List serial ports or connect to one of them. Usage: serial ((-l | --list) | <port> [options]) Options: -b X, --bauds X Connection speed in bauds [default: 9600] """ if arg['--list'] or arg['-l']: self.stdout.write("Available COM ports:\n") for port,desc,hid in list_ports.comports(): self.stdout.write("%s \t %s\n" % (port,desc)) return try: self.runner.disconnect() logger.warn("User requested connect without desconnecting first.") except (IOError,AttributeError) as e: logger.warn("Already disconnected. Continuing happily. E : {0}" .format(e)) pass self.topics.clear() logger.info("Cleared all topics for new session.") self.transport.resetStats(averaging_window=10) self.runner.resetStats() self.telemetry.resetStats() logger.info("Cleared all stats for new session.") try: b = int(arg['--bauds']) self.runner.connect(arg['<port>'],b) except IOError as e: self.stdout.write("Failed to connect to {0} at {1} (bauds).\n" .format(arg['<port>'],b)) logger.warn("Failed to connect to {0} at {1} (bauds). E : \n" .format(arg['<port>'],b,e)) else: s = "Connected to {0} at {1} (bauds).\n".format(arg['<port>'],b) self.stdout.write(s) logger.info(s) @docopt_cmd def do_print(self, arg): """ Prints X last received samples from <topic>. Usage: print <topic> [options] Options: -a, --all Display all received samples under <topic> -l X, --limit X Display X last received samples under <topic> [default: 1] """ topic = arg['<topic>'] if not self.topics.exists(topic): s = "Topic '{0}' unknown. Type 'ls' to list all available topics.\n".format(topic) self.stdout.write(s) logger.warn(s) return try: if arg['--all']: amount = 0 # 0 is understood as 'return all samples' by self.topics.samples() else: amount = int(arg['--limit']) except: s = "Could not cast --limit = '{0}' to integer. Using 1.\n".format(arg['--limit']) self.stdout.write(s) logger.warn(s) amount = 1 s = self.topics.samples(topic,amount) if s is not None: for i in s: self.stdout.write("{0}\n".format(i)) else: logger.error("Could not retrieve {0} sample(s) under topic '{1}'.\n".format(amount,topic)) @docopt_cmd def do_ls(self, arg): """ Prints available topics. Topics are basically labels under which data is available (for display, plot, etc). Data can come from remote source (a connected embedded device) or the command-line interface itself (reception speed, etc). Without flags, prints a list of remote topics. Usage: ls [options] Options: -c, --cli Prints all CLI topics. Use this to display topics for monitoring reception speed, errors amount, etc. """ if arg['--cli']: for topic in self.topics.ls(source='cli'): self.stdout.write("%s\n" % topic) return for topic in self.topics.ls(source='remote'): self.stdout.write("%s\n" % topic) @docopt_cmd def do_plot(self, arg): """ Plots <topic> in a graph window. Usage: plot <topic> """ topic = arg['<topic>'] if not self.topics.exists(topic): s = "Topic '{0}' unknown. Type 'ls' to list all available topics.\n".format(topic) self.stdout.write(s) logger.warn(s) return if self.topics.intransfer(topic): s = "Topic '{0}' already plotting.\n".format(topic) self.stdout.write(s) logger.warn(s) return has_indexes = self.topics.has_indexed_data(arg['<topic>']) if has_indexes: plotType = PlotType.indexed transferType = "indexed" else: plotType = PlotType.linear transferType = "linear" p = Superplot(topic,plotType) q, ctrl = p.start() # Protect self.plots from modifications from the runner thread self.plotsLock.acquire() self.plots.append({ 'topic': topic, 'plot': p, # Plot handler 'queue': q, # Data queue 'ctrl': ctrl # Plot control pipe }) self.plotsLock.release() self.topics.transfer(topic,q, transfer_type=transferType) s = "Plotting '{0}' in mode [{1}].\n".format(topic,transferType) logger.info(s) self.stdout.write(s) @docopt_cmd def do_pub(self, arg): """ Publishes a (value | string) on <topic>. Usage: pub (--u8 | --u16 | --u32 | --i8 | --i16 | --i32 | --f32 | --s) <topic> <value> """ if arg['--f32']: arg['<value>'] = float(arg['<value>']) elif not arg['--s']: try: arg['<value>'] = int(arg['<value>']) except: # User most likely entered a float with an integer flag inter = float(arg['<value>']) rounded = int(inter) if isclose(inter,rounded): arg['<value>'] = rounded else: s = "Aborted : Wrote decimal number ({0}) with integer flag.".format(arg['<value>']) self.stdout.write(s + "\n") logger.warning(s) return subset = {k: arg[k] for k in ("--u8","--u16","--u32","--i8","--i16","--i32","--f32","--s")} valtype = None for i, k in subset.items(): if k: valtype = self.types_lookup[i] if not valtype: logger.error( "Payload type [{0}] unkown." .format(arg)) return try: self.telemetry.publish(arg['<topic>'],arg['<value>'],valtype) except SerialTimeoutException as e: self.stdout.write("Pub failed. Connection most likely terminated.") logger.error("Pub failed. Connection most likely terminated. exception : %s" % e) return except AttributeError as e: self.stdout.write("Pub failed because you are not connected to any device. Connect first using `serial` command.") logger.warning("Trying to publish while not connected. exception : %s" % e) return s = "Published on topic '{0}' : {1} [{2}]".format(arg['<topic>'], arg['<value>'],valtype) self.stdout.write(s + "\n") logger.info(s) @docopt_cmd def do_count(self, arg): """ Prints a count of received samples for each topic. Usage: count """ for topic in self.topics.ls(): self.stdout.write("{0} : {1}\n".format(topic, self.topics.count(topic))) @docopt_cmd def do_disconnect(self, arg): """ Disconnects from any open connection. Usage: disconnect """ try: self.runner.disconnect() self.stdout.write("Disconnected.\n") logger.info("Disconnected.") measures = self.transport.stats() for key,item in measures.items(): logger.info("Raw IO : %s : %s" % (key,item)) measures = self.runner.stats() for key,item in measures.items(): logger.info("IO speeds : %s : %s" % (key,item)) measures = self.telemetry.stats() for key,item in measures['framing'].items(): logger.info("Framing : %s : %s" % (key,item)) for key,item in measures['protocol'].items(): logger.info("Protocol : %s : %s" % (key,item)) logger.info("Logged session statistics.") except: logger.warn("Already disconnected. Continuing happily.") @docopt_cmd def do_info(self, arg): """ Prints out cli.py full path, module version. Usage: info """ self.stdout.write("- CLI path : %s\n" % os.path.dirname(os.path.realpath(__file__))) try: self.stdout.write("- version : %s\n" % __version__) except: self.stdout.write("- version : not found.\n") @docopt_cmd def do_stats(self, arg): """ Displays different metrics about the active transport (ex : serial port). This allows you to know if for instance corrupted frames are received, what fraction of the maximum baudrate is being used, etc. Usage: stats """ measures = self.transport.stats() self.stdout.write("Raw IO:\n") for key,item in measures.items(): self.stdout.write("\t%s : %s\n" % (key,item)) measures = self.runner.stats() self.stdout.write("IO speeds:\n") for key,item in measures.items(): self.stdout.write("\t%s : %s\n" % (key,item)) measures = self.telemetry.stats() self.stdout.write("Framing:\n") for key,item in measures['framing'].items(): self.stdout.write("\t%s : %s\n" % (key,item)) self.stdout.write("Protocol:\n") for key,item in measures['protocol'].items(): self.stdout.write("\t%s : %s\n" % (key,item)) def do_quit(self, arg): """ Exits the terminal application. Usage: quit """ self.runner.terminate() self.do_disconnect("") self.stdout.write("Good Bye!\n") logger.info("Application quit.") exit()
def test_protocol_stats(): t = transportMock() p = Pytelemetry(t) measures = p.api.stats() assert measures["rx_decoded_frames"] == 0 assert measures["rx_corrupted_crc"] == 0 assert measures["rx_corrupted_header"] == 0 assert measures["rx_corrupted_eol"] == 0 assert measures["rx_corrupted_topic"] == 0 assert measures["rx_corrupted_payload"] == 0 assert measures["tx_encoded_frames"] == 0 # Add a frame inside the transport queue t.write(bytearray.fromhex("f70700666f6f0062617247027f")) # update to read the new frame p.update() # get measurements measures = p.api.stats() assert measures["rx_decoded_frames"] == 1 assert measures["rx_corrupted_crc"] == 0 assert measures["rx_corrupted_header"] == 0 assert measures["rx_corrupted_eol"] == 0 assert measures["rx_corrupted_topic"] == 0 assert measures["rx_corrupted_payload"] == 0 assert measures["tx_encoded_frames"] == 0 # replaced CRC '4702' by '4701' to corrupt crc t.write(bytearray.fromhex("f70700666f6f0062617247017f")) # update to read the new frame p.update() # get measurements measures = p.api.stats() assert measures["rx_decoded_frames"] == 1 assert measures["rx_corrupted_crc"] == 1 assert measures["rx_corrupted_header"] == 0 assert measures["rx_corrupted_eol"] == 0 assert measures["rx_corrupted_topic"] == 0 assert measures["rx_corrupted_payload"] == 0 assert measures["tx_encoded_frames"] == 0 # replaced byte n3 '00' by '10' to corrupt crc t.write(bytearray.fromhex("f70710666f6f0062617247027f")) # update to read the new frame p.update() # get measurements measures = p.api.stats() assert measures["rx_decoded_frames"] == 1 assert measures["rx_corrupted_crc"] == 2 assert measures["rx_corrupted_header"] == 0 assert measures["rx_corrupted_eol"] == 0 assert measures["rx_corrupted_topic"] == 0 assert measures["rx_corrupted_payload"] == 0 assert measures["tx_encoded_frames"] == 0 #crc = crc16(bytearray.fromhex("0900666f6f00626172")) #print(crc) #crc = struct.pack("<H",crc) #print(crc.hex()) # Replaced header from 0700 to 0900 to corrupt it. Crc is valid to not discard t.write(bytearray.fromhex("f70900666f6f006261725fc57f")) # update to read the new frame p.update() # get measurements measures = p.api.stats() assert measures["rx_decoded_frames"] == 1 assert measures["rx_corrupted_crc"] == 2 assert measures["rx_corrupted_header"] == 1 assert measures["rx_corrupted_eol"] == 0 assert measures["rx_corrupted_topic"] == 0 assert measures["rx_corrupted_payload"] == 0 assert measures["tx_encoded_frames"] == 0 #crc = crc16(bytearray.fromhex("0700666f6f01626172")) #print(crc) #crc = struct.pack("<H",crc) #print(crc.hex()) # Removed EOL. Crc is valid to not discard t.write(bytearray.fromhex("f70700666f6f0162617224417f")) # update to read the new frame p.update() # get measurements measures = p.api.stats() assert measures["rx_decoded_frames"] == 1 assert measures["rx_corrupted_crc"] == 2 assert measures["rx_corrupted_header"] == 1 assert measures["rx_corrupted_eol"] == 1 assert measures["rx_corrupted_topic"] == 0 assert measures["rx_corrupted_payload"] == 0 assert measures["tx_encoded_frames"] == 0 # Impossible to detect corrupted payload of type string because length is unkown. One more reason to store framesize inside frame # Use a frame of type u32 instead #crc = crc16(bytearray.fromhex("03006b6c6d6f707100ffffff")) #crc = struct.pack("<H",crc) #print(crc.hex()) # Corruped payload by removing third & fourth hex number from end. Crc will pass # t.write(bytearray.fromhex("f703006b6c6d6f707100fffffff2dd7f")) # update to read the new frame p.update() # get measurements measures = p.api.stats() assert measures["rx_decoded_frames"] == 1 assert measures["rx_corrupted_crc"] == 2 assert measures["rx_corrupted_header"] == 1 assert measures["rx_corrupted_eol"] == 1 assert measures["rx_corrupted_topic"] == 0 assert measures["rx_corrupted_payload"] == 1 assert measures["tx_encoded_frames"] == 0 topmeasures = p.stats() assert measures["rx_decoded_frames"] == topmeasures['protocol']["rx_decoded_frames"] assert measures["rx_corrupted_crc"] == topmeasures['protocol']["rx_corrupted_crc"] assert measures["rx_corrupted_header"] == topmeasures['protocol']["rx_corrupted_header"] assert measures["rx_corrupted_eol"] == topmeasures['protocol']["rx_corrupted_eol"] assert measures["rx_corrupted_topic"] == topmeasures['protocol']["rx_corrupted_topic"] assert measures["rx_corrupted_payload"] == topmeasures['protocol']["rx_corrupted_payload"] assert measures["tx_encoded_frames"] == topmeasures['protocol']["tx_encoded_frames"] p.publish("boo", 123, "uint8") # get measurements measures = p.stats() assert measures['protocol']["rx_decoded_frames"] == 1 assert measures['protocol']["rx_corrupted_crc"] == 2 assert measures['protocol']["rx_corrupted_header"] == 1 assert measures['protocol']["rx_corrupted_eol"] == 1 assert measures['protocol']["rx_corrupted_topic"] == 0 assert measures['protocol']["rx_corrupted_payload"] == 1 assert measures['protocol']["tx_encoded_frames"] == 1 measures = p.api.delimiter.stats() assert measures["rx_processed_bytes"] > 0 assert measures["rx_discarded_bytes"] == 0 assert measures["rx_escaped_bytes"] == 0 assert measures["rx_complete_frames"] > 0 assert measures["rx_uncomplete_frames"] == 0 assert measures["tx_processed_bytes"] > 0 assert measures["tx_encoded_frames"] > 0 assert measures["tx_escaped_bytes"] == 0 p.resetStats() measures = p.stats() assert measures['protocol']["rx_decoded_frames"] == 0 assert measures['protocol']["rx_corrupted_crc"] == 0 assert measures['protocol']["rx_corrupted_header"] == 0 assert measures['protocol']["rx_corrupted_eol"] == 0 assert measures['protocol']["rx_corrupted_topic"] == 0 assert measures['protocol']["rx_corrupted_payload"] == 0 assert measures['protocol']["tx_encoded_frames"] == 0 assert measures['framing']["rx_processed_bytes"] == 0 assert measures['framing']["rx_discarded_bytes"] == 0 assert measures['framing']["rx_escaped_bytes"] == 0 assert measures['framing']["rx_complete_frames"] == 0 assert measures['framing']["rx_uncomplete_frames"] == 0 assert measures['framing']["tx_processed_bytes"] == 0 assert measures['framing']["tx_encoded_frames"] == 0 assert measures['framing']["tx_escaped_bytes"] == 0