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)
|
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.
|
// httpIndex is the / view.
|
||||||
func (d *daemon) httpIndex(w http.ResponseWriter, r *http.Request) {
|
func (d *daemon) httpIndex(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.URL.Path != "/" {
|
if r.URL.Path != "/" {
|
||||||
|
@ -57,33 +141,9 @@ func (d *daemon) httpIndex(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
volts, mbar := d.pirani()
|
data := d.apiData(false)
|
||||||
rp := d.rpGet()
|
//data.Pirani.Mbar = html.St
|
||||||
dp := d.dpGet()
|
templateIndex.Execute(w, data)
|
||||||
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,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// httpStream is the websocket clientwards data hose, returning a 10Hz update
|
// 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, "")
|
c.Close(websocket.StatusNormalClosure, "")
|
||||||
return
|
return
|
||||||
case <-t.C:
|
case <-t.C:
|
||||||
// TODO(q3k): don't poll, get notified when new ADC readout is available.
|
v := d.apiData(true)
|
||||||
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,
|
|
||||||
}
|
|
||||||
if err := wsjson.Write(ctx, c, v); err != nil {
|
if err := wsjson.Write(ctx, c, v); err != nil {
|
||||||
klog.Errorf("Websocket write failed: %v", err)
|
klog.Errorf("Websocket write failed: %v", err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -59,25 +59,25 @@ td > span {
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Voltage</th>
|
<th>Voltage</th>
|
||||||
<td id="volts" colspan="4">{{.volts}}</td>
|
<td id="volts" colspan="4">{{ .Pirani.Volts }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Pressure</th>
|
<th>Pressure</th>
|
||||||
<td id="mbar" colspan="4">{{.mbar}}</td>
|
<td id="mbar" colspan="4">{{ .Pirani.Mbar }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Thresholds</th>
|
<th>Thresholds</th>
|
||||||
<td>Rough:</td>
|
<td>Rough:</td>
|
||||||
<td id="trough">...</td>
|
<td id="trough">{{ if .Feedback.RoughReached }}OK{{ else }}NOK{{ end }}</td>
|
||||||
<td>High:</td>
|
<td>High:</td>
|
||||||
<td id="thigh">...</td>
|
<td id="thigh">{{ if .Feedback.HighReached }}OK{{ else }}NOK{{ end }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Pumps</th>
|
<th>Pumps</th>
|
||||||
<td>RP:</td>
|
<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>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>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Evac Control</th>
|
<th>Evac Control</th>
|
||||||
|
@ -96,11 +96,11 @@ td > span {
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Failsafe</th>
|
<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>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Diffusion Pump Lockout</th>
|
<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>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</p>
|
</p>
|
||||||
|
@ -109,7 +109,7 @@ td > span {
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p style="font-style: italic; font-size: 12px; margin-top: 5em;">
|
<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>
|
</p>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -288,50 +288,53 @@ window.addEventListener("load", (_) => {
|
||||||
});
|
});
|
||||||
socket.addEventListener("message", (event) => {
|
socket.addEventListener("message", (event) => {
|
||||||
const data = JSON.parse(event.data);
|
const data = JSON.parse(event.data);
|
||||||
volts.innerHTML = data.Volts;
|
volts.innerHTML = data.Pirani.Volts;
|
||||||
mbar.innerHTML = data.Mbar;
|
mbar.innerHTML = data.Pirani.Mbar;
|
||||||
failsafe.innerHTML = data.Failsafe ? "ON" : "OFF";
|
if (data.Safety.Failsafe) {
|
||||||
if (data.Failsafe) {
|
failsafe.innerHTML = "ON";
|
||||||
failsafe.style = "background-color: #f06060";
|
failsafe.style = "background-color: #f06060";
|
||||||
} else {
|
} else {
|
||||||
failsafe.style = "background-color: #60f060";
|
failsafe.innerHTML = "OFF";
|
||||||
|
failsafe.style = "background-color: #60f060";
|
||||||
}
|
}
|
||||||
if (data.HighPressure) {
|
if (data.Safety.HighPressure) {
|
||||||
highpressure.innerHTML = "ON";
|
highpressure.innerHTML = "ON";
|
||||||
highpressure.style = "background-color: #f06060";
|
highpressure.style = "background-color: #f06060";
|
||||||
} else {
|
} else {
|
||||||
highpressure.innerHTML = "OFF";
|
highpressure.innerHTML = "OFF";
|
||||||
highpressure.style = "background-color: #60f060";
|
highpressure.style = "background-color: #60f060";
|
||||||
}
|
}
|
||||||
rp.innerHTML = data.RPOn ? "ON" : "OFF";
|
if (data.Pumps.RPOn) {
|
||||||
if (data.RPOn) {
|
rp.innerHTML = "ON";
|
||||||
rp.style = "background-color: #60f060";
|
rp.style = "background-color: #60f060";
|
||||||
} else {
|
} else {
|
||||||
|
rp.innerHTML = "OFF";
|
||||||
rp.style = "background-color: #f06060";
|
rp.style = "background-color: #f06060";
|
||||||
}
|
}
|
||||||
dp.innerHTML = data.DPOn ? "ON" : "OFF";
|
if (data.Pumps.DPOn) {
|
||||||
if (data.DPOn) {
|
dp.innerHTML = "ON";
|
||||||
dp.style = "background-color: #60f060";
|
dp.style = "background-color: #60f060";
|
||||||
} else {
|
} else {
|
||||||
|
dp.innerHTML = "OFF";
|
||||||
dp.style = "background-color: #f06060";
|
dp.style = "background-color: #f06060";
|
||||||
}
|
}
|
||||||
|
|
||||||
let t = [];
|
let t = [];
|
||||||
if (data.RoughReached) {
|
if (data.Feedback.RoughReached) {
|
||||||
trough.innerHTML = "OK";
|
trough.innerHTML = "OK";
|
||||||
trough.style = "background-color: #60f060";
|
trough.style = "background-color: #60f060";
|
||||||
} else {
|
} else {
|
||||||
trough.innerHTML = "NOK";
|
trough.innerHTML = "NOK";
|
||||||
trough.style = "background-color: #f06060";
|
trough.style = "background-color: #f06060";
|
||||||
}
|
}
|
||||||
if (data.HighReached) {
|
if (data.Feedback.HighReached) {
|
||||||
thigh.innerHTML = "OK";
|
thigh.innerHTML = "OK";
|
||||||
thigh.style = "background-color: #60f060";
|
thigh.style = "background-color: #60f060";
|
||||||
} else {
|
} else {
|
||||||
thigh.innerHTML = "NOK";
|
thigh.innerHTML = "NOK";
|
||||||
thigh.style = "background-color: #f06060";
|
thigh.style = "background-color: #f06060";
|
||||||
}
|
}
|
||||||
historicalPush(data.MbarFloat);
|
historicalPush(data.Pirani.MbarFloat);
|
||||||
ping.innerHTML = Date.now();
|
ping.innerHTML = Date.now();
|
||||||
});
|
});
|
||||||
socket.addEventListener("close", (event) => {
|
socket.addEventListener("close", (event) => {
|
||||||
|
|
Loading…
Reference in a new issue