|
|
@ -7,12 +7,10 @@ import (
|
|
|
|
pb "FRMS/internal/pkg/grpc"
|
|
|
|
pb "FRMS/internal/pkg/grpc"
|
|
|
|
"FRMS/internal/pkg/influxdb"
|
|
|
|
"FRMS/internal/pkg/influxdb"
|
|
|
|
"FRMS/internal/pkg/logging"
|
|
|
|
"FRMS/internal/pkg/logging"
|
|
|
|
|
|
|
|
"FRMS/internal/pkg/manager"
|
|
|
|
"FRMS/internal/pkg/system"
|
|
|
|
"FRMS/internal/pkg/system"
|
|
|
|
"context"
|
|
|
|
"context"
|
|
|
|
"errors"
|
|
|
|
|
|
|
|
"fmt"
|
|
|
|
"fmt"
|
|
|
|
"math"
|
|
|
|
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"time"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/spf13/viper"
|
|
|
|
"github.com/spf13/viper"
|
|
|
@ -21,6 +19,18 @@ import (
|
|
|
|
"google.golang.org/grpc/status"
|
|
|
|
"google.golang.org/grpc/status"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// basic manager
|
|
|
|
|
|
|
|
type Manager interface {
|
|
|
|
|
|
|
|
Start() error
|
|
|
|
|
|
|
|
Exit() error
|
|
|
|
|
|
|
|
Timeout() (time.Duration, error)
|
|
|
|
|
|
|
|
HeartBeat(chan struct{}, int, int, time.Duration) // creates a hb
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func NewManager(max int) Manager {
|
|
|
|
|
|
|
|
return manager.New(max)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// db client interface
|
|
|
|
// db client interface
|
|
|
|
type DBClient interface {
|
|
|
|
type DBClient interface {
|
|
|
|
//
|
|
|
|
//
|
|
|
@ -42,86 +52,91 @@ func NewI2CClient(config *viper.Viper) (I2CClient, error) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type Server struct {
|
|
|
|
type Server struct {
|
|
|
|
// embed
|
|
|
|
|
|
|
|
Ip string `mapstructure:"ip"`
|
|
|
|
Ip string `mapstructure:"ip"`
|
|
|
|
Port int `mapstructure:"port"`
|
|
|
|
Port int `mapstructure:"port"`
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Coordinator == Reactor Level Coordinator
|
|
|
|
type Info struct {
|
|
|
|
|
|
|
|
|
|
|
|
type Coordinator struct {
|
|
|
|
|
|
|
|
Name string `mapstructure:"name,omitempty"`
|
|
|
|
Name string `mapstructure:"name,omitempty"`
|
|
|
|
ID int `mapstructure:"id,omitempty"`
|
|
|
|
ID int `mapstructure:"id,omitempty"`
|
|
|
|
Model string `mapstructure:"model,omitempty"`
|
|
|
|
Model string `mapstructure:"model,omitempty"`
|
|
|
|
// server info embedded
|
|
|
|
HB int `mapstructure:"heartbeat"`
|
|
|
|
Server
|
|
|
|
Server
|
|
|
|
// database
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type ReactorCoordinator struct {
|
|
|
|
|
|
|
|
Manager // base manager
|
|
|
|
|
|
|
|
Config *viper.Viper // config
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Info `mapstructure:",squash"`
|
|
|
|
|
|
|
|
|
|
|
|
Database DBClient
|
|
|
|
Database DBClient
|
|
|
|
I2C I2CClient
|
|
|
|
I2C I2CClient
|
|
|
|
// config
|
|
|
|
|
|
|
|
Config *viper.Viper
|
|
|
|
MonitoringClient pb.MonitoringClient // grpc
|
|
|
|
MonitoringClient pb.MonitoringClient
|
|
|
|
|
|
|
|
// connected devices
|
|
|
|
|
|
|
|
*DeviceCoordinator // struct for locking
|
|
|
|
*DeviceCoordinator // struct for locking
|
|
|
|
// other stuff and things
|
|
|
|
|
|
|
|
Err chan error
|
|
|
|
|
|
|
|
mu sync.Mutex
|
|
|
|
|
|
|
|
HB time.Duration
|
|
|
|
|
|
|
|
PingTimer chan struct{}
|
|
|
|
|
|
|
|
// db client
|
|
|
|
|
|
|
|
Active active
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type active struct {
|
|
|
|
Err chan error
|
|
|
|
bool
|
|
|
|
|
|
|
|
int
|
|
|
|
|
|
|
|
sync.Mutex
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func NewCoordinator(config *viper.Viper, ch chan error) *Coordinator {
|
|
|
|
func NewCoordinator(config *viper.Viper, errCh chan error) *ReactorCoordinator {
|
|
|
|
// coord
|
|
|
|
|
|
|
|
c := &Coordinator{Err: ch, Config: config}
|
|
|
|
|
|
|
|
c.DeviceCoordinator = NewDeviceCoordinator()
|
|
|
|
|
|
|
|
// hb defaults to 5
|
|
|
|
|
|
|
|
c.HB = time.Duration(5 * time.Second)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// this is going to be scuffed
|
|
|
|
m := NewManager(6) // max 6 attempts
|
|
|
|
//c.DB = &DB{Bucket: "bb", Org: "ForeLight", URL: url, Token: "S1UZssBu6KPfHaQCt34pZFpyc5lzbH9XanYJWCkOI5FqLY7gq205C6FTH-CmugiPH6o2WoKlTkEuPgIfaJjAhw=="}
|
|
|
|
dc := NewDeviceCoordinator(config)
|
|
|
|
// setup db
|
|
|
|
|
|
|
|
var err error
|
|
|
|
|
|
|
|
if c.Database, err = NewDBClient(config); err != nil {
|
|
|
|
|
|
|
|
ch <- err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if c.I2C, err = NewI2CClient(config); err != nil {
|
|
|
|
c := &ReactorCoordinator{
|
|
|
|
ch <- err
|
|
|
|
Manager: m,
|
|
|
|
|
|
|
|
Config: config,
|
|
|
|
|
|
|
|
DeviceCoordinator: dc,
|
|
|
|
|
|
|
|
Err: errCh,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
c.PingTimer = make(chan struct{})
|
|
|
|
// this is going to be scuffed
|
|
|
|
|
|
|
|
//c.DB = &DB{Bucket: "bb", Org: "ForeLight", URL: url, Token: "S1UZssBu6KPfHaQCt34pZFpyc5lzbH9XanYJWCkOI5FqLY7gq205C6FTH-CmugiPH6o2WoKlTkEuPgIfaJjAhw=="}
|
|
|
|
|
|
|
|
|
|
|
|
return c
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (c *Coordinator) Start() {
|
|
|
|
func (c *ReactorCoordinator) Start() {
|
|
|
|
// should discover hwinfo and sensors on its own
|
|
|
|
// should discover hwinfo and sensors on its own
|
|
|
|
// now setting up sensor managers
|
|
|
|
// now setting up sensor managers
|
|
|
|
c.Activate()
|
|
|
|
var err error
|
|
|
|
// load hwinfo
|
|
|
|
|
|
|
|
if err := c.LoadInfo(); err != nil { // loads info
|
|
|
|
if err = c.Manager.Start(); err != nil {
|
|
|
|
c.Err <- err
|
|
|
|
c.Err <- err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// grab config stuff
|
|
|
|
// load config
|
|
|
|
c.Config.UnmarshalKey("reactor", c)
|
|
|
|
if err = c.LoadConfig(); err != nil { // loads info
|
|
|
|
go c.Monitor()
|
|
|
|
c.Err <- err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// loading clients
|
|
|
|
|
|
|
|
if c.Database, err = NewDBClient(c.Config); err != nil {
|
|
|
|
|
|
|
|
c.Err <- err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if c.I2C, err = NewI2CClient(c.Config); err != nil {
|
|
|
|
|
|
|
|
c.Err <- err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
go c.Discover()
|
|
|
|
go c.Discover()
|
|
|
|
go c.Database.Start()
|
|
|
|
go c.Database.Start()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (c *Coordinator) LoadInfo() error {
|
|
|
|
func (c *ReactorCoordinator) LoadConfig() error {
|
|
|
|
// check ID
|
|
|
|
|
|
|
|
var err error
|
|
|
|
var err error
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// get hb
|
|
|
|
|
|
|
|
if !c.Config.IsSet("reactor.heartbeat") {
|
|
|
|
|
|
|
|
// default to 5 seconds
|
|
|
|
|
|
|
|
c.Config.Set("reactor.heartbeat", 5)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// check id
|
|
|
|
if !c.Config.IsSet("reactor.id") {
|
|
|
|
if !c.Config.IsSet("reactor.id") {
|
|
|
|
// get id
|
|
|
|
// get from hw
|
|
|
|
var id int
|
|
|
|
var id int
|
|
|
|
if id, err = system.GetId("eth0"); err != nil {
|
|
|
|
if id, err = system.GetId("eth0"); err != nil {
|
|
|
|
return err
|
|
|
|
return err
|
|
|
@ -131,42 +146,39 @@ func (c *Coordinator) LoadInfo() error {
|
|
|
|
|
|
|
|
|
|
|
|
// check Model
|
|
|
|
// check Model
|
|
|
|
if !c.Config.IsSet("reactor.model") {
|
|
|
|
if !c.Config.IsSet("reactor.model") {
|
|
|
|
// get model
|
|
|
|
// get from hw
|
|
|
|
var model string
|
|
|
|
var model string
|
|
|
|
if model, err = system.GetModel(); err != nil {
|
|
|
|
if model, err = system.GetModel(); err != nil {
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
c.Config.Set("reactor.model", model)
|
|
|
|
c.Config.Set("reactor.model", model)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// all good
|
|
|
|
|
|
|
|
|
|
|
|
// all good, unmarhsaling
|
|
|
|
|
|
|
|
c.Config.UnmarshalKey("reactor", c)
|
|
|
|
|
|
|
|
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (c *Coordinator) Monitor() {
|
|
|
|
func (c *ReactorCoordinator) Monitor() {
|
|
|
|
// periodically grabs connected devs and updates list
|
|
|
|
// periodically grabs connected devs and updates list
|
|
|
|
for c.IsActive() {
|
|
|
|
ch := make(chan struct{})
|
|
|
|
select {
|
|
|
|
go c.HeartBeat(ch, c.HB, 0, time.Second)
|
|
|
|
case <-c.PingTimer:
|
|
|
|
|
|
|
|
// check devs and ping
|
|
|
|
|
|
|
|
active, err := c.I2C.GetConnected()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
c.Err <- err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
go c.UpdateDevices(c.Config, c.I2C, active)
|
|
|
|
|
|
|
|
go c.Ping()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (c *Coordinator) HeartBeat() {
|
|
|
|
for range ch {
|
|
|
|
for c.IsActive() {
|
|
|
|
// check devs and ping
|
|
|
|
c.PingTimer <- struct{}{}
|
|
|
|
|
|
|
|
logging.Debug(logging.DClient, "RLC Pinging server")
|
|
|
|
logging.Debug(logging.DClient, "RLC Pinging server")
|
|
|
|
time.Sleep(c.HB)
|
|
|
|
// this can probably be offloaded
|
|
|
|
|
|
|
|
active, err := c.I2C.GetConnected()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
c.Err <- err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
go c.UpdateDevices(c.Config, c.I2C, active)
|
|
|
|
|
|
|
|
go c.Ping()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (c *Coordinator) Discover() {
|
|
|
|
func (c *ReactorCoordinator) Discover() {
|
|
|
|
// sets up connection to central coordiantor
|
|
|
|
// sets up connection to central coordiantor
|
|
|
|
conn, err := c.Connect(c.Ip, c.Port)
|
|
|
|
conn, err := c.Connect(c.Ip, c.Port)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
@ -187,11 +199,12 @@ func (c *Coordinator) Discover() {
|
|
|
|
c.Err <- err
|
|
|
|
c.Err <- err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
c.MonitoringClient = pb.NewMonitoringClient(clientConn)
|
|
|
|
c.MonitoringClient = pb.NewMonitoringClient(clientConn)
|
|
|
|
go c.HeartBeat()
|
|
|
|
// manager
|
|
|
|
|
|
|
|
go c.Monitor()
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (c *Coordinator) Connect(ip string, port int) (*grpc.ClientConn, error) {
|
|
|
|
func (c *ReactorCoordinator) Connect(ip string, port int) (*grpc.ClientConn, error) {
|
|
|
|
// function connects to central server and passes hwinfo
|
|
|
|
// function connects to central server and passes hwinfo
|
|
|
|
var opts []grpc.DialOption
|
|
|
|
var opts []grpc.DialOption
|
|
|
|
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
|
|
|
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
|
|
@ -202,13 +215,13 @@ func (c *Coordinator) Connect(ip string, port int) (*grpc.ClientConn, error) {
|
|
|
|
code := status.Code(err)
|
|
|
|
code := status.Code(err)
|
|
|
|
if code != 0 { // != OK
|
|
|
|
if code != 0 { // != OK
|
|
|
|
if code == (5 | 14) { // service temp down
|
|
|
|
if code == (5 | 14) { // service temp down
|
|
|
|
to := c.Timeout()
|
|
|
|
var to time.Duration
|
|
|
|
if to == 0 {
|
|
|
|
if to, err = c.Timeout(); err != nil {
|
|
|
|
err = errors.New("Failed to connect to central server")
|
|
|
|
// from manager
|
|
|
|
return &grpc.ClientConn{}, err
|
|
|
|
return &grpc.ClientConn{}, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
logging.Debug(logging.DClient, "Server currently unavailable, retrying in %v ms", to)
|
|
|
|
logging.Debug(logging.DClient, "Server currently unavailable, retrying in %v", to)
|
|
|
|
time.Sleep(time.Duration(to) * time.Millisecond)
|
|
|
|
time.Sleep(to)
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
return &grpc.ClientConn{}, err
|
|
|
|
return &grpc.ClientConn{}, err
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -217,48 +230,3 @@ func (c *Coordinator) Connect(ip string, port int) (*grpc.ClientConn, error) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return conn, nil
|
|
|
|
return conn, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (c *Coordinator) Timeout() int {
|
|
|
|
|
|
|
|
c.Active.Lock()
|
|
|
|
|
|
|
|
defer c.Active.Unlock()
|
|
|
|
|
|
|
|
if c.Active.int < 9 {
|
|
|
|
|
|
|
|
v := int(5 * math.Pow(float64(2), float64(c.Active.int)))
|
|
|
|
|
|
|
|
c.Active.int += 1
|
|
|
|
|
|
|
|
return v
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
//excedded retries
|
|
|
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (c *Coordinator) IsActive() bool {
|
|
|
|
|
|
|
|
c.Active.Lock()
|
|
|
|
|
|
|
|
defer c.Active.Unlock()
|
|
|
|
|
|
|
|
return c.Active.bool
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (c *Coordinator) Exit() bool {
|
|
|
|
|
|
|
|
c.Active.Lock()
|
|
|
|
|
|
|
|
defer c.Active.Unlock()
|
|
|
|
|
|
|
|
if c.Active.bool {
|
|
|
|
|
|
|
|
c.Active.bool = false
|
|
|
|
|
|
|
|
logging.Debug(logging.DClient, "RLC Exiting...")
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
logging.Debug(logging.DError, "RLC Already Dead!")
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (c *Coordinator) Activate() bool {
|
|
|
|
|
|
|
|
c.Active.Lock()
|
|
|
|
|
|
|
|
defer c.Active.Unlock()
|
|
|
|
|
|
|
|
if c.Active.bool {
|
|
|
|
|
|
|
|
logging.Debug(logging.DError, "RLC Already Started!")
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
logging.Debug(logging.DClient, "RLC Starting")
|
|
|
|
|
|
|
|
c.Active.bool = true
|
|
|
|
|
|
|
|
return c.Active.bool
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|