From 2941f7a52789cda2be5db877155cf1f579a9c8af Mon Sep 17 00:00:00 2001 From: Markus Pesch Date: Mon, 17 Jun 2019 23:37:48 +0200 Subject: [PATCH] fix(pkg/sensor): use context to terminate go routines --- cmd/daemon/daemon.go | 11 ++- cmd/temperature/read.go | 9 +- pkg/daemon/daemon.go | 42 +++++--- pkg/internal/prittyprint/prittyprint.go | 8 +- pkg/logfile/logfile.go | 1 + pkg/sensor/dht11.go | 56 +++++++++++ pkg/sensor/dht22.go | 56 +++++++++++ pkg/sensor/ds18b20.go | 52 +++++++++- pkg/sensor/error.go | 8 ++ pkg/sensor/interfaces.go | 11 ++- pkg/sensor/sensor.go | 124 ++++++++++++++---------- 11 files changed, 298 insertions(+), 80 deletions(-) create mode 100644 pkg/sensor/error.go diff --git a/cmd/daemon/daemon.go b/cmd/daemon/daemon.go index d77e0bc..0288780 100644 --- a/cmd/daemon/daemon.go +++ b/cmd/daemon/daemon.go @@ -11,7 +11,8 @@ import ( var compression bool var configFile string -var cleanCacheIntervall string +var cleanCacheInterval string +var round float64 var daemonCmd = &cobra.Command{ Use: "daemon", @@ -23,12 +24,12 @@ var daemonCmd = &cobra.Command{ log.Fatalln(err) } - duration, err := time.ParseDuration(cleanCacheIntervall) + duration, err := time.ParseDuration(cleanCacheInterval) if err != nil { log.Fatalf("Can not parse clean cache interval into duration time: %v", err) } - err = daemon.Start(cnf, duration, compression) + err = daemon.Start(cnf, duration, compression, round) if err != nil { log.Fatalln(err) } @@ -40,6 +41,6 @@ func InitCmd(cmd *cobra.Command, cnfFile string) { configFile = cnfFile cmd.AddCommand(daemonCmd) daemonCmd.Flags().BoolVar(&compression, "compression", true, "Compress measured values") - daemonCmd.Flags().StringVar(&cleanCacheIntervall, "clean-cache-intervall", "30m", "Minute intervall to clean cache and write measured values into logfile") - + daemonCmd.Flags().StringVar(&cleanCacheInterval, "clean-cache-interval", "5m", "Minute intervall to clean cache and write measured values into logfile") + daemonCmd.Flags().Float64Var(&round, "round", 0.25, "Round values. The value 0 deactivates the function") } diff --git a/cmd/temperature/read.go b/cmd/temperature/read.go index 5dbe24d..86df075 100644 --- a/cmd/temperature/read.go +++ b/cmd/temperature/read.go @@ -14,6 +14,7 @@ import ( var compression bool var logs bool +var round float64 var readTemperatureCmd = &cobra.Command{ Use: "read", @@ -35,8 +36,7 @@ var readTemperatureCmd = &cobra.Command{ temperatureSensors = cnf.GetTemperatureSensorsByName(args) } - // read temperature from sensors - temperatures, err := sensor.ReadTemperatures(temperatureSensors) + temperatures, err := sensor.ReadTemperatures(temperatureSensors, round) if err != nil { log.Fatalln(err) } @@ -55,6 +55,7 @@ var readTemperatureCmd = &cobra.Command{ func init() { temperatureCmd.AddCommand(readTemperatureCmd) - readTemperatureCmd.Flags().BoolVarP(&logs, "logs", "l", true, "Log temperature") - readTemperatureCmd.Flags().BoolVarP(&compression, "compression", "c", true, "Compress measured with logged temperatures") + readTemperatureCmd.Flags().BoolVar(&logs, "logs", true, "Log temperature") + readTemperatureCmd.Flags().BoolVar(&compression, "compression", true, "Compress measured with logged temperatures") + readTemperatureCmd.Flags().Float64VarP(&round, "round", "r", 0.25, "Round values. The value 0 deactivates the function") } diff --git a/pkg/daemon/daemon.go b/pkg/daemon/daemon.go index 3ea61e9..5100750 100644 --- a/pkg/daemon/daemon.go +++ b/pkg/daemon/daemon.go @@ -1,6 +1,7 @@ package daemon import ( + "context" "fmt" "log" "os" @@ -9,6 +10,8 @@ import ( "time" "github.com/go-flucky/flucky/pkg/config" + "github.com/go-flucky/flucky/pkg/internal/collect" + "github.com/go-flucky/flucky/pkg/internal/prittyprint" "github.com/go-flucky/flucky/pkg/logfile" "github.com/go-flucky/flucky/pkg/rgbled" "github.com/go-flucky/flucky/pkg/sensor" @@ -16,7 +19,7 @@ import ( ) // Start the daemon -func Start(cnf *config.Configuration, cleanCacheIntervall time.Duration, compression bool) error { +func Start(cnf *config.Configuration, cleanCacheIntervall time.Duration, compression bool, round float64) error { ticker := time.Tick(cleanCacheIntervall) @@ -27,14 +30,18 @@ func Start(cnf *config.Configuration, cleanCacheIntervall time.Duration, compres //humidityChannel := make(chan *types.Humidity, 0) temperatureChannel := make(chan *types.Temperature, 0) + ctx := context.Background() + childContext, cancel := context.WithCancel(ctx) + // go sensor.ReadHumiditiesContinuously(cnf.GetHumiditySensors(config.ENABLED), humidityChannel, errorChannel) - go sensor.ReadTemperaturesContinuously(cnf.GetTemperatureSensors(config.ENABLED), temperatureChannel, errorChannel) + go sensor.ReadTemperaturesContinuously(childContext, cnf.GetTemperatureSensors(config.ENABLED), temperatureChannel, errorChannel) temperatures := make([]*types.Temperature, 0) rgbLEDs := cnf.GetRGBLEDs(config.ENABLED) err := rgbled.Green(rgbLEDs) if err != nil { + cancel() return fmt.Errorf("Can not turn on blue info light: %v", err) } @@ -43,43 +50,50 @@ func Start(cnf *config.Configuration, cleanCacheIntervall time.Duration, compres case <-ticker: err := rgbled.Blue(rgbLEDs) if err != nil { + cancel() return fmt.Errorf("Can not turn on yellow info light: %v", err) } - log.Printf("Write measured values into logfile") - - log.Printf("%v new measured temperature values", len(temperatures)) err = logfile.WriteTemperatures(temperatures, cnf.Device.TemperatureLogfile, compression) if err != nil { + cancel() return fmt.Errorf("Can not save temperatures: %v", err) } temperatures = make([]*types.Temperature, 0) err = rgbled.Green(rgbLEDs) if err != nil { + cancel() return fmt.Errorf("Can not turn on green info light: %v", err) } case temperature, more := <-temperatureChannel: if more { temperatures = append(temperatures, temperature) - } else { - log.Printf("Temperature Channel closed. Write remaining values into the logfile") - err := logfile.WriteTemperatures(temperatures, cnf.Device.TemperatureLogfile, compression) - if err != nil { - return fmt.Errorf("Can not save temperatures: %v", err) - } + continue } case killSignal := <-interrupt: + log.Printf("Daemon was interruped by system signal %v\n", killSignal) + + cancel() + err := rgbled.Red(rgbLEDs) if err != nil { return fmt.Errorf("Can not turn on info light: %v", err) } - //close(humidityChannel) - close(temperatureChannel) - return fmt.Errorf("Daemon was interruped by system signal %v", killSignal) + errors := collect.Errors(errorChannel) + if len(errors) > 0 { + log.Println(prittyprint.FormatErrors(errors)) + } + + err = logfile.WriteTemperatures(temperatures, cnf.Device.TemperatureLogfile, compression) + if err != nil { + return fmt.Errorf("Can not save temperatures: %v", err) + } + + return nil } } } diff --git a/pkg/internal/prittyprint/prittyprint.go b/pkg/internal/prittyprint/prittyprint.go index d5a840b..539c36a 100644 --- a/pkg/internal/prittyprint/prittyprint.go +++ b/pkg/internal/prittyprint/prittyprint.go @@ -5,8 +5,12 @@ import "fmt" func FormatErrors(errors []error) error { if len(errors) > 0 { errMsg := "" - for _, err := range errors { - errMsg = fmt.Sprintf("%v\n%v", errMsg, err.Error()) + for i, err := range errors { + if i == 0 { + errMsg = fmt.Sprintf("%v", err.Error()) + } else { + errMsg = fmt.Sprintf("%v\n%v", errMsg, err.Error()) + } } return fmt.Errorf(errMsg) } diff --git a/pkg/logfile/logfile.go b/pkg/logfile/logfile.go index 737acf4..452bf26 100644 --- a/pkg/logfile/logfile.go +++ b/pkg/logfile/logfile.go @@ -181,6 +181,7 @@ func WriteTemperatures(temperatures []*types.Temperature, temperatureLogfile str // custom writer. Compression can be enabled over a bolean parameter func WriteTemperaturesCustom(temperatures []*types.Temperature, w io.Writer, compression bool) error { + // CompressTemperature if compression { temperatures = CompressTemperature(temperatures) } diff --git a/pkg/sensor/dht11.go b/pkg/sensor/dht11.go index 8fb29ef..d524bf9 100644 --- a/pkg/sensor/dht11.go +++ b/pkg/sensor/dht11.go @@ -1,7 +1,9 @@ package sensor import ( + "context" "fmt" + "sync" "time" "github.com/go-flucky/flucky/pkg/types" @@ -52,6 +54,33 @@ func (s *DHT11) ReadHumidity() (*types.Humidity, error) { return humidity, nil } +// ReadHumidityWriteIntoChannel and write values into a channel +func (s *DHT11) ReadHumidityWriteIntoChannel(humidityChannel chan<- *types.Humidity, errorChannel chan<- error, wg *sync.WaitGroup) { + if wg != nil { + defer wg.Done() + } + + humidity, err := s.ReadHumidity() + if err != nil { + errorChannel <- err + return + } + humidityChannel <- humidity +} + +// ReadHumidityContinously into a channel until context closed +func (s *DHT11) ReadHumidityContinously(ctx context.Context, humidityChannel chan<- *types.Humidity, errorChannel chan<- error) { + for { + select { + case <-ctx.Done(): + errorChannel <- fmt.Errorf("%v: Context closed: %v", s.SensorName, ctx.Err()) + return + default: + s.ReadHumidityWriteIntoChannel(humidityChannel, errorChannel, nil) + } + } +} + // ReadTemperature measure the temperature func (s *DHT11) ReadTemperature() (*types.Temperature, error) { err := dht.HostInit() @@ -84,3 +113,30 @@ func (s *DHT11) ReadTemperature() (*types.Temperature, error) { return temperature, nil } + +// ReadTemperatureWriteIntoChannel and write values into a channel +func (s *DHT11) ReadTemperatureWriteIntoChannel(temperatureChannel chan<- *types.Temperature, errorChannel chan<- error, wg *sync.WaitGroup) { + if wg != nil { + defer wg.Done() + } + + temperature, err := s.ReadTemperature() + if err != nil { + errorChannel <- err + return + } + temperatureChannel <- temperature +} + +// ReadTemperatureContinously into a channel until context closed +func (s *DHT11) ReadTemperatureContinously(ctx context.Context, temperatureChannel chan<- *types.Temperature, errorChannel chan<- error) { + for { + select { + case <-ctx.Done(): + errorChannel <- fmt.Errorf("%v: Context closed: %v", s.SensorName, ctx.Err()) + return + default: + s.ReadTemperatureWriteIntoChannel(temperatureChannel, errorChannel, nil) + } + } +} diff --git a/pkg/sensor/dht22.go b/pkg/sensor/dht22.go index 810db5b..4a71bb1 100644 --- a/pkg/sensor/dht22.go +++ b/pkg/sensor/dht22.go @@ -1,7 +1,9 @@ package sensor import ( + "context" "fmt" + "sync" "time" "github.com/go-flucky/flucky/pkg/types" @@ -52,6 +54,33 @@ func (s *DHT22) ReadHumidity() (*types.Humidity, error) { return humidity, nil } +// ReadHumidityWriteIntoChannel and write values into a channel +func (s *DHT22) ReadHumidityWriteIntoChannel(humidityChannel chan<- *types.Humidity, errorChannel chan<- error, wg *sync.WaitGroup) { + if wg != nil { + defer wg.Done() + } + + humidity, err := s.ReadHumidity() + if err != nil { + errorChannel <- err + return + } + humidityChannel <- humidity +} + +// ReadHumidityContinously into a channel until context closed +func (s *DHT22) ReadHumidityContinously(ctx context.Context, humidityChannel chan<- *types.Humidity, errorChannel chan<- error) { + for { + select { + case <-ctx.Done(): + errorChannel <- fmt.Errorf("%v: Context closed: %v", s.SensorName, ctx.Err()) + return + default: + s.ReadHumidityWriteIntoChannel(humidityChannel, errorChannel, nil) + } + } +} + // ReadTemperature measure the temperature func (s *DHT22) ReadTemperature() (*types.Temperature, error) { err := dht.HostInit() @@ -84,3 +113,30 @@ func (s *DHT22) ReadTemperature() (*types.Temperature, error) { return temperature, nil } + +// ReadTemperatureWriteIntoChannel and write values into a channel +func (s *DHT22) ReadTemperatureWriteIntoChannel(temperatureChannel chan<- *types.Temperature, errorChannel chan<- error, wg *sync.WaitGroup) { + if wg != nil { + defer wg.Done() + } + + temperature, err := s.ReadTemperature() + if err != nil { + errorChannel <- err + return + } + temperatureChannel <- temperature +} + +// ReadTemperatureContinously into a channel until context closed +func (s *DHT22) ReadTemperatureContinously(ctx context.Context, temperatureChannel chan<- *types.Temperature, errorChannel chan<- error) { + for { + select { + case <-ctx.Done(): + errorChannel <- fmt.Errorf("%v: Context closed: %v", s.SensorName, ctx.Err()) + return + default: + s.ReadTemperatureWriteIntoChannel(temperatureChannel, errorChannel, nil) + } + } +} diff --git a/pkg/sensor/ds18b20.go b/pkg/sensor/ds18b20.go index 0e43e47..1c807c5 100644 --- a/pkg/sensor/ds18b20.go +++ b/pkg/sensor/ds18b20.go @@ -1,12 +1,17 @@ package sensor import ( + "context" "fmt" + "io/ioutil" + "path/filepath" + "strconv" + "strings" + "sync" "time" "github.com/go-flucky/flucky/pkg/types" uuid "github.com/satori/go.uuid" - "github.com/yryz/ds18b20" ) // DS18B20 is a sensor to measure humidity and temperature. @@ -27,14 +32,26 @@ func (s *DS18B20) GetSensor() *types.Sensor { // ReadTemperature measure the temperature func (s *DS18B20) ReadTemperature() (*types.Temperature, error) { - t, err := ds18b20.Temperature(*s.WireID) + data, err := ioutil.ReadFile(filepath.Join("/sys/bus/w1/devices", *s.WireID, "/w1_slave")) if err != nil { - return nil, fmt.Errorf("Can not read from Sensor %v (UUID: %v, Wire-ID: %v): %v", s.SensorName, s.SensorID, s.WireID, err) + return nil, fmt.Errorf("Can not read data from sensor %v", s.SensorName) + } + + raw := string(data) + + i := strings.LastIndex(raw, "t=") + if i == -1 { + return nil, ErrReadSensor + } + + celsius, err := strconv.ParseFloat(raw[i+2:len(raw)-1], 64) + if err != nil { + return nil, ErrParseData } temperature := &types.Temperature{ TemperatureID: uuid.NewV4().String(), - TemperatureValue: t, + TemperatureValue: celsius / 1000, TemperatureFromDate: time.Now(), TemperatureTillDate: time.Now(), SensorID: s.SensorID, @@ -43,3 +60,30 @@ func (s *DS18B20) ReadTemperature() (*types.Temperature, error) { return temperature, nil } + +// ReadTemperatureWriteIntoChannel and write values into a channel +func (s *DS18B20) ReadTemperatureWriteIntoChannel(temperatureChannel chan<- *types.Temperature, errorChannel chan<- error, wg *sync.WaitGroup) { + if wg != nil { + defer wg.Done() + } + + temperature, err := s.ReadTemperature() + if err != nil { + errorChannel <- err + return + } + temperatureChannel <- temperature +} + +// ReadTemperatureContinously into a channel until context closed +func (s *DS18B20) ReadTemperatureContinously(ctx context.Context, temperatureChannel chan<- *types.Temperature, errorChannel chan<- error) { + for { + select { + case <-ctx.Done(): + errorChannel <- fmt.Errorf("%v: Context closed: %v", s.SensorName, ctx.Err()) + return + default: + s.ReadTemperatureWriteIntoChannel(temperatureChannel, errorChannel, nil) + } + } +} diff --git a/pkg/sensor/error.go b/pkg/sensor/error.go new file mode 100644 index 0000000..054ff47 --- /dev/null +++ b/pkg/sensor/error.go @@ -0,0 +1,8 @@ +package sensor + +import ( + "errors" +) + +var ErrParseData = errors.New("Can not parse data") +var ErrReadSensor = errors.New("Can not read data from Sensor") diff --git a/pkg/sensor/interfaces.go b/pkg/sensor/interfaces.go index 61efba6..7c39d01 100644 --- a/pkg/sensor/interfaces.go +++ b/pkg/sensor/interfaces.go @@ -1,15 +1,24 @@ package sensor -import "github.com/go-flucky/flucky/pkg/types" +import ( + "context" + "sync" + + "github.com/go-flucky/flucky/pkg/types" +) // HumiditySensor is a interface to describe required functions to measure humidities type HumiditySensor interface { GetSensorModel() types.SensorModel ReadHumidity() (*types.Humidity, error) + ReadHumidityWriteIntoChannel(humidityChannel chan<- *types.Humidity, errorChannel chan<- error, wg *sync.WaitGroup) + ReadHumidityContinously(ctx context.Context, humidityChannel chan<- *types.Humidity, errorChannel chan<- error) } // TemperatureSensor is a interface to describe required functions to measure temperatures type TemperatureSensor interface { GetSensorModel() types.SensorModel ReadTemperature() (*types.Temperature, error) + ReadTemperatureWriteIntoChannel(temperatureChannel chan<- *types.Temperature, errorChannel chan<- error, wg *sync.WaitGroup) + ReadTemperatureContinously(ctx context.Context, temperatureChannel chan<- *types.Temperature, errorChannel chan<- error) } diff --git a/pkg/sensor/sensor.go b/pkg/sensor/sensor.go index 4874ca4..2d2ddb2 100644 --- a/pkg/sensor/sensor.go +++ b/pkg/sensor/sensor.go @@ -1,6 +1,9 @@ package sensor import ( + "context" + "fmt" + "math" "sync" "github.com/go-flucky/flucky/pkg/internal/collect" @@ -9,81 +12,102 @@ import ( ) // ReadHumidities returns a list of measured humidities by humidity sensors -func ReadHumidities(humiditySensors []HumiditySensor) ([]*types.Humidity, error) { - errorChannel := make(chan error, len(humiditySensors)) +func ReadHumidities(humiditySensors []HumiditySensor, round float64) ([]*types.Humidity, error) { humidityChannel := make(chan *types.Humidity, len(humiditySensors)) - ReadHumiditiesIntoChannel(humiditySensors, humidityChannel, errorChannel) + errorChannel := make(chan error, len(humiditySensors)) - errorList := collect.Errors(errorChannel) - if len(errorList) != 0 { - return nil, prittyprint.FormatErrors(errorList) - } - - humidityList := collect.Humidities(humidityChannel) - return humidityList, nil -} - -// ReadHumiditiesIntoChannel reads the humidity values of humidity sensors and writes them into a channel -func ReadHumiditiesIntoChannel(humiditySensors []HumiditySensor, humidityChannel chan<- *types.Humidity, errorChannel chan<- error) { wg := new(sync.WaitGroup) wg.Add(len(humiditySensors)) + for _, humiditySensor := range humiditySensors { - go func(hs HumiditySensor) { - defer wg.Done() - humidity, err := hs.ReadHumidity() - if err != nil { - errorChannel <- err - return - } - humidityChannel <- humidity - }(humiditySensor) + go humiditySensor.ReadHumidityWriteIntoChannel(humidityChannel, errorChannel, wg) } + wg.Wait() + + errors := collect.Errors(errorChannel) + if len(errors) > 0 { + return nil, prittyprint.FormatErrors(errors) + } + + humidities := collect.Humidities(humidityChannel) + + if round != 0 { + for _, humidity := range humidities { + humidity.HumidityValue = math.Round(humidity.HumidityValue/round) * round + } + } + + return humidities, nil +} + +// ReadHumiditiesWriteIntoChannel reads the humidity values of humidity sensors and writes them into a channel +func ReadHumiditiesWriteIntoChannel(ctx context.Context, humiditySensors []HumiditySensor, humidityChannel chan<- *types.Humidity, errorChannel chan<- error, wg *sync.WaitGroup) { + for _, humiditySensor := range humiditySensors { + humiditySensor.ReadHumidityWriteIntoChannel(humidityChannel, errorChannel, wg) + } } // ReadHumiditiesContinuously reads the humidity values of humidity sensors continuously and writes them into a channel -func ReadHumiditiesContinuously(humiditySensors []HumiditySensor, humidityChannel chan<- *types.Humidity, errorChannel chan<- error) { +func ReadHumiditiesContinuously(ctx context.Context, humiditySensors []HumiditySensor, humidityChannel chan<- *types.Humidity, errorChannel chan<- error) { for { - ReadHumiditiesIntoChannel(humiditySensors, humidityChannel, errorChannel) + select { + case <-ctx.Done(): + errorChannel <- fmt.Errorf("Context closed: %v", ctx.Err()) + return + default: + ReadHumiditiesWriteIntoChannel(ctx, humiditySensors, humidityChannel, errorChannel, nil) + } } } // ReadTemperatures returns a list of measured temperatures by temperature sensors -func ReadTemperatures(temperatureSensors []TemperatureSensor) ([]*types.Temperature, error) { - errorChannel := make(chan error, len(temperatureSensors)) +func ReadTemperatures(temperatureSensors []TemperatureSensor, round float64) ([]*types.Temperature, error) { temperatureChannel := make(chan *types.Temperature, len(temperatureSensors)) - ReadTemperaturesIntoChannel(temperatureSensors, temperatureChannel, errorChannel) + errorChannel := make(chan error, len(temperatureSensors)) - errorList := collect.Errors(errorChannel) - if len(errorList) != 0 { - return nil, prittyprint.FormatErrors(errorList) - } - - temperatureList := collect.Temperatures(temperatureChannel) - return temperatureList, nil -} - -// ReadTemperaturesIntoChannel reads the temperature values of temperature sensors and writes them into a channel -func ReadTemperaturesIntoChannel(temperatureSensors []TemperatureSensor, temperatureChannel chan<- *types.Temperature, errorChannel chan<- error) { wg := new(sync.WaitGroup) wg.Add(len(temperatureSensors)) + for _, temperatureSensor := range temperatureSensors { - go func(ts TemperatureSensor) { - defer wg.Done() - temperature, err := ts.ReadTemperature() - if err != nil { - errorChannel <- err - return - } - temperatureChannel <- temperature - }(temperatureSensor) + go temperatureSensor.ReadTemperatureWriteIntoChannel(temperatureChannel, errorChannel, wg) } + wg.Wait() + + errors := collect.Errors(errorChannel) + if len(errors) > 0 { + return nil, prittyprint.FormatErrors(errors) + } + + temperatures := collect.Temperatures(temperatureChannel) + + if round != 0 { + for _, temperature := range temperatures { + temperature.TemperatureValue = math.Round(temperature.TemperatureValue/round) * round + } + } + + return temperatures, nil +} + +// ReadTemperaturesWriteIntoChannel reads the temperature values of temperature sensors and writes them into a channel +func ReadTemperaturesWriteIntoChannel(ctx context.Context, temperatureSensors []TemperatureSensor, temperatureChannel chan<- *types.Temperature, errorChannel chan<- error, wg *sync.WaitGroup) { + for _, temperatureSensor := range temperatureSensors { + temperatureSensor.ReadTemperatureWriteIntoChannel(temperatureChannel, errorChannel, wg) + } + } // ReadTemperaturesContinuously reads the temperature values of temperature sensors continuously and writes them into a chann -func ReadTemperaturesContinuously(temperatureSensors []TemperatureSensor, temperatureChannel chan<- *types.Temperature, errorChannel chan<- error) { +func ReadTemperaturesContinuously(ctx context.Context, temperatureSensors []TemperatureSensor, temperatureChannel chan<- *types.Temperature, errorChannel chan<- error) { for { - ReadTemperaturesIntoChannel(temperatureSensors, temperatureChannel, errorChannel) + select { + case <-ctx.Done(): + errorChannel <- fmt.Errorf("Context closed: %v", ctx.Err()) + return + default: + ReadTemperaturesWriteIntoChannel(ctx, temperatureSensors, temperatureChannel, errorChannel, nil) + } } }