First implementation
This commit is contained in:
		
							
								
								
									
										118
									
								
								part/part.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								part/part.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,118 @@
 | 
			
		||||
package part
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/cyrilix/robocar-base/mode"
 | 
			
		||||
	"github.com/cyrilix/robocar-base/mqttdevice"
 | 
			
		||||
	"github.com/cyrilix/robocar-led/led"
 | 
			
		||||
	mqtt "github.com/eclipse/paho.mqtt.golang"
 | 
			
		||||
	"log"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func NewPart(client mqtt.Client, driveModeTopic, recordTopic string) *LedPart {
 | 
			
		||||
	return &LedPart{
 | 
			
		||||
		led:              led.New(),
 | 
			
		||||
		client:           client,
 | 
			
		||||
		onDriveModeTopic: driveModeTopic,
 | 
			
		||||
		onRecordTopic:    recordTopic,
 | 
			
		||||
		muDriveMode:      sync.Mutex{},
 | 
			
		||||
		m:                mode.DriveModeInvalid,
 | 
			
		||||
		muRecord:         sync.Mutex{},
 | 
			
		||||
		recordEnabled:    false,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type LedPart struct {
 | 
			
		||||
	led              led.ColoredLed
 | 
			
		||||
	client           mqtt.Client
 | 
			
		||||
	onDriveModeTopic string
 | 
			
		||||
	onRecordTopic    string
 | 
			
		||||
 | 
			
		||||
	muDriveMode   sync.Mutex
 | 
			
		||||
	m             mode.DriveMode
 | 
			
		||||
	muRecord      sync.Mutex
 | 
			
		||||
	recordEnabled bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *LedPart) Start() error {
 | 
			
		||||
	if err := p.registerCallbacks(); err != nil {
 | 
			
		||||
		return fmt.Errorf("unable to start service: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer p.Stop()
 | 
			
		||||
	for {
 | 
			
		||||
		time.Sleep(1 * time.Hour)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *LedPart) Stop() {
 | 
			
		||||
	StopService("led", p.client, p.onDriveModeTopic, p.onRecordTopic)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *LedPart) onDriveMode(_ mqtt.Client, message mqtt.Message) {
 | 
			
		||||
	mqttValue := mqttdevice.NewMqttValue(message.Payload())
 | 
			
		||||
	m, err := mqttValue.IntValue()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Printf("unable to convert message payload '%v' to DriveMode: %v", message.Payload(), err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	switch m {
 | 
			
		||||
	case mode.DriveModeUser:
 | 
			
		||||
		p.led.SetRed(0)
 | 
			
		||||
		p.led.SetGreen(255)
 | 
			
		||||
		p.led.SetBlue(0)
 | 
			
		||||
	case mode.DriveModePilot:
 | 
			
		||||
		p.led.SetRed(0)
 | 
			
		||||
		p.led.SetGreen(0)
 | 
			
		||||
		p.led.SetBlue(255)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *LedPart) onRecord(client mqtt.Client, message mqtt.Message) {
 | 
			
		||||
	mqttValue := mqttdevice.NewMqttValue(message.Payload())
 | 
			
		||||
	rec, err := mqttValue.BoolValue()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Printf("unable to convert message payload '%v' to bool: %v", message.Payload(), err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if rec {
 | 
			
		||||
		p.led.SetBlink(2)
 | 
			
		||||
	} else {
 | 
			
		||||
		p.led.SetBlink(0)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *LedPart) registerCallbacks() error {
 | 
			
		||||
	err := RegisterCallback(p.client, p.onDriveModeTopic, p.onDriveMode)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = RegisterCallback(p.client, p.onRecordTopic, p.onRecord)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func StopService(name string, client mqtt.Client, topics ...string) {
 | 
			
		||||
	log.Printf("Stop %s service", name)
 | 
			
		||||
	token := client.Unsubscribe(topics...)
 | 
			
		||||
	token.Wait()
 | 
			
		||||
	if token.Error() != nil {
 | 
			
		||||
		log.Printf("unable to unsubscribe service: %v", token.Error())
 | 
			
		||||
	}
 | 
			
		||||
	client.Disconnect(50)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func RegisterCallback(client mqtt.Client, topic string, callback mqtt.MessageHandler) error {
 | 
			
		||||
	log.Printf("Register callback on topic %v", topic)
 | 
			
		||||
	token := client.Subscribe(topic, 0, callback)
 | 
			
		||||
	token.Wait()
 | 
			
		||||
	if token.Error() != nil {
 | 
			
		||||
		return fmt.Errorf("unable to register callback on topic %s: %v", topic, token.Error())
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										105
									
								
								part/part_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								part/part_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,105 @@
 | 
			
		||||
package part
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/cyrilix/robocar-base/mode"
 | 
			
		||||
	"github.com/cyrilix/robocar-base/mqttdevice"
 | 
			
		||||
	"github.com/cyrilix/robocar-base/testtools"
 | 
			
		||||
	mqtt "github.com/eclipse/paho.mqtt.golang"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type fakeLed struct {
 | 
			
		||||
	red, green, blue int
 | 
			
		||||
	blink            bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *fakeLed) SetBlink(freq float64) {
 | 
			
		||||
	if freq > 0 {
 | 
			
		||||
		f.blink = true
 | 
			
		||||
	} else {
 | 
			
		||||
		f.blink = false
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *fakeLed) SetRed(value int) {
 | 
			
		||||
	f.red = value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *fakeLed) SetGreen(value int) {
 | 
			
		||||
	f.green = value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *fakeLed) SetBlue(value int) {
 | 
			
		||||
	f.blue = value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestLedPart_OnDriveMode(t *testing.T) {
 | 
			
		||||
	led := fakeLed{}
 | 
			
		||||
	p := LedPart{led: &led}
 | 
			
		||||
 | 
			
		||||
	cases := []struct {
 | 
			
		||||
		msg              mqtt.Message
 | 
			
		||||
		red, green, blue int
 | 
			
		||||
	}{
 | 
			
		||||
		{testtools.NewFakeMessage("drive", mqttdevice.NewMqttValue(mode.DriveModeUser)), 0, 255, 0},
 | 
			
		||||
		{testtools.NewFakeMessage("drive", mqttdevice.NewMqttValue(mode.DriveModePilot)), 0, 0, 255},
 | 
			
		||||
		{testtools.NewFakeMessage("drive", mqttdevice.NewMqttValue(mode.DriveModeInvalid)), 0, 0, 255},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, c := range cases {
 | 
			
		||||
		p.onDriveMode(nil, c.msg)
 | 
			
		||||
		time.Sleep(1 * time.Millisecond)
 | 
			
		||||
		if led.red != c.red {
 | 
			
		||||
			payload := mqttdevice.NewMqttValue(c.msg.Payload())
 | 
			
		||||
			value, err := payload.IntValue()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Errorf("payload isn't a led value: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			t.Errorf("driveMode(%v)=invalid value for red channel: %v, wants %v", value, led.red, c.red)
 | 
			
		||||
		}
 | 
			
		||||
		if led.green != c.green {
 | 
			
		||||
			payload := mqttdevice.NewMqttValue(c.msg.Payload())
 | 
			
		||||
			value, err := payload.IntValue()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Errorf("payload isn't a led value: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			t.Errorf("driveMode(%v)=invalid value for green channel: %v, wants %v", value, led.green, c.green)
 | 
			
		||||
		}
 | 
			
		||||
		if led.blue != c.blue {
 | 
			
		||||
			payload := mqttdevice.NewMqttValue(c.msg.Payload())
 | 
			
		||||
			value, err := payload.IntValue()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Errorf("payload isn't a led value: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			t.Errorf("driveMode(%v)=invalid value for blue channel: %v, wants %v", value, led.blue, c.blue)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
func TestLedPart_OnRecord(t *testing.T) {
 | 
			
		||||
	led := fakeLed{}
 | 
			
		||||
	p := LedPart{led: &led}
 | 
			
		||||
 | 
			
		||||
	cases := []struct {
 | 
			
		||||
		msg    mqtt.Message
 | 
			
		||||
		record bool
 | 
			
		||||
		blink  bool
 | 
			
		||||
	}{
 | 
			
		||||
		{testtools.NewFakeMessage("record", mqttdevice.NewMqttValue(false)), true, false},
 | 
			
		||||
		{testtools.NewFakeMessage("record", mqttdevice.NewMqttValue(true)), false, true},
 | 
			
		||||
		{testtools.NewFakeMessage("record", mqttdevice.NewMqttValue(false)), true, false},
 | 
			
		||||
		{testtools.NewFakeMessage("record", mqttdevice.NewMqttValue(true)), false, true},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, c := range cases {
 | 
			
		||||
		p.onRecord(nil, c.msg)
 | 
			
		||||
		if led.blink != c.blink {
 | 
			
		||||
			payload := mqttdevice.NewMqttValue(c.msg.Payload())
 | 
			
		||||
			value, err := payload.BoolValue()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Errorf("payload isn't a bool value: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			t.Errorf("onRecord(%v): %v, wants %v", value, c.record, led.blink)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user