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:
2025-07-26 22:46:49 -04:00
parent 0f2fc8f92c
commit 5c2cb1a4ed
22 changed files with 4207 additions and 35 deletions

View File

@@ -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"