package main import ( "net/http" "sync" "time" "gonum.org/v1/plot" "gonum.org/v1/plot/plotter" "gonum.org/v1/plot/plotutil" "gonum.org/v1/plot/vg" "gonum.org/v1/plot/vg/draw" "gonum.org/v1/plot/vg/vgsvg" ) type statSheet struct { Values []float64 Ok bool } type provider struct { Current int Values [3600]statSheet Fetch func() []float64 NoVals int ValTags []string Name string Y string } func (p *provider) Run() { tick := time.NewTicker(time.Second) for { <-tick.C vals := p.Fetch() // fmt.Println("tick", p.Name, vals) p.Values[p.Current] = statSheet{vals, true} p.Current++ if p.Current >= 3600 { p.Current = 0 } } } var xypool = sync.Pool{ New: func() interface{} { return make(plotter.XYs, 3600) }, } func (prov *provider) Render(w http.ResponseWriter, r *http.Request) { p := plot.New() p.Legend.Top = true p.Title.Text = prov.Name p.X.Label.Text = "Time (seconds)" p.Y.Label.Text = prov.Y var points = make([]plotter.XYs, prov.NoVals) var allocs = make([]plotter.XYs, prov.NoVals) defer func() { for _, x := range allocs { xypool.Put(x) } }() for i := range points { alloc := xypool.Get().(plotter.XYs) points[i] = alloc allocs[i] = alloc } for i, x := range prov.Values { if !x.Ok { for yi := range points { points[yi] = points[yi][:i] } break } for yi, y := range x.Values { points[yi][i] = plotter.XY{ Y: y, X: float64(i), } } } for i, x := range points { line, err := plotter.NewLine(x) if err != nil { panic(err) } line.Color = plotutil.Color(i) line.Dashes = plotutil.Dashes(i) p.Add(line) p.Legend.Add(prov.ValTags[i], line) } 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) }