merging rust
commit
c94afb1d9e
@ -1,4 +0,0 @@
|
|||||||
INFLUXDB_USERNAME=admin
|
|
||||||
INFLUXDB_PASSWORD=forelight
|
|
||||||
INFLUXDB_ORG=ForeLight
|
|
||||||
INFLUXDB_BUCKET=test
|
|
@ -1,57 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"FRMS/internal/pkg/config"
|
|
||||||
"FRMS/internal/pkg/logging"
|
|
||||||
"FRMS/internal/pkg/reactor"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"os/signal"
|
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
type reactorCoordinator interface {
|
|
||||||
Start()
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewReactorCoordinator(config *viper.Viper, ch chan error) reactorCoordinator {
|
|
||||||
// allows interface checking as opposed to calling directly
|
|
||||||
return reactor.NewCoordinator(config, ch)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewConfig(fname string) *viper.Viper {
|
|
||||||
return config.LoadConfig(fname)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// shutdown
|
|
||||||
gracefulShutdown := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(gracefulShutdown, syscall.SIGINT, syscall.SIGTERM)
|
|
||||||
|
|
||||||
// load any stored configs
|
|
||||||
conf := NewConfig("reactor")
|
|
||||||
|
|
||||||
ch := make(chan error)
|
|
||||||
rlc := NewReactorCoordinator(conf, ch) // passing conf and err
|
|
||||||
go rlc.Start()
|
|
||||||
logging.Debug(logging.DStart, "Reactor Started")
|
|
||||||
|
|
||||||
// check for errors
|
|
||||||
select {
|
|
||||||
case err := <-ch:
|
|
||||||
if err != nil {
|
|
||||||
conf.WriteConfig() // save changes
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
case <-gracefulShutdown:
|
|
||||||
// sigint
|
|
||||||
fmt.Printf("\nStoring config to %s\n", conf.ConfigFileUsed())
|
|
||||||
if err := conf.WriteConfig(); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import "errors"
|
|
||||||
|
|
||||||
type ReactorCoordinator interface {
|
|
||||||
Start ()
|
|
||||||
}
|
|
||||||
|
|
||||||
type reactorCoordinator struct {
|
|
||||||
|
|
||||||
|
|
||||||
func StartReactor
|
|
@ -1,3 +0,0 @@
|
|||||||
orginization: ForeLight
|
|
||||||
token: Zrtg0Q9u65HbFaK4KPWbl9y1xofJwsRHVwuWcIq3xvSOstVbjshDoRNjPiwsz31vIoP-GwDuGL8gzouEHqMuYg==
|
|
||||||
url: http://localhost:8086
|
|
@ -1,5 +0,0 @@
|
|||||||
INFLUXDB_USERNAME=admin
|
|
||||||
INFLUXDB_PASSWORD=admin
|
|
||||||
INFLUXDB_ORG=ForeLight
|
|
||||||
INFLUXDB_BUCKET=default
|
|
||||||
|
|
@ -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
|
|
@ -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
|
|
@ -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?
|
|
||||||
-
|
|
File diff suppressed because it is too large
Load Diff
@ -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??
|
|
@ -1,4 +0,0 @@
|
|||||||
## Weekly Planning
|
|
||||||
|
|
||||||
[Jan 16-20](weekly/Jan-16-20.md)
|
|
||||||
[Jan 23-27](weekly/Jan-23-27.md)
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
||||||
}
|
|
@ -1,84 +0,0 @@
|
|||||||
package logging
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"errors"
|
|
||||||
"time"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getLogType() string {
|
|
||||||
if t, ok := os.LookupEnv("LOGTYPE"); ok {
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
return "DEFAULT"
|
|
||||||
}
|
|
||||||
|
|
||||||
func getVerbosity() int {
|
|
||||||
v := os.Getenv("VERBOSE")
|
|
||||||
level := 0
|
|
||||||
if v != "" {
|
|
||||||
var err error
|
|
||||||
level, err = strconv.Atoi(v)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Invalid Verbosity %v", v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return level
|
|
||||||
}
|
|
||||||
|
|
||||||
type logTopic string
|
|
||||||
const (
|
|
||||||
// define 4 character topic abbreviations for coloring
|
|
||||||
DError logTopic = "ERRO"
|
|
||||||
DClient logTopic = "CLNT"
|
|
||||||
DStart logTopic = "STRT"
|
|
||||||
DExit logTopic = "EXIT"
|
|
||||||
DPing logTopic = "PING"
|
|
||||||
DScan logTopic = "SCAN"
|
|
||||||
DSpawn logTopic = "SPWN"
|
|
||||||
)
|
|
||||||
// the list can grow
|
|
||||||
|
|
||||||
var debugStart time.Time
|
|
||||||
var debugVerbosity int
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
|
|
||||||
debugVerbosity = getVerbosity()
|
|
||||||
debugStart = time.Now()
|
|
||||||
if debugVerbosity > 0 {
|
|
||||||
path := "log/"
|
|
||||||
if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) {
|
|
||||||
err := os.Mkdir(path, os.ModePerm)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logtype := getLogType() // start with "REACTOR" etc
|
|
||||||
timestamp := time.Now().Format("Mon-15:04:05")
|
|
||||||
filename := fmt.Sprintf("%s-%s.log", logtype, timestamp)
|
|
||||||
f, err := os.OpenFile(path+filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0664)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
log.SetOutput(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.SetFlags(log.Flags() &^ (log.Ldate | log.Ltime)) // turns off date and time so we can set manually
|
|
||||||
}
|
|
||||||
|
|
||||||
// example call Debug(dClient, "R%d connecting to client %d", r.Id, c.Id)
|
|
||||||
func Debug(topic logTopic, format string, a ...interface{}) {
|
|
||||||
if debugVerbosity >= 1 {
|
|
||||||
time := time.Since(debugStart).Microseconds()
|
|
||||||
time /= 100
|
|
||||||
prefix := fmt.Sprintf("%06d %v ", time, string(topic))
|
|
||||||
format = prefix + format
|
|
||||||
log.Printf(format, a...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
|||||||
// implemenets a reactor object with websocket methods
|
|
||||||
package websocket
|
|
@ -0,0 +1,7 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "reactor"
|
||||||
|
version = "0.1.0"
|
@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "reactor"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
@ -1,4 +1,4 @@
|
|||||||
// package system uses linux commands to get hardware info from devices
|
// package system uses linux commands to get hardware info for identifation
|
||||||
package system
|
package system
|
||||||
|
|
||||||
import (
|
import (
|
@ -0,0 +1,22 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
pub struct Device {
|
||||||
|
address: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Device {
|
||||||
|
fn new(address: i32) -> Device {
|
||||||
|
return Device {address: address};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Device {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(fmt, "{}", self.address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// outward factory function
|
||||||
|
pub fn create_device(address: i32) -> Device {
|
||||||
|
return Device::new(address);
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
/// get_connected returns an array of Device objects.
|
||||||
|
pub fn get_connected() -> Vec<i32>{
|
||||||
|
let mut devices: Vec<i32> = Vec::new();
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_i2c_command() -> {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
mod i2c;
|
||||||
|
mod device;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let addresses = i2c::get_connected();
|
||||||
|
let mut devices: Vec<device::Device> = Vec::new();
|
||||||
|
for address in addresses.iter() {
|
||||||
|
let device: device::Device = device::create_device(*address);
|
||||||
|
println!("Device: {}", device);
|
||||||
|
devices.push(device);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
// Package config provides an interface to load and store config files
|
||||||
|
// using the XDG standard ($HOME/.config/FRMS) as the base directory
|
||||||
|
//
|
||||||
|
// WARNING: only built for Linux
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"FRMS/internal/pkg/logging"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LoadConfig takes a filename as an aruguement and returns a *viper.Viper object.
|
||||||
|
// Loads and stores config files into the base directory according to XDG standard ($HOME/.config/FRMS/).
|
||||||
|
// Will create directory and config if they don't exist.
|
||||||
|
func LoadConfig(filename string) (*viper.Viper, error) {
|
||||||
|
|
||||||
|
config := viper.New()
|
||||||
|
|
||||||
|
// default config dir
|
||||||
|
path := fmt.Sprintf("%s/.config/FRMS", os.Getenv("HOME"))
|
||||||
|
filetype := "yaml"
|
||||||
|
|
||||||
|
// checking for existence
|
||||||
|
if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) {
|
||||||
|
if err := os.Mkdir(path, os.ModePerm); err != nil {
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logging.Debug(logging.DStart, "Loading config (%s)", filename)
|
||||||
|
|
||||||
|
// setting config file info
|
||||||
|
config.SetConfigName(filename)
|
||||||
|
config.SetConfigType(filetype)
|
||||||
|
config.AddConfigPath(path)
|
||||||
|
|
||||||
|
config.AutomaticEnv()
|
||||||
|
|
||||||
|
// reading in config
|
||||||
|
if err := config.ReadInConfig(); err != nil {
|
||||||
|
|
||||||
|
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
||||||
|
|
||||||
|
fullpath := fmt.Sprintf("%s/%s.%s", path, filename, filetype)
|
||||||
|
logging.Debug(logging.DStart, "CON config does not exist!")
|
||||||
|
if err = config.WriteConfigAs(fullpath); err != nil {
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
logging.Debug(logging.DStart, "CON created at %s", fullpath)
|
||||||
|
} else {
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logging.Debug(logging.DStart, "CON Loaded configs from %v", config.ConfigFileUsed())
|
||||||
|
|
||||||
|
return config, nil
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
package logging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getLogType() string {
|
||||||
|
if t, ok := os.LookupEnv("LOGTYPE"); ok {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
return "DEFAULT"
|
||||||
|
}
|
||||||
|
|
||||||
|
func getVerbosity() int {
|
||||||
|
v := os.Getenv("VERBOSE")
|
||||||
|
level := 0
|
||||||
|
if v != "" {
|
||||||
|
var err error
|
||||||
|
level, err = strconv.Atoi(v)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Invalid Verbosity %v", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return level
|
||||||
|
}
|
||||||
|
|
||||||
|
type logTopic string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// define 4 character topic abbreviations for coloring
|
||||||
|
DError logTopic = "ERRO"
|
||||||
|
DClient logTopic = "CLNT"
|
||||||
|
DStart logTopic = "STRT"
|
||||||
|
DExit logTopic = "EXIT"
|
||||||
|
DPing logTopic = "PING"
|
||||||
|
DScan logTopic = "SCAN"
|
||||||
|
DSpawn logTopic = "SPWN"
|
||||||
|
DStop logTopic = "STOP"
|
||||||
|
)
|
||||||
|
|
||||||
|
// the list can grow
|
||||||
|
|
||||||
|
var debugStart time.Time
|
||||||
|
var debugVerbosity int
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
debugVerbosity = getVerbosity()
|
||||||
|
debugStart = time.Now()
|
||||||
|
if debugVerbosity > 0 {
|
||||||
|
path := "log/"
|
||||||
|
if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) {
|
||||||
|
err := os.Mkdir(path, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logtype := getLogType() // start with "REACTOR" etc
|
||||||
|
timestamp := time.Now().Format("Mon-15:04:05")
|
||||||
|
filename := fmt.Sprintf("%s-%s.log", logtype, timestamp)
|
||||||
|
f, err := os.OpenFile(path+filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0664)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.SetOutput(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.SetFlags(log.Flags() &^ (log.Ldate | log.Ltime)) // turns off date and time so we can set manually
|
||||||
|
}
|
||||||
|
|
||||||
|
// example call Debug(dClient, "R%d connecting to client %d", r.Id, c.Id)
|
||||||
|
func Debug(topic logTopic, format string, a ...interface{}) {
|
||||||
|
if debugVerbosity >= 1 {
|
||||||
|
time := time.Since(debugStart).Microseconds()
|
||||||
|
time /= 100
|
||||||
|
prefix := fmt.Sprintf("%06d %v ", time, string(topic))
|
||||||
|
format = prefix + format
|
||||||
|
log.Printf(format, a...)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,118 @@
|
|||||||
|
// package system uses linux commands to get hardware info for identifation
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
ip := strings.Trim(out.String(), " \n")
|
||||||
|
return ip, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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[b]; !ok {
|
||||||
|
return 0, errors.New(fmt.Sprintf("No bus for dev %s", b))
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns correct bus
|
||||||
|
return bus, 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
|
||||||
|
}
|
||||||
|
b := out.String()
|
||||||
|
b = strings.Trim(b, " \n")
|
||||||
|
return b, 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
|
||||||
|
|
||||||
|
ipcmd := "ifconfig eth0 | awk '/inet / {print $2}'"
|
||||||
|
maccmd := "ifconfig eth0 | awk '/ether / {print $2}'"
|
||||||
|
devcmd := "lshw -C system 2>/dev/null | head -n 1"
|
||||||
|
|
||||||
|
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]
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// formatting
|
||||||
|
ip := res[0].String()
|
||||||
|
ip = strings.Trim(ip, " \n")
|
||||||
|
|
||||||
|
hash := fnv.New32a()
|
||||||
|
hash.Write(res[1].Bytes())
|
||||||
|
|
||||||
|
b := res[2].String()
|
||||||
|
b = strings.Trim(b, " \n")
|
||||||
|
return nil
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
// Package websocket sets up websocket connections with clients and allows live reactor readouts.
|
||||||
package websocket
|
package websocket
|
||||||
|
|
||||||
// creates websocket server and upgrades incoming connections
|
// creates websocket server and upgrades incoming connections
|
Loading…
Reference in New Issue