Modern TUI with Enhanced Protocol Hierarchy Interface
Major Features: - Complete modern TUI interface with three focused views - Enhanced multi-column layout: Source | Proto | Destination | Extended | Frame Type | Metrics - Simplified navigation with 1/2/3 hotkeys instead of F1/F2/F3 - Protocol hierarchy: Transport (TCP/UDP) → Extended (CH10/PTP) → Frame Types - Classic TUI preserved with --classic flag Views Implemented: 1. Flow Analysis View: Enhanced multi-column flow overview with protocol detection 2. Packet Decoder View: Three-panel deep inspection (Flows | Frames | Fields) 3. Statistical Analysis View: Four analysis modes with timing and quality metrics Technical Improvements: - Left-aligned text columns with IP:port precision - Transport protocol separation from extended protocols - Frame type identification (CH10-Data, TMATS, PTP Sync) - Cross-view communication with persistent flow selection - Context-sensitive help and status bars - Comprehensive error handling with console fallback
This commit is contained in:
@@ -19,9 +19,9 @@ class FlowListPanel:
|
||||
flows_list: List[FlowStats], selected_flow: int):
|
||||
"""Draw the flow list panel"""
|
||||
|
||||
# Draw flows table header with adjusted column widths for better alignment
|
||||
stdscr.addstr(y_offset, x_offset, "FLOWS:", curses.A_BOLD)
|
||||
headers = f"{'Src:Port':22} {'Dst:Port':22} {'Proto':6} {'Cast':5} {'#Frames':>7} {'Bytes':>7} {'Encoding':12} {'ΔT Avg':>9}"
|
||||
# Draw flows table header with enhanced analysis columns
|
||||
stdscr.addstr(y_offset, x_offset, "FLOWS (Enhanced Analysis):", curses.A_BOLD)
|
||||
headers = f"{'Src:Port':22} {'Dst:Port':22} {'Proto':6} {'Cast':5} {'#Frames':>7} {'Bytes':>7} {'Encoding':12} {'Quality':>7} {'Drift':>8} {'ΔT Avg':>9}"
|
||||
stdscr.addstr(y_offset + 1, x_offset, headers[:width-1], curses.A_UNDERLINE)
|
||||
|
||||
# Calculate scrolling parameters
|
||||
@@ -56,7 +56,11 @@ class FlowListPanel:
|
||||
# Abbreviate traffic classification
|
||||
cast_abbrev = flow.traffic_classification[:4] if flow.traffic_classification != "Unknown" else "Unk"
|
||||
|
||||
line = f"{src_endpoint:22} {dst_endpoint:22} {flow.transport_protocol:6} {cast_abbrev:5} {flow.frame_count:>7} {bytes_str:>7} {encoding_str:12} {avg_time:>9}"
|
||||
# Enhanced analysis data
|
||||
quality_str = self._format_quality_score(flow)
|
||||
drift_str = self._format_drift_info(flow)
|
||||
|
||||
line = f"{src_endpoint:22} {dst_endpoint:22} {flow.transport_protocol:6} {cast_abbrev:5} {flow.frame_count:>7} {bytes_str:>7} {encoding_str:12} {quality_str:>7} {drift_str:>8} {avg_time:>9}"
|
||||
|
||||
if display_item == selected_flow:
|
||||
stdscr.addstr(current_row, x_offset, line[:width-1], curses.A_REVERSE)
|
||||
@@ -78,9 +82,14 @@ class FlowListPanel:
|
||||
ft_avg = f"{ft_stats.avg_inter_arrival:.3f}s" if ft_stats.avg_inter_arrival > 0 else "N/A"
|
||||
outlier_count = len(ft_stats.outlier_details) if ft_stats.outlier_details else 0
|
||||
|
||||
# Create frame type line aligned with new column layout
|
||||
# Create frame type line aligned with enhanced column layout
|
||||
bytes_str_ft = self._format_bytes(ft_stats.total_bytes)
|
||||
ft_line = f" └─{frame_type:18} {'':22} {'':6} {'':5} {ft_stats.count:>7} {bytes_str_ft:>7} {'':12} {ft_avg:>9}"
|
||||
|
||||
# Enhanced analysis for frame types (inherit from parent flow)
|
||||
quality_str_ft = self._format_quality_score(flow) if frame_type.startswith('CH10') or frame_type == 'TMATS' else ""
|
||||
drift_str_ft = self._format_drift_info(flow) if frame_type.startswith('CH10') or frame_type == 'TMATS' else ""
|
||||
|
||||
ft_line = f" └─{frame_type:18} {'':22} {'':6} {'':5} {ft_stats.count:>7} {bytes_str_ft:>7} {'':12} {quality_str_ft:>7} {drift_str_ft:>8} {ft_avg:>9}"
|
||||
|
||||
if display_item == selected_flow:
|
||||
stdscr.addstr(current_row, x_offset, ft_line[:width-1], curses.A_REVERSE)
|
||||
@@ -179,4 +188,38 @@ class FlowListPanel:
|
||||
if found_app:
|
||||
return list(found_app)[0]
|
||||
|
||||
return "Unknown"
|
||||
return "Unknown"
|
||||
|
||||
def _format_quality_score(self, flow: FlowStats) -> str:
|
||||
"""Format quality score for display"""
|
||||
enhanced = flow.enhanced_analysis
|
||||
|
||||
if enhanced.decoder_type == "Standard" or enhanced.avg_frame_quality == 0:
|
||||
return "N/A"
|
||||
|
||||
# Format quality as percentage
|
||||
quality = enhanced.avg_frame_quality
|
||||
if quality >= 90:
|
||||
return f"{quality:.0f}%"
|
||||
elif quality >= 70:
|
||||
return f"{quality:.0f}%"
|
||||
else:
|
||||
return f"{quality:.0f}%"
|
||||
|
||||
def _format_drift_info(self, flow: FlowStats) -> str:
|
||||
"""Format clock drift information for display"""
|
||||
enhanced = flow.enhanced_analysis
|
||||
|
||||
if not enhanced.has_internal_timing or enhanced.avg_clock_drift_ppm == 0:
|
||||
return "N/A"
|
||||
|
||||
# Format drift in PPM
|
||||
drift_ppm = abs(enhanced.avg_clock_drift_ppm)
|
||||
if drift_ppm >= 1000:
|
||||
return f"{drift_ppm/1000:.1f}K" # Show in thousands
|
||||
elif drift_ppm >= 100:
|
||||
return f"{drift_ppm:.0f}ppm"
|
||||
elif drift_ppm >= 10:
|
||||
return f"{drift_ppm:.1f}ppm"
|
||||
else:
|
||||
return f"{drift_ppm:.2f}ppm"
|
||||
Reference in New Issue
Block a user