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">
<style>
body {
font-size: 14px;
font-size: 12px;
padding: 2em;
}
table {
font-size: 40px;
margin-top: 1em;
}
table.status td {
width: 2em;
@ -52,6 +51,16 @@ td > span {
.logo > img {
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>
<div class="logo"><img src="/favicon.png" /></div>
@ -59,19 +68,7 @@ td > span {
<h1>succd</h1>
<h2>nothing more permanent than a temporary solution</h2>
<p style="margin-top: 2em; clear: both;">
<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>
<div class="main-grid">
<table class="status">
<tr>
<th>Thresholds</th>
@ -96,6 +93,19 @@ td > span {
</tr>
</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>
<tr>
<th rowspan="3">Control</th>
@ -122,13 +132,14 @@ td > span {
<th>Status</th>
<td id="status" colspan="1">OK</td>
<th>Load</th>
<td id="load" colspan="1">...</td>
<td id="load" colspan="1" style="width: 4em;">...</td>
</tr>
</table>
</p>
<p style="margin-top: 2em;">
<p>
<canvas id="graph" width="1024" height="512" style="max-width: 100%;"></canvas>
</p>
</div>
<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>

View file

@ -14,14 +14,18 @@ var (
flagFake bool
flagListenHTTP string
flagPressureThresholdRough = ScientificNotationValue(1e-1)
flagPressureThresholdRoughHysteresis = ScientificNotationValue(2e-2)
flagPressureThresholdHigh = ScientificNotationValue(1e-4)
flagPressureThresholdHighHysteresis = ScientificNotationValue(2e-5)
)
func main() {
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.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(&flagPressureThresholdHighHysteresis, "pressure_threshold_high_hysteresis", &flagPressureThresholdHighHysteresis, "+-Hysteresis for high threshold (mbar)")
flag.Parse()
ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt)
@ -32,7 +36,9 @@ func main() {
d.daemonState.piraniVolts100.limit = 100
d.aboveRough.threshold = float64(flagPressureThresholdRough)
d.aboveRough.hysteresis = float64(flagPressureThresholdRoughHysteresis)
d.aboveHigh.threshold = float64(flagPressureThresholdHigh)
d.aboveHigh.hysteresis = float64(flagPressureThresholdHighHysteresis)
if flagFake {
klog.Infof("Starting with fake peripherals")
d.adcPirani = &fakeADC{}

View file

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

View file

@ -27,19 +27,36 @@ func TestMomentaryOutput(t *testing.T) {
func TestThresholdOutput(t *testing.T) {
to := thresholdOutput{
threshold: 1,
hysteresis: 0.2,
}
to.process(0)
to.process(0.7)
if to.output {
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 {
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 {
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) {