import statistics class MtrHop: def __init__(self, idx): self.idx = idx self.host = '???' self.timings = [] self.received_count = 0 class MtrParser: def __init__(self): self.hops = {} def update(self, line): parts = line.strip().split() if len(parts) < 3: return type, idx_str, value = parts[0], parts[1], parts[2] if type not in ('h', 'd', 'p'): return try: idx = int(idx_str) except ValueError: return if idx not in self.hops: self.hops[idx] = MtrHop(idx) hop = self.hops[idx] if type == 'h': hop.host = value elif type == 'p': hop.received_count += 1 try: hop.timings.append(float(value) / 1000.0) except ValueError: pass def render_table(self): if not self.hops: return "" sorted_hops = sorted(self.hops.values(), key=lambda h: h.idx) if len(sorted_hops) >= 2 and sorted_hops[-1].host == sorted_hops[-2].host: sorted_hops.pop(-1) col_widths = {'host': 43, 'loss': 7, 'snt': 5, 'last': 7, 'avg': 7, 'best': 7, 'wrst': 7, 'stdev': 7} header = ( f"{'Host':<{col_widths['host']}}" f"{'Loss%':>{col_widths['loss']}}" f"{'Snt':>{col_widths['snt']}}" f"{'Last':>{col_widths['last']}}" f"{'Avg':>{col_widths['avg']}}" f"{'Best':>{col_widths['best']}}" f"{'Wrst':>{col_widths['wrst']}}" f"{'StDev':>{col_widths['stdev']}}\n" ) output = header for hop in sorted_hops: received = hop.received_count loss_percent = 0.0 if received > 0: last, avg, best, worst, stdev = ( hop.timings[-1], statistics.mean(hop.timings), min(hop.timings), max(hop.timings), statistics.stdev(hop.timings) if received > 1 else 0.0, ) else: last, avg, best, worst, stdev = 0.0, 0.0, 0.0, 0.0, 0.0 host_str = f" {hop.idx:d}.|-- {hop.host}" loss_str = f"{loss_percent:.1f}%" row = ( f"{host_str:<{col_widths['host']}}" f"{loss_str:>{col_widths['loss']}}" f"{hop.received_count:>{col_widths['snt']}}" f"{last:>{col_widths['last']}.1f}" f"{avg:>{col_widths['avg']}.1f}" f"{best:>{col_widths['best']}.1f}" f"{worst:>{col_widths['wrst']}.1f}" f"{stdev:>{col_widths['stdev']}.1f}\n" ) output += row return output