From 25fb0d86fc57d4d120b3542f53aa441773732484 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Thu, 19 Jan 2023 14:29:22 -0500 Subject: [PATCH] Buggy impl --- internal/notes/weekly/Jan-16-20.md | 3 + internal/pkg/controller/manager.go | 34 ---- internal/pkg/device/atlas.go | 85 ++++++++++ internal/pkg/device/controller.go | 22 +++ internal/pkg/device/do.go | 41 +++++ internal/pkg/device/manager.go | 149 ++++-------------- internal/pkg/device/mappings.go | 35 ++-- internal/pkg/device/ph.go | 40 +++++ internal/pkg/{controller => device}/pwm.go | 13 +- internal/pkg/device/rtd.go | 38 +++++ internal/pkg/device/sensor.go | 88 +++++++++++ internal/pkg/{I2C => i2c}/bus.go | 77 ++------- internal/pkg/reactor/coordinator.go | 32 ++-- .../coordinator.go => reactor/device.go} | 67 ++++---- internal/pkg/sensor/atlas.go | 67 -------- internal/pkg/sensor/do_sensor.go | 43 ----- internal/pkg/sensor/manager.go | 75 --------- internal/pkg/sensor/ph_sensor.go | 42 ----- internal/pkg/sensor/rtd_sensor.go | 41 ----- 19 files changed, 430 insertions(+), 562 deletions(-) delete mode 100644 internal/pkg/controller/manager.go create mode 100644 internal/pkg/device/atlas.go create mode 100644 internal/pkg/device/controller.go create mode 100644 internal/pkg/device/do.go create mode 100644 internal/pkg/device/ph.go rename internal/pkg/{controller => device}/pwm.go (67%) create mode 100644 internal/pkg/device/rtd.go create mode 100644 internal/pkg/device/sensor.go rename internal/pkg/{I2C => i2c}/bus.go (56%) rename internal/pkg/{device/coordinator.go => reactor/device.go} (58%) delete mode 100644 internal/pkg/sensor/atlas.go delete mode 100644 internal/pkg/sensor/do_sensor.go delete mode 100644 internal/pkg/sensor/manager.go delete mode 100644 internal/pkg/sensor/ph_sensor.go delete mode 100644 internal/pkg/sensor/rtd_sensor.go diff --git a/internal/notes/weekly/Jan-16-20.md b/internal/notes/weekly/Jan-16-20.md index 7774293..35d2fd9 100644 --- a/internal/notes/weekly/Jan-16-20.md +++ b/internal/notes/weekly/Jan-16-20.md @@ -141,3 +141,6 @@ This just feels *wrong* - maybe go back to the unified package? Not quite clear what the purpose of seperate is beyond convience - although... the idea of the device manager as a reusable peice makes enough sense to potentially keep it as a seperate package - I'll stick with the seperate for now and keep it unless it becomes unworkable + +### I2C Changes +The i2c bus is locked at the device level, so I am going to rewrite the bs to just use a function with no struct and remove the whole passing of structs garbage diff --git a/internal/pkg/controller/manager.go b/internal/pkg/controller/manager.go deleted file mode 100644 index fffd651..0000000 --- a/internal/pkg/controller/manager.go +++ /dev/null @@ -1,34 +0,0 @@ -package controller - -import ( - "FRMS/internal/pkg/manager" - "sync" -) - -// base controller manager - -type Manager interface { - Start() error - Exit() error - IsActive() int -} - -func NewManager(max int) Manager { - return manager.New(max) -} - -type ControllerManager struct { - Manager - - sync.Mutex - Enabled bool // turn controller on or off -} - -func NewControllerManager() *ControllerManager { - m := NewManager(0) // no connections - return &ControllerManager{Manager: m} -} - -func (c *ControllerManager) Start(addr int) error { - return c.Manager.Start() -} diff --git a/internal/pkg/device/atlas.go b/internal/pkg/device/atlas.go new file mode 100644 index 0000000..1900d6e --- /dev/null +++ b/internal/pkg/device/atlas.go @@ -0,0 +1,85 @@ +package device + +import ( + "FRMS/internal/pkg/i2c" + "encoding/hex" + "errors" + "strconv" + "strings" + "time" +) + +// atlas helpers to fulfill sensor manager functions + +type Atlas struct { + // delays passed by caller + CalDelay int + ReadDelay int +} + +func (a *Atlas) Calibrate(bus, addr int, cal string) error { + // calibrate sensor + if a.CalDelay == 0 { + return errors.New("Cal delay unset, please check config") + } + if _, err := i2c.SendCmd(bus, addr, cal); err != nil { + return err + } + + time.Sleep(time.Duration(a.CalDelay) * time.Millisecond) // sleep + + _, err := i2c.SendCmd(bus, addr, "") // read for success + // return the err if there is any + return err +} + +var ErrReadFail = errors.New("atlas read failure") + +func (a *Atlas) TakeReading(bus, addr int) (float64, error) { + // take reading function + if _, err := i2c.SendCmd(bus, addr, "R"); err != nil { + // read command + return 0, err + } + if a.ReadDelay == 0 { + return 0, errors.New("Read Delay unset, please check config") + } + sleep := time.Duration(a.ReadDelay) * time.Millisecond + time.Sleep(sleep) // sleep between reads + data, err := i2c.SendCmd(bus, addr, "") + if err != nil { + return 0, ErrReadFail + } + + // fmt data from 0x... to proper + var final string + split := strings.Split(data, " ") + for i, v := range split { + // loop over chars + if i == 0 && v != "0x01" { + // reading failed + return 0, ErrReadFail + } + // trimming bs + trimmed := strings.TrimLeft(v, "0x ") + trimmed = strings.TrimRight(trimmed, " \n") + if trimmed != "ff" && i != 0 { + final += trimmed + } + } + // return as a float + var b []byte + if b, err = hex.DecodeString(final); err != nil { + return 0, err + } + return strconv.ParseFloat(string(b), 32) +} + +// for config +func (a *Atlas) GetCalDelay() int { + return a.CalDelay +} + +func (a *Atlas) GetReadDelay() int { + return a.ReadDelay +} diff --git a/internal/pkg/device/controller.go b/internal/pkg/device/controller.go new file mode 100644 index 0000000..ca03bf1 --- /dev/null +++ b/internal/pkg/device/controller.go @@ -0,0 +1,22 @@ +package device + +import ( + "sync" +) + +// base controller manager + +type ControllerManager struct { + *DeviceManager + + sync.Mutex + Enabled bool // turn controller on or off +} + +func NewControllerManager() *ControllerManager { + return &ControllerManager{} +} + +func (c *ControllerManager) SetDeviceManager(d *DeviceManager) { + c.DeviceManager = d +} diff --git a/internal/pkg/device/do.go b/internal/pkg/device/do.go new file mode 100644 index 0000000..ade64e8 --- /dev/null +++ b/internal/pkg/device/do.go @@ -0,0 +1,41 @@ +package device + +// do sensor and methods + +import ( + "sync" +) + +type DOManager struct { + // do sensor manager + *SensorManager + *Atlas + + sync.RWMutex +} + +func NewDOManager() *DOManager { + // atlas delays + a := &Atlas{ + CalDelay: 1300, + ReadDelay: 600, + } + + sm := NewSensorManager() + + m := &DOManager{ + Atlas: a, + SensorManager: sm, + } + return m +} + +func (m *DOManager) Start() error { + // start sensor manager + return m.SensorManager.Start(m.Atlas.TakeReading) +} + +func (m *DOManager) String() string { + // TODO + return "" +} diff --git a/internal/pkg/device/manager.go b/internal/pkg/device/manager.go index 6acd6fd..c288ad7 100644 --- a/internal/pkg/device/manager.go +++ b/internal/pkg/device/manager.go @@ -1,150 +1,69 @@ package device import ( + "FRMS/internal/pkg/manager" "fmt" "sync" + "time" "github.com/spf13/viper" ) -type SubManager interface { - Start(int) error +// base device manager + +type Manager interface { + // core manager + Start() error Exit() error IsActive() int - String() string // printing info about the sub manager - LoadConfig(*viper.Viper, string) - - // for config bs - GetDefaultName() string + HeartBeat(chan struct{}, int, int, time.Duration) } -type NameChan struct { - Ch chan string +func NewManager() Manager { + // no timeouts needed + return manager.New(0) } -// base device manager - type DeviceManager struct { - SubManager - - // across controllers/sensors - Address int `mapstructure:"address"` - Name string `mapstructure:"name"` - - infoMu sync.RWMutex - - Config *viper.Viper - ConfigPrefix string + // for device agnostic fields/methods + Address int `mapstructure:"address"` + Bus int // i2c bus + // mutable + infoMu sync.RWMutex + Name string `mapstructure:"name"` + defaultName string + // base manager + Manager + // config + Config *viper.Viper } -func NewDeviceManager(addr int, config *viper.Viper, configPrefix string, i2c I2CClient) (*DeviceManager, error) { - - // validate prefix - s, err := NewSubManager(addr, i2c) - +func NewDeviceManager(bus, addr int, config *viper.Viper, defaultName string) *DeviceManager { + // new base dm + m := NewManager() dm := &DeviceManager{ - SubManager: s, - Config: config, - Address: addr, - ConfigPrefix: configPrefix, + Address: addr, + Bus: bus, + defaultName: defaultName, + Manager: m, + Config: config, } - return dm, err + return dm } func (m *DeviceManager) LoadConfig() error { // setting default name - mainKey := fmt.Sprintf("%sdevices.%d", m.ConfigPrefix, m.Address) + mainKey := fmt.Sprintf("devices.%d", m.Address) nameKey := fmt.Sprintf("%s.name", mainKey) if !m.Config.IsSet(nameKey) { - m.Config.Set(nameKey, m.SubManager.GetDefaultName()) + m.Config.Set(nameKey, m.defaultName) } - - m.Config.UnmarshalKey(mainKey, m) - m.SubManager.LoadConfig(m.Config, mainKey) return nil } func (m *DeviceManager) Start() error { - // load config and then start - var err error - - // load config - if err = m.LoadConfig(); err != nil { - return err - } - // start - if err = m.SubManager.Start(m.Address); err != nil { - return err - } - - return err + return m.Manager.Start() } - -// dev info grpc handlers -// func (m *DeviceManager) Name(stream pb.DeviceInfo_StatusServer) error { -// // this might be scuffed - -// } - -// I think I can use grpc streams to do better -// func (m *DeviceManger) GetName(ctx context.Context, req *pb.DeviceName) (*pb.DeviceName, error) { -// // gets name - -// var name string -// var err error - -// // quick lock -// m.infoMu.RLock() -// name = m.Name -// m.infoMu.RUnlock() - -// if name == "" { -// name = m.GetDefaultName() -// err = m.UpdateName(name) -// } - -// response := &pb.DeviceName{Address: int32(m.Address), Name: name} - -// return response, err -// } - -// func (m *DeviceManger) SetName(ctx context.Context, req *pb.DeviceName) (*pb.DeviceName, error) { -// // handles incoming req - -// response := &pb.DeviceName{Address: int32(m.Address)} -// err := m.UpdateName() -// return response, err -// } - -// func (m *DeviceManager) UpdateName(name string) error { -// // updates name/config -// m.infoMu.Lock() -// defer m.infoMu.Unlock() - -// nameKey := fmt.Sprintf("%s.devices.%d.name", m.ConfigPrefix, m.Address) -// m.Config.SetKey(nameKey, name) - -// return nil -// } - -// func (m *DeviceManger) GetStatus(ctx context.Context, req *pb.DeviceStatus) (*pb.DeviceStatus, error) { - -// } - -// func (m *DeviceManger) SetStatus(ctx context.Context, req *pb.DeviceStatus) (*pb.DeviceStatus, error) { - -// } - -// monitoring grpc -// func (m *DeviceManager) GetDevice() (*pb.Device, error) { -// // turns info into pb.Device for monitoring grpc - -// } - -// func (m *DeviceManger) UpdateDevice(*pb.Device) error { -// // unpacks pb.Device to update itself - -// } diff --git a/internal/pkg/device/mappings.go b/internal/pkg/device/mappings.go index 8553505..6374e31 100644 --- a/internal/pkg/device/mappings.go +++ b/internal/pkg/device/mappings.go @@ -1,35 +1,48 @@ package device import ( - "FRMS/internal/pkg/controller" - "FRMS/internal/pkg/sensor" "errors" "fmt" + + "github.com/spf13/viper" ) -/* - Returns the correct manager for sensor/controller -*/ +// Returns the correct manager for sensor/controller +type Device interface { + Start() error + Exit() error + IsActive() int + SetDeviceManager(*DeviceManager) +} -func NewSubManager(addr int, i2c I2CClient) (SubManager, error) { +func New(bus, addr int, config *viper.Viper) (Device, error) { // returns correct device manager by ID - var m SubManager var err error + var defaultName string + var m Device + switch addr { case 97: // DO - m = sensor.NewDOManager(i2c) + defaultName = "DO Sensor" + m = NewDOManager() case 99: // pH - m = sensor.NewPHManager(i2c) + defaultName = "pH Sensor" + m = NewPHManager() case 102: // RTD - m = sensor.NewRTDManager(i2c) + defaultName = "RTD Sensor" + m = NewRTDManager() case 256: // PWM - m = controller.NewPWMManager() + defaultName = "PWM Controller" + m = NewPWMManager() default: err = errors.New(fmt.Sprintf("Error: device id %d unrecognized!", addr)) } + // setting device manager + dm := NewDeviceManager(bus, addr, config, defaultName) + m.SetDeviceManager(dm) return m, err } diff --git a/internal/pkg/device/ph.go b/internal/pkg/device/ph.go new file mode 100644 index 0000000..f6d8dde --- /dev/null +++ b/internal/pkg/device/ph.go @@ -0,0 +1,40 @@ +package device + +// do sensor and methods + +import ( + "sync" +) + +type PHManager struct { + // do sensor manager + *SensorManager + *Atlas + + sync.RWMutex +} + +func NewPHManager() *PHManager { + // atlas delays + a := &Atlas{ + CalDelay: 900, + ReadDelay: 900, + } + + sm := NewSensorManager() + m := &PHManager{ + Atlas: a, + SensorManager: sm, + } + return m +} + +func (m *PHManager) Start() error { + // start sensor manager + return m.SensorManager.Start(m.Atlas.TakeReading) +} + +func (m PHManager) String() string { + // TODO + return "" +} diff --git a/internal/pkg/controller/pwm.go b/internal/pkg/device/pwm.go similarity index 67% rename from internal/pkg/controller/pwm.go rename to internal/pkg/device/pwm.go index 90805ca..130c17e 100644 --- a/internal/pkg/controller/pwm.go +++ b/internal/pkg/device/pwm.go @@ -1,12 +1,9 @@ -package controller +package device // do sensor and methods import ( - "fmt" "sync" - - "github.com/spf13/viper" ) type PWMManager struct { @@ -29,14 +26,6 @@ func (m *PWMManager) GetFrequency() (int, error) { return m.Frequency, nil } -func (m *PWMManager) LoadConfig(config *viper.Viper, key string) { - fmt.Printf("config\n") -} - -func (m *PWMManager) GetDefaultName() string { - return "pwm controller" -} - func (m *PWMManager) String() string { // TODO return "" diff --git a/internal/pkg/device/rtd.go b/internal/pkg/device/rtd.go new file mode 100644 index 0000000..07ef3a4 --- /dev/null +++ b/internal/pkg/device/rtd.go @@ -0,0 +1,38 @@ +package device + +// do sensor and methods + +import ( + "sync" +) + +type RTDManager struct { + // do sensor manager + *Atlas + *SensorManager + sync.RWMutex +} + +func NewRTDManager() *RTDManager { + // atlas delays + a := &Atlas{ + CalDelay: 600, + ReadDelay: 600, + } + + sm := NewSensorManager() + m := &RTDManager{ + Atlas: a, + SensorManager: sm, + } + return m +} + +func (m *RTDManager) Start() error { + return m.SensorManager.Start(m.Atlas.TakeReading) +} + +func (m *RTDManager) String() string { + // TODO + return "" +} diff --git a/internal/pkg/device/sensor.go b/internal/pkg/device/sensor.go new file mode 100644 index 0000000..dc2f5a2 --- /dev/null +++ b/internal/pkg/device/sensor.go @@ -0,0 +1,88 @@ +package device + +import ( + "errors" + "fmt" + "sync" + "time" +) + +type SensorManager struct { + SampleRate int `mapstructure:"sample_rate"` // in (ms) + + // sampling + sampleMu sync.RWMutex + LatestSample float32 + + *DeviceManager `mapstructure:",squash"` +} + +func NewSensorManager() *SensorManager { + s := &SensorManager{} + return s +} + +func (s *SensorManager) SetDeviceManager(d *DeviceManager) { + s.DeviceManager = d +} + +type takeReading func(int, int) (float64, error) + +func (s *SensorManager) Start(f takeReading) error { + + // loading config + if err := s.LoadConfig(); err != nil { + return err + } + + // starting + if err := s.DeviceManager.Start(); err != nil { + return err + } + + // starting monitoring + go s.Monitor(f) + return nil +} + +func (s *SensorManager) LoadConfig() error { + // setting keys + mainKey := fmt.Sprintf("devices.%d", s.Address) + sampleKey := fmt.Sprintf("%s.sample_rate", mainKey) + + if !s.Config.IsSet(sampleKey) { + // no sample rate, default to 10s + s.Config.Set(sampleKey, 10000) + } + + // loading lower + s.DeviceManager.LoadConfig() + + s.Config.UnmarshalKey(mainKey, s) + + return nil +} + +func (s *SensorManager) Monitor(f takeReading) { + ch := make(chan struct{}) // hb chan + go s.HeartBeat(ch, s.SampleRate, 2000, time.Millisecond) + + var reading float64 + var err error + for range ch { + if reading, err = f(s.Bus, s.Address); err != nil { + if !errors.Is(err, ErrReadFail) { + // unknown error, panic + panic(err) + } + fmt.Printf("Reading failed, skipping!\n") + } + // update sample + if !errors.Is(err, ErrReadFail) { + fmt.Printf("Got %f\n", reading) + s.sampleMu.Lock() + s.LatestSample = float32(reading) + s.sampleMu.Unlock() + } + } +} diff --git a/internal/pkg/I2C/bus.go b/internal/pkg/i2c/bus.go similarity index 56% rename from internal/pkg/I2C/bus.go rename to internal/pkg/i2c/bus.go index 08d5ab4..12a55d4 100644 --- a/internal/pkg/I2C/bus.go +++ b/internal/pkg/i2c/bus.go @@ -1,52 +1,22 @@ -package I2C +package i2c // file has general wrappers to interact with i2c-tools import ( "FRMS/internal/pkg/logging" - "FRMS/internal/pkg/system" "bytes" - "encoding/hex" - "errors" "fmt" _ "log" "os/exec" "strconv" "strings" - "sync" - - "github.com/spf13/viper" ) -type I2CClient struct { - Bus int `mapstructure:"bus"` - sync.Mutex -} - -func NewClient(config *viper.Viper) (*I2CClient, error) { - var err error - var bus int - client := &I2CClient{} - if !config.IsSet("i2c.bus") { - // no bus - if bus, err = system.GetBus(); err != nil { - return client, err - } - config.Set("i2c.bus", bus) - } - err = config.UnmarshalKey("i2c", client) - return client, err -} - -func (b *I2CClient) GetConnected() (map[int]bool, error) { - /* - Returns all the connected devices by address - I can def improve this - */ - b.Lock() - defer b.Unlock() +func GetConnected(b int) (map[int]bool, error) { + // Returns all the connected devices by address + // might just do this in bash and make it easier + bus := strconv.Itoa(b) devices := make(map[int]bool) // only keys - bus := strconv.Itoa(b.Bus) cmd := exec.Command("i2cdetect", "-y", "-r", bus) var out bytes.Buffer var errs bytes.Buffer @@ -85,13 +55,11 @@ func (b *I2CClient) GetConnected() (map[int]bool, error) { return devices, nil } -func (b *I2CClient) SendCmd(addr int, command string) (string, error) { - - b.Lock() - defer b.Unlock() - // formatting parameters +func SendCmd(b, addr int, command string) (string, error) { + // sends an arbituary commnd over specified bus to int + // might make a base script for this too var cmd *exec.Cmd - bus := strconv.Itoa(b.Bus) + bus := strconv.Itoa(b) operation := "r20" // default read frmt_cmd := "" // empty cmd if command != "" { @@ -113,31 +81,8 @@ func (b *I2CClient) SendCmd(addr int, command string) (string, error) { cmd.Stderr = &errs cmd.Stdout = &out if err := cmd.Run(); err != nil { - logging.Debug(logging.DError, "I2C error getting data! %v", errs.String()) - fmt.Println(errs.String()) + logging.Debug(logging.DError, "I2C error getting data! %v", err) return "", err } - - outString := out.String() - if outString == "" { - return outString, nil - } - split := strings.Split(outString, " ") //getting chars 0x12 0x2f etc - var final string - for i, v := range split { - if i == 0 && v != "0x01" { - // atlas check - return "", errors.New(fmt.Sprintf("Command %s not recognized!", command)) - - } - trimmed := strings.TrimLeft(v, "0x ") // trimming extra bs in front of num - trimmed = strings.TrimRight(trimmed, " \n") // trimming back - if trimmed != "ff" && i != 0 { - // remove padding - final += trimmed - } - } - ret, err := hex.DecodeString(final) - // return - return string(ret), err + return out.String(), nil } diff --git a/internal/pkg/reactor/coordinator.go b/internal/pkg/reactor/coordinator.go index b07a5f7..ba50545 100644 --- a/internal/pkg/reactor/coordinator.go +++ b/internal/pkg/reactor/coordinator.go @@ -3,7 +3,6 @@ package reactor // file describes reactor level coordinator and associated implementation import ( - "FRMS/internal/pkg/device" pb "FRMS/internal/pkg/grpc" "FRMS/internal/pkg/influxdb" "FRMS/internal/pkg/logging" @@ -41,27 +40,18 @@ func NewDBClient(config *viper.Viper) (DBClient, error) { return influxdb.NewDBClient(config) } -// device coordinator -type DeviceCoordinator interface { - Start() error - // in grpc format - GetDeviceInfo() ([]*pb.Device, error) -} - -func NewDeviceCoordinator(config *viper.Viper) DeviceCoordinator { - return device.NewCoordinator(config) -} - type Server struct { Ip string `mapstructure:"ip"` Port int `mapstructure:"port"` } -type Info struct { +type ReactorInfo struct { Name string `mapstructure:"name,omitempty"` ID int `mapstructure:"id,omitempty"` Model string `mapstructure:"model,omitempty"` HB int `mapstructure:"heartbeat"` + Bus int `mapstructure:"bus"` + Server } @@ -69,13 +59,13 @@ type ReactorCoordinator struct { Manager // base manager Config *viper.Viper // config - Info `mapstructure:",squash"` + ReactorInfo `mapstructure:",squash"` Database DBClient pb.MonitoringClient // grpc embedding - DeviceCoordinator // struct for locking + *DeviceCoordinator // struct for locking Err chan error } @@ -111,7 +101,7 @@ func (c *ReactorCoordinator) Start() { c.Err <- err } - if err = c.DeviceCoordinator.Start(); err != nil { + if err = c.DeviceCoordinator.Start(c.ReactorInfo.Bus); err != nil { c.Err <- err } @@ -154,6 +144,16 @@ func (c *ReactorCoordinator) LoadConfig() error { c.Config.Set("reactor.model", model) } + // check i2c bus + if !c.Config.IsSet("reactor.bus") { + // get from hw + var bus int + if bus, err = system.GetBus(); err != nil { + return err + } + c.Config.Set("reactor.bus", bus) + } + // all good, unmarhsaling c.Config.UnmarshalKey("reactor", c) diff --git a/internal/pkg/device/coordinator.go b/internal/pkg/reactor/device.go similarity index 58% rename from internal/pkg/device/coordinator.go rename to internal/pkg/reactor/device.go index 425abc9..c2afc6c 100644 --- a/internal/pkg/device/coordinator.go +++ b/internal/pkg/reactor/device.go @@ -1,9 +1,9 @@ -package device +package reactor import ( - "FRMS/internal/pkg/I2C" + "FRMS/internal/pkg/device" pb "FRMS/internal/pkg/grpc" - "FRMS/internal/pkg/manager" + "FRMS/internal/pkg/i2c" "fmt" "sync" "time" @@ -13,61 +13,48 @@ import ( // Created by rlc to manage devices -// basic manager to embed -type Manager interface { +// device manager +type DeviceManager interface { Start() error Exit() error - // create a heartbeat to send to chan at intervals - HeartBeat(chan struct{}, int, int, time.Duration) + IsActive() int } -func NewManager() Manager { - // dont need timeout functionality - return manager.New(0) -} - -// I2C client for locking -type I2CClient interface { - // i2c client w/ locking - GetConnected() (map[int]bool, error) // gets connected addresses - SendCmd(int, string) (string, error) -} - -func NewI2CClient(config *viper.Viper) (I2CClient, error) { - return I2C.NewClient(config) +func NewDeviceManager(bus, addr int, config *viper.Viper) (DeviceManager, error) { + return device.New(bus, addr, config) } // device coordinator itself type DeviceCoordinator struct { - I2C I2CClient + // base level manager for heartbeat + Bus int // i2c bus Manager Config *viper.Viper - managersMu sync.RWMutex - Managers map[int]*DeviceManager + managersMu sync.RWMutex + DeviceManagers map[int]DeviceManager } -func NewCoordinator(config *viper.Viper) *DeviceCoordinator { - dm := make(map[int]*DeviceManager) - m := NewManager() +func NewDeviceCoordinator(config *viper.Viper) *DeviceCoordinator { + dm := make(map[int]DeviceManager) + m := NewManager(0) c := &DeviceCoordinator{ - Manager: m, - Managers: dm, - Config: config, + Manager: m, + DeviceManagers: dm, + Config: config, } return c } -func (c *DeviceCoordinator) Start() error { +func (c *DeviceCoordinator) Start(bus int) error { var err error if err = c.Manager.Start(); err != nil { return err } + // i2c bus + c.Bus = bus - if c.I2C, err = NewI2CClient(c.Config); err != nil { - return err - } go c.Monitor() return err } @@ -79,7 +66,7 @@ func (c *DeviceCoordinator) Monitor() { for range ch { // on notification (10s) - devs, err := c.I2C.GetConnected() + devs, err := i2c.GetConnected(c.Bus) if err != nil { panic(err) } @@ -93,7 +80,7 @@ func (c *DeviceCoordinator) UpdateManagers(active map[int]bool) { c.managersMu.Lock() defer c.managersMu.Unlock() - for addr, dm := range c.Managers { + for addr, dm := range c.DeviceManagers { _, ok := active[addr] if ok && dm.IsActive() == 0 { @@ -101,7 +88,7 @@ func (c *DeviceCoordinator) UpdateManagers(active map[int]bool) { if err := dm.Start(); err != nil { panic(err) } - } else if dm.IsActive() == 1 { + } else if !ok && dm.IsActive() == 1 { // not active and dm is if err := dm.Exit(); err != nil { panic(err) @@ -115,7 +102,7 @@ func (c *DeviceCoordinator) UpdateManagers(active map[int]bool) { // no manager, create one fmt.Printf("New device %d!\n", addr) - dm, err := NewDeviceManager(addr, c.Config, "", c.I2C) + dm, err := NewDeviceManager(c.Bus, addr, c.Config) if err != nil { panic(err) } @@ -124,7 +111,7 @@ func (c *DeviceCoordinator) UpdateManagers(active map[int]bool) { panic(err) } - c.Managers[addr] = dm + c.DeviceManagers[addr] = dm } } @@ -135,7 +122,7 @@ func (c *DeviceCoordinator) GetDeviceInfo() ([]*pb.Device, error) { var devices []*pb.Device - for addr, dm := range c.Managers { + for addr, dm := range c.DeviceManagers { // looping over devices devices = append(devices, &pb.Device{ Addr: int32(addr), diff --git a/internal/pkg/sensor/atlas.go b/internal/pkg/sensor/atlas.go deleted file mode 100644 index 7dab58f..0000000 --- a/internal/pkg/sensor/atlas.go +++ /dev/null @@ -1,67 +0,0 @@ -package sensor - -import ( - "errors" - "strconv" - "time" -) - -// atlas helpers to aid with sensors - -type I2CClient interface { - SendCmd(int, string) (string, error) -} - -type Atlas struct { - // helper struct to embedd - I2C I2CClient - // delays unmarshalled - CalDelay int `mapstructure:"cal"` - ReadDelay int `mapstructure:"read"` -} - -func (a *Atlas) Calibrate(addr int, cal string) error { - // calibrate sensor - if a.CalDelay == 0 { - return errors.New("Cal delay unset, please check config") - } - if _, err := a.I2C.SendCmd(addr, cal); err != nil { - return err - } - - time.Sleep(time.Duration(a.CalDelay) * time.Millisecond) // sleep - - _, err := a.I2C.SendCmd(addr, "") // read for success - // return the err if there is any - return err -} - -func (a *Atlas) Read(addr int) (float32, error) { - // take reading function - if _, err := a.I2C.SendCmd(addr, "R"); err != nil { - // read command - //fmt.Printf("Error writing! %s", err) - return 0, err - } - if a.ReadDelay == 0 { - return 0, errors.New("Read Delay unset, please check config") - } - sleep := time.Duration(a.ReadDelay) * time.Millisecond - time.Sleep(sleep) // sleep between reads - data, err := a.I2C.SendCmd(addr, "") - if err != nil { - return 0, err - } - - f, err := strconv.ParseFloat(data, 32) - return float32(f), err -} - -// for config -func (a *Atlas) GetCalDelay() int { - return a.CalDelay -} - -func (a *Atlas) GetReadDelay() int { - return a.ReadDelay -} diff --git a/internal/pkg/sensor/do_sensor.go b/internal/pkg/sensor/do_sensor.go deleted file mode 100644 index 64b0489..0000000 --- a/internal/pkg/sensor/do_sensor.go +++ /dev/null @@ -1,43 +0,0 @@ -package sensor - -// do sensor and methods - -import ( - "fmt" - "sync" - - "github.com/spf13/viper" -) - -type DOManager struct { - // do sensor manager - *Atlas - *SensorManager `mapstructure:",squash"` - - sync.RWMutex -} - -func NewDOManager(i2c I2CClient) *DOManager { - a := &Atlas{I2C: i2c} - sm := NewSensorManager(a.Read) - - m := &DOManager{ - SensorManager: sm, - Atlas: a, - } - return m -} - -func (m *DOManager) LoadConfig(config *viper.Viper, key string) { - config.UnmarshalKey(key, m) - fmt.Printf("DO: %v\n", m.Atlas) -} - -func (m *DOManager) GetDefaultName() string { - return "DO Sensor" -} - -func (m *DOManager) String() string { - // TODO - return "" -} diff --git a/internal/pkg/sensor/manager.go b/internal/pkg/sensor/manager.go deleted file mode 100644 index 153771f..0000000 --- a/internal/pkg/sensor/manager.go +++ /dev/null @@ -1,75 +0,0 @@ -package sensor - -import ( - "FRMS/internal/pkg/manager" - "fmt" - "sync" - "time" -) - -type Manager interface { - Start() error - Exit() error - IsActive() int - HeartBeat(chan struct{}, int, int, time.Duration) -} - -func NewManager(max int) Manager { - return manager.New(max) -} - -type Reading struct { - sync.RWMutex - Latest float32 - New func(int) (float32, error) -} - -type SensorManager struct { - SampleRate int `mapstructure:"samplerate"` // in (ms) - Manager - - *Reading -} - -func NewSensorManager(f func(int) (float32, error)) *SensorManager { - m := NewManager(0) // no timeout - r := &Reading{New: f} - s := &SensorManager{ - Manager: m, - Reading: r, - } - return s -} - -func (s *SensorManager) Start(addr int) error { - if err := s.Manager.Start(); err != nil { - return err - } - fmt.Printf("Sensor %d: %v\n", addr, s) - // starting monitoring - go s.Monitor(addr) - return nil -} - -func (s *SensorManager) Monitor(addr int) { - ch := make(chan struct{}) // hb chan - if s.SampleRate == 0 { - s.SampleRate = 5000 - } - go s.HeartBeat(ch, s.SampleRate, 1000, time.Millisecond) - - for range ch { - go s.TakeReading(addr) - } -} - -func (r *Reading) TakeReading(addr int) { - sample, err := r.New(addr) - if err != nil { - panic(err) - } - fmt.Printf("got sample: %v\n", sample) - r.Lock() - defer r.Unlock() - r.Latest = sample -} diff --git a/internal/pkg/sensor/ph_sensor.go b/internal/pkg/sensor/ph_sensor.go deleted file mode 100644 index b85c528..0000000 --- a/internal/pkg/sensor/ph_sensor.go +++ /dev/null @@ -1,42 +0,0 @@ -package sensor - -// do sensor and methods - -import ( - "fmt" - "sync" - - "github.com/spf13/viper" -) - -type PHManager struct { - // do sensor manager - *Atlas - *SensorManager `mapstructure:",squash"` - - sync.RWMutex -} - -func NewPHManager(i2c I2CClient) *PHManager { - a := &Atlas{I2C: i2c} - sm := NewSensorManager(a.Read) - m := &PHManager{ - SensorManager: sm, - Atlas: a, - } - return m -} - -func (s *PHManager) LoadConfig(config *viper.Viper, key string) { - config.UnmarshalKey(key, s) - fmt.Printf("PH: %v\n", s.Atlas) -} - -func (s *PHManager) GetDefaultName() string { - return "pH Sensor" -} - -func (s PHManager) String() string { - // TODO - return "" -} diff --git a/internal/pkg/sensor/rtd_sensor.go b/internal/pkg/sensor/rtd_sensor.go deleted file mode 100644 index da095ba..0000000 --- a/internal/pkg/sensor/rtd_sensor.go +++ /dev/null @@ -1,41 +0,0 @@ -package sensor - -// do sensor and methods - -import ( - "fmt" - "sync" - - "github.com/spf13/viper" -) - -type RTDManager struct { - // do sensor manager - *Atlas - *SensorManager `mapstructure:",squash"` - sync.RWMutex -} - -func NewRTDManager(i2c I2CClient) *RTDManager { - a := &Atlas{I2C: i2c} - sm := NewSensorManager(a.Read) - m := &RTDManager{ - SensorManager: sm, - Atlas: a, - } - return m -} - -func (s *RTDManager) LoadConfig(config *viper.Viper, key string) { - config.UnmarshalKey(key, s) - fmt.Printf("RTD: %v\n", s.Atlas) -} - -func (s *RTDManager) GetDefaultName() string { - return "RTD Sensor" -} - -func (s *RTDManager) String() string { - // TODO - return "" -}