From 263d3c29a36f46da6025f7c2c656c5b2c47f0804 Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Mon, 21 Sep 2026 11:45:10 +0100 Subject: [PATCH] services decoupling --- devices/cdashdisplay/events.go | 9 ++++++ devices/uidevice/events.go | 9 ++++++ peripheral/peripheral.go | 2 ++ services/devices.go | 12 ++++---- services/devices_events.go | 1 + services/devices_lookup.go | 11 +++++-- services/services.go | 32 +++++++++++++++++++++ services/telemetry.go | 40 ++++++-------------------- services/telemetry_events.go | 41 +++++++++++++++++++++++++++ tui/internal/controllers/device.go | 21 +++++++------- tui/internal/controllers/streaming.go | 5 ---- tui/tui.go | 14 ++++----- 12 files changed, 134 insertions(+), 63 deletions(-) create mode 100644 devices/cdashdisplay/events.go create mode 100644 devices/uidevice/events.go create mode 100644 services/devices_events.go create mode 100644 services/telemetry_events.go diff --git a/devices/cdashdisplay/events.go b/devices/cdashdisplay/events.go new file mode 100644 index 0000000..365e32b --- /dev/null +++ b/devices/cdashdisplay/events.go @@ -0,0 +1,9 @@ +package cdashdisplay + +func (cds *CDashDisplay) OnLoad() error { + return nil +} + +func (cds *CDashDisplay) OnTelemetryProviderFound() error { + return nil +} diff --git a/devices/uidevice/events.go b/devices/uidevice/events.go new file mode 100644 index 0000000..f43b2a4 --- /dev/null +++ b/devices/uidevice/events.go @@ -0,0 +1,9 @@ +package uidevice + +func (uid *UIDevice) OnLoad() error { + return nil +} + +func (uid *UIDevice) OnTelemetryProviderFound() error { + return nil +} diff --git a/peripheral/peripheral.go b/peripheral/peripheral.go index 0984667..fc918ee 100644 --- a/peripheral/peripheral.go +++ b/peripheral/peripheral.go @@ -20,6 +20,8 @@ type Peripheral interface { Setup() error SendData(*telemetry.TelemetryData) error RequiredFields() []telemetry.FieldID + OnLoad() error + OnTelemetryProviderFound() error Close() error } diff --git a/services/devices.go b/services/devices.go index 214208a..8219254 100644 --- a/services/devices.go +++ b/services/devices.go @@ -22,15 +22,17 @@ type DeviceService struct { TelemCh <-chan telemetry.TelemetryData // Output Messages chan string + // Callbacks + OnTelemetryProviderDiscovered func() } -func NewDeviceService(logger *slog.Logger) *DeviceService { - sharedChannel := make(chan string, 10) - +func NewDeviceService(logger *slog.Logger, msg chan string) *DeviceService { dev := &DeviceService{ - PSS: NewPeripheralStateStore(logger.With("Service", "PeripheralStateStore"), devices.List), + PSS: NewPeripheralStateStore( + logger.With("Service", "PeripheralStateStore"), devices.List, msg, + ), Logger: logger, - Messages: sharedChannel, + Messages: msg, } // Start the routine that looks for devices - should always be running in the background diff --git a/services/devices_events.go b/services/devices_events.go new file mode 100644 index 0000000..5e568ea --- /dev/null +++ b/services/devices_events.go @@ -0,0 +1 @@ +package services diff --git a/services/devices_lookup.go b/services/devices_lookup.go index 6968784..febabd1 100644 --- a/services/devices_lookup.go +++ b/services/devices_lookup.go @@ -2,6 +2,7 @@ package services import ( "errors" + "fmt" "log/slog" "maps" "sync" @@ -51,15 +52,19 @@ type PeripheralStateStore struct { Logger *slog.Logger mu sync.RWMutex store map[string]*PeripheralState + // Messaging for UI and stuff + Messages chan string } func NewPeripheralStateStore( nLogger *slog.Logger, devList map[string]*devices.Device, + msg chan string, ) *PeripheralStateStore { store := PeripheralStateStore{ - Logger: nLogger, - store: make(map[string]*PeripheralState), + Logger: nLogger, + store: make(map[string]*PeripheralState), + Messages: msg, } for _, dev := range devList { @@ -154,6 +159,8 @@ func (pss *PeripheralStateStore) setDeviceConnected(pname string, per peripheral defer pss.mu.Unlock() pss.store[pname].Peripheral = per pss.store[pname].State = DeviceIsConnected + + pss.Messages <- fmt.Sprintf("Device successfuly connected: %s\n", pname) } func (pss *PeripheralStateStore) setDeviceTimedOut(pname string) { diff --git a/services/services.go b/services/services.go index bb02e66..d76cb62 100644 --- a/services/services.go +++ b/services/services.go @@ -1,2 +1,34 @@ // Package services interacts with the other libraries required for this UI package services + +import ( + "errors" + "log/slog" +) + +type Orchestrator struct { + DeviceService *DeviceService + TelemetryService *TelemetryService + Messages chan string +} + +func NewOrchestrator(logger *slog.Logger) (*Orchestrator, error) { + msg := make(chan string, 10) + + devService := NewDeviceService(logger.With("service", "DeviceService"), msg) + + telemService := NewTelemetryService(logger.With("service", "TelemetryService"), devService, msg) + if telemService == nil { + return nil, errors.New("failed to create telemetry service") + } + + go telemService.FindProvider(telemService.CtxMonitor) + + // Need to setup the callbacks on the services + + return &Orchestrator{ + DeviceService: devService, + TelemetryService: telemService, + Messages: msg, + }, nil +} diff --git a/services/telemetry.go b/services/telemetry.go index 674be83..679a85b 100644 --- a/services/telemetry.go +++ b/services/telemetry.go @@ -32,16 +32,21 @@ type TelemetryService struct { cancelMonitor context.CancelFunc CtxHealthcheck context.Context healthCheckCancel context.CancelFunc + // Callbacks + OnDevicesDiscovered func() } -func NewTelemetryService(logger *slog.Logger, devServo *DeviceService) *TelemetryService { - sharedChannel := make(chan string, 10) +func NewTelemetryService( + logger *slog.Logger, + devServo *DeviceService, + msg chan string, +) *TelemetryService { newService := &TelemetryService{ logger: logger, isConnected: false, devService: devServo, listeners: make(map[string]chan telem.TelemetryData), - Messages: sharedChannel, + Messages: msg, } newService.CtxMonitor, newService.cancelMonitor = context.WithCancel(context.Background()) @@ -59,6 +64,7 @@ func (t *TelemetryService) ProviderMonitor(ctx context.Context) { case <-ticker.C: slog.Info("checking if provider is still running") if !t.activeProvider.IsAlive(500 * time.Millisecond) { + t.Messages <- "Healthcheck on provider failing. Dropping provider.\n" slog.Warn("provider healthcheck failed") t.dropActiveProvider() t.onProviderHealthCheckFailed() @@ -154,34 +160,6 @@ func (t *TelemetryService) SwitchProvider(newProvider telem.TelemetryProvider) e return nil } -func (t *TelemetryService) onProviderHealthCheckFailed() { - // Just restart the whole lookup process - go t.FindProvider(t.CtxMonitor) -} - -func (t *TelemetryService) onFindProvider(prov telem.TelemetryProvider) { - // Attach to the provider - t.logger.Info("found provider for " + prov.Name()) - err := t.SwitchProvider(prov) - if err != nil { - t.logger.Error("failed to switch to provider onFindProvider", "err", err) - return - } - - // Create a routine to poll this provider while we wait to start the stream or pause it - t.CtxHealthcheck, t.healthCheckCancel = context.WithCancel(context.Background()) - go t.ProviderMonitor(t.CtxHealthcheck) -} - -func (t *TelemetryService) onProviderStopsMidStream() { - // clear the current provider - // TODO: now we need to also clear the devices to restart everything, - // if the stream stopped we have to restart the devices and everything - t.logger.Info("cleaning dropped provider and restarting lookup service") - t.dropActiveProvider() - go t.FindProvider(t.CtxMonitor) -} - // TODO: add some way of retriggering this. Currently it should: // start monitoring on startup -> find provider -> stop monitoring (when game closes for example) func (t *TelemetryService) FindProvider(ctx context.Context) { diff --git a/services/telemetry_events.go b/services/telemetry_events.go new file mode 100644 index 0000000..0899855 --- /dev/null +++ b/services/telemetry_events.go @@ -0,0 +1,41 @@ +package services + +import ( + "context" + "fmt" + + telem "esdi/telemetry" +) + +func (t *TelemetryService) onProviderHealthCheckFailed() { + // Just restart the whole lookup process + go t.FindProvider(t.CtxMonitor) +} + +func (t *TelemetryService) onFindProvider(prov telem.TelemetryProvider) { + // Attach to the provider + t.logger.Info("found provider for " + prov.Name()) + t.Messages <- fmt.Sprintf("Found provider \"%s\"\n", prov.Name()) + err := t.SwitchProvider(prov) + if err != nil { + t.Messages <- fmt.Sprintf("Failed to switch to provider: %+v\n", err.Error()) + t.logger.Error("failed to switch to provider onFindProvider", "err", err) + return + } + + // Start the healthcheck on our provider so we can drop it if it stops + t.CtxHealthcheck, t.healthCheckCancel = context.WithCancel(context.Background()) + go t.ProviderMonitor(t.CtxHealthcheck) + + // Tell the devices service we got a provider +} + +func (t *TelemetryService) onProviderStopsMidStream() { + // clear the current provider + // TODO: now we need to also clear the devices to restart everything, + // if the stream stopped we have to restart the devices and everything + t.Messages <- "Telemetry provider stopped mid stream\n" + t.logger.Info("cleaning dropped provider and restarting lookup service") + t.dropActiveProvider() + go t.FindProvider(t.CtxMonitor) +} diff --git a/tui/internal/controllers/device.go b/tui/internal/controllers/device.go index f0f86a3..9d3d653 100644 --- a/tui/internal/controllers/device.go +++ b/tui/internal/controllers/device.go @@ -16,19 +16,18 @@ type DeviceController struct { DeviceAPIView *views.DeviceAPIView LayoutCtrl *LayoutController StreamCtrl *StreamingCtrl - DevService *serv.DeviceService + Orchestrator *serv.Orchestrator } func NewDeviceController( base *Controller, - devService *serv.DeviceService, - telemService *serv.TelemetryService, + orchestrator *serv.Orchestrator, ) *DeviceController { mc := &DeviceController{ - Controller: base, - LayoutCtrl: NewLayoutController(base, devService), - DevService: devService, - StreamCtrl: NewStreamingCtrl(base, devService, telemService), + Controller: base, + LayoutCtrl: NewLayoutController(base, orchestrator.DeviceService), + Orchestrator: orchestrator, + StreamCtrl: NewStreamingCtrl(base, orchestrator.DeviceService, orchestrator.TelemetryService), } return mc @@ -57,7 +56,7 @@ func (mc *DeviceController) setDeviceAPIViewEvents() { SetInputCapture(func(ev *tcell.EventKey) *tcell.EventKey { switch ev.Rune() { case 'r': - go mc.DevService.FindDevices() + go mc.Orchestrator.DeviceService.FindDevices() } return ev }) @@ -67,8 +66,8 @@ func (mc *DeviceController) AddDeviceAPIListItems() { mc.DeviceAPIView.DevAPIList. AddItem("layout", "build a layout for CDashDisplay", func() { // This CDashDisplay specific, only load if we have a CDashDisplay - if !mc.DevService.PeripheralExists(cdashdisplay.NAME) { - mc.DevService.Messages <- "CDashDisplay it not loaded yet\n" + if !mc.Orchestrator.DeviceService.PeripheralExists(cdashdisplay.NAME) { + mc.Orchestrator.DeviceService.Messages <- "CDashDisplay it not loaded yet\n" return } @@ -116,7 +115,7 @@ func (mc *DeviceController) injectControllerCallbacks() { func (mc *DeviceController) injectChannels() { go func() { - for msg := range mc.DevService.Messages { + for msg := range mc.Orchestrator.Messages { mc.PrintToOutputWindow(msg) } }() diff --git a/tui/internal/controllers/streaming.go b/tui/internal/controllers/streaming.go index 9778602..cb3f211 100644 --- a/tui/internal/controllers/streaming.go +++ b/tui/internal/controllers/streaming.go @@ -59,11 +59,6 @@ func NewStreamingCtrl( return ctrl } -// func (sc *StreamingCtrl) subscribeListeners() { -// // Here I will set a UIDevice -// sc.TelemetryCh = sc.TelemServ.SubscribeListener("UI", 1) -// } - func (sc *StreamingCtrl) registerHooks() { sc.StreamView.Options.Form.SetInputCapture(func(ev *tcell.EventKey) *tcell.EventKey { switch ev.Key() { diff --git a/tui/tui.go b/tui/tui.go index 4add4bb..a1c1f3b 100644 --- a/tui/tui.go +++ b/tui/tui.go @@ -23,19 +23,15 @@ func NewControlPanel(logger *slog.Logger) *ControlPanel { App: tview.NewApplication(), } - // NOTE: create our device service here - devService := services.NewDeviceService(logger.With("service", "DeviceService")) - - telemService := services.NewTelemetryService(logger, devService) - if telemService == nil { - panic("failed to create the telemetry service") + orchestrator, err := services.NewOrchestrator(logger) + if err != nil { + // TODO: no panic here + panic("failed to create services orchestrator") } - go telemService.FindProvider(telemService.CtxMonitor) - return &ControlPanel{ Controller: baseController, - DeviceController: controllers.NewDeviceController(baseController, devService, telemService), + DeviceController: controllers.NewDeviceController(baseController, orchestrator), } }