From 843ada835713fe8ed109214e02b64ba3b0b8e201 Mon Sep 17 00:00:00 2001 From: Cyrille Nofficial Date: Sun, 29 Nov 2020 18:42:33 +0100 Subject: [PATCH] Implements Gateway for car controls --- camera/camera.go | 53 +++---- camera/camera_test.go | 118 +++++++++------- camera/testdata/msg.json | 4 +- controls/controls.go | 123 +++++++++++++++++ controls/controls_test.go | 281 ++++++++++++++++++++++++++++++++++++++ simulator/simulator.go.go | 10 +- 6 files changed, 511 insertions(+), 78 deletions(-) create mode 100644 controls/controls.go create mode 100644 controls/controls_test.go diff --git a/camera/camera.go b/camera/camera.go index 14be013..04f5219 100644 --- a/camera/camera.go +++ b/camera/camera.go @@ -17,28 +17,31 @@ import ( ) func New(publisher Publisher, addressSimulator string) *Gateway { - log.Info("run camera camera") + l := log.WithField("simulator", addressSimulator) + l.Info("run camera from simulator") return &Gateway{ - address: addressSimulator, - publisher: publisher, + address: addressSimulator, + publisher: publisher, + log: l, } } /* Simulator interface to publish camera frames into mqtt topic */ type Gateway struct { - cancel chan interface{} + cancel chan interface{} address string conn io.ReadCloser publisher Publisher + log *log.Entry } func (p *Gateway) Start() error { - log.Info("connect to simulator") + p.log.Info("connect to simulator") p.cancel = make(chan interface{}) - msgChan := make(chan *simulator.SimulatorMsg) + msgChan := make(chan *simulator.TelemetryMsg) go p.run(msgChan) @@ -53,17 +56,17 @@ func (p *Gateway) Start() error { } func (p *Gateway) Stop() { - log.Info("close simulator gateway") + p.log.Info("close simulator gateway") close(p.cancel) if err := p.Close(); err != nil { - log.Printf("unexpected error while simulator connection is closed: %v", err) + p.log.Warnf("unexpected error while simulator connection is closed: %v", err) } } func (p *Gateway) Close() error { if p.conn == nil { - log.Warnln("no connection to close") + p.log.Warn("no connection to close") return nil } if err := p.conn.Close(); err != nil { @@ -72,19 +75,21 @@ func (p *Gateway) Close() error { return nil } -func (p *Gateway) run(msgChan chan<- *simulator.SimulatorMsg) { +func (p *Gateway) run(msgChan chan<- *simulator.TelemetryMsg) { err := retry.Do(func() error { + p.log.Info("connect to simulator") conn, err := connect(p.address) if err != nil { return fmt.Errorf("unable to connect to simulator at %v", p.address) } p.conn = conn + p.log.Info("connection success") return nil }, retry.Delay(1*time.Second), ) if err != nil { - log.Panicf("unable to connect to simulator: %v", err) + p.log.Panicf("unable to connect to simulator: %v", err) } reader := bufio.NewReader(p.conn) @@ -93,25 +98,25 @@ func (p *Gateway) run(msgChan chan<- *simulator.SimulatorMsg) { func() error { return p.listen(msgChan, reader) }, ) if err != nil { - log.Errorf("unable to connect to server: %v", err) + p.log.Errorf("unable to connect to server: %v", err) } } -func (p *Gateway) listen(msgChan chan<- *simulator.SimulatorMsg, reader *bufio.Reader) error { +func (p *Gateway) listen(msgChan chan<- *simulator.TelemetryMsg, reader *bufio.Reader) error { for { rawLine, err := reader.ReadBytes('\n') if err == io.EOF { - log.Info("Connection closed") + p.log.Info("Connection closed") return err } if err != nil { return fmt.Errorf("unable to read response: %v", err) } - var msg simulator.SimulatorMsg + var msg simulator.TelemetryMsg err = json.Unmarshal(rawLine, &msg) if err != nil { - log.Errorf("unable to unmarshal simulator msg: %v", err) + p.log.Errorf("unable to unmarshal simulator msg: %v", err) } if "telemetry" != msg.MsgType { continue @@ -120,7 +125,7 @@ func (p *Gateway) listen(msgChan chan<- *simulator.SimulatorMsg, reader *bufio.R } } -func (p *Gateway) publishFrame(msgSim *simulator.SimulatorMsg) { +func (p *Gateway) publishFrame(msgSim *simulator.TelemetryMsg) { now := time.Now() msg := &events.FrameMessage{ Id: &events.FrameRef{ @@ -136,9 +141,9 @@ func (p *Gateway) publishFrame(msgSim *simulator.SimulatorMsg) { payload, err := proto.Marshal(msg) if err != nil { - log.Errorf("unable to marshal protobuf message: %v", err) + p.log.Errorf("unable to marshal protobuf message: %v", err) } - p.publisher.Publish(&payload) + p.publisher.Publish(payload) } var connect = func(address string) (io.ReadWriteCloser, error) { @@ -150,23 +155,23 @@ var connect = func(address string) (io.ReadWriteCloser, error) { } type Publisher interface { - Publish(payload *[]byte) + Publish(payload []byte) } func NewMqttPublisher(client mqtt.Client, topic string) Publisher { return &MqttPublisher{ client: client, - topic: topic, + topic: topic, } } type MqttPublisher struct { client mqtt.Client - topic string + topic string } -func (m *MqttPublisher) Publish(payload *[]byte) { - token := m.client.Publish(m.topic, 0, false, *payload) +func (m *MqttPublisher) Publish(payload []byte) { + token := m.client.Publish(m.topic, 0, false, payload) token.WaitTimeout(10 * time.Millisecond) if err := token.Error(); err != nil { log.Errorf("unable to publish frame: %v", err) diff --git a/camera/camera_test.go b/camera/camera_test.go index 5f704dc..486bd1c 100644 --- a/camera/camera_test.go +++ b/camera/camera_test.go @@ -12,44 +12,44 @@ import ( "strings" "sync" "testing" - "time" ) type MockPublisher struct { - muPubEvents sync.Mutex - publishedEvents []*[]byte + notifyChan chan []byte + initNotifyChan sync.Once } -func (p *MockPublisher) Publish(payload *[]byte) { - p.muPubEvents.Lock() - defer p.muPubEvents.Unlock() - p.publishedEvents = append(p.publishedEvents, payload) +func (p *MockPublisher) Close() error { + if p.notifyChan != nil { + close(p.notifyChan) + } + return nil } -func (p *MockPublisher) Events() []*[]byte { - p.muPubEvents.Lock() - defer p.muPubEvents.Unlock() - eventsMsg := make([]*[]byte, len(p.publishedEvents), len(p.publishedEvents)) - copy(eventsMsg, p.publishedEvents) - return eventsMsg +func (p *MockPublisher) Publish(payload []byte) { + p.notifyChan <- payload +} + +func (p *MockPublisher) Notify() <-chan []byte { + p.initNotifyChan.Do(func() { p.notifyChan = make(chan []byte) }) + return p.notifyChan } func TestPart_ListenEvents(t *testing.T) { - connMock := ConnMock{} - err := connMock.Listen() + simulatorMock := SimulatorMock{} + err := simulatorMock.Start() if err != nil { t.Errorf("unable to start mock server: %v", err) } defer func() { - if err := connMock.Close(); err != nil { + if err := simulatorMock.Close(); err != nil { t.Errorf("unable to close mock server: %v", err) } }() + publisher := MockPublisher{} - publisher := MockPublisher{publishedEvents: make([]*[]byte, 0)} - - part := New(&publisher, connMock.Addr()) + part := New(&publisher, simulatorMock.Addr()) go func() { err := part.Start() if err != nil { @@ -62,25 +62,20 @@ func TestPart_ListenEvents(t *testing.T) { } }() + simulatorMock.WaitConnection() + log.Trace("read test data") testContent, err := ioutil.ReadFile("testdata/msg.json") lines := strings.Split(string(testContent), "\n") for idx, line := range lines { - time.Sleep(5 * time.Millisecond) - err = connMock.WriteMsg(line) + err = simulatorMock.EmitMsg(line) if err != nil { t.Errorf("[line %v/%v] unable to write line: %v", idx+1, len(lines), err) } - } - time.Sleep(10 * time.Millisecond) - expectedFrames := 16 - if len(publisher.Events()) != expectedFrames { - t.Errorf("invalid number of frame emmitted: %v, wants %v", len(publisher.Events()), expectedFrames) - } - for _, byteMsg := range publisher.Events() { + byteMsg := <-publisher.Notify() var msg events.FrameMessage - err = proto.Unmarshal(*byteMsg, &msg) + err = proto.Unmarshal(byteMsg, &msg) if err != nil { t.Errorf("unable to unmarshal frame msg: %v", err) continue @@ -94,57 +89,80 @@ func TestPart_ListenEvents(t *testing.T) { } } -type ConnMock struct { - ln net.Listener - conn net.Conn - muWriter sync.Mutex - writer *bufio.Writer +type SimulatorMock struct { + ln net.Listener + muConn sync.Mutex + conn net.Conn + writer *bufio.Writer + newConnection chan net.Conn + logger *log.Entry } -func (c *ConnMock) WriteMsg(p string) (err error) { - c.muWriter.Lock() - defer c.muWriter.Unlock() +func (c *SimulatorMock) EmitMsg(p string) (err error) { + c.muConn.Lock() + defer c.muConn.Unlock() _, err = c.writer.WriteString(p + "\n") if err != nil { - log.Errorf("unable to write response: %v", err) + c.logger.Errorf("unable to write response: %v", err) } if err == io.EOF { - log.Info("Connection closed") + c.logger.Info("Connection closed") return err } err = c.writer.Flush() return err } -func (c *ConnMock) Listen() error { - c.muWriter.Lock() - defer c.muWriter.Unlock() +func (c *SimulatorMock) WaitConnection() { + c.muConn.Lock() + defer c.muConn.Unlock() + c.logger.Debug("simulator waiting connection") + if c.conn != nil { + return + } + c.logger.Debug("new connection") + conn := <-c.newConnection + + c.conn = conn + c.writer = bufio.NewWriter(conn) +} + +func (c *SimulatorMock) Start() error { + c.logger = log.WithField("simulator", "mock") + c.newConnection = make(chan net.Conn) ln, err := net.Listen("tcp", "127.0.0.1:") c.ln = ln if err != nil { - return fmt.Errorf("unable to listen on port: %v", err) } - go func() { for { - c.conn, err = c.ln.Accept() + conn, err := c.ln.Accept() if err != nil && err == io.EOF { - log.Infof("connection close: %v", err) + c.logger.Errorf("connection close: %v", err) break } - c.writer = bufio.NewWriter(c.conn) + if c.newConnection == nil { + break + } + c.newConnection <- conn } }() return nil } -func (c *ConnMock) Addr() string { +func (c *SimulatorMock) Addr() string { return c.ln.Addr().String() } -func (c *ConnMock) Close() error { - log.Infof("close mock server") +func (c *SimulatorMock) Close() error { + c.logger.Debug("close mock server") + + if c == nil { + return nil + } + close(c.newConnection) + c.newConnection = nil err := c.ln.Close() if err != nil { return fmt.Errorf("unable to close mock server: %v", err) diff --git a/camera/testdata/msg.json b/camera/testdata/msg.json index 87376ff..aeca6c5 100644 --- a/camera/testdata/msg.json +++ b/camera/testdata/msg.json @@ -1,4 +1,3 @@ -{"msg_type":"car_loaded"} {"msg_type":"telemetry","steering_angle":0,"throttle":0,"speed":0.2596469,"image":"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","hit":"none","pos_x":50.00034,"pos_y":0.7937839,"pos_z":49.99669,"time":1201.61,"cte":0.0006424509} {"msg_type":"telemetry","steering_angle":0,"throttle":0,"speed":0.1318146,"image":"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","hit":"none","pos_x":50.00065,"pos_y":0.7065881,"pos_z":49.99732,"time":1201.943,"cte":0.000949396} {"msg_type":"telemetry","steering_angle":0,"throttle":0,"speed":0.1463221,"image":"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","hit":"none","pos_x":50.00129,"pos_y":0.7099024,"pos_z":50.00373,"time":1201.987,"cte":0.001561815} @@ -14,5 +13,4 @@ {"msg_type":"telemetry","steering_angle":0,"throttle":0,"speed":0.01692938,"image":"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","hit":"none","pos_x":50.00051,"pos_y":0.7401103,"pos_z":49.99978,"time":1203.817,"cte":0.0007964625} {"msg_type":"telemetry","steering_angle":0,"throttle":0,"speed":0.01101699,"image":"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","hit":"none","pos_x":50.00056,"pos_y":0.7397854,"pos_z":49.99924,"time":1204.15,"cte":0.0008554289} {"msg_type":"telemetry","steering_angle":0,"throttle":0,"speed":0.01104343,"image":"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","hit":"none","pos_x":50.0006,"pos_y":0.7395392,"pos_z":49.99965,"time":1204.181,"cte":0.0008846212} -{} -{"msg_type":"telemetry","steering_angle":0,"throttle":0,"speed":0.006377342,"image":"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","hit":"none","pos_x":50.00059,"pos_y":0.7391969,"pos_z":50.00017,"time":1204.514,"cte":0.0008791223} +{"msg_type":"telemetry","steering_angle":0,"throttle":0,"speed":0.006377342,"image":"/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAB4AKADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDyPexYBzvwRhwcMPxq4qtJDtfMgIxno3ofrVN1KOA5DoTww6//AK60bddvb/PT+lSy46mZPYyQ52DzY+BgDJBqCKR4huRsqMZ9vwrTmxHIHQlGOV39SccHI71WlhjlG47YnPAZfuMf6VSfclrsbel+Jdb0jTL3TbDUWisL9WW5g2q6uGUqSMg7SQcEjBOB6Cqt/rGpat5R1PVbm/8AKJ8v7RO0vl5xnGT3wOmOgrPuEkQRNlQ4IGM8c/8A6qvaFd6aur2r6tbTT2Cyg3MEL7XZe+D/APqz0yuchAysCNzMSSR14xVi0sbnULxLS0tri5nfJSKFC5fjJwo54GT+FdBrWoeE59Ie30nSry1vYZEaG9kJY3MfO5ZU3kRtypyuQxXogOBjaPqd3oerWup2JKXFrIJI8scNjqrYIO0jIIzyDQA+40HWI7NryfSL+OzjYxvM8DqiMG2kEkYB3DbjPXjrWdwVQ7evT2rvtb+J11rGm6vpcWlWken6jN5vkySOxgbC52FSo5kXzOnLO2QcnPBZDEDkncSTzj86A1HHD43H5uuc5poxtJO7njmvSJviu2qapaXfiHw5pmpR28EsUcONqbpGjO75w/IEeBgfxGvO5ZHmnaeQKHdi7eWiooY+iqAAOegAAoAiQlAMNjYcgEYx6f41p2PiLXdPKrZ6zdReWrrGEnb92GYMwHoGIBOOpAz0rptB8SeErWLTIdU8PzSR2i5nkWKKVrpsliCCqEAuVOSzELEExiR8v8J+Db/4ieIri/eGGw0vzM3MlrCI0T0iiUDG7Hc5x95skgMAN+Gfgq18X67Il9dqlnaATS2wfEtwCcAA9QucbmHPIAxuyO6+I/xITSUbwv4XdIZogIbi5gAVbYAY8qLHAfAwSPudB833fNIPFuoaXqWuL4Zb+z7PVbguFjiVZEi3SbEGMhABJ/DyCo2kd8VUSLIVhJIp5wMAD0ouO2pXuJJJXkuLqV5HlYu7SNuZ2JySxPUk00ySTfIu5Y88AcMR7064CsyqWxjnA5JqaC2mkwEHlp3x1P40ge41I4osI/zHr5af596nEMtwuJPliwfkX+p/GrcdtBax7nOB6nv/AI9/yqN7wupMMDNzncwwOP50CMqFGljKSHK8cOeB+NTxBfKDKckcH/P1ArS07UGgTyJIo5oc4IkXr6etJem1km3WcUcauOFVge/H+eKbtYIvUzblGYEqQTkEA+lUXTbkk7ccEfWtlYjJAXH3VYKSR65xmq76bcOXVE83H93nikhyK4hBgKcFccrzg9+PT8KrR24V/kbAZcYP3h9D3/pWj5bRx7HUrtAznqMf/qqtFH+9IIGV7j64/rQPsS6Ff2ttdiXUtOh1K3I2vBJM8eQSOQyEENxjnI56Hir1zaNfQLdados1vZRkw+ZGJJFdlUuSzHjeEyW27VwpIVRmslo95aPAyQMkgZ/+v/OnwSqCPOjWQbCi+ZngkFc8YOQSGHuBkEcUybHa+EvFWvQ2J0uw8QWmk2tj5t8Jrx2wcgKYwMNu+8WCBeWJPJArmtctPsGtXds11YXbBt7T6ewMLFgG+TAAwM4wAAMY7VAs2y7gnhhhTy9hC7dyMVAySr5B3EZIPHJ4A4qa9FxcRW05jIhjX7PHIlssSvsAJBKjDsAQSxyeRkmkB0GtavrwF3f6q2h3d1eolu11GbOeaMruIdfKYlW25Xfjpt5yFIxdJs7pN+rLpdvqFlF5izQzHcD8nzAqpDBgrFwRyNjMOI2w2+1KW6ieN0t8tL5jiKxiiIYKFABRQQMD7owM8kEnNWNPhtNSmjF1ZQ2ljbCBr+5hLGRId4jeTDMwJJkXICnkLgAbsgWNrw/p0fjzXIrOc6fpOn2Fs9zdzQRrGzRhgXctjG4luM4RAPlUAbTjad4l1ez8OSaDbXbQadcyM1yEADyllQFSxycYToMZy2cjiqkmp3VzBbROdkMNv9l2oWHmJ5rS4fnn52z6cL3GajhiIjdh93Jx6d/8KYyuEIZgCAuPmY9enSpUj+Q7Mrjk+4zVi3i5Zkj3sT1PAFJMrhiznBU4wDx60rjW5Cnk+cWb5mHAUDJrQQXEjfIqxL2LDJqvbSxQru5d/wCIKuf1qyLi5lUCOIRluMk5P1xQLqPj0+MYeTfI395zmi7kRImVWG9ugBH41E8Vy65uLiQDptX5f5fjVXyYo5lSMDgDceppAtzPjOyRhH909v7vP1qRmQSgICmOwz1/E1pf8IxfyOWEBGT2Ip7eGtSGCLeQnGM5zmjnhbcSi7iWmGNxF8xMkRCBR1OQwH6Val8yQLJGqCRkVmwME8DoRUkOg6qrwyfZW46gMMj8zV210O/SIA2+wqSPmYfMPU4zj/61Q6se5fLcwZY5FZWmx8xzuDZyDUbQ/Kr7NoIKhiMZNb8/hrUZh8scS9ON9SW/hjVHR4/KiYbsr844o9rDuHK7bHKiCPzH/eEEgHDD/PH+FSiyj/s7zyy743KnP8WcYx7j/PStu48J6nGu42weTbtwkg9OvOPanDwZqrxPDJLEIm5G07jn9KTrQ7hys5mBd86wZwGTjHUcdq0BaADLxyHI424+nTNbMPgm8juUmadTt7dM/rVybw3fCMFI97KRgAj155zSdaD2YKEjlpbVQScOFAyRwT/P61HfT/a4bcLaW1uLaEREQqQZCDku+4ksxLDJ44AAAAxXWT+G7vy2UFCSMc8Cq8/hC/uDM8DRZfopwMDjj9DQq0O4crOYTAAkVgPlZl789fwqSOQGIKQPxPQnNa//AAiOqptD+WAq7ceYcd+2Peoz4U1MKoCwZxz856/lVe1h3DlZTt1UQjM4CnnjAx3qvO64JGWOSSSOcVo/2Xq0KCIaacISAykEHnrxz+dI/h/Uio2qg2ngNnke/FP2kO4KLM8ahAkJARmK45xj8D+lTC9mcAxxMTyV5qyuh3yDBituvPJ/wqaLTL6JSALcH3LcfpR7SHcXLLsVkS5kdhmOIKOWOSQB9fyqlLJJjeGIcsBkLV97LUiWTyQV5GQ4w305zUJ02/8A9WLb5cfe3Lyfzo549ykmekCVT3NSqkjj5Y5D9BS/a2X/AFYRD/soB/KmPcyyffkZvrk15ljW5MIJR97Cf7zgfpTwsY+9Ln/cXP8APFU9xo8yiwXLqvEpz5bN/vNx+lO+0uD+7CR/7oqj5h9aAWY4UEn2osBO0xJycE0qyDI3YxQLSTZukkRB6E1AzBG+Rs471N09gLc8/AQEFR0PcVXM2PeoXlZzljmozIBVRjZBcuLdSBdu47f7p5H5UhuIj9+Pn+8p2n/D9Kplie9Ickdc1XKK5qLNHIyr5wlzwFuBg/QN/wDXp09lGMbt0DnoHGVP0YVkc9zVi3v7m3XakhKHqjDcv5GiwE01tdQqGKlo+zKdw/Squ4nrj8q0INRtw2WR7d+7RcqfqtXTBDex79kco/56QHn8RRZBzGGWYjr+tMIUj5jz/ug1oy6M5yYJA/8Asngis6W3mgbEkbL9RRyhcj8tScbUP+8MfyoNqrf8s0/4C3/66TJFLnPeizHcnz70m73NMLYHTNKiyS8KvPtT2EOB9RRuqzFp/eVs+wp8hhtx2FZuor2Q7FIt7VKt6YRtjjwe7Nyaie4VjlB+NQs7Hnqaq3NuhEzzvK2XJJpm8/5FQ/MetKBVJJCuSZzSHHem80uc0wF3Yo3elNNIT6UAKXI96A+SM8CmZJPOMUtOwEhPvmlR3jffFI0b/wB5Tg1EGI4pwbjmlYDXt9XLKqXsfmY/5aKMEf59q0opFuYz5EyTp3R+o/H/ABrl9xFOSQq4ZSVYfxA4NKwrG7NYW0pwUaBz+RqjPpE8YJTbIPbrUttrDgBLgeYv97FaNvcwzcRSAH+6aLiu0Y0Nn3kP4CrY2RjCjFZst+zNhPlX2phuJGGM4FY8spbmt0i7PebBhOTWbIGkbc7EmnZ9ajZj2rWMEiWxQoWl3AVFg9zS4FXYQ/cp7U4EelMUZp+MUWQDaM0EikHWgBaKWkJxQIQnFMLknAFOPNNphcaTxSq/vQRmkC4pASAmlz7U0cU4GgYBqduI9R9KMimEsDQA0cGnhiaaFp4oAWkxRRQgDFJsz1NLRnFAhwAFBpu6kLUWADQOKbvo307APo4qPfShqAHUYzSbqTdQIdikIApN9IWzRYYv40A0wmk5osMmApcVGkh6Gpc5pAJTgKKKAAkCoy4oopoBu80ZzRRTACaYWoopiG7qN1FFIQm6l3UUUDF30oOaKKBAaTNFFMY4EGlIoopMBvQ09WNFFIZ//9k=","hit":"none","pos_x":50.00059,"pos_y":0.7391969,"pos_z":50.00017,"time":1204.514,"cte":0.0008791223} \ No newline at end of file diff --git a/controls/controls.go b/controls/controls.go new file mode 100644 index 0000000..0293593 --- /dev/null +++ b/controls/controls.go @@ -0,0 +1,123 @@ +package controls + +import ( + "bufio" + "encoding/json" + "fmt" + "github.com/cyrilix/robocar-protobuf/go/events" + "github.com/cyrilix/robocar-simulator/simulator" + log "github.com/sirupsen/logrus" + "io" + "net" + "sync" +) + +type SteeringController interface { + WriteSteering(message *events.SteeringMessage) +} + +type ThrottleController interface { + WriteThrottle(message *events.ThrottleMessage) +} + +func New(address string) (*Gateway, error) { + conn, err := net.Dial("tcp", address) + if err != nil { + return nil, fmt.Errorf("unable to connect to %v", address) + } + return &Gateway{ + conn: conn, + }, nil +} + +/* Simulator interface to publish command controls from mqtt topic */ +type Gateway struct { + cancel chan interface{} + + muControl sync.Mutex + lastControl *simulator.ControlMsg + conn io.WriteCloser +} + +func (g *Gateway) Start() { + log.Info("connect to simulator") + g.cancel = make(chan interface{}) +} + +func (g *Gateway) Stop() { + log.Info("close simulator gateway") + close(g.cancel) + + if err := g.Close(); err != nil { + log.Printf("unexpected error while simulator connection is closed: %v", err) + } +} + +func (g *Gateway) Close() error { + if g.conn == nil { + log.Warnln("no connection to close") + return nil + } + if err := g.conn.Close(); err != nil { + return fmt.Errorf("unable to close connection to simulator: %v", err) + } + return nil +} + +func (g *Gateway) WriteSteering(message *events.SteeringMessage) { + g.muControl.Lock() + defer g.muControl.Unlock() + g.initLastControlMsg() + + g.lastControl.Steering = message.Steering + g.writeContent() +} + +func (g *Gateway) WriteThrottle(message *events.ThrottleMessage) { + g.muControl.Lock() + defer g.muControl.Unlock() + g.initLastControlMsg() + + if message.Throttle > 0 { + g.lastControl.Throttle = message.Throttle + g.lastControl.Brake = 0. + } else { + g.lastControl.Throttle = 0. + g.lastControl.Brake = -1 * message.Throttle + } + + g.writeContent() +} + +func (g *Gateway) writeContent() { + w := bufio.NewWriter(g.conn) + content, err := json.Marshal(g.lastControl) + if err != nil { + log.Errorf("unable to marshall control msg \"%#v\": %v", g.lastControl, err) + return + } + + _, err = w.Write(append(content, '\n')) + if err != nil { + log.Errorf("unable to write control msg \"%#v\" to simulator: %v", g.lastControl, err) + return + } + err = w.Flush() + if err != nil { + log.Errorf("unable to flush control msg \"%#v\" to simulator: %v", g.lastControl, err) + return + } +} + +func (g *Gateway) initLastControlMsg() { + if g.lastControl != nil { + return + } + g.lastControl = &simulator.ControlMsg{ + MsgType: "control", + Steering: 0., + Throttle: 0., + Brake: 0., + } +} + diff --git a/controls/controls_test.go b/controls/controls_test.go new file mode 100644 index 0000000..6f5c8ad --- /dev/null +++ b/controls/controls_test.go @@ -0,0 +1,281 @@ +package controls + +import ( + "bufio" + "encoding/json" + "fmt" + "github.com/cyrilix/robocar-protobuf/go/events" + "github.com/cyrilix/robocar-simulator/simulator" + log "github.com/sirupsen/logrus" + "io" + "net" + "sync" + "testing" +) + +func TestGateway_WriteSteering(t *testing.T) { + + cases := []struct { + name string + msg *events.SteeringMessage + previousMsg *simulator.ControlMsg + expectedMsg simulator.ControlMsg + }{ + {"First Message", + &events.SteeringMessage{Steering: 0.5, Confidence: 1}, + nil, + simulator.ControlMsg{ + MsgType: "control", + Steering: 0.5, + Throttle: 0, + Brake: 0, + }}, + {"Update steering", + &events.SteeringMessage{Steering: -0.5, Confidence: 1}, + &simulator.ControlMsg{ + MsgType: "control", + Steering: 0.2, + Throttle: 0, + Brake: 0, + }, + simulator.ControlMsg{ + MsgType: "control", + Steering: -0.5, + Throttle: 0, + Brake: 0, + }}, + {"Update steering shouldn't erase throttle value", + &events.SteeringMessage{Steering: -0.3, Confidence: 1}, + &simulator.ControlMsg{ + MsgType: "control", + Steering: 0.2, + Throttle: 0.6, + Brake: 0.1, + }, + simulator.ControlMsg{ + MsgType: "control", + Steering: -0.3, + Throttle: 0.6, + Brake: 0.1, + }}, + } + + simulatorMock := ConnMock{} + err := simulatorMock.listen() + if err != nil { + t.Errorf("unable to start mock server: %v", err) + } + defer func(){ + if err := simulatorMock.Close(); err != nil { + t.Errorf("unable to stop simulator mock: %v", err) + } + }() + + server, err := New(simulatorMock.Addr()) + if err != nil { + t.Fatalf("unable to init simulator gateway: %v", err) + } + + for _, c := range cases { + server.lastControl = c.previousMsg + + server.WriteSteering(c.msg) + + ctrlMsg := <- simulatorMock.Notify() + if *ctrlMsg != c.expectedMsg { + t.Errorf("[%v] bad messge received: %#v, wants %#v", c.name, ctrlMsg, c.expectedMsg) + } + } +} + +func TestGateway_WriteThrottle(t *testing.T) { + + cases := []struct { + name string + msg *events.ThrottleMessage + previousMsg *simulator.ControlMsg + expectedMsg simulator.ControlMsg + }{ + {"First Message", + &events.ThrottleMessage{Throttle: 0.5, Confidence: 1}, + nil, + simulator.ControlMsg{ + MsgType: "control", + Steering: 0, + Throttle: 0.5, + Brake: 0, + }}, + {"Update Throttle", + &events.ThrottleMessage{Throttle: 0.6, Confidence: 1}, + &simulator.ControlMsg{ + MsgType: "control", + Steering: 0, + Throttle: 0.4, + Brake: 0, + }, + simulator.ControlMsg{ + MsgType: "control", + Steering: 0, + Throttle: 0.6, + Brake: 0, + }}, + {"Update steering shouldn't erase throttle value", + &events.ThrottleMessage{Throttle: 0.3, Confidence: 1}, + &simulator.ControlMsg{ + MsgType: "control", + Steering: 0.2, + Throttle: 0.6, + Brake: 0., + }, + simulator.ControlMsg{ + MsgType: "control", + Steering: 0.2, + Throttle: 0.3, + Brake: 0., + }}, + {"Throttle to brake", + &events.ThrottleMessage{Throttle: -0.7, Confidence: 1}, + &simulator.ControlMsg{ + MsgType: "control", + Steering: 0.2, + Throttle: 0.6, + Brake: 0., + }, + simulator.ControlMsg{ + MsgType: "control", + Steering: 0.2, + Throttle: 0., + Brake: 0.7, + }}, + {"Update brake", + &events.ThrottleMessage{Throttle: -0.2, Confidence: 1}, + &simulator.ControlMsg{ + MsgType: "control", + Steering: 0.2, + Throttle: 0., + Brake: 0.5, + }, + simulator.ControlMsg{ + MsgType: "control", + Steering: 0.2, + Throttle: 0., + Brake: 0.2, + }}, + {"Brake to throttle", + &events.ThrottleMessage{Throttle: 0.9, Confidence: 1}, + &simulator.ControlMsg{ + MsgType: "control", + Steering: 0.2, + Throttle: 0., + Brake: 0.4, + }, + simulator.ControlMsg{ + MsgType: "control", + Steering: 0.2, + Throttle: 0.9, + Brake: 0., + }}, + } + + simulatorMock := ConnMock{} + err := simulatorMock.listen() + if err != nil { + t.Errorf("unable to start mock server: %v", err) + } + defer func(){ + if err := simulatorMock.Close(); err != nil { + t.Errorf("unable to stop simulator mock: %v", err) + } + }() + + server, err := New(simulatorMock.Addr()) + if err != nil { + t.Fatalf("unable to init simulator gateway: %v", err) + } + + for _, c := range cases { + server.lastControl = c.previousMsg + + server.WriteThrottle(c.msg) + + ctrlMsg := <- simulatorMock.Notify() + if *ctrlMsg != c.expectedMsg { + t.Errorf("[%v] bad messge received: %#v, wants %#v", c.name, ctrlMsg, c.expectedMsg) + } + } +} + +type ConnMock struct { + initMsgsOnce sync.Once + + ln net.Listener + notifyChan chan *simulator.ControlMsg + initNotifyChan sync.Once +} + +func (c *ConnMock) Notify() <-chan *simulator.ControlMsg { + c.initNotifyChan.Do(func() { c.notifyChan = make(chan *simulator.ControlMsg) }) + return c.notifyChan +} + +func (c *ConnMock) listen() error { + ln, err := net.Listen("tcp", "127.0.0.1:") + c.ln = ln + if err != nil { + + return fmt.Errorf("unable to listen on port: %v", err) + } + + go func() { + for { + conn, err := c.ln.Accept() + if err != nil { + log.Infof("connection close: %v", err) + break + } + go c.handleConnection(conn) + } + }() + return nil +} + +func (c *ConnMock) Addr() string { + return c.ln.Addr().String() +} + +func (c *ConnMock) handleConnection(conn net.Conn) { + c.initNotifyChan.Do(func() { c.notifyChan = make(chan *simulator.ControlMsg) }) + reader := bufio.NewReader(conn) + for { + rawCmd, err := reader.ReadBytes('\n') + if err != nil { + if err == io.EOF { + log.Info("connection closed") + break + } + log.Errorf("unable to read request: %v", err) + return + } + + var msg simulator.ControlMsg + err = json.Unmarshal(rawCmd, &msg) + if err != nil { + log.Errorf("unable to unmarchal control msg \"%v\": %v", string(rawCmd), err) + continue + } + + c.notifyChan <- &msg + } +} + +func (c *ConnMock) Close() error { + log.Infof("close mock server") + err := c.ln.Close() + if err != nil { + return fmt.Errorf("unable to close mock server: %v", err) + } + if c.notifyChan != nil { + close(c.notifyChan) + } + return nil +} diff --git a/simulator/simulator.go.go b/simulator/simulator.go.go index a02aa04..f9b31af 100644 --- a/simulator/simulator.go.go +++ b/simulator/simulator.go.go @@ -1,6 +1,6 @@ package simulator -type SimulatorMsg struct { +type TelemetryMsg struct { MsgType string `json:"msg_type"` SteeringAngle float64 `json:"steering_angle"` Throttle float64 `json:"throttle"` @@ -13,3 +13,11 @@ type SimulatorMsg struct { Time float64 `json:"time"` Cte float64 `json:"cte"` } + +/* Json msg used to control cars. MsgType must be filled with "control" */ +type ControlMsg struct { + MsgType string `json:"msg_type"` + Steering float32 `json:"steering"` + Throttle float32 `json:"throttle"` + Brake float32 `json:"brake"` +}