From b97f1f465f22750d2d77e522123146484ded4832 Mon Sep 17 00:00:00 2001 From: Russell Stanley Date: Fri, 4 Mar 2022 17:09:50 +1030 Subject: [PATCH] cmd/rv/probe.go improved error handling --- cmd/rv/main.go | 2 +- cmd/rv/probe.go | 66 +++++++++++++++++++++---------------- codec/h264/parse.go | 2 +- turbidity/turbidity.go | 12 +++---- turbidity/turbidity_test.go | 6 ++-- 5 files changed, 48 insertions(+), 40 deletions(-) diff --git a/cmd/rv/main.go b/cmd/rv/main.go index 21901ea0..bd22e8b9 100644 --- a/cmd/rv/main.go +++ b/cmd/rv/main.go @@ -148,7 +148,7 @@ func main() { p, err := NewTurbidityProbe(*log, 60*time.Second) if err != nil { - log.Log(logger.Error, "could not create new turbidity probe", "error", err.Error()) + log.Log(logger.Fatal, "could not create new turbidity probe", "error", err.Error()) } log.Log(logger.Debug, "initialising netsender client") diff --git a/cmd/rv/probe.go b/cmd/rv/probe.go index c94a2a25..657382c1 100644 --- a/cmd/rv/probe.go +++ b/cmd/rv/probe.go @@ -31,6 +31,8 @@ package main import ( "bytes" + "errors" + "fmt" "os" "time" @@ -80,7 +82,7 @@ func NewTurbidityProbe(log logger.Logger, delay time.Duration) (*turbidityProbe, template := gocv.IMRead("../../turbidity/images/template.jpg", gocv.IMReadGrayScale) ts, err := turbidity.NewTurbiditySensor(template, standard, k1, k2, filterSize, scale, alpha, log) if err != nil { - log.Error("failed create turbidity sensor", "error", err.Error()) + return nil, fmt.Errorf("failed to create turbidity sensor: %w", err) } tp.ts = ts return tp, nil @@ -89,63 +91,58 @@ func NewTurbidityProbe(log logger.Logger, delay time.Duration) (*turbidityProbe, // Write, reads input h264 frames in the form of a byte stream and writes the the sharpness and contrast // scores of a video to the the turbidity probe. func (tp *turbidityProbe) Write(p []byte) (int, error) { - out := len(p) if tp.buffer.Len() == 0 { // The first entry in the buffer must be a keyframe to speed up decoding. video, err := h264.Trim(p) if err != nil { - tp.log.Log(logger.Debug, "no key frame detected", "error", err.Error()) - return 0, nil + return 0, fmt.Errorf("could not trim h264: %w", err) } n, err := tp.buffer.Write(video) if err != nil { - tp.log.Log(logger.Error, "could not write trimmed video to buffer", "error", err.Error()) tp.buffer.Reset() - return 0, nil + return 0, fmt.Errorf("could not write trimmed video to buffer: %w", err) } tp.log.Log(logger.Debug, "video trimmed, write keyframe complete", "size(bytes)", n) } else if tp.buffer.Len() < bufferLimit { // Buffer is limited to speed up decoding. n, err := tp.buffer.Write(p) if err != nil { - tp.log.Log(logger.Error, "could not write to buffer, reseting", "error", err.Error()) tp.buffer.Reset() - return 0, nil + return 0, fmt.Errorf("could not write to buffer: %w", err) } tp.log.Log(logger.Debug, "write to video buffer complete", "size(bytes)", n) - } else { - out = 0 } select { case <-tp.ticker.C: tp.log.Log(logger.Debug, "beginning turbidity calculation") startTime := time.Now() - tp.turbidityCalculation() + err := tp.turbidityCalculation() + if err != nil { + return 0, fmt.Errorf("could not calculate turbidity: %w", err) + } tp.log.Log(logger.Debug, "finished turbidity calculation", "total duration (sec)", time.Since(startTime).Seconds()) default: } - return out, nil + return len(p), nil } func (tp *turbidityProbe) Close() error { return nil } -func (tp *turbidityProbe) turbidityCalculation() { +func (tp *turbidityProbe) turbidityCalculation() error { var imgs []gocv.Mat img := gocv.NewMat() // Write byte array to a temp file. file, err := os.CreateTemp("temp", "video*.h264") if err != nil { - tp.log.Error("failed to create temp file", "error", err.Error()) - return + return fmt.Errorf("failed to create temp file: %w", err) } tp.log.Log(logger.Debug, "writing to file", "buffer size(bytes)", tp.buffer.Len()) _, err = file.Write(tp.buffer.Bytes()) if err != nil { - tp.log.Error("failed to write to temporary file", "error", err.Error()) - return + return fmt.Errorf("failed to write to temporary file: %w", err) } tp.log.Log(logger.Debug, "write to file success", "buffer size(bytes)", tp.buffer.Len()) tp.buffer.Reset() @@ -154,37 +151,48 @@ func (tp *turbidityProbe) turbidityCalculation() { startTime := time.Now() vc, err := gocv.VideoCaptureFile(file.Name()) if err != nil { - tp.log.Error("failed to open video file", "error", err.Error()) - return + return fmt.Errorf("failed to open video file: %w", err) } tp.log.Log(logger.Debug, "video capture open", "total duration (sec)", time.Since(startTime).Seconds()) - // Store each frame untill max. + // Store each frame until maximum amount is reached. startTime = time.Now() for vc.Read(&img) && len(imgs) < maxImages { imgs = append(imgs, img.Clone()) } if len(imgs) <= 0 { - tp.log.Log(logger.Warning, "no frames found") - return + return errors.New("no frames found") } tp.log.Log(logger.Debug, "read time", "total duration (sec)", time.Since(startTime).Seconds()) // Process video data to get saturation and contrast scores. res, err := tp.ts.Evaluate(imgs) if err != nil { - tp.log.Error("evaluate failed", "error", err.Error()) - } else { - tp.contrast = stat.Mean(res.Contrast, nil) - tp.sharpness = stat.Mean(res.Sharpness, nil) + err_ := cleanUp(file.Name(), vc) + if err_ != nil { + return fmt.Errorf("could not clean up after evaluation error: %w", err_) + } + return fmt.Errorf("evaluation error: %w", err) } - err = os.Remove(file.Name()) + tp.contrast = stat.Mean(res.Contrast, nil) + tp.sharpness = stat.Mean(res.Sharpness, nil) + + err = cleanUp(file.Name(), vc) if err != nil { - tp.log.Error("could not remove file", "error", err.Error()) + return fmt.Errorf("could not clean up: %w", err) + } + return nil +} + +func cleanUp(file string, vc *gocv.VideoCapture) error { + err := os.Remove(file) + if err != nil { + return fmt.Errorf("could not remove temp file: %w", err) } err = vc.Close() if err != nil { - tp.log.Error("could not close video capture", "error", err.Error()) + return fmt.Errorf("could not close video capture device: %w", err) } + return nil } diff --git a/codec/h264/parse.go b/codec/h264/parse.go index e9d97230..f341517e 100644 --- a/codec/h264/parse.go +++ b/codec/h264/parse.go @@ -79,7 +79,7 @@ func (s *frameScanner) readByte() (b byte, ok bool) { return b, true } -// Trim, will trim down a given byte stream of video data so that a key frame appears first. +// Trim will trim down a given byte stream of video data so that a key frame appears first. func Trim(n []byte) ([]byte, error) { sc := frameScanner{buf: n} for { diff --git a/turbidity/turbidity.go b/turbidity/turbidity.go index 04a24209..8bbc4d6e 100644 --- a/turbidity/turbidity.go +++ b/turbidity/turbidity.go @@ -48,11 +48,11 @@ type TurbiditySensor struct { standard, standardCorners gocv.Mat k1, k2, sobelFilterSize int scale, alpha float64 - logger logger.Logger + log logger.Logger } // NewTurbiditySensor returns a new TurbiditySensor. -func NewTurbiditySensor(template, standard gocv.Mat, k1, k2, sobelFilterSize int, scale, alpha float64, logger logger.Logger) (*TurbiditySensor, error) { +func NewTurbiditySensor(template, standard gocv.Mat, k1, k2, sobelFilterSize int, scale, alpha float64, log logger.Logger) (*TurbiditySensor, error) { ts := new(TurbiditySensor) templateCorners := gocv.NewMat() standardCorners := gocv.NewMat() @@ -79,7 +79,7 @@ func NewTurbiditySensor(template, standard gocv.Mat, k1, k2, sobelFilterSize int ts.k1, ts.k2, ts.sobelFilterSize = k1, k2, sobelFilterSize ts.alpha, ts.scale = alpha, scale - ts.logger = logger + ts.log = log return ts, nil } @@ -97,18 +97,18 @@ func (ts TurbiditySensor) Evaluate(imgs []gocv.Mat) (*Results, error) { return nil, fmt.Errorf("could not transform image: %d: %w", i, err) } - ts.logger.Log(logger.Debug, "transform successful", "transform duration (sec)", time.Since(timer).Seconds()) + ts.log.Log(logger.Debug, "transform successful", "transform duration (sec)", time.Since(timer).Seconds()) timer = time.Now() edge := ts.sobel(marker) - ts.logger.Log(logger.Debug, "sobel filter successful", "sobel duration", time.Since(timer).Seconds()) + ts.log.Log(logger.Debug, "sobel filter successful", "sobel duration", time.Since(timer).Seconds()) timer = time.Now() sharpScore, contScore, err := ts.EvaluateImage(marker, edge) if err != nil { return result, err } - ts.logger.Log(logger.Debug, "sharpness and contrast evaluation successful", "evaluation duration", time.Since(timer).Seconds()) + ts.log.Log(logger.Debug, "sharpness and contrast evaluation successful", "evaluation duration", time.Since(timer).Seconds()) result.Update(sharpScore, contScore, float64(i), i) } return result, nil diff --git a/turbidity/turbidity_test.go b/turbidity/turbidity_test.go index 6d27d833..4c97b65a 100644 --- a/turbidity/turbidity_test.go +++ b/turbidity/turbidity_test.go @@ -74,7 +74,7 @@ func TestImages(t *testing.T) { MaxBackups: logMaxBackup, MaxAge: logMaxAge, } - log := logger.New(logVerbosity, io.MultiWriter(fileLog), logSuppress) + log := *logger.New(logVerbosity, io.MultiWriter(fileLog), logSuppress) template := gocv.IMRead("images/template.jpg", gocv.IMReadGrayScale) standard := gocv.IMRead("images/default.jpg", gocv.IMReadGrayScale) @@ -89,7 +89,7 @@ func TestImages(t *testing.T) { } } - ts, err := NewTurbiditySensor(template, standard, k1, k2, filterSize, scale, alpha, *log) + ts, err := NewTurbiditySensor(template, standard, k1, k2, filterSize, scale, alpha, log) if err != nil { t.Fatalf("could not create turbidity sensor: %v", err) } @@ -111,7 +111,7 @@ func TestImages(t *testing.T) { results.Update(stat.Mean(sample_result.Sharpness, nil), stat.Mean(sample_result.Contrast, nil), float64(i)*increment, i) } - err = plotResults(results.Turbidity, normalize(results.Sharpness), normalize(results.Contrast)) + err = plotResults(results.Turbidity, results.Sharpness, results.Contrast) if err != nil { t.Fatalf("plotting Failed: %v", err) }