diff --git a/succbone/succd/http.go b/succbone/succd/http.go index fd39c87..d32d274 100644 --- a/succbone/succd/http.go +++ b/succbone/succd/http.go @@ -50,6 +50,90 @@ func formatMbar(v float32) template.HTML { return template.HTML(res) } +// apiData is the data model served to the user via HTTP/WebSockets +type apiData struct { + // Safety interlocks. + Safety struct { + // Failsafe mode enabled - pirani gauge seems disconnected. + Failsafe bool + // HighPressure interlock enabled - pressure too high to run diffusion + // pump. + HighPressure bool + } + // Pirani gauge data. + Pirani struct { + // Volts read from the gauge (0-10). + Volts string + // Mbar read from the gauge, formatted as HTML. + Mbar template.HTML + // MbarFloat read from the gauge. + MbarFloat float32 + } + // Pump state. + Pumps struct { + // RPOn means the roughing pump is turned on. + RPOn bool + // DPOn means the diffusion pump is turned on. + DPOn bool + } + // Pressure feedback into evacuation board. + Feedback struct { + // RoughReached is true when the system has reached a rough vacuum + // stage. + RoughReached bool + // HighReached is true when the system has reached a high vacuum stage. + HighReached bool + } + // System junk. + System struct { + // Load of the system. + Load string + // Hostname of the system. + Hostname string + } +} + +// apiData returns the user data model for the current state of the system. If +// skipSystem is set, the System subset is ignored (saves system load, and is +// not being served via websockets). +func (d *daemon) apiData(skipSystem bool) *apiData { + volts, mbar := d.pirani() + rp := d.rpGet() + dp := d.dpGet() + rough, high := d.vacuumStatusGet() + safety := d.safetyStatusGet() + + var hostname, load string + var err error + + if !skipSystem { + hostname, err = os.Hostname() + if err != nil { + hostname = "unknown" + } + loadB, err := os.ReadFile("/proc/loadavg") + load = "unknown" + if err == nil { + parts := strings.Fields(string(loadB)) + load = strings.Join(parts[:3], " ") + } + } + + ad := apiData{} + ad.Safety.Failsafe = safety.failsafe + ad.Safety.HighPressure = safety.highPressure + ad.Pirani.Volts = formatVolts(volts) + ad.Pirani.Mbar = formatMbar(mbar) + ad.Pirani.MbarFloat = mbar + ad.Pumps.RPOn = rp + ad.Pumps.DPOn = dp + ad.Feedback.RoughReached = rough + ad.Feedback.HighReached = high + ad.System.Load = load + ad.System.Hostname = hostname + return &ad +} + // httpIndex is the / view. func (d *daemon) httpIndex(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { @@ -57,33 +141,9 @@ func (d *daemon) httpIndex(w http.ResponseWriter, r *http.Request) { return } - volts, mbar := d.pirani() - rp := d.rpGet() - dp := d.dpGet() - safety := d.safetyStatusGet() - - loadB, err := os.ReadFile("/proc/loadavg") - load := "unknown" - if err == nil { - parts := strings.Fields(string(loadB)) - load = strings.Join(parts[:3], " ") - } - - hostname, err := os.Hostname() - if err != nil { - hostname = "unknown" - } - - templateIndex.Execute(w, map[string]any{ - "failsafe": safety.failsafe, - "highpressure": safety.highPressure, - "volts": formatVolts(volts), - "mbar": formatMbar(mbar), - "rp": rp, - "dp": dp, - "hostname": hostname, - "load": load, - }) + data := d.apiData(false) + //data.Pirani.Mbar = html.St + templateIndex.Execute(w, data) } // httpStream is the websocket clientwards data hose, returning a 10Hz update @@ -105,33 +165,7 @@ func (d *daemon) httpStream(w http.ResponseWriter, r *http.Request) { c.Close(websocket.StatusNormalClosure, "") return case <-t.C: - // TODO(q3k): don't poll, get notified when new ADC readout is available. - volts, mbar := d.pirani() - rp := d.rpGet() - dp := d.dpGet() - rough, high := d.vacuumStatusGet() - safety := d.safetyStatusGet() - v := struct { - Failsafe bool - HighPressure bool - Volts string - Mbar string - MbarFloat float32 - RPOn bool - DPOn bool - RoughReached bool - HighReached bool - }{ - Failsafe: safety.failsafe, - HighPressure: safety.highPressure, - Volts: formatVolts(volts), - Mbar: string(formatMbar(mbar)), - MbarFloat: mbar, - RPOn: rp, - DPOn: dp, - RoughReached: rough, - HighReached: high, - } + v := d.apiData(true) if err := wsjson.Write(ctx, c, v); err != nil { klog.Errorf("Websocket write failed: %v", err) return diff --git a/succbone/succd/index.html b/succbone/succd/index.html index 4d758be..e12c73a 100644 --- a/succbone/succd/index.html +++ b/succbone/succd/index.html @@ -59,25 +59,25 @@ td > span { - + - + - + - + - + - + @@ -96,11 +96,11 @@ td > span { - + - +
Voltage{{.volts}}{{ .Pirani.Volts }}
Pressure{{.mbar}}{{ .Pirani.Mbar }}
Thresholds Rough:...{{ if .Feedback.RoughReached }}OK{{ else }}NOK{{ end }} High:...{{ if .Feedback.HighReached }}OK{{ else }}NOK{{ end }}
Pumps RP:{{ if .rp }}ON{{ else }}OFF{{ end }}{{ if .Pumps.RPOn }}ON{{ else }}OFF{{ end }} DP:{{ if .dp }}ON{{ else }}OFF{{ end }}{{ if .Pumps.DPOn }}ON{{ else }}OFF{{ end }}
Evac Control
FailsafeOK{{ if .Safety.Failsafe }}ON{{ else }}OFF{{ end }}
Diffusion Pump LockoutOK{{ if .Safety.HighPressure }}ON{{ else }}OFF{{ end }}

@@ -109,7 +109,7 @@ td > span {

- {{.hostname}} | load: {{.load}} | pprof | metrics | ws ping: + {{ .System.Hostname }} | load: {{ .System.Load }} | pprof | metrics | ws ping: