Compare commits

...

2 Commits

Author SHA1 Message Date
2ab3f1fe9e good layout 2025-07-28 11:06:10 -04:00
a3c50fd845 Enhanced TUI with split flow details and timing analysis
- Added ΔT (deltaT), σ (sigma), and outlier count columns to flow table
- Split right panel into top (main flow) and bottom (sub-flow) sections
- Removed inner panel borders for clean 3-panel layout
- Added sub-flow selection logic with detailed timing statistics
- Implemented per-frame-type timing analysis in sub-flow details
- Color-coded outliers and timing data for quick visual assessment
2025-07-28 09:50:59 -04:00
7 changed files with 894 additions and 44 deletions

View File

@@ -19,7 +19,8 @@ import time
from .widgets.sparkline import SparklineWidget
from .widgets.metric_card import MetricCard
from .widgets.flow_table_v2 import EnhancedFlowTable
from .widgets.flow_details import FlowDetailsPanel
from .widgets.split_flow_details import FlowMainDetailsPanel, SubFlowDetailsPanel
from .widgets.debug_panel import DebugPanel
if TYPE_CHECKING:
from ...analysis.core import EthernetAnalyzer
@@ -59,6 +60,7 @@ class StreamLensAppV2(App):
bytes_per_sec = reactive(0.0)
enhanced_flows = reactive(0)
outlier_count = reactive(0)
debug_visible = reactive(False) # Hide debug panel for now
# Update timers
metric_timer: Timer = None
@@ -90,25 +92,41 @@ class StreamLensAppV2(App):
yield MetricCard("Enhanced", f"{self.enhanced_flows}", color="success", id="enhanced-metric")
yield MetricCard("Outliers", f"{self.outlier_count}", color="warning" if self.outlier_count > 0 else "normal", id="outliers-metric")
# Main content area with horizontal split
# Main content area with conditional debug panel
with Horizontal(id="content-area"):
# Left - Enhanced flow table (wider)
with Vertical(id="left-panel", classes="panel-wide"):
yield EnhancedFlowTable(
self.analyzer,
id="flow-table"
)
# Left - Enhanced flow table
yield EnhancedFlowTable(
self.analyzer,
id="flow-table",
classes="panel-wide"
)
# Right - Selected flow details
with Vertical(id="right-panel", classes="panel"):
yield FlowDetailsPanel(
id="flow-details"
)
# Middle - Flow details
with Vertical(id="flow-panels"):
yield FlowMainDetailsPanel(id="main-flow-details")
yield SubFlowDetailsPanel(id="sub-flow-details")
# Right - Debug panel (conditionally visible)
if self.debug_visible:
yield DebugPanel(id="debug-panel")
yield Footer()
def on_mount(self) -> None:
"""Initialize the application with TipTop-style updates"""
try:
debug_panel = self.query_one("#debug-panel", DebugPanel)
debug_panel.add_debug_message("APP: Application mounted, checking panels...")
try:
main_panel = self.query_one("#main-flow-details", FlowMainDetailsPanel)
sub_panel = self.query_one("#sub-flow-details", SubFlowDetailsPanel)
debug_panel.add_debug_message("APP: Both panels found successfully")
except Exception as e:
debug_panel.add_debug_message(f"APP: Panel query failed: {e}")
except:
pass # Debug panel not visible
self.update_metrics()
# Set up update intervals like TipTop
@@ -240,9 +258,21 @@ class StreamLensAppV2(App):
def on_enhanced_flow_table_flow_selected(self, event: EnhancedFlowTable.FlowSelected) -> None:
"""Handle flow selection events"""
try:
debug_panel = self.query_one("#debug-panel", DebugPanel)
flow_info = f"{event.flow.src_ip}:{event.flow.src_port}" if event.flow else "None"
debug_panel.add_debug_message(f"APP: Flow selected - {flow_info}, subflow={event.subflow_type}")
except:
pass # Debug panel not visible
if event.flow:
details_panel = self.query_one("#flow-details", FlowDetailsPanel)
details_panel.update_flow(event.flow)
# Update main flow details panel
main_panel = self.query_one("#main-flow-details", FlowMainDetailsPanel)
main_panel.update_flow(event.flow)
# Update sub-flow details panel
sub_panel = self.query_one("#sub-flow-details", SubFlowDetailsPanel)
sub_panel.update_flow(event.flow, event.subflow_type)
def _format_bytes_per_sec(self, bps: float) -> str:

View File

@@ -35,15 +35,13 @@ MetricCard {
padding: 0;
}
/* Panel Styling - Minimal borders */
/* Panel Styling - No borders for clean look */
.panel {
border: solid #99ccff;
padding: 0;
margin: 0;
}
.panel-wide {
border: solid #99ccff;
padding: 0;
margin: 0;
}
@@ -62,13 +60,31 @@ MetricCard {
padding: 0;
}
/* Right Panel - Details (compact) */
#right-panel {
width: 25%;
/* Flow Panels - Details (30% width) */
#flow-panels {
width: 30%;
background: #1a1a1a;
padding: 0;
}
FlowMainDetailsPanel {
height: 3fr;
background: #1a1a1a;
border: solid #ff8800;
}
SubFlowDetailsPanel {
height: 2fr;
background: #1a1a1a;
border: solid #ff8800;
}
/* Debug Panel - Fixed width when visible */
#debug-panel {
width: 25%;
background: #1a1a1a;
}
/* Sparkline Charts */
SparklineWidget {
height: 5;
@@ -188,9 +204,6 @@ DataTable:focus {
border: solid #00ffcc;
}
/* Panel Borders */
Static {
border: round #0080ff;
}
/* Panel Borders - Removed for clean look */
/* End of styles */

View File

@@ -0,0 +1,69 @@
"""
Debug Panel - Real-time debugging information in TUI
"""
from textual.widget import Widget
from textual.containers import Vertical
from textual.widgets import Static
from rich.text import Text
from rich.console import RenderableType
from typing import Optional, List
from datetime import datetime
class DebugPanel(Vertical):
"""Debug panel showing real-time flow selection and logic information"""
DEFAULT_CSS = """
DebugPanel {
height: 1fr;
padding: 1;
background: #1a1a1a;
border: solid #ff0080;
}
DebugPanel Static {
margin-bottom: 0;
}
"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.debug_messages = []
self.max_messages = 20
def compose(self):
"""Create the debug panel layout"""
yield Static("DEBUG PANEL", classes="panel-header")
yield Static(
"Waiting for flow selection...",
id="debug-content"
)
def add_debug_message(self, message: str) -> None:
"""Add a debug message with timestamp"""
timestamp = datetime.now().strftime("%H:%M:%S.%f")[:-3]
full_message = f"[{timestamp}] {message}"
self.debug_messages.append(full_message)
if len(self.debug_messages) > self.max_messages:
self.debug_messages.pop(0)
self._update_display()
def _update_display(self) -> None:
"""Update the debug display with recent messages"""
content_widget = self.query_one("#debug-content", Static)
if not self.debug_messages:
content_widget.update("No debug messages yet...")
return
# Show recent messages, newest at bottom
display_text = "\n".join(self.debug_messages[-15:]) # Show last 15 messages
content_widget.update(Text(display_text, style="white"))
def clear_messages(self) -> None:
"""Clear all debug messages"""
self.debug_messages.clear()
self._update_display()

View File

@@ -75,6 +75,9 @@ class EnhancedFlowTable(Vertical):
table.add_column("Frame Type", width=10, key="frame_type")
table.add_column("Pkts", width=6, key="rate")
table.add_column("Size", width=8, key="volume")
table.add_column("ΔT(ms)", width=8, key="delta_t")
table.add_column("σ(ms)", width=8, key="sigma")
table.add_column("Out", width=5, key="outliers")
self.refresh_data()
@@ -95,6 +98,7 @@ class EnhancedFlowTable(Vertical):
# Clear row mapping
self.row_to_flow_map.clear()
self.row_to_subflow_map = {} # Map row keys to (flow_index, subflow_type)
# Get and sort flows
self.flows_list = self._get_sorted_flows()
@@ -139,10 +143,14 @@ class EnhancedFlowTable(Vertical):
# Add sub-rows for protocol breakdown
if self._should_show_subrows(flow):
sub_rows = self._create_protocol_subrows(flow)
combinations = self._get_protocol_frame_combinations(flow)
for j, sub_row in enumerate(sub_rows):
sub_key = table.add_row(*sub_row, key=f"flow_{i}_sub_{j}")
# Map sub-row to parent flow
# Map sub-row to parent flow and subflow type
self.row_to_flow_map[sub_key] = i
if j < len(combinations):
_, frame_type, _, _ = combinations[j]
self.row_to_subflow_map[sub_key] = (i, frame_type)
# Restore cursor position
if selected_row_key and selected_row_key in table.rows:
@@ -188,24 +196,37 @@ class EnhancedFlowTable(Vertical):
size_value = self._format_bytes(flow.total_bytes)
size_text = Text(f"{size_value:>8}")
# Quality with bar chart and color
quality_bar, quality_color = self._create_quality_bar(flow)
quality_value = self._get_quality_score(flow)
quality_text = Text(f"{quality_value:>3}% {quality_bar}", style=quality_color)
# Delta T (average time between packets in ms)
if flow.avg_inter_arrival > 0:
delta_t_ms = flow.avg_inter_arrival * 1000
if delta_t_ms >= 1000:
delta_t_str = f"{delta_t_ms/1000:.1f}s"
else:
delta_t_str = f"{delta_t_ms:.1f}"
else:
delta_t_str = "N/A"
delta_t_text = Text(delta_t_str, justify="right")
# Status indicator
status = self._get_flow_status(flow)
status_color = {
"Normal": "green",
"Enhanced": "bold green",
"Warning": "yellow",
"Alert": "red"
}.get(status, "white")
status_text = Text(status, style=status_color)
# Sigma (standard deviation in ms)
if flow.std_inter_arrival > 0:
sigma_ms = flow.std_inter_arrival * 1000
if sigma_ms >= 1000:
sigma_str = f"{sigma_ms/1000:.1f}s"
else:
sigma_str = f"{sigma_ms:.1f}"
else:
sigma_str = "N/A"
sigma_text = Text(sigma_str, justify="right")
# Outlier count (packets outside tolerance)
outlier_count = len(flow.outlier_frames)
outlier_text = Text(str(outlier_count), justify="right",
style="red" if outlier_count > 0 else "green")
return [
num_text, source_text, proto_text, dest_text,
extended_text, frame_text, rate_text, size_text
extended_text, frame_text, rate_text, size_text,
delta_t_text, sigma_text, outlier_text
]
def _create_rate_sparkline(self, history: List[float]) -> str:
@@ -307,7 +328,22 @@ class EnhancedFlowTable(Vertical):
subrows = []
combinations = self._get_protocol_frame_combinations(flow)
for extended_proto, frame_type, count, percentage in combinations[:3]: # Max 3 subrows
for extended_proto, frame_type, count, percentage in combinations: # Show all subrows
# Calculate timing for this frame type if available
frame_delta_t = ""
frame_sigma = ""
frame_outliers = ""
if frame_type in flow.frame_types:
ft_stats = flow.frame_types[frame_type]
if ft_stats.avg_inter_arrival > 0:
dt_ms = ft_stats.avg_inter_arrival * 1000
frame_delta_t = f"{dt_ms:.1f}" if dt_ms < 1000 else f"{dt_ms/1000:.1f}s"
if ft_stats.std_inter_arrival > 0:
sig_ms = ft_stats.std_inter_arrival * 1000
frame_sigma = f"{sig_ms:.1f}" if sig_ms < 1000 else f"{sig_ms/1000:.1f}s"
frame_outliers = str(len(ft_stats.outlier_frames))
subrow = [
Text(""), # Empty flow number
Text(""), # Empty source
@@ -316,7 +352,10 @@ class EnhancedFlowTable(Vertical):
Text(f" {extended_proto}", style="dim yellow"),
Text(frame_type, style="dim blue"),
Text(f"{count}", style="dim", justify="right"),
Text(f"{self._format_bytes(count * (flow.total_bytes // flow.frame_count) if flow.frame_count > 0 else 0):>8}", style="dim")
Text(f"{self._format_bytes(count * (flow.total_bytes // flow.frame_count) if flow.frame_count > 0 else 0):>8}", style="dim"),
Text(frame_delta_t, style="dim", justify="right"),
Text(frame_sigma, style="dim", justify="right"),
Text(frame_outliers, style="dim red" if frame_outliers and int(frame_outliers) > 0 else "dim", justify="right")
]
subrows.append(subrow)
@@ -348,8 +387,9 @@ class EnhancedFlowTable(Vertical):
class FlowSelected(Message):
"""Message sent when a flow is selected"""
def __init__(self, flow: Optional['FlowStats']) -> None:
def __init__(self, flow: Optional['FlowStats'], subflow_type: Optional[str] = None) -> None:
self.flow = flow
self.subflow_type = subflow_type
super().__init__()
def get_selected_flow(self) -> Optional['FlowStats']:
@@ -372,10 +412,43 @@ class EnhancedFlowTable(Vertical):
return None
def get_selected_subflow_type(self) -> Optional[str]:
"""Get currently selected sub-flow type if applicable"""
table = self.query_one("#flows-data-table", DataTable)
if table.cursor_row is None or not table.rows:
return None
# Get the row key at cursor position
row_keys = list(table.rows.keys())
if table.cursor_row >= len(row_keys):
return None
row_key = row_keys[table.cursor_row]
# Check if this is a sub-row
if row_key in self.row_to_subflow_map:
_, subflow_type = self.row_to_subflow_map[row_key]
return subflow_type
return None
def on_data_table_row_highlighted(self, event: DataTable.RowHighlighted) -> None:
"""Handle row highlight to update selection"""
selected_flow = self.get_selected_flow()
self.post_message(self.FlowSelected(selected_flow))
subflow_type = self.get_selected_subflow_type()
# Debug through app's debug panel
flow_info = f"{selected_flow.src_ip}:{selected_flow.src_port}" if selected_flow else "None"
table = self.query_one("#flows-data-table", DataTable)
current_row = table.cursor_row if table.cursor_row is not None else -1
try:
debug_panel = self.app.query_one("#debug-panel")
debug_panel.add_debug_message(f"TABLE: Row {current_row} - {flow_info}, subflow:{subflow_type}")
except:
pass # Debug panel might not be available yet
self.post_message(self.FlowSelected(selected_flow, subflow_type))
# Helper methods from original implementation
def _get_extended_protocol(self, flow: 'FlowStats') -> str:

View File

@@ -0,0 +1,367 @@
"""
Split Flow Details Panel - Top/Bottom layout for flow and sub-flow details
"""
from textual.widget import Widget
from textual.containers import Vertical
from textual.widgets import Static
from textual.reactive import reactive
from rich.text import Text
from rich.panel import Panel
from rich.console import RenderableType, Group
from rich.table import Table
from typing import TYPE_CHECKING, Optional
if TYPE_CHECKING:
from ....models import FlowStats, FrameTypeStats
class FlowMainDetailsPanel(Vertical):
"""Top panel showing main flow details"""
DEFAULT_CSS = """
FlowMainDetailsPanel {
height: 1fr;
padding: 1;
}
FlowMainDetailsPanel Static {
margin-bottom: 0;
}
"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.current_flow = None
def compose(self):
"""Create the main flow details layout"""
yield Static("Main Flow Details", classes="panel-header")
yield Static(
"Select a flow to view details",
id="main-details-content"
)
def update_flow(self, flow: Optional['FlowStats']) -> None:
"""Update panel with main flow details"""
flow_info = f"{flow.src_ip}:{flow.src_port}" if flow else "None"
try:
debug_panel = self.app.query_one("#debug-panel")
debug_panel.add_debug_message(f"MAIN_PANEL: Update called - {flow_info}")
except:
pass
self.current_flow = flow
content_widget = self.query_one("#main-details-content", Static)
if not flow:
content_widget.update("Select a flow to view details")
return
details = self._create_main_flow_details(flow)
# Debug what content we're actually setting
try:
debug_panel = self.app.query_one("#debug-panel")
debug_panel.add_debug_message(f"CONTENT: Setting panel content for {flow.src_ip}:{flow.src_port}")
except:
pass
content_widget.update(details)
def _create_main_flow_details(self, flow: 'FlowStats') -> RenderableType:
"""Create main flow details display"""
sections = []
# Flow identification
id_table = Table(show_header=False, box=None, padding=0)
id_table.add_column(style="dim", width=12)
id_table.add_column()
id_table.add_row("Source:", f"{flow.src_ip}:{flow.src_port}")
id_table.add_row("Destination:", f"{flow.dst_ip}:{flow.dst_port}")
id_table.add_row("Protocol:", flow.transport_protocol)
id_table.add_row("Packets:", f"{flow.frame_count:,}")
id_table.add_row("Volume:", self._format_bytes(flow.total_bytes))
sections.append(Text("Flow Information", style="bold blue"))
sections.append(id_table)
# Enhanced analysis
if flow.enhanced_analysis.decoder_type != "Standard":
enhanced_table = Table(show_header=False, box=None, padding=0)
enhanced_table.add_column(style="dim", width=12)
enhanced_table.add_column()
enhanced_table.add_row("Decoder:", flow.enhanced_analysis.decoder_type)
enhanced_table.add_row("Quality:", f"{flow.enhanced_analysis.avg_frame_quality:.1f}%")
enhanced_table.add_row("Fields:", str(flow.enhanced_analysis.field_count))
if flow.enhanced_analysis.frame_types:
types_str = ", ".join(list(flow.enhanced_analysis.frame_types)[:3])
if len(flow.enhanced_analysis.frame_types) > 3:
types_str += f" +{len(flow.enhanced_analysis.frame_types) - 3}"
enhanced_table.add_row("Types:", types_str)
sections.append(Text("Enhanced Analysis", style="bold green"))
sections.append(enhanced_table)
# Timing analysis - only show if no sub-flows exist
# Match the same logic as _should_show_subrows in flow_table_v2.py
has_subflows = (len(flow.frame_types) > 1 or
flow.enhanced_analysis.decoder_type != "Standard")
# Debug output
try:
debug_panel = self.app.query_one("#debug-panel")
debug_panel.add_debug_message(f"TIMING_LOGIC: {flow.src_ip}:{flow.src_port} - types={len(flow.frame_types)}, decoder={flow.enhanced_analysis.decoder_type}, has_subflows={has_subflows}")
except:
pass
if not has_subflows:
try:
debug_panel = self.app.query_one("#debug-panel")
debug_panel.add_debug_message(f"BRANCH: Taking FULL timing branch for {flow.src_ip}:{flow.src_port}")
except:
pass
timing_table = Table(show_header=False, box=None, padding=0)
timing_table.add_column(style="dim", width=12)
timing_table.add_column()
timing_table.add_row("Duration:", f"{flow.duration:.2f}s")
timing_table.add_row("Avg ΔT:", f"{flow.avg_inter_arrival * 1000:.1f}ms")
timing_table.add_row("Std σ:", f"{flow.std_inter_arrival * 1000:.1f}ms")
timing_table.add_row("Outliers:", f"{len(flow.outlier_frames)}")
timing_table.add_row("Jitter:", f"{flow.jitter * 1000:.2f}ms")
timing_table.add_row("First Seen:", self._format_timestamp(flow.first_seen))
timing_table.add_row("Last Seen:", self._format_timestamp(flow.last_seen))
sections.append(Text("Timing Analysis", style="bold cyan"))
sections.append(timing_table)
else:
try:
debug_panel = self.app.query_one("#debug-panel")
debug_panel.add_debug_message(f"BRANCH: Taking BASIC timeline branch for {flow.src_ip}:{flow.src_port}")
except:
pass
# Just show duration and timestamps for flows with sub-flows
basic_timing_table = Table(show_header=False, box=None, padding=0)
basic_timing_table.add_column(style="dim", width=12)
basic_timing_table.add_column()
basic_timing_table.add_row("Duration:", f"{flow.duration:.2f}s")
basic_timing_table.add_row("First Seen:", self._format_timestamp(flow.first_seen))
basic_timing_table.add_row("Last Seen:", self._format_timestamp(flow.last_seen))
sections.append(Text("Flow Timeline", style="bold cyan"))
sections.append(basic_timing_table)
return Group(*sections)
def _format_bytes(self, bytes_count: int) -> str:
"""Format byte count with units"""
if bytes_count >= 1_000_000_000:
return f"{bytes_count / 1_000_000_000:.2f} GB"
elif bytes_count >= 1_000_000:
return f"{bytes_count / 1_000_000:.2f} MB"
elif bytes_count >= 1_000:
return f"{bytes_count / 1_000:.2f} KB"
else:
return f"{bytes_count} B"
def _format_timestamp(self, timestamp: float) -> str:
"""Format timestamp for display"""
import datetime
dt = datetime.datetime.fromtimestamp(timestamp)
return dt.strftime("%H:%M:%S.%f")[:-3]
class SubFlowDetailsPanel(Vertical):
"""Bottom panel showing sub-flow details or summary"""
DEFAULT_CSS = """
SubFlowDetailsPanel {
height: 1fr;
padding: 1;
}
SubFlowDetailsPanel Static {
margin-bottom: 0;
}
"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.current_flow = None
self.selected_subflow = None
def compose(self):
"""Create the sub-flow details layout"""
yield Static("Sub-Flow Details", classes="panel-header")
yield Static(
"No sub-flow selected",
id="sub-details-content"
)
def update_flow(self, flow: Optional['FlowStats'], subflow_type: Optional[str] = None) -> None:
"""Update panel with sub-flow details or summary"""
self.current_flow = flow
self.selected_subflow = subflow_type
content_widget = self.query_one("#sub-details-content", Static)
if not flow:
content_widget.update("No sub-flow selected")
return
if subflow_type and subflow_type in flow.frame_types:
# Show specific sub-flow details
details = self._create_subflow_details(flow, flow.frame_types[subflow_type])
else:
# Show summary of all sub-flows
details = self._create_subflow_summary(flow)
content_widget.update(details)
def _create_subflow_details(self, flow: 'FlowStats', subflow: 'FrameTypeStats') -> RenderableType:
"""Create specific sub-flow details"""
sections = []
# Sub-flow identification
id_table = Table(show_header=False, box=None, padding=0)
id_table.add_column(style="dim", width=12)
id_table.add_column()
id_table.add_row("Frame Type:", subflow.frame_type)
id_table.add_row("Packets:", f"{subflow.count:,}")
id_table.add_row("Volume:", self._format_bytes(subflow.total_bytes))
percentage = (subflow.count / flow.frame_count * 100) if flow.frame_count > 0 else 0
id_table.add_row("% of Flow:", f"{percentage:.1f}%")
sections.append(Text("Sub-Flow Information", style="bold yellow"))
sections.append(id_table)
# Sub-flow timing
timing_table = Table(show_header=False, box=None, padding=0)
timing_table.add_column(style="dim", width=12)
timing_table.add_column()
timing_table.add_row("Avg ΔT:", f"{subflow.avg_inter_arrival * 1000:.1f}ms" if subflow.avg_inter_arrival > 0 else "N/A")
timing_table.add_row("Std σ:", f"{subflow.std_inter_arrival * 1000:.1f}ms" if subflow.std_inter_arrival > 0 else "N/A")
timing_table.add_row("Outliers:", f"{len(subflow.outlier_frames)}")
sections.append(Text("Sub-Flow Timing", style="bold cyan"))
sections.append(timing_table)
# Outlier details if any
if subflow.outlier_frames and subflow.outlier_details:
outlier_table = Table(show_header=True, box=None)
outlier_table.add_column("Frame#", justify="right")
outlier_table.add_column("ΔT(ms)", justify="right")
for frame_num, delta_t in subflow.outlier_details[:5]: # Show first 5 outliers
outlier_table.add_row(
str(frame_num),
f"{delta_t * 1000:.1f}"
)
if len(subflow.outlier_details) > 5:
outlier_table.add_row("...", f"+{len(subflow.outlier_details) - 5} more")
sections.append(Text("Outlier Details", style="bold red"))
sections.append(outlier_table)
return Group(*sections)
def _create_subflow_summary(self, flow: 'FlowStats') -> RenderableType:
"""Create summary of all sub-flows"""
if not flow.frame_types or len(flow.frame_types) <= 1:
return Text("No sub-flows available", style="dim")
sections = []
sections.append(Text("Sub-Flow Summary", style="bold yellow"))
# Frame type breakdown table
frame_table = Table(show_header=True, box=None)
frame_table.add_column("Frame Type", style="blue")
frame_table.add_column("Count", justify="right")
frame_table.add_column("%", justify="right", style="dim")
frame_table.add_column("ΔT(ms)", justify="right", style="cyan")
frame_table.add_column("σ(ms)", justify="right", style="cyan")
frame_table.add_column("Out", justify="right", style="red")
total = flow.frame_count
for frame_type, stats in sorted(
flow.frame_types.items(),
key=lambda x: x[1].count,
reverse=True
):
percentage = (stats.count / total * 100) if total > 0 else 0
delta_t = f"{stats.avg_inter_arrival * 1000:.1f}" if stats.avg_inter_arrival > 0 else "N/A"
sigma = f"{stats.std_inter_arrival * 1000:.1f}" if stats.std_inter_arrival > 0 else "N/A"
outliers = str(len(stats.outlier_frames))
frame_table.add_row(
frame_type[:15],
f"{stats.count:,}",
f"{percentage:.1f}%",
delta_t,
sigma,
outliers
)
sections.append(frame_table)
return Group(*sections)
def _format_bytes(self, bytes_count: int) -> str:
"""Format byte count with units"""
if bytes_count >= 1_000_000_000:
return f"{bytes_count / 1_000_000_000:.2f} GB"
elif bytes_count >= 1_000_000:
return f"{bytes_count / 1_000_000:.2f} MB"
elif bytes_count >= 1_000:
return f"{bytes_count / 1_000:.2f} KB"
else:
return f"{bytes_count} B"
class SplitFlowDetailsPanel(Vertical):
"""Combined panel with top/bottom split for flow and sub-flow details"""
DEFAULT_CSS = """
SplitFlowDetailsPanel {
height: 1fr;
padding: 0;
}
SplitFlowDetailsPanel > FlowMainDetailsPanel {
height: 3fr;
}
SplitFlowDetailsPanel > SubFlowDetailsPanel {
height: 2fr;
}
"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.current_flow = None
def compose(self):
"""Create the split layout"""
yield FlowMainDetailsPanel(id="main-flow-details")
yield SubFlowDetailsPanel(id="sub-flow-details")
def update_flow(self, flow: Optional['FlowStats'], subflow_type: Optional[str] = None) -> None:
"""Update both panels with flow data"""
self.current_flow = flow
# Update main flow details
main_panel = self.query_one("#main-flow-details", FlowMainDetailsPanel)
main_panel.update_flow(flow)
# Update sub-flow details
sub_panel = self.query_one("#sub-flow-details", SubFlowDetailsPanel)
sub_panel.update_flow(flow, subflow_type)

237
debug.log Normal file
View File

@@ -0,0 +1,237 @@
APP DEBUG: Application mounted, checking panels...
APP DEBUG: Both panels found successfully
APP DEBUG: Flow selected event - flow=192.168.4.89:49154 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 192.168.4.89:49154 if flow else None
PANEL DEBUG: 192.168.4.89:49154 - frame_types=10, decoder=Chapter10_Enhanced, has_subflows=True
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.4.89:49154 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.4.89:49154 if event.flow else None, subflow_type=CH10-Data
UPDATE_FLOW DEBUG: Called with flow 192.168.4.89:49154 if flow else None
PANEL DEBUG: 192.168.4.89:49154 - frame_types=10, decoder=Chapter10_Enhanced, has_subflows=True
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.4.89:49154 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.4.89:49154 if event.flow else None, subflow_type=UDP
UPDATE_FLOW DEBUG: Called with flow 192.168.4.89:49154 if flow else None
PANEL DEBUG: 192.168.4.89:49154 - frame_types=10, decoder=Chapter10_Enhanced, has_subflows=True
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.4.89:49154 if selected_flow else None
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.4.89:49154 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.4.89:49154 if event.flow else None, subflow_type=UDP
UPDATE_FLOW DEBUG: Called with flow 192.168.4.89:49154 if flow else None
PANEL DEBUG: 192.168.4.89:49154 - frame_types=10, decoder=Chapter10_Enhanced, has_subflows=True
APP DEBUG: Flow selected event - flow=192.168.4.89:49154 if event.flow else None, subflow_type=UDP
UPDATE_FLOW DEBUG: Called with flow 192.168.4.89:49154 if flow else None
PANEL DEBUG: 192.168.4.89:49154 - frame_types=10, decoder=Chapter10_Enhanced, has_subflows=True
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.4.89:49154 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.4.89:49154 if event.flow else None, subflow_type=TMATS
UPDATE_FLOW DEBUG: Called with flow 192.168.4.89:49154 if flow else None
PANEL DEBUG: 192.168.4.89:49154 - frame_types=10, decoder=Chapter10_Enhanced, has_subflows=True
TABLE DEBUG: Row highlighted, posting FlowSelected event for 11.59.19.204:320 if selected_flow else None
APP DEBUG: Flow selected event - flow=11.59.19.204:320 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 11.59.19.204:320 if flow else None
PANEL DEBUG: 11.59.19.204:320 - frame_types=3, decoder=Standard, has_subflows=True
TABLE DEBUG: Row highlighted, posting FlowSelected event for 11.59.19.204:320 if selected_flow else None
APP DEBUG: Flow selected event - flow=11.59.19.204:320 if event.flow else None, subflow_type=PTP-Signaling
UPDATE_FLOW DEBUG: Called with flow 11.59.19.204:320 if flow else None
PANEL DEBUG: 11.59.19.204:320 - frame_types=3, decoder=Standard, has_subflows=True
TABLE DEBUG: Row highlighted, posting FlowSelected event for 11.59.19.204:320 if selected_flow else None
APP DEBUG: Flow selected event - flow=11.59.19.204:320 if event.flow else None, subflow_type=PTP-Sync
UPDATE_FLOW DEBUG: Called with flow 11.59.19.204:320 if flow else None
PANEL DEBUG: 11.59.19.204:320 - frame_types=3, decoder=Standard, has_subflows=True
TABLE DEBUG: Row highlighted, posting FlowSelected event for 11.59.19.204:320 if selected_flow else None
APP DEBUG: Flow selected event - flow=11.59.19.204:320 if event.flow else None, subflow_type=PTP-Unknown (0x6)
UPDATE_FLOW DEBUG: Called with flow 11.59.19.204:320 if flow else None
PANEL DEBUG: 11.59.19.204:320 - frame_types=3, decoder=Standard, has_subflows=True
TABLE DEBUG: Row highlighted, posting FlowSelected event for 11.59.19.202:5010 if selected_flow else None
APP DEBUG: Flow selected event - flow=11.59.19.202:5010 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 11.59.19.202:5010 if flow else None
PANEL DEBUG: 11.59.19.202:5010 - frame_types=1, decoder=Standard, has_subflows=False
TABLE DEBUG: Row highlighted, posting FlowSelected event for 11.59.19.202:5010 if selected_flow else None
TABLE DEBUG: Row highlighted, posting FlowSelected event for 11.59.19.202:5010 if selected_flow else None
APP DEBUG: Flow selected event - flow=11.59.19.202:5010 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 11.59.19.202:5010 if flow else None
PANEL DEBUG: 11.59.19.202:5010 - frame_types=1, decoder=Standard, has_subflows=False
APP DEBUG: Flow selected event - flow=11.59.19.202:5010 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 11.59.19.202:5010 if flow else None
PANEL DEBUG: 11.59.19.202:5010 - frame_types=1, decoder=Standard, has_subflows=False
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.43.111:61112 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.43.111:61112 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 192.168.43.111:61112 if flow else None
PANEL DEBUG: 192.168.43.111:61112 - frame_types=1, decoder=Standard, has_subflows=False
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.43.111:61113 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.43.111:61113 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 192.168.43.111:61113 if flow else None
PANEL DEBUG: 192.168.43.111:61113 - frame_types=1, decoder=Standard, has_subflows=False
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.4.89:319 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.4.89:319 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 192.168.4.89:319 if flow else None
PANEL DEBUG: 192.168.4.89:319 - frame_types=1, decoder=Standard, has_subflows=False
TABLE DEBUG: Row highlighted, posting FlowSelected event for 11.59.19.204:0 if selected_flow else None
APP DEBUG: Flow selected event - flow=11.59.19.204:0 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 11.59.19.204:0 if flow else None
PANEL DEBUG: 11.59.19.204:0 - frame_types=1, decoder=Standard, has_subflows=False
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.43.111:5353 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.43.111:5353 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 192.168.43.111:5353 if flow else None
PANEL DEBUG: 192.168.43.111:5353 - frame_types=1, decoder=Standard, has_subflows=False
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.43.111:5353 if selected_flow else None
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.43.111:5353 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.43.111:5353 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 192.168.43.111:5353 if flow else None
PANEL DEBUG: 192.168.43.111:5353 - frame_types=1, decoder=Standard, has_subflows=False
APP DEBUG: Flow selected event - flow=192.168.43.111:5353 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 192.168.43.111:5353 if flow else None
PANEL DEBUG: 192.168.43.111:5353 - frame_types=1, decoder=Standard, has_subflows=False
TABLE DEBUG: Row highlighted, posting FlowSelected event for 169.254.0.1:5353 if selected_flow else None
APP DEBUG: Flow selected event - flow=169.254.0.1:5353 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 169.254.0.1:5353 if flow else None
PANEL DEBUG: 169.254.0.1:5353 - frame_types=1, decoder=Standard, has_subflows=False
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.43.111:5353 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.43.111:5353 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 192.168.43.111:5353 if flow else None
PANEL DEBUG: 192.168.43.111:5353 - frame_types=1, decoder=Standard, has_subflows=False
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.43.111:5353 if selected_flow else None
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.43.111:5353 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.43.111:5353 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 192.168.43.111:5353 if flow else None
PANEL DEBUG: 192.168.43.111:5353 - frame_types=1, decoder=Standard, has_subflows=False
APP DEBUG: Flow selected event - flow=192.168.43.111:5353 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 192.168.43.111:5353 if flow else None
PANEL DEBUG: 192.168.43.111:5353 - frame_types=1, decoder=Standard, has_subflows=False
TABLE DEBUG: Row highlighted, posting FlowSelected event for 11.59.19.204:0 if selected_flow else None
APP DEBUG: Flow selected event - flow=11.59.19.204:0 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 11.59.19.204:0 if flow else None
PANEL DEBUG: 11.59.19.204:0 - frame_types=1, decoder=Standard, has_subflows=False
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.4.89:319 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.4.89:319 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 192.168.4.89:319 if flow else None
PANEL DEBUG: 192.168.4.89:319 - frame_types=1, decoder=Standard, has_subflows=False
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.43.111:61113 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.43.111:61113 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 192.168.43.111:61113 if flow else None
PANEL DEBUG: 192.168.43.111:61113 - frame_types=1, decoder=Standard, has_subflows=False
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.43.111:61112 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.43.111:61112 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 192.168.43.111:61112 if flow else None
PANEL DEBUG: 192.168.43.111:61112 - frame_types=1, decoder=Standard, has_subflows=False
TABLE DEBUG: Row highlighted, posting FlowSelected event for 11.59.19.202:5010 if selected_flow else None
APP DEBUG: Flow selected event - flow=11.59.19.202:5010 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 11.59.19.202:5010 if flow else None
PANEL DEBUG: 11.59.19.202:5010 - frame_types=1, decoder=Standard, has_subflows=False
TABLE DEBUG: Row highlighted, posting FlowSelected event for 11.59.19.204:320 if selected_flow else None
APP DEBUG: Flow selected event - flow=11.59.19.204:320 if event.flow else None, subflow_type=PTP-Unknown (0x6)
UPDATE_FLOW DEBUG: Called with flow 11.59.19.204:320 if flow else None
PANEL DEBUG: 11.59.19.204:320 - frame_types=3, decoder=Standard, has_subflows=True
TABLE DEBUG: Row highlighted, posting FlowSelected event for 11.59.19.204:320 if selected_flow else None
APP DEBUG: Flow selected event - flow=11.59.19.204:320 if event.flow else None, subflow_type=PTP-Sync
UPDATE_FLOW DEBUG: Called with flow 11.59.19.204:320 if flow else None
PANEL DEBUG: 11.59.19.204:320 - frame_types=3, decoder=Standard, has_subflows=True
TABLE DEBUG: Row highlighted, posting FlowSelected event for 11.59.19.204:320 if selected_flow else None
APP DEBUG: Flow selected event - flow=11.59.19.204:320 if event.flow else None, subflow_type=PTP-Signaling
UPDATE_FLOW DEBUG: Called with flow 11.59.19.204:320 if flow else None
PANEL DEBUG: 11.59.19.204:320 - frame_types=3, decoder=Standard, has_subflows=True
TABLE DEBUG: Row highlighted, posting FlowSelected event for 11.59.19.204:320 if selected_flow else None
APP DEBUG: Flow selected event - flow=11.59.19.204:320 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 11.59.19.204:320 if flow else None
PANEL DEBUG: 11.59.19.204:320 - frame_types=3, decoder=Standard, has_subflows=True
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.4.89:49154 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.4.89:49154 if event.flow else None, subflow_type=TMATS
UPDATE_FLOW DEBUG: Called with flow 192.168.4.89:49154 if flow else None
PANEL DEBUG: 192.168.4.89:49154 - frame_types=10, decoder=Chapter10_Enhanced, has_subflows=True
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.4.89:49154 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.4.89:49154 if event.flow else None, subflow_type=UDP
UPDATE_FLOW DEBUG: Called with flow 192.168.4.89:49154 if flow else None
PANEL DEBUG: 192.168.4.89:49154 - frame_types=10, decoder=Chapter10_Enhanced, has_subflows=True
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.4.89:49154 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.4.89:49154 if event.flow else None, subflow_type=CH10-Data
UPDATE_FLOW DEBUG: Called with flow 192.168.4.89:49154 if flow else None
PANEL DEBUG: 192.168.4.89:49154 - frame_types=10, decoder=Chapter10_Enhanced, has_subflows=True
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.4.89:49154 if selected_flow else None
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.4.89:49154 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.4.89:49154 if event.flow else None, subflow_type=CH10-Data
UPDATE_FLOW DEBUG: Called with flow 192.168.4.89:49154 if flow else None
PANEL DEBUG: 192.168.4.89:49154 - frame_types=10, decoder=Chapter10_Enhanced, has_subflows=True
APP DEBUG: Flow selected event - flow=192.168.4.89:49154 if event.flow else None, subflow_type=CH10-Data
UPDATE_FLOW DEBUG: Called with flow 192.168.4.89:49154 if flow else None
PANEL DEBUG: 192.168.4.89:49154 - frame_types=10, decoder=Chapter10_Enhanced, has_subflows=True
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.4.89:49154 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.4.89:49154 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 192.168.4.89:49154 if flow else None
PANEL DEBUG: 192.168.4.89:49154 - frame_types=10, decoder=Chapter10_Enhanced, has_subflows=True
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.4.89:49154 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.4.89:49154 if event.flow else None, subflow_type=CH10-Data
UPDATE_FLOW DEBUG: Called with flow 192.168.4.89:49154 if flow else None
PANEL DEBUG: 192.168.4.89:49154 - frame_types=10, decoder=Chapter10_Enhanced, has_subflows=True
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.4.89:49154 if selected_flow else None
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.4.89:49154 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.4.89:49154 if event.flow else None, subflow_type=CH10-Data
UPDATE_FLOW DEBUG: Called with flow 192.168.4.89:49154 if flow else None
PANEL DEBUG: 192.168.4.89:49154 - frame_types=10, decoder=Chapter10_Enhanced, has_subflows=True
APP DEBUG: Flow selected event - flow=192.168.4.89:49154 if event.flow else None, subflow_type=CH10-Data
UPDATE_FLOW DEBUG: Called with flow 192.168.4.89:49154 if flow else None
PANEL DEBUG: 192.168.4.89:49154 - frame_types=10, decoder=Chapter10_Enhanced, has_subflows=True
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.4.89:49154 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.4.89:49154 if event.flow else None, subflow_type=UDP
UPDATE_FLOW DEBUG: Called with flow 192.168.4.89:49154 if flow else None
PANEL DEBUG: 192.168.4.89:49154 - frame_types=10, decoder=Chapter10_Enhanced, has_subflows=True
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.4.89:49154 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.4.89:49154 if event.flow else None, subflow_type=TMATS
UPDATE_FLOW DEBUG: Called with flow 192.168.4.89:49154 if flow else None
PANEL DEBUG: 192.168.4.89:49154 - frame_types=10, decoder=Chapter10_Enhanced, has_subflows=True
TABLE DEBUG: Row highlighted, posting FlowSelected event for 11.59.19.204:320 if selected_flow else None
APP DEBUG: Flow selected event - flow=11.59.19.204:320 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 11.59.19.204:320 if flow else None
PANEL DEBUG: 11.59.19.204:320 - frame_types=3, decoder=Standard, has_subflows=True
TABLE DEBUG: Row highlighted, posting FlowSelected event for 11.59.19.204:320 if selected_flow else None
APP DEBUG: Flow selected event - flow=11.59.19.204:320 if event.flow else None, subflow_type=PTP-Signaling
UPDATE_FLOW DEBUG: Called with flow 11.59.19.204:320 if flow else None
PANEL DEBUG: 11.59.19.204:320 - frame_types=3, decoder=Standard, has_subflows=True
TABLE DEBUG: Row highlighted, posting FlowSelected event for 11.59.19.204:320 if selected_flow else None
APP DEBUG: Flow selected event - flow=11.59.19.204:320 if event.flow else None, subflow_type=PTP-Sync
UPDATE_FLOW DEBUG: Called with flow 11.59.19.204:320 if flow else None
PANEL DEBUG: 11.59.19.204:320 - frame_types=3, decoder=Standard, has_subflows=True
TABLE DEBUG: Row highlighted, posting FlowSelected event for 11.59.19.204:320 if selected_flow else None
APP DEBUG: Flow selected event - flow=11.59.19.204:320 if event.flow else None, subflow_type=PTP-Unknown (0x6)
UPDATE_FLOW DEBUG: Called with flow 11.59.19.204:320 if flow else None
PANEL DEBUG: 11.59.19.204:320 - frame_types=3, decoder=Standard, has_subflows=True
TABLE DEBUG: Row highlighted, posting FlowSelected event for 11.59.19.202:5010 if selected_flow else None
APP DEBUG: Flow selected event - flow=11.59.19.202:5010 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 11.59.19.202:5010 if flow else None
PANEL DEBUG: 11.59.19.202:5010 - frame_types=1, decoder=Standard, has_subflows=False
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.43.111:61112 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.43.111:61112 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 192.168.43.111:61112 if flow else None
PANEL DEBUG: 192.168.43.111:61112 - frame_types=1, decoder=Standard, has_subflows=False
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.43.111:61112 if selected_flow else None
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.43.111:61112 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.43.111:61112 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 192.168.43.111:61112 if flow else None
PANEL DEBUG: 192.168.43.111:61112 - frame_types=1, decoder=Standard, has_subflows=False
APP DEBUG: Flow selected event - flow=192.168.43.111:61112 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 192.168.43.111:61112 if flow else None
PANEL DEBUG: 192.168.43.111:61112 - frame_types=1, decoder=Standard, has_subflows=False
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.43.111:61113 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.43.111:61113 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 192.168.43.111:61113 if flow else None
PANEL DEBUG: 192.168.43.111:61113 - frame_types=1, decoder=Standard, has_subflows=False
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.4.89:319 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.4.89:319 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 192.168.4.89:319 if flow else None
PANEL DEBUG: 192.168.4.89:319 - frame_types=1, decoder=Standard, has_subflows=False
TABLE DEBUG: Row highlighted, posting FlowSelected event for 11.59.19.204:0 if selected_flow else None
APP DEBUG: Flow selected event - flow=11.59.19.204:0 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 11.59.19.204:0 if flow else None
PANEL DEBUG: 11.59.19.204:0 - frame_types=1, decoder=Standard, has_subflows=False
TABLE DEBUG: Row highlighted, posting FlowSelected event for 192.168.43.111:5353 if selected_flow else None
APP DEBUG: Flow selected event - flow=192.168.43.111:5353 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 192.168.43.111:5353 if flow else None
PANEL DEBUG: 192.168.43.111:5353 - frame_types=1, decoder=Standard, has_subflows=False
TABLE DEBUG: Row highlighted, posting FlowSelected event for 169.254.0.1:5353 if selected_flow else None
APP DEBUG: Flow selected event - flow=169.254.0.1:5353 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 169.254.0.1:5353 if flow else None
PANEL DEBUG: 169.254.0.1:5353 - frame_types=1, decoder=Standard, has_subflows=False
TABLE DEBUG: Row highlighted, posting FlowSelected event for 169.254.0.1:5353 if selected_flow else None
TABLE DEBUG: Row highlighted, posting FlowSelected event for 169.254.0.1:5353 if selected_flow else None
APP DEBUG: Flow selected event - flow=169.254.0.1:5353 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 169.254.0.1:5353 if flow else None
PANEL DEBUG: 169.254.0.1:5353 - frame_types=1, decoder=Standard, has_subflows=False
APP DEBUG: Flow selected event - flow=169.254.0.1:5353 if event.flow else None, subflow_type=None
UPDATE_FLOW DEBUG: Called with flow 169.254.0.1:5353 if flow else None
PANEL DEBUG: 169.254.0.1:5353 - frame_types=1, decoder=Standard, has_subflows=False

61
debug_flow.py Normal file
View File

@@ -0,0 +1,61 @@
#!/usr/bin/env python3
"""
Debug script to check flow data for the 192.168.7.168 source
"""
import sys
sys.path.append('.')
from analyzer.analysis.core import EthernetAnalyzer
def debug_flow():
analyzer = EthernetAnalyzer()
# Load the sample file
pcap_file = "1 PTPGM.pcapng"
print(f"Loading {pcap_file}...")
try:
analyzer.analyze_pcap(pcap_file) # Load the file
# Find the flow with source 192.168.7.168
target_flow = None
for flow in analyzer.flows.values():
if flow.src_ip == "192.168.7.168":
target_flow = flow
break
if not target_flow:
print("No flow found with source 192.168.7.168")
print("Available flows:")
for flow in analyzer.flows.values():
print(f" {flow.src_ip}:{flow.src_port} -> {flow.dst_ip}:{flow.dst_port}")
return
print(f"\n=== Flow: {target_flow.src_ip}:{target_flow.src_port} -> {target_flow.dst_ip}:{target_flow.dst_port} ===")
print(f"Frame types: {len(target_flow.frame_types)} types")
if target_flow.frame_types:
for frame_type, stats in target_flow.frame_types.items():
print(f" - {frame_type}: {stats.count} packets")
print(f"Enhanced decoder: {target_flow.enhanced_analysis.decoder_type}")
# Check the subflow logic
has_subflows_condition1 = len(target_flow.frame_types) > 1
has_subflows_condition2 = target_flow.enhanced_analysis.decoder_type != "Standard"
has_subflows = has_subflows_condition1 or has_subflows_condition2
print(f"\nSubflow logic:")
print(f" Multiple frame types (>1): {has_subflows_condition1}")
print(f" Enhanced decoder (!='Standard'): {has_subflows_condition2}")
print(f" Should show subflows: {has_subflows}")
print(f" Should show timing in main panel: {not has_subflows}")
except Exception as e:
print(f"Error: {e}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
debug_flow()