diff --git a/succbone/succd/http.go b/succbone/succd/http.go index 3d38823..43bee92 100644 --- a/succbone/succd/http.go +++ b/succbone/succd/http.go @@ -15,10 +15,6 @@ import ( "k8s.io/klog" ) -type httpServer struct { - d daemonController -} - var ( //go:embed index.html templateIndexText string @@ -27,7 +23,7 @@ var ( favicon []byte ) -func (s *httpServer) viewFavicon(w http.ResponseWriter, r *http.Request) { +func (d *daemon) httpFavicon(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "image/png") w.Write(favicon) } @@ -54,103 +50,45 @@ 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 (s *httpServer) apiData(skipSystem bool) *apiData { - state := s.d.snapshot() - volts, mbar := state.pirani() - rough, high := state.vacuumStatus() - - 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 = state.safety.failsafe - ad.Safety.HighPressure = state.safety.highPressure - ad.Pirani.Volts = formatVolts(volts) - ad.Pirani.Mbar = formatMbar(mbar) - ad.Pirani.MbarFloat = mbar - ad.Pumps.RPOn = state.rpOn - ad.Pumps.DPOn = state.dpOn - ad.Feedback.RoughReached = rough - ad.Feedback.HighReached = high - ad.System.Load = load - ad.System.Hostname = hostname - return &ad -} - // httpIndex is the / view. -func (s *httpServer) viewIndex(w http.ResponseWriter, r *http.Request) { +func (d *daemon) httpIndex(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { http.NotFound(w, r) return } - data := s.apiData(false) - //data.Pirani.Mbar = html.St - templateIndex.Execute(w, data) + volts, mbar := d.pirani() + rp := d.rpGet() + dp := d.dpGet() + failsafe, highpressure := 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": failsafe, + "highpressure": highpressure, + "volts": formatVolts(volts), + "mbar": formatMbar(mbar), + "rp": rp, + "dp": dp, + "hostname": hostname, + "load": load, + }) } // httpStream is the websocket clientwards data hose, returning a 10Hz update // stream of pressure/voltage. -func (s *httpServer) viewStream(w http.ResponseWriter, r *http.Request) { +func (d *daemon) httpStream(w http.ResponseWriter, r *http.Request) { c, err := websocket.Accept(w, r, nil) if err != nil { return @@ -167,7 +105,33 @@ func (s *httpServer) viewStream(w http.ResponseWriter, r *http.Request) { c.Close(websocket.StatusNormalClosure, "") return case <-t.C: - v := s.apiData(true) + // 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() + failsafe, highpressure := d.safetyStatusGet() + v := struct { + Failsafe bool + HighPressure bool + Volts string + Mbar string + MbarFloat float32 + RPOn bool + DPOn bool + RoughReached bool + HighReached bool + }{ + Failsafe: failsafe, + HighPressure: highpressure, + Volts: formatVolts(volts), + Mbar: string(formatMbar(mbar)), + MbarFloat: mbar, + RPOn: rp, + DPOn: dp, + RoughReached: rough, + HighReached: high, + } if err := wsjson.Write(ctx, c, v); err != nil { klog.Errorf("Websocket write failed: %v", err) return @@ -177,54 +141,39 @@ func (s *httpServer) viewStream(w http.ResponseWriter, r *http.Request) { } // httpMetrics serves minimalistic Prometheus-compatible metrics. -func (s *httpServer) viewMetrics(w http.ResponseWriter, r *http.Request) { +func (d *daemon) httpMetrics(w http.ResponseWriter, r *http.Request) { // TODO(q3k): also serve Go stuff using the actual Prometheus metrics client // library. - // TODO(q3k): serve the rest of the data model - state := s.d.snapshot() - _, mbar := state.pirani() + _, mbar := d.pirani() fmt.Fprintf(w, "# HELP sem_pressure_mbar Pressure in the SEM chamber, in millibar\n") fmt.Fprintf(w, "# TYPE sem_pressure_mbar gauge\n") fmt.Fprintf(w, "sem_pressure_mbar %f\n", mbar) } -func (s *httpServer) viewRoughingPumpEnable(w http.ResponseWriter, r *http.Request) { - s.d.rpSet(true) +func (d *daemon) httpRoughingPumpEnable(w http.ResponseWriter, r *http.Request) { + d.rpSet(true) fmt.Fprintf(w, "succ on\n") } -func (s *httpServer) viewRoughingPumpDisable(w http.ResponseWriter, r *http.Request) { - s.d.rpSet(false) +func (d *daemon) httpRoughingPumpDisable(w http.ResponseWriter, r *http.Request) { + d.rpSet(false) fmt.Fprintf(w, "succ off\n") } -func (s *httpServer) viewDiffusionPumpEnable(w http.ResponseWriter, r *http.Request) { - s.d.dpSet(true) +func (d *daemon) httpDiffusionPumpEnable(w http.ResponseWriter, r *http.Request) { + d.dpSet(true) fmt.Fprintf(w, "deep succ on\n") } -func (s *httpServer) viewDiffusionPumpDisable(w http.ResponseWriter, r *http.Request) { - s.d.dpSet(false) +func (d *daemon) httpDiffusionPumpDisable(w http.ResponseWriter, r *http.Request) { + d.dpSet(false) fmt.Fprintf(w, "deep succ off\n") } -func (s *httpServer) viewButtonPumpDown(w http.ResponseWriter, r *http.Request) { - s.d.pumpDownPress() +func (d *daemon) httpButtonPumpDown(w http.ResponseWriter, r *http.Request) { + d.pumpDownPress() } -func (s *httpServer) viewButtonVent(w http.ResponseWriter, r *http.Request) { - s.d.ventPress() -} - -func (s *httpServer) setupViews() { - http.HandleFunc("/", s.viewIndex) - http.HandleFunc("/favicon.png", s.viewFavicon) - http.HandleFunc("/stream", s.viewStream) - http.HandleFunc("/metrics", s.viewMetrics) - http.HandleFunc("/button/vent", s.viewButtonVent) - http.HandleFunc("/button/pumpdown", s.viewButtonPumpDown) - http.HandleFunc("/rp/on", s.viewRoughingPumpEnable) - http.HandleFunc("/rp/off", s.viewRoughingPumpDisable) - http.HandleFunc("/dp/on", s.viewDiffusionPumpEnable) - http.HandleFunc("/dp/off", s.viewDiffusionPumpDisable) +func (d *daemon) httpButtonVent(w http.ResponseWriter, r *http.Request) { + d.ventPress() } diff --git a/succbone/succd/index.html b/succbone/succd/index.html index 08acab3..4d758be 100644 --- a/succbone/succd/index.html +++ b/succbone/succd/index.html @@ -10,10 +10,6 @@ body { } table { font-size: 40px; - margin-top: 1em; -} -table.status td { - width: 2em; } th, td { background-color: #e8e8e8; @@ -62,66 +58,50 @@ td > span {

- - + - + -
Pirani Gauge Voltage{{ .Pirani.Volts }}{{.volts}}
Pressure{{ .Pirani.Mbar }}{{.mbar}}
- - - - - - + + + + - - - - + + + + - - - - - - -
ThresholdsRough{{ if .Feedback.RoughReached }}OK{{ else }}NOK{{ end }}High{{ if .Feedback.HighReached }}OK{{ else }}NOK{{ end }}Rough:...High:...
PumpsRP{{ if .Pumps.RPOn }}ON{{ else }}OFF{{ end }}DP{{ if .Pumps.DPOn }}ON{{ else }}OFF{{ end }}RP:{{ if .rp }}ON{{ else }}OFF{{ end }}DP:{{ if .dp }}ON{{ else }}OFF{{ end }}
SafetyPirani
Failsafe
{{ if .Safety.Failsafe }}ON{{ else }}OFF{{ end }}DP
Lockout
{{ if .Safety.HighPressure }}ON{{ else }}OFF{{ end }}
- - - - - - - - - - - - - + + + + + + + + +
ControlRP - - -
DP - - -
+ Evac Control + + + +
Status OK
FailsafeOK
Diffusion Pump LockoutOK

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

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