diff --git a/commands/co2mon.go b/commands/co2mon.go index 6ec79b7..7b09a59 100644 --- a/commands/co2mon.go +++ b/commands/co2mon.go @@ -10,19 +10,15 @@ import ( "github.com/spf13/cobra" "github.com/gaiaz-iusipov/rpi-violet/internal/monitor" - "github.com/gaiaz-iusipov/rpi-violet/internal/monitor/co2mon" ) var co2monCmd = &cobra.Command{ Use: "co2mon", RunE: func(cmd *cobra.Command, args []string) error { - dev, err := co2mon.Open(co2mon.WithRandomKey()) + mon, err := monitor.New() if err != nil { - return fmt.Errorf("co2mon.Open(): %w", err) + return fmt.Errorf("monitor.New(): %w", err) } - defer dev.Close() - - mon := monitor.New(dev) defer mon.Close() done := make(chan struct{}) diff --git a/commands/run.go b/commands/run.go index d009359..ea7ea85 100644 --- a/commands/run.go +++ b/commands/run.go @@ -81,7 +81,10 @@ func runE(_ *cobra.Command, _ []string) error { } defer co2monDev.Close() - mon := monitor.New(co2monDev) + mon, err := monitor.New(monitor.WithDevOptions(co2mon.WithRandomKey())) + if err != nil { + return fmt.Errorf("monitor.New(): %w", err) + } defer mon.Close() c, err := cron.New(cfg.Cron, location, pin, rs, tg, mon) diff --git a/internal/monitor/monitor.go b/internal/monitor/monitor.go index 1280ec9..3593807 100644 --- a/internal/monitor/monitor.go +++ b/internal/monitor/monitor.go @@ -1,6 +1,7 @@ package monitor import ( + "fmt" "log" "strings" "time" @@ -10,30 +11,36 @@ import ( // Monitor is a USB CO2 monitor type Monitor struct { - dev *co2mon.Device - readDelay time.Duration - co2 *co2 - temp *temp - done chan struct{} + opts *options + co2 *co2 + temp *temp + done chan struct{} + readErrCount int + dev *co2mon.Device } -func New(dev *co2mon.Device, setters ...OptionSetter) *Monitor { +func New(setters ...OptionSetter) (*Monitor, error) { opts := newOptions(setters) monitor := &Monitor{ - dev: dev, - readDelay: opts.readDelay, - temp: &temp{ttl: opts.co2TTL}, - co2: &co2{ttl: opts.co2TTL}, - done: make(chan struct{}), + opts: opts, + temp: &temp{ttl: opts.co2TTL}, + co2: &co2{ttl: opts.co2TTL}, + done: make(chan struct{}), + } + + if err := monitor.initDev(); err != nil { + return nil, err } go monitor.run() - return monitor + + return monitor, nil } func (m *Monitor) Close() error { close(m.done) + _ = m.dev.Close() return nil } @@ -54,8 +61,17 @@ func (m *Monitor) Measurements() (string, bool) { return strings.Join(parts, ", "), true } +func (m *Monitor) initDev() error { + dev, err := co2mon.Open(m.opts.devOpts...) + if err != nil { + return fmt.Errorf("co2mon.Open(): %w", err) + } + m.dev = dev + return nil +} + func (m *Monitor) run() { - ticker := time.NewTicker(m.readDelay) + ticker := time.NewTicker(m.opts.readDelay) defer ticker.Stop() for { @@ -66,6 +82,22 @@ func (m *Monitor) run() { pack, err := m.dev.ReadPacket() if err != nil { log.Println(err) + m.readErrCount++ + + if m.readErrCount > m.opts.readErrThreshold { + err = m.dev.Close() + if err != nil { + log.Println(err) + } + + err = m.initDev() + if err != nil { + log.Println(err) + } else { + m.readErrCount = 0 + } + } + continue } diff --git a/internal/monitor/options.go b/internal/monitor/options.go index 50b43ba..c4b78b0 100644 --- a/internal/monitor/options.go +++ b/internal/monitor/options.go @@ -2,18 +2,23 @@ package monitor import ( "time" + + "github.com/gaiaz-iusipov/rpi-violet/internal/monitor/co2mon" ) type options struct { - readDelay time.Duration - co2TTL, tempTTL time.Duration + readDelay time.Duration + co2TTL, tempTTL time.Duration + readErrThreshold int + devOpts []co2mon.OptionSetter } func newOptions(setters []OptionSetter) *options { opts := &options{ - readDelay: time.Second, - co2TTL: 10 * time.Second, - tempTTL: 10 * time.Second, + readDelay: time.Second, + co2TTL: 10 * time.Second, + tempTTL: 10 * time.Second, + readErrThreshold: 5, } for _, setter := range setters { setter(opts) @@ -23,6 +28,12 @@ func newOptions(setters []OptionSetter) *options { type OptionSetter func(opts *options) +func WithDevOptions(setters ...co2mon.OptionSetter) OptionSetter { + return func(opts *options) { + opts.devOpts = setters + } +} + func WithReadDelay(readDelay time.Duration) OptionSetter { return func(opts *options) { opts.readDelay = readDelay diff --git a/internal/monitor/temp.go b/internal/monitor/temp.go index 9fd1f8f..cebd7aa 100644 --- a/internal/monitor/temp.go +++ b/internal/monitor/temp.go @@ -46,7 +46,7 @@ func (t *temp) getCelsius() (float64, bool) { func (t *temp) String() string { if val, ok := t.getCelsius(); ok { - return fmt.Sprintf("%.4f °C", val) + return fmt.Sprintf("%.1f °C", val) } return "" }