fix(pkg/sensor): reduce interface functions for better error handling
This commit is contained in:
		| @@ -5,13 +5,12 @@ import ( | ||||
| 	"log" | ||||
| 	"os" | ||||
|  | ||||
| 	"github.com/go-flucky/flucky/pkg/storage" | ||||
| 	"github.com/go-flucky/flucky/pkg/types" | ||||
|  | ||||
| 	"github.com/go-flucky/flucky/pkg/cli" | ||||
| 	"github.com/go-flucky/flucky/pkg/config" | ||||
| 	"github.com/go-flucky/flucky/pkg/rgbled" | ||||
| 	"github.com/go-flucky/flucky/pkg/sensor" | ||||
| 	"github.com/go-flucky/flucky/pkg/storage" | ||||
| 	"github.com/go-flucky/flucky/pkg/types" | ||||
| 	"github.com/spf13/cobra" | ||||
| ) | ||||
|  | ||||
| @@ -45,9 +44,8 @@ var readHumidityCmd = &cobra.Command{ | ||||
| 			log.Fatalln(err) | ||||
| 		} | ||||
|  | ||||
| 		ctx := context.Background() | ||||
| 		measuredValues, err := sensor.Read(ctx, sensors) | ||||
| 		if err != nil { | ||||
| 		measuredValues, err := sensor.Read(sensors, types.MeasuredValueTypeTemperature) | ||||
| 		if err := rgbled.Run(rgbLEDs); err != nil { | ||||
| 			log.Fatalln(err) | ||||
| 		} | ||||
|  | ||||
| @@ -61,6 +59,8 @@ var readHumidityCmd = &cobra.Command{ | ||||
| 				log.Fatalln(err) | ||||
| 			} | ||||
|  | ||||
| 			ctx := context.Background() | ||||
|  | ||||
| 			err = storage.Write(ctx, measuredValues, storageEndpoint) | ||||
| 			if err != nil { | ||||
| 				log.Fatalln(err) | ||||
|   | ||||
| @@ -45,14 +45,11 @@ var readPressureCmd = &cobra.Command{ | ||||
| 			log.Fatalln(err) | ||||
| 		} | ||||
|  | ||||
| 		ctx := context.Background() | ||||
| 		measuredValues, err := sensor.Read(ctx, sensors) | ||||
| 		if err != nil { | ||||
| 		measuredValues, err := sensor.Read(sensors, types.MeasuredValueTypePressure) | ||||
| 		if err := rgbled.Run(rgbLEDs); err != nil { | ||||
| 			log.Fatalln(err) | ||||
| 		} | ||||
|  | ||||
| 		measuredValues = types.SelectMeasuredValues(types.MeasuredValueTypePressure, measuredValues) | ||||
|  | ||||
| 		cli.PrintMeasuredValues(measuredValues, cnf, os.Stdout) | ||||
|  | ||||
| 		if logs { | ||||
| @@ -61,6 +58,8 @@ var readPressureCmd = &cobra.Command{ | ||||
| 				log.Fatalln(err) | ||||
| 			} | ||||
|  | ||||
| 			ctx := context.Background() | ||||
|  | ||||
| 			err = storage.Write(ctx, measuredValues, storageEndpoint) | ||||
| 			if err != nil { | ||||
| 				log.Fatalln(err) | ||||
|   | ||||
| @@ -6,12 +6,12 @@ import ( | ||||
| 	"log" | ||||
| 	"os" | ||||
|  | ||||
| 	"github.com/go-flucky/flucky/pkg/storage" | ||||
| 	"github.com/go-flucky/flucky/pkg/types" | ||||
|  | ||||
| 	"github.com/go-flucky/flucky/pkg/cli" | ||||
| 	"github.com/go-flucky/flucky/pkg/config" | ||||
| 	"github.com/go-flucky/flucky/pkg/rgbled" | ||||
| 	"github.com/go-flucky/flucky/pkg/sensor" | ||||
| 	"github.com/go-flucky/flucky/pkg/storage" | ||||
| 	"github.com/go-flucky/flucky/pkg/types" | ||||
| 	"github.com/spf13/cobra" | ||||
| ) | ||||
|  | ||||
| @@ -39,36 +39,33 @@ var readTemperatureCmd = &cobra.Command{ | ||||
| 			log.Fatalln("No sensors found, specified or configured") | ||||
| 		} | ||||
|  | ||||
| 		ctx := context.Background() | ||||
| 		measuredValues, err := sensor.Read(ctx, sensors) | ||||
| 		if err != nil { | ||||
| 		rgbLEDs := cnf.GetRGBLEDs(config.ENABLED) | ||||
| 		if err := rgbled.Run(rgbLEDs); err != nil { | ||||
| 			log.Fatalln(err) | ||||
| 		} | ||||
|  | ||||
| 		storage.Round(measuredValues, round) | ||||
|  | ||||
| 		measuredValues = types.SelectMeasuredValues(types.MeasuredValueTypeTemperature, measuredValues) | ||||
|  | ||||
| 		// print temperatures on stdout | ||||
| 		cli.PrintMeasuredValues(measuredValues, cnf, os.Stdout) | ||||
|  | ||||
| 		// Save the new measured values, if desired | ||||
| 		if logs { | ||||
|  | ||||
| 			if compression { | ||||
| 				measuredValues = storage.Compression(measuredValues) | ||||
| 		measuredValues, err := sensor.Read(sensors, types.MeasuredValueTypeTemperature) | ||||
| 		if err := rgbled.Run(rgbLEDs); err != nil { | ||||
| 			log.Fatalln(err) | ||||
| 		} | ||||
|  | ||||
| 		cli.PrintMeasuredValues(measuredValues, cnf, os.Stdout) | ||||
|  | ||||
| 		if logs { | ||||
| 			storageEndpoint, err := cnf.GetStorageEndpointURL() | ||||
| 			if err != nil { | ||||
| 				log.Fatalln(err) | ||||
| 			} | ||||
|  | ||||
| 			ctx := context.Background() | ||||
|  | ||||
| 			err = storage.Write(ctx, measuredValues, storageEndpoint) | ||||
| 			if err != nil { | ||||
| 				log.Fatalln(err) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		rgbled.Off(rgbLEDs) | ||||
| 	}, | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package daemon | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"os/signal" | ||||
| 	"syscall" | ||||
| @@ -59,8 +60,49 @@ func Start(cnf *config.Configuration, cleanCacheInterval time.Duration, compress | ||||
| 	measuredValuesCache := make([]*types.MeasuredValue, 0) | ||||
| 	// measuredValuesLogfile := logfile.New(cnf.Logfile) | ||||
|  | ||||
| 	// Producer | ||||
| 	go sensor.ReadContinuously(ctx, cnf.GetSensors(config.ENABLED), measuredValueChannel, errorChannel) | ||||
| 	// Init semaphoreChannel | ||||
| 	semaphoreChannels := make(map[string]chan struct{}) | ||||
| 	for _, sensor := range cnf.GetSensors(config.ENABLED) { | ||||
| 		semaphoreChannels[sensor.ID()] = make(chan struct{}, 1) | ||||
| 	} | ||||
|  | ||||
| 	// Start producers | ||||
| 	for _, s := range cnf.GetSensors(config.ENABLED) { | ||||
|  | ||||
| 		// start go routine for each sensor | ||||
| 		go func(sensor sensor.Sensor) { | ||||
| 			// run forever | ||||
| 			for { | ||||
| 				select { | ||||
| 				case <-ctx.Done(): | ||||
| 					errorChannel <- fmt.Errorf("Closed context: %v", ctx.Err().Error()) | ||||
| 					return | ||||
| 				case <-semaphoreChannels[sensor.ID()]: | ||||
| 					measuredValues, err := sensor.Read() | ||||
| 					if err != nil { | ||||
| 						errorChannel <- err | ||||
| 						return | ||||
| 					} | ||||
| 					for _, measmeasuredValue := range measuredValues { | ||||
| 						measuredValueChannel <- measmeasuredValue | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		}(s) | ||||
|  | ||||
| 		// start ticker for each sensor | ||||
| 		go func(sensor sensor.Sensor) { | ||||
| 			for { | ||||
| 				select { | ||||
| 				case <-ctx.Done(): | ||||
| 					errorChannel <- fmt.Errorf("Closed context: %v", ctx.Err().Error()) | ||||
| 					return | ||||
| 				case <-sensor.Ticker().C: | ||||
| 					semaphoreChannels[sensor.ID()] <- struct{}{} | ||||
| 				} | ||||
| 			} | ||||
| 		}(s) | ||||
| 	} | ||||
|  | ||||
| 	// Distributor | ||||
| 	//measuredValueChannels := distribute.MeasuredValues(ctx, 5, measuredValueChannel) | ||||
| @@ -105,7 +147,11 @@ func Start(cnf *config.Configuration, cleanCacheInterval time.Duration, compress | ||||
|  | ||||
| 			measuredValuesCache = make([]*types.MeasuredValue, 0) | ||||
|  | ||||
| 		case measuredValue, _ := <-measuredValueChannel: | ||||
| 		case measuredValue, open := <-measuredValueChannel: | ||||
| 			if !open { | ||||
| 				errorChannel <- fmt.Errorf("MeasuredValue channel closed") | ||||
| 				cancel() | ||||
| 			} | ||||
| 			measuredValuesCache = append(measuredValuesCache, measuredValue) | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import ( | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/d2r2/go-bsbmp" | ||||
| 	"github.com/d2r2/go-i2c" | ||||
| @@ -19,9 +20,8 @@ type BME280 struct { | ||||
| 	*types.Sensor | ||||
| } | ||||
|  | ||||
| // GetSensorModel returns the sensor model | ||||
| func (s *BME280) GetSensorModel() types.SensorModel { | ||||
| 	return s.Sensor.SensorModel | ||||
| func (s *BME280) ID() string { | ||||
| 	return s.SensorID | ||||
| } | ||||
|  | ||||
| // Read measured values | ||||
| @@ -124,3 +124,12 @@ func (s *BME280) ReadContinously(ctx context.Context, measuredValueChannel chan< | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Ticker returns a new ticker, which tick every when the sensor should be read | ||||
| func (s *BME280) Ticker() *time.Ticker { | ||||
| 	duration, err := time.ParseDuration(s.TickDuration) | ||||
| 	if err != nil { | ||||
| 		duration = time.Minute | ||||
| 	} | ||||
| 	return time.NewTicker(duration) | ||||
| } | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/go-flucky/flucky/pkg/internal/format" | ||||
| 	"github.com/go-flucky/flucky/pkg/types" | ||||
| @@ -16,9 +17,8 @@ type DHT11 struct { | ||||
| 	*types.Sensor | ||||
| } | ||||
|  | ||||
| // GetSensorModel returns the sensor model | ||||
| func (s *DHT11) GetSensorModel() types.SensorModel { | ||||
| 	return s.Sensor.SensorModel | ||||
| func (s *DHT11) ID() string { | ||||
| 	return s.SensorID | ||||
| } | ||||
|  | ||||
| // Read measured values | ||||
| @@ -98,3 +98,12 @@ func (s *DHT11) ReadContinously(ctx context.Context, measuredValueChannel chan<- | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Ticker returns a new ticker, which tick every when the sensor should be read | ||||
| func (s *DHT11) Ticker() *time.Ticker { | ||||
| 	duration, err := time.ParseDuration(s.TickDuration) | ||||
| 	if err != nil { | ||||
| 		duration = time.Minute | ||||
| 	} | ||||
| 	return time.NewTicker(duration) | ||||
| } | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/go-flucky/flucky/pkg/internal/format" | ||||
| 	"github.com/go-flucky/flucky/pkg/types" | ||||
| @@ -16,9 +17,8 @@ type DHT22 struct { | ||||
| 	*types.Sensor | ||||
| } | ||||
|  | ||||
| // GetSensorModel returns the sensor model | ||||
| func (s *DHT22) GetSensorModel() types.SensorModel { | ||||
| 	return s.Sensor.SensorModel | ||||
| func (s *DHT22) ID() string { | ||||
| 	return s.SensorID | ||||
| } | ||||
|  | ||||
| // Read measured values | ||||
| @@ -98,3 +98,12 @@ func (s *DHT22) ReadContinously(ctx context.Context, measuredValueChannel chan<- | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Ticker returns a new ticker, which tick every when the sensor should be read | ||||
| func (s *DHT22) Ticker() *time.Ticker { | ||||
| 	duration, err := time.ParseDuration(s.TickDuration) | ||||
| 	if err != nil { | ||||
| 		duration = time.Minute | ||||
| 	} | ||||
| 	return time.NewTicker(duration) | ||||
| } | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import ( | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/go-flucky/flucky/pkg/internal/format" | ||||
| 	"github.com/go-flucky/flucky/pkg/types" | ||||
| @@ -19,9 +20,8 @@ type DS18B20 struct { | ||||
| 	*types.Sensor | ||||
| } | ||||
|  | ||||
| // GetSensorModel returns the sensor model | ||||
| func (s *DS18B20) GetSensorModel() types.SensorModel { | ||||
| 	return s.Sensor.SensorModel | ||||
| func (s *DS18B20) ID() string { | ||||
| 	return s.SensorID | ||||
| } | ||||
|  | ||||
| // Read measured values | ||||
| @@ -40,12 +40,12 @@ func (s *DS18B20) Read() ([]*types.MeasuredValue, error) { | ||||
|  | ||||
| 	i := strings.LastIndex(raw, "t=") | ||||
| 	if i == -1 { | ||||
| 		return nil, ErrReadSensor | ||||
| 		return nil, errorReadData | ||||
| 	} | ||||
|  | ||||
| 	c, err := strconv.ParseFloat(raw[i+2:len(raw)-1], 64) | ||||
| 	if err != nil { | ||||
| 		return nil, ErrParseData | ||||
| 		return nil, errorParseData | ||||
| 	} | ||||
|  | ||||
| 	temperatureValue := c / 1000 | ||||
| @@ -97,3 +97,12 @@ func (s *DS18B20) ReadContinously(ctx context.Context, measuredValueChannel chan | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Ticker returns a new ticker, which tick every when the sensor should be read | ||||
| func (s *DS18B20) Ticker() *time.Ticker { | ||||
| 	duration, err := time.ParseDuration(s.TickDuration) | ||||
| 	if err != nil { | ||||
| 		duration = time.Minute | ||||
| 	} | ||||
| 	return time.NewTicker(duration) | ||||
| } | ||||
|   | ||||
| @@ -4,5 +4,7 @@ import ( | ||||
| 	"errors" | ||||
| ) | ||||
|  | ||||
| var ErrParseData = errors.New("Can not parse data") | ||||
| var ErrReadSensor = errors.New("Can not read data from Sensor") | ||||
| var ( | ||||
| 	errorParseData = errors.New("Failed to parse data") | ||||
| 	errorReadData  = errors.New("Failed to read data from sensor") | ||||
| ) | ||||
|   | ||||
| @@ -1,15 +1,12 @@ | ||||
| package sensor | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/go-flucky/flucky/pkg/types" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| type Sensor interface { | ||||
| 	GetSensorModel() types.SensorModel | ||||
| 	ID() string | ||||
| 	Read() ([]*types.MeasuredValue, error) | ||||
| 	ReadChannel(measuredValueChannel chan<- *types.MeasuredValue, errorChannel chan<- error, wg *sync.WaitGroup) | ||||
| 	ReadContinously(ctx context.Context, measuredValueChannel chan<- *types.MeasuredValue, errorChannel chan<- error) | ||||
| 	Ticker() *time.Ticker | ||||
| } | ||||
|   | ||||
| @@ -1,58 +1,46 @@ | ||||
| package sensor | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/go-flucky/flucky/pkg/internal/collect" | ||||
| 	"github.com/go-flucky/flucky/pkg/internal/prittyprint" | ||||
|  | ||||
| 	"github.com/go-flucky/flucky/pkg/types" | ||||
| ) | ||||
|  | ||||
| // Read measured values from sensors | ||||
| func Read(ctx context.Context, sensors []Sensor) ([]*types.MeasuredValue, error) { | ||||
| func Read(sensors []Sensor, measuredValueType types.MeasuredValueType) ([]*types.MeasuredValue, error) { | ||||
|  | ||||
| 	measuredValueChannel := make(chan *types.MeasuredValue, len(sensors)) | ||||
| 	errorChannel := make(chan error, len(sensors)) | ||||
|  | ||||
| 	ReadChannel(ctx, sensors, measuredValueChannel, errorChannel) | ||||
|  | ||||
| 	errors := collect.Errors(errorChannel) | ||||
| 	if len(errors) > 0 { | ||||
| 		return nil, prittyprint.FormatErrors(errors) | ||||
| 	type result struct { | ||||
| 		measuredValues []*types.MeasuredValue | ||||
| 		err            error | ||||
| 	} | ||||
|  | ||||
| 	measuredValues := collect.MeasuredValues(measuredValueChannel) | ||||
| 	resultChannel := make(chan *result, len(sensors)) | ||||
|  | ||||
| 	return measuredValues, nil | ||||
| } | ||||
|  | ||||
| // ReadChannel reads the measured values from sensors and writes them to a | ||||
| // channel. | ||||
| func ReadChannel(ctx context.Context, sensors []Sensor, measuredValueChannel chan<- *types.MeasuredValue, errorChannel chan<- error) { | ||||
|  | ||||
| 	wg := new(sync.WaitGroup) | ||||
| 	wg.Add(len(sensors)) | ||||
|  | ||||
| 	for _, sensor := range sensors { | ||||
| 		go sensor.ReadChannel(measuredValueChannel, errorChannel, wg) | ||||
| 	// producers | ||||
| 	// read measured values | ||||
| 	for _, s := range sensors { | ||||
| 		go func(s Sensor) { | ||||
| 			measuredValues, err := s.Read() | ||||
| 			resultChannel <- &result{ | ||||
| 				measuredValues: measuredValues, | ||||
| 				err:            err, | ||||
| 			} | ||||
| 		}(s) | ||||
| 	} | ||||
|  | ||||
| 	wg.Wait() | ||||
| } | ||||
|  | ||||
| // ReadContinuously reads the measured values continously from sensors and writes | ||||
| // them to a channel. | ||||
| func ReadContinuously(ctx context.Context, sensors []Sensor, measuredValueChannel chan<- *types.MeasuredValue, errorChannel chan<- error) { | ||||
| 	// consumer | ||||
| 	measuredValues := make([]*types.MeasuredValue, 0) | ||||
| 	counter := len(sensors) | ||||
| 	for { | ||||
| 		if counter == 0 { | ||||
| 			break | ||||
| 		} | ||||
| 		select { | ||||
| 		case <-ctx.Done(): | ||||
| 			errorChannel <- fmt.Errorf("Context closed: %v", ctx.Err()) | ||||
| 			return | ||||
| 		default: | ||||
| 			ReadChannel(ctx, sensors, measuredValueChannel, errorChannel) | ||||
| 		case result := <-resultChannel: | ||||
| 			counter-- | ||||
| 			if result.err != nil { | ||||
| 				return nil, result.err | ||||
| 			} | ||||
| 			measuredValues = append(measuredValues, result.measuredValues...) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return types.SelectMeasuredValues(measuredValueType, measuredValues), nil | ||||
| } | ||||
|   | ||||
| @@ -19,6 +19,7 @@ type Sensor struct { | ||||
| 	SensorModel       SensorModel `json:"sensor_model" xml:"sensor_model"` | ||||
| 	SensorEnabled     bool        `json:"sensor_enabled" xml:"sensor_enabled"` | ||||
| 	SensorLastContact *time.Time  `json:"sensor_last_contact" xml:"sensor_last_contact"` | ||||
| 	TickDuration      string      `json:"sensor_tick_duration" xml:"sensor_tick_duration"` | ||||
| 	DeviceID          string      `json:"device_id" xml:"device_id"` | ||||
| 	CreationDate      time.Time   `json:"creation_date" xml:"creation_date"` | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user