From fc83dae5298087ca8726890430761bae28accd04 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Wed, 14 Jun 2023 11:03:23 -0400 Subject: [PATCH 01/44] setting up unit tests and documentation --- README.md | 65 +++---------------------------------------------------- 1 file changed, 3 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index 4648f5f..90190f9 100644 --- a/README.md +++ b/README.md @@ -1,66 +1,7 @@ # FRMS -ForeLight Reactor Management System - -Time to get to buisness - -**[Notes](internal/notes/index.md)** - - -## Main design principles - - Flexible - - Reliable - - Scalable - -Those principles guide our design choices - -### Flexibile - - System should support any reactor config - - should be able to change sub packages in isolation - - portability of packages i.e. can swap databases or sub in testing packages for memory based storage - -### Realiable - - should support any # of reactors failing (including network related failures) - - should provide log and database correctness via atomic commits - - automatic log recovery and reactor functioning despite sub-system or network failures - - 100% uptime and seamless updates **goal - -### Scalable - - Add and use sensor packages at will - - Group reactors logically regardless of physical location - - Scale infastructure to efficiently support any # of reactors - -#### Brainstorming -Everything here is subject to change -At first I want to focus on the "Backend" of the system which I consider to be everything from the server down to the sensors. This part of the system would laeve me with a central database of values which would then let me do whatever I wanted on the front end. - -In order to make the backend more manageable, and hopefully build a more modular system, I want to desing the backend in stages and through the use of interfaces and independent packages. This codebase is sure to be refactored many times over its lifetime, but I hope ot not have to stray far from the initial design. I will be attempting to follow the Golangs core design philosophy at least as best as I understand it. - -Getting into the core of the plan,I hope to layout Phase 1 and create a working document that I can reference and change. The plan will be as follows - -Phase 1 - The goal of phase 1 is to create a working backend of the sytem with a few basicpackages i.e. temperature DO etc. -Phase 2 - Phase 2 implements a frontend control and user management service where the rest of the company can view and update reactor parameters in real time -Phase 3 - It is now that I can revist the back end and start to create a private infastructure to group reactors logically based on an internal structure allowing for remote reactor control. - -During any of these phases the expectation is that I go back and tweak and repair things as needed. The software is built with the goal and expectation that repairs to subpackages should not impact other packages implementations beyond the nessecary changes. - -Now lets get into some more concrete design choices - -On the server there should be several docker containers running to help keep the application streamlined - -The main container will create the coordinator process also responsible for creating the sub management processes. This archetecture will be expanded upon in further sections but in essence will allow for easier implementation of the skeleton and will force the sensor data aquisition to be smarter at the application layer. - -There will also be a container responsible for databsase manipulations and this could and should be a seperate package. The choice of a packge allows us to create a memory based package for easier testing. - -The frontend will also have a host of other containers but those will be covered in phase 2 +ForeLight Reactor Management System -Starting from the coordinator; at intiial boot the coordinator will search a central reactor config file that will have all of the active reactor IP addresses. Phase 1 assumes control at the network level and thus we can use IP addresses as identification. This limits us to 255 active reactors, but we can expand this to 256*256*256 by using the entire private address space in more clever Identification schemas. 256 will be suitable for now - *as a side note, the coordinator could also send a discovery ping to add reactors dynamically, but this is harder to implement and should be saved for phase 3 -For each reactor, the coordinator will spin up a subordinate reactor level cooridnator in a goroutine. This sub coordinator will be respobsible for retriving a reactors active sensors and creating the associated goroutines for each aquisition. This choice more easily fits into our package structure and also allows for very clean reactor information as we can easily tell which sensors are active and up to date or recovering after a network outage. -For each active sensor, the subcoordiantor will call the associated package and create a gourtine for this sensor. This long running routine will respond to gRPC calls from either coordinator (this may be unnesecary and we might end up killing subcoordiantor after reactor init) to update sensor function and make sure that central databsaes are up to date. We can simplify log recovery based on a custom logging schema (term and seq based?) and create atomic logging practice based on checks betwen the coordinator and local reactor. -At the reactor level, a central coordinator will be resposble for creating goroutines for each active sensor based on config at boot and responding to incoming RPC calls. Each goroutine for the sensors will be in charge of polling the sensors and storing in a temporary log file based on custom interfaces. This log file will have to be cleaned and we should look into 16gb or even large flash storage options. -Each subprocess will handle associated RPC calls to update sensor values and respond to requests for log info. This will probably have to be sufficiently vague or unique to each sensor type +## Outline +This branch will serve as the staging ground for adding unit tests and documentation. This file will serve as the index page used to navigate around. From 7e33632f9e5ccd7628c2c095ba16f20971c2e176 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Wed, 14 Jun 2023 11:06:25 -0400 Subject: [PATCH 02/44] setting up unit tests and documentation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 90190f9..e77b3f8 100644 --- a/README.md +++ b/README.md @@ -4,4 +4,4 @@ ForeLight Reactor Management System ## Outline -This branch will serve as the staging ground for adding unit tests and documentation. This file will serve as the index page used to navigate around. +This branch will serve as the staging ground for adding unit tests and documentation in order to finalize v0.1.0-alpha From bf2040807cf381a850ed5c1805e17f142d9c258c Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Wed, 14 Jun 2023 13:01:46 -0400 Subject: [PATCH 03/44] wrote manager tests --- internal/pkg/manager/manager.go | 37 +++-- internal/pkg/manager/manager_test.go | 206 +++++++++++++++++++++++++++ 2 files changed, 231 insertions(+), 12 deletions(-) create mode 100644 internal/pkg/manager/manager_test.go diff --git a/internal/pkg/manager/manager.go b/internal/pkg/manager/manager.go index ecdd3a0..fe316c9 100644 --- a/internal/pkg/manager/manager.go +++ b/internal/pkg/manager/manager.go @@ -9,8 +9,10 @@ import ( "time" ) +// max allowable connection attempts is 255 +const MaxConnAttempts = 0xFF + // basic manager for starting/stopping checks plus built in heartbeat for downtime detection -// used across server/reactor type Connection struct { Attempts float64 // float for pow @@ -23,31 +25,43 @@ type Manager struct { Active int32 // atomic checks } -func New(maxCon int) *Manager { +// errors +var ( + ErrInvalidMaxConn = errors.New("invalid max connection attempts") + ErrManagerInactive = errors.New("manager inactive") + ErrManagerActive = errors.New("manager active") + ErrMaxAttemptsExceeded = errors.New("max connection attempts exceeded") +) + +func New(maxConn int) (*Manager, error) { + + if maxConn < 0 || maxConn > MaxConnAttempts { + return &Manager{}, ErrInvalidMaxConn + } - c := &Connection{MaxAttempts: maxCon} + c := &Connection{MaxAttempts: maxConn} m := &Manager{ Connection: c, } - return m + return m, nil } func (m *Manager) Start() error { - // atomically checks/updates status if atomic.CompareAndSwapInt32(&m.Active, 0, 1) { m.ResetConnections() return nil } - // already running - return errors.New("Manager already started!") + + return ErrManagerActive } -func (m *Manager) Exit() error { +func (m *Manager) Stop() error { if atomic.CompareAndSwapInt32(&m.Active, 1, 0) { return nil } - return errors.New("Manager not active!") + + return ErrManagerInactive } func (m *Manager) IsActive() int { @@ -85,12 +99,11 @@ func (c *Connection) Timeout() (time.Duration, error) { c.Lock() defer c.Unlock() if int(c.Attempts) < c.MaxAttempts { - c.Attempts += 1 - // 50, 100, 200... to := time.Duration(50*math.Pow(2, c.Attempts)) * time.Millisecond + c.Attempts += 1 return to, nil } - return 0, errors.New("Connection Failed") + return 0, ErrMaxAttemptsExceeded } func (c *Connection) ResetConnections() { diff --git a/internal/pkg/manager/manager_test.go b/internal/pkg/manager/manager_test.go new file mode 100644 index 0000000..5221a6b --- /dev/null +++ b/internal/pkg/manager/manager_test.go @@ -0,0 +1,206 @@ +package manager + +import ( + "math/rand" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +// creating, starting and stopping tests + +// newManager is a helper for creating new managers for tests +func newManager(conn int, want error, t *testing.T) *Manager { + var manager *Manager + var err error + assert := assert.New(t) + + manager, err = New(conn) + + if err != want { + t.Fatalf( + `New(%d) = %v, %v, %d max connections failed`, + conn, + manager, + err, + conn, + ) + } + + assert.Equal(manager.IsActive(), 0, "manager should start inactive") + + return manager +} + +// TestEmptyManager creates a new manager with 0 max connections +func TestEmptyManager(t *testing.T) { + conn := 0 + newManager(conn, nil, t) +} + +// TestPostiveManager creates a new manager with valid max connections +func TestPositiveManager(t *testing.T) { + conn := rand.Intn(MaxConnAttempts) + newManager(conn, nil, t) +} + +// TestNegativeManager creates a new manager with negative max connections +func TestNegativeManager(t *testing.T) { + conn := -1 * rand.Intn(MaxConnAttempts) + newManager(conn, ErrInvalidMaxConn, t) +} + +// TestInvalidManager creates a new manager with large max connections +func TestInvalidManager(t *testing.T) { + conn := MaxConnAttempts + 0xf + newManager(conn, ErrInvalidMaxConn, t) +} + +// TestManagerLifeCycle tests that a manager can start and exit several times +func TestManagerLifeCycle(t *testing.T) { + + var manager *Manager + assert := assert.New(t) + + conn := rand.Intn(MaxConnAttempts) + manager = newManager(conn, nil, t) + + cycles := 10 + + // starting and stopping sequentially + for i := 0; i < cycles; i++ { + + assert.NoError(manager.Start(), "starting manager failed") + assert.Equal(manager.IsActive(), 1, "manager is inactive after start") + + assert.NoError(manager.Stop(), "stopping manager failed") + assert.Equal(manager.IsActive(), 0, "manager is active after stop") + } +} + +// TestManagerStopFail tests that stopping an inactive manager will error +func TestManagerStopFail(t *testing.T) { + + var manager *Manager + assert := assert.New(t) + + conn := rand.Intn(MaxConnAttempts) + manager = newManager(conn, nil, t) + + assert.NoError(manager.Start(), "starting manager failed") + + // stopping sequentially + assert.NoError(manager.Stop(), "stopping manager failed") + assert.Error(manager.Stop(), "stopping inactive manager should fail") +} + +// TestManagerStartFail tests that starting an active manager will error +func TestManagerStartFail(t *testing.T) { + + var manager *Manager + assert := assert.New(t) + + conn := rand.Intn(MaxConnAttempts) + manager = newManager(conn, nil, t) + + // starting sequentially + assert.NoError(manager.Start(), "starting manager failed") + assert.Error(manager.Start(), "starting active manager should fail") +} + +// auxiliary tests + +// TestManagerTimeout checks that timeouts exponentially backoff +func TestManagerTimeout(t *testing.T) { + var manager *Manager + assert := assert.New(t) + + conn := 10 + manager = newManager(conn, nil, t) + + assert.NoError(manager.Start(), "starting manager failed") + assert.Equal(manager.IsActive(), 1, "manager is inactive") + + prevTimeout, err := manager.Timeout() + + for i := 1; i <= conn; i++ { + assert.NoError(err, "generating timeout failed") + assert.True(prevTimeout > 0, "invalid timeout") + + timeout, err := manager.Timeout() + + if i == conn { + assert.Error(err, "allowed exceeding max attempts") + } else { + assert.NoError(err, "generating timeout failed") + assert.True( + timeout == 2*prevTimeout, + "incorrect timeout %d, expected %d", + timeout, 2*prevTimeout, + ) + } + + prevTimeout = timeout + } +} + +// TestManagerHB tests the heartbeat channel opens and closes +func TestManagerHB(t *testing.T) { + + var manager *Manager + assert := assert.New(t) + + conn := rand.Intn(MaxConnAttempts) + manager = newManager(conn, nil, t) + + assert.NoError(manager.Start(), "starting manager failed") + assert.Equal(manager.IsActive(), 1, "manager is inactive") + + ch := make(chan struct{}) + + go manager.HeartBeat(ch, 10, 0, time.Millisecond) + + for range ch { + // close on first ping + assert.NoError(manager.Stop(), "stopping manager failed") + } + + assert.Equal(manager.IsActive(), 0, "manager is active") +} + +// TestManagerHBTiming tests the heartbeat channel timing is correct +func TestManagerHBTiming(t *testing.T) { + + var manager *Manager + assert := assert.New(t) + + conn := rand.Intn(MaxConnAttempts) + manager = newManager(conn, nil, t) + + assert.NoError(manager.Start(), "starting manager failed") + assert.Equal(manager.IsActive(), 1, "manager is inactive") + + ch := make(chan struct{}) + hb := 100 + pings := 10 + + // expected time with some tolerance for other events + expected := time.Duration(pings*hb+5) * time.Millisecond + + go manager.HeartBeat(ch, hb, 0, time.Millisecond) + + iter := 0 + start := time.Now() + for range ch { + // close after 10 pings + iter += 1 + if iter >= pings { + assert.NoError(manager.Stop(), "stopping manager failed") + } + } + end := time.Now() + + assert.Equal(manager.IsActive(), 0, "manager is active") + assert.WithinDuration(start, end, expected, "inaccurate heartbeat") +} From 8936570c59e72fee9daddd7eaca19142713174f9 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Wed, 14 Jun 2023 13:07:35 -0400 Subject: [PATCH 04/44] improved manager test coverage --- internal/pkg/manager/manager_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/pkg/manager/manager_test.go b/internal/pkg/manager/manager_test.go index 5221a6b..5a7096c 100644 --- a/internal/pkg/manager/manager_test.go +++ b/internal/pkg/manager/manager_test.go @@ -185,10 +185,10 @@ func TestManagerHBTiming(t *testing.T) { hb := 100 pings := 10 - // expected time with some tolerance for other events - expected := time.Duration(pings*hb+5) * time.Millisecond + // expected time with tolerance for other logic and worst case rand timeout + expected := time.Duration(pings*hb+15) * time.Millisecond - go manager.HeartBeat(ch, hb, 0, time.Millisecond) + go manager.HeartBeat(ch, hb, 1, time.Millisecond) iter := 0 start := time.Now() From 4914b9340866023666ef648a94e881e61ddc951e Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Wed, 14 Jun 2023 13:56:01 -0400 Subject: [PATCH 05/44] documented manager --- internal/pkg/manager/manager.go | 105 +++++++++++++++++---------- internal/pkg/manager/manager_test.go | 16 ++-- 2 files changed, 73 insertions(+), 48 deletions(-) diff --git a/internal/pkg/manager/manager.go b/internal/pkg/manager/manager.go index fe316c9..314f7f7 100644 --- a/internal/pkg/manager/manager.go +++ b/internal/pkg/manager/manager.go @@ -1,3 +1,4 @@ +// Package manager provides basic manager functions to embed into higher level managers package manager import ( @@ -9,23 +10,6 @@ import ( "time" ) -// max allowable connection attempts is 255 -const MaxConnAttempts = 0xFF - -// basic manager for starting/stopping checks plus built in heartbeat for downtime detection - -type Connection struct { - Attempts float64 // float for pow - MaxAttempts int // max allowed - sync.Mutex -} - -type Manager struct { - *Connection // embedded for timeout stuff - Active int32 // atomic checks -} - -// errors var ( ErrInvalidMaxConn = errors.New("invalid max connection attempts") ErrManagerInactive = errors.New("manager inactive") @@ -33,22 +17,48 @@ var ( ErrMaxAttemptsExceeded = errors.New("max connection attempts exceeded") ) +// ManagerStatus is used as an enum for the current status +// Could be expanded to include others such as killed, sleeping, etc. +type ManagerStatus int + +const ( + Inactive ManagerStatus = 0 + Active ManagerStatus = 1 +) + +// MaxConnAttempts is the maximum allowable connection attempts +// Limited to 255 to prevent excessive timeout scaling +const MaxConnAttempts = 0xFF + +// Manager is a general purpose structure to implement basic capabilities +// Tracks its state via Active, modifying it on starts and stops +// Embeds a connection to be used in generating timeouts +type Manager struct { + *connection // embedded for timeout stuff + active int32 // atomic checks +} + +// New creates a new manager with the maxConn maximum attempts +// Throws ErrInvalidMaxConn if maxConn is not in [0, MaxConnAttempts] func New(maxConn int) (*Manager, error) { if maxConn < 0 || maxConn > MaxConnAttempts { return &Manager{}, ErrInvalidMaxConn } - c := &Connection{MaxAttempts: maxConn} + c := &connection{MaxAttempts: maxConn} + m := &Manager{ - Connection: c, + connection: c, } return m, nil } +// Start attempts to start the manager +// Throws ErrManagerActive error if the manager is already active func (m *Manager) Start() error { - if atomic.CompareAndSwapInt32(&m.Active, 0, 1) { + if atomic.CompareAndSwapInt32(&m.active, 0, 1) { m.ResetConnections() return nil } @@ -56,57 +66,72 @@ func (m *Manager) Start() error { return ErrManagerActive } +// Stop attempts to stop the manager +// Throws ErrManagerInactive error if the manager is already inactive func (m *Manager) Stop() error { - if atomic.CompareAndSwapInt32(&m.Active, 1, 0) { + if atomic.CompareAndSwapInt32(&m.active, 1, 0) { return nil } return ErrManagerInactive } -func (m *Manager) IsActive() int { - return int(atomic.LoadInt32(&m.Active)) +// IsActive returns the current ManagerStatus +func (m *Manager) IsActive() ManagerStatus { + return ManagerStatus(atomic.LoadInt32(&m.active)) } -// Heartbeat tracker +// HeartBeat will send an empty struct over ping every hb (scale) +// The pings are sent ever (hb + rand(interval)) * scale +// Where scale is typically time.Millisecond, time.Second etc. +// Will close the channel on exit to prevent leaks +func (m *Manager) HeartBeat( + ping chan struct{}, + hb, interval int, + scale time.Duration) { -func (m *Manager) HeartBeat(ping chan struct{}, hb, interval int, scale time.Duration) { - // pings channel every (HB + randInterval) * time.Duration - // can be used anywhere a heartbeat is needed - // closes channel on exit + for m.IsActive() == Active { + ping <- struct{}{} - if interval > 0 { - rand.Seed(time.Now().UnixNano()) - } - - for atomic.LoadInt32(&m.Active) > 0 { - // atomoic read may cause memory leak, can revisit - ping <- struct{}{} // no mem sleep := time.Duration(hb-interval) * scale + if interval > 0 { sleep += time.Duration(rand.Intn(2*interval)) * scale } + time.Sleep(sleep) } - // exited, close chan + close(ping) } -// connection timeout generator +// connection keeps track of maximum and current number of connection attempts +// Concurrency safe as it is protected by a mutex +type connection struct { + Attempts float64 + MaxAttempts int + sync.Mutex +} + +// Timeout returns an exponentially decaying timeout based on attempts +// Returns timeout of type time.Duration in milliseconds +// Returns ErrMaxAttemptsExceeded if too many attempts are tried +func (c *connection) Timeout() (time.Duration, error) { -func (c *Connection) Timeout() (time.Duration, error) { - // exponential backoff c.Lock() defer c.Unlock() + if int(c.Attempts) < c.MaxAttempts { to := time.Duration(50*math.Pow(2, c.Attempts)) * time.Millisecond c.Attempts += 1 return to, nil } + return 0, ErrMaxAttemptsExceeded } -func (c *Connection) ResetConnections() { +// ResetConnections sets the current connection attempts back to 0 +func (c *connection) ResetConnections() { c.Lock() defer c.Unlock() c.Attempts = 0 diff --git a/internal/pkg/manager/manager_test.go b/internal/pkg/manager/manager_test.go index 5a7096c..017ad24 100644 --- a/internal/pkg/manager/manager_test.go +++ b/internal/pkg/manager/manager_test.go @@ -28,7 +28,7 @@ func newManager(conn int, want error, t *testing.T) *Manager { ) } - assert.Equal(manager.IsActive(), 0, "manager should start inactive") + assert.Equal(manager.IsActive(), Inactive, "manager should start inactive") return manager } @@ -72,10 +72,10 @@ func TestManagerLifeCycle(t *testing.T) { for i := 0; i < cycles; i++ { assert.NoError(manager.Start(), "starting manager failed") - assert.Equal(manager.IsActive(), 1, "manager is inactive after start") + assert.Equal(manager.IsActive(), Active, "manager inactive after start") assert.NoError(manager.Stop(), "stopping manager failed") - assert.Equal(manager.IsActive(), 0, "manager is active after stop") + assert.Equal(manager.IsActive(), Inactive, "manager active after stop") } } @@ -120,7 +120,7 @@ func TestManagerTimeout(t *testing.T) { manager = newManager(conn, nil, t) assert.NoError(manager.Start(), "starting manager failed") - assert.Equal(manager.IsActive(), 1, "manager is inactive") + assert.Equal(manager.IsActive(), Active, "manager is inactive") prevTimeout, err := manager.Timeout() @@ -155,7 +155,7 @@ func TestManagerHB(t *testing.T) { manager = newManager(conn, nil, t) assert.NoError(manager.Start(), "starting manager failed") - assert.Equal(manager.IsActive(), 1, "manager is inactive") + assert.Equal(manager.IsActive(), Active, "manager is inactive") ch := make(chan struct{}) @@ -166,7 +166,7 @@ func TestManagerHB(t *testing.T) { assert.NoError(manager.Stop(), "stopping manager failed") } - assert.Equal(manager.IsActive(), 0, "manager is active") + assert.Equal(manager.IsActive(), Inactive, "manager is active") } // TestManagerHBTiming tests the heartbeat channel timing is correct @@ -179,7 +179,7 @@ func TestManagerHBTiming(t *testing.T) { manager = newManager(conn, nil, t) assert.NoError(manager.Start(), "starting manager failed") - assert.Equal(manager.IsActive(), 1, "manager is inactive") + assert.Equal(manager.IsActive(), Active, "manager is inactive") ch := make(chan struct{}) hb := 100 @@ -201,6 +201,6 @@ func TestManagerHBTiming(t *testing.T) { } end := time.Now() - assert.Equal(manager.IsActive(), 0, "manager is active") + assert.Equal(manager.IsActive(), Inactive, "manager is active") assert.WithinDuration(start, end, expected, "inaccurate heartbeat") } From b622ac321d8944622fdc04f71ad1dbe335f265cd Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Wed, 14 Jun 2023 14:09:25 -0400 Subject: [PATCH 06/44] fixed documentation issues --- internal/pkg/manager/manager.go | 50 ++++++++++++++++----------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/internal/pkg/manager/manager.go b/internal/pkg/manager/manager.go index 314f7f7..3a8db51 100644 --- a/internal/pkg/manager/manager.go +++ b/internal/pkg/manager/manager.go @@ -1,4 +1,4 @@ -// Package manager provides basic manager functions to embed into higher level managers +// Package manager provides basic manager functions to embed into higher level managers. package manager import ( @@ -17,7 +17,7 @@ var ( ErrMaxAttemptsExceeded = errors.New("max connection attempts exceeded") ) -// ManagerStatus is used as an enum for the current status +// ManagerStatus is used as an enum for the current status. // Could be expanded to include others such as killed, sleeping, etc. type ManagerStatus int @@ -26,20 +26,20 @@ const ( Active ManagerStatus = 1 ) -// MaxConnAttempts is the maximum allowable connection attempts -// Limited to 255 to prevent excessive timeout scaling +// MaxConnAttempts is the maximum allowable connection attempts. +// Limited to 255 to prevent excessive timeout scaling. const MaxConnAttempts = 0xFF -// Manager is a general purpose structure to implement basic capabilities -// Tracks its state via Active, modifying it on starts and stops -// Embeds a connection to be used in generating timeouts +// Manager is a general purpose structure to implement basic capabilities. +// Stores state in active variable, modified through atomic swaps. +// Embeds a connection to be used in generating timeouts. type Manager struct { - *connection // embedded for timeout stuff - active int32 // atomic checks + *connection + active int32 } -// New creates a new manager with the maxConn maximum attempts -// Throws ErrInvalidMaxConn if maxConn is not in [0, MaxConnAttempts] +// New creates a new manager with the maxConn maximum attempts. +// Throws ErrInvalidMaxConn if maxConn is not in [0, MaxConnAttempts]. func New(maxConn int) (*Manager, error) { if maxConn < 0 || maxConn > MaxConnAttempts { @@ -55,8 +55,8 @@ func New(maxConn int) (*Manager, error) { return m, nil } -// Start attempts to start the manager -// Throws ErrManagerActive error if the manager is already active +// Start attempts to start the manager. +// Throws ErrManagerActive error if the manager is already active. func (m *Manager) Start() error { if atomic.CompareAndSwapInt32(&m.active, 0, 1) { m.ResetConnections() @@ -66,8 +66,8 @@ func (m *Manager) Start() error { return ErrManagerActive } -// Stop attempts to stop the manager -// Throws ErrManagerInactive error if the manager is already inactive +// Stop attempts to stop the manager. +// Throws ErrManagerInactive error if the manager is already inactive. func (m *Manager) Stop() error { if atomic.CompareAndSwapInt32(&m.active, 1, 0) { return nil @@ -76,15 +76,15 @@ func (m *Manager) Stop() error { return ErrManagerInactive } -// IsActive returns the current ManagerStatus +// IsActive returns the current ManagerStatus. func (m *Manager) IsActive() ManagerStatus { return ManagerStatus(atomic.LoadInt32(&m.active)) } -// HeartBeat will send an empty struct over ping every hb (scale) -// The pings are sent ever (hb + rand(interval)) * scale +// HeartBeat will send an empty struct over ping every hb (scale). +// The pings are sent ever (hb + rand(interval)) * scale. // Where scale is typically time.Millisecond, time.Second etc. -// Will close the channel on exit to prevent leaks +// Will close the channel on exit to prevent leaks. func (m *Manager) HeartBeat( ping chan struct{}, hb, interval int, @@ -105,17 +105,17 @@ func (m *Manager) HeartBeat( close(ping) } -// connection keeps track of maximum and current number of connection attempts -// Concurrency safe as it is protected by a mutex +// connection keeps track of maximum and current number of connection attempts. +// Concurrency safe as it is protected by a mutex. type connection struct { Attempts float64 MaxAttempts int sync.Mutex } -// Timeout returns an exponentially decaying timeout based on attempts -// Returns timeout of type time.Duration in milliseconds -// Returns ErrMaxAttemptsExceeded if too many attempts are tried +// Timeout returns an exponentially decaying timeout based on attempts. +// Returns timeout of type time.Duration in milliseconds. +// Returns ErrMaxAttemptsExceeded if too many attempts are tried. func (c *connection) Timeout() (time.Duration, error) { c.Lock() @@ -130,7 +130,7 @@ func (c *connection) Timeout() (time.Duration, error) { return 0, ErrMaxAttemptsExceeded } -// ResetConnections sets the current connection attempts back to 0 +// ResetConnections sets the current connection attempts back to 0. func (c *connection) ResetConnections() { c.Lock() defer c.Unlock() From 25f75e705c5c2792dd4fa57d8dda2c93a9756b7c Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Wed, 14 Jun 2023 14:10:16 -0400 Subject: [PATCH 07/44] cleaning up old files --- internal/pkg/server/manager.go | 29 --- internal/pkg/server/reactormanager.go | 64 ------ internal/pkg/server/system.go | 309 -------------------------- 3 files changed, 402 deletions(-) delete mode 100644 internal/pkg/server/manager.go delete mode 100644 internal/pkg/server/system.go diff --git a/internal/pkg/server/manager.go b/internal/pkg/server/manager.go deleted file mode 100644 index 58ecbcb..0000000 --- a/internal/pkg/server/manager.go +++ /dev/null @@ -1,29 +0,0 @@ -package server - -import ( - //"log" - - _ "context" -) - -// will condense into the rm soon enough -// manager connects to client on start and returns the gRPC connection to make gRPC clients - -// type ClientManager struct { -// *Client // gives access to c.Ip c.Id etc -// Hb time.Duration // used for managing hb timer for client -// Sig chan bool -// sync.Mutex -// } - -// func NewClientManager(cl *Client) *ClientManager { -// return &ClientManager{Client: cl} -// } - -// func (m *ClientManager) UpdateClient(cl *Client) error { -// m.Lock() -// defer m.Unlock() -// logging.Debug(logging.DClient, "MAN Updating client %v", cl.Id) -// m.Client = cl -// return nil -// } diff --git a/internal/pkg/server/reactormanager.go b/internal/pkg/server/reactormanager.go index d31108f..85c8914 100644 --- a/internal/pkg/server/reactormanager.go +++ b/internal/pkg/server/reactormanager.go @@ -37,12 +37,6 @@ type ReactorManager struct { Err chan error } -// type ReactorDevices struct { -// // device struct -// Devices map[int]DeviceManager -// sync.RWMutex -// } - func NewReactorManager(cl *Client, config *viper.Viper, errCh chan error) *ReactorManager { // making managers m := NewManager(6) @@ -89,61 +83,3 @@ func (r *ReactorManager) ReactorStatusHandler(ctx context.Context, req *pb.React return &pb.ReactorStatusResponse{Id: int32(r.Id)}, nil } - -// // device stuff - -// type DeviceManager interface { -// LoadConfig() error -// UpdateStatus(string) error -// String() string // printing -// } - -// func NewDeviceManager(addr int, config *viper.Viper, prefix string) (DeviceManager, error) { -// // returns a manager struct -// return device.NewDeviceManager(addr, config, prefix) -// } - -//func (r *ReactorManager) UpdateDevices(devs []*pb.Device) { -// // pass updates to correct manager -// r.ReactorDevices.RLock() // read lock only -// defer r.ReactorDevices.RUnlock() - -// for _, dev := range devs { -// // looping over devs -// if dm, ok := r.ReactorDevices.Devices[int(dev.GetAddr())]; ok { -// // device manager found -// go dm.UpdateStatus(dev.GetStatus().String()) -// //fmt.Println(dm) -// } else { -// // not found -// go r.AddDevice(dev, r.Id, r.Config, r.Err) -// } -// } -//} - -// func (r *ReactorDevices) AddDevice(dev *pb.Device, id int, config *viper.Viper, errCh chan error) { - -// // setting vars -// prefix := fmt.Sprintf("reactors.%d.", id) -// addr := int(dev.GetAddr()) -// var dm DeviceManager -// var err error -// // write locking -// r.Lock() -// defer r.Unlock() - -// if dm, err = NewDeviceManager(addr, config, prefix); err != nil { -// errCh <- err -// } - -// // setting status -// if err = dm.UpdateStatus(dev.GetStatus().String()); err != nil { -// errCh <- err -// } - -// // loading config -// if err = dm.LoadConfig(); err != nil { -// errCh <- err -// } -// r.Devices[int(addr)] = dm -// } diff --git a/internal/pkg/server/system.go b/internal/pkg/server/system.go deleted file mode 100644 index 11d1c3f..0000000 --- a/internal/pkg/server/system.go +++ /dev/null @@ -1,309 +0,0 @@ -package server - -import ( - _ "fmt" - // sensor components -) - -/* - -type StatusMonitor struct { - // allows for embedding into managers - TransactionId chan uint32 // monotonically increases to track outdated reqs - DevChan chan *DeviceInfo // channel for device status - ReactorChan chan *DeviceInfo // channel for reactor status - *SystemViewer - DevBuf *devbuf - sync.Mutex -} - -type devbuf struct { - ReactorId int // reactor we are looking at, if any - Buf map[string]map[int]*DeviceInfo // convienent way to store/seperate device data - sync.Mutex -} - -func NewBuffer() map[string]map[int]*DeviceInfo { - rbuf := make(map[int]*DeviceInfo) - dbuf := make(map[int]*DeviceInfo) - sbuf := make(map[string]map[int]*DeviceInfo) - sbuf["Reactor"] = rbuf - sbuf["Device"] = dbuf - return sbuf -} - -func NewStatusMonitor(t string, id int, sys *SystemViewer) *StatusMonitor { - tid := make(chan uint32) - sm := &StatusMonitor{TransactionId: tid} - sm.SystemViewer = sys - logging.Debug(logging.DClient, "SYS Creating new status monitor") - if t == "Reactor" { - // reactor status monitor - sm.ReactorChan = sys.AddReactorSender() - sm.DevChan = sys.AddDeviceSender(id) - go sm.GenerateIds() - } else { - // tui status monitor - sbuf := NewBuffer() - //sm.ReactorChan, sbuf["Reactor"] = sys.AddListener(id,0) - sm.DevBuf = &devbuf{Buf: sbuf} // makes it easier to work with - go sm.UpdateListener(id, 0) - } - return sm -} - -func (s *StatusMonitor) GenerateIds() { - var id uint32 - id = 0 - for { - s.TransactionId <- id - id += 1 - } -} - -func (s *StatusMonitor) Send(d *DeviceInfo, dtype string) { - d.TransactionId = <-s.TransactionId - logging.Debug(logging.DClient, "SYS 01 Sending update for: %s (%s)", d.Type, d.Status) - if dtype == "Reactor" { - s.ReactorChan <- d - } else { - s.DevChan <- d - } -} - -func (s *StatusMonitor) GetBuffer() []*DeviceInfo { - // also clears buffer - s.DevBuf.Lock() - defer s.DevBuf.Unlock() - res := []*DeviceInfo{} - if len(s.DevBuf.Buf["Reactor"]) != 0 || len(s.DevBuf.Buf["Device"]) != 0 { - logging.Debug(logging.DClient, "Clearing buff %v", s.DevBuf.Buf) - } - for _, devs := range s.DevBuf.Buf { - for _, dev := range devs { - // loops over reactors then devices - res = append(res, dev) - } - } - s.DevBuf.Buf = NewBuffer() // clearing old buffer - return res -} - -func (s *StatusMonitor) UpdateListener(tid, reactorId uint32) { - s.DevBuf.Lock() - defer s.DevBuf.Unlock() - // clearing proper buffer - if reactorId == 0 { - logging.Debug(logging.DClient, "SYS 01 Adding %v as reactor listener", tid) - s.DevBuf.Buf["Reactor"] = make(map[uint32]*DeviceInfo) - s.ReactorChan, s.DevBuf.Buf["Reactor"] = s.SystemViewer.AddListener(tid, reactorId) - go s.Listen(s.ReactorChan) - } else { - logging.Debug(logging.DClient, "SYS 01 Adding %v as reactor %v listener", tid, reactorId) - s.DevBuf.Buf["Device"] = make(map[uint32]*DeviceInfo) // clearing old devices - if s.DevBuf.ReactorId != reactorId && s.DevBuf.ReactorId != 0 { - go s.SystemViewer.RemoveListener(s.DevBuf.ReactorId, tid) - } - s.DevBuf.ReactorId = reactorId - s.DevChan, s.DevBuf.Buf["Device"] = s.SystemViewer.AddListener(tid, reactorId) - go s.Listen(s.DevChan) - } -} - -func (s *StatusMonitor) UpdateBuffer(d *DeviceInfo, dtype string, ch chan *DeviceInfo) { - s.DevBuf.Lock() - defer s.DevBuf.Unlock() - logging.Debug(logging.DClient, "SYS 01 Dev %v update requested", d) - if dev, exists := s.DevBuf.Buf[dtype][d.Id]; exists { - // already a device in the buffer - if dev.TransactionId > d.TransactionId { - logging.Debug(logging.DClient, "SYS 01 Update Processed. Old: %v, New: %v \n", dev, d) - d = dev // not sure if i can do this lol - } - } - if ch == s.ReactorChan || ch == s.DevChan { - // hacky way to check if the device came from a listener of a current channel - s.DevBuf.Buf[dtype][d.Id] = d - } else { - logging.Debug(logging.DClient, "Dev out of date!") - } -} - -func (s *StatusMonitor) Listen(ch chan *DeviceInfo) { - for dev := range ch { - if dev.Type == "Reactor" { - go s.UpdateBuffer(dev, "Reactor", ch) - } else { - go s.UpdateBuffer(dev, "Device", ch) - } - } -} - -type InfoStream struct { - // base stream for any reactor/device - // NewSender embedds the channel to send updates on - // NewListener will add the statusmon to the list of devs to echo to - Stream chan *DeviceInfo - Layout *syslayout - *listeners -} - -type listeners struct { - sync.RWMutex - Listeners map[uint32]*lischan -} - -type lischan struct { - sync.WaitGroup - StatusChan chan *DeviceInfo -} - -type syslayout struct { - Devs map[uint32]*DeviceInfo - uint32 //index - sync.RWMutex -} - -func NewLisChan(ch chan *DeviceInfo) *lischan { - l := &lischan{StatusChan: ch} - return l -} - -func NewInfoStream() *InfoStream { - dch := make(chan *DeviceInfo) - s := &InfoStream{Stream: dch} - m := make(map[uint32]*DeviceInfo) - s.Layout = &syslayout{Devs: m} - s.listeners = &listeners{Listeners: make(map[uint32]*lischan)} - return s -} - -func (s *InfoStream) Start() { - // consistency - go s.Listen() -} - -// goal is to hook every new manager into the reactor status chan -func (s *InfoStream) AddSender() chan *DeviceInfo { - return s.Stream -} - -func (s *InfoStream) Listen() { - for deviceInfo := range s.Stream { - go s.Update(deviceInfo) - } -} - -func (s *InfoStream) Update(d *DeviceInfo) { - s.Layout.Lock() - defer s.Layout.Unlock() - if dev, ok := s.Layout.Devs[d.Id]; !ok { - d.Index = s.Layout.uint32 - s.Layout.uint32 += 1 - } else { - d.Index = dev.Index - } - s.Layout.Devs[d.Id] = d - go s.Echo(d) -} - -func (l *listeners) Echo(d *DeviceInfo) { - l.RLock() - defer l.RUnlock() - // read only lock - for _, lis := range l.Listeners { - lis.Add(1) - go func(listener *lischan, dev *DeviceInfo) { - defer listener.Done() - listener.StatusChan <- dev - }(lis, d) - } -} - -func (s *InfoStream) AddListener(id int, ch chan *DeviceInfo) map[uint32]*DeviceInfo { - // if i get a memory leak ill eat my shoe - s.listeners.Lock() - defer s.listeners.Unlock() - if _, ok := s.listeners.Listeners[id]; ok { - // exists - go s.RemoveListener(id) - } - s.listeners.Listeners[id] = NewLisChan(ch) - logging.Debug(logging.DClient, "SYS 01 Getting Devices") - //s.Layout.RLock() - //defer s.Layout.RUnlock() - logging.Debug(logging.DClient, "SYS 01 Returning Devices %v", s.Layout.Devs) - return s.Layout.Devs -} - -func (l *listeners) RemoveListener(id int) { - l.Lock() - defer l.Unlock() - if lis, ok := l.Listeners[id]; ok { - delete(l.Listeners, id) - go func(ls *lischan) { - ls.Wait() - close(ls.StatusChan) - }(lis) - } -} - -// status buffer maintaince - -type SystemViewer struct { - // stores system directory and provide methods to be embedded in managers - ReactorStream *InfoStream // can add itself as a listener to provide methods - DeviceStream *ds -} - -type ds struct { - Reactors map[uint32]*InfoStream //map from reactor id to its device info stream - sync.Mutex -} - -func NewSystemViewer() *SystemViewer { - rs := NewInfoStream() - s := &SystemViewer{ReactorStream: rs} - m := make(map[uint32]*InfoStream) - s.DeviceStream = &ds{Reactors: m} - return s -} - -func (s *SystemViewer) Start() { - go s.ReactorStream.Start() -} - -func (s *SystemViewer) AddReactorSender() chan *DeviceInfo { - return s.ReactorStream.AddSender() -} - -func (s *SystemViewer) AddDeviceSender(reactorId uint32) chan *DeviceInfo { - s.DeviceStream.Lock() - defer s.DeviceStream.Unlock() - var ds *InfoStream - var ok bool - if ds, ok = s.DeviceStream.Reactors[reactorId]; !ok { - ds = NewInfoStream() - s.DeviceStream.Reactors[reactorId] = ds - go ds.Start() - } - return ds.AddSender() -} - -func (s *SystemViewer) AddListener(id, rid int) (chan *DeviceInfo, map[uint32]*DeviceInfo) { - // returns a listener for that chan - ch := make(chan *DeviceInfo) - if rid != 0 { - return ch, s.DeviceStream.Reactors[rid].AddListener(id, ch) - } else { - return ch, s.ReactorStream.AddListener(id, ch) - } -} - -func (s *SystemViewer) RemoveListener(rid, tid int) { - // removes chan for specific tid and rid - s.DeviceStream.Lock() - defer s.DeviceStream.Unlock() - go s.DeviceStream.Reactors[rid].RemoveListener(tid) -} -*/ From 28ceb0cc319f61be4a76aa0bdff15813893ec1fc Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Wed, 14 Jun 2023 14:12:19 -0400 Subject: [PATCH 08/44] trimming garbage code from file --- internal/pkg/server/reactormanager.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/pkg/server/reactormanager.go b/internal/pkg/server/reactormanager.go index 85c8914..0ef6bc3 100644 --- a/internal/pkg/server/reactormanager.go +++ b/internal/pkg/server/reactormanager.go @@ -18,11 +18,11 @@ import ( type Manager interface { Start() error // status checks - Exit() error + Stop() error Timeout() (time.Duration, error) // TO Generator } -func NewManager(max int) Manager { +func NewManager(max int) (Manager, error) { // takes a heartbeat and max connection attempts return manager.New(max) } @@ -37,30 +37,31 @@ type ReactorManager struct { Err chan error } -func NewReactorManager(cl *Client, config *viper.Viper, errCh chan error) *ReactorManager { +func NewReactorManager(cl *Client, + config *viper.Viper, + errCh chan error, +) (*ReactorManager, error) { // making managers - m := NewManager(6) + m, err := NewManager(6) r := &ReactorManager{ Manager: m, Client: cl, Config: config, Err: errCh, } - return r + return r, err } func (r *ReactorManager) Start() error { // allows for extra stuff logging.Debug(logging.DStart, "RMA %v starting", r.Id) return r.Manager.Start() - //go r.StatusMon.Send(&DeviceInfo{Id: r.Id, Type: "Reactor", Status: "[green]ONLINE[white]"}, "Reactor") } func (r *ReactorManager) Exit() error { // allows for extra stuff logging.Debug(logging.DExit, "RMA %v exiting", r.Id) return r.Manager.Exit() - //go r.StatusMon.Send(&DeviceInfo{Id: r.Id, Type: "Reactor", Status: "[red]OFFLINE[white]", Data: fmt.Sprintf("Last Seen %v", time.Now().Format("Mon at 03:04:05pm MST"))}, "Reactor") } func (r *ReactorManager) UpdateClient(cl *Client) error { @@ -72,7 +73,6 @@ func (r *ReactorManager) UpdateClient(cl *Client) error { func (r *ReactorManager) ReactorStatusHandler(ctx context.Context, req *pb.ReactorStatusPing) (*pb.ReactorStatusResponse, error) { // function client will call to update reactor information - //go r.PingReset() fmt.Printf("Recieved ping from %d!\n", req.GetId()) // update devices/sensors for _, dev := range req.GetDevices() { From 20d1cd317c100af39c49ea303fa8e1b9e530a0cb Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Wed, 14 Jun 2023 14:37:35 -0400 Subject: [PATCH 09/44] documented reactormanager --- internal/pkg/server/reactormanager.go | 83 ++++++++++++++++++--------- 1 file changed, 56 insertions(+), 27 deletions(-) diff --git a/internal/pkg/server/reactormanager.go b/internal/pkg/server/reactormanager.go index 0ef6bc3..5c88dcc 100644 --- a/internal/pkg/server/reactormanager.go +++ b/internal/pkg/server/reactormanager.go @@ -6,80 +6,109 @@ import ( "FRMS/internal/pkg/manager" "time" - //"FRMS/internal/pkg/device" "context" - "fmt" - _ "log" "github.com/spf13/viper" ) -// manager stuff +// MaxConnectionAttempts is the max number of tries to allow +// when connecting to a reactor. +const MaxConnectionAttempts = 10 +// Manager is an interface requiring a structure that can be started +// and stopped as well as provide timeouts in milliseconds. type Manager interface { Start() error // status checks Stop() error Timeout() (time.Duration, error) // TO Generator } +// NewManager returns a manager fulfilling the Manager interface as well as +// any potential errors. func NewManager(max int) (Manager, error) { - // takes a heartbeat and max connection attempts return manager.New(max) } +// ReactorManager contains a base manager, client, global +// config, and error channel. +// The ReactorManager can be started/stopped as clients connect/disconnect. +// Also serves as handler for gRPC requests from reactors. +// Can be extended to write changes to config. type ReactorManager struct { Manager // base manager interface - // *ClientManager // client manager (OUTDATED) - *Client // access to ID etc - // StatusMon *StatusMonitor putting on pause - // *ReactorDevices - Config *viper.Viper // config to update + *Client + Config *viper.Viper // global config to maintain Err chan error } -func NewReactorManager(cl *Client, +// NewReactorManager takes in a client, config and channel to pass errors on. +// Returns a new reactor manager as well as any errors that occured during +// creation. +// Uses MaxConnectionAttempts which defaults to 10 to prevent +// unnessecary network load and/or timeout lengths. +func NewReactorManager( + cl *Client, config *viper.Viper, errCh chan error, ) (*ReactorManager, error) { - // making managers - m, err := NewManager(6) + + m, err := NewManager(MaxConnectionAttempts) + + if err != nil { + return &ReactorManager{}, err + } + r := &ReactorManager{ Manager: m, Client: cl, Config: config, Err: errCh, } + return r, err } +// Start logs the start and calls start on the embedded manager. func (r *ReactorManager) Start() error { - // allows for extra stuff logging.Debug(logging.DStart, "RMA %v starting", r.Id) return r.Manager.Start() } -func (r *ReactorManager) Exit() error { - // allows for extra stuff - logging.Debug(logging.DExit, "RMA %v exiting", r.Id) - return r.Manager.Exit() +// Stop logs the stop and calls stop on the embedded manager. +func (r *ReactorManager) Stop() error { + logging.Debug(logging.DExit, "RMA %v stopping", r.Id) + return r.Manager.Stop() } +// UpdateClient is used to change the underlying manager client if there +// changes to its data. +// +// BUG(Keegan): Client is not protected by a lock and may lead to races func (r *ReactorManager) UpdateClient(cl *Client) error { - // this is probably unnessecary - fmt.Printf("Reactor Manager %d updating client!\n", r.Id) + logging.Debug(logging.DClient, "RMA %v updating client", r.Id) r.Client = cl return nil } -func (r *ReactorManager) ReactorStatusHandler(ctx context.Context, req *pb.ReactorStatusPing) (*pb.ReactorStatusResponse, error) { - // function client will call to update reactor information - fmt.Printf("Recieved ping from %d!\n", req.GetId()) - // update devices/sensors +// ReactorStatusHandler implements a gRPC handler that is called by reactors. +// Takes in a context and request which has reactor and device information. +// For now, loops over devices and logs information about their status. +func (r *ReactorManager) ReactorStatusHandler( + ctx context.Context, + req *pb.ReactorStatusPing, +) (*pb.ReactorStatusResponse, error) { + + logging.Debug(logging.DClient, "RMA %v recieved ping", r.Id) + for _, dev := range req.GetDevices() { - fmt.Printf("Device %d is %s ", dev.GetAddr(), dev.GetStatus().String()) + logging.Debug( + logging.DClient, + "RMA %v device %v is %v", + r.Id, + dev.GetAddr(), + dev.GetStatus().String(), + ) } - fmt.Printf("\n") - // go r.UpdateDevices(req.GetDevices()) return &pb.ReactorStatusResponse{Id: int32(r.Id)}, nil } From 4652fea1d819dbd309b78cc04c113d69fcaecf0e Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Wed, 14 Jun 2023 14:53:56 -0400 Subject: [PATCH 10/44] tested reactormanager except for grpc --- internal/pkg/server/coordinator.go | 7 ++- internal/pkg/server/reactormanager_test.go | 58 ++++++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 internal/pkg/server/reactormanager_test.go diff --git a/internal/pkg/server/coordinator.go b/internal/pkg/server/coordinator.go index 65c98a8..6bf495c 100644 --- a/internal/pkg/server/coordinator.go +++ b/internal/pkg/server/coordinator.go @@ -102,7 +102,7 @@ func (c *CentralCoordinator) ClientHandler(cl *Client) *ClientResponse { cr.URL, cr.Org, cr.Token, cr.Bucket, err = c.Database.GetReactorClient(cl.Id) } else { // throw error - err = errors.New(fmt.Sprintf("Client type %s not recognized!")) + err = fmt.Errorf("Client type %s not recognized!", cl.Type) } // returns based on cl type if err != nil { @@ -166,7 +166,10 @@ func (m *ReactorManagers) UpdateReactorManager(cl *Client, errCh chan error) err if !exists { logging.Debug(logging.DClient, "RCO creating manager for reactor client %v", cl.Id) // creating - rm = NewReactorManager(cl, m.Config, errCh) + if rm, err = NewReactorManager(cl, m.Config, errCh); err != nil { + return err + } + // starting if err = rm.Start(); err != nil { return err diff --git a/internal/pkg/server/reactormanager_test.go b/internal/pkg/server/reactormanager_test.go new file mode 100644 index 0000000..64cd1b0 --- /dev/null +++ b/internal/pkg/server/reactormanager_test.go @@ -0,0 +1,58 @@ +package server + +import ( + "math/rand" + "testing" + + "github.com/spf13/viper" + "github.com/stretchr/testify/assert" +) + +// dummyClient creates a dummy client for testing. +func dummyClient() *Client { + return &Client{ + Id: rand.Int(), + Model: "dummy", + Type: "dummy", + } +} + +// dummyReactorManager creates a dummy reactor manager for testing. +func dummyReactorManager() (*ReactorManager, error) { + + ch := make(chan error) + cl := dummyClient() + return NewReactorManager(cl, viper.New(), ch) +} + +// TestNewReactorManager tries to create a new reactor manager. +func TestNewReactorManager(t *testing.T) { + assert := assert.New(t) + _, err := dummyReactorManager() + assert.Equal(err, nil, "failed to create reactor manager") +} + +// TestReactorManager tries to start/stop reactor manager +func TestReactorManager(t *testing.T) { + assert := assert.New(t) + rm, err := dummyReactorManager() + assert.Equal(err, nil, "failed to create reactor manager") + + cycles := 10 + for i := 0; i < cycles; i++ { + assert.NoError(rm.Start(), "failed to start") + assert.NoError(rm.Stop(), "failed to start") + } +} + +// TestUpdateClient tries to update a reactor managers embedded client. +func TestUpdateClient(t *testing.T) { + + assert := assert.New(t) + rm, err := dummyReactorManager() + assert.Equal(err, nil, "failed to create reactor manager") + + cl := dummyClient() + + assert.NoError(rm.UpdateClient(cl), "failed to update client") +} From a0bda5d6b3901ee2e442c4babac40662cd856eb4 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Wed, 14 Jun 2023 15:17:46 -0400 Subject: [PATCH 11/44] documented coordinator --- internal/pkg/server/coordinator.go | 140 +++++++++++++++++++++-------- 1 file changed, 105 insertions(+), 35 deletions(-) diff --git a/internal/pkg/server/coordinator.go b/internal/pkg/server/coordinator.go index 6bf495c..af52cd3 100644 --- a/internal/pkg/server/coordinator.go +++ b/internal/pkg/server/coordinator.go @@ -1,12 +1,12 @@ +// package Server provides a way to listen for incoming connections +// and manage multiple reactor clients. package server import ( pb "FRMS/internal/pkg/grpc" "FRMS/internal/pkg/influxdb" - _ "FRMS/internal/pkg/influxdb" "FRMS/internal/pkg/logging" "context" - "errors" "fmt" "net" "sync" @@ -15,20 +15,26 @@ import ( "google.golang.org/grpc" ) -// this package creates the central coordiantor and sub coordiantors to route clients - -// db client interface +// Database is an interface to interact with the server database. +// Used mainly to find existing credentials for +// incoming reactor client connections. type Database interface { - // getters (all create if doesnt exist) GetReactorClient(int) (string, string, string, string, error) // returns (url, org, bucket, token, err) } +// NewDatabaseAdmin creates a new database admin that implements the +// Database interface. +// Allows access to the database to find/create reactor credentials. +// Implemented via the influxdb package. func NewDatabaseAdmin(config *viper.Viper) (Database, error) { return influxdb.NewDBAdmin(config) } +// CentralCoordinator is the main coordinator struct that runs on the server. +// Used to oversee the reactor managers as well as process incoming +// client connections. +// Also interacts with the database and global config. type CentralCoordinator struct { - // main coordinator ClientConnections *ClientPacket *ReactorCoordinator Database @@ -38,6 +44,10 @@ type CentralCoordinator struct { Err chan error } +// NewCentralCoordinator creates a central coordinator with the given global +// config and error channel. +// It will create a new reactor coordinator and database admin. +// It will also try to load the existing configuration information. func NewCentralCoordinator(config *viper.Viper, ch chan error) *CentralCoordinator { // create a central coordinator to manage requests db, err := NewDatabaseAdmin(config) @@ -49,13 +59,16 @@ func NewCentralCoordinator(config *viper.Viper, ch chan error) *CentralCoordinat if err != nil { ch <- err } - config.UnmarshalKey("server.ports", rc) // get reactor port + + config.UnmarshalKey("server.ports", rc) + c := &CentralCoordinator{ Err: ch, Config: config, Database: db, ReactorCoordinator: rc, } + // grab config settings if err = config.UnmarshalKey("server", c); err != nil { ch <- err @@ -64,53 +77,64 @@ func NewCentralCoordinator(config *viper.Viper, ch chan error) *CentralCoordinat return c } +// Start activates the central coordinator and ensures it is ready for +// new clients. +// Creates a listener and starts both reactor coordinator and listener. func (c *CentralCoordinator) Start() { - // starts up associated funcs + clientChan := make(chan *ClientPacket) l := NewListener(clientChan, c.Err) - // grabs lis port + c.Config.UnmarshalKey("server.ports", l) - // starting reactor coordinator if err := c.ReactorCoordinator.Start(); err != nil { c.Err <- err } - // starting listener + if err := l.Start(); err != nil { c.Err <- err } - // lastly start client listener + go c.ClientListener(clientChan) } +// ClientListener listens on the given channel for clients that are sent +// over from the listener. +// The clients are then passed to the handler before returning the response. func (c *CentralCoordinator) ClientListener(ch chan *ClientPacket) { + for client := range ch { - // basically loops until channel is closed client.Response <- c.ClientHandler(client.Client) // respond with cred } } +// ClientHandler takes in a client and retrieves the associated +// database credentials. +// Currently only handles reactor type clients, can be modified +// to support others. func (c *CentralCoordinator) ClientHandler(cl *Client) *ClientResponse { // returns reactor db info var err error cr := &ClientResponse{Port: c.Ports[cl.Type]} - if cl.Type == "reactor" { - // get reactor info - go c.ReactorCoordinator.ClientHandler(cl) - // db info - cr.URL, cr.Org, cr.Token, cr.Bucket, err = c.Database.GetReactorClient(cl.Id) - } else { - // throw error - err = fmt.Errorf("Client type %s not recognized!", cl.Type) + if cl.Type != "reactor" { + c.Err <- fmt.Errorf("client type %s not recognized", cl.Type) } - // returns based on cl type + + go c.ReactorCoordinator.ClientHandler(cl) + + // db info + cr.URL, cr.Org, cr.Token, cr.Bucket, err = c.Database.GetReactorClient(cl.Id) + if err != nil { c.Err <- err } + return cr } +// ReactorCoordinator is a strucutre used to store reactor managers for +// clients that have connected at some point. type ReactorCoordinator struct { Port int `mapstructure:"reactor"` *ReactorManagers @@ -118,84 +142,130 @@ type ReactorCoordinator struct { pb.UnimplementedMonitoringServer } +// ReactorManagers is a structure that stores a concurrent map of the +// reactor managers as well as the global config. type ReactorManagers struct { Config *viper.Viper Directory map[int]*ReactorManager sync.RWMutex } +// NewReactorCoordinator takes the global config and error channel and returns +// a pointer to a ReactorCoordinator as well as any errors. func NewReactorCoordinator(config *viper.Viper, errCh chan error) (*ReactorCoordinator, error) { + rmap := make(map[int]*ReactorManager) - rm := &ReactorManagers{Directory: rmap, Config: config} - c := &ReactorCoordinator{Err: errCh, ReactorManagers: rm} - return c, nil + + rm := &ReactorManagers{ + Directory: rmap, + Config: config, + } + + return &ReactorCoordinator{ + Err: errCh, + ReactorManagers: rm, + }, nil } +// Start starts the reactor coordinator and kicks off +// registering the gRPC service func (c *ReactorCoordinator) Start() error { + logging.Debug(logging.DStart, "RCO 01 Starting!") - // register grpc service + return c.Register() } +// ClientHandler takes in a client and finds or creates the correct +// manager for said client. func (c *ReactorCoordinator) ClientHandler(cl *Client) { - // updates clients if nessecary + if err := c.UpdateReactorManager(cl, c.Err); err != nil { c.Err <- err } } +// GetReactorManager attempts to locate a reactor manager for a given id. +// Returns either the associated reactor manager, or an error if +// a manager does not exist for the given id. func (m *ReactorManagers) GetReactorManager(id int) (*ReactorManager, error) { m.RLock() defer m.RUnlock() rm, exists := m.Directory[id] + if !exists { - return &ReactorManager{}, errors.New(fmt.Sprintf("No manager for reactor %d!", id)) + return &ReactorManager{}, fmt.Errorf("no manager for reactor %d", id) } return rm, nil } +// UpdateReactorManager takes in a client and error channel and passes the +// client to the associate reactor manager. +// If the client does not have an existing reactor manager, it will create one +// , start it, and add it to the map for future calls. +// The function then calls UpdateClient on the reactor manager and returns +// any errors generated by this function. func (m *ReactorManagers) UpdateReactorManager(cl *Client, errCh chan error) error { - // locking m.RLock() defer m.RUnlock() var err error rm, exists := m.Directory[cl.Id] + if !exists { - logging.Debug(logging.DClient, "RCO creating manager for reactor client %v", cl.Id) - // creating + // reactor manager does not exist, creating new one + logging.Debug( + logging.DClient, + "RCO 01 creating manager for %v", + cl.Id, + ) + if rm, err = NewReactorManager(cl, m.Config, errCh); err != nil { return err } - // starting if err = rm.Start(); err != nil { return err } + m.Directory[cl.Id] = rm } + return rm.UpdateClient(cl) } +// Register attaches to the servers port and attempts to bind +// a gRPC server to it. func (r *ReactorCoordinator) Register() error { + lis, err := net.Listen("tcp", fmt.Sprintf(":%v", r.Port)) if err != nil { return err } + grpcServer := grpc.NewServer() pb.RegisterMonitoringServer(grpcServer, r) + go grpcServer.Serve(lis) - logging.Debug(logging.DClient, "RCO ready for client requests") + + logging.Debug(logging.DClient, "RCO 01 ready") + return nil } +// ReactorStatusHandler is a gRPC handler used to handle incoming +// reactor requests containing information about said reactor. +// It will get the associate reactor manager and pass the +// request down for further processing. func (r *ReactorCoordinator) ReactorStatusHandler(ctx context.Context, req *pb.ReactorStatusPing) (*pb.ReactorStatusResponse, error) { + rm, err := r.GetReactorManager(int(req.GetId())) - // error checking + if err != nil { return &pb.ReactorStatusResponse{}, err } + return rm.ReactorStatusHandler(ctx, req) } From 11914d8d679dda54f789a74b34c8f210d33ca9b0 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Wed, 14 Jun 2023 15:32:55 -0400 Subject: [PATCH 12/44] tweaked coordinator and reactor manager for better test coverage. documented both, tested reactormanager --- internal/pkg/server/coordinator.go | 6 +++-- internal/pkg/server/reactormanager.go | 16 ++++--------- internal/pkg/server/reactormanager_test.go | 27 ++++++++++++++++++++++ 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/internal/pkg/server/coordinator.go b/internal/pkg/server/coordinator.go index af52cd3..f4c8ea3 100644 --- a/internal/pkg/server/coordinator.go +++ b/internal/pkg/server/coordinator.go @@ -258,7 +258,7 @@ func (r *ReactorCoordinator) Register() error { // ReactorStatusHandler is a gRPC handler used to handle incoming // reactor requests containing information about said reactor. // It will get the associate reactor manager and pass the -// request down for further processing. +// request device information before returning an acknowledgement. func (r *ReactorCoordinator) ReactorStatusHandler(ctx context.Context, req *pb.ReactorStatusPing) (*pb.ReactorStatusResponse, error) { rm, err := r.GetReactorManager(int(req.GetId())) @@ -267,5 +267,7 @@ func (r *ReactorCoordinator) ReactorStatusHandler(ctx context.Context, req *pb.R return &pb.ReactorStatusResponse{}, err } - return rm.ReactorStatusHandler(ctx, req) + go rm.ReactorDeviceHandler(req.GetDevices()) + + return &pb.ReactorStatusResponse{Id: int32(rm.Id)}, nil } diff --git a/internal/pkg/server/reactormanager.go b/internal/pkg/server/reactormanager.go index 5c88dcc..ed113f7 100644 --- a/internal/pkg/server/reactormanager.go +++ b/internal/pkg/server/reactormanager.go @@ -6,8 +6,6 @@ import ( "FRMS/internal/pkg/manager" "time" - "context" - "github.com/spf13/viper" ) @@ -90,17 +88,13 @@ func (r *ReactorManager) UpdateClient(cl *Client) error { return nil } -// ReactorStatusHandler implements a gRPC handler that is called by reactors. -// Takes in a context and request which has reactor and device information. -// For now, loops over devices and logs information about their status. -func (r *ReactorManager) ReactorStatusHandler( - ctx context.Context, - req *pb.ReactorStatusPing, -) (*pb.ReactorStatusResponse, error) { +// ReactorDeviceHandler processes incoming device information and +// updates the manager accordingly. +func (r *ReactorManager) ReactorDeviceHandler(devs []*pb.Device) error { logging.Debug(logging.DClient, "RMA %v recieved ping", r.Id) - for _, dev := range req.GetDevices() { + for _, dev := range devs { logging.Debug( logging.DClient, "RMA %v device %v is %v", @@ -110,5 +104,5 @@ func (r *ReactorManager) ReactorStatusHandler( ) } - return &pb.ReactorStatusResponse{Id: int32(r.Id)}, nil + return nil } diff --git a/internal/pkg/server/reactormanager_test.go b/internal/pkg/server/reactormanager_test.go index 64cd1b0..9f3b321 100644 --- a/internal/pkg/server/reactormanager_test.go +++ b/internal/pkg/server/reactormanager_test.go @@ -1,6 +1,7 @@ package server import ( + pb "FRMS/internal/pkg/grpc" "math/rand" "testing" @@ -17,6 +18,22 @@ func dummyClient() *Client { } } +func dummyDevices() []*pb.Device { + numDevs := 10 + + devs := make([]*pb.Device, numDevs) + + for i := 0; i < numDevs; i++ { + dev := &pb.Device{ + Addr: int32(rand.Intn(255)), + Status: pb.Status(rand.Intn(2)), + } + devs = append(devs, dev) + } + + return devs +} + // dummyReactorManager creates a dummy reactor manager for testing. func dummyReactorManager() (*ReactorManager, error) { @@ -56,3 +73,13 @@ func TestUpdateClient(t *testing.T) { assert.NoError(rm.UpdateClient(cl), "failed to update client") } + +// TestReactorDeviceHandler ensures that a list of devices can be processed. +func TestReactorDeviceHandler(t *testing.T) { + assert := assert.New(t) + rm, err := dummyReactorManager() + assert.Equal(err, nil, "failed to create reactor manager") + + devs := dummyDevices() + assert.NoError(rm.ReactorDeviceHandler(devs), "failed to handle devices") +} From 265c14d34439cbbbbab2330cd96011795d5050eb Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Wed, 14 Jun 2023 15:45:53 -0400 Subject: [PATCH 13/44] fixing error returns, barebones listener testing --- internal/pkg/server/coordinator.go | 7 ++- internal/pkg/server/listener.go | 78 ++++++++++++++++++++-------- internal/pkg/server/listener_test.go | 17 ++++++ 3 files changed, 80 insertions(+), 22 deletions(-) create mode 100644 internal/pkg/server/listener_test.go diff --git a/internal/pkg/server/coordinator.go b/internal/pkg/server/coordinator.go index f4c8ea3..1df9b7e 100644 --- a/internal/pkg/server/coordinator.go +++ b/internal/pkg/server/coordinator.go @@ -83,7 +83,12 @@ func NewCentralCoordinator(config *viper.Viper, ch chan error) *CentralCoordinat func (c *CentralCoordinator) Start() { clientChan := make(chan *ClientPacket) - l := NewListener(clientChan, c.Err) + + l, err := NewListener(clientChan, c.Err) + + if err != nil { + c.Err <- err + } c.Config.UnmarshalKey("server.ports", l) diff --git a/internal/pkg/server/listener.go b/internal/pkg/server/listener.go index 3f9dc0d..c68356c 100644 --- a/internal/pkg/server/listener.go +++ b/internal/pkg/server/listener.go @@ -1,7 +1,6 @@ package server import ( - //"log" pb "FRMS/internal/pkg/grpc" "FRMS/internal/pkg/logging" "context" @@ -11,23 +10,24 @@ import ( "google.golang.org/grpc" ) -/* -Listens on a supplied port and sends incoming clients over a supplied channel -Waits for a response on that channel to send back to the client with DB credentials -*/ - -type Listener struct { // exporting for easy use in the short term +// Listener is a struct that listens for incoming clients on a given port +// and passes them the central coordinator. +// Implements the gRPC handshake server for clients. +type Listener struct { Port int `mapstructure:"lis"` ClientConnections chan *ClientPacket Err chan error pb.UnimplementedHandshakeServer } +// ClientPacket is a uniform type to pass on a channel to the server. type ClientPacket struct { *Client Response chan *ClientResponse } +// Client is a struct containing information about the client on +// the incoming connection. type Client struct { //Ip string //Port int @@ -36,6 +36,8 @@ type Client struct { Type string } +// ClientResponse is the database credentials returned from the central +// coordinator for the given client. type ClientResponse struct { Port int URL string @@ -44,41 +46,75 @@ type ClientResponse struct { Bucket string } -func NewListener(cch chan *ClientPacket, ech chan error) *Listener { - l := &Listener{Err: ech, ClientConnections: cch} - return l +// NewListener createsa new listener with the given client and error channels +func NewListener( + cch chan *ClientPacket, + ech chan error, +) (*Listener, error) { + + return &Listener{ + Err: ech, + ClientConnections: cch, + }, nil } +// Start activates the listener and kicks off the gRPC binding process func (l *Listener) Start() error { - // start grpc server and implement reciever logging.Debug(logging.DStart, "LIS 01 Started client listener") return l.Register() } +// Register creates a net listener on the port and binds a grpc server to it +// before registering a handshake server. func (l *Listener) Register() error { - // creates a gRPC service and binds it to our handler + lis, err := net.Listen("tcp", fmt.Sprintf(":%v", l.Port)) if err != nil { return err } + grpcServer := grpc.NewServer() pb.RegisterHandshakeServer(grpcServer, l) + go grpcServer.Serve(lis) + logging.Debug(logging.DStart, "LIS 01 Registered on port %v", l.Port) + return nil } +// ClientDiscoveryHandler implements the grpc method which can be called +// by incoming clients to first make connection to the central +// coordinator and receive database credentials. func (l *Listener) ClientDiscoveryHandler(ctx context.Context, ping *pb.ClientRequest) (*pb.ClientResponse, error) { - // incoming client ping, notify coord and wait for DB credentials to respond - c := &Client{Id: int(ping.GetClientId()), Type: ping.GetClientType()} - logging.Debug(logging.DClient, "LIS %v %v has connected\n", c.Type, c.Id) - // prepare packet to send to coordinator + + c := &Client{ + Id: int(ping.GetClientId()), + Type: ping.GetClientType(), + } + + logging.Debug(logging.DClient, "LIS 01 %v %v has connected\n", c.Type, c.Id) + ch := make(chan *ClientResponse) - p := &ClientPacket{Client: c, Response: ch} - // blocking + p := &ClientPacket{ + Client: c, + Response: ch, + } + l.ClientConnections <- p + resp := <-ch - // prepare object to return to client - db := &pb.Database{URL: resp.URL, ORG: resp.Org, Token: resp.Token, Bucket: resp.Bucket} - return &pb.ClientResponse{ClientId: uint32(c.Id), ServerPort: uint32(resp.Port), Database: db}, nil + + db := &pb.Database{ + URL: resp.URL, + ORG: resp.Org, + Token: resp.Token, + Bucket: resp.Bucket, + } + + return &pb.ClientResponse{ + ClientId: uint32(c.Id), + ServerPort: uint32(resp.Port), + Database: db, + }, nil } diff --git a/internal/pkg/server/listener_test.go b/internal/pkg/server/listener_test.go new file mode 100644 index 0000000..ae5d770 --- /dev/null +++ b/internal/pkg/server/listener_test.go @@ -0,0 +1,17 @@ +package server + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// TestNewListener tries to create a new listener +func TestNewListener(t *testing.T) { + assert := assert.New(t) + + cch := make(chan *ClientPacket) + ech := make(chan error) + _, err := NewListener(cch, ech) + assert.Equal(err, nil, "creating listener failed") +} From f7f679ba5863b04e6b2ea929e26953bc754a1592 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Wed, 14 Jun 2023 17:56:19 -0400 Subject: [PATCH 14/44] python testing helper with colors --- go.mod | 21 +++++++-------- go.sum | 30 +++++++++------------ gotest.py | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 29 deletions(-) create mode 100755 gotest.py diff --git a/go.mod b/go.mod index 1643e45..5c4d6d8 100644 --- a/go.mod +++ b/go.mod @@ -3,39 +3,38 @@ module FRMS go 1.18 require ( - github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1 + github.com/gorilla/websocket v1.5.0 github.com/influxdata/influxdb-client-go/v2 v2.9.1 - github.com/rivo/tview v0.0.0-20220610163003-691f46d6f500 github.com/spf13/viper v1.12.0 + github.com/stretchr/testify v1.7.1 google.golang.org/grpc v1.47.0 google.golang.org/protobuf v1.28.0 ) require ( + github.com/davecgh/go-spew v1.1.1 // indirect github.com/deepmap/oapi-codegen v1.8.2 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect - github.com/gdamore/encoding v1.0.0 // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/gorilla/websocket v1.5.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect - github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/magiconair/properties v1.8.6 // indirect - github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.1 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/rivo/uniseg v0.2.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/afero v1.8.2 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.3.0 // indirect - golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 // indirect - golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b // indirect - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect - golang.org/x/text v0.3.7 // indirect + github.com/yuin/goldmark v1.4.13 // indirect + golang.org/x/mod v0.10.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect + golang.org/x/tools v0.9.3 // indirect google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd // indirect gopkg.in/ini.v1 v1.66.4 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 3c6907e..b6dc52f 100644 --- a/go.sum +++ b/go.sum @@ -69,10 +69,6 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= -github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= -github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1 h1:QqwPZCwh/k1uYqq6uXSb9TRDhTkfQbO80v8zhnIe5zM= -github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1/go.mod h1:Az6Jt+M5idSED2YPGtwnfJV0kXohgdCBPmHGSYc1r04= github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= @@ -167,8 +163,6 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= -github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= -github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -180,8 +174,6 @@ github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= -github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= @@ -195,10 +187,6 @@ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qR github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rivo/tview v0.0.0-20220610163003-691f46d6f500 h1:KvoRB2TMfMqK2NF2mIvZprDT/Ofvsa4RphWLoCmUDag= -github.com/rivo/tview v0.0.0-20220610163003-691f46d6f500/go.mod h1:WIfMkQNY+oq/mWwtsjOYHIZBuwthioY2srOmljJkTnk= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= @@ -229,6 +217,8 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -278,6 +268,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -313,6 +305,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y= golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -370,7 +364,6 @@ golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -378,12 +371,10 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b h1:2n253B2r0pYSmEV+UNCQoPfU/FiaizQEK5Gu4Bq4JE8= golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -391,9 +382,10 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -446,6 +438,8 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= +golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/gotest.py b/gotest.py new file mode 100755 index 0000000..37a72a3 --- /dev/null +++ b/gotest.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +import json +import subprocess + +class PackageTest: + def __init__(self): + self.status = "" + self.tests = [] + self.totaltime = 0 + +res = {} + +output = subprocess.run(["go","test","-count=1","-json","./..."], capture_output=True, text=True) + +output = str(output.stdout) +output = output.split('\n') + +for line in output[:-1]: + # parse the json + parsed = json.loads(line) + action = parsed["Action"] + + # skip + if action in ["start", "output", "run"]: + continue + + # create blank if doesn't exist + if parsed["Package"] not in res: + res[parsed["Package"]] = PackageTest() + + pkg = res[parsed["Package"]] + + if "Test" not in parsed: + # top level package result + pkg.status = action + if "Elapsed" in parsed: + pkg.totaltime = parsed["Elapsed"] + else: + # individual test + pkg.tests.append((parsed["Test"],parsed["Action"],parsed["Elapsed"])) + +# generating output from parsed json +total = 0 +for name, info in res.items(): + pkgname = name.split('/') + pkgname = '/'.join(name.split('/')[1:]) + if info.status == "skip": + print("Skipped %s" % (pkgname)) + continue + + print("\nTesting %s:" % (pkgname)) + + passed = 0 + total = 0 + + for test in info.tests: + total += 1 + out = [] + if test[1] == "pass": + passed += 1 + out = [" " + test[0] + ":",'\033[32mpass\033[0m ','(' + str(test[2]) + 's)'] + elif test[1] == "fail": + out = [" " + test[0] + ":",'\033[31mfail\033[0m ','(' + str(test[2]) + 's)'] + + print(f"{out[0] : <30}{out[1] : >5}{out[2] : >8}") + + result = "" + if info.status == "pass": + result = "\033[32mPASSED\033[0m" + else: + result = "\033[31mFAILED\033[0m" + + print("summary:\n\t%s (%d/%d in %.3fs)" % (result, passed, total, info.totaltime)) + + +# print("OVERALL:\n\tSkipped %d/%d\n\tFailed %d/%d\n\tPassed %d/%d\n" % (skippedTests, totalTests, failedTests, totalTests, passedTests, totalTests)) + + From 444bb8684e31f27505732d759f718cc00314c6aa Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 13:38:54 -0400 Subject: [PATCH 15/44] cleaning up old files and working on readme --- README.md | 23 +- Taskfile.dist.yml | 5 +- gotest.py | 21 +- internal/notes/archive/notes.md | 71 -- internal/notes/archive/old_notes | 1001 ---------------------------- internal/notes/archive/structure | 103 --- internal/notes/index.md | 4 - internal/notes/weekly/Jan-16-20.md | 149 ----- internal/notes/weekly/Jan-23-27.md | 49 -- internal/pkg/i2c/bus.go | 66 +- 10 files changed, 86 insertions(+), 1406 deletions(-) delete mode 100644 internal/notes/archive/notes.md delete mode 100644 internal/notes/archive/old_notes delete mode 100644 internal/notes/archive/structure delete mode 100644 internal/notes/index.md delete mode 100644 internal/notes/weekly/Jan-16-20.md delete mode 100644 internal/notes/weekly/Jan-23-27.md diff --git a/README.md b/README.md index e77b3f8..c18b524 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,24 @@ # FRMS -ForeLight Reactor Management System - -## Outline +## ForeLight Reactor Management System This branch will serve as the staging ground for adding unit tests and documentation in order to finalize v0.1.0-alpha + +## Table of Contents + +* [Introduction](#introduction) +* [Getting Started](#getting-started) + * [Installation](#installation) + * [Usage](#usage) +* [Wiki](#wiki) + +## Introduction + +## Getting Started + +### Installation + +### Usage + +## Wiki + diff --git a/Taskfile.dist.yml b/Taskfile.dist.yml index 6d48331..e1956da 100644 --- a/Taskfile.dist.yml +++ b/Taskfile.dist.yml @@ -43,4 +43,7 @@ tasks: GOARM: "{{.GOARM}}" GOARCH: "{{.GOARCH}}" GOOS: "{{.GOOS}}" - + test: + desc: "Runs the full test suite" + cmds: + - ./gotest.py diff --git a/gotest.py b/gotest.py index 37a72a3..1fa03c9 100755 --- a/gotest.py +++ b/gotest.py @@ -39,8 +39,10 @@ for line in output[:-1]: # individual test pkg.tests.append((parsed["Test"],parsed["Action"],parsed["Elapsed"])) +totalRan = 0 +totalPassed = 0 +totalTime = 0 # generating output from parsed json -total = 0 for name, info in res.items(): pkgname = name.split('/') pkgname = '/'.join(name.split('/')[1:]) @@ -58,9 +60,9 @@ for name, info in res.items(): out = [] if test[1] == "pass": passed += 1 - out = [" " + test[0] + ":",'\033[32mpass\033[0m ','(' + str(test[2]) + 's)'] + out = [" " + test[0] + ":",'\033[32mpass\033[0m ',str(test[2]) + 's'] elif test[1] == "fail": - out = [" " + test[0] + ":",'\033[31mfail\033[0m ','(' + str(test[2]) + 's)'] + out = [" " + test[0] + ":",'\033[31mfail\033[0m ',str(test[2]) + 's'] print(f"{out[0] : <30}{out[1] : >5}{out[2] : >8}") @@ -70,9 +72,18 @@ for name, info in res.items(): else: result = "\033[31mFAILED\033[0m" - print("summary:\n\t%s (%d/%d in %.3fs)" % (result, passed, total, info.totaltime)) + # keep track of grand totals + totalRan += total + totalPassed += passed + totalTime += info.totaltime + print(" %s %d/%d in %.3fs/n" % (result, passed, total, info.totaltime)) -# print("OVERALL:\n\tSkipped %d/%d\n\tFailed %d/%d\n\tPassed %d/%d\n" % (skippedTests, totalTests, failedTests, totalTests, passedTests, totalTests)) +# output overall test statistics +if totalRan == totalPassed: + result = "\033[32mPASSED\033[0m" +else: + result = "\033[31mFAILED\033[0m" +print("\nSUMMARY:\n\t%s %d/%d in %.3fs" % (result, totalPassed, totalRan, totalTime)) diff --git a/internal/notes/archive/notes.md b/internal/notes/archive/notes.md deleted file mode 100644 index 69938dd..0000000 --- a/internal/notes/archive/notes.md +++ /dev/null @@ -1,71 +0,0 @@ -*Time for a coherent plan of attack* - -### Current Issues: -- There is a lot of redundancy between the managers/coordinators when it comes to basic checks -- the package seperation kind of makes sense, but it needs to be better fleshed out -- I need to enforce better seperation of responsibilities. Somewhat unclear when state is being kept centrally in the coordinator for no apparent reason. - -### Solution: -- Go through the packages and consolidate -- Reduce the state we have to keep centrally, push responsibility to the other packages - -### Plan of attack: -- Outline core information flow -- Examine what interfaces are nessecary to make this work -- Stop looking at the server/reactor as seperate entities - -*I need to put the whole docker thing on the back burner for now. It isn't that important when it comes to immediate goals.* - -#### 12/05 TODO -- Cleanup server side config stuff to make it coherent -- Reflect changes to reactor side startup -- Boil down interface to address core issues -- Config outline: - 1) Startup and load the existing config - 2) Overwrite any previous settings with the flags - 3) Intelligently translate config into action - 4) launch coordinator and start up existing reactor managers -- Config Structure: - - Wrap viper functions in config struct methods to be used thrtugh interfaces - - minimize the reliance on viper so we can sub in othermethods -- is it even important to launch reactor managers? Wont they just be started on connection? - - -#### 12/06 TODO -- I think I can completely remove the old config way and just pass the viper object directly. I think its not worth the hassle of trying to keep track of a million interfaces - -#### 12/07 TODO -- I concede, I will just remove flags as most people will never use them anyway and instead rely on env vars and config files. To hell with the flags. -- I am ripping out all of the TUI and status manager stuff, its convoluted and harder than just pulling info from database. - - I can eventaully rework TUI to pull from DB which is fine, there will never be that many clients anyway and a lot of them are only 1 time calls with refreshes which aren't that slow anyway. -- alright I gutted the tui and system viewer, reworking sub coord to launch at start. That way there is a listener active -- time to boil down to functionality a LOT, right now its clumsy and inefficent, there needs to be a better way to keep everything straight -- Moving the DB responsibilites to the reactor itself seems to be the best way to do it in the short term. Reduce network load and overall keep things efficient. May lead to duplicte copies of data? Not the end of the world, logging system can make sure we are maintaining entries. - -**IDEA** -Reactors log data themselves, Send periodic status updates over grpc to enable monitoring faster than the sample rate -*This could work!* -Outline: -- Reactors reach out to server on boot to get DB info - - compare this against what they have internally to ensure they are up to date and allow for migrations - - Maybe not even save the db info because we don't need to?? -- Reactors also recieve port for their specific manager - - Can be dynamically given out to allow for spread out load -- Reactors then reach out with sensor and device info periodically (5s?) which can be used for live monitoring -- RM responds with any potential updates for the device settings i.e. change pwm duty on web interface, pass on to reactor -- Allows for a live view with current reading as well as historical data at differing interval via grafana. (i.e. 5s live view with 10 min sample interval) - -Need to differentiate sensors vs devices that can be changed -- Sensors have a variable sample rate and eventually name/address -- Devices have more and widley varying parameters, could be pwm with freq/duty/onoff or ph pump with on, time or off etc. - -#### 12/09 TODO -- Alright I have a baseline! I want to start to integrate atlas type stuff so that I have some mock data/sensors to work with. I am going to try to flesh out the "atlas" interface/struct to implement some of the more basic commands. - -#### 1/11 TODO -Plan of attack for websocket stuff and things - -**Questions** -- What to do about the reactor to user comms - - Websockets? GRPC? smoke signals? -- diff --git a/internal/notes/archive/old_notes b/internal/notes/archive/old_notes deleted file mode 100644 index 99d46ce..0000000 --- a/internal/notes/archive/old_notes +++ /dev/null @@ -1,1001 +0,0 @@ -time to plan - -terms - -RLC - reactor level coordinator (Beagleboard) -RH - Reactor Handler (goroutine) -SH - sensor handler (gourtine) -Reactor Side: - -needs - - way to discover active sensors - - spin up goroutine for each sensor responsible for keeping status and logs - - way to read back and truncate logs for safe data delivery to servr - - routing requests from reactor level coordinator to relevant sensor - - internal memory sharing and channels for reactor level coordination - -thoughts - - maybe the RLC can be responsible for packaging data for coordinator response - adv: - - clears up the network - - simplifies pinging - - keeps the data aributrary - cons: - - unknown data size - - how to coordinate data structure - -Server Side: - -needs - - way to look into a config file for active reactors - - should then spin up a goroutine for each reactor - - responsible for recovery and consistent communication - - individual database entries - - API? - - use gRPC for comms between server and BB - - each reactor handler needs mechanism for pinging, recovery, and database correctness - - - -message PingRequest { - // do we even need anything in a ping request? -} - -message PingResponse { - repeated Sensor sensor = 1; -} - -message Sensor { - string type = 1; - bool status = 2; - byte data = 3; -} - -sensors := [string]q - -6/23 TODO: - -X- BBB mem fix - - 32 gig for the main but where to put the OS? - - obv in EMMC but how to init sd card? (probably dev tree :( ) -Y- Server side impl - Y - Need a struct for the RC - X - Should we store and load configs based on IDs? (efficiency of this vs performance increases i.e. bandwidth vs storage) - Y/X - Who should kill the RC and how do we know its dead? (Garbage collection to the rescue hopefully) - -X- UPDATE PRES - - Add bottle necks for each part in that section - - I2C: 128 addrs and ~90 bytes/s per device at 128 devs optimally - - BB: Hardware is upgradeable even customizable ;) - - Server: Its overkill as is, can benchmark with a rudementary go overload once its completed -- Sensor configs - - how to store sensor info efficiently and searchably lol - - who needs to know what the sensor is? (Just the SM? Even the SM?) -X- TUI - - pls this would be so sick -TODO: 6-24 - -Y - Pres stuff from yesterday + python gRPC abstraction -Y - RPI flash -- Add resiliance to coordinator process (aka error handley blech) - - -TODO 6/27 -- Time to tackle sensor managers officially - - to hell with port generation - - going to use channels but not like a jackass - - going to try generating channels interface side but via implicit types to avoid the interface stff - - should set up a structure where I can use arbiturary types on the backend and fulfill methods to get/infer information on the frontend -- rewrite I2C interface to employ same method, should allow for this - 1) generate type - 2) send it to worker - 3) receive back (original? copy?) - 4) use interface methods to get required values -- should simplify all internal communication and potentially suggests api for implementation - -TODO 6/28 -- It works... kind of - - I learned a lot about - "the smaller the interface, the more useful it is" --Y time to tackle the server side error handleing aka reconnect - - custom backoff? Max TO? Sleep Period? - 5ms -> 10ms -> 25ms -> 50ms -> 100ms -> 250ms -> 500ms -> 1s -> 1s --Y Actual logic? - 1) Create listener - 2) create reactor managers for each reactor - a) whose job is it to kill on disconnect? Should we kill? - b) the RM will have the actual ping mechanism with coordinator aggregating in eventual TUI - 3) reactivated reactors should connect to the same RM to resume connections with less downtime. Memory use be damned (ALLOCATED?? ID VS IP) - 4) need a way to purge manually disconnected reactors - a) also should check ids which are deterministic and map actual hardware - 5) continue forever (for select??) --Y RM Logic? - 1) on spawn ping reactor to get initial sensor status - 2) enter ping loop to get status and keep heartbeat alive - 3) no action on sensor going down (unless method but duh) - 4) on reactor going down - 1) save to config? - 2) "sleep mode" - i.e. stop pinging and wasting resources doing stuff - 3) wait for coordinator to reactivated - 5) reactivation: call start and resume pinging -- RM Struct? - - needs to know - - ip:port of reactor - - id of reactor - - mapping of sensors to status - - last seen for reactor (and sensor?) - - needs to be able to - - ping reactor for status - - get status - - store and update status reliabily - - stop pinging on down detection - - detection outages - - respond to coordinator requests? (rpc or method most likely?) - - relies on - - SM for sensor status - - implements - - start/restart mechanism for coordinator - - ping response for coordinator -- Coordinator Struct? - - needs to know - - mapping of ids of connected reactors to RM - - its own ip:port to serve listener on - - internal mapping of system? (any efficiency benifiets here vs mem usage?) - - needs to be able to - - setup new RMs on incoming connections - - call existing RMs on incoming connections - - ping for reactor status from RMs - - store status for TUI? - - relies on - - RM for reactor status - - implements - - application logic - -That went surprisingly well... Same method for tui - -process outline: -TUI - 1) display TUI outline and connect to coordinator - 2) let coordinator know where to send reactor/sensor changes - 3) enter loop of checking for changes and drawing - 4) on quit should gracefully exit - -Coordinator - 1) on TUI connection start routine - 2) collect? sensor/reactor info and send init to TUI - 3) upon changes? send to TUI - 4) exit when TUI connection closes - - -- TUI struct - - needs to know - - all the tui stuff (duh) - - reactor/sensor status - - needs to be able to - - create (several instances of) a TUI - - receive and display up to date system info - - delete reactors/sensors - - be efficient - - i know this is broad but bear with me - - relies on - - coordinator struct for system info - - coordinator struct to fulfil delete request - - implements - - tui - - user interface or management -- Coordinator Struct - - needs to know - - all the sensor and reactor states - - needs to be able to - - know what the TUI knows/doesnt know - - efficiently notify tui of change in system - - notify sensors or reactors of removal - - relies on - - rm/sm to implement reactor/sensor removal - - rm for reactor/sensor status - - implements - - sender of system status for TUI - -TODO 6/29 -- refactoring - - creating general listener and then a coordinator for reactor/tui that uses listener to field incoming requests - - change update loops for status to only send new data or empty messages for pings -- tui - - creating coordinator/manager for TUI clients - - create update loop for tui clients - - grpc to tui client with updates and hb - - drawfunc loops over change buffer - - on disconnect should display static terminal with offline warning - -- Listener Struct - - needs to know - - IP - - Port - - needs to be able to - - respond to incoming gRPC - - create corrisponding manager // taken care of in the actual handler - - relies on - - grpc for the endpoint stuff - - manager impl - - implements - - listener for main func - - manager generator essentially - -coordinator should be seperate *** -- new coordinator struct - - listener sends new connections to coordinator who appends to internal registery and reacts - - needs to know - - incoming: - - ip - - port - - client type? - - needs to be able to - - wait for incoming connections - - create a new manager for the client - - update internal directory - - remove entries ?? (tui -> reactor) - - implements - - manager creation - - connection handling - - client hb - - relies on - - manager impl - - listener call? - - -alright complete redesign -server acts as singular listener -routes all requests to a central coordiantor -this calls cooresponding client coordinator which then initiates managers etc - -now redesinging sensor info - -new fmt -1) have a seperate long running coordinator routine responsible for a "changed" list of sensors -2) on reactor status request: - a) send the changed list - b) if the send was acknowledged purge the change list - * both must be atomic - -new rlc struct - -- needs to know - - sensors connected - - changes in sensors - - how to establish connection to central server -- needs to be able to - - track changes in its system layout - - atomically relay these to the cc -- depends on - - I2C sensor info -- implements - - reactor sensor change tracking - -new general coord - -What does it need to do? -- Needs to respond to incoming clients from listener - - what does this mean? - - needs to link a client to a manager - - this is the whole high level idea - - can everything else be abstracted away? Why not? - - how to link? Channel? Shared Memory? - - channel is probably the best choice here - - structure: - managers - [uint32] : chan<- bool - 10292133 : chan<- bool - - how to use? - - when a client connects we search to see if a channel exits - - if it does we just send "true" down the channel to signal that the client connected - - if we dont we create a channel and a manager and start the manager - - we then send true down the newly created channel - -Do we ever close the channel? - - shouldn't that would mean the program is done accepting connections (could be a useful behavior in the future) - - do we ever send false? - - no, not sure what that would signal. - - coordinator is specifically only designed to create/notify managers of a client connection - -formally, New Coordinator: -- needs to know - - client type -- needs to be able to - - either notify or create and notify a manager on client connection - - handle concurrency -- relies on - - listener to send incoming clients - - manager to implement actual manager -- implements - - manager activation and memory - -TODO 6/30 -creating efficient system mapping and data logging/structure info - -idea # 1.5 -use json maybe? - -how? -- use json to build in the structure of our system via heirarchy -ex) -[ - { - "reactor": uint32, - "status": bool, - "connected devices": [ - "device" : { - "addr": "0x10" - "type": "ph sensor", - "status": uint32, - "data": [{"ph7.3"}, // json marshelling of the specific struct - }, - "device" : { - "addr": "0x11" - "type": "temp sensor" - status: uint32 - "data": "t24.5C" - } - ] - } -] - -use go structs to map components and embed them -can send - - -need to just spitball here - -what am I trying to do at the end of the day? -I am taking sensor measurements -and potentially tweaking control paramters -lets treat each one sperately at firs - -sensor measurements - -each particular sensor manager will only be responsible for getting data from its sensor -what is the scope of responsibilities? -the sensor manager should log this data locally using a method? json? - -how do we aggregate this info? - -what if we structure our reactor as a mirror of our coordiantor - -rlc job would be to -- establish connection with central server -- wait for connections from devices -- create reactor managers for these devices - -this could be really nice - -rm (general) job: -- establish connection with device via I2C (client via wifi) -- shut down when device connection drops -- start when device connects again - -adding data responsiblities - -tuim: - needs to know of a struct of system - [reactorid][deviceaddress][device] - thus needs to know: - - reactor id - - connected device addresses - - device info: can just be a string! - - made up of status and relevant data - what do we rely on - - accurate device info string - - can have someone else make/maintain struct and proxy updates -tuic: - -needs to maintain an atomic struct of system - as above - - still only needs to know - - reactor id - - connected device address maped to device info [string] - relies on - - accurate status updates - - accurate device info - -RC ** could have this guy be responsible for change parsing - - respond to updated status from RM and send to TUI - - basically a focus point - -RM - - needs to call corret handlers for data coming in from rlc - - can we just use grpc handlers that get embedded in the manager at start? - - handlers can also notify when data doesnt match previous entry - - this would prompt the data to be sent to the rc where it can be forwardd - -RLC - - needs to have internal reactor state - - - - -this gives us a complete "skeleton" of service where we can connect/reconnect clients with appropriate managers -there isnt any functionality yet to actually log data - -how do we leverage our current connections and add functionality to managers and coordinators? - -methods and channels - -each manager is responsible for pinging the associate device {reactor, device, tui} -either sending device info in tui case -or recieving it in reactor/device case - -this is why wrapping the gen structures is nessecary. Two different operations - -device manager: -could recieve 0-100000000 values -could be any type -could be any number per entry -common struct? -"timestamp":"data" -data could be json struct - - makes it easy to parse at some point - - force sensor driver to write a go struct for the data - - Parse___Data(*json.Unmarshalled) - - -complete i2c monitor redesign - -i2c interface needs to do - data stuff: - - locking structure to serialize commands/reads - - removal function to manually parse dead devices - - reconnecting should reinit device manager and stuff - init stuff: - - keep track of devices seen and connected - - notify rlc of devices that connect/reconnect -build init stuff into a struct that can be embedded? -I2CCoordinator - - created when rlc is created - - tie rlc to i2ccoord via channels - - new devices channel for devices that go offline->online - - send the i2cdevice struct to embed in rm - - can call interface funcs on the embedded interface - - - -Eureka part 2? -we are writing all of the software libraries which means we should (with some basic cleansing) be able to just send direct database queries -this means some things which could be pros or cons - -- each sensor realistically will have its own table for each reactor. -- we can group entries by reactor and look across time stamps (tidy?) -- we can log sql entries as text based backups -- we can use basic string struct with time stamps -- each sensor library will use a common struct and probably just use string fmting -- there are some efficiency benfiets if we used custom gRPC calls for each db entry - - but we can just leverage a biolerplate call with some extra overhead? -- we still need a way of representing state of components - - reactor is easy and could be kept server side - - sensor needs to be transmitted across rlc - - should default to down if the reactor goes offline (unknown?) - -direct query approach -pros - - easy to implement - - easy to use interfaces for common libs (compiling efficiency) - - easy to add sensors (use common libs and just make custom string in wrapper) - - can develop logging and db parts as manager funcs -cons - - need unique daemon to parse data on server for state struct - - trusting each sensor to maintain itself - - still need a way of translating state - -state problem - -it just should be an enumeration -its efficeint (could be as little as 4 bits but probably 1 byte) as opposed to a string ( len(s) * byte ex "ONLINE" = 6) - - is this all worth ~1-10? bytes of savings per dev? - - 100 reactors @ ~45 sensors = 46*100 = ~4.5 kb of state or ~ 36kb if we use strings - - so maybe? -more important than memory are network calls -need to update on tui: - - state changes (hopefully less frequent) - - current value (~5 seconds - ~30 minutes) -store both client and server side - - only store actively view data client side to prevent leaks - - store full struct but only serve as request response to prevent extra copies - -system struct - - mapping of reactor ids to "Reactor" structs - - reactor is mapping of addr to sensor structs - - sensor struct is basic info - - device type (enum vs string) - - device status (enum vs string) - - device most recent value (int? vs string?) - - if offline last seen time - -notes on struct - - should ideally have locks at reactor and sensor level - - use func to return sensor list via lock on reactor - - use func to update sensor list via lock on reactor - - use returned list to parse and request value from each sensor - - use goroutines and channels for efficient operation - - build response via returned structs - - respond to client - -note on tui manager - - ideally should keep simplified current client struct to spawn copies of the update daemons for each sensor - - each daemon should be EXTREMELY light weight and just feed new data values to the manager - - tuimanager will be responsible for efficently buffering for tui client requests - - tui pings should be frequent and response should be any data to update - - client side we should be able to essentialy overwrite any entries on our response - - simplifies interface - -data aggregation outline -Starting from sensor -1) specific sensor manager initiates a read of the embedded i2c dev -2) on success read gets logged with the time to the internal txt log (json) -RLC loop: - 3) rlc has long running method with sub routines reading each log and adding pending entries to the buffer - - buffer is bounded and routines block when it fills (use to limit rpc reply length) - 4) on ping buffer is parsed into rpc reply - - send buffered log ids to cleanup routine but dont delete from log yet - 5) next req has transaction ids of previous data that have been acked - 6) send ids to cleanup process - 7) respond with new buffer repeat -RM side: -received data from rlc -1) send reply to data parsing goroutine -parser loop: - 1) start a completion listener - 2) read each device in reply - 3) start goroutine of db daemon for each dev with completion chan - 4) once reply is empty can end -db daemon loop: - 1) loop over device data entries - 2) initiate db connection - 3) parse through each data entry and send to db - 4) if it was succesfull send the transaction id to the completion channel -monitoring rpc loop: - 1) listen for completed transaction entries - 2) append entries to ack - 3) send to rm on ping timer - -Data is now in database for all intents and purposes - - - -process ensures that the data is collected -now the data is on the server -6) server sends grpc reply results to a parsing gorotuine -7) the parser loops over reply and spawns db daemons to enter info - - -should we even aggregate data? why would we not just write a db client as part of the rlc and let the sensor managers themselves log - -need to focus: - -2 major things going on - -rlc can do data stuff on the reactor itself and just use the db client - - relies on exposed db endpoint but can just handle auth stuff - - can log locally - -rlc also responds to status requests - - queries sensors for status - - adds to and sends reply - - recieves these pings <= 5 seconds apart - - should have down detection to kill db actions - - optionally include a "data" string of the most recent reading - -going to focus on status -want system to - init reactors - poll for status - respond with sensor info - view and manage on tui - -how? - -all structs only in status context - -rlc struct -- knows - - connected devs and managers -- is able to - - poll managers for state info -- relies on - - managers for accurate and fast data -- implements data aggregation for rm - -dm struct -- knows - - underlying i2c dev interface - - basic device info -- is able to - - respond to rlc status requests -- relies on - - rlc to ask for status -- implements - - status response - - -alright holy shit -i have rewritten the same five functions so many times - -time to take inventory - -right now the system has -a central coordinator -that can spawn managers -that can manage clients -and a reactor coordinator -that can spawn device managers -that can manage devices - -I have a rudimentary ping system that queries the reactors for their status - -where to go next - -I want to build in control before I worry about actual data -this means tui and removal of managers - -how to start? - -need to create a central struct that serves as a representation of the system - -map[reactorid] -> [dev addr] device info -reactor is online by nature of responding to a ping -device info is what we query for - -tui manager will request data from the server struct - -server struct should bridge between reactor coordiantor and terminal coordinator -needs to be concurrent -needs to synchronize incoming sensor data - -instead of a big stupid struct - - just have rm maintain struct for each reactor - - connect tui requests to rm - -pros - - prevent redundancies in data - - limit operations after the ping - - serve copies? - -what will this look like - -TODO 7/5 -time to flesh out the tui and move into actual sensor/db libraries - -tuitime: -tui client (user side) -*will most likely be divided -needs to know: -- ip address of host -- eventually - - username/pass auth -needs to be able to -- request system info via server -- establish connection to central server -implements -- basic system management functionality -relies on - -- server for up to date reactor and sensor data - - -TUI TIME -coordinator/listner/reactor seem to be stable - - will have to add exiting for tui manager - -need to create the actual TUI at this point -seperate into two logical aspects - - The client that gets the system info from the server - - the tui which actually displays this info into the tui - -how to share the data between the client and tui? -- structs - - pros - - very efficient - - cons - - risky -- chan - - pros - - synchronize stuff - - cons - - hard to set up and finnicky -- methods - - pros - - syncronized - - easy to implement - - cons - - need to store/embed struct - -systemviewer.go -TODO 7/7 -time to make the synchronous system view -have a few ideas for the approach - a) make RM responsible for maintaining their own "branch" and store pointers to their branch - b) store the branch centrally and have the RM post changes - -I like the idea of a central system viewer and you post updates to worker clients - a) posting updates vs calling methods - blocking vs non blocking - b) - -lets layout expectations -RM should keep a current view of itself and whether it is online - - this should persist despite reactor outage - - in case of outage every sensor is UKNOWN - - optional last seen time for sensors/reactor - - exit should save to memory? persist for a given id? - - have a removal mechanism - - use case is to purge dead mechanism aka no black list - - each rm keeps most recent sensor view or reactor view in mem and can accept incoming system viewer connections -system viewer clients - - spawn 1 per tui client - - can do 2 things to any rm - a) call it for a complete copy which needs to be fast (gRPC?) - b) latch onto update chan to build its own copy for when the client requests the new devices - - follow a buffer -> client -> delete life cycle -system viewer "server" - a) spawn new system veiwer clients and direct them to the proper reactors - -aside: can we use this to replace coordinator system? just make a system directory - -what are we already storing? - in coordinator we have a mapping of ids to managers for reactor and - what if we remap system viewer and coordiantor to system coordinator which does both - seems redudent to keep multiple system copies - - any speed concerns? Client connections spawning new managers? - - we must lock map - - channels is probably the answer here, just desync the whole process from itself - - listener gets incoming clients - - clients get parsed into a "Client" object and sent to system coodiantor - - types have slight diffences but essentially: - 1) see if there is a stored manager or if we need to create one - 1.5) create manager if it doesnt exits - 2) start the manager with the client details - 3) create 2 chans (<-reactor tui<-) for reactor & device info - now the divergence - Reactor Manager: - 1) Connect to reactor and send initial ping - - if reactor ever doesnt respond (maybe backoff for unavailable) just kill manager and send offline to reactor status chan - 2) As device info comes in start maintaining a system struct - this must persist exits and starts - 3) For the sensor info coming in, send a copy on the chan to the void for all youre concerned - 4) Respond to requests for entire system copies as clients initially connect - - probably just a method - 5) only need to send reactor status on changes aka starts and exits - TUI Manager: - 1) Buffer the currently known reactor status via chan hook - 2) Wait (Timeout) for connection for tui client - - probably sub 5 seconds before we exit - - need to keep track via a last seen - 3) respond with the buffered reactor info - 4) on request for specific info - - request system viewer for the reactor info which will return the reactors sensor chan - - spawn goroutine to hook onto this chan and maintain a "local" copy of the new data for the client - - can probably make this more efficient but f it - - biggest buffer gets is # devs * size dev struct (bytes) - - drop anything but most recent - 5) as client requests for info we either reply with the buffer from the hook or create a new buffer - 6) translates into pages client side which are more efficent - 7) could even look at batching these eventually - 8) should probably kill the listeners (atleas the chan) when the tui client - a) disconnects - b) goes idle - - System Coordinator must then - 1) have a method to handle client connections that is concurrent safe - 2) start client managers on connection - 3) keep a directory of all the channels for clients for device and reactor info - 4) link tui client managers to rm properly -no need for a name change coordinator will have - system viewing functions in systemview.go - -alright check in time - -now have system viewer -which embeds a straight up info stream -and a map from reactor ids -> Info streams - -InfoStreams are structs with methods for adding listeners and senders -both return monitor objects which you can either - Send(device info) -or GetBuffer() and ClearBuffer([]da) - -this is all the foundation -just need to wrap into a thing the coordinator can useor maybe even replace coordinator - -systemviewer has 4 methods -every tui manager will embed a reactor listener -every reactor manager will embed a reactor sender -when a tui client selects a reactor we will embed the device listener -every reactor will be its own device sender - -the only thing that happens after init is tui may add device listeners - - - -should unify so that its only 1 ping request or a special request when we go to a page for the first time - -ex devinfo - -{ -Id uint32 // either id or addr -Type string //['reactor','device'] set by rlc -Status string //set by sm -Data string //optional -Index //added by monitor for consistent location in tui -Transaction ID //added by server and discarded in reply - -I may just scrap this shit in favor of a synced view -overhead is probably minimal anyway -redo listener bullshit to just route to the cs -tui clients will just get a fresh copy of the reactor info and device infofor every request - - -ADDING STANDARDIZED LOGGING -adding a log package for all packages -logs to a file named after start time -going to be of format - -TIME PROC CODE ID MSG -so -00013 STRT COR 912939123 Central coordinator started -00033 STRT -CODES -CCO - Central Coordinator -RCO - Reactor Coordinator -TCO - TUI Coordinator -RMA - Reactor Manager -TMA - TUI Manager -RLC - Reactor Level Coordinator -DMA - Device Manager -TUI - TUI Client - -every debug message will be of format -topic, devcode: id - -alright time to get this damn tui updating working - -general implementation details - -- libary card method - - only allow clients to checkout 1 device stream at a time (besides reactor stream obviously) - - close stream when call to open new one - - should we even store the reactor devices locally? - - or we could request when its selected and then let stream take care of itself -- simplifies a few things - - same setup for reactors/devices - - just call/create device listeners dynamically and reactor listeners at the start - - only check out reactor stream and 1 device stream at a time - - request for devices gets you the current state and adds your listener to the echo chain so that you recieve any updates - - need to ensure sends can complete even if the manager is dead - - close the channel? - - - -docker time -Need to refactor code so its eaisier to run in both envs -Refactoring server code now - - bind all gRPC services to the same IP:port to make it efficent - - funnel all rpcs through the one external port - - could even use nginx to route from default :80 -is there ever a situation where I would need to run this not on docker? - - can i just hardcode for docker and then rely on nginx for routing etc? - - - -ALRIGHT TIME TO LOCK TF IN -#TODO 8/1 -Time to set up proper config loading and storing - -Doing it all through interfaces and tagged structs - -On start up -Server needs to load up its own config - - take action on that config -wait for client connections - - load client config and reply with associated data -on client disconnect - - store any updates and return to idle state -restructing away from "listener" and coordiantor and stuff -going to just have central server -with an embedded listener -and database and shit -so -SERVER will call NewServer which will take care of subsequents - -# TODO 8/5 -Config storing time -going to probably have to add admin database client(aka server client which makes 0 sense) -can abstract all operations through interface and plugable package - -I just reliazed I coupled my mechanism with influxs token thing because it wokrs well but I am going to have to completely rebuild that if its properietary or we transition to a new DB - - hopefully null point due to the nature of OSS and time series - -CONFIG (and by extension DB) - -config.UpdateReactor(id, key, value) -config.UpdateSelf(key, value) - -should just be a basic way to update a given entry for an reactor -seperating server and reactor methods should lead to less finicky behaviour -should also probably wrap these in a seperate struct - - methods? - - who can call? - - who stores ptr? - - do we even need a ptr? can configs be stored and loaded statically or is that a bitch on the FS - -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 - -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 - -implementation time -#TODO 9/4 -Might be dying nbd - - i think its just freshman flu but could be clot who knows - -on to code -Need to have a functional BETA by 9/15 at the latest - pref 9/8 with a week to test - -What do we NEED out of FRMS v0.1.0 (pre-alpha - as an aside v1.#.#-apha then v.1.#.#-beta for versions) - -Needs: - - Connect and disconnect at will - - set sample and log rate - - set name - - live view data - - export expiriement data to CSV - -Notes: - - all sensors will be atlas - - can leverage for a unified library - - can use grafana for the UI - - can bash script to eport data for a given time range into resspective sheet aka sheet of DO measurements etc. - - can setuo the format pretty easily and probably just print F the query worst case I mean its 3 data points at probabnly 1 sample per minute at worst - -Architecture planning phase - -What would each need require software wise - -Need: Connect and disconnect at will - - directory of which device manager to load - - a way to store and load settings - - a way to monitor the i2c lines for new devices - - -Config interface - At a core -Load() - - load keys, config and env - - prompt for any updates - - store said updates - - store any future requests - -functions both server and reactor will use: -- load config -- load keys - - dif keys -- load env - - dif env - -order of ops - load config - load keys and env to overwrite config - store updates - have config with methods to get/set values - - - - - - diff --git a/internal/notes/archive/structure b/internal/notes/archive/structure deleted file mode 100644 index 7cc3e54..0000000 --- a/internal/notes/archive/structure +++ /dev/null @@ -1,103 +0,0 @@ -this will be a living doc - -starting with for connection management: - -listener: -- knows - - ip:port to listen to - - clients connect with{ip, port, clientType, model, id} -- is able to - - create coordinators for each clientType - - send new clients to coordiantor handlers via chan -- depends on - - clients sending known data (gRPC) - - NewCoordinator func -- implements - * shouldnt really have any callable methods - -coordinator: (General) -- knows - - what client ids have already connected - - which managers correlate to which clients -- is able to - - create managers for new clients - - start managers for clients -- depends on - - listener for client structure - - manager for NewManager() function -- implements - - client connection handling - - general manager call - -manager (general): -- knows - - client info - - timeout - - if it is active -- is able to - - establish a connection with a client - - stop when the connection drops -- depends on - - coordinator for start calls -- implements - - client connection creation - - client info storage - -manager (reactor): -* embedds the gm -- knows - - devices attached to the reactor - - underlying client manager -- is able to - - maintain device struct - - no pings only control logic (i.e remove device, restart etc) -- depends on - - gm for client conn - - coordiantor for starts -- implements - - reactor structure tracking - -manager (tui): -* embedds the gm -- knows - - structure of the system (ie reactors:devices) - - underlying client manager -- is able to - - keep track of system changes - - updates client from buffer or concurrent grpc? -- depends on - - RM to get system info - - coordinator for starts -- implements - - system tracking - -reactor level coordinator: (right away this feels so fat compared) -- knows - - current connected devices - - server to init to - - its own hwinfo to establish itself as a client -- is able to: - - reach out to server on boot - - transmit client details - - keep reactor devices current -- depends on - - I2C package to notify of connected devices - - hardware info to get its client info - - server to handle connection - - sm for new manager -- implements - - reactor status handler for updates to other coords/managers - -device itself: -- knows - - probe status ( maybe) - - data in buffer -- is able to - - clear buffer on request - - respond to commands -- implements - - data collection - - control execution -- depends on - - nothing its a driver - - maybe the control logic?? diff --git a/internal/notes/index.md b/internal/notes/index.md deleted file mode 100644 index 24f3e20..0000000 --- a/internal/notes/index.md +++ /dev/null @@ -1,4 +0,0 @@ -## Weekly Planning - -[Jan 16-20](weekly/Jan-16-20.md) -[Jan 23-27](weekly/Jan-23-27.md) diff --git a/internal/notes/weekly/Jan-16-20.md b/internal/notes/weekly/Jan-16-20.md deleted file mode 100644 index ceb8d75..0000000 --- a/internal/notes/weekly/Jan-16-20.md +++ /dev/null @@ -1,149 +0,0 @@ -# Jan 18 -### Planning -**Monitoring Changes** - -I want to refactor the reactor stuff to be less method oriented as far as data collection. For example, the monitoring stuff is all about events that happen pretty infrequently. It makes sense to then use a channel on the device side to just feed relevant status updates back to the reactor. I think that this makes the most sense because this will synchronize updates and leverage the rarity of events to cut down on errant calls. -- pros - - less repitive method calls needed - - less device locking - - localize the information to different packages -- cons - - extra memory for channels and duplicate storage info - - could just remove status from dm? - -**New Idea** - -I can leverage wireguard to do server-> reactor connections even beyond the testing phase - -Changes: -1) move device coordinator into device package -2) expose relevant methods to reactor interface -3) clarify individual package responsibilities -4) add stuff server side to create/destroy grpc connections as the information is rendered client side - - this might be scuffed but oh well - -### Package Separation -**Reactor** -- coordinator - - creates initial link to the server - - creates database client - - creates and starts a device coordinator - -**Device** -- coordinator - - searches i2c bus for connected devices - - spins up managers to control the connected devices - - relays information back up to the reactor coordinator -- manager - - control over singular device - - has the core information that will be needed across any type of device (name, status, address etc) -- sub-manager - - fine grained struct with methods specific to the device - -**Server** - -Going to ignore for now because I am lazy -- central coordinator starts up database connection config etc -- reactor coordinator - -### TODO -**Monitoring Changes** -- [] change methods to channel based - - [] internal methods with spins - - [] pass structs with interface for methods - - -# Jan 19 - -### Orginizational changes - -What structure makes the most sense for the devices? - -#### Top-Down - -Ex) DeviceManager -> SensorManager -> DOManager -> Manager - -**Pros** -- probably a less complex interface layout? - - -**Cons** -- annoying to keep/pass state - - i.e. atlas needs the address to pass to the I2C but right now the devicemanager is storing that. Have to pass down via start which doesn't make a ton of sense - -#### Bottom-Up - -Ex) DOManager -> SensorManager -> DeviceManager -> Manager - -**Pros** -- top level manager has access to common info - - i.e. address, name etc -- can easily define common functions and use this to pass info upwards -- still don't have to import device manager as interfaces can handle getting/setting stuff - -**Cons** -- might get ugly with interfaces - - there might have to be a bunch of interfaces in the device package to handle nesting the manager itself - - this might not be true though as the device coordinator dictates what interfaces are needed, and already it doesn't really use any of the dm functionality - -**What would it look like?** -Device coordinator would call NewDeviceManager, - -### Outline of functionality - -Hopefully by going over what is expected of each manager, it will become clear what the layout should look like - -**Device Coordinator** -- responsibilities - - starting/stopping device managers as devices connect/disconnect - - maintaining a map of the devices and their status - - updating the server with this information at set intervals - - pass the I2C client to the device managers - -**Device Manager** -- responsibilities - - struct to store information that is used by any type of device - - i.e. Address, Name, Config(prefix and file)? Status? - - probably don't need status as this can be determined via IsActive() - - config might be helpful to have, could pass up to managers via a Get function - - start/stop as requested by the device coordinator -- serves - - broad functions such as SetName(), GetName(), etc. - -**Sensor/Controller Manager** -- responsibilities - - provide corresponding broad struct that will be consistent across types of each - - i.e. sensors all have sample rate - - provide methods all will use such as TakeReading() -- serves - - more specific functions such as GetSampleRate(), Set... - -**Specific Managers** -- responsibilities - - provides specific functions that a certain sensor/controller might need - - i.e. pwm will need setFreq, DO might need a conversion etc. - - broadly will need access to I2C for comms -- serves - - Hyper Specific functions such as SetFreq() etc. - -### Trying Bottom-Up - -Right now, I am using some hybrid format which doesn't really make any sense. It goes - -DeviceManager -> DOManager -> SensorManager -> Manager - -This just feels *wrong* - -**Changes** -- Going to use the specifc -> broad becaus it seems intiuitive - - the most common methods/information is at the base and propogates up through the more specific managers - - should make it simplier to define -- 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 - -#### For tomorrow -What I have now works, but it is still pretty backwards. Need further improvements and need to start thinking about what a websocket might look like in the current model diff --git a/internal/notes/weekly/Jan-23-27.md b/internal/notes/weekly/Jan-23-27.md deleted file mode 100644 index 76afd80..0000000 --- a/internal/notes/weekly/Jan-23-27.md +++ /dev/null @@ -1,49 +0,0 @@ -# Jan 23 - -### Connecting Clients to reactors - -**Client -> Server -> Reactor** - -I can take advantage of the private network created via wireguard to allow the server to connected back to individual reactors and then intiate gRPC calls. - -**Pros** -- This *VASTLY* simplifies the implementation as I can now connect back to the reactors themselves - - from there, I can implement various functions I will need server side - - i.e. GetName() SetName() etc. - -**Cons** -- I will eventually need to build the wiregaurd implementation - - although because its all local network for now, I can plug and play down the road - -### TODO -- refactor packages to provide a cleaner interface via simple commands as opposed to the convoluted passing structure that was present with the old I2C library -- start working on the interface between the websocket and the reactor - - react side this is the actual content that will be rendered by the client - - server side this will be a connection to a reactor with the gRPC calls -- moving monitoring functionality to the reactor - - refactoring to use streaming functionality to avoid needing to re initiate request - - have server connect each reactor manager to the rlc - - have the reactor manager ping for server info - - handle disconnects via exit - - sets up cleaner device handling via multiplexing - -# Jan 24 - -### Proto changes - -It's time to refactor the current protobuf stuff to make more sense from the servers perspective. In this sense, I am going to have the reactor provide connection details to the server on connect, and then the server can connect/disconnect at will. - -### Outline -- Update the server to connect to the reactor itself for the information -- Decide what information is important enough to send to the server consistently, vs what only is needed upon "further inspection" - - need reactor information on connect - - need basic device information such as address and status - - when selected - - need specific device breakouts with advanced functions per device - - this can be multiplexed over the same gRPC connection and can be fulfilled by the device coordinator - - dc will catch all incoming requests and forward to the correct DM based on address - -### TODO -- reverse monitoring stuff - - make it so reactor manager has a timeout/ recognizes disconnects gracefully - - convert monitoring to a stream as opposed to consistent calls diff --git a/internal/pkg/i2c/bus.go b/internal/pkg/i2c/bus.go index 12a55d4..7ba012e 100644 --- a/internal/pkg/i2c/bus.go +++ b/internal/pkg/i2c/bus.go @@ -1,7 +1,9 @@ +// package i2c wraps the [i2c-tools] commands to interact +// with devices on the buss +// +// [i2c-tools]: https://manpages.debian.org/unstable/i2c-tools/index.html package i2c -// file has general wrappers to interact with i2c-tools - import ( "FRMS/internal/pkg/logging" "bytes" @@ -12,56 +14,75 @@ import ( "strings" ) +// GetConnected returns a map of each device address and its current +// connection status. 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 + cmd := exec.Command("i2cdetect", "-y", "-r", bus) + var out bytes.Buffer var errs bytes.Buffer cmd.Stderr = &errs cmd.Stdout = &out + if err := cmd.Run(); err != nil { - logging.Debug(logging.DError, "I2C error performing scan. %v", errs.String()) + + logging.Debug( + logging.DError, + "I2C scan error %v", + errs.String(), + ) + return devices, err } + // parsing the command output outString := out.String() - // could split by \n too split := strings.SplitAfter(outString, ":") - // 1st entry is garbage headers and ending is always \n##: + + // 1st entry is reserved and ending is always \n##: split = split[1:] + // create empty slice for all the devices for i, v := range split { - lst := strings.Index(v, "\n") - trimmed := v[:lst] + lastDevice := strings.Index(v, "\n") + + trimmed := v[:lastDevice] trimmed = strings.Trim(trimmed, " ") - // trimmed now holds just possible sensor addresses + count := strings.Split(trimmed, " ") + for j, d := range count { // the first row has to be offset by 3 but after its just i*16 + j - offset := 0 + offset := j if i == 0 { - offset = 3 + offset += 3 } - addr := i*16 + j + offset + + addr := i*16 + offset + if !strings.Contains(d, "--") && !strings.Contains(d, "UU") { - // active devices[addr] = true } } } + return devices, nil } +// SendCmd sends an arbitrary command string to the device at addr on i2c bus b. +// Command will be converted from a string to bytes before +// attempting to be sent. 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) - operation := "r20" // default read - frmt_cmd := "" // empty cmd + // default to an empty read + operation := "r20" + frmt_cmd := "" if command != "" { // command, do write operation = fmt.Sprintf("w%d", len(command)) // write @@ -75,14 +96,19 @@ func SendCmd(b, addr int, command string) (string, error) { // reading cmd = exec.Command("i2ctransfer", "-y", bus, fmt.Sprintf("%s@0x%x", operation, addr)) } - // exec command + + // execute command var out bytes.Buffer var errs bytes.Buffer cmd.Stderr = &errs cmd.Stdout = &out + if err := cmd.Run(); err != nil { - logging.Debug(logging.DError, "I2C error getting data! %v", err) + + logging.Debug(logging.DError, "I2C command error %v", err) + return "", err } + return out.String(), nil } From c58ad98e4e1386717c5bf4b0c7f49874bfbc76af Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 13:47:50 -0400 Subject: [PATCH 16/44] cleaning up old files and working on readme --- README.md | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c18b524..c1aee79 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,14 @@ This branch will serve as the staging ground for adding unit tests and documenta * [Getting Started](#getting-started) * [Installation](#installation) * [Usage](#usage) -* [Wiki](#wiki) +* [Technical Information](#technical-information) + * [Overview](#overview) + * [Server](#server) + * [Reactor](#reactor) + * [Networking](#networking) + * [GUI](#gui) + * [API](#api) + * [Future Work](#future-work) ## Introduction @@ -20,5 +27,19 @@ This branch will serve as the staging ground for adding unit tests and documenta ### Usage -## Wiki +## Technical Information + +### Overview + +### Server + +### Reactor + +### Networking + +### GUI + +### API + +### Future Work From 94d3acc5dabdb7993a8c0822599eafdfcfc2d71a Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 13:54:43 -0400 Subject: [PATCH 17/44] cleaning up old files and working on readme --- wiki/server.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 wiki/server.md diff --git a/wiki/server.md b/wiki/server.md new file mode 100644 index 0000000..70b6706 --- /dev/null +++ b/wiki/server.md @@ -0,0 +1,3 @@ +# Server Information + +## This is a sample From 4724ff666476c0945c0e72dc9a49e20d90b23b16 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 13:56:29 -0400 Subject: [PATCH 18/44] cleaning up old files and working on readme --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c1aee79..ce97ac8 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,9 @@ This branch will serve as the staging ground for adding unit tests and documenta * [Usage](#usage) * [Technical Information](#technical-information) * [Overview](#overview) - * [Server](#server) + * [Server](/wiki/server) * [Reactor](#reactor) + * [Hardware](#hardware) * [Networking](#networking) * [GUI](#gui) * [API](#api) @@ -21,6 +22,10 @@ This branch will serve as the staging ground for adding unit tests and documenta ## Introduction +FRMS serves as both an internal framework for interacting with reactors as well as a scalable customer facing application. +The project makes heavy use of low-cost yet powerful embedded systems capable of running full Linux distributions. +The [BeagleBone Black](https://beagleboard.org/black) is an example of the type of board used + ## Getting Started ### Installation @@ -31,8 +36,6 @@ This branch will serve as the staging ground for adding unit tests and documenta ### Overview -### Server - ### Reactor ### Networking From 69f1402e7670148b9e1f20d5240c89bd5f80b9b8 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 14:04:26 -0400 Subject: [PATCH 19/44] adding wiki folder because github hates free accounts --- README.md | 18 +++++++++--------- wiki/api.md | 3 +++ wiki/future-work.md | 3 +++ wiki/gui.md | 3 +++ wiki/networking.md | 3 +++ wiki/wiki.md | 14 ++++++++++++++ 6 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 wiki/api.md create mode 100644 wiki/future-work.md create mode 100644 wiki/gui.md create mode 100644 wiki/networking.md create mode 100644 wiki/wiki.md diff --git a/README.md b/README.md index ce97ac8..66e8f8a 100644 --- a/README.md +++ b/README.md @@ -10,15 +10,15 @@ This branch will serve as the staging ground for adding unit tests and documenta * [Getting Started](#getting-started) * [Installation](#installation) * [Usage](#usage) -* [Technical Information](#technical-information) - * [Overview](#overview) - * [Server](/wiki/server) - * [Reactor](#reactor) - * [Hardware](#hardware) - * [Networking](#networking) - * [GUI](#gui) - * [API](#api) - * [Future Work](#future-work) +* [Wiki](wiki/wiki.md) + * [Overview](wiki/wiki.md#overview) + * [Server](wiki/server.md) + * [Reactor](wiki/reactor.md) + * [Hardware](wiki/reactor.md#hardware) + * [Networking](wiki/networking.md) + * [GUI](wiki/gui.md) + * [API](wiki/api.md) + * [Future Work](wiki/future-work.md) ## Introduction diff --git a/wiki/api.md b/wiki/api.md new file mode 100644 index 0000000..cfb474b --- /dev/null +++ b/wiki/api.md @@ -0,0 +1,3 @@ +# API + +This will describe the idea for the API diff --git a/wiki/future-work.md b/wiki/future-work.md new file mode 100644 index 0000000..3b4eb14 --- /dev/null +++ b/wiki/future-work.md @@ -0,0 +1,3 @@ +# Future Work + +This will describe where to take the project from here diff --git a/wiki/gui.md b/wiki/gui.md new file mode 100644 index 0000000..0fc9611 --- /dev/null +++ b/wiki/gui.md @@ -0,0 +1,3 @@ +# GUI + +This will describe the basic outline for the front-end gui diff --git a/wiki/networking.md b/wiki/networking.md new file mode 100644 index 0000000..24352c4 --- /dev/null +++ b/wiki/networking.md @@ -0,0 +1,3 @@ +# Networking + +This will describe how the reactor/server/client talk to each other diff --git a/wiki/wiki.md b/wiki/wiki.md new file mode 100644 index 0000000..acc1d23 --- /dev/null +++ b/wiki/wiki.md @@ -0,0 +1,14 @@ +# Wiki + +## Overview + +This is an example of the overview section of the wiki + +## Table of Contents + +* [Server](server.md) +* [Reactor](reactor.md) +* [Networking](networking.md) +* [GUI](gui.md) +* [API](api.md) +* [Future Work](future-work.md) From d8af5314c1c5569f6316ec1cff5930d50476401d Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 14:05:47 -0400 Subject: [PATCH 20/44] adding wiki folder because github hates free accounts --- wiki/reactor.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 wiki/reactor.md diff --git a/wiki/reactor.md b/wiki/reactor.md new file mode 100644 index 0000000..a3c6394 --- /dev/null +++ b/wiki/reactor.md @@ -0,0 +1,7 @@ +# Reactor + +This will talk about the reactor setup + +## Hardware + +This will describe the hardware used From e0c4a2a612ab85066886acbe10ad457b7fd5d806 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 14:12:20 -0400 Subject: [PATCH 21/44] basic introduction --- README.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 66e8f8a..a7e870b 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,16 @@ This branch will serve as the staging ground for adding unit tests and documenta ## Introduction -FRMS serves as both an internal framework for interacting with reactors as well as a scalable customer facing application. -The project makes heavy use of low-cost yet powerful embedded systems capable of running full Linux distributions. -The [BeagleBone Black](https://beagleboard.org/black) is an example of the type of board used +FRMS serves as both an internal framework for testing reactor designs as well as a scalable customer facing application for monitoring and control. +The project makes heavy use of low-cost yet powerful embedded systems capable of running full Linux kernels. +Examples include the [BeagleBone Black](https://beagleboard.org/black) which was heavily used in development of FRMS as well as the popular [Raspberry Pi 4](https://www.raspberrypi.com/products/raspberry-pi-4-model-b/). +For more information about the hardware used in the reactors see [here](wiki/reactor.md#hardware). + +In its current state, FRMS is very bare bones and exists mostly as a proof of concept. +There are several routes to improving the software described in the [future work](wiki/future-work.md) section. +To get started with FRMS see [here](#getting-started). +For further information about any of the technical information see [here](wiki/wiki.md). +For any bugs/questions please file an issue [here](https://github.com/fl-src/FRMS/issues/new). ## Getting Started From f68159e7cb76f4fa7b0198c0ff05a3c1e67e07e9 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 14:16:31 -0400 Subject: [PATCH 22/44] basic introduction --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a7e870b..14e72f1 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,11 @@ Examples include the [BeagleBone Black](https://beagleboard.org/black) which was For more information about the hardware used in the reactors see [here](wiki/reactor.md#hardware). In its current state, FRMS is very bare bones and exists mostly as a proof of concept. -There are several routes to improving the software described in the [future work](wiki/future-work.md) section. -To get started with FRMS see [here](#getting-started). -For further information about any of the technical information see [here](wiki/wiki.md). -For any bugs/questions please file an issue [here](https://github.com/fl-src/FRMS/issues/new). +Quickly navigate to: +- [Getting started](#getting-started) +- [Improving the project](wiki/future-work.md) +- [More information](wiki/wiki.md) +- [Bugs/questions](https://github.com/fl-src/FRMS/issues/new) ## Getting Started From d4ed5c9a43c918a33f1eafc7a27d5e6e915c4e96 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 14:23:16 -0400 Subject: [PATCH 23/44] cleaning up old files and working on readme --- README.md | 7 +++++++ wiki/reactor.md | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/README.md b/README.md index 14e72f1..bee19cd 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,13 @@ Quickly navigate to: ## Getting Started +### Development + +FRMS was initially written almost entirely in [Go](https://go.dev/) with some bash/python helper scripts. +Go was used primarily for its emphasis on readable and maintainable code as well as its approach to concurrent applications. +Creating software capable of scaling up to many reactors made heavy use of [Goroutines](https://go.dev/tour/concurrency/1) especially on the server. +To begin developing use `git clone git@github.com:fl-src/FRMS.git` to clone the repository. + ### Installation ### Usage diff --git a/wiki/reactor.md b/wiki/reactor.md index a3c6394..591102b 100644 --- a/wiki/reactor.md +++ b/wiki/reactor.md @@ -5,3 +5,7 @@ This will talk about the reactor setup ## Hardware This will describe the hardware used + +## I2C + + From ef7b50bf668d321540328b8daff8f4e44d81a1aa Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 14:31:07 -0400 Subject: [PATCH 24/44] cleaning up old files and working on readme --- README.md | 14 ++++++++------ wiki/reactor.md | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index bee19cd..d436af9 100644 --- a/README.md +++ b/README.md @@ -36,15 +36,17 @@ Quickly navigate to: ## Getting Started -### Development - -FRMS was initially written almost entirely in [Go](https://go.dev/) with some bash/python helper scripts. -Go was used primarily for its emphasis on readable and maintainable code as well as its approach to concurrent applications. -Creating software capable of scaling up to many reactors made heavy use of [Goroutines](https://go.dev/tour/concurrency/1) especially on the server. -To begin developing use `git clone git@github.com:fl-src/FRMS.git` to clone the repository. +For specific information about decisions made in development see [here](wiki/wiki.md). ### Installation +The project uses a make alternative called [task](https://github.com/go-task/task) written in go for building and testing. +After using `git clone git@github.com:fl-src/FRMS.git` to clone the repository, you can then build binaries of the two commands `server` and `reactor` for testing. +The binaries will be put into the `bin/` folder and will be labeled with the platform and architecture they were built for. + +**WARNING**: The reactor binary currently relies on a Linux cli application to interact with the i2c bus. +This may cause undefined behavior when run on a device without the tools installed. More information about this design choice can be found [here](wiki/reactor.md#i2c-issues) + ### Usage ## Technical Information diff --git a/wiki/reactor.md b/wiki/reactor.md index 591102b..7c8d478 100644 --- a/wiki/reactor.md +++ b/wiki/reactor.md @@ -8,4 +8,45 @@ This will describe the hardware used ## I2C +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +Should not link here +### [Issues](#i2c-issues) + +Should link here From 02e306cb3232181a2ddaee8fd9a23d489d57cef1 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 14:32:48 -0400 Subject: [PATCH 25/44] cleaning up old files and working on readme --- wiki/reactor.md | 41 +---------------------------------------- 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/wiki/reactor.md b/wiki/reactor.md index 7c8d478..a373c6d 100644 --- a/wiki/reactor.md +++ b/wiki/reactor.md @@ -8,45 +8,6 @@ This will describe the hardware used ## I2C -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here -Should not link here - -### [Issues](#i2c-issues) +### Issues Should link here From 79045f830080b3d1cf0192a736f5c014411e9138 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 14:34:36 -0400 Subject: [PATCH 26/44] cleaning up old files and working on readme --- wiki/reactor.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wiki/reactor.md b/wiki/reactor.md index a373c6d..84f06f6 100644 --- a/wiki/reactor.md +++ b/wiki/reactor.md @@ -8,6 +8,6 @@ This will describe the hardware used ## I2C -### Issues +### Issues Should link here From 1cb29899b9c2aa3e21bb74a286d203e9a379d773 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 14:35:44 -0400 Subject: [PATCH 27/44] cleaning up old files and working on readme --- wiki/reactor.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/wiki/reactor.md b/wiki/reactor.md index 84f06f6..72f1bd0 100644 --- a/wiki/reactor.md +++ b/wiki/reactor.md @@ -8,6 +8,8 @@ This will describe the hardware used ## I2C -### Issues + +### Issues + Should link here From 5085500786f9f1af9cfb4b29bffcae6835cd03fd Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 15:28:15 -0400 Subject: [PATCH 28/44] moved docker files to sub directory and cleaned up task file --- .gitignore | 4 - .task/checksum/go-build | 2 +- README.md | 4 +- Taskfile.dist.yml | 53 +++++-- build.sh | 149 ------------------ .../Dockerfile.reactor | 2 +- Dockerfile.server => docker/Dockerfile.server | 0 .../docker-compose.yml | 0 gotest.py | 89 ----------- reactor_build.sh | 44 ------ wc.sh | 2 - wiki/reactor.md | 5 - 12 files changed, 40 insertions(+), 314 deletions(-) delete mode 100755 build.sh rename Dockerfile.reactor => docker/Dockerfile.reactor (97%) rename Dockerfile.server => docker/Dockerfile.server (100%) rename docker-compose.yml => docker/docker-compose.yml (100%) delete mode 100755 gotest.py delete mode 100755 reactor_build.sh delete mode 100755 wc.sh diff --git a/.gitignore b/.gitignore index 2f90cba..d0f8570 100644 --- a/.gitignore +++ b/.gitignore @@ -21,10 +21,6 @@ bin *.tar.gz # logs *.log -# binaries generated in testing -cmd/server/server -cmd/reactor/reactor -cmd/tui/tui # task related .task diff --git a/.task/checksum/go-build b/.task/checksum/go-build index c7012f1..464558c 100644 --- a/.task/checksum/go-build +++ b/.task/checksum/go-build @@ -1 +1 @@ -b43ecff1fe53e18c4c9b756b32d38078 +fe0b33dbcc236558952eb8f9f91422c9 diff --git a/README.md b/README.md index d436af9..c818c63 100644 --- a/README.md +++ b/README.md @@ -44,8 +44,8 @@ The project uses a make alternative called [task](https://github.com/go-task/tas After using `git clone git@github.com:fl-src/FRMS.git` to clone the repository, you can then build binaries of the two commands `server` and `reactor` for testing. The binaries will be put into the `bin/` folder and will be labeled with the platform and architecture they were built for. -**WARNING**: The reactor binary currently relies on a Linux cli application to interact with the i2c bus. -This may cause undefined behavior when run on a device without the tools installed. More information about this design choice can be found [here](wiki/reactor.md#i2c-issues) +**WARNING**: The reactor binary currently relies on the Linux [i2c-tools](https://archive.kernel.org/oldwiki/i2c.wiki.kernel.org/index.php/I2C_Tools.html) to interact with the i2c bus. +This may cause undefined behavior when run on a device without the tools installed. More information about this design choice can be found [here](wiki/reactor.md#i2c) ### Usage diff --git a/Taskfile.dist.yml b/Taskfile.dist.yml index e1956da..780f4be 100644 --- a/Taskfile.dist.yml +++ b/Taskfile.dist.yml @@ -4,14 +4,19 @@ tasks: clean: desc: "clean all of the old binaries" cmds: - - rm -v bin/* 2>/dev/null + - rm -vf bin/frms_* 2>/dev/null + + test: + desc: "Runs the full test suite" + cmds: + - bin/gotest.py all: - desc: "cleans and builds all" - deps: [clean, bb, server] + desc: "builds arm reactor binaries and arm/amd server binaries" + deps: [arm32-reactor, arm64-reactor, arm64-server, amd64-server] - bb: - desc: "Builds and sends to the beaglebone" + arm32-reactor: + desc: "Builds reactor binary for 32 bit arm linux device" cmds: - task: go-build vars: @@ -19,31 +24,45 @@ tasks: GOARCH: "arm" GOOS: "linux" BUILD_DIR: "reactor" - - scp bin/reactor_linux_arm debian:~/ - server: - desc: "Builds server binary" + arm64-reactor: + desc: "Builds reactor binary for 64 bit arm linux device" + cmds: + - task: go-build + vars: + GOARCH: "arm64" + GOOS: "linux" + BUILD_DIR: "reactor" + + arm64-server: + desc: "Builds server binary for 64 bit arm linux device" + cmds: + - task: go-build + vars: + GOARCH: "arm64" + GOOS: "linux" + BUILD_DIR: "server" + + amd64-server: + desc: "Builds server binary for amd linux machine" cmds: - task: go-build - vars: + vars: + GOARCH: "amd64" + GOOS: "linux" BUILD_DIR: "server" - GOOS: "{{OS}}" - GOARCH: "{{ARCH}}" go-build: internal: true cmds: - - go build -o bin/{{.BUILD_DIR}}_{{.GOOS}}_{{.GOARCH}} cmd/{{.BUILD_DIR}}/main.go + - go build -o bin/frms_{{.BUILD_DIR}}_{{.GOOS}}_{{.GOARCH}} cmd/{{.BUILD_DIR}}/main.go sources: - internal/pkg/**/*.go - cmd/{{.BUILD_DIR}}/main.go generates: - - bin/{{.BUILD_DIR}}_{{.GOOS}}_{{.GOARCH}} + - bin/frms_{{.BUILD_DIR}}_{{.GOOS}}_{{.GOARCH}} env: GOARM: "{{.GOARM}}" GOARCH: "{{.GOARCH}}" GOOS: "{{.GOOS}}" - test: - desc: "Runs the full test suite" - cmds: - - ./gotest.py + diff --git a/build.sh b/build.sh deleted file mode 100755 index 3dbe3fe..0000000 --- a/build.sh +++ /dev/null @@ -1,149 +0,0 @@ -#!/bin/bash - -# adding commands -usage() { - # how to use this build script - cat </dev/null - else - read -p "Clean old builds?(y/n) " -n 1 -r - if [[ $REPLY =~ ^[Yy]$ ]] ; then - rm -v bin/* 2>/dev/null - fi - fi - printf 'Clean!\n' -} - -create_build() { - # create build for $1 - case $1 in - 'rpi' ) - printf 'Building for Raspberry Pi!\n' - GARCH="arm64" - PLATFORM="reactor" - ;; - 'bb') - printf 'Building for BeagleBone!\n' - GARCH="arm" - GARM="GOARM=7" - PLATFORM="reactor" - ;; - 's') - printf 'Building for Server!\n' - GARCH="amd64" - PLATFORM="server" - ;; - 'd') - printf 'Building for Desktop!\n' - GARCH="amd64" - PLATFORM="server" - ;; - * ) - printf 'ERROR: %s type unrecognized!\n' "$1" - usage - exit 1 - ;; - esac - # setting up build - OUTFILE=$(printf '%s_linux_%s' "$PLATFORM" "$GARCH") - INFILE=$(printf '%s/main.go' "$PLATFORM") - # building - env GOOS=linux GOARCH="$GARCH" $GARM go build -o bin/"$OUTFILE" cmd/"$INFILE" - echo "Finished" - if [[ "$SCP"=true ]] ; then - printf 'Attempting to transfer to %s\n' "$2" - if [[ "$1" == "bb" ]] ; then - printf 'Copying to %s\n' "192.168.100.90" - scp "$HOME/FRMS/bin/$OUTFILE" debian:~/ - else - printf 'SCP Not available!\n' - fi - fi -} - -# handle long form -for arg in "$@"; do - shift - case "$arg" in - '--help') set -- "$@" "-h" ;; - '--list') set -- "$@" "-l" ;; - '--scp') set -- "$@" "-s" ;; - '--clean') set -- "$@" "-c" ;; - '--force') set -- "$@" "-f" ;; - *) set -- "$@" "$arg" ;; - esac -done - -# handle args -while getopts "lcsfh" opt ; do - case "$opt" in - 'h' ) - usage - exit 0 - ;; - 'c' ) - clean_builds - ;; - 'f' ) - FORCE=true - clean_builds - ;; - 's' ) - SCP=true - ;; - 'l') - list_systems - ;; - '?' ) - usage - exit 1 - ;; - esac -done - -shift $(($OPTIND - 1)) - -for dev in "$@"; do - case "$dev" in - 'RaspberryPi') dev='rpi' ;; - 'BeagleBone') dev='bb' ;; - 'Server') dev='s' ;; - 'Desktop') dev='d' ;; - esac - create_build "$dev" -done -printf 'Nothing else to do!\n' - -# echo "Compressing binaries for distrubution" -# tar -czf pireactor.tar.gz -C bin reactor_linux_arm64 -# tar -czf bbreactor.tar.gz -C bin reactor_linux_arm -# tar -czf server.tar.gz -C bin server_linux_amd64 -# tar -czf tui.tar.gz -C bin tui_linux_amd64 tui_linux_arm tui_linux_arm64 diff --git a/Dockerfile.reactor b/docker/Dockerfile.reactor similarity index 97% rename from Dockerfile.reactor rename to docker/Dockerfile.reactor index 371d40e..43d7023 100644 --- a/Dockerfile.reactor +++ b/docker/Dockerfile.reactor @@ -3,7 +3,7 @@ FROM --platform=$BUILDPLATFORM golang:1.18-alpine as builder WORKDIR /app -COPY . . +COPY ../ . RUN go mod download diff --git a/Dockerfile.server b/docker/Dockerfile.server similarity index 100% rename from Dockerfile.server rename to docker/Dockerfile.server diff --git a/docker-compose.yml b/docker/docker-compose.yml similarity index 100% rename from docker-compose.yml rename to docker/docker-compose.yml diff --git a/gotest.py b/gotest.py deleted file mode 100755 index 1fa03c9..0000000 --- a/gotest.py +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env python -import json -import subprocess - -class PackageTest: - def __init__(self): - self.status = "" - self.tests = [] - self.totaltime = 0 - -res = {} - -output = subprocess.run(["go","test","-count=1","-json","./..."], capture_output=True, text=True) - -output = str(output.stdout) -output = output.split('\n') - -for line in output[:-1]: - # parse the json - parsed = json.loads(line) - action = parsed["Action"] - - # skip - if action in ["start", "output", "run"]: - continue - - # create blank if doesn't exist - if parsed["Package"] not in res: - res[parsed["Package"]] = PackageTest() - - pkg = res[parsed["Package"]] - - if "Test" not in parsed: - # top level package result - pkg.status = action - if "Elapsed" in parsed: - pkg.totaltime = parsed["Elapsed"] - else: - # individual test - pkg.tests.append((parsed["Test"],parsed["Action"],parsed["Elapsed"])) - -totalRan = 0 -totalPassed = 0 -totalTime = 0 -# generating output from parsed json -for name, info in res.items(): - pkgname = name.split('/') - pkgname = '/'.join(name.split('/')[1:]) - if info.status == "skip": - print("Skipped %s" % (pkgname)) - continue - - print("\nTesting %s:" % (pkgname)) - - passed = 0 - total = 0 - - for test in info.tests: - total += 1 - out = [] - if test[1] == "pass": - passed += 1 - out = [" " + test[0] + ":",'\033[32mpass\033[0m ',str(test[2]) + 's'] - elif test[1] == "fail": - out = [" " + test[0] + ":",'\033[31mfail\033[0m ',str(test[2]) + 's'] - - print(f"{out[0] : <30}{out[1] : >5}{out[2] : >8}") - - result = "" - if info.status == "pass": - result = "\033[32mPASSED\033[0m" - else: - result = "\033[31mFAILED\033[0m" - - # keep track of grand totals - totalRan += total - totalPassed += passed - totalTime += info.totaltime - - print(" %s %d/%d in %.3fs/n" % (result, passed, total, info.totaltime)) - - -# output overall test statistics -if totalRan == totalPassed: - result = "\033[32mPASSED\033[0m" -else: - result = "\033[31mFAILED\033[0m" - -print("\nSUMMARY:\n\t%s %d/%d in %.3fs" % (result, totalPassed, totalRan, totalTime)) diff --git a/reactor_build.sh b/reactor_build.sh deleted file mode 100755 index 455f838..0000000 --- a/reactor_build.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash - -display_usage() { - echo "Usage: $0 reactor_type" -} - -# checking for help options -if [[ $@ == "--help" || $@ == "-h" ]] -then - display_usage - exit 0 -fi - -# checking that arguements are not empty -if [[ -z $1 ]] -then - echo "Type of reactor not specified!" - display_usage - exit 1 -fi - -# checking for valid reactor types -if [[ $1 == "pi" ]] -then - platform="linux/arm64" -elif [[ $1 == "bb" ]] -then - platform="linux/arm/v7" -else - echo "Reactor type $1 not supported!" - echo "Supported reactors include: pi, bb" - display_usage - exit 1 -fi - -# building reactor image - -echo "Building Reactor image for $1 platform=$platform" - -docker buildx build --rm --platform=$platform -f Dockerfile.reactor --tag localhost:5000/reactor . - -echo "Cleaning local images" - -docker image remove localhost:5000/reactor diff --git a/wc.sh b/wc.sh deleted file mode 100755 index 3777a06..0000000 --- a/wc.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -echo $(git ls-files | grep .go | awk '!/pb/' | xargs wc -l | tail -n 1) diff --git a/wiki/reactor.md b/wiki/reactor.md index 72f1bd0..1b4cfaf 100644 --- a/wiki/reactor.md +++ b/wiki/reactor.md @@ -8,8 +8,3 @@ This will describe the hardware used ## I2C - -### Issues - - -Should link here From e3433e3a417974755013911e0fdce60978dd6f1d Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 15:32:16 -0400 Subject: [PATCH 29/44] purged influx db scripts, could be reverted if nessecary --- influxdb/startup/influxsetup.sh | 14 -------------- influxdb/startup/template.yaml | 6 ------ 2 files changed, 20 deletions(-) delete mode 100755 influxdb/startup/influxsetup.sh delete mode 100644 influxdb/startup/template.yaml diff --git a/influxdb/startup/influxsetup.sh b/influxdb/startup/influxsetup.sh deleted file mode 100755 index d78f709..0000000 --- a/influxdb/startup/influxsetup.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -#DB_URL=$(cat "$INFLUX_CONFIGS_PATH" | awk '/url/ {print $3}' | head -n 1) -DB_URL="frms-db-1:8086" - -TOKEN=$(influx auth list --user ${DOCKER_INFLUXDB_INIT_USER_ID} --hide-headers | cut -f 3) -ORG=$(influx org list | grep ${DOCKER_INFLUXDB_INIT_ORG_ID} | awk '{print $2}') -# creating starting server YAML -echo -e "server:\n db-url: ${DB_URL}\n db-org: ${ORG}\n db-token: ${TOKEN}" >/configs/server.yaml; - -# creating grafana yaml -influx user create -n grafana -o ${ORG} -GRAFANA_TOKEN=$(influx auth list --user grafana --hide-headers | cut -f 3) -echo -e "apiVersion: 1\n\ndeleteDatasources:\n\ndatasources:\n - name: INFLUXDB\n type: influxdb\n access: proxy\n url: ${DB_URL}\n jsonData:\n httpMode: GET\n httpHeaderName1: 'Authorization'\n secureJsonData:\n httpHeaderValue1: 'Token ${GRAFANA_TOKEN}'" >/grafana/datasources/datasource.yaml diff --git a/influxdb/startup/template.yaml b/influxdb/startup/template.yaml deleted file mode 100644 index d832594..0000000 --- a/influxdb/startup/template.yaml +++ /dev/null @@ -1,6 +0,0 @@ ----- -# ${gen_statement} -server: - db-url: "${db_url}" - db-org: "${db_org}" - db-token: "${db_token}" From f8c51d27a98c9e2ebda61463ce855871f829dc29 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 15:38:36 -0400 Subject: [PATCH 30/44] purging more files --- internal/api/reactor.go | 12 ------------ internal/configs/database.yaml | 3 --- internal/configs/db.env | 5 ----- 3 files changed, 20 deletions(-) delete mode 100644 internal/api/reactor.go delete mode 100644 internal/configs/database.yaml delete mode 100644 internal/configs/db.env diff --git a/internal/api/reactor.go b/internal/api/reactor.go deleted file mode 100644 index c1cb9eb..0000000 --- a/internal/api/reactor.go +++ /dev/null @@ -1,12 +0,0 @@ -package api - -import "errors" - -type ReactorCoordinator interface { - Start () -} - -type reactorCoordinator struct { - - -func StartReactor diff --git a/internal/configs/database.yaml b/internal/configs/database.yaml deleted file mode 100644 index afe6cc3..0000000 --- a/internal/configs/database.yaml +++ /dev/null @@ -1,3 +0,0 @@ -orginization: ForeLight -token: Zrtg0Q9u65HbFaK4KPWbl9y1xofJwsRHVwuWcIq3xvSOstVbjshDoRNjPiwsz31vIoP-GwDuGL8gzouEHqMuYg== -url: http://localhost:8086 diff --git a/internal/configs/db.env b/internal/configs/db.env deleted file mode 100644 index dbc19f5..0000000 --- a/internal/configs/db.env +++ /dev/null @@ -1,5 +0,0 @@ -INFLUXDB_USERNAME=admin -INFLUXDB_PASSWORD=admin -INFLUXDB_ORG=ForeLight -INFLUXDB_BUCKET=default - From 9086b8099484cecf6a557895336fd572cf5d4c16 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 15:44:37 -0400 Subject: [PATCH 31/44] purged websocket garbage, just going to use web grpc --- .env | 4 -- .task/checksum/go-build | 2 +- cmd/server/main.go | 13 ----- internal/pkg/websocket/connect.go | 89 ------------------------------- internal/pkg/websocket/reactor.go | 2 - 5 files changed, 1 insertion(+), 109 deletions(-) delete mode 100644 .env delete mode 100644 internal/pkg/websocket/connect.go delete mode 100644 internal/pkg/websocket/reactor.go diff --git a/.env b/.env deleted file mode 100644 index ab2e438..0000000 --- a/.env +++ /dev/null @@ -1,4 +0,0 @@ -INFLUXDB_USERNAME=admin -INFLUXDB_PASSWORD=forelight -INFLUXDB_ORG=ForeLight -INFLUXDB_BUCKET=test diff --git a/.task/checksum/go-build b/.task/checksum/go-build index 464558c..aed43fb 100644 --- a/.task/checksum/go-build +++ b/.task/checksum/go-build @@ -1 +1 @@ -fe0b33dbcc236558952eb8f9f91422c9 +0bbb9b59233e7c3a6b58764d03788b82 diff --git a/cmd/server/main.go b/cmd/server/main.go index 42f6bf5..1c217ca 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -8,7 +8,6 @@ import ( "FRMS/internal/pkg/config" "FRMS/internal/pkg/logging" "FRMS/internal/pkg/server" - "FRMS/internal/pkg/websocket" "os" "github.com/spf13/viper" @@ -26,14 +25,6 @@ func NewConfig(fname string) *viper.Viper { return config.LoadConfig(fname) } -type ws interface { - Start() -} - -func NewWebSocket() ws { - return websocket.New() -} - func main() { // lets get this bread gracefulShutdown := make(chan os.Signal, 1) @@ -47,10 +38,6 @@ func main() { c := NewCoordinator(conf, errCh) go c.Start() logging.Debug(logging.DStart, "CCO 01 Server %s started", conf.Get("name")) - // starting websocket server - - w := NewWebSocket() - go w.Start() select { case err := <-errCh: // blocking to wait for any errors and keep alive otherwise diff --git a/internal/pkg/websocket/connect.go b/internal/pkg/websocket/connect.go deleted file mode 100644 index 4378083..0000000 --- a/internal/pkg/websocket/connect.go +++ /dev/null @@ -1,89 +0,0 @@ -package websocket - -// creates websocket server and upgrades incoming connections -import ( - "encoding/json" - "fmt" - "net/http" - - ws "github.com/gorilla/websocket" -) - -type ReactorTest struct { - Id int `json:"id"` - Name string `json:"name"` -} - -type WebSocket struct { - // dummy struct for interface - N string -} - -func New() *WebSocket { - return &WebSocket{} -} - -func (s *WebSocket) Start() { - fmt.Println("Starting ws server!") - setupRoutes() - http.ListenAndServe(":8080", nil) -} - -// default opts allow all origins -var upgrader = ws.Upgrader{ - CheckOrigin: func(r *http.Request) bool { return true }, -} - -// reader -func reader(conn *ws.Conn) { - - for { - // read forever - //messageType, p, err := conn.ReadMessage() - _, p, err := conn.ReadMessage() - - if err != nil { - if ws.IsCloseError(err, ws.CloseNormalClosure, ws.CloseGoingAway) { - // normally closed - return - } - panic(err) - } - fmt.Printf("Msg: %s\n", string(p)) - } -} - -func serverWs(w http.ResponseWriter, r *http.Request) { - fmt.Println(r.Host) - - websocket, err := upgrader.Upgrade(w, r, nil) - - if err != nil { - panic(err) - } - - // try sending reactor - t1 := &ReactorTest{Id: 1111, Name: "test1"} - t2 := &ReactorTest{Id: 1112, Name: "test2"} - t3 := &ReactorTest{Id: 1113, Name: "test3"} - n := []*ReactorTest{t1, t2, t3} - msg, err := json.Marshal(n) - if err != nil { - panic(err) - } - // pass to connection - if err := websocket.WriteMessage(ws.TextMessage, msg); err != nil { - panic(err) - } - - // pass to reader - reader(websocket) -} - -func setupRoutes() { - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Simple Server") - }) - - http.HandleFunc("/ws", serverWs) -} diff --git a/internal/pkg/websocket/reactor.go b/internal/pkg/websocket/reactor.go deleted file mode 100644 index d055e26..0000000 --- a/internal/pkg/websocket/reactor.go +++ /dev/null @@ -1,2 +0,0 @@ -// implemenets a reactor object with websocket methods -package websocket From 7c3bb7e26cea6c0287ffa55b681b08813a2eaea3 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 16:55:38 -0400 Subject: [PATCH 32/44] fixed config loading on fresh machines, moved to XDG /home/keegan/.config directory --- .task/checksum/go-build | 2 +- cmd/server/main.go | 21 ++++++++++---- internal/pkg/config/config.go | 52 +++++++++++++++++++++++++++++++++++ internal/pkg/config/load.go | 46 ------------------------------- 4 files changed, 69 insertions(+), 52 deletions(-) create mode 100644 internal/pkg/config/config.go delete mode 100644 internal/pkg/config/load.go diff --git a/.task/checksum/go-build b/.task/checksum/go-build index aed43fb..fb78ec6 100644 --- a/.task/checksum/go-build +++ b/.task/checksum/go-build @@ -1 +1 @@ -0bbb9b59233e7c3a6b58764d03788b82 +e832d011a7759af5da2bf569d9d999ac diff --git a/cmd/server/main.go b/cmd/server/main.go index 1c217ca..810c6dd 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -21,17 +21,28 @@ func NewCoordinator(config *viper.Viper, ch chan error) coordinator { return server.NewCentralCoordinator(config, ch) } -func NewConfig(fname string) *viper.Viper { - return config.LoadConfig(fname) +func LoadConfig(file, path, ext string) (*viper.Viper, error) { + return config.LoadConfig(file, path, ext) } func main() { - // lets get this bread + gracefulShutdown := make(chan os.Signal, 1) signal.Notify(gracefulShutdown, syscall.SIGINT, syscall.SIGTERM) - // config file - conf := NewConfig("server") + userHome, err := os.UserHomeDir() + + if err != nil { + panic(err) + } + + configPath := fmt.Sprintf("%s/.config/FRMS", userHome) + configFile := "server" + configExt := "yaml" + conf, err := LoadConfig(configFile, configPath, configExt) + if err != nil { + panic(err) + } errCh := make(chan error) diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go new file mode 100644 index 0000000..4338b37 --- /dev/null +++ b/internal/pkg/config/config.go @@ -0,0 +1,52 @@ +// package Config wraps the viper library to setup/manage files for FRMS +package config + +import ( + "FRMS/internal/pkg/logging" + "fmt" + "os" + + "github.com/spf13/viper" +) + +// LoadConfig loads the file at path/file into a viper object +// Expects config file to be yaml +func LoadConfig(file, path, ext string) (*viper.Viper, error) { + + logging.Debug(logging.DStart, "CON Loading config for %s", file) + + config := viper.New() + + configFile := fmt.Sprintf("%s/%s.%s", path, file, ext) + + config.SetConfigName(file) + config.AddConfigPath(path) + config.SetConfigType(ext) + + // Sets env vars + config.AutomaticEnv() + + // create config directory if it doesn't exist + if err := os.MkdirAll(path, 0750); err != nil && !os.IsExist(err) { + return config, err + } + + // attempt to create an empty config incase it doesn't exist + if err := config.SafeWriteConfigAs(configFile); err != nil { + // if error thrown because file exists, fine to ignore + if _, ok := err.(viper.ConfigFileAlreadyExistsError); !ok { + return config, err + } + } + + if err := config.ReadInConfig(); err != nil { + fmt.Printf("read error %v\n", config) + return config, err + } + + fmt.Printf("Found %s\n", config.ConfigFileUsed()) + logging.Debug(logging.DStart, "CON Loaded configs from %#V", config.ConfigFileUsed()) + + // returning config object + return config, nil +} diff --git a/internal/pkg/config/load.go b/internal/pkg/config/load.go deleted file mode 100644 index 22568ac..0000000 --- a/internal/pkg/config/load.go +++ /dev/null @@ -1,46 +0,0 @@ -package config - -/* -Load.go contains methods to load values from config, flags and env. -*/ - -import ( - "FRMS/internal/pkg/logging" - "fmt" - - "github.com/spf13/viper" -) - -func LoadConfig(fname string) *viper.Viper { - // Demarshalls a given filename into the struct - // returns nil if successful - config := viper.New() - configPath := "$HOME/FRMS/internal/configs" - logging.Debug(logging.DStart, "Loading config for %s", fname) - config.SetConfigName(fname) - config.SetConfigType("yaml") - //viper.AddConfigPath("/etc/frms/config") - config.AddConfigPath(configPath) - // struct and env vars - - // Sets env vars - config.AutomaticEnv() - - // reading - if err := config.ReadInConfig(); err != nil { - if _, ok := err.(viper.ConfigFileNotFoundError); ok { - // no config file found - fmt.Printf("No config file found! creating empty one at %s.\n", configPath) - if err = config.WriteConfigAs(configPath); err != nil { - panic(err) - } - } else { - panic(err) - } - } - - logging.Debug(logging.DStart, "CON Loaded configs from %v", config.ConfigFileUsed()) - - // returning config object - return config -} From 36ccdf1df02b5e2ce173047a262b264f70fa8124 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 17:12:56 -0400 Subject: [PATCH 33/44] adding timing to server start/stop, reduced output --- internal/pkg/config/config.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go index 4338b37..0321571 100644 --- a/internal/pkg/config/config.go +++ b/internal/pkg/config/config.go @@ -44,7 +44,6 @@ func LoadConfig(file, path, ext string) (*viper.Viper, error) { return config, err } - fmt.Printf("Found %s\n", config.ConfigFileUsed()) logging.Debug(logging.DStart, "CON Loaded configs from %#V", config.ConfigFileUsed()) // returning config object From 920b0b3746206e929abbee039efcacfb832f2816 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 17:14:18 -0400 Subject: [PATCH 34/44] adding timing to server start/stop, reduced output --- .task/checksum/go-build | 2 +- cmd/server/main.go | 20 ++++++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/.task/checksum/go-build b/.task/checksum/go-build index fb78ec6..2244bd3 100644 --- a/.task/checksum/go-build +++ b/.task/checksum/go-build @@ -1 +1 @@ -e832d011a7759af5da2bf569d9d999ac +a38a750f4484175244aabc5aebd5ddf9 diff --git a/cmd/server/main.go b/cmd/server/main.go index 810c6dd..f5109db 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -4,6 +4,7 @@ import ( "fmt" "os/signal" "syscall" + "time" "FRMS/internal/pkg/config" "FRMS/internal/pkg/logging" @@ -27,6 +28,9 @@ func LoadConfig(file, path, ext string) (*viper.Viper, error) { func main() { + fmt.Printf("starting server... ") + start := time.Now() + gracefulShutdown := make(chan os.Signal, 1) signal.Notify(gracefulShutdown, syscall.SIGINT, syscall.SIGTERM) @@ -48,19 +52,23 @@ func main() { c := NewCoordinator(conf, errCh) go c.Start() - logging.Debug(logging.DStart, "CCO 01 Server %s started", conf.Get("name")) + logging.Debug(logging.DStart, "CCO 01 %s started", conf.Get("name")) + + elapsed := time.Now().Sub(start) + fmt.Printf("done %v\n", elapsed.Round(time.Microsecond)) select { - case err := <-errCh: // blocking to wait for any errors and keep alive otherwise + case err := <-errCh: panic(err) case <-gracefulShutdown: - // Shutdown via INT - // storing config - fmt.Printf("\nStoring config to %s\n", conf.ConfigFileUsed()) + fmt.Printf("\nstopping server... ") + start := time.Now() if err := conf.WriteConfig(); err != nil { panic(err) } - fmt.Println("Stored config successfully. Exiting...") + logging.Debug(logging.DExit, "CON wrote %s", conf.ConfigFileUsed()) + elapsed := time.Now().Sub(start) + fmt.Printf("done %v\n", elapsed.Round(time.Microsecond)) os.Exit(0) } } From 3da90c5cfa9bda5e108f89f0db961fff4f8815e5 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 17:14:44 -0400 Subject: [PATCH 35/44] removing task output --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d0f8570..774fc26 100644 --- a/.gitignore +++ b/.gitignore @@ -23,7 +23,7 @@ bin *.log # task related -.task +.task/ # machine dependent tokens/ From e61a7b3d650e6fb65ecdd1b898bda4189b5fd761 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 17:17:39 -0400 Subject: [PATCH 36/44] adding test file --- .gitignore | 12 +------ bin/gotest.py | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 11 deletions(-) create mode 100755 bin/gotest.py diff --git a/.gitignore b/.gitignore index 774fc26..44e7ac8 100644 --- a/.gitignore +++ b/.gitignore @@ -13,19 +13,9 @@ # swap files *.swp -# Dependency directories (remove the comment below to include it) -# vendor/ # binaries -bin -*.tar.gz -# logs -*.log +bin/frms* # task related .task/ - -# machine dependent -tokens/ -logs/ -influxdb/config diff --git a/bin/gotest.py b/bin/gotest.py new file mode 100755 index 0000000..1fa03c9 --- /dev/null +++ b/bin/gotest.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +import json +import subprocess + +class PackageTest: + def __init__(self): + self.status = "" + self.tests = [] + self.totaltime = 0 + +res = {} + +output = subprocess.run(["go","test","-count=1","-json","./..."], capture_output=True, text=True) + +output = str(output.stdout) +output = output.split('\n') + +for line in output[:-1]: + # parse the json + parsed = json.loads(line) + action = parsed["Action"] + + # skip + if action in ["start", "output", "run"]: + continue + + # create blank if doesn't exist + if parsed["Package"] not in res: + res[parsed["Package"]] = PackageTest() + + pkg = res[parsed["Package"]] + + if "Test" not in parsed: + # top level package result + pkg.status = action + if "Elapsed" in parsed: + pkg.totaltime = parsed["Elapsed"] + else: + # individual test + pkg.tests.append((parsed["Test"],parsed["Action"],parsed["Elapsed"])) + +totalRan = 0 +totalPassed = 0 +totalTime = 0 +# generating output from parsed json +for name, info in res.items(): + pkgname = name.split('/') + pkgname = '/'.join(name.split('/')[1:]) + if info.status == "skip": + print("Skipped %s" % (pkgname)) + continue + + print("\nTesting %s:" % (pkgname)) + + passed = 0 + total = 0 + + for test in info.tests: + total += 1 + out = [] + if test[1] == "pass": + passed += 1 + out = [" " + test[0] + ":",'\033[32mpass\033[0m ',str(test[2]) + 's'] + elif test[1] == "fail": + out = [" " + test[0] + ":",'\033[31mfail\033[0m ',str(test[2]) + 's'] + + print(f"{out[0] : <30}{out[1] : >5}{out[2] : >8}") + + result = "" + if info.status == "pass": + result = "\033[32mPASSED\033[0m" + else: + result = "\033[31mFAILED\033[0m" + + # keep track of grand totals + totalRan += total + totalPassed += passed + totalTime += info.totaltime + + print(" %s %d/%d in %.3fs/n" % (result, passed, total, info.totaltime)) + + +# output overall test statistics +if totalRan == totalPassed: + result = "\033[32mPASSED\033[0m" +else: + result = "\033[31mFAILED\033[0m" + +print("\nSUMMARY:\n\t%s %d/%d in %.3fs" % (result, totalPassed, totalRan, totalTime)) From 193b037e029d5d636c6a74f65e88ae1639720623 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 17:18:07 -0400 Subject: [PATCH 37/44] adding test file --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 44e7ac8..1569065 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,4 @@ bin/frms* # task related -.task/ +.task From 74cc4f854d19d6f5ea5fe18f1ddad798abc43a20 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 17:19:21 -0400 Subject: [PATCH 38/44] removing task --- .gitignore | 2 +- .task/checksum/go-build | 1 - .dockerignore => docker/.dockerignore | 0 3 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 .task/checksum/go-build rename .dockerignore => docker/.dockerignore (100%) diff --git a/.gitignore b/.gitignore index 1569065..44e7ac8 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,4 @@ bin/frms* # task related -.task +.task/ diff --git a/.task/checksum/go-build b/.task/checksum/go-build deleted file mode 100644 index 2244bd3..0000000 --- a/.task/checksum/go-build +++ /dev/null @@ -1 +0,0 @@ -a38a750f4484175244aabc5aebd5ddf9 diff --git a/.dockerignore b/docker/.dockerignore similarity index 100% rename from .dockerignore rename to docker/.dockerignore From cd0a5186200ba9e010c87727a3984825707a83d3 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 17:26:35 -0400 Subject: [PATCH 39/44] purged configs --- internal/configs/reactor.yaml | 11 ----------- internal/configs/server.yaml | 26 -------------------------- 2 files changed, 37 deletions(-) delete mode 100644 internal/configs/reactor.yaml delete mode 100644 internal/configs/server.yaml diff --git a/internal/configs/reactor.yaml b/internal/configs/reactor.yaml deleted file mode 100644 index 8682f29..0000000 --- a/internal/configs/reactor.yaml +++ /dev/null @@ -1,11 +0,0 @@ -devices: - address: 112 - name: DO Sensor -reactor: - heartbeat: 5 - id: 2166136261 - model: "" - name: Dummy Reactor - server: - ip: 192.168.100.2 - port: 2022 diff --git a/internal/configs/server.yaml b/internal/configs/server.yaml deleted file mode 100644 index 43928db..0000000 --- a/internal/configs/server.yaml +++ /dev/null @@ -1,26 +0,0 @@ -db: - org: ForeLight - url: http://192.168.100.2:8086 -ports_db: 2022 -ports_lis: 2022 -reactors: - "10002123": - db: - bucket: test - token: "" - name: Beaglebone Black - "2062445129": - devices: - "97": - name: DO Sensor - "99": - name: pH Sensor - "102": - name: RTD Sensor -server: - name: Rack Server - ports: - db: 8086 - lis: 2022 - reactor: 2023 - tui: 2024 From d4388dee8606e2ac2fab5b429ad757ca9dffa613 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 17:30:01 -0400 Subject: [PATCH 40/44] todo list for tracking --- todo.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 todo.md diff --git a/todo.md b/todo.md new file mode 100644 index 0000000..37631fa --- /dev/null +++ b/todo.md @@ -0,0 +1,34 @@ +# TODO + +### Documentation + +- [ ] API +- [ ] Config +- [ ] Device +- [ ] gRPC +- [ ] I2C +- [ ] DB +- [ ] logging +- [ ] reactor +- [ ] server +- [ ] system? + +### Testing + +- [ ] Config +- [ ] Device +- [ ] i2c +- [ ] logging +- [ ] reactor +- [O] server +- [ ] gRPC? +- [ ] db? +- [ ] system? + +### Misc + +- [ ] i2c -> lib +- [ ] strip device pkg +- [ ] future work writeup +- [ ] strip reactor down +- [ ] websocket -> web-gRPC From 88e70503d1609eac6659614ad47a04e03c1ae715 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Fri, 16 Jun 2023 17:31:21 -0400 Subject: [PATCH 41/44] minor print error --- bin/gotest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/gotest.py b/bin/gotest.py index 1fa03c9..3a47f33 100755 --- a/bin/gotest.py +++ b/bin/gotest.py @@ -77,7 +77,7 @@ for name, info in res.items(): totalPassed += passed totalTime += info.totaltime - print(" %s %d/%d in %.3fs/n" % (result, passed, total, info.totaltime)) + print(" %s %d/%d in %.3fs" % (result, passed, total, info.totaltime)) # output overall test statistics From 9392892edec3b79260d0f328e564e07df704dfab Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Tue, 20 Jun 2023 13:05:45 -0400 Subject: [PATCH 42/44] fixed hwinfo to use global var and repaired reactor side interfaces --- cmd/reactor/main.go | 29 ++++- go.mod | 4 - go.sum | 14 -- internal/pkg/device/controller.go | 4 +- internal/pkg/device/do.go | 7 +- internal/pkg/device/manager.go | 25 ++-- internal/pkg/device/mappings.go | 26 ++-- internal/pkg/device/ph.go | 7 +- internal/pkg/device/pwm.go | 9 +- internal/pkg/device/rtd.go | 7 +- internal/pkg/device/sensor.go | 5 +- internal/pkg/i2c/{bus.go => i2c.go} | 6 +- internal/pkg/manager/manager.go | 12 +- internal/pkg/reactor/coordinator.go | 29 +++-- internal/pkg/reactor/device.go | 22 ++-- internal/pkg/system/hwinfo.go | 191 ++++++++++++++++------------ internal/pkg/system/hwinfo_test.go | 12 ++ 17 files changed, 235 insertions(+), 174 deletions(-) rename internal/pkg/i2c/{bus.go => i2c.go} (93%) create mode 100644 internal/pkg/system/hwinfo_test.go diff --git a/cmd/reactor/main.go b/cmd/reactor/main.go index 1db35e6..a7b4bdb 100644 --- a/cmd/reactor/main.go +++ b/cmd/reactor/main.go @@ -17,13 +17,16 @@ type reactorCoordinator interface { Start() } -func NewReactorCoordinator(config *viper.Viper, ch chan error) reactorCoordinator { +func NewReactorCoordinator( + config *viper.Viper, + ch chan error, +) (reactorCoordinator, error) { // allows interface checking as opposed to calling directly return reactor.NewCoordinator(config, ch) } -func NewConfig(fname string) *viper.Viper { - return config.LoadConfig(fname) +func NewConfig(file, path, ext string) (*viper.Viper, error) { + return config.LoadConfig(file, path, ext) } func main() { @@ -32,10 +35,26 @@ func main() { signal.Notify(gracefulShutdown, syscall.SIGINT, syscall.SIGTERM) // load any stored configs - conf := NewConfig("reactor") + home, err := os.UserHomeDir() + if err != nil { + panic(err) + } + + configPath := fmt.Sprintf("%s/.config/FRMS", home) + configFile := "reactor" + configExt := "yaml" + + conf, err := NewConfig(configFile, configPath, configExt) + if err != nil { + panic(err) + } ch := make(chan error) - rlc := NewReactorCoordinator(conf, ch) // passing conf and err + rlc, err := NewReactorCoordinator(conf, ch) // passing conf and err + if err != nil { + panic(err) + } + go rlc.Start() logging.Debug(logging.DStart, "Reactor Started") diff --git a/go.mod b/go.mod index 5c4d6d8..476454a 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module FRMS go 1.18 require ( - github.com/gorilla/websocket v1.5.0 github.com/influxdata/influxdb-client-go/v2 v2.9.1 github.com/spf13/viper v1.12.0 github.com/stretchr/testify v1.7.1 @@ -29,12 +28,9 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.3.0 // indirect - github.com/yuin/goldmark v1.4.13 // indirect - golang.org/x/mod v0.10.0 // indirect golang.org/x/net v0.10.0 // indirect golang.org/x/sys v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect - golang.org/x/tools v0.9.3 // indirect google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd // indirect gopkg.in/ini.v1 v1.66.4 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index b6dc52f..6e81e5d 100644 --- a/go.sum +++ b/go.sum @@ -139,8 +139,6 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -217,8 +215,6 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -268,8 +264,6 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -303,8 +297,6 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y= -golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -369,8 +361,6 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b h1:2n253B2r0pYSmEV+UNCQoPfU/FiaizQEK5Gu4Bq4JE8= -golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -382,8 +372,6 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -438,8 +426,6 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= -golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/pkg/device/controller.go b/internal/pkg/device/controller.go index ca03bf1..feaef36 100644 --- a/internal/pkg/device/controller.go +++ b/internal/pkg/device/controller.go @@ -13,8 +13,8 @@ type ControllerManager struct { Enabled bool // turn controller on or off } -func NewControllerManager() *ControllerManager { - return &ControllerManager{} +func NewControllerManager() (*ControllerManager, error) { + return &ControllerManager{}, nil } func (c *ControllerManager) SetDeviceManager(d *DeviceManager) { diff --git a/internal/pkg/device/do.go b/internal/pkg/device/do.go index ade64e8..e64871b 100644 --- a/internal/pkg/device/do.go +++ b/internal/pkg/device/do.go @@ -14,7 +14,7 @@ type DOManager struct { sync.RWMutex } -func NewDOManager() *DOManager { +func NewDOManager() (*DOManager, error) { // atlas delays a := &Atlas{ CalDelay: 1300, @@ -23,11 +23,10 @@ func NewDOManager() *DOManager { sm := NewSensorManager() - m := &DOManager{ + return &DOManager{ Atlas: a, SensorManager: sm, - } - return m + }, nil } func (m *DOManager) Start() error { diff --git a/internal/pkg/device/manager.go b/internal/pkg/device/manager.go index 92c321e..77faaf4 100644 --- a/internal/pkg/device/manager.go +++ b/internal/pkg/device/manager.go @@ -14,12 +14,12 @@ import ( type Manager interface { // core manager Start() error - Exit() error - IsActive() int + Stop() error + IsActive() manager.Status HeartBeat(chan struct{}, int, int, time.Duration) } -func NewManager() Manager { +func NewManager() (Manager, error) { // no timeouts needed return manager.New(0) } @@ -36,21 +36,26 @@ type DeviceManager struct { Manager // config Config *viper.Viper - // gRPC server - pb.UnimplementedDeviceServer } -func NewDeviceManager(bus, addr int, config *viper.Viper, defaultName string) *DeviceManager { +func NewDeviceManager( + bus, addr int, + config *viper.Viper, + defaultName string, +) (*DeviceManager, error) { // new base dm - m := NewManager() - dm := &DeviceManager{ + m, err := NewManager() + if err != nil { + return &DeviceManager{}, err + } + + return &DeviceManager{ Address: addr, Bus: bus, defaultName: defaultName, Manager: m, Config: config, - } - return dm + }, nil } func (m *DeviceManager) LoadConfig() error { diff --git a/internal/pkg/device/mappings.go b/internal/pkg/device/mappings.go index 92c6ff7..143b27f 100644 --- a/internal/pkg/device/mappings.go +++ b/internal/pkg/device/mappings.go @@ -1,6 +1,7 @@ package device import ( + "FRMS/internal/pkg/manager" "errors" "fmt" @@ -10,8 +11,8 @@ import ( // Returns the correct manager for sensor/controller type Device interface { Start() error - Exit() error - IsActive() int + Stop() error + IsActive() manager.Status SetDeviceManager(*DeviceManager) } @@ -25,26 +26,33 @@ func New(bus, addr int, config *viper.Viper) (Device, error) { case 97: // DO defaultName = "DO Sensor" - m = NewDOManager() + m, err = NewDOManager() case 99: // pH defaultName = "pH Sensor" - m = NewPHManager() + m, err = NewPHManager() case 102: // RTD defaultName = "RTD Sensor" - m = NewRTDManager() + m, err = NewRTDManager() case 256: // PWM defaultName = "PWM Controller" - m = NewPWMManager() + m, err = NewPWMManager() default: err = errors.New(fmt.Sprintf("Error: device id %d unrecognized!", addr)) } - // setting device manager - dm := NewDeviceManager(bus, addr, config, defaultName) + + if err != nil { + return m, err + } + + dm, err := NewDeviceManager(bus, addr, config, defaultName) + if err != nil { + return m, err + } + m.SetDeviceManager(dm) - // setting up gRPC server functionality return m, err } diff --git a/internal/pkg/device/ph.go b/internal/pkg/device/ph.go index f6d8dde..f86646d 100644 --- a/internal/pkg/device/ph.go +++ b/internal/pkg/device/ph.go @@ -14,7 +14,7 @@ type PHManager struct { sync.RWMutex } -func NewPHManager() *PHManager { +func NewPHManager() (*PHManager, error) { // atlas delays a := &Atlas{ CalDelay: 900, @@ -22,11 +22,10 @@ func NewPHManager() *PHManager { } sm := NewSensorManager() - m := &PHManager{ + return &PHManager{ Atlas: a, SensorManager: sm, - } - return m + }, nil } func (m *PHManager) Start() error { diff --git a/internal/pkg/device/pwm.go b/internal/pkg/device/pwm.go index 130c17e..5840a3c 100644 --- a/internal/pkg/device/pwm.go +++ b/internal/pkg/device/pwm.go @@ -14,9 +14,12 @@ type PWMManager struct { DutyCycle int } -func NewPWMManager() *PWMManager { - cm := NewControllerManager() - return &PWMManager{ControllerManager: cm} +func NewPWMManager() (*PWMManager, error) { + cm, err := NewControllerManager() + + return &PWMManager{ + ControllerManager: cm, + }, err } // freq changing diff --git a/internal/pkg/device/rtd.go b/internal/pkg/device/rtd.go index 07ef3a4..eb82af4 100644 --- a/internal/pkg/device/rtd.go +++ b/internal/pkg/device/rtd.go @@ -13,7 +13,7 @@ type RTDManager struct { sync.RWMutex } -func NewRTDManager() *RTDManager { +func NewRTDManager() (*RTDManager, error) { // atlas delays a := &Atlas{ CalDelay: 600, @@ -21,11 +21,10 @@ func NewRTDManager() *RTDManager { } sm := NewSensorManager() - m := &RTDManager{ + return &RTDManager{ Atlas: a, SensorManager: sm, - } - return m + }, nil } func (m *RTDManager) Start() error { diff --git a/internal/pkg/device/sensor.go b/internal/pkg/device/sensor.go index 560b0da..b53393d 100644 --- a/internal/pkg/device/sensor.go +++ b/internal/pkg/device/sensor.go @@ -16,9 +16,6 @@ type SensorManager struct { SampleTimestamp int64 *DeviceManager `mapstructure:",squash"` - - // gRPC server - pb.UnimplementedSensorServer } func NewSensorManager() *SensorManager { @@ -86,7 +83,7 @@ func (s *SensorManager) Monitor(f takeReading) { fmt.Printf("Got %f\n", reading) s.sampleMu.Lock() s.LatestSample = float32(reading) - s.SampleTimestamp = time.Now.Unix() + s.SampleTimestamp = time.Now().Unix() s.sampleMu.Unlock() } } diff --git a/internal/pkg/i2c/bus.go b/internal/pkg/i2c/i2c.go similarity index 93% rename from internal/pkg/i2c/bus.go rename to internal/pkg/i2c/i2c.go index 7ba012e..867168c 100644 --- a/internal/pkg/i2c/bus.go +++ b/internal/pkg/i2c/i2c.go @@ -1,7 +1,7 @@ -// package i2c wraps the [i2c-tools] commands to interact -// with devices on the buss +// package i2c wraps the [i2c package] to interact with the i2c +// with devices on the bus // -// [i2c-tools]: https://manpages.debian.org/unstable/i2c-tools/index.html +// [i2c package]: https://pkg.go.dev/periph.io/x/conn/v3/i2c#pkg-overview package i2c import ( diff --git a/internal/pkg/manager/manager.go b/internal/pkg/manager/manager.go index 3a8db51..eaf4376 100644 --- a/internal/pkg/manager/manager.go +++ b/internal/pkg/manager/manager.go @@ -17,13 +17,13 @@ var ( ErrMaxAttemptsExceeded = errors.New("max connection attempts exceeded") ) -// ManagerStatus is used as an enum for the current status. +// Status is used as an enum for the current status. // Could be expanded to include others such as killed, sleeping, etc. -type ManagerStatus int +type Status int const ( - Inactive ManagerStatus = 0 - Active ManagerStatus = 1 + Inactive Status = 0 + Active Status = 1 ) // MaxConnAttempts is the maximum allowable connection attempts. @@ -77,8 +77,8 @@ func (m *Manager) Stop() error { } // IsActive returns the current ManagerStatus. -func (m *Manager) IsActive() ManagerStatus { - return ManagerStatus(atomic.LoadInt32(&m.active)) +func (m *Manager) IsActive() Status { + return Status(atomic.LoadInt32(&m.active)) } // HeartBeat will send an empty struct over ping every hb (scale). diff --git a/internal/pkg/reactor/coordinator.go b/internal/pkg/reactor/coordinator.go index 1d9dd8b..11b562c 100644 --- a/internal/pkg/reactor/coordinator.go +++ b/internal/pkg/reactor/coordinator.go @@ -22,12 +22,12 @@ import ( // I dont think I actually need this interface, package manager has a point type Manager interface { Start() error - Exit() error + Stop() error Timeout() (time.Duration, error) HeartBeat(chan struct{}, int, int, time.Duration) // creates a hb } -func NewManager(max int) Manager { +func NewManager(max int) (Manager, error) { return manager.New(max) } @@ -71,10 +71,22 @@ type ReactorCoordinator struct { Err chan error } -func NewCoordinator(config *viper.Viper, errCh chan error) *ReactorCoordinator { +func NewCoordinator( + config *viper.Viper, + errCh chan error, +) (*ReactorCoordinator, error) { - m := NewManager(6) // max 6 attempts - dc := NewDeviceCoordinator(config) + m, err := NewManager(6) // max 6 attempts + + if err != nil { + return &ReactorCoordinator{}, err + } + + dc, err := NewDeviceCoordinator(config) + + if err != nil { + return &ReactorCoordinator{}, err + } c := &ReactorCoordinator{ Manager: m, @@ -83,10 +95,7 @@ func NewCoordinator(config *viper.Viper, errCh chan error) *ReactorCoordinator { Err: errCh, } - // this is going to be scuffed - //c.DB = &DB{Bucket: "bb", Org: "ForeLight", URL: url, Token: "S1UZssBu6KPfHaQCt34pZFpyc5lzbH9XanYJWCkOI5FqLY7gq205C6FTH-CmugiPH6o2WoKlTkEuPgIfaJjAhw=="} - - return c + return c, nil } func (c *ReactorCoordinator) Start() { @@ -129,7 +138,7 @@ func (c *ReactorCoordinator) LoadConfig() error { if !c.Config.IsSet("reactor.id") { // get from hw var id int - if id, err = system.GetId("eth0"); err != nil { + if id, err = system.GetID(); err != nil { return err } c.Config.Set("reactor.id", id) diff --git a/internal/pkg/reactor/device.go b/internal/pkg/reactor/device.go index c2afc6c..76a2022 100644 --- a/internal/pkg/reactor/device.go +++ b/internal/pkg/reactor/device.go @@ -4,6 +4,7 @@ import ( "FRMS/internal/pkg/device" pb "FRMS/internal/pkg/grpc" "FRMS/internal/pkg/i2c" + "FRMS/internal/pkg/manager" "fmt" "sync" "time" @@ -16,8 +17,8 @@ import ( // device manager type DeviceManager interface { Start() error - Exit() error - IsActive() int + Stop() error + IsActive() manager.Status } func NewDeviceManager(bus, addr int, config *viper.Viper) (DeviceManager, error) { @@ -35,15 +36,16 @@ type DeviceCoordinator struct { DeviceManagers map[int]DeviceManager } -func NewDeviceCoordinator(config *viper.Viper) *DeviceCoordinator { +func NewDeviceCoordinator(config *viper.Viper) (*DeviceCoordinator, error) { dm := make(map[int]DeviceManager) - m := NewManager(0) - c := &DeviceCoordinator{ + + m, err := NewManager(0) + + return &DeviceCoordinator{ Manager: m, DeviceManagers: dm, Config: config, - } - return c + }, err } func (c *DeviceCoordinator) Start(bus int) error { @@ -83,14 +85,14 @@ func (c *DeviceCoordinator) UpdateManagers(active map[int]bool) { for addr, dm := range c.DeviceManagers { _, ok := active[addr] - if ok && dm.IsActive() == 0 { + if ok && dm.IsActive() == manager.Inactive { // active and dm not if err := dm.Start(); err != nil { panic(err) } - } else if !ok && dm.IsActive() == 1 { + } else if !ok && dm.IsActive() == manager.Active { // not active and dm is - if err := dm.Exit(); err != nil { + if err := dm.Stop(); err != nil { panic(err) } } diff --git a/internal/pkg/system/hwinfo.go b/internal/pkg/system/hwinfo.go index dca66ed..50bf9de 100644 --- a/internal/pkg/system/hwinfo.go +++ b/internal/pkg/system/hwinfo.go @@ -1,118 +1,145 @@ -// package system uses linux commands to get hardware info from devices +// package system uses linux ip command to get hardware info from devices package system import ( "bytes" + "encoding/json" "errors" - "fmt" "hash/fnv" - "net" "os/exec" "strings" ) -func GetId() (int, error) { - // gets the mac address and hashes into consistent id - maccmd := fmt.Sprintf("ifconfig %v | awk '/ether / {print $2}'", et) - var stderr bytes.Buffer - var out bytes.Buffer - cmd := exec.Command("bash", "-c", maccmd) - cmd.Stdout = &out - cmd.Stderr = &stderr - if err := cmd.Run(); err != nil { - return 0, err - } - hash := fnv.New32a() - hash.Write(out.Bytes()) - id := hash.Sum32() - return int(id), nil +var ( + ErrBusNotFound = errors.New("bus not found for device") + ErrNoNetworkInterface = errors.New("no default network found") +) + +var HardwareInfo = &hardwareInfo{} + +type hardwareInfo struct { + MAC string + IP string + ID int + Model string +} + +// NetInterfaces is a struct to unmarshal the ip command json. +type netInterfaces []network + +// Networks holds relevant information for each network interface. +// Used to unmarshal ip command json. +type network struct { + Subnets []netInfo `json:"addr_info"` + Mac string `json:"address"` + Group string `json:"group"` + State string `json:"operstate"` +} + +type netInfo struct { + Family string `json:"family"` + Ip string `json:"local"` } -func GetIp() (string, error) { - ipcmd := "ip route get 1 | sed 's/^.*src \([^ ]*\).*$/\1/;q'" - var stderr bytes.Buffer - var out bytes.Buffer - cmd := exec.Command("bash", "-c", ipcmd) - cmd.Stdout = &out +func getInfo() error { + + var stderr, stdout bytes.Buffer + cmd := exec.Command("ip", "-j", "a") + cmd.Stdout = &stdout cmd.Stderr = &stderr + if err := cmd.Run(); err != nil { - return "", err + return err } - ip := strings.Trim(out.String(), " \n") - return ip, nil -} + var networks netInterfaces -func GetPort() (int, error) { - // obsolete - if addr, err := net.ResolveTCPAddr("tcp", ":0"); err != nil { - return 0, err - } else if lis, err := net.ListenTCP("tcp", addr); err != nil { - return 0, err - } else { - defer lis.Close() - return lis.Addr().(*net.TCPAddr).Port, nil + if err := json.Unmarshal(stdout.Bytes(), &networks); err != nil { + return err } + + // loop over found networks finding first default, IPv4 network currently + // UP (active). + // Eventually need to look only at wg interface which simplifies + // implementation. + + for _, network := range networks { + if network.Group == "default" && network.State == "UP" { + + for _, subnet := range network.Subnets { + if subnet.Family == "inet" { + + hash := fnv.New32a() + hash.Write([]byte(network.Mac)) + id := hash.Sum32() + + HardwareInfo.MAC = network.Mac + HardwareInfo.IP = subnet.Ip + HardwareInfo.ID = int(id) + + return nil + } + } + } + } + + return nil } -func GetBus() (int, error) { - // preset busses - busList := map[string]int{"raspberrypi": 1, "beaglebone": 2} - // vars - var bus int - var ok bool +func GetID() (int, error) { - if name, err =: GetModel(); err != nil { - return bus, err - } else if bus, ok = busList[b]; !ok { - return 0, errors.New(fmt.Sprintf("No bus for dev %s", b)) + if HardwareInfo.ID == 0 { + if err := getInfo(); err != nil { + return 0, err + } } - // returns correct bus - return bus, nil + return HardwareInfo.ID, nil } -func GetModel() (string, error) { - var stderr, out bytes.Buffer - cmd := exec.Command("bash", "-c", "hostname") - cmd.Stdout = &out - cmd.Stderr = &stderr - if err := cmd.Run(); err != nil { - return "", err +func GetIP() (string, error) { + + if HardwareInfo.IP == "" { + if err := getInfo(); err != nil { + return "", err + } } - b := out.String() - b = strings.Trim(b, " \n") - return b, nil + + return HardwareInfo.IP, nil } -func Get() error { - // responsible for filling out struct - //bus := map[string]int{"raspberrypi":1,"beaglebone":2} // eventually will replace this with a config file +func GetModel() (string, error) { - ipcmd := "ifconfig eth0 | awk '/inet / {print $2}'" - maccmd := "ifconfig eth0 | awk '/ether / {print $2}'" - devcmd := "lshw -C system 2>/dev/null | head -n 1" + if HardwareInfo.Model == "" { - res := [3]bytes.Buffer{} - var stderr bytes.Buffer - cmds := []string{ipcmd, maccmd, devcmd} - for i, c := range cmds { - cmd := exec.Command("bash", "-c", c) - cmd.Stdout = &res[i] + var stderr, out bytes.Buffer + cmd := exec.Command("uname", "-n") + cmd.Stdout = &out cmd.Stderr = &stderr - err := cmd.Run() - if err != nil { - return err + + if err := cmd.Run(); err != nil { + return "", err } + + b := out.String() + HardwareInfo.Model = strings.Trim(b, " \n") } - // formatting - ip := res[0].String() - ip = strings.Trim(ip, " \n") - hash := fnv.New32a() - hash.Write(res[1].Bytes()) + return HardwareInfo.Model, nil +} - b := res[2].String() - b = strings.Trim(b, " \n") - return nil +func GetBus() (int, error) { + // preset busses + busList := map[string]int{"raspberrypi": 1, "beaglebone": 2} + // vars + var bus int + var ok bool + + if name, err := GetModel(); err != nil { + return bus, err + } else if bus, ok = busList[name]; !ok { + return 0, ErrBusNotFound + } + + return bus, nil } diff --git a/internal/pkg/system/hwinfo_test.go b/internal/pkg/system/hwinfo_test.go new file mode 100644 index 0000000..968e01a --- /dev/null +++ b/internal/pkg/system/hwinfo_test.go @@ -0,0 +1,12 @@ +package system + +import ( + "testing" +) + +func TestGetInfo(t *testing.T) { + info, err := GetInfo() + if err != nil { + panic(err) + } +} From 72fe309a25a6f32d23e07e9c9166a39436fbe4d5 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Tue, 20 Jun 2023 13:14:22 -0400 Subject: [PATCH 43/44] added barebones tests for system package --- internal/pkg/system/hwinfo_test.go | 35 +++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/internal/pkg/system/hwinfo_test.go b/internal/pkg/system/hwinfo_test.go index 968e01a..c06a6ca 100644 --- a/internal/pkg/system/hwinfo_test.go +++ b/internal/pkg/system/hwinfo_test.go @@ -4,9 +4,38 @@ import ( "testing" ) +// TestGetInfo ensures that the array can be populated with device info. func TestGetInfo(t *testing.T) { - info, err := GetInfo() - if err != nil { - panic(err) + if err := getInfo(); err != nil { + t.Fatalf(`getInfo() failed %v`, err) } } + +// TestGetIP tests that the IP is returned without error and not empty. +func TestGetIP(t *testing.T) { + if ip, err := GetIP(); err != nil || ip == "" { + t.Fatalf(`GetIP() failed, got %s, err: %v`, ip, err) + } +} + +// TestGetID tests that the ID is returned without error and not empty. +func TestGetID(t *testing.T) { + if id, err := GetID(); err != nil || id == 0 { + t.Fatalf(`GetID() failed, got %d %v`, id, err) + } +} + +// TestGetModel tests that the Model is returned without error and not empty. +func TestGetModel(t *testing.T) { + if model, err := GetModel(); err != nil || model == "" { + t.Fatalf(`GetModel() failed, got %s %v`, model, err) + } +} + +// TestGetModel tests that the correct error is thrown as the bus does not exist on the test rig. +func TestGetBus(t *testing.T) { + if bus, err := GetBus(); err != ErrBusNotFound { + t.Fatalf(`GetBus() should fail, got %d %v`, bus, err) + } + +} From f209980e8b1d1a655b047064533334bdfa0a5ed4 Mon Sep 17 00:00:00 2001 From: KeeganForelight Date: Thu, 22 Jun 2023 15:19:25 -0400 Subject: [PATCH 44/44] refactored a lot of server, creating actual database package --- Taskfile.dist.yml | 5 + cmd/reactor/main.go | 6 +- cmd/server/main.go | 30 +- internal/pkg/config/config.go | 22 +- internal/pkg/database/reactor.go | 64 ++++ internal/pkg/grpc/device.proto | 42 --- internal/pkg/grpc/handshake.pb.go | 260 ++++++++++++++ internal/pkg/grpc/handshake.proto | 21 ++ ...server_grpc.pb.go => handshake_grpc.pb.go} | 34 +- internal/pkg/grpc/monitoring.pb.go | 233 +++--------- internal/pkg/grpc/monitoring.proto | 19 +- internal/pkg/grpc/monitoring_grpc.pb.go | 88 +++-- internal/pkg/grpc/server.pb.go | 335 ------------------ internal/pkg/grpc/server.proto | 28 -- internal/pkg/influxdb/client.go | 81 ----- internal/pkg/server/coordinator.go | 272 ++------------ internal/pkg/server/database.go | 13 + internal/pkg/server/handler.go | 48 +++ internal/pkg/server/listener.go | 120 ------- internal/pkg/server/listener_test.go | 17 - internal/pkg/server/reactor.go | 121 +++++++ internal/pkg/server/reactormanager.go | 108 ------ internal/pkg/server/reactormanager_test.go | 85 ----- internal/pkg/server/register.go | 19 + 24 files changed, 742 insertions(+), 1329 deletions(-) create mode 100644 internal/pkg/database/reactor.go delete mode 100644 internal/pkg/grpc/device.proto create mode 100644 internal/pkg/grpc/handshake.pb.go create mode 100644 internal/pkg/grpc/handshake.proto rename internal/pkg/grpc/{server_grpc.pb.go => handshake_grpc.pb.go} (64%) delete mode 100644 internal/pkg/grpc/server.pb.go delete mode 100644 internal/pkg/grpc/server.proto delete mode 100644 internal/pkg/influxdb/client.go create mode 100644 internal/pkg/server/database.go create mode 100644 internal/pkg/server/handler.go delete mode 100644 internal/pkg/server/listener.go delete mode 100644 internal/pkg/server/listener_test.go create mode 100644 internal/pkg/server/reactor.go delete mode 100644 internal/pkg/server/reactormanager.go delete mode 100644 internal/pkg/server/reactormanager_test.go create mode 100644 internal/pkg/server/register.go diff --git a/Taskfile.dist.yml b/Taskfile.dist.yml index 780f4be..4f0d97f 100644 --- a/Taskfile.dist.yml +++ b/Taskfile.dist.yml @@ -11,6 +11,11 @@ tasks: cmds: - bin/gotest.py + proto: + desc: "Rebuilds protobuf for gRPC" + cmds: + - protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative internal/pkg/grpc/*.proto + all: desc: "builds arm reactor binaries and arm/amd server binaries" deps: [arm32-reactor, arm64-reactor, arm64-server, amd64-server] diff --git a/cmd/reactor/main.go b/cmd/reactor/main.go index a7b4bdb..eb7e6a3 100644 --- a/cmd/reactor/main.go +++ b/cmd/reactor/main.go @@ -25,10 +25,6 @@ func NewReactorCoordinator( return reactor.NewCoordinator(config, ch) } -func NewConfig(file, path, ext string) (*viper.Viper, error) { - return config.LoadConfig(file, path, ext) -} - func main() { // shutdown gracefulShutdown := make(chan os.Signal, 1) @@ -44,7 +40,7 @@ func main() { configFile := "reactor" configExt := "yaml" - conf, err := NewConfig(configFile, configPath, configExt) + conf, err := config.Load(configFile, configPath, configExt) if err != nil { panic(err) } diff --git a/cmd/server/main.go b/cmd/server/main.go index f5109db..9de6f69 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -6,7 +6,8 @@ import ( "syscall" "time" - "FRMS/internal/pkg/config" + conf "FRMS/internal/pkg/config" + "FRMS/internal/pkg/database" "FRMS/internal/pkg/logging" "FRMS/internal/pkg/server" "os" @@ -15,17 +16,13 @@ import ( ) type coordinator interface { - Start() + Start() error } func NewCoordinator(config *viper.Viper, ch chan error) coordinator { return server.NewCentralCoordinator(config, ch) } -func LoadConfig(file, path, ext string) (*viper.Viper, error) { - return config.LoadConfig(file, path, ext) -} - func main() { fmt.Printf("starting server... ") @@ -43,18 +40,23 @@ func main() { configPath := fmt.Sprintf("%s/.config/FRMS", userHome) configFile := "server" configExt := "yaml" - conf, err := LoadConfig(configFile, configPath, configExt) + config, err := conf.Load(configFile, configPath, configExt) if err != nil { panic(err) } + database.Connect(config) + errCh := make(chan error) - c := NewCoordinator(conf, errCh) - go c.Start() - logging.Debug(logging.DStart, "CCO 01 %s started", conf.Get("name")) + c := NewCoordinator(config, errCh) + if err := c.Start(); err != nil { + panic(err) + } + + logging.Debug(logging.DStart, "CCO 01 %s started", config.Get("name")) - elapsed := time.Now().Sub(start) + elapsed := time.Since(start) fmt.Printf("done %v\n", elapsed.Round(time.Microsecond)) select { @@ -63,11 +65,11 @@ func main() { case <-gracefulShutdown: fmt.Printf("\nstopping server... ") start := time.Now() - if err := conf.WriteConfig(); err != nil { + if err := config.WriteConfig(); err != nil { panic(err) } - logging.Debug(logging.DExit, "CON wrote %s", conf.ConfigFileUsed()) - elapsed := time.Now().Sub(start) + logging.Debug(logging.DExit, "CON wrote %s", config.ConfigFileUsed()) + elapsed := time.Since(start) fmt.Printf("done %v\n", elapsed.Round(time.Microsecond)) os.Exit(0) } diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go index 0321571..54e7907 100644 --- a/internal/pkg/config/config.go +++ b/internal/pkg/config/config.go @@ -9,15 +9,15 @@ import ( "github.com/spf13/viper" ) -// LoadConfig loads the file at path/file into a viper object -// Expects config file to be yaml -func LoadConfig(file, path, ext string) (*viper.Viper, error) { +// Load the file at path/file into a viper object. +// Expects config file to be yaml. +func Load(file, path, ext string) (*viper.Viper, error) { - logging.Debug(logging.DStart, "CON Loading config for %s", file) + logging.Debug(logging.DStart, "CON loading %s", file) config := viper.New() - configFile := fmt.Sprintf("%s/%s.%s", path, file, ext) + //configFile := fmt.Sprintf("%s/%s.%s", path, file, ext) config.SetConfigName(file) config.AddConfigPath(path) @@ -32,12 +32,12 @@ func LoadConfig(file, path, ext string) (*viper.Viper, error) { } // attempt to create an empty config incase it doesn't exist - if err := config.SafeWriteConfigAs(configFile); err != nil { - // if error thrown because file exists, fine to ignore - if _, ok := err.(viper.ConfigFileAlreadyExistsError); !ok { - return config, err - } - } + // if err := config.SafeWriteConfigAs(configFile); err != nil { + // // if error thrown because file exists, fine to ignore + // if _, ok := err.(viper.ConfigFileAlreadyExistsError); !ok { + // return config, err + // } + // } if err := config.ReadInConfig(); err != nil { fmt.Printf("read error %v\n", config) diff --git a/internal/pkg/database/reactor.go b/internal/pkg/database/reactor.go new file mode 100644 index 0000000..84df903 --- /dev/null +++ b/internal/pkg/database/reactor.go @@ -0,0 +1,64 @@ +// package Database wraps some influx db methods to provide functionality. +package database + +import ( + "context" + "errors" + "fmt" + + influx "github.com/influxdata/influxdb-client-go/v2" + "github.com/spf13/viper" +) + +var ( + ErrDBConnection = errors.New("connection to database failed") + ErrNoURLFound = errors.New("database url not found") +) + +var db influx.Client + +// Connect takes in a config and attempts to create a client for influxdb. +// Will automatically write changes back to config for future attempts +func Connect(config *viper.Viper) error { + + url := config.GetString("db.url") + token := config.GetString("db.token") + + if url == "" { + return ErrNoURLFound + } + + db = influx.NewClient(url, token) + + if token == "" { + // try setup + fmt.Printf("attempting to setup database at %v\n", url) + + user := config.GetString("db.username") + password := config.GetString("db.password") + org := config.GetString("db.org") + bucket := config.GetString("db.bucket") + + Setup(user, pass, org, bucket + } + + db = influx.NewClient(url, token) + + return nil +} + +func Setup(user, pass, org, bucket string, ret int) (string, error) { + + resp, err := db.Setup(context.Background(), user, pass, org, bucket, ret) + + return "", nil +} + +func GetBucket(id int) (string, error) { + return "", nil +} + +func GetToken(id int) (string, error) { + // bucket, err := client.BucketsAPI().FindBucketByName(context.Background(), id) + return "", nil +} diff --git a/internal/pkg/grpc/device.proto b/internal/pkg/grpc/device.proto deleted file mode 100644 index 69ec364..0000000 --- a/internal/pkg/grpc/device.proto +++ /dev/null @@ -1,42 +0,0 @@ -syntax = "proto3"; -package grpc; - -option go_package = "internal/pkg/grpc"; - -service device { - // groups basic device interactions - // get/set name based on request - rpc Name(NameRequest) returns (NameResponse) -} - -message NameRequest { - // empty for future expansion - string Name = 1; -} - -message NameResponse { - string Name = 1; -} - -service sensor { - // sensor specific functions - rpc Reading(ReadingRequest) returns (ReadingResponse) - rpc SampleRate(SampleRateRequest) returns (SampleRateResponse) -} - -message ReadingRequest { - // empty -} - -message ReadingResponse { - string Reading = 1; // formatted reading "9.7 pH" - int64 Timestamp = 2; // when the reading was taken -} - -message SampleRateRequest { - int32 SampleRate = 1; // 0 to return current sample rate, value in seconds -} - -message SampleRateResponse { - int32 SampleRate = 1; // returns the set sample rate -} diff --git a/internal/pkg/grpc/handshake.pb.go b/internal/pkg/grpc/handshake.pb.go new file mode 100644 index 0000000..3444896 --- /dev/null +++ b/internal/pkg/grpc/handshake.pb.go @@ -0,0 +1,260 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.21.12 +// source: internal/pkg/grpc/handshake.proto + +package grpc + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ReactorClientRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` // client gRPC port +} + +func (x *ReactorClientRequest) Reset() { + *x = ReactorClientRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_internal_pkg_grpc_handshake_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReactorClientRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReactorClientRequest) ProtoMessage() {} + +func (x *ReactorClientRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_pkg_grpc_handshake_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReactorClientRequest.ProtoReflect.Descriptor instead. +func (*ReactorClientRequest) Descriptor() ([]byte, []int) { + return file_internal_pkg_grpc_handshake_proto_rawDescGZIP(), []int{0} +} + +func (x *ReactorClientRequest) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *ReactorClientRequest) GetPort() uint32 { + if x != nil { + return x.Port + } + return 0 +} + +type ReactorClientResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` + Org string `protobuf:"bytes,3,opt,name=org,proto3" json:"org,omitempty"` + Token string `protobuf:"bytes,4,opt,name=token,proto3" json:"token,omitempty"` + Bucket string `protobuf:"bytes,5,opt,name=bucket,proto3" json:"bucket,omitempty"` +} + +func (x *ReactorClientResponse) Reset() { + *x = ReactorClientResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_internal_pkg_grpc_handshake_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReactorClientResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReactorClientResponse) ProtoMessage() {} + +func (x *ReactorClientResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_pkg_grpc_handshake_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReactorClientResponse.ProtoReflect.Descriptor instead. +func (*ReactorClientResponse) Descriptor() ([]byte, []int) { + return file_internal_pkg_grpc_handshake_proto_rawDescGZIP(), []int{1} +} + +func (x *ReactorClientResponse) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *ReactorClientResponse) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +func (x *ReactorClientResponse) GetOrg() string { + if x != nil { + return x.Org + } + return "" +} + +func (x *ReactorClientResponse) GetToken() string { + if x != nil { + return x.Token + } + return "" +} + +func (x *ReactorClientResponse) GetBucket() string { + if x != nil { + return x.Bucket + } + return "" +} + +var File_internal_pkg_grpc_handshake_proto protoreflect.FileDescriptor + +var file_internal_pkg_grpc_handshake_proto_rawDesc = []byte{ + 0x0a, 0x21, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67, + 0x72, 0x70, 0x63, 0x2f, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x67, 0x72, 0x70, 0x63, 0x22, 0x3a, 0x0a, 0x14, 0x52, 0x65, 0x61, + 0x63, 0x74, 0x6f, 0x72, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, + 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x04, 0x70, 0x6f, 0x72, 0x74, 0x22, 0x79, 0x0a, 0x15, 0x52, 0x65, 0x61, 0x63, 0x74, 0x6f, 0x72, + 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x10, + 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, + 0x12, 0x10, 0x0a, 0x03, 0x6f, 0x72, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6f, + 0x72, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x63, 0x6b, + 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, + 0x32, 0x5c, 0x0a, 0x09, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x12, 0x4f, 0x0a, + 0x14, 0x52, 0x65, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x48, 0x61, + 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x61, + 0x63, 0x74, 0x6f, 0x72, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x61, 0x63, 0x74, 0x6f, 0x72, + 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x13, + 0x5a, 0x11, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67, + 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_internal_pkg_grpc_handshake_proto_rawDescOnce sync.Once + file_internal_pkg_grpc_handshake_proto_rawDescData = file_internal_pkg_grpc_handshake_proto_rawDesc +) + +func file_internal_pkg_grpc_handshake_proto_rawDescGZIP() []byte { + file_internal_pkg_grpc_handshake_proto_rawDescOnce.Do(func() { + file_internal_pkg_grpc_handshake_proto_rawDescData = protoimpl.X.CompressGZIP(file_internal_pkg_grpc_handshake_proto_rawDescData) + }) + return file_internal_pkg_grpc_handshake_proto_rawDescData +} + +var file_internal_pkg_grpc_handshake_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_internal_pkg_grpc_handshake_proto_goTypes = []interface{}{ + (*ReactorClientRequest)(nil), // 0: grpc.ReactorClientRequest + (*ReactorClientResponse)(nil), // 1: grpc.ReactorClientResponse +} +var file_internal_pkg_grpc_handshake_proto_depIdxs = []int32{ + 0, // 0: grpc.handshake.ReactorClientHandler:input_type -> grpc.ReactorClientRequest + 1, // 1: grpc.handshake.ReactorClientHandler:output_type -> grpc.ReactorClientResponse + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_internal_pkg_grpc_handshake_proto_init() } +func file_internal_pkg_grpc_handshake_proto_init() { + if File_internal_pkg_grpc_handshake_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_internal_pkg_grpc_handshake_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReactorClientRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_internal_pkg_grpc_handshake_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReactorClientResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_internal_pkg_grpc_handshake_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_internal_pkg_grpc_handshake_proto_goTypes, + DependencyIndexes: file_internal_pkg_grpc_handshake_proto_depIdxs, + MessageInfos: file_internal_pkg_grpc_handshake_proto_msgTypes, + }.Build() + File_internal_pkg_grpc_handshake_proto = out.File + file_internal_pkg_grpc_handshake_proto_rawDesc = nil + file_internal_pkg_grpc_handshake_proto_goTypes = nil + file_internal_pkg_grpc_handshake_proto_depIdxs = nil +} diff --git a/internal/pkg/grpc/handshake.proto b/internal/pkg/grpc/handshake.proto new file mode 100644 index 0000000..716477e --- /dev/null +++ b/internal/pkg/grpc/handshake.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; +package grpc; + +option go_package = "internal/pkg/grpc"; + +service handshake { + rpc ReactorClientHandler(ReactorClientRequest) returns (ReactorClientResponse); +} + +message ReactorClientRequest { + uint32 id = 1; + uint32 port = 2; // client gRPC port +} + +message ReactorClientResponse { + uint32 id = 1; + string url = 2; + string org = 3; + string token = 4; + string bucket = 5; +} diff --git a/internal/pkg/grpc/server_grpc.pb.go b/internal/pkg/grpc/handshake_grpc.pb.go similarity index 64% rename from internal/pkg/grpc/server_grpc.pb.go rename to internal/pkg/grpc/handshake_grpc.pb.go index eaa3c5d..a78492f 100644 --- a/internal/pkg/grpc/server_grpc.pb.go +++ b/internal/pkg/grpc/handshake_grpc.pb.go @@ -1,8 +1,8 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.2.0 -// - protoc v3.6.1 -// source: internal/pkg/grpc/server.proto +// - protoc v3.21.12 +// source: internal/pkg/grpc/handshake.proto package grpc @@ -22,7 +22,7 @@ const _ = grpc.SupportPackageIsVersion7 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type HandshakeClient interface { - ClientDiscoveryHandler(ctx context.Context, in *ClientRequest, opts ...grpc.CallOption) (*ClientResponse, error) + ReactorClientHandler(ctx context.Context, in *ReactorClientRequest, opts ...grpc.CallOption) (*ReactorClientResponse, error) } type handshakeClient struct { @@ -33,9 +33,9 @@ func NewHandshakeClient(cc grpc.ClientConnInterface) HandshakeClient { return &handshakeClient{cc} } -func (c *handshakeClient) ClientDiscoveryHandler(ctx context.Context, in *ClientRequest, opts ...grpc.CallOption) (*ClientResponse, error) { - out := new(ClientResponse) - err := c.cc.Invoke(ctx, "/grpc.handshake/ClientDiscoveryHandler", in, out, opts...) +func (c *handshakeClient) ReactorClientHandler(ctx context.Context, in *ReactorClientRequest, opts ...grpc.CallOption) (*ReactorClientResponse, error) { + out := new(ReactorClientResponse) + err := c.cc.Invoke(ctx, "/grpc.handshake/ReactorClientHandler", in, out, opts...) if err != nil { return nil, err } @@ -46,7 +46,7 @@ func (c *handshakeClient) ClientDiscoveryHandler(ctx context.Context, in *Client // All implementations must embed UnimplementedHandshakeServer // for forward compatibility type HandshakeServer interface { - ClientDiscoveryHandler(context.Context, *ClientRequest) (*ClientResponse, error) + ReactorClientHandler(context.Context, *ReactorClientRequest) (*ReactorClientResponse, error) mustEmbedUnimplementedHandshakeServer() } @@ -54,8 +54,8 @@ type HandshakeServer interface { type UnimplementedHandshakeServer struct { } -func (UnimplementedHandshakeServer) ClientDiscoveryHandler(context.Context, *ClientRequest) (*ClientResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ClientDiscoveryHandler not implemented") +func (UnimplementedHandshakeServer) ReactorClientHandler(context.Context, *ReactorClientRequest) (*ReactorClientResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ReactorClientHandler not implemented") } func (UnimplementedHandshakeServer) mustEmbedUnimplementedHandshakeServer() {} @@ -70,20 +70,20 @@ func RegisterHandshakeServer(s grpc.ServiceRegistrar, srv HandshakeServer) { s.RegisterService(&Handshake_ServiceDesc, srv) } -func _Handshake_ClientDiscoveryHandler_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ClientRequest) +func _Handshake_ReactorClientHandler_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ReactorClientRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(HandshakeServer).ClientDiscoveryHandler(ctx, in) + return srv.(HandshakeServer).ReactorClientHandler(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/grpc.handshake/ClientDiscoveryHandler", + FullMethod: "/grpc.handshake/ReactorClientHandler", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(HandshakeServer).ClientDiscoveryHandler(ctx, req.(*ClientRequest)) + return srv.(HandshakeServer).ReactorClientHandler(ctx, req.(*ReactorClientRequest)) } return interceptor(ctx, in, info, handler) } @@ -96,10 +96,10 @@ var Handshake_ServiceDesc = grpc.ServiceDesc{ HandlerType: (*HandshakeServer)(nil), Methods: []grpc.MethodDesc{ { - MethodName: "ClientDiscoveryHandler", - Handler: _Handshake_ClientDiscoveryHandler_Handler, + MethodName: "ReactorClientHandler", + Handler: _Handshake_ReactorClientHandler_Handler, }, }, Streams: []grpc.StreamDesc{}, - Metadata: "internal/pkg/grpc/server.proto", + Metadata: "internal/pkg/grpc/handshake.proto", } diff --git a/internal/pkg/grpc/monitoring.pb.go b/internal/pkg/grpc/monitoring.pb.go index f47a538..59cd1d3 100644 --- a/internal/pkg/grpc/monitoring.pb.go +++ b/internal/pkg/grpc/monitoring.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.0 -// protoc v3.12.4 +// protoc-gen-go v1.28.1 +// protoc v3.21.12 // source: internal/pkg/grpc/monitoring.proto package grpc @@ -20,56 +20,7 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -type Status int32 - -const ( - Status_DEAD Status = 0 - Status_ALIVE Status = 1 - Status_UNKOWN Status = 2 -) - -// Enum value maps for Status. -var ( - Status_name = map[int32]string{ - 0: "DEAD", - 1: "ALIVE", - 2: "UNKOWN", - } - Status_value = map[string]int32{ - "DEAD": 0, - "ALIVE": 1, - "UNKOWN": 2, - } -) - -func (x Status) Enum() *Status { - p := new(Status) - *p = x - return p -} - -func (x Status) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (Status) Descriptor() protoreflect.EnumDescriptor { - return file_internal_pkg_grpc_monitoring_proto_enumTypes[0].Descriptor() -} - -func (Status) Type() protoreflect.EnumType { - return &file_internal_pkg_grpc_monitoring_proto_enumTypes[0] -} - -func (x Status) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use Status.Descriptor instead. -func (Status) EnumDescriptor() ([]byte, []int) { - return file_internal_pkg_grpc_monitoring_proto_rawDescGZIP(), []int{0} -} - -type ReactorStatusResponse struct { +type ReactorAck struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -77,8 +28,8 @@ type ReactorStatusResponse struct { Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` } -func (x *ReactorStatusResponse) Reset() { - *x = ReactorStatusResponse{} +func (x *ReactorAck) Reset() { + *x = ReactorAck{} if protoimpl.UnsafeEnabled { mi := &file_internal_pkg_grpc_monitoring_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -86,13 +37,13 @@ func (x *ReactorStatusResponse) Reset() { } } -func (x *ReactorStatusResponse) String() string { +func (x *ReactorAck) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ReactorStatusResponse) ProtoMessage() {} +func (*ReactorAck) ProtoMessage() {} -func (x *ReactorStatusResponse) ProtoReflect() protoreflect.Message { +func (x *ReactorAck) ProtoReflect() protoreflect.Message { mi := &file_internal_pkg_grpc_monitoring_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -104,30 +55,28 @@ func (x *ReactorStatusResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ReactorStatusResponse.ProtoReflect.Descriptor instead. -func (*ReactorStatusResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use ReactorAck.ProtoReflect.Descriptor instead. +func (*ReactorAck) Descriptor() ([]byte, []int) { return file_internal_pkg_grpc_monitoring_proto_rawDescGZIP(), []int{0} } -func (x *ReactorStatusResponse) GetId() int32 { +func (x *ReactorAck) GetId() int32 { if x != nil { return x.Id } return 0 } -type ReactorStatusPing struct { +type ReactorPing struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - // new devices - Devices []*Device `protobuf:"bytes,2,rep,name=devices,proto3" json:"devices,omitempty"` } -func (x *ReactorStatusPing) Reset() { - *x = ReactorStatusPing{} +func (x *ReactorPing) Reset() { + *x = ReactorPing{} if protoimpl.UnsafeEnabled { mi := &file_internal_pkg_grpc_monitoring_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -135,13 +84,13 @@ func (x *ReactorStatusPing) Reset() { } } -func (x *ReactorStatusPing) String() string { +func (x *ReactorPing) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ReactorStatusPing) ProtoMessage() {} +func (*ReactorPing) ProtoMessage() {} -func (x *ReactorStatusPing) ProtoReflect() protoreflect.Message { +func (x *ReactorPing) ProtoReflect() protoreflect.Message { mi := &file_internal_pkg_grpc_monitoring_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -153,108 +102,34 @@ func (x *ReactorStatusPing) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ReactorStatusPing.ProtoReflect.Descriptor instead. -func (*ReactorStatusPing) Descriptor() ([]byte, []int) { +// Deprecated: Use ReactorPing.ProtoReflect.Descriptor instead. +func (*ReactorPing) Descriptor() ([]byte, []int) { return file_internal_pkg_grpc_monitoring_proto_rawDescGZIP(), []int{1} } -func (x *ReactorStatusPing) GetId() int32 { +func (x *ReactorPing) GetId() int32 { if x != nil { return x.Id } return 0 } -func (x *ReactorStatusPing) GetDevices() []*Device { - if x != nil { - return x.Devices - } - return nil -} - -type Device struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Addr int32 `protobuf:"varint,1,opt,name=addr,proto3" json:"addr,omitempty"` // i2c addr - Status Status `protobuf:"varint,2,opt,name=status,proto3,enum=grpc.Status" json:"status,omitempty"` // most recent status -} - -func (x *Device) Reset() { - *x = Device{} - if protoimpl.UnsafeEnabled { - mi := &file_internal_pkg_grpc_monitoring_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Device) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Device) ProtoMessage() {} - -func (x *Device) ProtoReflect() protoreflect.Message { - mi := &file_internal_pkg_grpc_monitoring_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Device.ProtoReflect.Descriptor instead. -func (*Device) Descriptor() ([]byte, []int) { - return file_internal_pkg_grpc_monitoring_proto_rawDescGZIP(), []int{2} -} - -func (x *Device) GetAddr() int32 { - if x != nil { - return x.Addr - } - return 0 -} - -func (x *Device) GetStatus() Status { - if x != nil { - return x.Status - } - return Status_DEAD -} - var File_internal_pkg_grpc_monitoring_proto protoreflect.FileDescriptor var file_internal_pkg_grpc_monitoring_proto_rawDesc = []byte{ 0x0a, 0x22, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x6d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x67, 0x72, 0x70, 0x63, 0x22, 0x27, 0x0a, 0x15, 0x52, 0x65, - 0x61, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x02, 0x69, 0x64, 0x22, 0x4b, 0x0a, 0x11, 0x52, 0x65, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x26, 0x0a, 0x07, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x72, 0x70, 0x63, - 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, - 0x22, 0x42, 0x0a, 0x06, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, - 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x24, - 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0c, - 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x2a, 0x29, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x08, - 0x0a, 0x04, 0x44, 0x45, 0x41, 0x44, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x4c, 0x49, 0x56, - 0x45, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x4e, 0x4b, 0x4f, 0x57, 0x4e, 0x10, 0x02, 0x32, - 0x5a, 0x0a, 0x0a, 0x6d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x4c, 0x0a, - 0x14, 0x52, 0x65, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x48, 0x61, - 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x61, - 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x50, 0x69, 0x6e, 0x67, 0x1a, 0x1b, - 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x13, 0x5a, 0x11, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67, 0x72, 0x70, 0x63, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x67, 0x72, 0x70, 0x63, 0x22, 0x1c, 0x0a, 0x0a, 0x52, 0x65, + 0x61, 0x63, 0x74, 0x6f, 0x72, 0x41, 0x63, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x22, 0x1d, 0x0a, 0x0b, 0x52, 0x65, 0x61, 0x63, + 0x74, 0x6f, 0x72, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x32, 0x49, 0x0a, 0x0a, 0x6d, 0x6f, 0x6e, 0x69, 0x74, + 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x3b, 0x0a, 0x12, 0x52, 0x65, 0x61, 0x63, 0x74, 0x6f, 0x72, + 0x50, 0x69, 0x6e, 0x67, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x12, 0x11, 0x2e, 0x67, 0x72, + 0x70, 0x63, 0x2e, 0x52, 0x65, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x50, 0x69, 0x6e, 0x67, 0x1a, 0x10, + 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x41, 0x63, 0x6b, + 0x28, 0x01, 0x42, 0x13, 0x5a, 0x11, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, + 0x6b, 0x67, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -269,24 +144,19 @@ func file_internal_pkg_grpc_monitoring_proto_rawDescGZIP() []byte { return file_internal_pkg_grpc_monitoring_proto_rawDescData } -var file_internal_pkg_grpc_monitoring_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_internal_pkg_grpc_monitoring_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_internal_pkg_grpc_monitoring_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_internal_pkg_grpc_monitoring_proto_goTypes = []interface{}{ - (Status)(0), // 0: grpc.Status - (*ReactorStatusResponse)(nil), // 1: grpc.ReactorStatusResponse - (*ReactorStatusPing)(nil), // 2: grpc.ReactorStatusPing - (*Device)(nil), // 3: grpc.Device + (*ReactorAck)(nil), // 0: grpc.ReactorAck + (*ReactorPing)(nil), // 1: grpc.ReactorPing } var file_internal_pkg_grpc_monitoring_proto_depIdxs = []int32{ - 3, // 0: grpc.ReactorStatusPing.devices:type_name -> grpc.Device - 0, // 1: grpc.Device.status:type_name -> grpc.Status - 2, // 2: grpc.monitoring.ReactorStatusHandler:input_type -> grpc.ReactorStatusPing - 1, // 3: grpc.monitoring.ReactorStatusHandler:output_type -> grpc.ReactorStatusResponse - 3, // [3:4] is the sub-list for method output_type - 2, // [2:3] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 1, // 0: grpc.monitoring.ReactorPingHandler:input_type -> grpc.ReactorPing + 0, // 1: grpc.monitoring.ReactorPingHandler:output_type -> grpc.ReactorAck + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name } func init() { file_internal_pkg_grpc_monitoring_proto_init() } @@ -296,7 +166,7 @@ func file_internal_pkg_grpc_monitoring_proto_init() { } if !protoimpl.UnsafeEnabled { file_internal_pkg_grpc_monitoring_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReactorStatusResponse); i { + switch v := v.(*ReactorAck); i { case 0: return &v.state case 1: @@ -308,19 +178,7 @@ func file_internal_pkg_grpc_monitoring_proto_init() { } } file_internal_pkg_grpc_monitoring_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReactorStatusPing); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_internal_pkg_grpc_monitoring_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Device); i { + switch v := v.(*ReactorPing); i { case 0: return &v.state case 1: @@ -337,14 +195,13 @@ func file_internal_pkg_grpc_monitoring_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_internal_pkg_grpc_monitoring_proto_rawDesc, - NumEnums: 1, - NumMessages: 3, + NumEnums: 0, + NumMessages: 2, NumExtensions: 0, NumServices: 1, }, GoTypes: file_internal_pkg_grpc_monitoring_proto_goTypes, DependencyIndexes: file_internal_pkg_grpc_monitoring_proto_depIdxs, - EnumInfos: file_internal_pkg_grpc_monitoring_proto_enumTypes, MessageInfos: file_internal_pkg_grpc_monitoring_proto_msgTypes, }.Build() File_internal_pkg_grpc_monitoring_proto = out.File diff --git a/internal/pkg/grpc/monitoring.proto b/internal/pkg/grpc/monitoring.proto index 128d9ad..8f307b2 100644 --- a/internal/pkg/grpc/monitoring.proto +++ b/internal/pkg/grpc/monitoring.proto @@ -4,26 +4,13 @@ package grpc; option go_package = "internal/pkg/grpc"; service monitoring { - rpc ReactorStatusHandler(ReactorStatusPing) returns (ReactorStatusResponse); + rpc ReactorPingHandler(stream ReactorPing) returns (ReactorAck); } -message ReactorStatusResponse { +message ReactorAck { int32 id = 1; } -message ReactorStatusPing { +message ReactorPing { int32 id = 1; - // new devices - repeated Device devices = 2; -} - -enum Status { - DEAD = 0; - ALIVE = 1; - UNKOWN = 2; -} - -message Device { - int32 addr = 1; // i2c addr - Status status = 2; // most recent status } diff --git a/internal/pkg/grpc/monitoring_grpc.pb.go b/internal/pkg/grpc/monitoring_grpc.pb.go index cc3461b..75a39d6 100644 --- a/internal/pkg/grpc/monitoring_grpc.pb.go +++ b/internal/pkg/grpc/monitoring_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.2.0 -// - protoc v3.12.4 +// - protoc v3.21.12 // source: internal/pkg/grpc/monitoring.proto package grpc @@ -22,7 +22,7 @@ const _ = grpc.SupportPackageIsVersion7 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type MonitoringClient interface { - ReactorStatusHandler(ctx context.Context, in *ReactorStatusPing, opts ...grpc.CallOption) (*ReactorStatusResponse, error) + ReactorPingHandler(ctx context.Context, opts ...grpc.CallOption) (Monitoring_ReactorPingHandlerClient, error) } type monitoringClient struct { @@ -33,20 +33,45 @@ func NewMonitoringClient(cc grpc.ClientConnInterface) MonitoringClient { return &monitoringClient{cc} } -func (c *monitoringClient) ReactorStatusHandler(ctx context.Context, in *ReactorStatusPing, opts ...grpc.CallOption) (*ReactorStatusResponse, error) { - out := new(ReactorStatusResponse) - err := c.cc.Invoke(ctx, "/grpc.monitoring/ReactorStatusHandler", in, out, opts...) +func (c *monitoringClient) ReactorPingHandler(ctx context.Context, opts ...grpc.CallOption) (Monitoring_ReactorPingHandlerClient, error) { + stream, err := c.cc.NewStream(ctx, &Monitoring_ServiceDesc.Streams[0], "/grpc.monitoring/ReactorPingHandler", opts...) if err != nil { return nil, err } - return out, nil + x := &monitoringReactorPingHandlerClient{stream} + return x, nil +} + +type Monitoring_ReactorPingHandlerClient interface { + Send(*ReactorPing) error + CloseAndRecv() (*ReactorAck, error) + grpc.ClientStream +} + +type monitoringReactorPingHandlerClient struct { + grpc.ClientStream +} + +func (x *monitoringReactorPingHandlerClient) Send(m *ReactorPing) error { + return x.ClientStream.SendMsg(m) +} + +func (x *monitoringReactorPingHandlerClient) CloseAndRecv() (*ReactorAck, error) { + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + m := new(ReactorAck) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil } // MonitoringServer is the server API for Monitoring service. // All implementations must embed UnimplementedMonitoringServer // for forward compatibility type MonitoringServer interface { - ReactorStatusHandler(context.Context, *ReactorStatusPing) (*ReactorStatusResponse, error) + ReactorPingHandler(Monitoring_ReactorPingHandlerServer) error mustEmbedUnimplementedMonitoringServer() } @@ -54,8 +79,8 @@ type MonitoringServer interface { type UnimplementedMonitoringServer struct { } -func (UnimplementedMonitoringServer) ReactorStatusHandler(context.Context, *ReactorStatusPing) (*ReactorStatusResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ReactorStatusHandler not implemented") +func (UnimplementedMonitoringServer) ReactorPingHandler(Monitoring_ReactorPingHandlerServer) error { + return status.Errorf(codes.Unimplemented, "method ReactorPingHandler not implemented") } func (UnimplementedMonitoringServer) mustEmbedUnimplementedMonitoringServer() {} @@ -70,22 +95,30 @@ func RegisterMonitoringServer(s grpc.ServiceRegistrar, srv MonitoringServer) { s.RegisterService(&Monitoring_ServiceDesc, srv) } -func _Monitoring_ReactorStatusHandler_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ReactorStatusPing) - if err := dec(in); err != nil { +func _Monitoring_ReactorPingHandler_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(MonitoringServer).ReactorPingHandler(&monitoringReactorPingHandlerServer{stream}) +} + +type Monitoring_ReactorPingHandlerServer interface { + SendAndClose(*ReactorAck) error + Recv() (*ReactorPing, error) + grpc.ServerStream +} + +type monitoringReactorPingHandlerServer struct { + grpc.ServerStream +} + +func (x *monitoringReactorPingHandlerServer) SendAndClose(m *ReactorAck) error { + return x.ServerStream.SendMsg(m) +} + +func (x *monitoringReactorPingHandlerServer) Recv() (*ReactorPing, error) { + m := new(ReactorPing) + if err := x.ServerStream.RecvMsg(m); err != nil { return nil, err } - if interceptor == nil { - return srv.(MonitoringServer).ReactorStatusHandler(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/grpc.monitoring/ReactorStatusHandler", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MonitoringServer).ReactorStatusHandler(ctx, req.(*ReactorStatusPing)) - } - return interceptor(ctx, in, info, handler) + return m, nil } // Monitoring_ServiceDesc is the grpc.ServiceDesc for Monitoring service. @@ -94,12 +127,13 @@ func _Monitoring_ReactorStatusHandler_Handler(srv interface{}, ctx context.Conte var Monitoring_ServiceDesc = grpc.ServiceDesc{ ServiceName: "grpc.monitoring", HandlerType: (*MonitoringServer)(nil), - Methods: []grpc.MethodDesc{ + Methods: []grpc.MethodDesc{}, + Streams: []grpc.StreamDesc{ { - MethodName: "ReactorStatusHandler", - Handler: _Monitoring_ReactorStatusHandler_Handler, + StreamName: "ReactorPingHandler", + Handler: _Monitoring_ReactorPingHandler_Handler, + ClientStreams: true, }, }, - Streams: []grpc.StreamDesc{}, Metadata: "internal/pkg/grpc/monitoring.proto", } diff --git a/internal/pkg/grpc/server.pb.go b/internal/pkg/grpc/server.pb.go deleted file mode 100644 index 20b821c..0000000 --- a/internal/pkg/grpc/server.pb.go +++ /dev/null @@ -1,335 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.0 -// protoc v3.6.1 -// source: internal/pkg/grpc/server.proto - -package grpc - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type ClientRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ClientId uint32 `protobuf:"varint,1,opt,name=clientId,proto3" json:"clientId,omitempty"` - ClientType string `protobuf:"bytes,2,opt,name=clientType,proto3" json:"clientType,omitempty"` -} - -func (x *ClientRequest) Reset() { - *x = ClientRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_internal_pkg_grpc_server_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ClientRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ClientRequest) ProtoMessage() {} - -func (x *ClientRequest) ProtoReflect() protoreflect.Message { - mi := &file_internal_pkg_grpc_server_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ClientRequest.ProtoReflect.Descriptor instead. -func (*ClientRequest) Descriptor() ([]byte, []int) { - return file_internal_pkg_grpc_server_proto_rawDescGZIP(), []int{0} -} - -func (x *ClientRequest) GetClientId() uint32 { - if x != nil { - return x.ClientId - } - return 0 -} - -func (x *ClientRequest) GetClientType() string { - if x != nil { - return x.ClientType - } - return "" -} - -type ClientResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ClientId uint32 `protobuf:"varint,1,opt,name=clientId,proto3" json:"clientId,omitempty"` - ServerPort uint32 `protobuf:"varint,2,opt,name=serverPort,proto3" json:"serverPort,omitempty"` - Database *Database `protobuf:"bytes,3,opt,name=database,proto3" json:"database,omitempty"` -} - -func (x *ClientResponse) Reset() { - *x = ClientResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_internal_pkg_grpc_server_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ClientResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ClientResponse) ProtoMessage() {} - -func (x *ClientResponse) ProtoReflect() protoreflect.Message { - mi := &file_internal_pkg_grpc_server_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ClientResponse.ProtoReflect.Descriptor instead. -func (*ClientResponse) Descriptor() ([]byte, []int) { - return file_internal_pkg_grpc_server_proto_rawDescGZIP(), []int{1} -} - -func (x *ClientResponse) GetClientId() uint32 { - if x != nil { - return x.ClientId - } - return 0 -} - -func (x *ClientResponse) GetServerPort() uint32 { - if x != nil { - return x.ServerPort - } - return 0 -} - -func (x *ClientResponse) GetDatabase() *Database { - if x != nil { - return x.Database - } - return nil -} - -type Database struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - URL string `protobuf:"bytes,1,opt,name=URL,proto3" json:"URL,omitempty"` - ORG string `protobuf:"bytes,2,opt,name=ORG,proto3" json:"ORG,omitempty"` - Token string `protobuf:"bytes,3,opt,name=token,proto3" json:"token,omitempty"` - Bucket string `protobuf:"bytes,4,opt,name=bucket,proto3" json:"bucket,omitempty"` -} - -func (x *Database) Reset() { - *x = Database{} - if protoimpl.UnsafeEnabled { - mi := &file_internal_pkg_grpc_server_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Database) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Database) ProtoMessage() {} - -func (x *Database) ProtoReflect() protoreflect.Message { - mi := &file_internal_pkg_grpc_server_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Database.ProtoReflect.Descriptor instead. -func (*Database) Descriptor() ([]byte, []int) { - return file_internal_pkg_grpc_server_proto_rawDescGZIP(), []int{2} -} - -func (x *Database) GetURL() string { - if x != nil { - return x.URL - } - return "" -} - -func (x *Database) GetORG() string { - if x != nil { - return x.ORG - } - return "" -} - -func (x *Database) GetToken() string { - if x != nil { - return x.Token - } - return "" -} - -func (x *Database) GetBucket() string { - if x != nil { - return x.Bucket - } - return "" -} - -var File_internal_pkg_grpc_server_proto protoreflect.FileDescriptor - -var file_internal_pkg_grpc_server_proto_rawDesc = []byte{ - 0x0a, 0x1e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67, - 0x72, 0x70, 0x63, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x12, 0x04, 0x67, 0x72, 0x70, 0x63, 0x22, 0x4b, 0x0a, 0x0d, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x22, 0x78, 0x0a, 0x0e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, - 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x6f, 0x72, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x6f, 0x72, - 0x74, 0x12, 0x2a, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x62, - 0x61, 0x73, 0x65, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x22, 0x5c, 0x0a, - 0x08, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x52, 0x4c, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x52, 0x4c, 0x12, 0x10, 0x0a, 0x03, 0x4f, - 0x52, 0x47, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x4f, 0x52, 0x47, 0x12, 0x14, 0x0a, - 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, - 0x6b, 0x65, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x32, 0x50, 0x0a, 0x09, 0x68, - 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x12, 0x43, 0x0a, 0x16, 0x43, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x48, 0x61, 0x6e, 0x64, 0x6c, - 0x65, 0x72, 0x12, 0x13, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x13, 0x5a, - 0x11, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67, 0x72, - 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_internal_pkg_grpc_server_proto_rawDescOnce sync.Once - file_internal_pkg_grpc_server_proto_rawDescData = file_internal_pkg_grpc_server_proto_rawDesc -) - -func file_internal_pkg_grpc_server_proto_rawDescGZIP() []byte { - file_internal_pkg_grpc_server_proto_rawDescOnce.Do(func() { - file_internal_pkg_grpc_server_proto_rawDescData = protoimpl.X.CompressGZIP(file_internal_pkg_grpc_server_proto_rawDescData) - }) - return file_internal_pkg_grpc_server_proto_rawDescData -} - -var file_internal_pkg_grpc_server_proto_msgTypes = make([]protoimpl.MessageInfo, 3) -var file_internal_pkg_grpc_server_proto_goTypes = []interface{}{ - (*ClientRequest)(nil), // 0: grpc.ClientRequest - (*ClientResponse)(nil), // 1: grpc.ClientResponse - (*Database)(nil), // 2: grpc.Database -} -var file_internal_pkg_grpc_server_proto_depIdxs = []int32{ - 2, // 0: grpc.ClientResponse.database:type_name -> grpc.Database - 0, // 1: grpc.handshake.ClientDiscoveryHandler:input_type -> grpc.ClientRequest - 1, // 2: grpc.handshake.ClientDiscoveryHandler:output_type -> grpc.ClientResponse - 2, // [2:3] is the sub-list for method output_type - 1, // [1:2] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name -} - -func init() { file_internal_pkg_grpc_server_proto_init() } -func file_internal_pkg_grpc_server_proto_init() { - if File_internal_pkg_grpc_server_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_internal_pkg_grpc_server_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ClientRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_internal_pkg_grpc_server_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ClientResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_internal_pkg_grpc_server_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Database); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_internal_pkg_grpc_server_proto_rawDesc, - NumEnums: 0, - NumMessages: 3, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_internal_pkg_grpc_server_proto_goTypes, - DependencyIndexes: file_internal_pkg_grpc_server_proto_depIdxs, - MessageInfos: file_internal_pkg_grpc_server_proto_msgTypes, - }.Build() - File_internal_pkg_grpc_server_proto = out.File - file_internal_pkg_grpc_server_proto_rawDesc = nil - file_internal_pkg_grpc_server_proto_goTypes = nil - file_internal_pkg_grpc_server_proto_depIdxs = nil -} diff --git a/internal/pkg/grpc/server.proto b/internal/pkg/grpc/server.proto deleted file mode 100644 index 27f336f..0000000 --- a/internal/pkg/grpc/server.proto +++ /dev/null @@ -1,28 +0,0 @@ -syntax = "proto3"; -package grpc; - -option go_package = "internal/pkg/grpc"; - -service handshake { - rpc ClientDiscoveryHandler(ClientRequest) returns (ClientResponse); -} - -message ClientRequest { - uint32 clientId = 1; - string clientType = 2; - string ip = 3; // client ip - uint32 port = 4; // client port for gRPC server -} - -message ClientResponse { - uint32 clientId = 1; - uint32 serverPort = 2; - Database database = 3; -} - -message Database { - string URL = 1; - string ORG = 2; - string token = 3; - string bucket = 4; -} diff --git a/internal/pkg/influxdb/client.go b/internal/pkg/influxdb/client.go deleted file mode 100644 index c0b56a9..0000000 --- a/internal/pkg/influxdb/client.go +++ /dev/null @@ -1,81 +0,0 @@ -package influxdb - -import ( - _ "fmt" - - _ "github.com/influxdata/influxdb-client-go/v2" - "github.com/spf13/viper" -) - -type DBInfo struct { - URL string `mapstructure:"url"` - Org string `mapstructure:"org,omitempty` - Bucket string `mapstructure:"bucket,omitempty"` - Token string `mapstructure:"token,omitempty"` - // Client *influxdb2.Client -} - -type DBAdmin struct { - // struct for admin methods - *DBInfo - Config *viper.Viper -} - -type DBClient struct { - // struct for client methods - *DBInfo - Config *viper.Viper -} - -func NewDBInfo(config *viper.Viper) (*DBInfo, error) { - db := &DBInfo{} - // grabbing config vals - err := config.UnmarshalKey("db", db) - return db, err -} - -func NewDBClient(config *viper.Viper) (*DBClient, error) { - - client := &DBClient{Config: config} - // grabbing config vals - var err error - client.DBInfo, err = NewDBInfo(config) - return client, err -} - -func NewDBAdmin(config *viper.Viper) (*DBAdmin, error) { - admin := &DBAdmin{Config: config} - var err error - // creating client - admin.DBInfo, err = NewDBInfo(config) - return admin, err -} - -// base level funcs -func (d *DBInfo) Start() error { - // connect to DB based w/ info - return nil -} - -func (d *DBAdmin) GetReactorClient(id int) (url, bucket, org, token string, err error) { - // given an id returns - // (url, org, bucket, token, error) for said id - /* - client := influxdb2.NewClient(d.URL, d.Token) - defer client.Close() - bucket, err := client.BucketsAPI().FindBucketByName(context.Background(), id) - if err != nil { - return "", "", err - } - if d.ReactorExists(id) { - // get corresponding reactor token and bucket - } - */ - url = d.URL - org = d.Org - token = "" - bucket = "" - //err = errors.New("Unimpl") - err = nil - return -} diff --git a/internal/pkg/server/coordinator.go b/internal/pkg/server/coordinator.go index 1df9b7e..5b89e8a 100644 --- a/internal/pkg/server/coordinator.go +++ b/internal/pkg/server/coordinator.go @@ -4,9 +4,7 @@ package server import ( pb "FRMS/internal/pkg/grpc" - "FRMS/internal/pkg/influxdb" - "FRMS/internal/pkg/logging" - "context" + "errors" "fmt" "net" "sync" @@ -15,264 +13,68 @@ import ( "google.golang.org/grpc" ) -// Database is an interface to interact with the server database. -// Used mainly to find existing credentials for -// incoming reactor client connections. -type Database interface { - GetReactorClient(int) (string, string, string, string, error) // returns (url, org, bucket, token, err) -} - -// NewDatabaseAdmin creates a new database admin that implements the -// Database interface. -// Allows access to the database to find/create reactor credentials. -// Implemented via the influxdb package. -func NewDatabaseAdmin(config *viper.Viper) (Database, error) { - return influxdb.NewDBAdmin(config) -} - -// CentralCoordinator is the main coordinator struct that runs on the server. -// Used to oversee the reactor managers as well as process incoming -// client connections. -// Also interacts with the database and global config. -type CentralCoordinator struct { - ClientConnections *ClientPacket - *ReactorCoordinator - Database - Config *viper.Viper - // from config - Ports map[string]int `mapstructure:"ports"` - Err chan error -} - -// NewCentralCoordinator creates a central coordinator with the given global -// config and error channel. -// It will create a new reactor coordinator and database admin. -// It will also try to load the existing configuration information. -func NewCentralCoordinator(config *viper.Viper, ch chan error) *CentralCoordinator { - // create a central coordinator to manage requests - db, err := NewDatabaseAdmin(config) - if err != nil { - ch <- err - } - - rc, err := NewReactorCoordinator(config, ch) - if err != nil { - ch <- err - } - - config.UnmarshalKey("server.ports", rc) - - c := &CentralCoordinator{ - Err: ch, - Config: config, - Database: db, - ReactorCoordinator: rc, - } - - // grab config settings - if err = config.UnmarshalKey("server", c); err != nil { - ch <- err - } - - return c -} - -// Start activates the central coordinator and ensures it is ready for -// new clients. -// Creates a listener and starts both reactor coordinator and listener. -func (c *CentralCoordinator) Start() { - - clientChan := make(chan *ClientPacket) - - l, err := NewListener(clientChan, c.Err) - - if err != nil { - c.Err <- err - } - - c.Config.UnmarshalKey("server.ports", l) - - if err := c.ReactorCoordinator.Start(); err != nil { - c.Err <- err - } - - if err := l.Start(); err != nil { - c.Err <- err - } - - go c.ClientListener(clientChan) -} - -// ClientListener listens on the given channel for clients that are sent -// over from the listener. -// The clients are then passed to the handler before returning the response. -func (c *CentralCoordinator) ClientListener(ch chan *ClientPacket) { - - for client := range ch { - client.Response <- c.ClientHandler(client.Client) // respond with cred - } -} - -// ClientHandler takes in a client and retrieves the associated -// database credentials. -// Currently only handles reactor type clients, can be modified -// to support others. -func (c *CentralCoordinator) ClientHandler(cl *Client) *ClientResponse { - // returns reactor db info - var err error - cr := &ClientResponse{Port: c.Ports[cl.Type]} - - if cl.Type != "reactor" { - c.Err <- fmt.Errorf("client type %s not recognized", cl.Type) - } - - go c.ReactorCoordinator.ClientHandler(cl) +var ( + ErrMissingPort = errors.New("port not set") +) - // db info - cr.URL, cr.Org, cr.Token, cr.Bucket, err = c.Database.GetReactorClient(cl.Id) +// Coordinator is runs on the server and is used to oversee +// the reactor managers as well as process incoming client connections. +type Coordinator struct { + Config *viper.Viper + listener net.Listener + grpcServer *grpc.Server - if err != nil { - c.Err <- err - } + DatabasePort int `mapstructure:"database_port"` + GRPCPort int `mapstructure:"grpc_port"` - return cr -} + directory map[int]*ReactorManager + managerMu sync.RWMutex -// ReactorCoordinator is a strucutre used to store reactor managers for -// clients that have connected at some point. -type ReactorCoordinator struct { - Port int `mapstructure:"reactor"` - *ReactorManagers Err chan error - pb.UnimplementedMonitoringServer -} -// ReactorManagers is a structure that stores a concurrent map of the -// reactor managers as well as the global config. -type ReactorManagers struct { - Config *viper.Viper - Directory map[int]*ReactorManager - sync.RWMutex + // grpc + pb.UnimplementedHandshakeServer + pb.UnimplementedMonitoringServer } -// NewReactorCoordinator takes the global config and error channel and returns -// a pointer to a ReactorCoordinator as well as any errors. -func NewReactorCoordinator(config *viper.Viper, errCh chan error) (*ReactorCoordinator, error) { +// NewCentralCoordinator creates a central coordinator with the given global +// config and error channel. +func NewCentralCoordinator(config *viper.Viper, ch chan error) *Coordinator { rmap := make(map[int]*ReactorManager) - rm := &ReactorManagers{ - Directory: rmap, + return &Coordinator{ + Err: ch, Config: config, - } - - return &ReactorCoordinator{ - Err: errCh, - ReactorManagers: rm, - }, nil -} - -// Start starts the reactor coordinator and kicks off -// registering the gRPC service -func (c *ReactorCoordinator) Start() error { - - logging.Debug(logging.DStart, "RCO 01 Starting!") - - return c.Register() -} - -// ClientHandler takes in a client and finds or creates the correct -// manager for said client. -func (c *ReactorCoordinator) ClientHandler(cl *Client) { - - if err := c.UpdateReactorManager(cl, c.Err); err != nil { - c.Err <- err + directory: rmap, } } -// GetReactorManager attempts to locate a reactor manager for a given id. -// Returns either the associated reactor manager, or an error if -// a manager does not exist for the given id. -func (m *ReactorManagers) GetReactorManager(id int) (*ReactorManager, error) { - m.RLock() - defer m.RUnlock() - - rm, exists := m.Directory[id] +// Start loads config, starts network listener and registers grpc handlers. +// Ready for new clients on return. +func (c *Coordinator) Start() error { - if !exists { - return &ReactorManager{}, fmt.Errorf("no manager for reactor %d", id) + if err := c.Config.Unmarshal(c); err != nil { + return err } - return rm, nil -} - -// UpdateReactorManager takes in a client and error channel and passes the -// client to the associate reactor manager. -// If the client does not have an existing reactor manager, it will create one -// , start it, and add it to the map for future calls. -// The function then calls UpdateClient on the reactor manager and returns -// any errors generated by this function. -func (m *ReactorManagers) UpdateReactorManager(cl *Client, errCh chan error) error { - m.RLock() - defer m.RUnlock() - - var err error - - rm, exists := m.Directory[cl.Id] - if !exists { - // reactor manager does not exist, creating new one - logging.Debug( - logging.DClient, - "RCO 01 creating manager for %v", - cl.Id, - ) + // ensure it shows up as missing + if c.GRPCPort == 0 { + c.Config.Set("grpc_port", 0) + c.Config.WriteConfig() - if rm, err = NewReactorManager(cl, m.Config, errCh); err != nil { - return err - } - - if err = rm.Start(); err != nil { - return err - } - - m.Directory[cl.Id] = rm + return ErrMissingPort } - return rm.UpdateClient(cl) -} - -// Register attaches to the servers port and attempts to bind -// a gRPC server to it. -func (r *ReactorCoordinator) Register() error { - - lis, err := net.Listen("tcp", fmt.Sprintf(":%v", r.Port)) + lis, err := net.Listen("tcp", fmt.Sprintf(":%v", c.GRPCPort)) if err != nil { return err } grpcServer := grpc.NewServer() - pb.RegisterMonitoringServer(grpcServer, r) - go grpcServer.Serve(lis) + c.listener = lis + c.grpcServer = grpcServer - logging.Debug(logging.DClient, "RCO 01 ready") - - return nil -} - -// ReactorStatusHandler is a gRPC handler used to handle incoming -// reactor requests containing information about said reactor. -// It will get the associate reactor manager and pass the -// request device information before returning an acknowledgement. -func (r *ReactorCoordinator) ReactorStatusHandler(ctx context.Context, req *pb.ReactorStatusPing) (*pb.ReactorStatusResponse, error) { - - rm, err := r.GetReactorManager(int(req.GetId())) - - if err != nil { - return &pb.ReactorStatusResponse{}, err - } - - go rm.ReactorDeviceHandler(req.GetDevices()) - - return &pb.ReactorStatusResponse{Id: int32(rm.Id)}, nil + return c.Register() } diff --git a/internal/pkg/server/database.go b/internal/pkg/server/database.go new file mode 100644 index 0000000..4195fad --- /dev/null +++ b/internal/pkg/server/database.go @@ -0,0 +1,13 @@ +package server + +type dbinfo struct { + url string + org string + token string + bucket string +} + +func (c *Coordinator) getReactorDatabaseCred(id int) (*dbinfo, error) { + + return &dbinfo{}, nil +} diff --git a/internal/pkg/server/handler.go b/internal/pkg/server/handler.go new file mode 100644 index 0000000..5e855a5 --- /dev/null +++ b/internal/pkg/server/handler.go @@ -0,0 +1,48 @@ +package server + +import ( + pb "FRMS/internal/pkg/grpc" + "FRMS/internal/pkg/logging" + "context" +) + +// ClientDiscoveryHandler implements the grpc method which can be called +// by incoming clients to first make connection to the central +// coordinator and receive database credentials. +func (c *Coordinator) ReactorClientHandler( + ctx context.Context, + req *pb.ReactorClientRequest, +) (*pb.ReactorClientResponse, error) { + + id := int(req.GetId()) + + logging.Debug( + logging.DClient, + "LIS 00 reactor %v has connected\n", + id, + ) + + db, err := c.getReactorDatabaseCred(id) + if err != nil { + return &pb.ReactorClientResponse{}, err + } + + return &pb.ReactorClientResponse{ + Id: id, + Url: db.url, + Org: db.org, + Token: db.token, + Bucket: db.bucket, + }, err +} + +// ReactorStatusHandler is a gRPC handler used to handle incoming +// reactor requests containing information about said reactor. +// It will get the associate reactor manager and pass the +// request device information before returning an acknowledgement. +func (c *Coordinator) ReactorStatusHandler(ctx context.Context, req *pb.ReactorStatusPing) (*pb.ReactorStatusResponse, error) { + + // rm, err := c.LoadReactorManager(int(req.GetId())) + + return &pb.ReactorStatusResponse{}, nil +} diff --git a/internal/pkg/server/listener.go b/internal/pkg/server/listener.go deleted file mode 100644 index c68356c..0000000 --- a/internal/pkg/server/listener.go +++ /dev/null @@ -1,120 +0,0 @@ -package server - -import ( - pb "FRMS/internal/pkg/grpc" - "FRMS/internal/pkg/logging" - "context" - "fmt" - "net" - - "google.golang.org/grpc" -) - -// Listener is a struct that listens for incoming clients on a given port -// and passes them the central coordinator. -// Implements the gRPC handshake server for clients. -type Listener struct { - Port int `mapstructure:"lis"` - ClientConnections chan *ClientPacket - Err chan error - pb.UnimplementedHandshakeServer -} - -// ClientPacket is a uniform type to pass on a channel to the server. -type ClientPacket struct { - *Client - Response chan *ClientResponse -} - -// Client is a struct containing information about the client on -// the incoming connection. -type Client struct { - //Ip string - //Port int - Id int - Model string - Type string -} - -// ClientResponse is the database credentials returned from the central -// coordinator for the given client. -type ClientResponse struct { - Port int - URL string - Org string - Token string - Bucket string -} - -// NewListener createsa new listener with the given client and error channels -func NewListener( - cch chan *ClientPacket, - ech chan error, -) (*Listener, error) { - - return &Listener{ - Err: ech, - ClientConnections: cch, - }, nil -} - -// Start activates the listener and kicks off the gRPC binding process -func (l *Listener) Start() error { - logging.Debug(logging.DStart, "LIS 01 Started client listener") - return l.Register() -} - -// Register creates a net listener on the port and binds a grpc server to it -// before registering a handshake server. -func (l *Listener) Register() error { - - lis, err := net.Listen("tcp", fmt.Sprintf(":%v", l.Port)) - if err != nil { - return err - } - - grpcServer := grpc.NewServer() - pb.RegisterHandshakeServer(grpcServer, l) - - go grpcServer.Serve(lis) - - logging.Debug(logging.DStart, "LIS 01 Registered on port %v", l.Port) - - return nil -} - -// ClientDiscoveryHandler implements the grpc method which can be called -// by incoming clients to first make connection to the central -// coordinator and receive database credentials. -func (l *Listener) ClientDiscoveryHandler(ctx context.Context, ping *pb.ClientRequest) (*pb.ClientResponse, error) { - - c := &Client{ - Id: int(ping.GetClientId()), - Type: ping.GetClientType(), - } - - logging.Debug(logging.DClient, "LIS 01 %v %v has connected\n", c.Type, c.Id) - - ch := make(chan *ClientResponse) - p := &ClientPacket{ - Client: c, - Response: ch, - } - - l.ClientConnections <- p - - resp := <-ch - - db := &pb.Database{ - URL: resp.URL, - ORG: resp.Org, - Token: resp.Token, - Bucket: resp.Bucket, - } - - return &pb.ClientResponse{ - ClientId: uint32(c.Id), - ServerPort: uint32(resp.Port), - Database: db, - }, nil -} diff --git a/internal/pkg/server/listener_test.go b/internal/pkg/server/listener_test.go deleted file mode 100644 index ae5d770..0000000 --- a/internal/pkg/server/listener_test.go +++ /dev/null @@ -1,17 +0,0 @@ -package server - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -// TestNewListener tries to create a new listener -func TestNewListener(t *testing.T) { - assert := assert.New(t) - - cch := make(chan *ClientPacket) - ech := make(chan error) - _, err := NewListener(cch, ech) - assert.Equal(err, nil, "creating listener failed") -} diff --git a/internal/pkg/server/reactor.go b/internal/pkg/server/reactor.go new file mode 100644 index 0000000..12dc3ad --- /dev/null +++ b/internal/pkg/server/reactor.go @@ -0,0 +1,121 @@ +package server + +import ( + "FRMS/internal/pkg/logging" + "FRMS/internal/pkg/manager" + "errors" + "time" +) + +var ( + ErrNoReactorManager = errors.New("no reactor manager found") +) + +// ReactorManager can be started/stopped as clients connect/disconnect. +type ReactorManager struct { + Manager // base manager interface +} + +// Manager is an interface requiring a structure that can be started +// and stopped as well as provide timeouts in milliseconds. +type Manager interface { + Start() error // status checks + Stop() error + Timeout() (time.Duration, error) // TO Generator +} + +// NewManager returns a manager fulfilling the Manager interface as well as +// any potential errors. +func NewManager(max int) (Manager, error) { + return manager.New(max) +} + +// GetReactorManager returns a reactor manager for passed id. +// Throws error if manager not found for id. +func (c *Coordinator) LoadReactorManager(id int) (*ReactorManager, error) { + + c.managerMu.RLock() + defer c.managerMu.RUnlock() + + rm, exists := c.directory[id] + + if !exists { + logging.Debug( + logging.DClient, + "RCO 00 creating manager for %v", + id, + ) + + m, err := NewManager(0) + + rm = &ReactorManager{ + Manager: m, + } + + if err = rm.Start(); err != nil { + return rm, err + } + + c.directory[id] = rm + } + + return rm, nil +} + +// // NewReactorManager takes in a client, config and channel to pass errors on. +// // Returns a new reactor manager as well as any errors that occured during +// // creation. +// // Uses MaxConnectionAttempts which defaults to 10 to prevent +// // unnessecary network load and/or timeout lengths. +// func NewReactorManager( +// ) (*ReactorManager, error) { + +// m, err := NewManager(MaxConnectionAttempts) + +// if err != nil { +// return &ReactorManager{}, err +// } + +// return r, err +// } + +// Start logs the start and calls start on the embedded manager. +func (r *ReactorManager) Start() error { + // logging.Debug(logging.DStart, "RMA starting", r.Id) + return r.Manager.Start() +} + +// Stop logs the stop and calls stop on the embedded manager. +func (r *ReactorManager) Stop() error { + // logging.Debug(logging.DExit, "RMA %v stopping", r.Id) + return r.Manager.Stop() +} + +// UpdateClient is used to change the underlying manager client if there +// changes to its data. +// +// BUG(Keegan): Client is not protected by a lock and may lead to races +// func (r *ReactorManager) UpdateClient(cl *Client) error { +// logging.Debug(logging.DClient, "RMA %v updating client", r.Id) +// r.Client = cl +// return nil +// } + +// // ReactorDeviceHandler processes incoming device information and +// // updates the manager accordingly. +// func (r *ReactorManager) ReactorDeviceHandler(devs []*pb.Device) error { + +// logging.Debug(logging.DClient, "CCO recieved ping from %v", r.Id) + +// for _, dev := range devs { +// logging.Debug( +// logging.DClient, +// "CCO %v device %v is %v", +// r.Id, +// dev.GetAddr(), +// dev.GetStatus().String(), +// ) +// } + +// return nil +// } diff --git a/internal/pkg/server/reactormanager.go b/internal/pkg/server/reactormanager.go deleted file mode 100644 index ed113f7..0000000 --- a/internal/pkg/server/reactormanager.go +++ /dev/null @@ -1,108 +0,0 @@ -package server - -import ( - pb "FRMS/internal/pkg/grpc" - "FRMS/internal/pkg/logging" - "FRMS/internal/pkg/manager" - "time" - - "github.com/spf13/viper" -) - -// MaxConnectionAttempts is the max number of tries to allow -// when connecting to a reactor. -const MaxConnectionAttempts = 10 - -// Manager is an interface requiring a structure that can be started -// and stopped as well as provide timeouts in milliseconds. -type Manager interface { - Start() error // status checks - Stop() error - Timeout() (time.Duration, error) // TO Generator -} - -// NewManager returns a manager fulfilling the Manager interface as well as -// any potential errors. -func NewManager(max int) (Manager, error) { - return manager.New(max) -} - -// ReactorManager contains a base manager, client, global -// config, and error channel. -// The ReactorManager can be started/stopped as clients connect/disconnect. -// Also serves as handler for gRPC requests from reactors. -// Can be extended to write changes to config. -type ReactorManager struct { - Manager // base manager interface - *Client - Config *viper.Viper // global config to maintain - Err chan error -} - -// NewReactorManager takes in a client, config and channel to pass errors on. -// Returns a new reactor manager as well as any errors that occured during -// creation. -// Uses MaxConnectionAttempts which defaults to 10 to prevent -// unnessecary network load and/or timeout lengths. -func NewReactorManager( - cl *Client, - config *viper.Viper, - errCh chan error, -) (*ReactorManager, error) { - - m, err := NewManager(MaxConnectionAttempts) - - if err != nil { - return &ReactorManager{}, err - } - - r := &ReactorManager{ - Manager: m, - Client: cl, - Config: config, - Err: errCh, - } - - return r, err -} - -// Start logs the start and calls start on the embedded manager. -func (r *ReactorManager) Start() error { - logging.Debug(logging.DStart, "RMA %v starting", r.Id) - return r.Manager.Start() -} - -// Stop logs the stop and calls stop on the embedded manager. -func (r *ReactorManager) Stop() error { - logging.Debug(logging.DExit, "RMA %v stopping", r.Id) - return r.Manager.Stop() -} - -// UpdateClient is used to change the underlying manager client if there -// changes to its data. -// -// BUG(Keegan): Client is not protected by a lock and may lead to races -func (r *ReactorManager) UpdateClient(cl *Client) error { - logging.Debug(logging.DClient, "RMA %v updating client", r.Id) - r.Client = cl - return nil -} - -// ReactorDeviceHandler processes incoming device information and -// updates the manager accordingly. -func (r *ReactorManager) ReactorDeviceHandler(devs []*pb.Device) error { - - logging.Debug(logging.DClient, "RMA %v recieved ping", r.Id) - - for _, dev := range devs { - logging.Debug( - logging.DClient, - "RMA %v device %v is %v", - r.Id, - dev.GetAddr(), - dev.GetStatus().String(), - ) - } - - return nil -} diff --git a/internal/pkg/server/reactormanager_test.go b/internal/pkg/server/reactormanager_test.go deleted file mode 100644 index 9f3b321..0000000 --- a/internal/pkg/server/reactormanager_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package server - -import ( - pb "FRMS/internal/pkg/grpc" - "math/rand" - "testing" - - "github.com/spf13/viper" - "github.com/stretchr/testify/assert" -) - -// dummyClient creates a dummy client for testing. -func dummyClient() *Client { - return &Client{ - Id: rand.Int(), - Model: "dummy", - Type: "dummy", - } -} - -func dummyDevices() []*pb.Device { - numDevs := 10 - - devs := make([]*pb.Device, numDevs) - - for i := 0; i < numDevs; i++ { - dev := &pb.Device{ - Addr: int32(rand.Intn(255)), - Status: pb.Status(rand.Intn(2)), - } - devs = append(devs, dev) - } - - return devs -} - -// dummyReactorManager creates a dummy reactor manager for testing. -func dummyReactorManager() (*ReactorManager, error) { - - ch := make(chan error) - cl := dummyClient() - return NewReactorManager(cl, viper.New(), ch) -} - -// TestNewReactorManager tries to create a new reactor manager. -func TestNewReactorManager(t *testing.T) { - assert := assert.New(t) - _, err := dummyReactorManager() - assert.Equal(err, nil, "failed to create reactor manager") -} - -// TestReactorManager tries to start/stop reactor manager -func TestReactorManager(t *testing.T) { - assert := assert.New(t) - rm, err := dummyReactorManager() - assert.Equal(err, nil, "failed to create reactor manager") - - cycles := 10 - for i := 0; i < cycles; i++ { - assert.NoError(rm.Start(), "failed to start") - assert.NoError(rm.Stop(), "failed to start") - } -} - -// TestUpdateClient tries to update a reactor managers embedded client. -func TestUpdateClient(t *testing.T) { - - assert := assert.New(t) - rm, err := dummyReactorManager() - assert.Equal(err, nil, "failed to create reactor manager") - - cl := dummyClient() - - assert.NoError(rm.UpdateClient(cl), "failed to update client") -} - -// TestReactorDeviceHandler ensures that a list of devices can be processed. -func TestReactorDeviceHandler(t *testing.T) { - assert := assert.New(t) - rm, err := dummyReactorManager() - assert.Equal(err, nil, "failed to create reactor manager") - - devs := dummyDevices() - assert.NoError(rm.ReactorDeviceHandler(devs), "failed to handle devices") -} diff --git a/internal/pkg/server/register.go b/internal/pkg/server/register.go new file mode 100644 index 0000000..7be0111 --- /dev/null +++ b/internal/pkg/server/register.go @@ -0,0 +1,19 @@ +package server + +import ( + pb "FRMS/internal/pkg/grpc" + "FRMS/internal/pkg/logging" +) + +func (c *Coordinator) Register() error { + // register services + pb.RegisterHandshakeServer(c.grpcServer, c) + + go c.grpcServer.Serve(c.listener) + // testing + pb.RegisterMonitoringServer(c.grpcServer, c) + + logging.Debug(logging.DStart, "CCO 00 registered grpc") + + return nil +}