Files
2026-05-17 14:49:17 -04:00

110 lines
2.2 KiB
Go

package roverd
import (
"context"
"log"
"time"
)
const (
autoChargeTimeout = 1 * time.Second
autoChargeCooldown = 0 * time.Minute
sourceHomeBase = 1 << 1
)
type AutoChargeController struct {
adapter *SerialAdapter
events chan<- RoverEvent
logger *log.Logger
timerStart time.Time
cooldownUntil time.Time
lastState byte
lastSources byte
}
func NewAutoChargeController(adapter *SerialAdapter, events chan<- RoverEvent, logger *log.Logger) *AutoChargeController {
return &AutoChargeController{
adapter: adapter,
events: events,
logger: logger,
}
}
func (a *AutoChargeController) Run(ctx context.Context, samples <-chan SensorSample) {
for {
select {
case <-ctx.Done():
return
case sample := <-samples:
a.processSample(sample)
}
}
}
func (a *AutoChargeController) processSample(sample SensorSample) {
now := time.Now()
docked := sample.ChargeSources&sourceHomeBase != 0
charging := isCharging(sample.ChargingState)
if !docked || charging {
if !a.timerStart.IsZero() {
a.emitEvent("autoCharge.timerCleared", map[string]any{
"durationMs": time.Since(a.timerStart).Milliseconds(),
})
}
a.timerStart = time.Time{}
a.lastState = sample.ChargingState
a.lastSources = sample.ChargeSources
return
}
// docked but not charging
if a.cooldownUntil.After(now) {
return
}
if a.timerStart.IsZero() {
a.timerStart = now
a.emitEvent("autoCharge.timerStarted", map[string]any{
"chargingState": sample.ChargingState,
})
return
}
if now.Sub(a.timerStart) >= autoChargeTimeout {
if err := a.adapter.SeekDock(); err != nil {
a.emitEvent("autoCharge.seekDockError", map[string]any{"error": err.Error()})
} else {
a.emitEvent("autoCharge.seekDockIssued", map[string]any{
"waitingMs": autoChargeTimeout.Milliseconds(),
})
}
a.timerStart = time.Time{}
a.cooldownUntil = now.Add(autoChargeCooldown)
}
}
func isCharging(state byte) bool {
switch state {
case 1, 2, 3, 4:
return true
default:
return false
}
}
func (a *AutoChargeController) emitEvent(event string, data map[string]any) {
if a.events == nil {
return
}
select {
case a.events <- RoverEvent{
Type: "event",
Event: event,
Ts: time.Now().UnixMilli(),
Data: data,
}:
default:
}
}