move route and module into internal

This commit is contained in:
Joel Wetzell
2025-12-07 10:34:58 -06:00
parent 3f6914282b
commit a5deea6447
17 changed files with 172 additions and 122 deletions

View File

@@ -1,26 +1,29 @@
package showbridge package module
import ( import (
"context"
"fmt" "fmt"
"log/slog" "log/slog"
"net/http" "net/http"
"time" "time"
"github.com/jwetzell/showbridge-go/internal/config" "github.com/jwetzell/showbridge-go/internal/config"
"github.com/jwetzell/showbridge-go/internal/route"
) )
type HTTPClient struct { type HTTPClient struct {
config config.ModuleConfig config config.ModuleConfig
router *Router ctx context.Context
client *http.Client client *http.Client
router route.RouteIO
} }
func init() { func init() {
RegisterModule(ModuleRegistration{ RegisterModule(ModuleRegistration{
Type: "net.http.client", Type: "net.http.client",
New: func(config config.ModuleConfig, router *Router) (Module, error) { New: func(ctx context.Context, config config.ModuleConfig, router route.RouteIO) (Module, error) {
return &HTTPClient{config: config, router: router}, nil return &HTTPClient{config: config, ctx: ctx, router: router}, nil
}, },
}) })
} }
@@ -39,7 +42,7 @@ func (hc *HTTPClient) Run() error {
Timeout: 10 * time.Second, Timeout: 10 * time.Second,
} }
<-hc.router.Context.Done() <-hc.ctx.Done()
slog.Debug("router context done in module", "id", hc.config.Id) slog.Debug("router context done in module", "id", hc.config.Id)
return nil return nil
} }

View File

@@ -1,18 +1,21 @@
package showbridge package module
import ( import (
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"log/slog" "log/slog"
"net/http" "net/http"
"github.com/jwetzell/showbridge-go/internal/config" "github.com/jwetzell/showbridge-go/internal/config"
"github.com/jwetzell/showbridge-go/internal/route"
) )
type HTTPServer struct { type HTTPServer struct {
config config.ModuleConfig config config.ModuleConfig
Port uint16 Port uint16
router *Router ctx context.Context
router route.RouteIO
} }
type ResponseData struct { type ResponseData struct {
@@ -23,7 +26,7 @@ type ResponseData struct {
func init() { func init() {
RegisterModule(ModuleRegistration{ RegisterModule(ModuleRegistration{
Type: "net.http.server", Type: "net.http.server",
New: func(config config.ModuleConfig, router *Router) (Module, error) { New: func(ctx context.Context, config config.ModuleConfig, router route.RouteIO) (Module, error) {
params := config.Params params := config.Params
port, ok := params["port"] port, ok := params["port"]
if !ok { if !ok {
@@ -36,7 +39,7 @@ func init() {
return nil, fmt.Errorf("net.http.server port must be uint16") return nil, fmt.Errorf("net.http.server port must be uint16")
} }
return &HTTPServer{Port: uint16(portNum), config: config, router: router}, nil return &HTTPServer{Port: uint16(portNum), config: config, ctx: ctx, router: router}, nil
}, },
}) })
} }
@@ -85,7 +88,7 @@ func (hs *HTTPServer) Run() error {
} }
go func() { go func() {
<-hs.router.Context.Done() <-hs.ctx.Done()
slog.Debug("router context done in module", "id", hs.config.Id) slog.Debug("router context done in module", "id", hs.config.Id)
httpServer.Close() httpServer.Close()
}() }()
@@ -97,7 +100,7 @@ func (hs *HTTPServer) Run() error {
return err return err
} }
<-hs.router.Context.Done() <-hs.ctx.Done()
return nil return nil
} }

View File

@@ -1,24 +1,27 @@
package showbridge package module
import ( import (
"context"
"fmt" "fmt"
"log/slog" "log/slog"
"time" "time"
"github.com/jwetzell/showbridge-go/internal/config" "github.com/jwetzell/showbridge-go/internal/config"
"github.com/jwetzell/showbridge-go/internal/route"
) )
type Interval struct { type Interval struct {
config config.ModuleConfig config config.ModuleConfig
Duration uint32 Duration uint32
router *Router ctx context.Context
router route.RouteIO
ticker *time.Ticker ticker *time.Ticker
} }
func init() { func init() {
RegisterModule(ModuleRegistration{ RegisterModule(ModuleRegistration{
Type: "gen.interval", Type: "gen.interval",
New: func(config config.ModuleConfig, router *Router) (Module, error) { New: func(ctx context.Context, config config.ModuleConfig, router route.RouteIO) (Module, error) {
params := config.Params params := config.Params
duration, ok := params["duration"] duration, ok := params["duration"]
@@ -32,7 +35,7 @@ func init() {
return nil, fmt.Errorf("gen.interval duration must be number") return nil, fmt.Errorf("gen.interval duration must be number")
} }
return &Interval{Duration: uint32(durationNum), config: config, router: router}, nil return &Interval{Duration: uint32(durationNum), config: config, ctx: ctx, router: router}, nil
}, },
}) })
} }
@@ -52,7 +55,7 @@ func (i *Interval) Run() error {
for { for {
select { select {
case <-i.router.Context.Done(): case <-i.ctx.Done():
slog.Debug("router context done in module", "id", i.config.Id) slog.Debug("router context done in module", "id", i.config.Id)
return nil return nil
case <-ticker.C: case <-ticker.C:

View File

@@ -1,19 +1,22 @@
//go:build cgo //go:build cgo
package showbridge package module
import ( import (
"context"
"fmt" "fmt"
"log/slog" "log/slog"
"github.com/jwetzell/showbridge-go/internal/config" "github.com/jwetzell/showbridge-go/internal/config"
"github.com/jwetzell/showbridge-go/internal/route"
"gitlab.com/gomidi/midi/v2" "gitlab.com/gomidi/midi/v2"
_ "gitlab.com/gomidi/midi/v2/drivers/rtmididrv" _ "gitlab.com/gomidi/midi/v2/drivers/rtmididrv"
) )
type MIDIClient struct { type MIDIClient struct {
config config.ModuleConfig config config.ModuleConfig
router *Router ctx context.Context
router route.RouteIO
InputPort string InputPort string
OutputPort string OutputPort string
SendFunc func(midi.Message) error SendFunc func(midi.Message) error
@@ -23,7 +26,7 @@ func init() {
RegisterModule(ModuleRegistration{ RegisterModule(ModuleRegistration{
//TODO(jwetzell): find a better namespace than "misc" //TODO(jwetzell): find a better namespace than "misc"
Type: "misc.midi.client", Type: "misc.midi.client",
New: func(config config.ModuleConfig, router *Router) (Module, error) { New: func(ctx context.Context, config config.ModuleConfig, router route.RouteIO) (Module, error) {
params := config.Params params := config.Params
input, ok := params["input"] input, ok := params["input"]
@@ -49,7 +52,7 @@ func init() {
return nil, fmt.Errorf("misc.midi.client output must be a string") return nil, fmt.Errorf("misc.midi.client output must be a string")
} }
return &MIDIClient{config: config, InputPort: inputString, OutputPort: outputString, router: router}, nil return &MIDIClient{config: config, InputPort: inputString, OutputPort: outputString, ctx: ctx, router: router}, nil
}, },
}) })
} }
@@ -95,7 +98,7 @@ func (mc *MIDIClient) Run() error {
mc.SendFunc = send mc.SendFunc = send
<-mc.router.Context.Done() <-mc.ctx.Done()
slog.Debug("router context done in module", "id", mc.config.Id) slog.Debug("router context done in module", "id", mc.config.Id)
return nil return nil
} }

View File

@@ -1,10 +1,12 @@
package showbridge package module
import ( import (
"context"
"fmt" "fmt"
"sync" "sync"
"github.com/jwetzell/showbridge-go/internal/config" "github.com/jwetzell/showbridge-go/internal/config"
"github.com/jwetzell/showbridge-go/internal/route"
) )
type ModuleError struct { type ModuleError struct {
@@ -22,7 +24,7 @@ type Module interface {
type ModuleRegistration struct { type ModuleRegistration struct {
Type string `json:"type"` Type string `json:"type"`
New func(config.ModuleConfig, *Router) (Module, error) New func(context.Context, config.ModuleConfig, route.RouteIO) (Module, error)
} }
func RegisterModule(mod ModuleRegistration) { func RegisterModule(mod ModuleRegistration) {
@@ -37,13 +39,13 @@ func RegisterModule(mod ModuleRegistration) {
moduleRegistryMu.Lock() moduleRegistryMu.Lock()
defer moduleRegistryMu.Unlock() defer moduleRegistryMu.Unlock()
if _, ok := moduleRegistry[string(mod.Type)]; ok { if _, ok := ModuleRegistry[string(mod.Type)]; ok {
panic(fmt.Sprintf("module already registered: %s", mod.Type)) panic(fmt.Sprintf("module already registered: %s", mod.Type))
} }
moduleRegistry[string(mod.Type)] = mod ModuleRegistry[string(mod.Type)] = mod
} }
var ( var (
moduleRegistryMu sync.RWMutex moduleRegistryMu sync.RWMutex
moduleRegistry = make(map[string]ModuleRegistration) ModuleRegistry = make(map[string]ModuleRegistration)
) )

View File

@@ -1,17 +1,20 @@
package showbridge package module
import ( import (
"context"
"fmt" "fmt"
"log/slog" "log/slog"
mqtt "github.com/eclipse/paho.mqtt.golang" mqtt "github.com/eclipse/paho.mqtt.golang"
"github.com/jwetzell/showbridge-go/internal/config" "github.com/jwetzell/showbridge-go/internal/config"
"github.com/jwetzell/showbridge-go/internal/processing" "github.com/jwetzell/showbridge-go/internal/processing"
"github.com/jwetzell/showbridge-go/internal/route"
) )
type MQTTClient struct { type MQTTClient struct {
config config.ModuleConfig config config.ModuleConfig
router *Router ctx context.Context
router route.RouteIO
Broker string Broker string
ClientID string ClientID string
Topic string Topic string
@@ -21,7 +24,7 @@ type MQTTClient struct {
func init() { func init() {
RegisterModule(ModuleRegistration{ RegisterModule(ModuleRegistration{
Type: "net.mqtt.client", Type: "net.mqtt.client",
New: func(config config.ModuleConfig, router *Router) (Module, error) { New: func(ctx context.Context, config config.ModuleConfig, router route.RouteIO) (Module, error) {
params := config.Params params := config.Params
broker, ok := params["broker"] broker, ok := params["broker"]
@@ -59,7 +62,7 @@ func init() {
return nil, fmt.Errorf("net.mqtt.client clientId must be string") return nil, fmt.Errorf("net.mqtt.client clientId must be string")
} }
return &MQTTClient{config: config, Broker: brokerString, Topic: topicString, ClientID: clientIdString, router: router}, nil return &MQTTClient{config: config, Broker: brokerString, Topic: topicString, ClientID: clientIdString, ctx: ctx, router: router}, nil
}, },
}) })
} }
@@ -96,7 +99,7 @@ func (mc *MQTTClient) Run() error {
return err return err
} }
<-mc.router.Context.Done() <-mc.ctx.Done()
slog.Debug("router context done in module", "id", mc.config.Id) slog.Debug("router context done in module", "id", mc.config.Id)
return nil return nil
} }

View File

@@ -1,17 +1,20 @@
package showbridge package module
import ( import (
"context"
"fmt" "fmt"
"log/slog" "log/slog"
"github.com/jwetzell/showbridge-go/internal/config" "github.com/jwetzell/showbridge-go/internal/config"
"github.com/jwetzell/showbridge-go/internal/processing" "github.com/jwetzell/showbridge-go/internal/processing"
"github.com/jwetzell/showbridge-go/internal/route"
"github.com/nats-io/nats.go" "github.com/nats-io/nats.go"
) )
type NATSClient struct { type NATSClient struct {
config config.ModuleConfig config config.ModuleConfig
router *Router ctx context.Context
router route.RouteIO
URL string URL string
Subject string Subject string
client *nats.Conn client *nats.Conn
@@ -20,7 +23,7 @@ type NATSClient struct {
func init() { func init() {
RegisterModule(ModuleRegistration{ RegisterModule(ModuleRegistration{
Type: "net.nats.client", Type: "net.nats.client",
New: func(config config.ModuleConfig, router *Router) (Module, error) { New: func(ctx context.Context, config config.ModuleConfig, router route.RouteIO) (Module, error) {
params := config.Params params := config.Params
url, ok := params["url"] url, ok := params["url"]
@@ -46,7 +49,7 @@ func init() {
return nil, fmt.Errorf("net.nats.client subject must be string") return nil, fmt.Errorf("net.nats.client subject must be string")
} }
return &NATSClient{config: config, URL: urlString, Subject: subjectString, router: router}, nil return &NATSClient{config: config, URL: urlString, Subject: subjectString, ctx: ctx, router: router}, nil
}, },
}) })
} }
@@ -83,7 +86,7 @@ func (nc *NATSClient) Run() error {
defer sub.Unsubscribe() defer sub.Unsubscribe()
<-nc.router.Context.Done() <-nc.ctx.Done()
slog.Debug("router context done in module", "id", nc.config.Id) slog.Debug("router context done in module", "id", nc.config.Id)
return nil return nil
} }

View File

@@ -1,6 +1,7 @@
package showbridge package module
import ( import (
"context"
"fmt" "fmt"
"log/slog" "log/slog"
"net" "net"
@@ -8,21 +9,23 @@ import (
"github.com/jwetzell/psn-go" "github.com/jwetzell/psn-go"
"github.com/jwetzell/showbridge-go/internal/config" "github.com/jwetzell/showbridge-go/internal/config"
"github.com/jwetzell/showbridge-go/internal/route"
) )
type PSNClient struct { type PSNClient struct {
config config.ModuleConfig config config.ModuleConfig
conn *net.UDPConn conn *net.UDPConn
router *Router ctx context.Context
router route.RouteIO
decoder *psn.Decoder decoder *psn.Decoder
} }
func init() { func init() {
RegisterModule(ModuleRegistration{ RegisterModule(ModuleRegistration{
Type: "net.psn.client", Type: "net.psn.client",
New: func(config config.ModuleConfig, router *Router) (Module, error) { New: func(ctx context.Context, config config.ModuleConfig, router route.RouteIO) (Module, error) {
return &PSNClient{config: config, decoder: psn.NewDecoder(), router: router}, nil return &PSNClient{config: config, decoder: psn.NewDecoder(), ctx: ctx, router: router}, nil
}, },
}) })
} }
@@ -53,7 +56,7 @@ func (pc *PSNClient) Run() error {
buffer := make([]byte, 2048) buffer := make([]byte, 2048)
for { for {
select { select {
case <-pc.router.Context.Done(): case <-pc.ctx.Done():
// TODO(jwetzell): cleanup? // TODO(jwetzell): cleanup?
slog.Debug("router context done in module", "id", pc.config.Id) slog.Debug("router context done in module", "id", pc.config.Id)
return nil return nil

View File

@@ -1,20 +1,23 @@
//go:build cgo //go:build cgo
package showbridge package module
import ( import (
"context"
"fmt" "fmt"
"log/slog" "log/slog"
"time" "time"
"github.com/jwetzell/showbridge-go/internal/config" "github.com/jwetzell/showbridge-go/internal/config"
"github.com/jwetzell/showbridge-go/internal/framing" "github.com/jwetzell/showbridge-go/internal/framing"
"github.com/jwetzell/showbridge-go/internal/route"
"go.bug.st/serial" "go.bug.st/serial"
) )
type SerialClient struct { type SerialClient struct {
config config.ModuleConfig config config.ModuleConfig
router *Router ctx context.Context
router route.RouteIO
Port string Port string
Framer framing.Framer Framer framing.Framer
Mode *serial.Mode Mode *serial.Mode
@@ -25,7 +28,7 @@ func init() {
RegisterModule(ModuleRegistration{ RegisterModule(ModuleRegistration{
//TODO(jwetzell): find a better namespace than "misc" //TODO(jwetzell): find a better namespace than "misc"
Type: "misc.serial.client", Type: "misc.serial.client",
New: func(config config.ModuleConfig, router *Router) (Module, error) { New: func(ctx context.Context, config config.ModuleConfig, router route.RouteIO) (Module, error) {
params := config.Params params := config.Params
port, ok := params["port"] port, ok := params["port"]
@@ -70,7 +73,7 @@ func init() {
BaudRate: int(baudRateNum), BaudRate: int(baudRateNum),
} }
return &SerialClient{config: config, Port: portString, Framer: framer, Mode: &mode, router: router}, nil return &SerialClient{config: config, Port: portString, Framer: framer, Mode: &mode, ctx: ctx, router: router}, nil
}, },
}) })
} }
@@ -99,7 +102,7 @@ func (mc *SerialClient) Run() error {
// TODO(jwetzell): shutdown with router.Context properly // TODO(jwetzell): shutdown with router.Context properly
go func() { go func() {
<-mc.router.Context.Done() <-mc.ctx.Done()
slog.Debug("router context done in module", "id", mc.config.Id) slog.Debug("router context done in module", "id", mc.config.Id)
if mc.port != nil { if mc.port != nil {
mc.port.Close() mc.port.Close()
@@ -109,7 +112,7 @@ func (mc *SerialClient) Run() error {
for { for {
err := mc.SetupPort() err := mc.SetupPort()
if err != nil { if err != nil {
if mc.router.Context.Err() != nil { if mc.ctx.Err() != nil {
slog.Debug("router context done in module", "id", mc.config.Id) slog.Debug("router context done in module", "id", mc.config.Id)
return nil return nil
} }
@@ -120,14 +123,14 @@ func (mc *SerialClient) Run() error {
buffer := make([]byte, 1024) buffer := make([]byte, 1024)
select { select {
case <-mc.router.Context.Done(): case <-mc.ctx.Done():
slog.Debug("router context done in module", "id", mc.config.Id) slog.Debug("router context done in module", "id", mc.config.Id)
return nil return nil
default: default:
READ: READ:
for { for {
select { select {
case <-mc.router.Context.Done(): case <-mc.ctx.Done():
slog.Debug("router context done in module", "id", mc.config.Id) slog.Debug("router context done in module", "id", mc.config.Id)
return nil return nil
default: default:

View File

@@ -1,6 +1,7 @@
package showbridge package module
import ( import (
"context"
"fmt" "fmt"
"log/slog" "log/slog"
"net" "net"
@@ -8,20 +9,22 @@ import (
"github.com/jwetzell/showbridge-go/internal/config" "github.com/jwetzell/showbridge-go/internal/config"
"github.com/jwetzell/showbridge-go/internal/framing" "github.com/jwetzell/showbridge-go/internal/framing"
"github.com/jwetzell/showbridge-go/internal/route"
) )
type TCPClient struct { type TCPClient struct {
config config.ModuleConfig config config.ModuleConfig
framer framing.Framer framer framing.Framer
conn *net.TCPConn conn *net.TCPConn
router *Router ctx context.Context
router route.RouteIO
Addr *net.TCPAddr Addr *net.TCPAddr
} }
func init() { func init() {
RegisterModule(ModuleRegistration{ RegisterModule(ModuleRegistration{
Type: "net.tcp.client", Type: "net.tcp.client",
New: func(config config.ModuleConfig, router *Router) (Module, error) { New: func(ctx context.Context, config config.ModuleConfig, router route.RouteIO) (Module, error) {
params := config.Params params := config.Params
host, ok := params["host"] host, ok := params["host"]
@@ -68,7 +71,7 @@ func init() {
return nil, err return nil, err
} }
return &TCPClient{framer: framer, Addr: addr, config: config, router: router}, nil return &TCPClient{framer: framer, Addr: addr, config: config, ctx: ctx, router: router}, nil
}, },
}) })
} }
@@ -85,7 +88,7 @@ func (tc *TCPClient) Run() error {
// TODO(jwetzell): shutdown with router.Context properly // TODO(jwetzell): shutdown with router.Context properly
go func() { go func() {
<-tc.router.Context.Done() <-tc.ctx.Done()
slog.Debug("router context done in module", "id", tc.config.Id) slog.Debug("router context done in module", "id", tc.config.Id)
if tc.conn != nil { if tc.conn != nil {
tc.conn.Close() tc.conn.Close()
@@ -95,7 +98,7 @@ func (tc *TCPClient) Run() error {
for { for {
err := tc.SetupConn() err := tc.SetupConn()
if err != nil { if err != nil {
if tc.router.Context.Err() != nil { if tc.ctx.Err() != nil {
slog.Debug("router context done in module", "id", tc.config.Id) slog.Debug("router context done in module", "id", tc.config.Id)
return nil return nil
} }
@@ -106,14 +109,14 @@ func (tc *TCPClient) Run() error {
buffer := make([]byte, 1024) buffer := make([]byte, 1024)
select { select {
case <-tc.router.Context.Done(): case <-tc.ctx.Done():
slog.Debug("router context done in module", "id", tc.config.Id) slog.Debug("router context done in module", "id", tc.config.Id)
return nil return nil
default: default:
READ: READ:
for { for {
select { select {
case <-tc.router.Context.Done(): case <-tc.ctx.Done():
slog.Debug("router context done in module", "id", tc.config.Id) slog.Debug("router context done in module", "id", tc.config.Id)
return nil return nil
default: default:

View File

@@ -1,6 +1,7 @@
package showbridge package module
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"log/slog" "log/slog"
@@ -12,13 +13,15 @@ import (
"github.com/jwetzell/showbridge-go/internal/config" "github.com/jwetzell/showbridge-go/internal/config"
"github.com/jwetzell/showbridge-go/internal/framing" "github.com/jwetzell/showbridge-go/internal/framing"
"github.com/jwetzell/showbridge-go/internal/route"
) )
type TCPServer struct { type TCPServer struct {
config config.ModuleConfig config config.ModuleConfig
Addr *net.TCPAddr Addr *net.TCPAddr
Framer framing.Framer Framer framing.Framer
router *Router ctx context.Context
router route.RouteIO
quit chan interface{} quit chan interface{}
wg sync.WaitGroup wg sync.WaitGroup
connections []*net.TCPConn connections []*net.TCPConn
@@ -28,7 +31,7 @@ type TCPServer struct {
func init() { func init() {
RegisterModule(ModuleRegistration{ RegisterModule(ModuleRegistration{
Type: "net.tcp.server", Type: "net.tcp.server",
New: func(config config.ModuleConfig, router *Router) (Module, error) { New: func(ctx context.Context, config config.ModuleConfig, router route.RouteIO) (Module, error) {
params := config.Params params := config.Params
port, ok := params["port"] port, ok := params["port"]
if !ok { if !ok {
@@ -76,7 +79,7 @@ func init() {
return nil, err return nil, err
} }
return &TCPServer{Framer: framer, Addr: addr, config: config, quit: make(chan interface{}), router: router}, nil return &TCPServer{Framer: framer, Addr: addr, config: config, quit: make(chan interface{}), ctx: ctx, router: router}, nil
}, },
}) })
} }
@@ -162,7 +165,7 @@ func (ts *TCPServer) Run() error {
ts.wg.Add(1) ts.wg.Add(1)
go func() { go func() {
<-ts.router.Context.Done() <-ts.ctx.Done()
close(ts.quit) close(ts.quit)
listener.Close() listener.Close()
slog.Debug("router context done in module", "id", ts.config.Id) slog.Debug("router context done in module", "id", ts.config.Id)

View File

@@ -1,24 +1,27 @@
package showbridge package module
import ( import (
"context"
"fmt" "fmt"
"log/slog" "log/slog"
"time" "time"
"github.com/jwetzell/showbridge-go/internal/config" "github.com/jwetzell/showbridge-go/internal/config"
"github.com/jwetzell/showbridge-go/internal/route"
) )
type Timer struct { type Timer struct {
config config.ModuleConfig config config.ModuleConfig
Duration uint32 Duration uint32
router *Router ctx context.Context
router route.RouteIO
timer *time.Timer timer *time.Timer
} }
func init() { func init() {
RegisterModule(ModuleRegistration{ RegisterModule(ModuleRegistration{
Type: "gen.timer", Type: "gen.timer",
New: func(config config.ModuleConfig, router *Router) (Module, error) { New: func(ctx context.Context, config config.ModuleConfig, router route.RouteIO) (Module, error) {
params := config.Params params := config.Params
duration, ok := params["duration"] duration, ok := params["duration"]
@@ -32,7 +35,7 @@ func init() {
return nil, fmt.Errorf("gen.timer duration must be a number") return nil, fmt.Errorf("gen.timer duration must be a number")
} }
return &Timer{Duration: uint32(durationNum), config: config, router: router}, nil return &Timer{Duration: uint32(durationNum), config: config, ctx: ctx, router: router}, nil
}, },
}) })
} }
@@ -50,7 +53,7 @@ func (t *Timer) Run() error {
defer t.timer.Stop() defer t.timer.Stop()
for { for {
select { select {
case <-t.router.Context.Done(): case <-t.ctx.Done():
t.timer.Stop() t.timer.Stop()
slog.Debug("router context done in module", "id", t.config.Id) slog.Debug("router context done in module", "id", t.config.Id)
return nil return nil

View File

@@ -1,11 +1,13 @@
package showbridge package module
import ( import (
"context"
"fmt" "fmt"
"log/slog" "log/slog"
"net" "net"
"github.com/jwetzell/showbridge-go/internal/config" "github.com/jwetzell/showbridge-go/internal/config"
"github.com/jwetzell/showbridge-go/internal/route"
) )
type UDPClient struct { type UDPClient struct {
@@ -13,13 +15,14 @@ type UDPClient struct {
Addr *net.UDPAddr Addr *net.UDPAddr
Port uint16 Port uint16
conn *net.UDPConn conn *net.UDPConn
router *Router ctx context.Context
router route.RouteIO
} }
func init() { func init() {
RegisterModule(ModuleRegistration{ RegisterModule(ModuleRegistration{
Type: "net.udp.client", Type: "net.udp.client",
New: func(config config.ModuleConfig, router *Router) (Module, error) { New: func(ctx context.Context, config config.ModuleConfig, router route.RouteIO) (Module, error) {
params := config.Params params := config.Params
host, ok := params["host"] host, ok := params["host"]
@@ -49,7 +52,7 @@ func init() {
return nil, err return nil, err
} }
return &UDPClient{Addr: addr, config: config, router: router}, nil return &UDPClient{Addr: addr, config: config, ctx: ctx, router: router}, nil
}, },
}) })
} }
@@ -70,8 +73,7 @@ func (uc *UDPClient) Run() error {
} }
uc.conn = client uc.conn = client
<-uc.ctx.Done()
<-uc.router.Context.Done()
slog.Debug("router context done in module", "id", uc.config.Id) slog.Debug("router context done in module", "id", uc.config.Id)
if uc.conn != nil { if uc.conn != nil {
uc.conn.Close() uc.conn.Close()

View File

@@ -1,25 +1,28 @@
package showbridge package module
import ( import (
"context"
"fmt" "fmt"
"log/slog" "log/slog"
"net" "net"
"time" "time"
"github.com/jwetzell/showbridge-go/internal/config" "github.com/jwetzell/showbridge-go/internal/config"
"github.com/jwetzell/showbridge-go/internal/route"
) )
type UDPMulticast struct { type UDPMulticast struct {
config config.ModuleConfig config config.ModuleConfig
conn *net.UDPConn conn *net.UDPConn
router *Router ctx context.Context
router route.RouteIO
Addr *net.UDPAddr Addr *net.UDPAddr
} }
func init() { func init() {
RegisterModule(ModuleRegistration{ RegisterModule(ModuleRegistration{
Type: "net.udp.multicast", Type: "net.udp.multicast",
New: func(config config.ModuleConfig, router *Router) (Module, error) { New: func(ctx context.Context, config config.ModuleConfig, router route.RouteIO) (Module, error) {
params := config.Params params := config.Params
ip, ok := params["ip"] ip, ok := params["ip"]
@@ -48,7 +51,7 @@ func init() {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &UDPMulticast{config: config, Addr: addr, router: router}, nil return &UDPMulticast{config: config, Addr: addr, ctx: ctx, router: router}, nil
}, },
}) })
} }
@@ -74,7 +77,7 @@ func (um *UDPMulticast) Run() error {
buffer := make([]byte, 2048) buffer := make([]byte, 2048)
for { for {
select { select {
case <-um.router.Context.Done(): case <-um.ctx.Done():
// TODO(jwetzell): cleanup? // TODO(jwetzell): cleanup?
slog.Debug("router context done in module", "id", um.config.Id) slog.Debug("router context done in module", "id", um.config.Id)
return nil return nil

View File

@@ -1,6 +1,7 @@
package showbridge package module
import ( import (
"context"
"fmt" "fmt"
"log" "log"
"log/slog" "log/slog"
@@ -8,18 +9,20 @@ import (
"time" "time"
"github.com/jwetzell/showbridge-go/internal/config" "github.com/jwetzell/showbridge-go/internal/config"
"github.com/jwetzell/showbridge-go/internal/route"
) )
type UDPServer struct { type UDPServer struct {
Addr *net.UDPAddr Addr *net.UDPAddr
config config.ModuleConfig config config.ModuleConfig
router *Router ctx context.Context
router route.RouteIO
} }
func init() { func init() {
RegisterModule(ModuleRegistration{ RegisterModule(ModuleRegistration{
Type: "net.udp.server", Type: "net.udp.server",
New: func(config config.ModuleConfig, router *Router) (Module, error) { New: func(ctx context.Context, config config.ModuleConfig, router route.RouteIO) (Module, error) {
params := config.Params params := config.Params
port, ok := params["port"] port, ok := params["port"]
if !ok { if !ok {
@@ -50,7 +53,7 @@ func init() {
log.Fatalf("error resolving UDP address: %v", err) log.Fatalf("error resolving UDP address: %v", err)
} }
return &UDPServer{Addr: addr, config: config, router: router}, nil return &UDPServer{Addr: addr, config: config, ctx: ctx, router: router}, nil
}, },
}) })
} }
@@ -75,7 +78,7 @@ func (us *UDPServer) Run() error {
buffer := make([]byte, 1024) buffer := make([]byte, 1024)
for { for {
select { select {
case <-us.router.Context.Done(): case <-us.ctx.Done():
// TODO(jwetzell): cleanup? // TODO(jwetzell): cleanup?
slog.Debug("router context done in module", "id", us.config.Id) slog.Debug("router context done in module", "id", us.config.Id)
return nil return nil

View File

@@ -1,6 +1,7 @@
package showbridge package route
import ( import (
"context"
"fmt" "fmt"
"github.com/jwetzell/showbridge-go/internal/config" "github.com/jwetzell/showbridge-go/internal/config"
@@ -13,11 +14,21 @@ type RouteError struct {
Error error Error error
} }
type RouteIOError struct {
Index int
Error error
}
type RouteIO interface {
HandleInput(sourceId string, payload any) []RouteIOError
HandleOutput(sourceId string, destinationId string, payload any) error
}
type Route interface { type Route interface {
Input() string Input() string
Output() string Output() string
HandleInput(sourceId string, payload any, router *Router) error HandleInput(ctx context.Context, sourceId string, payload any, router RouteIO) error
HandleOutput(sourceId string, payload any, router *Router) error HandleOutput(ctx context.Context, sourceId string, payload any, router RouteIO) error
} }
type ProcessorRoute struct { type ProcessorRoute struct {
@@ -55,10 +66,10 @@ func (r *ProcessorRoute) Output() string {
return r.output return r.output
} }
func (r *ProcessorRoute) HandleInput(sourceId string, payload any, router *Router) error { func (r *ProcessorRoute) HandleInput(ctx context.Context, sourceId string, payload any, router RouteIO) error {
var err error var err error
for _, processor := range r.processors { for _, processor := range r.processors {
payload, err = processor.Process(router.Context, payload) payload, err = processor.Process(ctx, payload)
if err != nil { if err != nil {
return err return err
} }
@@ -67,9 +78,9 @@ func (r *ProcessorRoute) HandleInput(sourceId string, payload any, router *Route
return nil return nil
} }
} }
return r.HandleOutput(sourceId, payload, router) return r.HandleOutput(ctx, sourceId, payload, router)
} }
func (r *ProcessorRoute) HandleOutput(sourceId string, payload any, router *Router) error { func (r *ProcessorRoute) HandleOutput(ctx context.Context, sourceId string, payload any, router RouteIO) error {
return router.HandleOutput(sourceId, r.output, payload) return router.HandleOutput(sourceId, r.output, payload)
} }

View File

@@ -8,23 +8,19 @@ import (
"sync" "sync"
"github.com/jwetzell/showbridge-go/internal/config" "github.com/jwetzell/showbridge-go/internal/config"
"github.com/jwetzell/showbridge-go/internal/module"
"github.com/jwetzell/showbridge-go/internal/route"
) )
type RoutingError struct {
Index int
Error error
}
type Router struct { type Router struct {
contextCancel context.CancelFunc contextCancel context.CancelFunc
Context context.Context Context context.Context
ModuleInstances []Module ModuleInstances []module.Module
RouteInstances []Route RouteInstances []route.Route
moduleWait sync.WaitGroup moduleWait sync.WaitGroup
} }
func NewRouter(ctx context.Context, config config.Config) (*Router, []ModuleError, []RouteError) { func NewRouter(ctx context.Context, config config.Config) (*Router, []module.ModuleError, []route.RouteError) {
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo, Level: slog.LevelInfo,
})) }))
@@ -37,20 +33,20 @@ func NewRouter(ctx context.Context, config config.Config) (*Router, []ModuleErro
router := Router{ router := Router{
Context: routerContext, Context: routerContext,
contextCancel: cancel, contextCancel: cancel,
ModuleInstances: []Module{}, ModuleInstances: []module.Module{},
RouteInstances: []Route{}, RouteInstances: []route.Route{},
} }
var moduleErrors []ModuleError var moduleErrors []module.ModuleError
for moduleIndex, moduleDecl := range config.Modules { for moduleIndex, moduleDecl := range config.Modules {
moduleInfo, ok := moduleRegistry[moduleDecl.Type] moduleInfo, ok := module.ModuleRegistry[moduleDecl.Type]
if !ok { if !ok {
if moduleErrors == nil { if moduleErrors == nil {
moduleErrors = []ModuleError{} moduleErrors = []module.ModuleError{}
} }
moduleErrors = append(moduleErrors, ModuleError{ moduleErrors = append(moduleErrors, module.ModuleError{
Index: moduleIndex, Index: moduleIndex,
Config: moduleDecl, Config: moduleDecl,
Error: fmt.Errorf("module type not defined"), Error: fmt.Errorf("module type not defined"),
@@ -63,9 +59,9 @@ func NewRouter(ctx context.Context, config config.Config) (*Router, []ModuleErro
if moduleInstance.Id() == moduleDecl.Id { if moduleInstance.Id() == moduleDecl.Id {
moduleInstanceExists = true moduleInstanceExists = true
if moduleErrors == nil { if moduleErrors == nil {
moduleErrors = []ModuleError{} moduleErrors = []module.ModuleError{}
} }
moduleErrors = append(moduleErrors, ModuleError{ moduleErrors = append(moduleErrors, module.ModuleError{
Index: moduleIndex, Index: moduleIndex,
Config: moduleDecl, Config: moduleDecl,
Error: fmt.Errorf("duplicate module id"), Error: fmt.Errorf("duplicate module id"),
@@ -75,12 +71,12 @@ func NewRouter(ctx context.Context, config config.Config) (*Router, []ModuleErro
} }
if !moduleInstanceExists { if !moduleInstanceExists {
moduleInstance, err := moduleInfo.New(moduleDecl, &router) moduleInstance, err := moduleInfo.New(router.Context, moduleDecl, &router)
if err != nil { if err != nil {
if moduleErrors == nil { if moduleErrors == nil {
moduleErrors = []ModuleError{} moduleErrors = []module.ModuleError{}
} }
moduleErrors = append(moduleErrors, ModuleError{ moduleErrors = append(moduleErrors, module.ModuleError{
Index: moduleIndex, Index: moduleIndex,
Config: moduleDecl, Config: moduleDecl,
Error: err, Error: err,
@@ -93,21 +89,21 @@ func NewRouter(ctx context.Context, config config.Config) (*Router, []ModuleErro
} }
var routeErrors []RouteError var routeErrors []route.RouteError
for routeIndex, routeDecl := range config.Routes { for routeIndex, routeDecl := range config.Routes {
route, err := NewRoute(routeDecl) routeInstance, err := route.NewRoute(routeDecl)
if err != nil { if err != nil {
if routeErrors == nil { if routeErrors == nil {
routeErrors = []RouteError{} routeErrors = []route.RouteError{}
} }
routeErrors = append(routeErrors, RouteError{ routeErrors = append(routeErrors, route.RouteError{
Index: routeIndex, Index: routeIndex,
Config: routeDecl, Config: routeDecl,
Error: err, Error: err,
}) })
continue continue
} }
router.RouteInstances = append(router.RouteInstances, route) router.RouteInstances = append(router.RouteInstances, routeInstance)
} }
return &router, moduleErrors, routeErrors return &router, moduleErrors, routeErrors
@@ -134,16 +130,16 @@ func (r *Router) Stop() {
r.contextCancel() r.contextCancel()
} }
func (r *Router) HandleInput(sourceId string, payload any) []RoutingError { func (r *Router) HandleInput(sourceId string, payload any) []route.RouteIOError {
var routingErrors []RoutingError var routingErrors []route.RouteIOError
for routeIndex, route := range r.RouteInstances { for routeIndex, routeInstance := range r.RouteInstances {
if route.Input() == sourceId { if routeInstance.Input() == sourceId {
err := route.HandleInput(sourceId, payload, r) err := routeInstance.HandleInput(r.Context, sourceId, payload, r)
if err != nil { if err != nil {
if routingErrors == nil { if routingErrors == nil {
routingErrors = []RoutingError{} routingErrors = []route.RouteIOError{}
} }
routingErrors = append(routingErrors, RoutingError{ routingErrors = append(routingErrors, route.RouteIOError{
Index: routeIndex, Index: routeIndex,
Error: err, Error: err,
}) })