Device reconnection handling #15

Merged
esilva merged 10 commits from device-reconnection-handling into auto-detect-devices 2026-09-23 10:27:52 +01:00
6 changed files with 107 additions and 9 deletions
Showing only changes of commit 5d5b7bfdc3 - Show all commits
+9
View File
@@ -437,3 +437,12 @@ func (d *CDashDisplay) SendData(data *telemetry.TelemetryData) error {
return nil return nil
} }
func (cds *CDashDisplay) Setup() error {
err := cds.LoadLayout("layout.yaml")
if err != nil {
return err
}
return nil
}
+27 -4
View File
@@ -2,28 +2,33 @@
package devices package devices
import ( import (
"errors"
"esdi/devices/cdashdisplay" "esdi/devices/cdashdisplay"
"esdi/devices/uidevice" "esdi/devices/uidevice"
"esdi/peripheral" "esdi/peripheral"
) )
var ErrInvalidDevice = errors.New("invalid device")
type Device struct { type Device struct {
Name string Name string
Discover func() (peripheral.Peripheral, error) Discover func() (peripheral.Peripheral, error)
// DefaultSetup func(peripheral.Peripheral) error
} }
var List map[string]*Device = map[string]*Device{ var List map[string]*Device = map[string]*Device{
uidevice.NAME: { uidevice.NAME: {
Name: uidevice.NAME, Name: uidevice.NAME,
Discover: DiscoverUIDevice, Discover: UIDeviceDiscover,
}, },
cdashdisplay.NAME: { cdashdisplay.NAME: {
Name: cdashdisplay.NAME, Name: cdashdisplay.NAME,
Discover: DiscoverCDashDisplay, Discover: CDashDisplayDiscover,
}, },
} }
func DiscoverUIDevice() (peripheral.Peripheral, error) { func UIDeviceDiscover() (peripheral.Peripheral, error) {
uidev, err := uidevice.NewUIDevice() uidev, err := uidevice.NewUIDevice()
if err != nil { if err != nil {
return nil, err return nil, err
@@ -32,7 +37,11 @@ func DiscoverUIDevice() (peripheral.Peripheral, error) {
return uidev, nil return uidev, nil
} }
func DiscoverCDashDisplay() (peripheral.Peripheral, error) { // func UIDeviceSetup(peripheral peripheral.Peripheral) error {
// return nil
// }
func CDashDisplayDiscover() (peripheral.Peripheral, error) {
// Create a cdashdisplay // Create a cdashdisplay
display, err := cdashdisplay.NewCDashDisplay() display, err := cdashdisplay.NewCDashDisplay()
if err != nil { if err != nil {
@@ -41,3 +50,17 @@ func DiscoverCDashDisplay() (peripheral.Peripheral, error) {
return display, nil return display, nil
} }
// func CDashDisplaySetup(peripheral peripheral.Peripheral) error {
// cdash, ok := peripheral.(*cdashdisplay.CDashDisplay)
// if !ok {
// return ErrInvalidDevice
// }
//
// err := cdash.LoadLayout("layout.yaml")
// if err != nil {
// return err
// }
//
// return nil
// }
+4
View File
@@ -25,6 +25,10 @@ func (uid *UIDevice) Close() error {
return nil return nil
} }
func (uid *UIDevice) Setup() error {
return nil
}
func (uid *UIDevice) SendData(data *telemetry.TelemetryData) error { func (uid *UIDevice) SendData(data *telemetry.TelemetryData) error {
if data == nil { if data == nil {
return peripheral.ErrInvalidData return peripheral.ErrInvalidData
+1
View File
@@ -17,6 +17,7 @@ const (
type Peripheral interface { type Peripheral interface {
Name() string Name() string
Setup() error
SendData(*telemetry.TelemetryData) error SendData(*telemetry.TelemetryData) error
RequiredFields() []telemetry.FieldID RequiredFields() []telemetry.FieldID
Close() error Close() error
+63 -4
View File
@@ -15,6 +15,7 @@ var (
ErrPeripheralAlreadyRegistered = errors.New("peripheral is already registered") ErrPeripheralAlreadyRegistered = errors.New("peripheral is already registered")
ErrNoSuchDevice = errors.New("device doesn't exist") ErrNoSuchDevice = errors.New("device doesn't exist")
ErrDeviceIsNotConnected = errors.New("device isn't connected") ErrDeviceIsNotConnected = errors.New("device isn't connected")
ErrFailedToSetupPeripheral = errors.New("peripheral setup failed")
) )
type DeviceState = uint8 type DeviceState = uint8
@@ -176,6 +177,56 @@ func (pss *PeripheralStateStore) setDeviceReconnected(pname string, per peripher
// Device State Handling [END] ------------------------------------------------- // Device State Handling [END] -------------------------------------------------
// Device Handling [START] ----------------------------------------------------- // Device Handling [START] -----------------------------------------------------
func (pss *PeripheralStateStore) discoverPeripheral(
pname string,
onDiscovery func(pName string, peripheral peripheral.Peripheral),
) error {
state, err := pss.GetState(pname)
if err != nil {
pss.Logger.Error("Can't reconnect device", "device", pname, "error", err)
return err
}
dev, err := state.device.Discover()
if err != nil {
return err
}
// Register the device we just found
onDiscovery(pname, dev)
return nil
}
func (pss *PeripheralStateStore) handleDeviceTimedOut(pname string) error {
err := pss.discoverPeripheral(pname, pss.setDeviceReconnected)
if err != nil {
// Log something
return err
}
return nil
}
func (pss *PeripheralStateStore) handleDeviceReconnected(pname string) error {
// The device has reconnected, but we must set its state again
state, err := pss.GetState(pname)
if err != nil {
return err
}
err = state.Peripheral.Setup()
if err != nil {
pss.Logger.Error("failed to setup peripheral", "peripheral", pname, "error", err)
return ErrFailedToSetupPeripheral
}
// Around here I believe I need to swap the states so the peripheral is setup
pss.setDeviceConnected(pname, state.Peripheral)
return nil
}
func (pss *PeripheralStateStore) HandleDeviceState() { func (pss *PeripheralStateStore) HandleDeviceState() {
peripherals := pss.GetStates() peripherals := pss.GetStates()
@@ -183,22 +234,30 @@ func (pss *PeripheralStateStore) HandleDeviceState() {
switch pState.State { switch pState.State {
case DeviceIsDisconnected: case DeviceIsDisconnected:
pss.Logger.Debug("looking for device", "name", pName) pss.Logger.Debug("looking for device", "name", pName)
dev, err := pState.device.Discover() err := pss.discoverPeripheral(pName, pss.setDeviceConnected)
if err != nil { if err != nil {
// Log something
continue continue
} }
// Register the device we just found
pss.setDeviceConnected(pName, dev)
case DeviceIsConnected: case DeviceIsConnected:
// Need to check if its streaming, if its not streaming than we have to do a healthcheck // Need to check if its streaming, if its not streaming than we have to do a healthcheck
pss.Logger.Debug("Device is connected. Normal", "device", pName) pss.Logger.Debug("Device is connected. Normal", "device", pName)
case DeviceReconnected: case DeviceReconnected:
// If the device has reconnected we need to reset the device and then set it as connected // If the device has reconnected we need to reset the device and then set it as connected
pss.Logger.Debug("Device has reconnected. Clearing up state", "device", pName) pss.Logger.Debug("Device has reconnected. Clearing up state", "device", pName)
err := pss.handleDeviceReconnected(pName)
if err != nil {
pss.Logger.Error("device reconnection handler failed", "error", err)
continue
}
case DeviceTimedOut: case DeviceTimedOut:
// If the device has timed out we need to re-discover it or something // If the device has timed out we need to re-discover it or something
pss.Logger.Debug("Device is timed out. Attempting to recconect", "device", pName) pss.Logger.Debug("Device is timed out. Attempting to recconect", "device", pName)
err := pss.handleDeviceTimedOut(pName)
if err != nil {
pss.Logger.Error("device timing out handler failed", "error", err)
continue
}
} }
} }
} }
@@ -35,6 +35,7 @@ func NewLayoutController(base *Controller, service *services.DeviceService) *Lay
DevService: service, DevService: service,
MoveToolState: &windowManipState{Mode: moveMode}, MoveToolState: &windowManipState{Mode: moveMode},
// SelectedLayout: "beamng.yaml", // SelectedLayout: "beamng.yaml",
// TODO: this can't be here - the service/peripheral needs to know about it
SelectedLayout: "layout.yaml", SelectedLayout: "layout.yaml",
} }
@@ -404,7 +405,8 @@ func (lc *LayoutController) loadLayout() {
} }
// --- // ---
// We would get the layout path from somewhere but for nots its layout.yaml // TODO: This can happen here, but we need to address how the layout is gotten
// THE UI SHOULD SET STATE IN THE SERVICES ONLY
err = display.LoadLayout(lc.SelectedLayout) err = display.LoadLayout(lc.SelectedLayout)
if err != nil { if err != nil {
lc.Messages <- "failed to load layout: " + err.Error() lc.Messages <- "failed to load layout: " + err.Error()