succd: split out http server, daemon state, daemon controller
This improves the structure of the code, separating the data/control interface out and then implementing the http interface as a user of this interface.
This commit is contained in:
		
							parent
							
								
									3ec6fd1d1b
								
							
						
					
					
						commit
						8f7ec7e141
					
				
					 5 changed files with 190 additions and 180 deletions
				
			
		| 
						 | 
					@ -15,6 +15,10 @@ import (
 | 
				
			||||||
	"k8s.io/klog"
 | 
						"k8s.io/klog"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type httpServer struct {
 | 
				
			||||||
 | 
						d daemonController
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	//go:embed index.html
 | 
						//go:embed index.html
 | 
				
			||||||
	templateIndexText string
 | 
						templateIndexText string
 | 
				
			||||||
| 
						 | 
					@ -23,7 +27,7 @@ var (
 | 
				
			||||||
	favicon []byte
 | 
						favicon []byte
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *daemon) httpFavicon(w http.ResponseWriter, r *http.Request) {
 | 
					func (s *httpServer) viewFavicon(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
	w.Header().Set("Content-Type", "image/png")
 | 
						w.Header().Set("Content-Type", "image/png")
 | 
				
			||||||
	w.Write(favicon)
 | 
						w.Write(favicon)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -96,12 +100,10 @@ type apiData struct {
 | 
				
			||||||
// apiData returns the user data model for the current state of the system. If
 | 
					// 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
 | 
					// skipSystem is set, the System subset is ignored (saves system load, and is
 | 
				
			||||||
// not being served via websockets).
 | 
					// not being served via websockets).
 | 
				
			||||||
func (d *daemon) apiData(skipSystem bool) *apiData {
 | 
					func (s *httpServer) apiData(skipSystem bool) *apiData {
 | 
				
			||||||
	volts, mbar := d.pirani()
 | 
						state := s.d.snapshot()
 | 
				
			||||||
	rp := d.rpGet()
 | 
						volts, mbar := state.pirani()
 | 
				
			||||||
	dp := d.dpGet()
 | 
						rough, high := state.vacuumStatus()
 | 
				
			||||||
	rough, high := d.vacuumStatusGet()
 | 
					 | 
				
			||||||
	safety := d.safetyStatusGet()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var hostname, load string
 | 
						var hostname, load string
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
| 
						 | 
					@ -120,13 +122,13 @@ func (d *daemon) apiData(skipSystem bool) *apiData {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ad := apiData{}
 | 
						ad := apiData{}
 | 
				
			||||||
	ad.Safety.Failsafe = safety.failsafe
 | 
						ad.Safety.Failsafe = state.safety.failsafe
 | 
				
			||||||
	ad.Safety.HighPressure = safety.highPressure
 | 
						ad.Safety.HighPressure = state.safety.highPressure
 | 
				
			||||||
	ad.Pirani.Volts = formatVolts(volts)
 | 
						ad.Pirani.Volts = formatVolts(volts)
 | 
				
			||||||
	ad.Pirani.Mbar = formatMbar(mbar)
 | 
						ad.Pirani.Mbar = formatMbar(mbar)
 | 
				
			||||||
	ad.Pirani.MbarFloat = mbar
 | 
						ad.Pirani.MbarFloat = mbar
 | 
				
			||||||
	ad.Pumps.RPOn = rp
 | 
						ad.Pumps.RPOn = state.rpOn
 | 
				
			||||||
	ad.Pumps.DPOn = dp
 | 
						ad.Pumps.DPOn = state.dpOn
 | 
				
			||||||
	ad.Feedback.RoughReached = rough
 | 
						ad.Feedback.RoughReached = rough
 | 
				
			||||||
	ad.Feedback.HighReached = high
 | 
						ad.Feedback.HighReached = high
 | 
				
			||||||
	ad.System.Load = load
 | 
						ad.System.Load = load
 | 
				
			||||||
| 
						 | 
					@ -135,20 +137,20 @@ func (d *daemon) apiData(skipSystem bool) *apiData {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// httpIndex is the / view.
 | 
					// httpIndex is the / view.
 | 
				
			||||||
func (d *daemon) httpIndex(w http.ResponseWriter, r *http.Request) {
 | 
					func (s *httpServer) viewIndex(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
	if r.URL.Path != "/" {
 | 
						if r.URL.Path != "/" {
 | 
				
			||||||
		http.NotFound(w, r)
 | 
							http.NotFound(w, r)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data := d.apiData(false)
 | 
						data := s.apiData(false)
 | 
				
			||||||
	//data.Pirani.Mbar = html.St
 | 
						//data.Pirani.Mbar = html.St
 | 
				
			||||||
	templateIndex.Execute(w, data)
 | 
						templateIndex.Execute(w, data)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// httpStream is the websocket clientwards data hose, returning a 10Hz update
 | 
					// httpStream is the websocket clientwards data hose, returning a 10Hz update
 | 
				
			||||||
// stream of pressure/voltage.
 | 
					// stream of pressure/voltage.
 | 
				
			||||||
func (d *daemon) httpStream(w http.ResponseWriter, r *http.Request) {
 | 
					func (s *httpServer) viewStream(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
	c, err := websocket.Accept(w, r, nil)
 | 
						c, err := websocket.Accept(w, r, nil)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
| 
						 | 
					@ -165,7 +167,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:
 | 
				
			||||||
			v := d.apiData(true)
 | 
								v := s.apiData(true)
 | 
				
			||||||
			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
 | 
				
			||||||
| 
						 | 
					@ -175,39 +177,54 @@ func (d *daemon) httpStream(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// httpMetrics serves minimalistic Prometheus-compatible metrics.
 | 
					// httpMetrics serves minimalistic Prometheus-compatible metrics.
 | 
				
			||||||
func (d *daemon) httpMetrics(w http.ResponseWriter, r *http.Request) {
 | 
					func (s *httpServer) viewMetrics(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
	// TODO(q3k): also serve Go stuff using the actual Prometheus metrics client
 | 
						// TODO(q3k): also serve Go stuff using the actual Prometheus metrics client
 | 
				
			||||||
	// library.
 | 
						// library.
 | 
				
			||||||
	_, mbar := d.pirani()
 | 
						// TODO(q3k): serve the rest of the data model
 | 
				
			||||||
 | 
						state := s.d.snapshot()
 | 
				
			||||||
 | 
						_, mbar := state.pirani()
 | 
				
			||||||
	fmt.Fprintf(w, "# HELP sem_pressure_mbar Pressure in the SEM chamber, in millibar\n")
 | 
						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, "# TYPE sem_pressure_mbar gauge\n")
 | 
				
			||||||
	fmt.Fprintf(w, "sem_pressure_mbar %f\n", mbar)
 | 
						fmt.Fprintf(w, "sem_pressure_mbar %f\n", mbar)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *daemon) httpRoughingPumpEnable(w http.ResponseWriter, r *http.Request) {
 | 
					func (s *httpServer) viewRoughingPumpEnable(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
	d.rpSet(true)
 | 
						s.d.rpSet(true)
 | 
				
			||||||
	fmt.Fprintf(w, "succ on\n")
 | 
						fmt.Fprintf(w, "succ on\n")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *daemon) httpRoughingPumpDisable(w http.ResponseWriter, r *http.Request) {
 | 
					func (s *httpServer) viewRoughingPumpDisable(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
	d.rpSet(false)
 | 
						s.d.rpSet(false)
 | 
				
			||||||
	fmt.Fprintf(w, "succ off\n")
 | 
						fmt.Fprintf(w, "succ off\n")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *daemon) httpDiffusionPumpEnable(w http.ResponseWriter, r *http.Request) {
 | 
					func (s *httpServer) viewDiffusionPumpEnable(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
	d.dpSet(true)
 | 
						s.d.dpSet(true)
 | 
				
			||||||
	fmt.Fprintf(w, "deep succ on\n")
 | 
						fmt.Fprintf(w, "deep succ on\n")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *daemon) httpDiffusionPumpDisable(w http.ResponseWriter, r *http.Request) {
 | 
					func (s *httpServer) viewDiffusionPumpDisable(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
	d.dpSet(false)
 | 
						s.d.dpSet(false)
 | 
				
			||||||
	fmt.Fprintf(w, "deep succ off\n")
 | 
						fmt.Fprintf(w, "deep succ off\n")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *daemon) httpButtonPumpDown(w http.ResponseWriter, r *http.Request) {
 | 
					func (s *httpServer) viewButtonPumpDown(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
	d.pumpDownPress()
 | 
						s.d.pumpDownPress()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *daemon) httpButtonVent(w http.ResponseWriter, r *http.Request) {
 | 
					func (s *httpServer) viewButtonVent(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
	d.ventPress()
 | 
						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)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,9 +26,9 @@ func main() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt)
 | 
						ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	d := daemon{
 | 
						d := daemon{}
 | 
				
			||||||
		rpOn: true,
 | 
						d.daemonState.rpOn = true
 | 
				
			||||||
	}
 | 
					
 | 
				
			||||||
	d.aboveRough.threshold = float64(flagPressureThresholdRough)
 | 
						d.aboveRough.threshold = float64(flagPressureThresholdRough)
 | 
				
			||||||
	d.aboveHigh.threshold = float64(flagPressureThresholdHigh)
 | 
						d.aboveHigh.threshold = float64(flagPressureThresholdHigh)
 | 
				
			||||||
	if flagFake {
 | 
						if flagFake {
 | 
				
			||||||
| 
						 | 
					@ -65,16 +65,10 @@ func main() {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	http.HandleFunc("/", d.httpIndex)
 | 
						httpServer := httpServer{
 | 
				
			||||||
	http.HandleFunc("/favicon.png", d.httpFavicon)
 | 
							d: &d,
 | 
				
			||||||
	http.HandleFunc("/stream", d.httpStream)
 | 
						}
 | 
				
			||||||
	http.HandleFunc("/metrics", d.httpMetrics)
 | 
						httpServer.setupViews()
 | 
				
			||||||
	http.HandleFunc("/button/vent", d.httpButtonVent)
 | 
					 | 
				
			||||||
	http.HandleFunc("/button/pumpdown", d.httpButtonPumpDown)
 | 
					 | 
				
			||||||
	http.HandleFunc("/rp/on", d.httpRoughingPumpEnable)
 | 
					 | 
				
			||||||
	http.HandleFunc("/rp/off", d.httpRoughingPumpDisable)
 | 
					 | 
				
			||||||
	http.HandleFunc("/dp/on", d.httpDiffusionPumpEnable)
 | 
					 | 
				
			||||||
	http.HandleFunc("/dp/off", d.httpDiffusionPumpDisable)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	klog.Infof("Listening for HTTP at %s", flagListenHTTP)
 | 
						klog.Infof("Listening for HTTP at %s", flagListenHTTP)
 | 
				
			||||||
	go func() {
 | 
						go func() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,21 +4,18 @@ import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"math"
 | 
					 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"k8s.io/klog"
 | 
						"k8s.io/klog"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// daemon is the main state of the succdaemon.
 | 
					// daemon is the main service of the succdaemon.
 | 
				
			||||||
type daemon struct {
 | 
					type daemon struct {
 | 
				
			||||||
	// adcPirani is the adc implementation returning the voltage of the Pfeiffer
 | 
						// adcPirani is the adc implementation returning the voltage of the Pfeiffer
 | 
				
			||||||
	// Pirani gauge.
 | 
						// Pirani gauge.
 | 
				
			||||||
	adcPirani adc
 | 
						adcPirani adc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	safety safetyStatus
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	gpioDiffusionPump gpio
 | 
						gpioDiffusionPump gpio
 | 
				
			||||||
	gpioRoughingPump  gpio
 | 
						gpioRoughingPump  gpio
 | 
				
			||||||
	gpioBtnPumpDown   gpio
 | 
						gpioBtnPumpDown   gpio
 | 
				
			||||||
| 
						 | 
					@ -26,27 +23,9 @@ type daemon struct {
 | 
				
			||||||
	gpioBelowRough    gpio
 | 
						gpioBelowRough    gpio
 | 
				
			||||||
	gpioBelowHigh     gpio
 | 
						gpioBelowHigh     gpio
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// mu guards state variables below.
 | 
						// mu guards the state below.
 | 
				
			||||||
	mu sync.RWMutex
 | 
						mu sync.RWMutex
 | 
				
			||||||
	// adcPiraniVolts is a moving window of read ADC values, used to calculate a
 | 
						daemonState
 | 
				
			||||||
	// moving average.
 | 
					 | 
				
			||||||
	adcPiraniVolts []float32
 | 
					 | 
				
			||||||
	rpOn           bool
 | 
					 | 
				
			||||||
	dpOn           bool
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	vent       momentaryOutput
 | 
					 | 
				
			||||||
	pumpdown   momentaryOutput
 | 
					 | 
				
			||||||
	aboveRough thresholdOutput
 | 
					 | 
				
			||||||
	aboveHigh  thresholdOutput
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type safetyStatus struct {
 | 
					 | 
				
			||||||
	// failsafe mode is enabled when the pirani gauge appears to be
 | 
					 | 
				
			||||||
	// disconnected, and is disabled only when an atmosphere is read.
 | 
					 | 
				
			||||||
	failsafe bool
 | 
					 | 
				
			||||||
	// highPressure mode is enabled when the pressure reading is above 1e-1
 | 
					 | 
				
			||||||
	// mbar, locking out the diffusion pump from being enabled.
 | 
					 | 
				
			||||||
	highPressure bool
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// momentaryOutput is an output that can be triggered for 500ms.
 | 
					// momentaryOutput is an output that can be triggered for 500ms.
 | 
				
			||||||
| 
						 | 
					@ -127,7 +106,7 @@ func (d *daemon) processOnce(_ context.Context) error {
 | 
				
			||||||
	d.pumpdown.process()
 | 
						d.pumpdown.process()
 | 
				
			||||||
	d.vent.process()
 | 
						d.vent.process()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, mbar := d.piraniUnlocked()
 | 
						_, mbar := d.daemonState.pirani()
 | 
				
			||||||
	d.aboveRough.process(float64(mbar))
 | 
						d.aboveRough.process(float64(mbar))
 | 
				
			||||||
	d.aboveHigh.process(float64(mbar))
 | 
						d.aboveHigh.process(float64(mbar))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -201,116 +180,3 @@ func (d *daemon) processOnce(_ context.Context) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
type piraniDetection uint
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const (
 | 
					 | 
				
			||||||
	// piraniDetectionUnknown means the system isn't yet sure whether the pirani
 | 
					 | 
				
			||||||
	// gauge is connected.
 | 
					 | 
				
			||||||
	piraniDetectionUnknown piraniDetection = iota
 | 
					 | 
				
			||||||
	// piraniDetectionConnected means the system assumes the pirani gauge is
 | 
					 | 
				
			||||||
	// connected.
 | 
					 | 
				
			||||||
	piraniDetectionConnected = iota
 | 
					 | 
				
			||||||
	// piraniDetectionDisconnected means the system assumes the pirani gauge is
 | 
					 | 
				
			||||||
	// disconnected.
 | 
					 | 
				
			||||||
	piraniDetectionDisconnected = iota
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// piraniDetection guesses whether the pirani gauge is connected.
 | 
					 | 
				
			||||||
func (d *daemon) piraniDetection() piraniDetection {
 | 
					 | 
				
			||||||
	if len(d.adcPiraniVolts) < 3 {
 | 
					 | 
				
			||||||
		return piraniDetectionUnknown
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	volts := float32(0.0)
 | 
					 | 
				
			||||||
	for _, v := range d.adcPiraniVolts[len(d.adcPiraniVolts)-3:] {
 | 
					 | 
				
			||||||
		volts += v
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	volts /= 3.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bar := math.Pow(10.0, float64(volts)-8.5)
 | 
					 | 
				
			||||||
	mbar := float32(bar * 1000.0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if mbar < 4e-6 {
 | 
					 | 
				
			||||||
		return piraniDetectionDisconnected
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return piraniDetectionConnected
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// pirani returns the Pirani gauge voltage and pressure.
 | 
					 | 
				
			||||||
func (d *daemon) pirani() (volts float32, mbar float32) {
 | 
					 | 
				
			||||||
	d.mu.RLock()
 | 
					 | 
				
			||||||
	volts, mbar = d.piraniUnlocked()
 | 
					 | 
				
			||||||
	d.mu.RUnlock()
 | 
					 | 
				
			||||||
	return
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (d *daemon) piraniUnlocked() (volts float32, mbar float32) {
 | 
					 | 
				
			||||||
	volts = 0.0
 | 
					 | 
				
			||||||
	for _, v := range d.adcPiraniVolts {
 | 
					 | 
				
			||||||
		volts += v
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(d.adcPiraniVolts) != 0 {
 | 
					 | 
				
			||||||
		volts /= float32(len(d.adcPiraniVolts))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Per Pirani probe docs.
 | 
					 | 
				
			||||||
	bar := math.Pow(10.0, float64(volts)-8.5)
 | 
					 | 
				
			||||||
	mbar = float32(bar * 1000.0)
 | 
					 | 
				
			||||||
	return
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// rpSet enables/disables the roughing pump.
 | 
					 | 
				
			||||||
func (d *daemon) rpSet(state bool) {
 | 
					 | 
				
			||||||
	d.mu.Lock()
 | 
					 | 
				
			||||||
	defer d.mu.Unlock()
 | 
					 | 
				
			||||||
	d.rpOn = state
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// rpGet returns whether the roughing pump is enabled/disabled.
 | 
					 | 
				
			||||||
func (d *daemon) rpGet() bool {
 | 
					 | 
				
			||||||
	d.mu.RLock()
 | 
					 | 
				
			||||||
	defer d.mu.RUnlock()
 | 
					 | 
				
			||||||
	return d.rpOn
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// dpSet enables/disables the diffusion pump.
 | 
					 | 
				
			||||||
func (d *daemon) dpSet(state bool) {
 | 
					 | 
				
			||||||
	d.mu.Lock()
 | 
					 | 
				
			||||||
	defer d.mu.Unlock()
 | 
					 | 
				
			||||||
	d.dpOn = state
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// dpGet returns whether the diffusion pump is enabled/disabled.
 | 
					 | 
				
			||||||
func (d *daemon) dpGet() bool {
 | 
					 | 
				
			||||||
	d.mu.RLock()
 | 
					 | 
				
			||||||
	defer d.mu.RUnlock()
 | 
					 | 
				
			||||||
	return d.dpOn
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (d *daemon) vacuumStatusGet() (rough, high bool) {
 | 
					 | 
				
			||||||
	d.mu.RLock()
 | 
					 | 
				
			||||||
	defer d.mu.RUnlock()
 | 
					 | 
				
			||||||
	rough = !d.aboveRough.output
 | 
					 | 
				
			||||||
	high = !d.aboveHigh.output
 | 
					 | 
				
			||||||
	return
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (d *daemon) safetyStatusGet() safetyStatus {
 | 
					 | 
				
			||||||
	d.mu.RLock()
 | 
					 | 
				
			||||||
	defer d.mu.RUnlock()
 | 
					 | 
				
			||||||
	return d.safety
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// pumpDownPressed toggles the pump down relay for 500ms.
 | 
					 | 
				
			||||||
func (d *daemon) pumpDownPress() {
 | 
					 | 
				
			||||||
	d.mu.Lock()
 | 
					 | 
				
			||||||
	defer d.mu.Unlock()
 | 
					 | 
				
			||||||
	d.pumpdown.trigger()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ventPress toggles the vent relay for 500ms.
 | 
					 | 
				
			||||||
func (d *daemon) ventPress() {
 | 
					 | 
				
			||||||
	d.mu.Lock()
 | 
					 | 
				
			||||||
	defer d.mu.Unlock()
 | 
					 | 
				
			||||||
	d.vent.trigger()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										49
									
								
								succbone/succd/process_controller.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								succbone/succd/process_controller.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,49 @@
 | 
				
			||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// daemonController is the control/data interface passed on to external system
 | 
				
			||||||
 | 
					// controllers, eg. the web interface.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This is a subset of daemon functions limited to a safe, explicit subset.
 | 
				
			||||||
 | 
					type daemonController interface {
 | 
				
			||||||
 | 
						// snapshot returns an internally consistent copy of the daemon state.
 | 
				
			||||||
 | 
						snapshot() *daemonState
 | 
				
			||||||
 | 
						// rpSet enables/disables the roughing pump.
 | 
				
			||||||
 | 
						rpSet(bool)
 | 
				
			||||||
 | 
						// dpSet enables/disables the diffusion pump.
 | 
				
			||||||
 | 
						dpSet(bool)
 | 
				
			||||||
 | 
						// pumpDownPress simulates a pumpdown button press.
 | 
				
			||||||
 | 
						pumpDownPress()
 | 
				
			||||||
 | 
						// ventPRess simulates a vent button press.
 | 
				
			||||||
 | 
						ventPress()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *daemon) snapshot() *daemonState {
 | 
				
			||||||
 | 
						d.mu.RLock()
 | 
				
			||||||
 | 
						ds := d.daemonState
 | 
				
			||||||
 | 
						d.mu.RUnlock()
 | 
				
			||||||
 | 
						return &ds
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *daemon) rpSet(state bool) {
 | 
				
			||||||
 | 
						d.mu.Lock()
 | 
				
			||||||
 | 
						defer d.mu.Unlock()
 | 
				
			||||||
 | 
						d.rpOn = state
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *daemon) dpSet(state bool) {
 | 
				
			||||||
 | 
						d.mu.Lock()
 | 
				
			||||||
 | 
						defer d.mu.Unlock()
 | 
				
			||||||
 | 
						d.dpOn = state
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *daemon) pumpDownPress() {
 | 
				
			||||||
 | 
						d.mu.Lock()
 | 
				
			||||||
 | 
						defer d.mu.Unlock()
 | 
				
			||||||
 | 
						d.pumpdown.trigger()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *daemon) ventPress() {
 | 
				
			||||||
 | 
						d.mu.Lock()
 | 
				
			||||||
 | 
						defer d.mu.Unlock()
 | 
				
			||||||
 | 
						d.vent.trigger()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										84
									
								
								succbone/succd/process_state.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								succbone/succd/process_state.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,84 @@
 | 
				
			||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "math"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// daemonState contains all the state of the daemon. A copy of it can be
 | 
				
			||||||
 | 
					// requested for consumers, eg. the web view.
 | 
				
			||||||
 | 
					type daemonState struct {
 | 
				
			||||||
 | 
						safety safetyStatus
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// adcPiraniVolts is a moving window of read ADC values, used to calculate a
 | 
				
			||||||
 | 
						// moving average.
 | 
				
			||||||
 | 
						adcPiraniVolts []float32
 | 
				
			||||||
 | 
						rpOn           bool
 | 
				
			||||||
 | 
						dpOn           bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vent       momentaryOutput
 | 
				
			||||||
 | 
						pumpdown   momentaryOutput
 | 
				
			||||||
 | 
						aboveRough thresholdOutput
 | 
				
			||||||
 | 
						aboveHigh  thresholdOutput
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type safetyStatus struct {
 | 
				
			||||||
 | 
						// failsafe mode is enabled when the pirani gauge appears to be
 | 
				
			||||||
 | 
						// disconnected, and is disabled only when an atmosphere is read.
 | 
				
			||||||
 | 
						failsafe bool
 | 
				
			||||||
 | 
						// highPressure mode is enabled when the pressure reading is above 1e-1
 | 
				
			||||||
 | 
						// mbar, locking out the diffusion pump from being enabled.
 | 
				
			||||||
 | 
						highPressure bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type piraniDetection uint
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						// piraniDetectionUnknown means the system isn't yet sure whether the pirani
 | 
				
			||||||
 | 
						// gauge is connected.
 | 
				
			||||||
 | 
						piraniDetectionUnknown piraniDetection = iota
 | 
				
			||||||
 | 
						// piraniDetectionConnected means the system assumes the pirani gauge is
 | 
				
			||||||
 | 
						// connected.
 | 
				
			||||||
 | 
						piraniDetectionConnected = iota
 | 
				
			||||||
 | 
						// piraniDetectionDisconnected means the system assumes the pirani gauge is
 | 
				
			||||||
 | 
						// disconnected.
 | 
				
			||||||
 | 
						piraniDetectionDisconnected = iota
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// piraniDetection guesses whether the pirani gauge is connected.
 | 
				
			||||||
 | 
					func (d *daemonState) piraniDetection() piraniDetection {
 | 
				
			||||||
 | 
						if len(d.adcPiraniVolts) < 3 {
 | 
				
			||||||
 | 
							return piraniDetectionUnknown
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						volts := float32(0.0)
 | 
				
			||||||
 | 
						for _, v := range d.adcPiraniVolts[len(d.adcPiraniVolts)-3:] {
 | 
				
			||||||
 | 
							volts += v
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						volts /= 3.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bar := math.Pow(10.0, float64(volts)-8.5)
 | 
				
			||||||
 | 
						mbar := float32(bar * 1000.0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if mbar < 4e-6 {
 | 
				
			||||||
 | 
							return piraniDetectionDisconnected
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return piraniDetectionConnected
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *daemonState) pirani() (volts float32, mbar float32) {
 | 
				
			||||||
 | 
						volts = 0.0
 | 
				
			||||||
 | 
						for _, v := range d.adcPiraniVolts {
 | 
				
			||||||
 | 
							volts += v
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(d.adcPiraniVolts) != 0 {
 | 
				
			||||||
 | 
							volts /= float32(len(d.adcPiraniVolts))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Per Pirani probe docs.
 | 
				
			||||||
 | 
						bar := math.Pow(10.0, float64(volts)-8.5)
 | 
				
			||||||
 | 
						mbar = float32(bar * 1000.0)
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *daemonState) vacuumStatus() (rough, high bool) {
 | 
				
			||||||
 | 
						rough = !d.aboveRough.output
 | 
				
			||||||
 | 
						high = !d.aboveHigh.output
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue