-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
216 lines (195 loc) · 6.2 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
package main
/* ===========
What started out to be a project for simple patio lighting with a set of relays that are driven by the clock likke a cron job has evolved into a prototype project to control the water pump of the aquaponics system. Relays are now thrown to drive the pump for scheduled intervals like pulse
author : [email protected]
date : 21-2-2024
place : Pune
=============== */
import (
"context"
"encoding/json"
"fmt"
"io"
"os"
"strconv"
"sync"
"time"
"github.com/eensymachines-in/patio/aquacfg"
"github.com/eensymachines-in/patio/digital"
"github.com/eensymachines-in/patio/interrupt"
"github.com/eensymachines-in/patio/tickers"
oled "github.com/eensymachines-in/ssd1306"
log "github.com/sirupsen/logrus"
_ "gobot.io/x/gobot"
"gobot.io/x/gobot/platforms/raspi"
)
var (
config = aquacfg.AppConfig{}
)
func init() {
// required environment variables
/*
PATH_APPCONFIG
NAME_SYSCTLSERVICE
MODE_DEBUGLVL
AMQP_LOGIN
AMQP_SERVER
AMQP_CFGCHNNL
GPIO_TOUCH
GPIO_ERRLED
GPIO_PUMP_MAIN
*/
for _, v := range []string{
"PATH_APPCONFIG",
"NAME_SYSCTLSERVICE",
"MODE_DEBUGLVL",
"AMQP_LOGIN",
"AMQP_SERVER",
"AMQP_CFGCHNNL",
"GPIO_TOUCH",
"GPIO_ERRLED",
"GPIO_PUMP_MAIN",
} {
if val := os.Getenv(v); val == "" {
log.Panicf("Required environment variable missing in ~/.bashrc: %s", v)
}
}
// Setting up the loggin framework
log.SetFormatter(&log.TextFormatter{DisableColors: false, FullTimestamp: false})
log.SetReportCaller(false)
log.SetOutput(os.Stdout)
lvl, err := strconv.Atoi(os.Getenv("MODE_DEBUGLVL"))
if err != nil {
log.Warnf("invalid env var value for logging level, only integers %s", os.Getenv("MODE_DEBUGLVL"))
log.SetLevel(log.DebugLevel)
} else {
log.SetLevel(log.Level(lvl)) // sets from the environment
}
// TODO: read configuration file and hoist all the vars
f, err := os.Open(os.Getenv("PATH_APPCONFIG"))
if err != nil || f == nil {
log.Panicf("failed to access application configuration file %s", err)
return
}
byt, err := io.ReadAll(f)
if err != nil {
log.Panicf("failed to read config.json %s", err)
return
}
if err := json.Unmarshal(byt, &config); err != nil {
log.Panicf("failed to unmarshal config.json %s", err)
return
}
log.WithFields(log.Fields{
"name": config.AppName,
"sched": config.Schedule.Config,
"tick": config.Schedule.TickAt,
"pulsegap": config.Schedule.PulseGap,
}).Debug("read in app config")
}
// this main loop would only setup the tickers
func main() {
log.WithFields(log.Fields{
"time": time.Now().Format(time.RFC822),
}).Debugf("Starting %s", config.AppName)
defer log.Warn("Closing the application now..")
// Contexts and waitgroups
ctx, cancel := context.WithCancel(context.Background())
var wg sync.WaitGroup
// initialized hardware drivers
r := raspi.NewAdaptor()
r.Connect()
wg.Add(1)
go func() {
defer wg.Done()
for intr := range interrupt.TouchOrSysSignal(os.Getenv("GPIO_TOUCH"), digital.SLOW_WATCH_3_3V, r, ctx, &wg) {
log.WithFields(log.Fields{
"time": intr.Format(time.RFC822),
}).Warn("Interrupted...")
cancel() // time for all the program to go down
}
}()
wg.Add(1)
go func() {
// display thread
defer wg.Done()
disp := oled.NewSundingOLED("oled", r)
flush_display := func() { // helps clear the display for prep and shutdown
log.Debug("Flushing display..")
disp.Clean()
disp.ResetImage().Render()
}
flush_display()
defer flush_display()
disp_date := func() string { // helps format current date as string
now := time.Now()
_, mn, dd := now.Date()
hr, min, _ := now.Clock()
return fmt.Sprintf("%s-%02d %02d:%02d", mn.String()[:3], dd, hr, min)
}
disp.Message(10, 10, disp_date()).Render()
for {
select {
case <-ctx.Done():
return
case <-time.After(1 * time.Minute):
disp.Clean()
disp.Message(10, 10, disp_date()).Render()
}
}
}()
wg.Add(1)
go func() {
defer wg.Done()
rs := digital.NewRelaySwitch(os.Getenv("GPIO_PUMP_MAIN"), false, r).Boot()
// TODO: This comes from configuration
var ticks chan time.Time
if config.Schedule.Config == aquacfg.PULSE_EVERY_DAYAT {
/*At specfic times every day this will send a pulse of triggers for the pulse width as set
Intervals are irrelevant here since the cycle is always for 24 hours */
pw := time.Duration(config.Schedule.PulseGap) * time.Second
log.WithFields(log.Fields{
"pulse gap": pw,
"ticking time": config.Schedule.TickAt,
}).Debug("Schedule mode: Pulse everyday at")
ticks, _ = tickers.PulseEveryDayAt(config.Schedule.TickAt, pw, ctx, &wg)
} else if config.Schedule.Config == aquacfg.TICK_EVERY_DAYAT {
/*At specfic times every day this will send tick triggers
Intervals are irrelevant here since the cycle is always for 24 hours */
log.WithFields(log.Fields{
"ticking time": config.Schedule.TickAt,
}).Debug("Schedule mode: Tick every day at")
ticks, _ = tickers.TickEveryDayAt(config.Schedule.TickAt, ctx, &wg)
} else if config.Schedule.Config == aquacfg.PULSE_EVERY {
/*For the given interval this can send pulse triggers for given pulse width
Clock times are irrelevant here since intervals define when to send the pulse*/
intrvl := time.Duration(config.Schedule.Interval) * time.Second
pw := time.Duration(config.Schedule.PulseGap) * time.Second
log.WithFields(log.Fields{
"pulse gap": pw,
"interval": intrvl,
}).Debug("Schedule mode: Pulse every interval")
ticks = tickers.PulseEvery(intrvl, pw, ctx, &wg)
} else if config.Schedule.Config == aquacfg.TICK_EVERY {
/*For the given interval this can send tick triggers
Clock times are irrelevant here since intervals define when to send the pulse*/
intrvl := time.Duration(config.Schedule.Interval) * time.Second
log.WithFields(log.Fields{
"interval": intrvl,
}).Debug("Schedule mode: Tick every interval")
ticks = tickers.TickEvery(intrvl, ctx, &wg)
} else { // no suitable schedule configuration
log.Errorf("Invalid schedule configuration: %d", config.Schedule.Config)
cancel()
return
}
for t := range ticks {
log.Debugf("Flipping the relay state: %s", t.Format(time.RFC822))
rs.Toggle()
}
rs.Low()
log.Warn("Now shutting down relay..")
}()
// Flushing the hardware states
wg.Wait()
}