Compare commits
No commits in common. "a8e7d407fe23fac4093d2e729225e9dfdda23442" and "c95593bb8628f8b87db5888d354cff4b4372a846" have entirely different histories.
a8e7d407fe
...
c95593bb86
|
@ -15,6 +15,8 @@ var subCmds = []*cli.Command{
|
||||||
xbeeCmd,
|
xbeeCmd,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var f os.File
|
||||||
|
|
||||||
func Execute() {
|
func Execute() {
|
||||||
app := &cli.App{
|
app := &cli.App{
|
||||||
Name: "gotelem",
|
Name: "gotelem",
|
||||||
|
|
|
@ -4,9 +4,11 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
|
||||||
|
@ -132,6 +134,108 @@ func serve(cCtx *cli.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type rpcService struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rpcService) Status() {
|
||||||
|
}
|
||||||
|
func (r *rpcService) String() string {
|
||||||
|
return "rpcService"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rpcService) Start(ctx *cli.Context, deps svcDeps) error {
|
||||||
|
logger := deps.Logger
|
||||||
|
broker := deps.Broker
|
||||||
|
// TODO: extract port/ip from cli context.
|
||||||
|
ln, err := net.Listen("tcp", "0.0.0.0:8082")
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn("error listening", "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
conn, err := ln.Accept()
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn("error accepting connection", "err", err)
|
||||||
|
}
|
||||||
|
go handleCon(conn, broker, logger.With("addr", conn.RemoteAddr()), ctx.Done())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleCon(conn net.Conn, broker *gotelem.Broker, l *slog.Logger, done <-chan struct{}) {
|
||||||
|
|
||||||
|
subname := fmt.Sprint("tcp", conn.RemoteAddr().String())
|
||||||
|
|
||||||
|
l.Info("started handling", "name", subname)
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
rxCh, err := broker.Subscribe(subname)
|
||||||
|
if err != nil {
|
||||||
|
l.Error("error subscribing to connection", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer broker.Unsubscribe(subname)
|
||||||
|
|
||||||
|
jEncode := json.NewEncoder(conn)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case msg := <-rxCh:
|
||||||
|
l.Info("got packet")
|
||||||
|
// FIXME: poorly optimized
|
||||||
|
err := jEncode.Encode(msg)
|
||||||
|
if err != nil {
|
||||||
|
l.Warn("error encoding json", "err", err)
|
||||||
|
}
|
||||||
|
case <-done:
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this spins up a new can socket on vcan0 and broadcasts a packet every second. for testing.
|
||||||
|
|
||||||
|
type canLoggerService struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *canLoggerService) String() string {
|
||||||
|
return "CanLoggerService"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *canLoggerService) Status() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *canLoggerService) Start(cCtx *cli.Context, deps svcDeps) (err error) {
|
||||||
|
broker := deps.Broker
|
||||||
|
l := deps.Logger
|
||||||
|
rxCh, err := broker.Subscribe("canDump")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t := time.Now()
|
||||||
|
fname := fmt.Sprintf("candump_%d-%02d-%02dT%02d.%02d.%02d.txt",
|
||||||
|
t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second())
|
||||||
|
|
||||||
|
l.Info("logging to file", "filename", fname)
|
||||||
|
|
||||||
|
f, err := os.Create(fname)
|
||||||
|
if err != nil {
|
||||||
|
l.Error("error opening file", "filename", fname, "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
enc := json.NewEncoder(f)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case msg := <-rxCh:
|
||||||
|
|
||||||
|
enc.Encode(msg)
|
||||||
|
|
||||||
|
case <-cCtx.Done():
|
||||||
|
f.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// xBeeService provides data over an Xbee device, either by serial or TCP
|
// xBeeService provides data over an Xbee device, either by serial or TCP
|
||||||
// based on the url provided in the xbee flag. see the description for details.
|
// based on the url provided in the xbee flag. see the description for details.
|
||||||
|
|
3
go.mod
3
go.mod
|
@ -1,6 +1,6 @@
|
||||||
module github.com/kschamplin/gotelem
|
module github.com/kschamplin/gotelem
|
||||||
|
|
||||||
go 1.22
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/go-chi/chi/v5 v5.0.12
|
github.com/go-chi/chi/v5 v5.0.12
|
||||||
|
@ -9,6 +9,7 @@ require (
|
||||||
github.com/mattn/go-sqlite3 v1.14.22
|
github.com/mattn/go-sqlite3 v1.14.22
|
||||||
github.com/urfave/cli/v2 v2.25.1
|
github.com/urfave/cli/v2 v2.25.1
|
||||||
go.bug.st/serial v1.5.0
|
go.bug.st/serial v1.5.0
|
||||||
|
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a
|
||||||
golang.org/x/sync v0.1.0
|
golang.org/x/sync v0.1.0
|
||||||
golang.org/x/sys v0.7.0
|
golang.org/x/sys v0.7.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -33,6 +33,8 @@ github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRT
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||||
go.bug.st/serial v1.5.0 h1:ThuUkHpOEmCVXxGEfpoExjQCS2WBVV4ZcUKVYInM9T4=
|
go.bug.st/serial v1.5.0 h1:ThuUkHpOEmCVXxGEfpoExjQCS2WBVV4ZcUKVYInM9T4=
|
||||||
go.bug.st/serial v1.5.0/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE=
|
go.bug.st/serial v1.5.0/go.mod h1:UABfsluHAiaNI+La2iESysd9Vetq7VRdpxvjx7CmmOE=
|
||||||
|
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE=
|
||||||
|
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
|
||||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||||
|
|
11
http.go
11
http.go
|
@ -19,6 +19,10 @@ import (
|
||||||
"nhooyr.io/websocket/wsjson"
|
"nhooyr.io/websocket/wsjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type slogHttpLogger struct {
|
||||||
|
slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
func TelemRouter(log *slog.Logger, broker *Broker, db *db.TelemDb) http.Handler {
|
func TelemRouter(log *slog.Logger, broker *Broker, db *db.TelemDb) http.Handler {
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
|
|
||||||
|
@ -161,8 +165,7 @@ func apiV1GetValues(db *db.TelemDb) http.HandlerFunc {
|
||||||
if startString != "" {
|
if startString != "" {
|
||||||
start, err = time.Parse(time.RFC3339, startString)
|
start, err = time.Parse(time.RFC3339, startString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// error out
|
|
||||||
panic("hi")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end := time.Now().Add(1 * time.Hour)
|
end := time.Now().Add(1 * time.Hour)
|
||||||
|
@ -170,7 +173,6 @@ func apiV1GetValues(db *db.TelemDb) http.HandlerFunc {
|
||||||
if endParam != "" {
|
if endParam != "" {
|
||||||
end, err = time.Parse(time.RFC3339, endParam)
|
end, err = time.Parse(time.RFC3339, endParam)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("hi")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
name := chi.URLParam(r, "name")
|
name := chi.URLParam(r, "name")
|
||||||
|
@ -184,9 +186,6 @@ func apiV1GetValues(db *db.TelemDb) http.HandlerFunc {
|
||||||
fmt.Print(err)
|
fmt.Print(err)
|
||||||
}
|
}
|
||||||
b, err := json.Marshal(res)
|
b, err := json.Marshal(res)
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
w.Write(b)
|
w.Write(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ package db
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -11,9 +12,14 @@ import (
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"github.com/kschamplin/gotelem/skylab"
|
"github.com/kschamplin/gotelem/skylab"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
sqlite3 "github.com/mattn/go-sqlite3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
sql.Register("custom_sqlite3", &sqlite3.SQLiteDriver{
|
||||||
|
// TODO: add helper that convert between unix milliseconds and sqlite times?
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
type TelemDb struct {
|
type TelemDb struct {
|
||||||
db *sqlx.DB
|
db *sqlx.DB
|
||||||
|
|
|
@ -23,7 +23,7 @@ func (e *FormatError) Error() string {
|
||||||
if e.err != nil {
|
if e.err != nil {
|
||||||
return fmt.Sprintf("%s:%s", e.msg, e.err.Error())
|
return fmt.Sprintf("%s:%s", e.msg, e.err.Error())
|
||||||
}
|
}
|
||||||
return e.msg
|
return fmt.Sprintf("%s", e.msg)
|
||||||
|
|
||||||
}
|
}
|
||||||
func (e *FormatError) Unwrap() error {
|
func (e *FormatError) Unwrap() error {
|
||||||
|
|
3
internal/middleware/recoverer2.go
Normal file
3
internal/middleware/recoverer2.go
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
// Recoverer2 is a reimplementation of recoverer but using slog as the backend.
|
72
internal/middleware/slogger.go
Normal file
72
internal/middleware/slogger.go
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
chi_middleware "github.com/go-chi/chi/v5/middleware"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Slogger is a slog-enabled logging middleware.
|
||||||
|
// It logs the start and end of the request, and logs info
|
||||||
|
// about the request itself, response status, and response time.
|
||||||
|
|
||||||
|
// Slogger returns a log handler that uses the given slog logger as the base.
|
||||||
|
func Slogger(sl *slog.Logger) func(next http.Handler) http.Handler {
|
||||||
|
|
||||||
|
logger := sl.WithGroup("http")
|
||||||
|
return func(next http.Handler) http.Handler {
|
||||||
|
|
||||||
|
// this triple-nested function is strange, but basically the Slogger() call makes a new middleware function (above)
|
||||||
|
// the middleware function returns a handler that calls the next handler in the chain(wrapping it)
|
||||||
|
|
||||||
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// wrap writer allows us to get info on the response from further handlers.
|
||||||
|
ww := chi_middleware.NewWrapResponseWriter(w, r.ProtoMajor)
|
||||||
|
t1 := time.Now()
|
||||||
|
// attrs is stored to allow for the helpers to add additional elements to the main record.
|
||||||
|
attrs := make([]slog.Attr, 0)
|
||||||
|
|
||||||
|
// This function runs at the end and adds all the response details to the attrs before logging them.
|
||||||
|
defer func() {
|
||||||
|
attrs = append(attrs, slog.Int("status_code", ww.Status()))
|
||||||
|
attrs = append(attrs, slog.Int("resp_size", ww.BytesWritten()))
|
||||||
|
attrs = append(attrs, slog.Duration("duration", time.Since(t1)))
|
||||||
|
attrs = append(attrs, slog.String("method", r.Method))
|
||||||
|
logger.LogAttrs(r.Context(), slog.LevelInfo, r.RequestURI, attrs...)
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
// embed the logger and the attrs for later items in the chain.
|
||||||
|
ctx := context.WithValue(r.Context(), SloggerAttrsKey, attrs)
|
||||||
|
ctx = context.WithValue(ctx, SloggerLogKey, logger)
|
||||||
|
// push it to the request and serve the next handler
|
||||||
|
r = r.WithContext(ctx)
|
||||||
|
next.ServeHTTP(ww, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.HandlerFunc(fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type slogKeyType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
SloggerLogKey slogKeyType = iota
|
||||||
|
SloggerAttrsKey
|
||||||
|
)
|
||||||
|
|
||||||
|
func AddSlogAttr(r *http.Request, attr slog.Attr) {
|
||||||
|
ctx := r.Context()
|
||||||
|
attrs, ok := ctx.Value(SloggerAttrsKey).([]slog.Attr)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
attrs = append(attrs, attr)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: write rest of functions
|
|
@ -1,4 +1,4 @@
|
||||||
// generated by gen_skylab.go at 2024-02-28 14:10:10.252960174 -0600 CST m=+0.003082874 DO NOT EDIT!
|
// generated by gen_skylab.go at 2024-02-27 19:26:47.373116343 -0600 CST m=+0.002925968 DO NOT EDIT!
|
||||||
|
|
||||||
package skylab
|
package skylab
|
||||||
|
|
||||||
|
@ -104,7 +104,11 @@ const (
|
||||||
WslSlipSpeedMeasurementId SkylabId = 0x117
|
WslSlipSpeedMeasurementId SkylabId = 0x117
|
||||||
)
|
)
|
||||||
|
|
||||||
// list of every packet ID. Can be used for O(1) checks.
|
var nameToIdMap = map[string]can.CanID{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// list of every packet ID. can be used for O(1) checks.
|
||||||
var idMap = map[can.CanID]bool{
|
var idMap = map[can.CanID]bool{
|
||||||
|
|
||||||
{ Id: 0x10, Extended: false }: true,
|
{ Id: 0x10, Extended: false }: true,
|
||||||
|
|
|
@ -100,7 +100,11 @@ const (
|
||||||
{{- end}}
|
{{- end}}
|
||||||
)
|
)
|
||||||
|
|
||||||
// list of every packet ID. Can be used for O(1) checks.
|
var nameToIdMap = map[string]can.CanID{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// list of every packet ID. can be used for O(1) checks.
|
||||||
var idMap = map[can.CanID]bool{
|
var idMap = map[can.CanID]bool{
|
||||||
{{ range $p := .Packets -}}
|
{{ range $p := .Packets -}}
|
||||||
{{ if $p.Repeat }}
|
{{ if $p.Repeat }}
|
||||||
|
|
Loading…
Reference in a new issue