mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 17:40:46 -04:00
110 lines
2.2 KiB
Go
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:
|
|
}
|
|
}
|