- Add PrintSummary writing per-protocol packet counts with percentages and TOTAL to io.Writer - Add PrintWindowLine writing single verbose window activity line in [window N] CLASS:count format - Add AccumulateTotals merging WindowSnapshot counts into cumulative totals map - Output sorted alphabetically for deterministic display - Add 6 tests: PrintSummary, PrintSummaryEmpty, PrintSummarySorted, PrintWindowLine, PrintWindowLineEmpty, AccumulateTotals
70 lines
2.0 KiB
Go
70 lines
2.0 KiB
Go
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
|
|
}
|
|
}
|