package aggregate import ( "fmt" "io" "sort" "github.com/netsynth/netsynth/classify" ) // PrintSummary writes a per-protocol packet count summary to w (typically os.Stderr). // Output format matches tcpdump conventions (CLAS-03): per-protocol counts with // percentages and a TOTAL line, sorted alphabetically for stable output. func PrintSummary(w io.Writer, totals map[classify.TrafficClass]int64) { var totalPackets int64 for _, count := range totals { totalPackets += count } fmt.Fprintln(w, "\n--- Protocol Summary ---") // Sort classes for stable output. classes := make([]string, 0, len(totals)) for c := range totals { classes = append(classes, string(c)) } sort.Strings(classes) for _, className := range classes { c := classify.TrafficClass(className) count := totals[c] pct := 0.0 if totalPackets > 0 { pct = float64(count) / float64(totalPackets) * 100.0 } fmt.Fprintf(w, " %-15s %8d packets (%5.1f%%)\n", c, count, pct) } fmt.Fprintf(w, " %-15s %8d packets\n", "TOTAL", totalPackets) } // PrintWindowLine writes a single verbose per-window activity line to w (CLAS-04). // Format: [window N] CLASS:count CLASS:count ... (total: N) // Classes with zero counts are omitted. Output is sorted for deterministic display. func PrintWindowLine(w io.Writer, snap classify.WindowSnapshot) { fmt.Fprintf(w, "[window %d]", snap.WindowIndex) // Sort for stable output. classes := make([]string, 0, len(snap.Counts)) for c := range snap.Counts { classes = append(classes, string(c)) } sort.Strings(classes) for _, className := range classes { c := classify.TrafficClass(className) if snap.Counts[c] > 0 { fmt.Fprintf(w, " %s:%d", c, snap.Counts[c]) } } fmt.Fprintf(w, " (total: %d)\n", snap.TotalPackets) } // AccumulateTotals merges a WindowSnapshot's counts into cumulative totals. // This is used to build the final summary passed to PrintSummary. func AccumulateTotals(totals map[classify.TrafficClass]int64, snap classify.WindowSnapshot) { for class, count := range snap.Counts { totals[class] += count } }