The full switch: Monitors and Disks read only the sensor API; /api/metrics retired
- stock Monitors modules fed by one sensor stream, the catalog and one batch of histories (GET /api/sensors/history?ids=…), through sampleAdapter.ts - the stream closes while the page is hidden and refills its curves on return - Disks tab live rates from the stream - new sensors: hamlab.sample_interval, ups.last_event - stream: asking for about the sampling rate means every sample - NUT runtime rounded at the source Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
f01175ceca
commit
4ddf10be51
+32
-23
@@ -11,10 +11,10 @@ import (
|
||||
// IDs are built from names the hardware reports (chip, label, interface, device, mount),
|
||||
// never from enumeration order like hwmon3, which moves between boots.
|
||||
|
||||
// flattener turns one Sample into sensors. With only set it builds just that one, so
|
||||
// walking the whole ring for a history costs string compares, not allocations.
|
||||
// flattener turns one Sample into sensors. With want set it builds only those, so walking
|
||||
// the whole ring for a history costs string compares, not allocations.
|
||||
type flattener struct {
|
||||
only string
|
||||
want func(id string) bool
|
||||
seen map[string]int
|
||||
out []sensors.Sensor
|
||||
}
|
||||
@@ -30,7 +30,7 @@ func (f *flattener) add(id, name, kind, unit string, v any, t int64, b bounds, l
|
||||
if n := f.seen[id]; n > 1 { // two identical chips: the second is coretemp_2
|
||||
id = fmt.Sprintf("%s_%d", id, n)
|
||||
}
|
||||
if f.only != "" && id != f.only {
|
||||
if f.want != nil && !f.want(id) {
|
||||
return
|
||||
}
|
||||
s := sensors.Sensor{ID: id, Name: name, Kind: kind, Unit: unit, Value: v, T: t, Min: b.min, Max: b.max, Warn: b.warn, Crit: b.crit}
|
||||
@@ -50,8 +50,8 @@ func ratio(part, whole uint64) float64 {
|
||||
return round1(float64(part) / float64(whole) * 100)
|
||||
}
|
||||
|
||||
func flatten(s Sample, only string) []sensors.Sensor {
|
||||
f := &flattener{only: only, seen: map[string]int{}}
|
||||
func flatten(s Sample, want func(id string) bool) []sensors.Sensor {
|
||||
f := &flattener{want: want, seen: map[string]int{}}
|
||||
t, none := s.T, bounds{}
|
||||
|
||||
f.add("cpu.usage", "CPU", "usage", "%", s.CPU, t, pct)
|
||||
@@ -66,6 +66,7 @@ func flatten(s Sample, only string) []sensors.Sensor {
|
||||
f.add("load."+w, "Load "+w+" min", "load", "", s.Load[i], t, none)
|
||||
}
|
||||
f.add("uptime", "Uptime", "duration", "s", s.Uptime, t, none)
|
||||
f.add("hamlab.sample_interval", "Sampling interval", "duration", "ms", float64(s.Interval), t, none)
|
||||
|
||||
for _, m := range []struct {
|
||||
id, name string
|
||||
@@ -132,6 +133,7 @@ func flatten(s Sample, only string) []sensors.Sensor {
|
||||
f.add("ups.on_battery", "UPS on battery", "state", "", u.OnBattery, ut, none, "model", u.Model)
|
||||
f.add("ups.state", "UPS state", "state", "", u.State, ut, none, "model", u.Model)
|
||||
f.add("ups.source", "UPS power source", "state", "", u.Source, ut, none, "model", u.Model)
|
||||
f.add("ups.last_event", "UPS last event", "state", "", u.LastEvent, ut, none, "model", u.Model, "driver", u.Driver)
|
||||
}
|
||||
return f.out
|
||||
}
|
||||
@@ -142,28 +144,35 @@ func (c *Collector) Sensors() []sensors.Sensor {
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return flatten(s, "")
|
||||
return flatten(s, nil)
|
||||
}
|
||||
|
||||
// SensorHistory implements sensors.Historian: the ring, seen through one sensor. Samples
|
||||
// where the sensor didn't exist (a disk plugged in since) are left out, not zero-filled.
|
||||
func (c *Collector) SensorHistory(id string) (sensors.Series, bool) {
|
||||
// SensorHistories implements sensors.Historian: the ring, seen through the sensors that
|
||||
// match. A sensor that didn't exist in a sample (a disk plugged in since) simply has a
|
||||
// shorter series; nothing is zero-filled. States have no curve and are left out.
|
||||
func (c *Collector) SensorHistories(match func(id string) bool) []sensors.Series {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
out := sensors.Series{ID: id, T: []int64{}, V: []float64{}}
|
||||
found := false
|
||||
by := map[string]*sensors.Series{}
|
||||
var order []string
|
||||
for _, smp := range c.ring {
|
||||
got := flatten(smp, id)
|
||||
if len(got) == 0 {
|
||||
continue
|
||||
for _, x := range flatten(smp, match) {
|
||||
v, ok := x.Value.(float64)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
s := by[x.ID]
|
||||
if s == nil {
|
||||
s = &sensors.Series{ID: x.ID, Unit: x.Unit, T: []int64{}, V: []float64{}}
|
||||
by[x.ID] = s
|
||||
order = append(order, x.ID)
|
||||
}
|
||||
s.T, s.V = append(s.T, smp.T), append(s.V, v)
|
||||
}
|
||||
found = true
|
||||
v, ok := got[0].Value.(float64)
|
||||
if !ok {
|
||||
return sensors.Series{}, false // states have no curve
|
||||
}
|
||||
out.Unit = got[0].Unit
|
||||
out.T, out.V = append(out.T, smp.T), append(out.V, v)
|
||||
}
|
||||
return out, found
|
||||
out := make([]sensors.Series, 0, len(order))
|
||||
for _, id := range order {
|
||||
out = append(out, *by[id])
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user