merging rust

main
spinach 1 year ago
commit c94afb1d9e

@ -1,4 +0,0 @@
INFLUXDB_USERNAME=admin
INFLUXDB_PASSWORD=forelight
INFLUXDB_ORG=ForeLight
INFLUXDB_BUCKET=test

@ -46,11 +46,13 @@ create_build() {
# create build for $1
case $1 in
'rpi' )
echo "NOT IMPL">&2 && exit 1
printf 'Building for Raspberry Pi!\n'
GARCH="arm64"
PLATFORM="reactor"
;;
'bb')
echo "NOT IMPL">&2 && exit 1
printf 'Building for BeagleBone!\n'
GARCH="arm"
GARM="GOARM=7"
@ -74,11 +76,14 @@ create_build() {
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"
( cd server; env GOOS=linux GOARCH="$GARCH" $GARM go build -o "$OUTFILE")
mv server/"$OUTFILE" bin/"$OUTFILE"
echo "Finished"
if [[ "$SCP"=true ]] ; then
# scp
if [[ -n "$SCP" ]] ; then
printf 'Attempting to transfer to %s\n' "$2"
if [[ "$1" == "bb" ]] ; then
printf 'Copying to %s\n' "192.168.100.90"
@ -142,7 +147,6 @@ for dev in "$@"; do
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

@ -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

7
reactor/Cargo.lock generated

@ -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
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);
}
}

@ -3,9 +3,8 @@ 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
google.golang.org/grpc v1.47.0
google.golang.org/protobuf v1.28.0
@ -14,19 +13,14 @@ require (
require (
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/spf13/afero v1.8.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
@ -34,7 +28,6 @@ require (
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
google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd // indirect
gopkg.in/ini.v1 v1.66.4 // indirect

@ -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=
@ -370,7 +358,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=
@ -380,10 +367,6 @@ golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b h1:2n253B2r0pYSmEV+UNCQoPfU/
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/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,7 +374,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.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/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=

@ -13,6 +13,14 @@ type reactorCoordinator struct {
id string
}
func (r *reactorCoordinator) Start() error {
return errors.New("todo")
}
func (r *reactorCoordinator) Ping() error {
return errors.New("todo")
}
func StartReactor(id string) ReactorCoordinator {
return &reactorCoordinator{id:id}
}

@ -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
}

@ -17,6 +17,7 @@ TOPICS = {
"PING": "#d0b343",
"SCAN": "#70c43f",
"SPWN": "#4878bc",
"STOP": "#ffff00",
#"LOG2": "#398280",
#"CMIT": "#98719f",
#"PERS": "#d08341",

@ -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...)
}
}

@ -3,7 +3,6 @@ package server
import (
pb "FRMS/internal/pkg/grpc"
"FRMS/internal/pkg/influxdb"
_ "FRMS/internal/pkg/influxdb"
"FRMS/internal/pkg/logging"
"context"
"errors"

@ -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
// creates websocket server and upgrades incoming connections

@ -22,8 +22,8 @@ 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 NewConfig(filename string) (*viper.Viper, error) {
return config.LoadConfig(filename)
}
type ws interface {
@ -40,29 +40,38 @@ func main() {
signal.Notify(gracefulShutdown, syscall.SIGINT, syscall.SIGTERM)
// config file
conf := NewConfig("server")
conf, err := NewConfig("server")
if err != nil {
panic(err)
}
errCh := make(chan error)
c := NewCoordinator(conf, errCh)
go c.Start()
logging.Debug(logging.DStart, "CCO 01 Server %s started", conf.Get("name"))
// starting websocket server
fmt.Printf("Coordiantor started\n")
logging.Debug(logging.DStart, "CCO %s started", conf.Get("name"))
// starting websocket server
// gated behind env for testing
if conf.Get("WEBSOCKET") == "start" {
w := NewWebSocket()
go w.Start()
fmt.Printf("Websocket started\n")
logging.Debug(logging.DStart, "WS websocket started")
}
select {
case err := <-errCh: // blocking to wait for any errors and keep alive otherwise
case err := <-errCh: // allows passing errors up for handling
panic(err)
case <-gracefulShutdown:
// Shutdown via INT
// storing config
fmt.Printf("\nStoring config to %s\n", conf.ConfigFileUsed())
fmt.Printf("\nExiting...\n")
logging.Debug(logging.DStop, "CON storing to %s", conf.ConfigFileUsed())
if err := conf.WriteConfig(); err != nil {
panic(err)
}
fmt.Println("Stored config successfully. Exiting...")
logging.Debug(logging.DStop, "CON stored successfully", conf.ConfigFileUsed())
os.Exit(0)
}
}
Loading…
Cancel
Save