Compare commits

...

4 Commits

5 changed files with 341 additions and 199 deletions

View File

@ -1,4 +1,4 @@
FROM --platform=$BUILDPLATFORM golang:1.17-alpine AS builder FROM --platform=$BUILDPLATFORM golang:1.19-alpine AS builder
ARG TARGETPLATFORM ARG TARGETPLATFORM
ARG BUILDPLATFORM ARG BUILDPLATFORM

View File

@ -38,7 +38,7 @@ func main() {
cli.InitMqttFlags(DefaultClientId, &mqttBroker, &username, &password, &clientId, &mqttQos, &mqttRetain) cli.InitMqttFlags(DefaultClientId, &mqttBroker, &username, &password, &clientId, &mqttQos, &mqttRetain)
var steeringLeftPWM, steeringRightPWM, steeringCenterPWM int var steeringLeftPWM, steeringRightPWM, steeringCenterPWM int
var throttleMinPWM, throttleMaxPWM, throttleZeroPWM int var secondarySteeringLeftPWM, secondarySteeringRightPWM, secondarySteeringCenterPWM int
if err := cli.SetIntDefaultValueFromEnv(&steeringLeftPWM, "STEERING_LEFT_PWM", SteeringLeftPWM); err != nil { if err := cli.SetIntDefaultValueFromEnv(&steeringLeftPWM, "STEERING_LEFT_PWM", SteeringLeftPWM); err != nil {
zap.S().Warnf("unable to init steeringLeftPWM arg: %v", err) zap.S().Warnf("unable to init steeringLeftPWM arg: %v", err)
} }
@ -46,7 +46,19 @@ func main() {
zap.S().Warnf("unable to init steeringRightPWM arg: %v", err) zap.S().Warnf("unable to init steeringRightPWM arg: %v", err)
} }
if err := cli.SetIntDefaultValueFromEnv(&steeringCenterPWM, "STEERING_CENTER_PWM", SteeringCenterPWM); err != nil { if err := cli.SetIntDefaultValueFromEnv(&steeringCenterPWM, "STEERING_CENTER_PWM", SteeringCenterPWM); err != nil {
zap.S().Warnf("unable to init steeringRightPWM arg: %v", err) zap.S().Warnf("unable to init steeringCenterPWM arg: %v", err)
}
var throttleMinPWM, throttleMaxPWM, throttleZeroPWM int
var secondaryThrottleMinPWM, secondaryThrottleMaxPWM, secondaryThrottleCenterPWM int
if err := cli.SetIntDefaultValueFromEnv(&throttleMinPWM, "THROTTLE_MIN_PWM", arduino.DefaultPwmThrottle.Min); err != nil {
zap.S().Warnf("unable to init throttleMinPWM arg: %v", err)
}
if err := cli.SetIntDefaultValueFromEnv(&throttleMaxPWM, "THROTTLE_MAX_PWM", arduino.DefaultPwmThrottle.Max); err != nil {
zap.S().Warnf("unable to init throttleMaxPWM arg: %v", err)
}
if err := cli.SetIntDefaultValueFromEnv(&throttleZeroPWM, "THROTTLE_CENTER_PWM", arduino.DefaultPwmThrottle.Middle); err != nil {
zap.S().Warnf("unable to init throttleZeroPWM arg: %v", err)
} }
if err := cli.SetIntDefaultValueFromEnv(&throttleMinPWM, "THROTTLE_MIN_PWM", ThrottleMinPWM); err != nil { if err := cli.SetIntDefaultValueFromEnv(&throttleMinPWM, "THROTTLE_MIN_PWM", ThrottleMinPWM); err != nil {
zap.S().Warnf("unable to init steeringLeftPWM arg: %v", err) zap.S().Warnf("unable to init steeringLeftPWM arg: %v", err)
@ -65,12 +77,20 @@ func main() {
flag.StringVar(&switchRecordTopic, "mqtt-topic-switch-record", os.Getenv("MQTT_TOPIC_SWITCH_RECORD"), "Mqtt topic where to publish switch record state, use MQTT_TOPIC_SWITCH_RECORD if args not set") flag.StringVar(&switchRecordTopic, "mqtt-topic-switch-record", os.Getenv("MQTT_TOPIC_SWITCH_RECORD"), "Mqtt topic where to publish switch record state, use MQTT_TOPIC_SWITCH_RECORD if args not set")
flag.StringVar(&device, "device", "/dev/serial0", "Serial device") flag.StringVar(&device, "device", "/dev/serial0", "Serial device")
flag.IntVar(&baud, "baud", 115200, "Serial baud") flag.IntVar(&baud, "baud", 115200, "Serial baud")
flag.IntVar(&steeringLeftPWM, "steering-left-pwm", steeringLeftPWM, "Right left value for steering PWM, STEERING_LEFT_PWM env if args not set")
flag.IntVar(&steeringRightPWM, "steering-right-pwm", steeringRightPWM, "Right right value for steering PWM, STEERING_RIGHT_PWM env if args not set") flag.IntVar(&steeringLeftPWM, "steering-left-pwm", steeringLeftPWM, "maxPwm left value for steering PWM, STEERING_LEFT_PWM env if args not set")
flag.IntVar(&steeringCenterPWM, "steering-center-pwm", steeringCenterPWM, "Center value for steering PWM, STEERING_CENTER_PWM env if args not set") flag.IntVar(&steeringRightPWM, "steering-right-pwm", steeringRightPWM, "maxPwm right value for steering PWM, STEERING_RIGHT_PWM env if args not set")
flag.IntVar(&throttleMinPWM, "throttle-min-pwm", throttleMinPWM, "Min value for throttle PWM, THROTTLE_MIN_PWM env if args not set") flag.IntVar(&steeringCenterPWM, "steering-center-pwm", steeringCenterPWM, "middlePwm value for steering PWM, STEERING_CENTER_PWM env if args not set")
flag.IntVar(&steeringRightPWM, "throttle-max-pwm", throttleMaxPWM, "Throttle max value for throttle PWM, THROTTLE_MAX_PWM env if args not set") flag.IntVar(&secondarySteeringLeftPWM, "steering-secondary-left-pwm", steeringLeftPWM, "maxPwm left value for secondary radio controller steering PWM, STEERING_LEFT_PWM env if args not set")
flag.IntVar(&steeringCenterPWM, "throttle-zero-pwm", throttleZeroPWM, "Zero value for throttle PWM, THROTTLE_ZERO_PWM env if args not set") flag.IntVar(&secondarySteeringRightPWM, "steering-secondary-right-pwm", steeringRightPWM, "maxPwm right value for secondary radio controller steering PWM, STEERING_RIGHT_PWM env if args not set")
flag.IntVar(&secondarySteeringCenterPWM, "steering-secondary-center-pwm", steeringCenterPWM, "middlePwm value for secondary radio controller steering PWM, STEERING_CENTER_PWM env if args not set")
flag.IntVar(&throttleMinPWM, "throttle-min-pwm", throttleMinPWM, "maxPwm min value for throttle PWM, THROTTLE_MIN_PWM env if args not set")
flag.IntVar(&throttleMaxPWM, "throttle-max-pwm", throttleMaxPWM, "maxPwm max value for throttle PWM, THROTTLE_MAX_PWM env if args not set")
flag.IntVar(&throttleZeroPWM, "throttle-center-pwm", throttleZeroPWM, "middlePwm value for throttle PWM, THROTTLE_CENTER_PWM env if args not set")
flag.IntVar(&secondaryThrottleMinPWM, "throttle-secondary-min-pwm", throttleMinPWM, "maxPwm min value for secondary radio controller throttle PWM, THROTTLE_MIN_PWM env if args not set")
flag.IntVar(&secondaryThrottleMaxPWM, "throttle-secondary-max-pwm", throttleMaxPWM, "maxPwm max value for secondary radio controller throttle PWM, THROTTLE_MAX_PWM env if args not set")
flag.IntVar(&secondaryThrottleCenterPWM, "throttle-secondary-center-pwm", throttleZeroPWM, "middlePwm value for secondary radio controller throttle PWM, THROTTLE_CENTER_PWM env if args not set")
logLevel := zap.LevelFlag("log", zap.InfoLevel, "log level") logLevel := zap.LevelFlag("log", zap.InfoLevel, "log level")
flag.Parse() flag.Parse()
@ -99,9 +119,17 @@ func main() {
} }
defer client.Disconnect(10) defer client.Disconnect(10)
sc := arduino.NewAsymetricPWMSteeringConfig(steeringLeftPWM, steeringRightPWM, steeringCenterPWM) sc := arduino.NewAsymetricPWMConfig(steeringLeftPWM, steeringRightPWM, steeringCenterPWM)
tc := arduino.PWMThrottleConfig{Min: throttleMinPWM, Max: throttleMaxPWM, Zero: throttleZeroPWM} secondarySc := arduino.NewAsymetricPWMConfig(secondarySteeringLeftPWM, secondarySteeringRightPWM, secondarySteeringCenterPWM)
a := arduino.NewPart(client, device, baud, throttleTopic, steeringTopic, driveModeTopic, switchRecordTopic, pubFrequency, sc, tc) tc := arduino.NewAsymetricPWMConfig(throttleMinPWM, throttleMaxPWM, throttleZeroPWM)
secondaryTc := arduino.NewAsymetricPWMConfig(secondaryThrottleMinPWM, secondaryThrottleMaxPWM, secondaryThrottleMaxPWM)
a := arduino.NewPart(client, device, baud, throttleTopic, steeringTopic, driveModeTopic, switchRecordTopic,
pubFrequency,
arduino.WithThrottleConfig(tc),
arduino.WithSteeringConfig(sc),
arduino.WithSecondaryRC(secondaryTc, secondarySc),
)
cli.HandleExit(a) cli.HandleExit(a)

2
go.mod
View File

@ -1,6 +1,6 @@
module github.com/cyrilix/robocar-arduino module github.com/cyrilix/robocar-arduino
go 1.18 go 1.19
require ( require (
github.com/cyrilix/robocar-base v0.1.7 github.com/cyrilix/robocar-base v0.1.7

View File

@ -21,7 +21,12 @@ const (
) )
var ( var (
serialLineRegex = regexp.MustCompile(`(?P<timestamp>\d+),(?P<channel_1>\d+),(?P<channel_2>\d+),(?P<channel_3>\d+),(?P<channel_4>\d+),(?P<channel_5>\d+),(?P<channel_6>\d+),(?P<frequency>\d+),(?P<distance_cm>\d+)?`) serialLineRegex = regexp.MustCompile(`(?P<timestamp>\d+),(?P<channel_1>\d+),(?P<channel_2>\d+),(?P<channel_3>\d+),(?P<channel_4>\d+),(?P<channel_5>\d+),(?P<channel_6>-?\d+),(?P<channel_7>\d+),(?P<channel_8>\d+),(?P<frequency>\d+)`)
DefaultPwmThrottle = PWMConfig{
Min: MinPwmThrottle,
Max: MaxPwmThrottle,
Middle: MinPwmThrottle + (MaxPwmThrottle-MinPwmThrottle)/2,
}
) )
type Part struct { type Part struct {
@ -30,50 +35,68 @@ type Part struct {
pubFrequency float64 pubFrequency float64
serial io.Reader serial io.Reader
mutex sync.Mutex mutex sync.Mutex
steering float32 steering, secondarySteering float32
throttle float32 throttle, secondaryThrottle float32
ctrlRecord bool ctrlRecord bool
driveMode events.DriveMode driveMode events.DriveMode
cancel chan interface{} cancel chan interface{}
pwmSteeringConfig PWMSteeringConfig useSecondaryRc bool
pwmThrottleConfig PWMThrottleConfig pwmSteeringConfig *PWMConfig
pwmSecondarySteeringConfig *PWMConfig
pwmThrottleConfig *PWMConfig
pwmSecondaryThrottleConfig *PWMConfig
} }
type PWMThrottleConfig struct { type PWMConfig struct {
Min int Min int
Max int Max int
Zero int Middle int
} }
type PWMSteeringConfig struct { func NewPWMConfig(min, max int) *PWMConfig {
Left int return &PWMConfig{
Right int Min: min,
Center int Max: max,
} Middle: min + (max-min)/2,
func NewPWMSteeringConfig(min, max int) PWMSteeringConfig {
return PWMSteeringConfig{
Left: min,
Right: max,
Center: min + (max-min)/2,
} }
} }
func NewAsymetricPWMSteeringConfig(min, max, middle int) PWMSteeringConfig { func NewAsymetricPWMConfig(min, max, middle int) *PWMConfig {
c := NewPWMSteeringConfig(min, max) c := NewPWMConfig(min, max)
c.Center = middle c.Middle = middle
return c return c
} }
type Option func(p *Part)
func WithThrottleConfig(throttleConfig *PWMConfig) Option {
return func(p *Part) {
p.pwmThrottleConfig = throttleConfig
}
}
func WithSteeringConfig(steeringConfig *PWMConfig) Option {
return func(p *Part) {
p.pwmSteeringConfig = steeringConfig
}
}
func WithSecondaryRC(throttleConfig, steeringConfig *PWMConfig) Option {
return func(p *Part) {
p.pwmSecondaryThrottleConfig = throttleConfig
p.pwmSecondarySteeringConfig = steeringConfig
}
}
func NewPart(client mqtt.Client, name string, baud int, throttleTopic, steeringTopic, driveModeTopic, func NewPart(client mqtt.Client, name string, baud int, throttleTopic, steeringTopic, driveModeTopic,
switchRecordTopic string, pubFrequency float64, steeringConfig PWMSteeringConfig, throttleConfig PWMThrottleConfig) *Part { switchRecordTopic string, pubFrequency float64, options ...Option) *Part {
c := &serial.Config{Name: name, Baud: baud} c := &serial.Config{Name: name, Baud: baud}
s, err := serial.OpenPort(c) s, err := serial.OpenPort(c)
if err != nil { if err != nil {
zap.S().Panicw("unable to open serial port: %v", err) zap.S().Panicw("unable to open serial port: %v", err)
} }
return &Part{ p := &Part{
client: client, client: client,
serial: s, serial: s,
throttleTopic: throttleTopic, throttleTopic: throttleTopic,
@ -84,9 +107,16 @@ func NewPart(client mqtt.Client, name string, baud int, throttleTopic, steeringT
driveMode: events.DriveMode_INVALID, driveMode: events.DriveMode_INVALID,
cancel: make(chan interface{}), cancel: make(chan interface{}),
pwmSteeringConfig: steeringConfig, pwmSteeringConfig: &DefaultPwmThrottle,
pwmThrottleConfig: throttleConfig, pwmSecondarySteeringConfig: &DefaultPwmThrottle,
pwmThrottleConfig: &DefaultPwmThrottle,
pwmSecondaryThrottleConfig: &DefaultPwmThrottle,
} }
for _, o := range options {
o(p)
}
return p
} }
func (a *Part) Start() error { func (a *Part) Start() error {
@ -121,6 +151,8 @@ func (a *Part) updateValues(values []string) {
a.processChannel4(values[4]) a.processChannel4(values[4])
a.processChannel5(values[5]) a.processChannel5(values[5])
a.processChannel6(values[6]) a.processChannel6(values[6])
a.processChannel7(values[7])
a.processChannel8(values[8])
} }
func (a *Part) Stop() { func (a *Part) Stop() {
@ -140,23 +172,23 @@ func (a *Part) processChannel1(v string) {
if err != nil { if err != nil {
zap.S().Errorf("invalid steering value for channel1, should be an int: %v", err) zap.S().Errorf("invalid steering value for channel1, should be an int: %v", err)
} }
a.steering = convertPwmSteeringToPercent(value, a.pwmSteeringConfig.Left, a.pwmSteeringConfig.Right, a.pwmSteeringConfig.Center) a.steering = convertPwmSteeringToPercent(value, a.pwmSteeringConfig)
} }
func convertPwmSteeringToPercent(value int, minPwm int, maxPwm int, middlePwm int) float32 { func convertPwmSteeringToPercent(value int, c *PWMConfig) float32 {
if value < minPwm { if value < c.Min {
value = minPwm value = c.Min
} else if value > maxPwm { } else if value > c.Max {
value = maxPwm value = c.Max
} }
if value == middlePwm { if value == c.Middle {
return 0. return 0.
} }
if value < middlePwm { if value < c.Middle {
return (float32(value) - float32(middlePwm)) / float32(middlePwm-minPwm) return (float32(value) - float32(c.Middle)) / float32(c.Middle-c.Min)
} }
// middle < value < max // middle < value < max
return (float32(value) - float32(middlePwm)) / float32(maxPwm-middlePwm) return (float32(value) - float32(c.Middle)) / float32(c.Max-c.Middle)
} }
func (a *Part) processChannel2(v string) { func (a *Part) processChannel2(v string) {
@ -172,11 +204,11 @@ func (a *Part) processChannel2(v string) {
} }
throttle := 0. throttle := 0.
if value > a.pwmThrottleConfig.Zero { if value > a.pwmThrottleConfig.Middle {
throttle = (float64(value) - float64(a.pwmThrottleConfig.Zero)) / float64(a.pwmThrottleConfig.Max-a.pwmThrottleConfig.Zero) throttle = (float64(value) - float64(a.pwmThrottleConfig.Middle)) / float64(a.pwmThrottleConfig.Max-a.pwmThrottleConfig.Middle)
} }
if value < a.pwmThrottleConfig.Zero { if value < a.pwmThrottleConfig.Middle {
throttle = -1. * (float64(a.pwmThrottleConfig.Zero) - float64(value)) / (float64(a.pwmThrottleConfig.Zero - a.pwmThrottleConfig.Min)) throttle = -1. * (float64(a.pwmThrottleConfig.Middle) - float64(value)) / (float64(a.pwmThrottleConfig.Middle - a.pwmThrottleConfig.Min))
} }
a.throttle = float32(throttle) a.throttle = float32(throttle)
@ -184,6 +216,11 @@ func (a *Part) processChannel2(v string) {
func (a *Part) processChannel3(v string) { func (a *Part) processChannel3(v string) {
zap.L().Debug("process new value for channel3", zap.String("value", v)) zap.L().Debug("process new value for channel3", zap.String("value", v))
value, err := strconv.Atoi(v)
if err != nil {
zap.S().Errorf("invalid throttle value for channel2, should be an int: %v", err)
}
a.useSecondaryRc = value > 1900
} }
func (a *Part) processChannel4(v string) { func (a *Part) processChannel4(v string) {
@ -218,6 +255,10 @@ func (a *Part) processChannel6(v string) {
zap.S().Errorf("invalid value for channel6 'drive-mode', should be an int: %v", err) zap.S().Errorf("invalid value for channel6 'drive-mode', should be an int: %v", err)
return return
} }
if value < 0 {
// No value, ignore it
return
}
if value > 1800 { if value > 1800 {
if a.driveMode != events.DriveMode_PILOT { if a.driveMode != events.DriveMode_PILOT {
zap.S().Infof("Update channel 6 'drive-mode' with value %v, new user_mode: %v", value, events.DriveMode_PILOT) zap.S().Infof("Update channel 6 'drive-mode' with value %v, new user_mode: %v", value, events.DriveMode_PILOT)
@ -231,6 +272,59 @@ func (a *Part) processChannel6(v string) {
} }
} }
func (a *Part) processChannel7(v string) {
zap.L().Debug("process new value for secondary steering on channel7", zap.String("value", v))
value, err := strconv.Atoi(v)
if err != nil {
zap.S().Errorf("invalid steering value for channel7, should be an int: %v", err)
}
a.secondarySteering = convertPwmSteeringToPercent(value, a.pwmSecondarySteeringConfig)
}
func (a *Part) processChannel8(v string) {
zap.L().Debug("process new throttle value on channel8", zap.String("value", v))
value, err := strconv.Atoi(v)
if err != nil {
zap.S().Errorf("invalid throttle value for channel8, should be an int: %v", err)
}
if value < a.pwmSecondaryThrottleConfig.Min {
value = a.pwmSecondaryThrottleConfig.Min
} else if value > a.pwmSecondaryThrottleConfig.Max {
value = a.pwmSecondaryThrottleConfig.Max
}
a.secondaryThrottle = ((float32(value)-float32(a.pwmSecondaryThrottleConfig.Min))/float32(a.pwmSecondaryThrottleConfig.Max-a.pwmSecondaryThrottleConfig.Min))*2.0 - 1.0
}
func (a *Part) Throttle() float32 {
a.mutex.Lock()
defer a.mutex.Unlock()
if a.useSecondaryRc {
return a.secondaryThrottle
}
return a.throttle
}
func (a *Part) Steering() float32 {
a.mutex.Lock()
defer a.mutex.Unlock()
if a.useSecondaryRc {
return a.secondarySteering
}
return a.steering
}
func (a *Part) DriveMode() events.DriveMode {
a.mutex.Lock()
defer a.mutex.Unlock()
return a.driveMode
}
func (a *Part) SwitchRecord() bool {
a.mutex.Lock()
defer a.mutex.Unlock()
return a.ctrlRecord
}
func (a *Part) publishLoop() { func (a *Part) publishLoop() {
ticker := time.NewTicker(time.Second / time.Duration(int(a.pubFrequency))) ticker := time.NewTicker(time.Second / time.Duration(int(a.pubFrequency)))
@ -246,9 +340,6 @@ func (a *Part) publishLoop() {
} }
func (a *Part) publishValues() { func (a *Part) publishValues() {
a.mutex.Lock()
defer a.mutex.Unlock()
a.publishThrottle() a.publishThrottle()
a.publishSteering() a.publishSteering()
a.publishDriveMode() a.publishDriveMode()
@ -257,7 +348,7 @@ func (a *Part) publishValues() {
func (a *Part) publishThrottle() { func (a *Part) publishThrottle() {
throttle := events.ThrottleMessage{ throttle := events.ThrottleMessage{
Throttle: a.throttle, Throttle: a.Throttle(),
Confidence: 1.0, Confidence: 1.0,
} }
throttleMessage, err := proto.Marshal(&throttle) throttleMessage, err := proto.Marshal(&throttle)
@ -271,7 +362,7 @@ func (a *Part) publishThrottle() {
func (a *Part) publishSteering() { func (a *Part) publishSteering() {
steering := events.SteeringMessage{ steering := events.SteeringMessage{
Steering: a.steering, Steering: a.Steering(),
Confidence: 1.0, Confidence: 1.0,
} }
steeringMessage, err := proto.Marshal(&steering) steeringMessage, err := proto.Marshal(&steering)
@ -285,7 +376,7 @@ func (a *Part) publishSteering() {
func (a *Part) publishDriveMode() { func (a *Part) publishDriveMode() {
dm := events.DriveModeMessage{ dm := events.DriveModeMessage{
DriveMode: a.driveMode, DriveMode: a.DriveMode(),
} }
driveModeMessage, err := proto.Marshal(&dm) driveModeMessage, err := proto.Marshal(&dm)
if err != nil { if err != nil {
@ -297,7 +388,7 @@ func (a *Part) publishDriveMode() {
func (a *Part) publishSwitchRecord() { func (a *Part) publishSwitchRecord() {
sr := events.SwitchRecordMessage{ sr := events.SwitchRecordMessage{
Enabled: !a.ctrlRecord, Enabled: !a.SwitchRecord(),
} }
switchRecordMessage, err := proto.Marshal(&sr) switchRecordMessage, err := proto.Marshal(&sr)
if err != nil { if err != nil {

View File

@ -58,11 +58,9 @@ func TestArduinoPart_Update(t *testing.T) {
} }
}() }()
defaultPwmThrottleConfig := PWMThrottleConfig{MinPwmThrottle, MaxPwmThrottle, MinPwmThrottle + (MaxPwmThrottle-MinPwmAngle)/int(2)} defaultPwmThrottleConfig := NewPWMConfig(MinPwmThrottle, MaxPwmThrottle)
a := Part{client: nil, serial: conn, pubFrequency: 100, a := Part{client: nil, serial: conn, pubFrequency: 100, pwmSteeringConfig: NewAsymetricPWMConfig(MinPwmAngle, MaxPwmAngle, MiddlePwmAngle),
pwmSteeringConfig: NewAsymetricPWMSteeringConfig(MinPwmAngle, MaxPwmAngle, MiddlePwmAngle), pwmThrottleConfig: &DefaultPwmThrottle, pwmSecondaryThrottleConfig: &DefaultPwmThrottle, pwmSecondarySteeringConfig: NewPWMConfig(MinPwmThrottle, MaxPwmThrottle)}
}
go func() { go func() {
err := a.Start() err := a.Start()
if err != nil { if err != nil {
@ -71,126 +69,129 @@ func TestArduinoPart_Update(t *testing.T) {
} }
}() }()
channel1, channel2, channel3, channel4, channel5, channel6, distanceCm := 678, 910, 1112, 1678, 1910, 112, 128 channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8 := 678, 910, 1012, 1678, 1910, 112, 0, 0
cases := []struct { cases := []struct {
name, content string name, content string
throttlePwmConfig PWMThrottleConfig throttlePwmConfig *PWMConfig
expectedThrottle, expectedSteering float32 expectedThrottle, expectedSteering float32
expectedDriveMode events.DriveMode expectedDriveMode events.DriveMode
expectedSwitchRecord bool expectedSwitchRecord bool
}{ }{
{"Good value", {"Good value",
fmt.Sprintf("12345,%d,%d,%d,%d,%d,%d,50,%d\n", channel1, channel2, channel3, channel4, channel5, channel6, distanceCm), fmt.Sprintf("12345,%d,%d,%d,%d,%d,%d,%d,%d,50\n", channel1, channel2, channel3, channel4, channel5, channel6, channel7, channel8),
defaultPwmThrottleConfig, -1., -1., defaultPwmThrottleConfig, -1., -1.,
events.DriveMode_USER, false}, events.DriveMode_USER, false},
{"Unparsable line", {"Invalid line",
"12350,invalid line\n", defaultPwmThrottleConfig, "12350,invalid line\n", defaultPwmThrottleConfig,
-1., -1., events.DriveMode_USER, false}, -1., -1., events.DriveMode_INVALID, false},
{"Switch record on", {"Switch record on",
fmt.Sprintf("12355,%d,%d,%d,%d,%d,%d,50,%d\n", channel1, channel2, channel3, channel4, 998, channel6, distanceCm), defaultPwmThrottleConfig, fmt.Sprintf("12355,%d,%d,%d,%d,%d,%d,%d,%d,50\n", channel1, channel2, channel3, channel4, 998, channel6, channel7, channel8),
-1., -1., events.DriveMode_USER, true}, defaultPwmThrottleConfig, -1., -1., events.DriveMode_USER, true},
{"Switch record off", {"Switch record off",
fmt.Sprintf("12360,%d,%d,%d,%d,%d,%d,50,%d\n", channel1, channel2, channel3, channel4, 1987, channel6, distanceCm), defaultPwmThrottleConfig, fmt.Sprintf("12360,%d,%d,%d,%d,%d,%d,%d,%d,50\n", channel1, channel2, channel3, channel4, 1987, channel6, channel7, channel8),
-1., -1., events.DriveMode_USER, false}, defaultPwmThrottleConfig, -1., -1., events.DriveMode_USER, false},
{"Switch record off", {"Switch record off",
fmt.Sprintf("12365,%d,%d,%d,%d,%d,%d,50,%d\n", channel1, channel2, channel3, channel4, 1850, channel6, distanceCm), defaultPwmThrottleConfig, fmt.Sprintf("12365,%d,%d,%d,%d,%d,%d,%d,%d,50\n", channel1, channel2, channel3, channel4, 1850, channel6, channel7, channel8),
-1., -1., events.DriveMode_USER, false}, defaultPwmThrottleConfig, -1., -1., events.DriveMode_USER, false},
{"Switch record on", {"Switch record on",
fmt.Sprintf("12370,%d,%d,%d,%d,%d,%d,50,%d\n", channel1, channel2, channel3, channel4, 1003, channel6, distanceCm), defaultPwmThrottleConfig, fmt.Sprintf("12370,%d,%d,%d,%d,%d,%d,%d,%d,50\n", channel1, channel2, channel3, channel4, 1003, channel6, channel7, channel8),
-1., -1., events.DriveMode_USER, true}, defaultPwmThrottleConfig, -1., -1., events.DriveMode_USER, true},
{"DriveMode: user", {"DriveMode: user",
fmt.Sprintf("12375,%d,%d,%d,%d,%d,%d,50,%d\n", channel1, channel2, channel3, channel4, channel5, 998, distanceCm), defaultPwmThrottleConfig, fmt.Sprintf("12375,%d,%d,%d,%d,%d,%d,%d,%d,50\n", channel1, channel2, channel3, channel4, channel5, 998, channel7, channel8),
-1., -1., events.DriveMode_USER, false}, defaultPwmThrottleConfig, -1., -1., events.DriveMode_USER, false},
{"DriveMode: pilot", {"DriveMode: pilot",
fmt.Sprintf("12380,%d,%d,%d,%d,%d,%d,50,%d\n", channel1, channel2, channel3, channel4, channel5, 1987, distanceCm), defaultPwmThrottleConfig, fmt.Sprintf("12380,%d,%d,%d,%d,%d,%d,%d,%d,50\n", channel1, channel2, channel3, channel4, channel5, 1987, channel7, channel8),
-1., -1., events.DriveMode_PILOT, false}, defaultPwmThrottleConfig, -1., -1., events.DriveMode_PILOT, false},
{"DriveMode: pilot", {"DriveMode: pilot",
fmt.Sprintf("12385,%d,%d,%d,%d,%d,%d,50,%d\n", channel1, channel2, channel3, channel4, channel5, 1850, distanceCm), defaultPwmThrottleConfig, fmt.Sprintf("12385,%d,%d,%d,%d,%d,%d,%d,%d,50\n", channel1, channel2, channel3, channel4, channel5, 1850, channel7, channel8),
-1., -1., events.DriveMode_PILOT, false}, defaultPwmThrottleConfig, -1., -1., events.DriveMode_PILOT, false},
// DriveMode: user // DriveMode: user
{"DriveMode: user", {"DriveMode: user",
fmt.Sprintf("12390,%d,%d,%d,%d,%d,%d,50,%d\n", channel1, channel2, channel3, channel4, channel5, 1003, distanceCm), fmt.Sprintf("12390,%d,%d,%d,%d,%d,%d,%d,%d,50\n", channel1, channel2, channel3, channel4, channel5, 1003, channel7, channel8),
defaultPwmThrottleConfig, defaultPwmThrottleConfig, -1., -1., events.DriveMode_USER, false},
-1., -1., events.DriveMode_USER, false},
{"Sterring: over left", {"Sterring: over left", fmt.Sprintf("12395,%d,%d,%d,%d,%d,%d,%d,%d,50\n", 99, channel2, channel3, channel4, channel5, channel6, channel7, channel8),
fmt.Sprintf("12395,%d,%d,%d,%d,%d,%d,50,%d\n", 99, channel2, channel3, channel4, channel5, channel6, distanceCm), defaultPwmThrottleConfig, -1., -1., events.DriveMode_USER, false},
defaultPwmThrottleConfig,
-1., -1., events.DriveMode_USER, false},
{"Sterring: left", {"Sterring: left",
fmt.Sprintf("12400,%d,%d,%d,%d,%d,%d,50,%d\n", int(MinPwmAngle+40), channel2, channel3, channel4, channel5, channel6, distanceCm), fmt.Sprintf("12400,%d,%d,%d,%d,%d,%d,%d,%d,50\n", int(MinPwmAngle+40), channel2, channel3, channel4, channel5, channel6, channel7, channel8),
defaultPwmThrottleConfig, defaultPwmThrottleConfig, -1., -0.92, events.DriveMode_USER, false},
-1., -0.92, events.DriveMode_USER, false},
{"Sterring: middle", {"Sterring: middle",
fmt.Sprintf("12405,%d,%d,%d,%d,%d,%d,50,%d\n", 1450, channel2, channel3, channel4, channel5, channel6, distanceCm), fmt.Sprintf("12405,%d,%d,%d,%d,%d,%d,%d,%d,50\n", 1450, channel2, channel3, channel4, channel5, channel6, channel7, channel8),
defaultPwmThrottleConfig, defaultPwmThrottleConfig, -1., -0.09, events.DriveMode_USER, false},
-1., -0.09, events.DriveMode_USER, false},
{"Sterring: right", {"Sterring: right",
fmt.Sprintf("12410,%d,%d,%d,%d,%d,%d,50,%d\n", 1958, channel2, channel3, channel4, channel5, channel6, distanceCm), fmt.Sprintf("12410,%d,%d,%d,%d,%d,%d,%d,%d,50\n", 1958, channel2, channel3, channel4, channel5, channel6, channel7, channel8),
defaultPwmThrottleConfig, defaultPwmThrottleConfig, -1., 0.95, events.DriveMode_USER, false},
-1., 0.95, events.DriveMode_USER, false},
{"Sterring: over right", {"Sterring: over right",
fmt.Sprintf("12415,%d,%d,%d,%d,%d,%d,50,%d\n", 2998, channel2, channel3, channel4, channel5, channel6, distanceCm), fmt.Sprintf("12415,%d,%d,%d,%d,%d,%d,%d,%d,50\n", 2998, channel2, channel3, channel4, channel5, channel6, channel7, channel8),
defaultPwmThrottleConfig, defaultPwmThrottleConfig, -1., 1., events.DriveMode_USER, false},
-1., 1., events.DriveMode_USER, false},
{"Throttle: over down", {"Throttle: over down",
fmt.Sprintf("12420,%d,%d,%d,%d,%d,%d,50,%d\n", channel1, 99, channel3, channel4, channel5, channel6, distanceCm), fmt.Sprintf("12420,%d,%d,%d,%d,%d,%d,%d,%d,50\n", channel1, 99, channel3, channel4, channel5, channel6, channel7, channel8),
defaultPwmThrottleConfig, defaultPwmThrottleConfig, -1., -1., events.DriveMode_USER, false},
-1., -1., events.DriveMode_USER, false},
{"Throttle: down", {"Throttle: down",
fmt.Sprintf("12425,%d,%d,%d,%d,%d,%d,50,%d\n", channel1, 998, channel3, channel4, channel5, channel6, distanceCm), fmt.Sprintf("12425,%d,%d,%d,%d,%d,%d,%d,%d,50\n", channel1, 998, channel3, channel4, channel5, channel6, channel7, channel8),
defaultPwmThrottleConfig, defaultPwmThrottleConfig, -0.95, -1., events.DriveMode_USER, false},
-0.95, -1., events.DriveMode_USER, false},
{"Throttle: stop", {"Throttle: stop",
fmt.Sprintf("12430,%d,%d,%d,%d,%d,%d,50,%d\n", channel1, 1450, channel3, channel4, channel5, channel6, distanceCm), fmt.Sprintf("12430,%d,%d,%d,%d,%d,%d,%d,%d,50\n", channel1, 1450, channel3, channel4, channel5, channel6, channel7, channel8),
defaultPwmThrottleConfig, NewPWMConfig(1000, 1900), 0.0, -1., events.DriveMode_USER, false},
0.0, -1., events.DriveMode_USER, false},
{"Throttle: up", {"Throttle: up",
fmt.Sprintf("12435,%d,%d,%d,%d,%d,%d,50,%d\n", channel1, 1948, channel3, channel4, channel5, channel6, distanceCm), fmt.Sprintf("12435,%d,%d,%d,%d,%d,%d,%d,%d,50\n", channel1, 1948, channel3, channel4, channel5, channel6, channel7, channel8),
defaultPwmThrottleConfig, defaultPwmThrottleConfig, 0.99, -1., events.DriveMode_USER, false},
0.99, -1., events.DriveMode_USER, false},
{"Throttle: over up", {"Throttle: over up",
fmt.Sprintf("12440,%d,%d,%d,%d,%d,%d,50,%d\n", channel1, 2998, channel3, channel4, channel5, channel6, distanceCm), fmt.Sprintf("12440,%d,%d,%d,%d,%d,%d,%d,%d,50\n", channel1, 2998, channel3, channel4, channel5, channel6, channel7, channel8),
defaultPwmThrottleConfig, defaultPwmThrottleConfig, 1., -1., events.DriveMode_USER, false},
1., -1., events.DriveMode_USER, false},
{"Throttle: zero not middle", {"Throttle: zero not middle",
fmt.Sprintf("12440,%d,%d,%d,%d,%d,%d,50,%d\n", channel1, 1600, channel3, channel4, channel5, channel6, distanceCm), fmt.Sprintf("12440,%d,%d,%d,%d,%d,%d,%d,%d,50\n", channel1, 1600, channel3, channel4, channel5, channel6, channel7, channel8),
PWMThrottleConfig{1000, 1700, 1500}, &PWMConfig{1000, 1700, 1500},
0.5, -1., events.DriveMode_USER, false}, 0.5, -1., events.DriveMode_USER, false},
{"Use 2nd rc: use channels 7 and 8",
fmt.Sprintf("12440,%d,%d,%d,%d,%d,%d,%d,%d,50\n", 1000, 1000, 1950, channel4, channel5, channel6, 2000, 2008),
defaultPwmThrottleConfig, 1., 1, events.DriveMode_USER, false},
{"Drive Mode: user",
fmt.Sprintf("12430,%d,%d,%d,%d,%d,%d,%d,%d,50\n", channel1, channel6, channel3, channel4, channel5, 900, channel7, channel8),
defaultPwmThrottleConfig, -1., -1., events.DriveMode_USER, false},
{"Drive Mode: pilot",
fmt.Sprintf("12430,%d,%d,%d,%d,%d,%d,%d,%d,50\n", channel1, channel6, channel3, channel4, channel5, 1950, channel7, channel8),
defaultPwmThrottleConfig, -1., -1., events.DriveMode_PILOT, false},
{"Drive Mode: no value",
fmt.Sprintf("12430,%d,%d,%d,%d,%d,%d,%d,%d,50\n", channel1, channel6, channel3, channel4, channel5, -1, channel7, channel8),
defaultPwmThrottleConfig, -1., -1., events.DriveMode_INVALID, false},
} }
for _, c := range cases { for _, c := range cases {
w := bufio.NewWriter(serialClient) t.Run(c.name, func(t *testing.T) {
_, err := w.WriteString(c.content) w := bufio.NewWriter(serialClient)
if err != nil { _, err := w.WriteString(c.content)
t.Errorf("unable to send test content: %v", c.content) if err != nil {
} t.Errorf("unable to send test content: %v", c.content)
err = w.Flush() }
if err != nil { err = w.Flush()
t.Error("unable to flush content") if err != nil {
} t.Error("unable to flush content")
}
a.pwmThrottleConfig = c.throttlePwmConfig a.pwmThrottleConfig = c.throttlePwmConfig
a.driveMode = events.DriveMode_INVALID
time.Sleep(10 * time.Millisecond) time.Sleep(10 * time.Millisecond)
a.mutex.Lock() a.mutex.Lock()
if fmt.Sprintf("%0.2f", a.throttle) != fmt.Sprintf("%0.2f", c.expectedThrottle) { a.mutex.Unlock()
t.Errorf("%s: bad throttle value, expected: %0.2f, actual: %.2f", c.name, c.expectedThrottle, a.throttle) if fmt.Sprintf("%0.2f", a.Throttle()) != fmt.Sprintf("%0.2f", c.expectedThrottle) {
} t.Errorf("%s: bad throttle value, expected: %0.2f, actual: %.2f", c.name, c.expectedThrottle, a.Throttle())
if fmt.Sprintf("%0.2f", a.steering) != fmt.Sprintf("%0.2f", c.expectedSteering) { }
t.Errorf("%s: bad steering value, expected: %0.2f, actual: %.2f", c.name, c.expectedSteering, a.steering) if fmt.Sprintf("%0.2f", a.Steering()) != fmt.Sprintf("%0.2f", c.expectedSteering) {
} t.Errorf("%s: bad steering value, expected: %0.2f, actual: %.2f", c.name, c.expectedSteering, a.Steering())
if a.driveMode != c.expectedDriveMode { }
t.Errorf("%s: bad drive mode, expected: %v, actual:%v", c.name, c.expectedDriveMode, a.driveMode) if a.DriveMode() != c.expectedDriveMode {
} t.Errorf("%s: bad drive mode, expected: %v, actual:%v", c.name, c.expectedDriveMode, a.DriveMode())
if a.ctrlRecord != c.expectedSwitchRecord { }
t.Errorf("%s: bad switch record, expected: %v, actual:%v", c.name, c.expectedSwitchRecord, a.ctrlRecord) if a.SwitchRecord() != c.expectedSwitchRecord {
} t.Errorf("%s: bad switch record, expected: %v, actual:%v", c.name, c.expectedSwitchRecord, a.SwitchRecord())
a.mutex.Unlock() }
})
} }
} }
@ -321,10 +322,8 @@ func unmarshalMsg(t *testing.T, payload []byte, msg proto.Message) {
func Test_convertPwmSteeringToPercent(t *testing.T) { func Test_convertPwmSteeringToPercent(t *testing.T) {
type args struct { type args struct {
value int value int
middle int steeringConfig *PWMConfig
min int
max int
} }
tests := []struct { tests := []struct {
name string name string
@ -334,127 +333,151 @@ func Test_convertPwmSteeringToPercent(t *testing.T) {
{ {
name: "middle", name: "middle",
args: args{ args: args{
value: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle, value: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle,
middle: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle, steeringConfig: &PWMConfig{
min: MinPwmAngle, Middle: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle,
max: MaxPwmAngle, Min: MinPwmAngle,
Max: MaxPwmAngle,
},
}, },
want: 0., want: 0.,
}, },
{ {
name: "left", name: "left",
args: args{ args: args{
value: MinPwmAngle, value: MinPwmAngle,
middle: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle, steeringConfig: &PWMConfig{
min: MinPwmAngle, Middle: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle,
max: MaxPwmAngle, Min: MinPwmAngle,
Max: MaxPwmAngle,
},
}, },
want: -1., want: -1.,
}, },
{ {
name: "mid-left", name: "mid-left",
args: args{ args: args{
value: int(math.Round((MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle - (MaxPwmAngle-MinPwmAngle)/4)), value: int(math.Round((MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle - (MaxPwmAngle-MinPwmAngle)/4)),
middle: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle, steeringConfig: &PWMConfig{
min: MinPwmAngle, Middle: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle,
max: MaxPwmAngle, Min: MinPwmAngle,
Max: MaxPwmAngle,
},
}, },
want: -0.4989858, want: -0.4989858,
}, },
{ {
name: "over left", name: "over left",
args: args{ args: args{
value: MinPwmAngle - 100, value: MinPwmAngle - 100,
middle: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle, steeringConfig: &PWMConfig{
min: MinPwmAngle, Middle: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle,
max: MaxPwmAngle, Min: MinPwmAngle,
Max: MaxPwmAngle,
},
}, },
want: -1., want: -1.,
}, },
{ {
name: "right", name: "right",
args: args{ args: args{
value: MaxPwmAngle, value: MaxPwmAngle,
middle: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle, steeringConfig: &PWMConfig{
min: MinPwmAngle, Middle: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle,
max: MaxPwmAngle, Min: MinPwmAngle,
Max: MaxPwmAngle,
},
}, },
want: 1., want: 1.,
}, },
{ {
name: "mid-right", name: "mid-right",
args: args{ args: args{
value: int(math.Round((MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle + (MaxPwmAngle-MinPwmAngle)/4)), value: int(math.Round((MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle + (MaxPwmAngle-MinPwmAngle)/4)),
middle: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle, steeringConfig: &PWMConfig{
min: MinPwmAngle, Middle: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle,
max: MaxPwmAngle, Min: MinPwmAngle,
Max: MaxPwmAngle,
},
}, },
want: 0.5010142, want: 0.5010142,
}, },
{ {
name: "over right", name: "over right",
args: args{ args: args{
value: MaxPwmAngle + 100, value: MaxPwmAngle + 100,
middle: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle, steeringConfig: &PWMConfig{
min: MinPwmAngle, Middle: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle,
max: MaxPwmAngle, Min: MinPwmAngle,
Max: MaxPwmAngle,
},
}, },
want: 1., want: 1.,
}, },
{ {
name: "asymetric middle", name: "asymetric middle",
args: args{ args: args{
value: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle + 100, value: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle + 100,
middle: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle + 100, steeringConfig: &PWMConfig{
min: MinPwmAngle, Middle: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle + 100,
max: MaxPwmAngle, Min: MinPwmAngle,
Max: MaxPwmAngle,
},
}, },
want: 0., want: 0.,
}, },
{ {
name: "asymetric mid-left", name: "asymetric mid-left",
args: args{ args: args{
value: int(math.Round(((MaxPwmAngle-MinPwmAngle)/2+MinPwmAngle+100-MinPwmAngle)/2) + MinPwmAngle), value: int(math.Round(((MaxPwmAngle-MinPwmAngle)/2+MinPwmAngle+100-MinPwmAngle)/2) + MinPwmAngle),
middle: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle + 100, steeringConfig: &PWMConfig{
min: MinPwmAngle, Middle: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle + 100,
max: MaxPwmAngle, Min: MinPwmAngle,
Max: MaxPwmAngle,
},
}, },
want: -0.49915683, want: -0.49915683,
}, },
{ {
name: "asymetric left", name: "asymetric left",
args: args{ args: args{
value: MinPwmAngle, value: MinPwmAngle,
middle: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle + 100, steeringConfig: &PWMConfig{
min: MinPwmAngle, Middle: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle + 100,
max: MaxPwmAngle, Min: MinPwmAngle,
Max: MaxPwmAngle,
},
}, },
want: -1., want: -1.,
}, },
{ {
name: "asymetric mid-right", name: "asymetric mid-right",
args: args{ args: args{
value: int(math.Round((MaxPwmAngle - (MaxPwmAngle-((MaxPwmAngle-MinPwmAngle)/2+MinPwmAngle+100))/2))), value: int(math.Round((MaxPwmAngle - (MaxPwmAngle-((MaxPwmAngle-MinPwmAngle)/2+MinPwmAngle+100))/2))),
middle: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle + 100, steeringConfig: &PWMConfig{
min: MinPwmAngle, Middle: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle + 100,
max: MaxPwmAngle, Min: MinPwmAngle,
Max: MaxPwmAngle,
},
}, },
want: 0.50127226, want: 0.50127226,
}, },
{ {
name: "asymetric right", name: "asymetric right",
args: args{ args: args{
value: MaxPwmAngle, value: MaxPwmAngle,
middle: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle + 100, steeringConfig: &PWMConfig{
min: MinPwmAngle, Middle: (MaxPwmAngle-MinPwmAngle)/2 + MinPwmAngle + 100,
max: MaxPwmAngle, Min: MinPwmAngle,
Max: MaxPwmAngle,
},
}, },
want: 1., want: 1.,
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
if got := convertPwmSteeringToPercent(tt.args.value, tt.args.min, tt.args.max, tt.args.middle); got != tt.want { if got := convertPwmSteeringToPercent(tt.args.value, tt.args.steeringConfig); got != tt.want {
t.Errorf("convertPwmSteeringToPercent() = %v, want %v", got, tt.want) t.Errorf("convertPwmSteeringToPercent() = %v, want %v", got, tt.want)
} }
}) })