adding reactor config parser for devices

main
Keegan 2 years ago
parent 1c2868daf8
commit b1520db055

@ -26,7 +26,9 @@ func NewCoordinator(ch chan error) coordinator {
} }
func LoadConfig(fname string) Config { func LoadConfig(fname string) Config {
config.Load(fname) if err := config.Load(fname); err != nil {
panic(err)
}
return config.LoadConfig() return config.LoadConfig()
} }

@ -0,0 +1,5 @@
reactor:
sensors:
address: 112 # decimal
name: "DO Sensor"

@ -0,0 +1,26 @@
package config
// pacakge serves to store/load config files for reactor
import (
"sync"
)
type ReactorConf struct {
Reactor ReactorConfig `mapstructure:"reactor"`
Devices map[int]DeviceConfig `mapstructure:"devices"`
sync.RWMutex
}
type DeviceConfig struct {
Address uint32 `mapstructure:"address"`
Interval uint32 `mapstructure:"interval"`
Name string `mapstructure:"name"`
}
// loaded in other file
func (c *ReactorConf) GetURL() (string, error) {
c.RLock()
defer c.RUnlock()
return c.Reactor.URL, nil
}

@ -17,7 +17,7 @@ import (
//"bytes" //"bytes"
) )
type Config struct { type ServerConf struct {
Server ServerConfig `mapstructure:"server"` Server ServerConfig `mapstructure:"server"`
Reactors map[string]ReactorConfig `mapstructure:"reactors"` Reactors map[string]ReactorConfig `mapstructure:"reactors"`
sync.RWMutex sync.RWMutex
@ -34,21 +34,36 @@ type ServerConfig struct {
type ReactorConfig struct { type ReactorConfig struct {
Token string `mapstructure:"db-token"` Token string `mapstructure:"db-token"`
Bucket string `mapstructure:"db-bucket"` Bucket string `mapstructure:"db-bucket"`
Name string URL string `mapstructure:"db-url",omitempty` // only needed by reactor
Id uint32 Name string `mapstructure:"name",omitempty` // not always set
Id uint32 `mapstructure:"id"`
} }
var C *Config type Config interface {
Store() error
}
var C Config
func Load(fname string) { func Load(fname string) error {
// read stored configs // read stored configs
C = &Config{} //C = &ServerConf{}
switch fname {
case "server":
C = &ServerConf{}
case "reactor":
C = &ReactorConf{}
default:
return fmt.Errorf("%s not recognized", fname)
}
logging.Debug(logging.DStart, "Loading config for %s", fname)
viper.SetConfigName(fname) viper.SetConfigName(fname)
viper.SetConfigType("yaml") viper.SetConfigType("yaml")
viper.AddConfigPath("/etc/frms/config") // viper.AddConfigPath("/etc/frms/config")
// defaults // defaults which we don't want to set nessecarily
viper.SetDefault("server.db-org", "ForeLight") //viper.SetDefault("server.db-org", "ForeLight")
viper.SetDefault("server.db-url", "http://192.168.100.2:8086") //viper.SetDefault("server.db-url", "http://192.168.100.2:8086")
//viper.SetDefault("reactor.db-url", "http://192.168.100.2:8086")
// unmarshalling // unmarshalling
err := viper.ReadInConfig() // the fact i never did this is infuriating err := viper.ReadInConfig() // the fact i never did this is infuriating
if err != nil { if err != nil {
@ -65,23 +80,23 @@ func Load(fname string) {
// unmarshalled at this point // unmarshalled at this point
} }
func LoadConfig() *Config { func LoadConfig() Config {
return C return C
} }
func (c *Config) GetURL() (string, error) { func (c *ServerConf) GetURL() (string, error) {
c.RLock() c.RLock()
defer c.RUnlock() defer c.RUnlock()
return C.Server.URL, nil return C.Server.URL, nil
} }
func (c *Config) GetOrg() (string, error) { func (c *ServerConf) GetOrg() (string, error) {
c.RLock() c.RLock()
defer c.RUnlock() defer c.RUnlock()
return c.Server.Orginization, nil return c.Server.Orginization, nil
} }
func (c *Config) GetPort(port string) (int, error) { func (c *ServerConf) GetPort(port string) (int, error) {
c.RLock() c.RLock()
defer c.RUnlock() defer c.RUnlock()
portString, ok := c.Server.Ports[port] portString, ok := c.Server.Ports[port]
@ -94,46 +109,46 @@ func (c *Config) GetPort(port string) (int, error) {
return portString, nil return portString, nil
} }
func (c *Config) GetServerToken() (string, error) { func (c *ServerConf) GetServerToken() (string, error) {
c.RLock() c.RLock()
defer c.RUnlock() defer c.RUnlock()
return c.Server.Token, nil return c.Server.Token, nil
} }
func (c *Config) GetReactorClient(id uint32) (string, string, error) { func (c *ServerConf) GetReactorClient(id uint32) (string, string, error) {
c.RLock() c.RLock()
defer c.RUnlock() defer c.RUnlock()
idString := strconv.FormatUint(uint64(id), 10) idString := strconv.FormatUint(uint64(id), 10)
if r, ok := c.Reactors[idString]; ok { if r, ok := c.Reactors[idString]; ok {
return r.Bucket, r.Token, nil return r.Bucket, r.Token, nil
} }
return "", "", fmt.Errorf("Reactor %v config doesnt exist!", id) return "", "", fmt.Errorf("reactor %v config doesnt exist", id)
} }
// setters // setters
func (c *Config) UpdateURL(url string) error { func (c *ServerConf) UpdateURL(url string) error {
c.Lock() c.Lock()
defer c.Unlock() defer c.Unlock()
if url == "" { if url == "" {
return errors.New("String cannot be empty!") return errors.New("string cannot be empty")
} }
c.Server.URL = url c.Server.URL = url
viper.Set("server.db-url", url) viper.Set("server.db-url", url)
return viper.WriteConfigAs(viper.ConfigFileUsed()) return viper.WriteConfigAs(viper.ConfigFileUsed())
} }
func (c *Config) UpdateOrg(org string) error { func (c *ServerConf) UpdateOrg(org string) error {
c.Lock() c.Lock()
defer c.Unlock() defer c.Unlock()
if org == "" { if org == "" {
return errors.New("String cannot be empty!") return errors.New("string cannot be empty")
} }
c.Server.Orginization = org c.Server.Orginization = org
viper.Set("server.db-org", org) viper.Set("server.db-org", org)
return viper.WriteConfigAs(viper.ConfigFileUsed()) return viper.WriteConfigAs(viper.ConfigFileUsed())
} }
func (c *Config) UpdatePort(pName string, port int) error { func (c *ServerConf) UpdatePort(pName string, port int) error {
c.Lock() c.Lock()
defer c.Unlock() defer c.Unlock()
if port < 1024 || port > 65535 { if port < 1024 || port > 65535 {
@ -149,7 +164,7 @@ func (c *Config) UpdatePort(pName string, port int) error {
return viper.WriteConfigAs(viper.ConfigFileUsed()) return viper.WriteConfigAs(viper.ConfigFileUsed())
} }
func (c *Config) UpdateServerToken(token string) error { func (c *ServerConf) UpdateServerToken(token string) error {
c.Lock() c.Lock()
defer c.Unlock() defer c.Unlock()
if token == "" { if token == "" {
@ -160,7 +175,7 @@ func (c *Config) UpdateServerToken(token string) error {
return viper.WriteConfigAs(viper.ConfigFileUsed()) return viper.WriteConfigAs(viper.ConfigFileUsed())
} }
func (c *Config) UpdateReactorClient(id uint32, bucket, token string) error { func (c *ServerConf) UpdateReactorClient(id uint32, bucket, token string) error {
c.Lock() c.Lock()
c.Unlock() c.Unlock()
sid := strconv.FormatUint(uint64(id), 10) sid := strconv.FormatUint(uint64(id), 10)
@ -181,6 +196,6 @@ func (c *Config) UpdateReactorClient(id uint32, bucket, token string) error {
return viper.WriteConfigAs(viper.ConfigFileUsed()) return viper.WriteConfigAs(viper.ConfigFileUsed())
} }
func (c *Config) Store() error { func (c *ServerConf) Store() error {
return viper.WriteConfigAs(viper.ConfigFileUsed()) return viper.WriteConfigAs(viper.ConfigFileUsed())
} }

@ -1,114 +1,114 @@
package sensor package sensor
import ( import (
_"fmt" _ "FRMS/internal/pkg/I2C"
"time" _ "fmt"
"sync" "log"
"strings" "strings"
_ "FRMS/internal/pkg/I2C" "sync"
"log" "time"
) )
type Manager struct { type Manager struct {
*Dev *Dev
I2CDevice I2CDevice
*Active *Active
Hb time.Duration Hb time.Duration
} }
type Active struct { type Active struct {
sync.Mutex sync.Mutex
bool bool
int int
} }
type Dev struct { type Dev struct {
// last known values // last known values
Addr int Addr int
Type string Type string
Status string // could be more efficient but to hell with it Status string // could be more efficient but to hell with it
Data string Data string
} }
type I2CDevice interface { type I2CDevice interface {
// basic device info // basic device info
GetAddr() int GetAddr() int
GetStatus() string GetStatus() string
GetType() string GetType() string
GetData() string GetData() string
} }
func NewDeviceManager(i2c I2CDevice) *Manager { func NewDeviceManager(i2c I2CDevice) *Manager {
m := &Manager{Hb:time.Duration(1*time.Second)} m := &Manager{Hb: time.Duration(1 * time.Second)}
m.I2CDevice = i2c m.I2CDevice = i2c
m.Active = &Active{} m.Active = &Active{}
m.Dev = &Dev{Addr:i2c.GetAddr(),Type:i2c.GetType(),Status:i2c.GetStatus(),Data:i2c.GetData()} m.Dev = &Dev{Addr: i2c.GetAddr(), Type: i2c.GetType(), Status: i2c.GetStatus(), Data: i2c.GetData()}
return m return m
} }
func (m *Manager) Start() { func (m *Manager) Start() {
// goal is to start a long running monitoring routine // goal is to start a long running monitoring routine
if !m.Activate() { if !m.Activate() {
log.Fatal("Manager already running!") log.Fatal("Manager already running!")
} // atomically activated if this runs } // atomically activated if this runs
// go m.Monitor() // go m.Monitor()
} }
func (m *Manager) Exit() { func (m *Manager) Exit() {
if !m.Deactivate() { if !m.Deactivate() {
log.Fatal("Manager already exited!") log.Fatal("Manager already exited!")
} }
} }
func (m *Manager) GetType() string { func (m *Manager) GetType() string {
return m.Type return m.Type
} }
func (m *Manager) GetStatus() string { func (m *Manager) GetStatus() string {
m.Status = m.I2CDevice.GetStatus() m.Status = m.I2CDevice.GetStatus()
if m.IsActive() && strings.Contains(m.Status,"KILLED") { if m.IsActive() && strings.Contains(m.Status, "KILLED") {
m.Exit() m.Exit()
} }
return m.Status return m.Status
} }
func (m *Manager) GetData() string { func (m *Manager) GetData() string {
m.Data = m.I2CDevice.GetData() m.Data = m.I2CDevice.GetData()
return m.Data return m.Data
} }
func (m *Manager) GetAddr() int { func (m *Manager) GetAddr() int {
return m.Addr return m.Addr
} }
// atomic activation and deactivation // atomic activation and deactivation
func (a *Active) Activate() bool { func (a *Active) Activate() bool {
// returns true if success, false otherwise // returns true if success, false otherwise
a.Lock() a.Lock()
defer a.Unlock() defer a.Unlock()
if a.bool { // already active if a.bool { // already active
return false return false
} else { } else {
a.bool = true a.bool = true
a.int = 0 a.int = 0
return a.bool return a.bool
} }
} }
func (a *Active) Deactivate() bool { func (a *Active) Deactivate() bool {
// returns true if success false otherise // returns true if success false otherise
a.Lock() a.Lock()
defer a.Unlock() defer a.Unlock()
if a.bool { if a.bool {
a.bool = false a.bool = false
return true return true
} else { // already deactivated } else { // already deactivated
return a.bool // false return a.bool // false
} }
} }
func (a *Active) IsActive() bool { func (a *Active) IsActive() bool {
a.Lock() a.Lock()
defer a.Unlock() defer a.Unlock()
return a.bool return a.bool
} }

@ -0,0 +1,36 @@
package sensor
import (
"fmt"
"sync"
)
/*
this file serves as a map that the sensor library can use to determine which manager to call
*/
type NewManager interface {
// serves as interface to restrict managers can be relocated
}
type DM struct {
DeviceManagers map[uint]NewManager
sync.Mutex
}
func NewManagerDirectory() *DM {
m := map[uint]NewManager{
// map to set functions up
112: NewDOManager(),
}
return &DM{DeviceManagers: m}
}
func (d *DM) GetManager(addr uint) (NewManager, error) {
d.Lock()
defer d.Unlock()
if m, ok := d.DeviceManagers[addr]; ok {
return m, nil
}
return &DM{}, fmt.Errorf("No manager found for address %d!", addr)
}

30
notes

@ -922,3 +922,33 @@ should also probably wrap these in a seperate struct
does it make more sensor to load different configs for each entity or justhave one monolithic config (probably load for each one and then let it update itself) does it make more sensor to load different configs for each entity or justhave one monolithic config (probably load for each one and then let it update itself)
going to have the init script set up the going to have the init script set up the
Welcome back
#TODO 8/31
Goals:
- Add a config parser to load/store device manager struct
- start figuring out what a generic config package looks like
- figure out how to load different sensor functions dynamically
Basic reactor workflow overview
1) On boot, scan I2C bus to find active devices
2) For every device shown as active, spawn a sensor manager from the assocaited config
3) on disconnect, shut the dm down and save current settings to config

Loading…
Cancel
Save