succd: unify html/js data source
This commit is contained in:
parent
776f7a9911
commit
3ec6fd1d1b
|
@ -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
|
||||
|
|
|
@ -59,25 +59,25 @@ td > span {
|
|||
<table>
|
||||
<tr>
|
||||
<th>Voltage</th>
|
||||
<td id="volts" colspan="4">{{.volts}}</td>
|
||||
<td id="volts" colspan="4">{{ .Pirani.Volts }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Pressure</th>
|
||||
<td id="mbar" colspan="4">{{.mbar}}</td>
|
||||
<td id="mbar" colspan="4">{{ .Pirani.Mbar }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Thresholds</th>
|
||||
<td>Rough:</td>
|
||||
<td id="trough">...</td>
|
||||
<td id="trough">{{ if .Feedback.RoughReached }}OK{{ else }}NOK{{ end }}</td>
|
||||
<td>High:</td>
|
||||
<td id="thigh">...</td>
|
||||
<td id="thigh">{{ if .Feedback.HighReached }}OK{{ else }}NOK{{ end }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Pumps</th>
|
||||
<td>RP:</td>
|
||||
<td id="rp">{{ if .rp }}ON{{ else }}OFF{{ end }}</td>
|
||||
<td id="rp">{{ if .Pumps.RPOn }}ON{{ else }}OFF{{ end }}</td>
|
||||
<td>DP:</td>
|
||||
<td id="dp">{{ if .dp }}ON{{ else }}OFF{{ end }}</td>
|
||||
<td id="dp">{{ if .Pumps.DPOn }}ON{{ else }}OFF{{ end }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Evac Control</th>
|
||||
|
@ -96,11 +96,11 @@ td > span {
|
|||
</tr>
|
||||
<tr>
|
||||
<th>Failsafe</th>
|
||||
<td id="failsafe" colspan="4">OK</td>
|
||||
<td id="failsafe" colspan="4">{{ if .Safety.Failsafe }}ON{{ else }}OFF{{ end }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Diffusion Pump Lockout</th>
|
||||
<td id="highpressure" colspan="4">OK</td>
|
||||
<td id="highpressure" colspan="4">{{ if .Safety.HighPressure }}ON{{ else }}OFF{{ end }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</p>
|
||||
|
@ -109,7 +109,7 @@ td > span {
|
|||
</p>
|
||||
|
||||
<p style="font-style: italic; font-size: 12px; margin-top: 5em;">
|
||||
{{.hostname}} | load: {{.load}} | <a href="/debug/pprof">pprof</a> | <a href="/metrics">metrics</a> | ws ping: <span id="ping">…</span>
|
||||
{{ .System.Hostname }} | load: {{ .System.Load }} | <a href="/debug/pprof">pprof</a> | <a href="/metrics">metrics</a> | ws ping: <span id="ping">…</span>
|
||||
</p>
|
||||
|
||||
<script>
|
||||
|
@ -288,50 +288,53 @@ window.addEventListener("load", (_) => {
|
|||
});
|
||||
socket.addEventListener("message", (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
volts.innerHTML = data.Volts;
|
||||
mbar.innerHTML = data.Mbar;
|
||||
failsafe.innerHTML = data.Failsafe ? "ON" : "OFF";
|
||||
if (data.Failsafe) {
|
||||
failsafe.style = "background-color: #f06060";
|
||||
volts.innerHTML = data.Pirani.Volts;
|
||||
mbar.innerHTML = data.Pirani.Mbar;
|
||||
if (data.Safety.Failsafe) {
|
||||
failsafe.innerHTML = "ON";
|
||||
failsafe.style = "background-color: #f06060";
|
||||
} else {
|
||||
failsafe.style = "background-color: #60f060";
|
||||
failsafe.innerHTML = "OFF";
|
||||
failsafe.style = "background-color: #60f060";
|
||||
}
|
||||
if (data.HighPressure) {
|
||||
highpressure.innerHTML = "ON";
|
||||
highpressure.style = "background-color: #f06060";
|
||||
if (data.Safety.HighPressure) {
|
||||
highpressure.innerHTML = "ON";
|
||||
highpressure.style = "background-color: #f06060";
|
||||
} else {
|
||||
highpressure.innerHTML = "OFF";
|
||||
highpressure.style = "background-color: #60f060";
|
||||
highpressure.innerHTML = "OFF";
|
||||
highpressure.style = "background-color: #60f060";
|
||||
}
|
||||
rp.innerHTML = data.RPOn ? "ON" : "OFF";
|
||||
if (data.RPOn) {
|
||||
if (data.Pumps.RPOn) {
|
||||
rp.innerHTML = "ON";
|
||||
rp.style = "background-color: #60f060";
|
||||
} else {
|
||||
rp.innerHTML = "OFF";
|
||||
rp.style = "background-color: #f06060";
|
||||
}
|
||||
dp.innerHTML = data.DPOn ? "ON" : "OFF";
|
||||
if (data.DPOn) {
|
||||
if (data.Pumps.DPOn) {
|
||||
dp.innerHTML = "ON";
|
||||
dp.style = "background-color: #60f060";
|
||||
} else {
|
||||
dp.innerHTML = "OFF";
|
||||
dp.style = "background-color: #f06060";
|
||||
}
|
||||
|
||||
let t = [];
|
||||
if (data.RoughReached) {
|
||||
if (data.Feedback.RoughReached) {
|
||||
trough.innerHTML = "OK";
|
||||
trough.style = "background-color: #60f060";
|
||||
} else {
|
||||
trough.innerHTML = "NOK";
|
||||
trough.style = "background-color: #f06060";
|
||||
}
|
||||
if (data.HighReached) {
|
||||
if (data.Feedback.HighReached) {
|
||||
thigh.innerHTML = "OK";
|
||||
thigh.style = "background-color: #60f060";
|
||||
} else {
|
||||
thigh.innerHTML = "NOK";
|
||||
thigh.style = "background-color: #f06060";
|
||||
}
|
||||
historicalPush(data.MbarFloat);
|
||||
historicalPush(data.Pirani.MbarFloat);
|
||||
ping.innerHTML = Date.now();
|
||||
});
|
||||
socket.addEventListener("close", (event) => {
|
||||
|
|
Loading…
Reference in a new issue