Add hysteresis to vacuum thresholds and also fix the web-fronted layout #7

Merged
q3k merged 5 commits from hysteresis into main 2024-10-04 21:32:08 +00:00
4 changed files with 70 additions and 29 deletions

View file

@ -5,12 +5,11 @@
<link rel="shortcut icon" type="image/png" href="/favicon.png"> <link rel="shortcut icon" type="image/png" href="/favicon.png">
<style> <style>
body { body {
font-size: 14px; font-size: 12px;
padding: 2em; padding: 2em;
} }
table { table {
font-size: 40px; font-size: 40px;
margin-top: 1em;
} }
table.status td { table.status td {
width: 2em; width: 2em;
@ -52,6 +51,16 @@ td > span {
.logo > img { .logo > img {
height: 10em; height: 10em;
} }
.main-grid {
margin: 2em;
clear: both;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(50em, 1fr));
column-gap: 2em;
rahix marked this conversation as resolved Outdated
Outdated
Review

Create CSS class for this instead of the massive inlined style.

Create CSS class for this instead of the massive inlined style.
row-gap: 2em;
}
</style> </style>
<div class="logo"><img src="/favicon.png" /></div> <div class="logo"><img src="/favicon.png" /></div>
@ -59,19 +68,7 @@ td > span {
<h1>succd</h1> <h1>succd</h1>
<h2>nothing more permanent than a temporary solution</h2> <h2>nothing more permanent than a temporary solution</h2>
<p style="margin-top: 2em; clear: both;"> <div class="main-grid">
<table>
<tr>
<th rowspan="2">Pirani Gauge</th>
<th>Voltage</th>
<td id="volts">{{ .Pirani.Volts }}</td>
</tr>
<tr>
<th>Pressure</th>
<td id="mbar">{{ .Pirani.Mbar }}</td>
</tr>
</table>
<table class="status"> <table class="status">
<tr> <tr>
<th>Thresholds</th> <th>Thresholds</th>
@ -96,6 +93,19 @@ td > span {
</tr> </tr>
</table> </table>
<table>
<tr>
<th rowspan="2">Pirani Gauge</th>
<th>Voltage</th>
<td id="volts">{{ .Pirani.Volts }}</td>
</tr>
<tr>
<th>Pressure</th>
<td id="mbar">{{ .Pirani.Mbar }}</td>
</tr>
</table>
<table> <table>
<tr> <tr>
<th rowspan="3">Control</th> <th rowspan="3">Control</th>
@ -122,13 +132,14 @@ td > span {
<th>Status</th> <th>Status</th>
<td id="status" colspan="1">OK</td> <td id="status" colspan="1">OK</td>
<th>Load</th> <th>Load</th>
<td id="load" colspan="1">...</td> <td id="load" colspan="1" style="width: 4em;">...</td>
</tr> </tr>
</table> </table>
</p>
<p style="margin-top: 2em;"> <p>
<canvas id="graph" width="1024" height="512" style="max-width: 100%;"></canvas> <canvas id="graph" width="1024" height="512" style="max-width: 100%;"></canvas>
</p> </p>
</div>
<p style="font-style: italic; font-size: 12px; margin-top: 5em;"> <p style="font-style: italic; font-size: 12px; margin-top: 5em;">
{{ .System.Hostname }} | load: {{ .System.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>

View file

@ -14,14 +14,18 @@ var (
flagFake bool flagFake bool
flagListenHTTP string flagListenHTTP string
flagPressureThresholdRough = ScientificNotationValue(1e-1) flagPressureThresholdRough = ScientificNotationValue(1e-1)
flagPressureThresholdRoughHysteresis = ScientificNotationValue(2e-2)
flagPressureThresholdHigh = ScientificNotationValue(1e-4) flagPressureThresholdHigh = ScientificNotationValue(1e-4)
flagPressureThresholdHighHysteresis = ScientificNotationValue(2e-5)
) )
func main() { func main() {
flag.BoolVar(&flagFake, "fake", false, "Enable fake mode which allows to run succd for tests outside the succbone") flag.BoolVar(&flagFake, "fake", false, "Enable fake mode which allows to run succd for tests outside the succbone")
flag.StringVar(&flagListenHTTP, "listen_http", ":8080", "Address at which to listen for HTTP requests") flag.StringVar(&flagListenHTTP, "listen_http", ":8080", "Address at which to listen for HTTP requests")
flag.TextVar(&flagPressureThresholdRough, "pressure_threshold_rough", &flagPressureThresholdRough, "Threshold for opening up diffusion pump (mbar)") flag.TextVar(&flagPressureThresholdRough, "pressure_threshold_rough", &flagPressureThresholdRough, "Threshold for opening up diffusion pump (mbar)")
flag.TextVar(&flagPressureThresholdRoughHysteresis, "pressure_threshold_rough_hysteresis", &flagPressureThresholdRoughHysteresis, "+-Hysteresis for rough threshold (mbar)")
flag.TextVar(&flagPressureThresholdHigh, "pressure_threshold_high", &flagPressureThresholdHigh, "Threshold for enabling high voltage circuits (mbar)") flag.TextVar(&flagPressureThresholdHigh, "pressure_threshold_high", &flagPressureThresholdHigh, "Threshold for enabling high voltage circuits (mbar)")
flag.TextVar(&flagPressureThresholdHighHysteresis, "pressure_threshold_high_hysteresis", &flagPressureThresholdHighHysteresis, "+-Hysteresis for high threshold (mbar)")
flag.Parse() flag.Parse()
ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt) ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt)
@ -32,7 +36,9 @@ func main() {
d.daemonState.piraniVolts100.limit = 100 d.daemonState.piraniVolts100.limit = 100
d.aboveRough.threshold = float64(flagPressureThresholdRough) d.aboveRough.threshold = float64(flagPressureThresholdRough)
d.aboveRough.hysteresis = float64(flagPressureThresholdRoughHysteresis)
d.aboveHigh.threshold = float64(flagPressureThresholdHigh) d.aboveHigh.threshold = float64(flagPressureThresholdHigh)
d.aboveHigh.hysteresis = float64(flagPressureThresholdHighHysteresis)
if flagFake { if flagFake {
klog.Infof("Starting with fake peripherals") klog.Infof("Starting with fake peripherals")
d.adcPirani = &fakeADC{} d.adcPirani = &fakeADC{}

View file

@ -30,13 +30,20 @@ type thresholdOutput struct {
debounce time.Time debounce time.Time
// threshold is the setpoint of the block. // threshold is the setpoint of the block.
threshold float64 threshold float64
// hysteresis around the process setpoint (min/max is threshold +- hysteresis)
hysteresis float64
} }
func (t *thresholdOutput) process(value float64) { func (t *thresholdOutput) process(value float64) {
if time.Now().Before(t.debounce) { if time.Now().Before(t.debounce) {
return return
} }
new := value > t.threshold new := t.output
if t.output {
new = value > (t.threshold - t.hysteresis)
} else {
new = value > (t.threshold + t.hysteresis)
}
if new != t.output { if new != t.output {
t.output = new t.output = new
t.debounce = time.Now().Add(time.Second * 5) t.debounce = time.Now().Add(time.Second * 5)

View file

@ -27,19 +27,36 @@ func TestMomentaryOutput(t *testing.T) {
func TestThresholdOutput(t *testing.T) { func TestThresholdOutput(t *testing.T) {
to := thresholdOutput{ to := thresholdOutput{
threshold: 1, threshold: 1,
hysteresis: 0.2,
} }
to.process(0) to.process(0.7)
if to.output { if to.output {
t.Fatalf("output shouldn't have triggered") t.Fatalf("output shouldn't have triggered")
} }
to.process(2) to.process(1)
if to.output {
t.Fatalf("output shouldn't have triggered")
}
to.process(1.3)
if !to.output { if !to.output {
t.Fatalf("output should have triggered") t.Fatalf("output should have triggered")
} }
to.process(0) to.process(1)
if !to.output {
t.Fatalf("output should have triggered")
}
to.process(0.7)
if !to.output { if !to.output {
t.Fatalf("output should have triggered (in debounce)") t.Fatalf("output should have triggered (in debounce)")
} }
// let debounce timeout pass
time.Sleep(time.Second * 6)
to.process(0.7)
if to.output {
t.Fatalf("output shouldn't have triggered")
}
} }
func TestRingbufferInput(t *testing.T) { func TestRingbufferInput(t *testing.T) {