From 72b0a690a66ece2627db81a702ba443e390bcb3c Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Wed, 13 Jul 2022 19:01:09 +0200 Subject: [PATCH] Add gateway and daemon boilerplate --- cmd/daemon/main.go | 93 ++++++++++++++++++++++++++++++++++++++ cmd/gateway/main.go | 46 +++++++++++++++++++ gateway/handler/handler.go | 21 +++++++++ gateway/store/store.go | 18 ++++++++ go.mod | 2 + go.sum | 9 ++++ 6 files changed, 189 insertions(+) create mode 100644 cmd/daemon/main.go create mode 100644 cmd/gateway/main.go create mode 100644 gateway/handler/handler.go create mode 100644 gateway/store/store.go diff --git a/cmd/daemon/main.go b/cmd/daemon/main.go new file mode 100644 index 0000000..2d50bca --- /dev/null +++ b/cmd/daemon/main.go @@ -0,0 +1,93 @@ +package main + +import ( + "bytes" + "context" + "encoding/json" + "flag" + "log" + "net" + "net/http" + "os" + "time" + + "git.netflux.io/rob/solar-toolkit/inverter" +) + +const ( + httpUserAgent = "solar-toolkit (git.netflux.io)" + httpTimeout = time.Second * 5 +) + +func main() { + var ( + inverterAddr string + gatewayEndpoint string + gatewayUsername string + gatewayPassword string + pollInterval time.Duration + ) + + flag.StringVar(&inverterAddr, "inverter-addr", "", "IP+port of solar inverter") + flag.StringVar(&gatewayEndpoint, "endpoint", "", "URL to post metrics to") + flag.StringVar(&gatewayUsername, "username", "", "HTTP basic auth username") + flag.StringVar(&gatewayPassword, "password", "", "HTTP basic auth password") + flag.DurationVar(&pollInterval, "pollInterval", time.Minute, "Poll interval, example: 60s") + flag.Parse() + + if gatewayEndpoint == "" { + flag.Usage() + os.Exit(1) + } + + conn, err := net.Dial("udp", inverterAddr) + if err != nil { + log.Fatalf("error dialing: %s", err) + } + defer conn.Close() + + var inv inverter.ET + + ticker := time.NewTicker(pollInterval) + defer ticker.Stop() + + client := http.Client{Timeout: httpTimeout} + + for ; true; <-ticker.C { + runtimeData, err := inv.RuntimeData(context.Background(), conn) + if err != nil { + log.Printf("error fetching runtime data: %s", err) + continue + } + + reqBody, err := json.Marshal(runtimeData) + if err != nil { + log.Printf("error encoding runtime data: %s", err) + continue + } + + req, err := http.NewRequest(http.MethodPost, gatewayEndpoint, bytes.NewReader(reqBody)) + if err != nil { + log.Printf("error building request: %s", err) + continue + } + + req.Header.Set("content-type", "application/json") + req.Header.Set("user-agent", httpUserAgent) + if gatewayUsername != "" && gatewayPassword != "" { + req.SetBasicAuth(gatewayUsername, gatewayPassword) + } + + resp, err := client.Do(req) + if err != nil { + log.Printf("error sending request: %s", err) + continue + } + if resp.StatusCode != http.StatusOK { + log.Printf("unexpected HTTP response code: %d", resp.StatusCode) + continue + } + + log.Printf("Sent: %+v", runtimeData) + } +} diff --git a/cmd/gateway/main.go b/cmd/gateway/main.go new file mode 100644 index 0000000..f5f9773 --- /dev/null +++ b/cmd/gateway/main.go @@ -0,0 +1,46 @@ +package main + +import ( + "log" + "net/http" + "os" + "time" + + "git.netflux.io/rob/solar-toolkit/gateway/handler" + "git.netflux.io/rob/solar-toolkit/gateway/store" + "github.com/jmoiron/sqlx" + _ "github.com/lib/pq" +) + +const defaultBindAddr = ":8888" + +func main() { + databaseURL := os.Getenv("DATABASE_URL") + if databaseURL == "" { + log.Fatal("missing configuration DATABASE_URL") + } + + bindAddr := os.Getenv("BIND_ADDR") + if bindAddr == "" { + bindAddr = defaultBindAddr + } + + db, err := sqlx.Connect("postgres", databaseURL) + if err != nil { + log.Fatalf("could not connect to database: %s", err) + } + + store := store.NewSQL(db) + handler := handler.NewHandler(store) + srv := http.Server{ + ReadTimeout: time.Second * 3, + WriteTimeout: time.Second * 3, + Handler: handler, + Addr: bindAddr, + } + + log.Printf("Listening on %s...", bindAddr) + if err := srv.ListenAndServe(); err != nil { + log.Fatal(err) + } +} diff --git a/gateway/handler/handler.go b/gateway/handler/handler.go new file mode 100644 index 0000000..122ed91 --- /dev/null +++ b/gateway/handler/handler.go @@ -0,0 +1,21 @@ +package handler + +import ( + "net/http" + + "git.netflux.io/rob/solar-toolkit/inverter" +) + +type Store interface { + InsertETRuntimeData(*inverter.ETRuntimeData) error +} + +type Handler struct { + store Store +} + +func NewHandler(store Store) *Handler { return &Handler{store: store} } + +func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + +} diff --git a/gateway/store/store.go b/gateway/store/store.go new file mode 100644 index 0000000..4209d09 --- /dev/null +++ b/gateway/store/store.go @@ -0,0 +1,18 @@ +package store + +import ( + "git.netflux.io/rob/solar-toolkit/inverter" + "github.com/jmoiron/sqlx" +) + +type PostgresStore struct { + db *sqlx.DB +} + +func NewSQL(db *sqlx.DB) *PostgresStore { + return &PostgresStore{db: db} +} + +func (s *PostgresStore) InsertETRuntimeData(runtimeData *inverter.ETRuntimeData) error { + return nil +} diff --git a/go.mod b/go.mod index 840e9ea..3262394 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,8 @@ module git.netflux.io/rob/solar-toolkit go 1.18 require ( + github.com/jmoiron/sqlx v1.3.5 + github.com/lib/pq v1.10.6 github.com/stretchr/testify v1.8.0 golang.org/x/exp v0.0.0-20220706164943-b4a6d9510983 ) diff --git a/go.sum b/go.sum index c490715..bb1f509 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,15 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= +github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs= +github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= 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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=