feat: red on brake

This commit is contained in:
Cyrille Nofficial 2023-05-10 19:52:44 +02:00
parent c971d51015
commit 8b67d8a434
3 changed files with 136 additions and 7 deletions

View File

@ -15,7 +15,8 @@ const (
func main() { func main() {
var mqttBroker, username, password, clientId string var mqttBroker, username, password, clientId string
var driveModeTopic, recordTopic, speedZoneTopic string var driveModeTopic, recordTopic, speedZoneTopic, throttleTopic string
var enableSpeedZoneMode bool
mqttQos := cli.InitIntFlag("MQTT_QOS", 0) mqttQos := cli.InitIntFlag("MQTT_QOS", 0)
_, mqttRetain := os.LookupEnv("MQTT_RETAIN") _, mqttRetain := os.LookupEnv("MQTT_RETAIN")
@ -25,6 +26,8 @@ func main() {
flag.StringVar(&driveModeTopic, "mqtt-topic-drive-mode", os.Getenv("MQTT_TOPIC_DRIVE_MODE"), "Mqtt topic that contains DriveMode value, use MQTT_TOPIC_DRIVE_MODE if args not set") flag.StringVar(&driveModeTopic, "mqtt-topic-drive-mode", os.Getenv("MQTT_TOPIC_DRIVE_MODE"), "Mqtt topic that contains DriveMode value, use MQTT_TOPIC_DRIVE_MODE if args not set")
flag.StringVar(&recordTopic, "mqtt-topic-record", os.Getenv("MQTT_TOPIC_RECORD"), "Mqtt topic that contains video recording state, use MQTT_TOPIC_RECORD if args not set") flag.StringVar(&recordTopic, "mqtt-topic-record", os.Getenv("MQTT_TOPIC_RECORD"), "Mqtt topic that contains video recording state, use MQTT_TOPIC_RECORD if args not set")
flag.StringVar(&speedZoneTopic, "mqtt-topic-speed-zone", os.Getenv("MQTT_TOPIC_SPEED_ZONE"), "Mqtt topic that contains speed zone, use MQTT_TOPIC_SPEED_ZONE if args not set") flag.StringVar(&speedZoneTopic, "mqtt-topic-speed-zone", os.Getenv("MQTT_TOPIC_SPEED_ZONE"), "Mqtt topic that contains speed zone, use MQTT_TOPIC_SPEED_ZONE if args not set")
flag.StringVar(&throttleTopic, "mqtt-topic-throttle", os.Getenv("MQTT_TOPIC_THROTTLE"), "Mqtt topic that contains throttle, use MQTT_TOPIC_THROTTLE if args not set")
flag.BoolVar(&enableSpeedZoneMode, "enable-speedzone-mode", false, "Enable speed-zone mode")
logLevel := zap.LevelFlag("log", zap.InfoLevel, "log level") logLevel := zap.LevelFlag("log", zap.InfoLevel, "log level")
flag.Parse() flag.Parse()
@ -53,7 +56,11 @@ func main() {
} }
defer client.Disconnect(50) defer client.Disconnect(50)
p := part.NewPart(client, driveModeTopic, recordTopic, speedZoneTopic) mode := part.LedModeBrake
if enableSpeedZoneMode {
mode = part.LedModeSpeedZone
}
p := part.NewPart(client, driveModeTopic, recordTopic, speedZoneTopic, throttleTopic, mode)
defer p.Stop() defer p.Stop()
cli.HandleExit(p) cli.HandleExit(p)

View File

@ -12,29 +12,41 @@ import (
"time" "time"
) )
func NewPart(client mqtt.Client, driveModeTopic, recordTopic, speedZoneTopic string) *LedPart { const (
LedModeBrake LedMode = iota
LedModeSpeedZone
)
type LedMode int
func NewPart(client mqtt.Client, driveModeTopic, recordTopic, speedZoneTopic, throttleTopic string, ledMode LedMode) *LedPart {
return &LedPart{ return &LedPart{
led: led.New(), led: led.New(),
mode: ledMode,
client: client, client: client,
onDriveModeTopic: driveModeTopic, onDriveModeTopic: driveModeTopic,
onRecordTopic: recordTopic, onRecordTopic: recordTopic,
onSpeedZoneTopic: speedZoneTopic, onSpeedZoneTopic: speedZoneTopic,
onThrottleTopic: throttleTopic,
muDriveMode: sync.Mutex{}, muDriveMode: sync.Mutex{},
driveMode: events.DriveMode_INVALID, driveMode: events.DriveMode_INVALID,
muRecord: sync.Mutex{}, muRecord: sync.Mutex{},
recordEnabled: false, recordEnabled: false,
muSpeedZone: sync.Mutex{}, muSpeedZone: sync.Mutex{},
speedZone: events.SpeedZone_UNKNOWN, speedZone: events.SpeedZone_UNKNOWN,
muThrottle: sync.Mutex{},
} }
} }
type LedPart struct { type LedPart struct {
led led.ColoredLed led led.ColoredLed
mode LedMode
client mqtt.Client client mqtt.Client
onDriveModeTopic string onDriveModeTopic string
onRecordTopic string onRecordTopic string
onSpeedZoneTopic string onSpeedZoneTopic string
onThrottleTopic string
muDriveMode sync.Mutex muDriveMode sync.Mutex
driveMode events.DriveMode driveMode events.DriveMode
@ -43,6 +55,9 @@ type LedPart struct {
muSpeedZone sync.Mutex muSpeedZone sync.Mutex
speedZone events.SpeedZone speedZone events.SpeedZone
muThrottle sync.Mutex
throttle float32
} }
func (p *LedPart) Start() error { func (p *LedPart) Start() error {
@ -57,7 +72,7 @@ func (p *LedPart) Start() error {
func (p *LedPart) Stop() { func (p *LedPart) Stop() {
defer p.led.SetBlink(0) defer p.led.SetBlink(0)
defer p.led.SetColor(led.ColorBlack) defer p.led.SetColor(led.ColorBlack)
service.StopService("led", p.client, p.onDriveModeTopic, p.onRecordTopic, p.onSpeedZoneTopic) service.StopService("led", p.client, p.onDriveModeTopic, p.onRecordTopic, p.onSpeedZoneTopic, p.onThrottleTopic)
} }
func (p *LedPart) setDriveMode(m events.DriveMode) { func (p *LedPart) setDriveMode(m events.DriveMode) {
@ -119,12 +134,46 @@ func (p *LedPart) onSpeedZone(_ mqtt.Client, message mqtt.Message) {
p.updateColor() p.updateColor()
} }
func (p *LedPart) setThrottle(throttle float32) {
p.muThrottle.Lock()
defer p.muThrottle.Unlock()
p.throttle = throttle
}
func (p *LedPart) onThrottle(_ mqtt.Client, message mqtt.Message) {
var throttleMessage events.ThrottleMessage
err := proto.Unmarshal(message.Payload(), &throttleMessage)
if err != nil {
zap.S().Errorf("unable to unmarshal %T message: %v", throttleMessage, err)
return
}
p.setThrottle(throttleMessage.GetThrottle())
p.updateColor()
}
func (p *LedPart) updateColor() { func (p *LedPart) updateColor() {
p.muSpeedZone.Lock() p.muSpeedZone.Lock()
defer p.muSpeedZone.Unlock() defer p.muSpeedZone.Unlock()
p.muDriveMode.Lock() p.muDriveMode.Lock()
defer p.muDriveMode.Unlock() defer p.muDriveMode.Unlock()
p.muThrottle.Lock()
defer p.muThrottle.Unlock()
if p.throttle <= -0.05 {
p.led.SetColor(led.Color{Red: int(p.throttle * -255)})
return
}
switch p.mode {
case LedModeBrake:
p.updateBrakeColor()
case LedModeSpeedZone:
p.updateSpeedZoneColor()
}
}
func (p *LedPart) updateSpeedZoneColor() {
switch p.driveMode { switch p.driveMode {
case events.DriveMode_USER: case events.DriveMode_USER:
p.led.SetColor(led.ColorGreen) p.led.SetColor(led.ColorGreen)
@ -142,6 +191,16 @@ func (p *LedPart) updateColor() {
} }
} }
func (p *LedPart) updateBrakeColor() {
switch p.driveMode {
case events.DriveMode_USER:
p.led.SetColor(led.ColorGreen)
case events.DriveMode_PILOT:
p.led.SetColor(led.ColorBlue)
}
}
func (p *LedPart) registerCallbacks() error { func (p *LedPart) registerCallbacks() error {
err := service.RegisterCallback(p.client, p.onDriveModeTopic, p.onDriveMode) err := service.RegisterCallback(p.client, p.onDriveModeTopic, p.onDriveMode)
if err != nil { if err != nil {
@ -158,5 +217,10 @@ func (p *LedPart) registerCallbacks() error {
return err return err
} }
err = service.RegisterCallback(p.client, p.onThrottleTopic, p.onThrottle)
if err != nil {
return err
}
return nil return nil
} }

View File

@ -50,7 +50,7 @@ func TestLedPart_OnDriveMode(t *testing.T) {
} }
value := msg.DriveMode value := msg.DriveMode
if l.color != c.color { if l.color != c.color {
t.Errorf("driveMode(%v)=invalid value for color: %v, wants %v", value, l.color, c.color) t.Errorf("driveMode(%v)=invalid value for expectedColor: %v, wants %v", value, l.color, c.color)
} }
} }
} }
@ -87,7 +87,7 @@ func TestLedPart_OnRecord(t *testing.T) {
func TestLedPart_OnSpeedZone(t *testing.T) { func TestLedPart_OnSpeedZone(t *testing.T) {
l := fakeLed{} l := fakeLed{}
p := LedPart{led: &l, driveMode: events.DriveMode_PILOT} p := LedPart{led: &l, mode: LedModeSpeedZone, driveMode: events.DriveMode_PILOT}
cases := []struct { cases := []struct {
msg mqtt.Message msg mqtt.Message
@ -109,7 +109,65 @@ func TestLedPart_OnSpeedZone(t *testing.T) {
} }
value := msg.GetSpeedZone() value := msg.GetSpeedZone()
if l.color != c.color { if l.color != c.color {
t.Errorf("driveMode(%v)=invalid value for color: %v, wants %v", value, l.color, c.color) t.Errorf("driveMode(%v)=invalid value for expectedColor: %v, wants %v", value, l.color, c.color)
} }
} }
} }
func TestLedPart_OnThrottle(t *testing.T) {
cases := []struct {
name string
msg mqtt.Message
expectedColor led.Color
}{
{"throttle stop",
testtools.NewFakeMessageFromProtobuf("throttle", &events.ThrottleMessage{Throttle: 0.}),
led.ColorBlue,
},
{
"throttle normal",
testtools.NewFakeMessageFromProtobuf("throttle", &events.ThrottleMessage{Throttle: 0.5}),
led.ColorBlue,
},
{
"near zero",
testtools.NewFakeMessageFromProtobuf("throttle", &events.ThrottleMessage{Throttle: -0.01}),
led.ColorBlue,
},
{
"slow brake",
testtools.NewFakeMessageFromProtobuf("throttle", &events.ThrottleMessage{Throttle: -0.06}),
led.Color{Red: 15},
},
{
"normal brake",
testtools.NewFakeMessageFromProtobuf("throttle", &events.ThrottleMessage{Throttle: -0.5}),
led.Color{Red: 127},
},
{
"high brake",
testtools.NewFakeMessageFromProtobuf("throttle", &events.ThrottleMessage{Throttle: -1.}),
led.ColorRed,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
l := fakeLed{}
p := LedPart{led: &l, mode: LedModeBrake, driveMode: events.DriveMode_PILOT}
p.onThrottle(nil, c.msg)
time.Sleep(1 * time.Millisecond)
var msg events.ThrottleMessage
err := proto.Unmarshal(c.msg.Payload(), &msg)
if err != nil {
t.Errorf("unable to unmarshal drive mode message: %v", err)
}
value := msg.GetThrottle()
if l.color != c.expectedColor {
t.Errorf("driveMode(%v)=invalid value for expectedColor: %v, wants %v", value, l.color, c.expectedColor)
}
})
}
}