Go - анализ ввода последовательного порта с использованием горутин, не печатающих

Я получаю ввод через последовательный порт (используя Arduino) и хочу проанализировать данные. Вот что у меня есть до сих пор:

package main

import (
    "log"
    "github.com/tarm/serial"
    "bufio"
    "sync"
    "fmt"
)


func readFirstLine(data []string, wg *sync.WaitGroup){
    defer wg.Done()

    fmt.Printf("This is the sensor data:\n%q\n%q", data[0], data[1])

}

func readSecondLine(data []string, wg *sync.WaitGroup){
    defer wg.Done()

    fmt.Printf("This is the actuator data:\n%q", data[2])
}

func main() {
    usbRead := &serial.Config{Name: "COM5", Baud: 9600, ReadTimeout: 0}
    port, err := serial.OpenPort(usbRead)
    var wg sync.WaitGroup
    wg.Add(2)


    if err != nil {
        log.Fatal(err)
    }
    data := []string{}
    scanner := bufio.NewScanner(port)


    for scanner.Scan() {
        data = append(data, scanner.Text())
    }


    for {

    go readFirstLine(data, &wg)
    go readSecondLine(data, &wg)
    wg.Wait()

    }
}   

Последовательный порт в настоящее время печатает это (зациклено):

{"temperature":[27.7],"humidity":[46.9],"sensor":"DHT22"}
{"temperature":[25.41545],"sensor":"LM35DZ"}
{"blink":["true"],"actuator":"arduinoLED"}

Я пытаюсь использовать горутины для анализа данных и распечатать это (также должно быть зациклено):

This is the sensor data: 
{"temperature":[27.7],"humidity":[46.9],"sensor":"DHT22"}
{"temperature":[25.41545],"sensor":"LM35DZ"}
This is the actuator data: 
{"blink":["true"],"actuator":"arduinoLED"}

Однако я не получаю результат. Программа просто не печатает. Я думаю, это связано с тем, как я сохраняю данные. Кто-нибудь знает, как это исправить? И если это исправлено, является ли это использование горутин правильным методом для достижения того, что я хочу?

Большое спасибо.


person Melissa    schedule 01.06.2018    source источник
comment
Как вы думаете, почему это связано с горутинами? Если вы удалите группу ожидания и вместо этого вызовете readFirstLine и readSecondLine без создания новой горутины, будет ли она печататься в этом случае?   -  person eugenioy    schedule 01.06.2018
comment
@eugenioy Если я удалю WaitGroup и удалю go и буду использовать их как обычные функции, это все равно не сработает. Я не думаю, что проблема связана с горутинами, я думаю, что я отправляю данные в горутины для разбора.   -  person Melissa    schedule 01.06.2018
comment
Ваш код никогда не достигает той части, где он вызывает readFirstLine и readSecondLine, он застревает в цикле сканера... см. мой ответ об альтернативной структуре программы, которая использует каналы для отправки данных в обрабатывающие горутины.   -  person ain    schedule 01.06.2018


Ответы (1)


Очевидная проблема с вашим кодом заключается в неправильном использовании группы ожидания - вы инициализируете ее с 2, а затем ждете ее в бесконечном цикле... кроме того, ваш код никогда не достигает той части, где он вызывает readFirstLine и readSecondLine, он застрял в петля сканера

Я думаю, что основная структура, которую вы будете использовать, будет выглядеть примерно так:

func main() {
    // create channels for data
    sensor := make(chan string)
    actuator := make(chan string)
    // launch goroutines which process the data
    var wg, pg sync.WaitGroup
    pg.Add(2)
    go func() {
       defer pg.Done()
       processSensorData(sensor)
    }()
    go func() {
       defer pg.Done()
       processActuatorData(actuator)
    }()
    // read from the data source
    usbRead := &serial.Config{Name: "COM5", Baud: 9600, ReadTimeout: 0}
    port, err := serial.OpenPort(usbRead)
    if err != nil {
        log.Fatal(err)
    }
    scanner := bufio.NewScanner(port)

    for scanner.Scan() {
        data := scanner.Text()
        wg.Add(1)
        go func(data string) {
           defer wg.Done()
           // figure out data packet type and
           // send it into approprioate channel
           if strings.Contains(data, `"sensor"`) {
              sensor <- data
           } else {
              actuator <- data
           }
        }(data)
    }
    // wait for all data to be sent for processing
    wg.Wait()
    // close the channels so goroutines terminate
    close(sensor)
    close(actuator)
    // wait for all data to be processed
    pg.Wait()
}

И горутины, которые обрабатывают данные, будут такими:

func processSensorData(data chan string) {
   for d := range data {
      // do something with data
   }
}
person ain    schedule 01.06.2018
comment
Спасибо за ваш ответ! Вот что у меня сейчас есть: func processSensorData(sensor <-chan byte){ for i := range sensor { fmt.Println(i) } } И я изменил тип с string на byte (поскольку data[0:2] считаются байтами?), но я получаю сообщение об ошибке fatal error: all goroutines are asleep - deadlock! goroutine 1 [chan send]: main.main() C:/Users/Documents/sensorRead.go:50 +0x1bb Почему это так? - person Melissa; 02.06.2018
comment
Тупик возникает из-за того, что каналы не буферизованы. Я отредактировал ответ, чтобы отправка на каналы происходила в другой горутине - это должно исправить. - person ain; 02.06.2018
comment
Кстати, действительно ли имеет смысл обрабатывать данные в отдельных горутинах? Мне кажется, что это делает вещи излишне сложными ... Я бы сказал, что имеет смысл использовать горутины только в том случае, если обработка каждой точки данных занимает значительное количество времени, в противном случае сделайте это встроенным. - person ain; 02.06.2018
comment
Спасибо, я больше не получаю эту ошибку! Однако существует несоответствие типа данных (переменной, которую вы отправляете в горутину) и строки данных []. Я попытался исправить это, выполнив следующее: data := []string{} for scanner.Scan() { data = append(data, scanner.Text()) go func(data []string) { sensor <- data[0] sensor <- data[1] actuator <- data[2] }(data) } что убирает ошибку, но теперь она больше не печатается... Что я здесь делаю неправильно (извините за так много вопросов, я действительно новичок) - person Melissa; 02.06.2018
comment
Я просто скопировал и вставил логику из вашего исходного кода, не особо задумываясь... вы, вероятно, получаете пакет данных в виде строки, и каждый вызов Scan/Text дает вам один пакет. Таким образом, вы должны выяснить его тип и отправить его в правильный канал. См. обновленный ответ (также обратите внимание на группу ожидания, которую я добавил обратно, так как без нее ваша основная может завершиться до обработки всех данных). - person ain; 02.06.2018
comment
Спасибо, это работает! Я понимаю, что еще недостаточно хорошо разбираюсь в горутинах и каналах, но ваш ответ очень помог. Большое спасибо :) - person Melissa; 04.06.2018