Buggy impl
parent
42332990ed
commit
25fb0d86fc
@ -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()
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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 ""
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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 ""
|
||||
}
|
@ -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 ""
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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 ""
|
||||
}
|
@ -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
|
||||
}
|
@ -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 ""
|
||||
}
|
@ -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 ""
|
||||
}
|
Loading…
Reference in New Issue