pinggraph/ping.go

194 lines
4.0 KiB
Go

package main
import (
"log"
"net/http"
"os"
"time"
"github.com/go-ping/ping"
"golang.org/x/image/colornames"
"gonum.org/v1/plot"
"gonum.org/v1/plot/plotter"
"gonum.org/v1/plot/vg"
"gonum.org/v1/plot/vg/draw"
"gonum.org/v1/plot/vg/vgsvg"
)
type stat struct {
t time.Duration
errs int
ok bool
}
var csec = 0
var pastHour [3600]stat
var cmin = 0
var pastWeek [10080]stat
func pinger() {
var q [60]time.Duration
pinger := ping.New("1.1.1.1")
// pinger.Interval = time.Millisecond * 1000
pinger.OnRecv = func(p *ping.Packet) {
pastHour[p.Seq-3600*(p.Seq/3600)] = stat{t: p.Rtt, ok: true}
sec := p.Seq - 60*(p.Seq/60)
q[sec] = p.Rtt
// fmt.Printf("%+v\n", p)
if p.Seq%60 == 0 {
var errs int
var sum time.Duration
for _, x := range q {
sum += x
if sum == 0 {
errs++
}
}
sum /= (60 - time.Duration(errs))
pastWeek[(p.Seq-10080*(p.Seq/10080))/60] = stat{t: sum, ok: true}
// fmt.Println("past minute", sum)
}
}
pinger.OnSend = func(p *ping.Packet) {
pastHour[p.Seq-3600*(p.Seq/3600)] = stat{t: p.Rtt, ok: true}
sec := p.Seq - 60*(p.Seq/60)
csec = p.Seq - 3600*(p.Seq/3600)
q[sec] = p.Rtt
// fmt.Printf("%+v\n", p)
if p.Seq%60 == 0 {
var sum time.Duration
for _, x := range q {
sum += x
}
sum /= 60
cmin = (p.Seq - 10080*(p.Seq/10080)) / 60
//fmt.Println(cmin)
pastWeek[(p.Seq-10080*(p.Seq/10080))/60] = stat{t: sum, ok: true}
// fmt.Println("past minute", sum)
}
}
if len(os.Args) > 1 {
pinger.SetPrivileged(true)
}
if err := pinger.Resolve(); err != nil {
log.Fatalln(err)
}
if err := pinger.Run(); err != nil {
log.Fatalln(err)
}
}
func pingEP(w http.ResponseWriter, r *http.Request) {
p := plot.New()
p.Title.Text = "Ping over past hour"
p.X.Label.Text = "Time (seconds)"
p.Y.Label.Text = "Ping (ms)"
current := csec
var period = pastHour[:]
if r.URL.Query().Get("week") != "" {
period = pastWeek[:]
p.Title.Text = "Ping over past week"
p.X.Label.Text = "Time (minutes)"
current = cmin
}
maxPing := 0.
maxErrs := 0
var pingpoints = make(plotter.XYs, len(period))
var errspoints = make(plotter.XYs, len(period))
for i, x := range period {
if !x.ok {
pingpoints = pingpoints[:i]
errspoints = errspoints[:i]
break
}
pingpoints[i] = plotter.XY{
Y: float64(x.t) / float64(time.Millisecond),
X: float64(i),
}
errs := 0
switch {
case x.errs != 0:
errs = x.errs
case x.t == 0:
errs = 1
}
errspoints[i] = plotter.XY{
Y: float64(errs),
X: float64(i),
}
if float64(x.t)/float64(time.Millisecond) > maxPing {
maxPing = float64(x.t) / float64(time.Millisecond)
}
if errs > maxErrs {
maxErrs = errs
}
}
switch {
case maxErrs == 0:
case maxErrs == 1:
for i := range errspoints {
errspoints[i].Y *= maxPing
}
default:
for i, x := range errspoints {
errspoints[i].Y = (x.Y / float64(maxErrs)) * maxPing
}
}
p.Legend.Top = true
/*
plotutil.DefaultColors = []color.Color{
colornames.Dodgerblue,
colornames.Darkseagreen,
colornames.Red,
}
plotutil.DefaultDashes = [][]vg.Length{
{vg.Points(2), vg.Points(4)},
{},
{},
}
plotutil.AddLines(p,
"Now", plotter.XYs{{X: float64(current), Y: maxPing}, {X: float64(current), Y: 0}},
"Ping", pingpoints,
"Errs", errspoints,
)*/
nowline, err := plotter.NewLine(plotter.XYs{{X: float64(current), Y: maxPing}, {X: float64(current), Y: 0}})
if err != nil {
panic(err)
}
nowline.Color = colornames.Dodgerblue
nowline.Dashes = []vg.Length{vg.Points(2), vg.Points(4)}
p.Add(nowline)
p.Legend.Add("Now", nowline)
pingline, err := plotter.NewLine(pingpoints)
if err != nil {
panic(err)
}
pingline.Color = colornames.Darkseagreen
p.Add(pingline)
p.Legend.Add("Ping", pingline)
errsline, err := plotter.NewLine(errspoints)
if err != nil {
panic(err)
}
errsline.Color = colornames.Red
p.Add(errsline)
p.Legend.Add("Errors", errsline)
canv := vgsvg.New(vg.Millimeter*290, vg.Millimeter*200)
p.Draw(draw.New(canv))
w.Header().Set("content-type", "image/svg+xml")
canv.WriteTo(w)
}