55"errors"
66"fmt"
77"io"
8- "math"
98"net/http"
109"net/netip"
1110"strings"
@@ -36,48 +35,42 @@ type pingSummary struct {
3635Min * time.Duration `table:"min"`
3736Avg * time.Duration `table:"avg"`
3837Max * time.Duration `table:"max"`
39- StdDev * time.Duration `table:"stddev"`
38+ Variance * time.Duration `table:"variance"`
39+ latencySum float64
40+ runningAvg float64
41+ m2 float64
4042}
4143
42- func buildSummary (wsname string ,r []* ipnstate.PingResult )* pingSummary {
43- out := pingSummary {
44- Workspace :wsname ,
44+ func (s * pingSummary )addResult (r * ipnstate.PingResult ) {
45+ s .Total ++
46+ if r == nil || r .Err != "" {
47+ return
4548}
46- totalLatency := 0.0
47- latencies := make ([]float64 ,0 ,len (r ))
48- for _ ,pong := range r {
49- out .Total ++
50- if pong .Err == "" {
51- out .Successful ++
52- latencies = append (latencies ,pong .LatencySeconds )
53- totalLatency += pong .LatencySeconds
54- }
49+ s .Successful ++
50+ if s .Min == nil || r .LatencySeconds < s .Min .Seconds () {
51+ s .Min = ptr .Ref (time .Duration (r .LatencySeconds * float64 (time .Second )))
5552}
56- if out .Successful > 0 {
57- minLatency := latencies [0 ]
58- maxLatency := latencies [0 ]
59- varianceSum := 0.0
60- avgLatency := totalLatency / float64 (out .Successful )
61- for _ ,l := range latencies {
62- if l < minLatency {
63- minLatency = l
64- }
65- if l > maxLatency {
66- maxLatency = l
67- }
68- varianceSum += (l - avgLatency )* (l - avgLatency )
69- }
70- stddev := math .Sqrt (varianceSum / float64 (out .Successful ))
71- out .StdDev = ptr .Ref (time .Duration (stddev * float64 (time .Second )))
72- out .Avg = ptr .Ref (time .Duration (avgLatency * float64 (time .Second )))
73- out .Max = ptr .Ref (time .Duration (maxLatency * float64 (time .Second )))
74- out .Min = ptr .Ref (time .Duration (minLatency * float64 (time .Second )))
53+ if s .Max == nil || r .LatencySeconds > s .Min .Seconds () {
54+ s .Max = ptr .Ref (time .Duration (r .LatencySeconds * float64 (time .Second )))
7555}
76- return & out
56+ s .latencySum += r .LatencySeconds
57+
58+ d := r .LatencySeconds - s .runningAvg
59+ s .runningAvg += d / float64 (s .Successful )
60+ d2 := r .LatencySeconds - s .runningAvg
61+ s .m2 += d * d2
7762}
7863
79- func (p * pingSummary )Write (w io.Writer ) {
80- out ,err := cliui .DisplayTable ([]* pingSummary {p },"" ,nil )
64+ // Write finalizes the summary and writes it
65+ func (s * pingSummary )Write (wsname string ,w io.Writer ) {
66+ s .Workspace = wsname
67+ if s .Successful > 0 {
68+ s .Avg = ptr .Ref (time .Duration (s .latencySum / float64 (s .Successful )* float64 (time .Second )))
69+ }
70+ if s .Successful > 1 {
71+ s .Variance = ptr .Ref (time .Duration ((s .m2 / float64 (s .Successful - 1 ))* float64 (time .Second )))
72+ }
73+ out ,err := cliui .DisplayTable ([]* pingSummary {s },"" ,nil )
8174if err != nil {
8275_ ,_ = fmt .Fprintf (w ,"Failed to display ping summary: %v\n " ,err )
8376return
@@ -194,10 +187,9 @@ func (r *RootCmd) ping() *serpent.Command {
194187}
195188
196189connDiags .Write (inv .Stdout )
197-
190+ results := & pingSummary {}
198191n := 0
199192start := time .Now ()
200- results := make ([]* ipnstate.PingResult ,0 )
201193pingLoop:
202194for {
203195if n > 0 {
@@ -208,7 +200,7 @@ func (r *RootCmd) ping() *serpent.Command {
208200ctx ,cancel := context .WithTimeout (ctx ,pingTimeout )
209201dur ,p2p ,pong ,err := conn .Ping (ctx )
210202cancel ()
211- results = append ( results , pong )
203+ results . addResult ( pong )
212204if err != nil {
213205if xerrors .Is (err ,context .DeadlineExceeded ) {
214206_ ,_ = fmt .Fprintf (inv .Stdout ,"ping to %q timed out\n " ,workspaceName )
@@ -274,14 +266,7 @@ func (r *RootCmd) ping() *serpent.Command {
274266}
275267}
276268
277- if didP2p {
278- _ ,_ = fmt .Fprintf (inv .Stdout ,"✔ You are connected directly (p2p)\n " )
279- }else {
280- _ ,_ = fmt .Fprintf (inv .Stdout ,"❗ You are connected via a DERP relay, not directly (p2p)\n %s#common-problems-with-direct-connections\n " ,connDiags .TroubleshootingURL )
281- }
282-
283- summary := buildSummary (workspaceName ,results )
284- summary .Write (inv .Stdout )
269+ results .Write (workspaceName ,inv .Stdout )
285270
286271return nil
287272},