aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSam Anthony <sam@samanthony.xyz>2024-02-08 01:37:07 -0500
committerSam Anthony <sam@samanthony.xyz>2024-02-08 01:37:07 -0500
commita4ca32963cba9922ec92efe1efa165d880513752 (patch)
treef887f1d40c0238e2c9977a130a3775078359c704
parent7da4e714fee24ac3dca1a6e060edf60bb3954e09 (diff)
downloadvolute-a4ca32963cba9922ec92efe1efa165d880513752.zip
refactor main
-rw-r--r--broadcast.go59
-rw-r--r--gui/widget/widget.go1
-rw-r--r--main.go156
3 files changed, 167 insertions, 49 deletions
diff --git a/broadcast.go b/broadcast.go
new file mode 100644
index 0000000..7268971
--- /dev/null
+++ b/broadcast.go
@@ -0,0 +1,59 @@
+package main
+
+import "sync"
+
+// Broadcast sends data sent from source to all destination channels.
+type Broadcast[T any] struct {
+ source chan T
+ destinations []chan<- T
+
+ mu sync.Mutex
+ wg sync.WaitGroup
+}
+
+// The caller is responsible for closing source. When source is closed,
+// Broadcast will close all destinations.
+func NewBroadcast[T any](source chan T) Broadcast[T] {
+ bc := Broadcast[T]{
+ source,
+ make([]chan<- T, 0),
+ sync.Mutex{},
+ sync.WaitGroup{},
+ }
+
+ go func(bc *Broadcast[T]) {
+ bc.wg.Add(1)
+
+ for v := range bc.source {
+ bc.mu.Lock()
+ for _, dest := range bc.destinations {
+ dest <- v
+ }
+ bc.mu.Unlock()
+ }
+
+ bc.mu.Lock()
+ for _, dest := range bc.destinations {
+ close(dest)
+ }
+ bc.mu.Unlock()
+
+ bc.wg.Done()
+ }(&bc)
+ return bc
+}
+
+func (bc *Broadcast[T]) AddDestination() <-chan T {
+ bc.mu.Lock()
+ defer bc.mu.Unlock()
+
+ ch := make(chan T)
+ bc.destinations = append(bc.destinations, ch)
+ return ch
+}
+
+// Wait for the Broadcast to see that source is closed and to close the
+// destinations.
+func (bc *Broadcast[T]) Wait() {
+ bc.wg.Wait()
+}
diff --git a/gui/widget/widget.go b/gui/widget/widget.go
index 82f67c6..61ba760 100644
--- a/gui/widget/widget.go
+++ b/gui/widget/widget.go
@@ -43,6 +43,7 @@ func Label(text string, r image.Rectangle, env gui.Env, wg *sync.WaitGroup) {
func Input(val chan<- uint, r image.Rectangle, focusChan <-chan bool, env gui.Env, wg *sync.WaitGroup) {
defer wg.Done()
defer close(env.Draw())
+ defer close(val)
redraw := func(text []byte, focus bool) func(draw.Image) image.Rectangle {
return func(drw draw.Image) image.Rectangle {
diff --git a/main.go b/main.go
index 6492533..7327ebd 100644
--- a/main.go
+++ b/main.go
@@ -27,13 +27,17 @@ const (
)
func run() {
- var (
- wg = new(sync.WaitGroup)
+ wg := new(sync.WaitGroup)
+ defer wg.Wait()
- focus = NewFocus([]int{1, POINTS, POINTS, POINTS, POINTS})
+ focus := NewFocus([]int{1, POINTS, POINTS, POINTS, POINTS})
+ defer focus.Close()
- displacementChan = make(chan uint)
+ displacementChan := make(chan uint)
+ displacementBroadcast := NewBroadcast(displacementChan)
+ defer displacementBroadcast.Wait()
+ var (
rpmChan [POINTS]chan uint
veChan [POINTS]chan uint
imapChan [POINTS]chan uint
@@ -41,22 +45,8 @@ func run() {
flowChan [POINTS]chan float64
)
- defer wg.Wait()
- defer focus.Close()
- defer close(displacementChan)
- for i := 0; i < POINTS; i++ {
- rpmChan[i] = make(chan uint)
- veChan[i] = make(chan uint)
- imapChan[i] = make(chan uint)
- actChan[i] = make(chan uint)
- defer close(rpmChan[i])
- defer close(veChan[i])
- defer close(imapChan[i])
- defer close(actChan[i])
-
- flowChan[i] = make(chan float64)
- defer close(flowChan[i])
- }
+ makeChans(rpmChan[:], veChan[:], imapChan[:], actChan[:])
+ makeChans(flowChan[:])
w, err := win.New(win.Title("volute"), win.Size(WIDTH, HEIGHT))
if err != nil {
@@ -66,6 +56,34 @@ func run() {
mux, env := gui.NewMux(w)
defer close(env.Draw())
+ spawnWidgets(
+ displacementChan,
+ rpmChan, veChan, imapChan, actChan,
+ flowChan,
+ &focus, mux, wg,
+ )
+
+ // TODO: make these output properly on screen.
+ for i := 0; i < POINTS; i++ {
+ wg.Add(1)
+ go calculateFlow(
+ flowChan[i],
+ displacementBroadcast.AddDestination(),
+ rpmChan[i], veChan[i], actChan[i], imapChan[i],
+ wg,
+ )
+ }
+
+ focus.Focus(true)
+ eventLoop(env, &focus)
+}
+
+func spawnWidgets(
+ displacementChan chan uint,
+ rpmChan, veChan, imapChan, actChan [POINTS]chan uint,
+ flowChan [POINTS]chan float64,
+ focus *Focus, mux *gui.Mux, wg *sync.WaitGroup,
+) {
bounds := layout.Grid{
Rows: []int{2, 7, 7, 7, 7, 7},
Background: color.Gray{255},
@@ -139,39 +157,36 @@ func run() {
wg,
)
}
+}
- focus.widgets[focus.p.Y][focus.p.X] <- true
-
-Loop:
- for {
- select {
- case _ = <-displacementChan:
- case _ = <-rpmChan[0]:
- case _ = <-veChan[0]:
- case event, ok := <-env.Events():
- if !ok { // channel closed
- break Loop
- }
- switch event := event.(type) {
- case win.WiClose:
- break Loop
- case win.KbType:
- switch event.Rune {
- case 'q':
- break Loop
- case 'h':
- focus.Left()
- case 'j':
- focus.Down()
- case 'k':
- focus.Up()
- case 'l':
- focus.Right()
- }
+func eventLoop(env gui.Env, focus *Focus) {
+ for event := range env.Events() {
+ switch event := event.(type) {
+ case win.WiClose:
+ return
+ case win.KbType:
+ switch event.Rune {
+ case 'q':
+ return
+ case 'h':
+ focus.Left()
+ case 'j':
+ focus.Down()
+ case 'k':
+ focus.Up()
+ case 'l':
+ focus.Right()
}
}
}
- fmt.Println("Shutting down...")
+}
+
+func makeChans[T any](chanss ...[]chan T) {
+ for i := range chanss {
+ for j := range chanss[i] {
+ chanss[i][j] = make(chan T)
+ }
+ }
}
func split(elements int, space int) []int {
@@ -197,6 +212,49 @@ func splitRows(elements int, space int) []int {
return bounds
}
+func calculateFlow(
+ flow chan<- float64,
+ displacementChan, rpmChan, veChan, actChan, imapChan <-chan uint,
+ wg *sync.WaitGroup,
+) {
+ defer wg.Done()
+ defer close(flow)
+
+ var (
+ displacement Volume
+ rpm uint
+ ve uint
+ act Temperature
+ imap Pressure
+
+ v uint
+ ok bool
+ )
+
+ for {
+ select {
+ case v, ok = <-displacementChan:
+ displacement = Volume(v) * CubicCentimetre
+ case rpm, ok = <-rpmChan:
+ case ve, ok = <-veChan:
+ case v, ok = <-actChan:
+ act = Temperature{float64(v), Celcius}
+ case v, ok = <-imapChan:
+ imap = Pressure(v) * Millibar
+ }
+ if !ok {
+ return
+ }
+ flow <- massFlow(displacement, rpm, ve, act, imap)
+ }
+}
+
+func massFlow(displacement Volume, rpm, ve uint, act Temperature, imap Pressure) float64 {
+ density := (M / R) * float64(imap/Pascal) / act.AsUnit(Kelvin) // kg/m3
+ volumeFlow := float64(displacement/CubicMetre) * float64(rpm/2) * (float64(ve) / 100.0) // m3/min
+ return density * volumeFlow
+}
+
func main() {
mainthread.Run(run)
}