changeset 2967:7c301ff449bc

Fairway availibility: Started with class building.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Mon, 08 Apr 2019 18:54:13 +0200
parents 4bc2cc6364bc
children ac5ba5a0e963 ef79b635857b
files pkg/controllers/bottlenecks.go
diffstat 1 files changed, 112 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/pkg/controllers/bottlenecks.go	Mon Apr 08 17:44:49 2019 +0200
+++ b/pkg/controllers/bottlenecks.go	Mon Apr 08 18:54:13 2019 +0200
@@ -80,6 +80,108 @@
 `
 )
 
+type (
+	availReferenceValue struct {
+		level int
+		value int
+	}
+
+	availMeasurement struct {
+		when  time.Time
+		depth int
+		value int
+	}
+)
+
+func min(a, b int) int {
+	if a < b {
+		return a
+	}
+	return b
+}
+
+func max(a, b int) int {
+	if a > b {
+		return a
+	}
+	return b
+}
+
+func interpolate(
+	m1, m2 *availMeasurement,
+	diff time.Duration,
+) int {
+	tdiff := m2.when.Sub(m1.when)
+
+	// f(0)     = m1.value
+	// f(tdiff) = m2.value
+	// f(x) = m*x + b
+	// m1.value = m*0 + b     <=> b = m1.value
+	// m2.value = m*tdiff + b <=> m = (m2.value - b)/tdiff
+	// f(diff) = diff*(m2.value - m1.value)/tdiff + m1.value
+
+	return int(diff*time.Duration(m2.value-m1.value)/tdiff) + m1.value
+}
+
+func classifyAvailMeasurements(
+	from, to time.Time,
+	measurements []availMeasurement,
+	classes []availReferenceValue,
+) []time.Duration {
+
+	results := make([]time.Duration, len(classes)+2)
+
+	if from.Before(measurements[0].when) {
+		results[len(results)-1] = measurements[0].when.Sub(from)
+		from = measurements[0].when
+	}
+
+	if to.After(measurements[len(measurements)-1].when) {
+		results[len(results)-1] += to.Sub(measurements[len(measurements)-1].when)
+		to = measurements[len(measurements)-1].when
+	}
+
+	for i := 0; i < len(measurements)-1; i++ {
+		p1 := &measurements[i]
+		p2 := &measurements[i+1]
+		tdiff := p2.when.Sub(p1.when)
+		if tdiff <= 0 {
+			continue
+		}
+
+		if from.After(p2.when) || to.Before(p1.when) {
+			continue
+		}
+
+		if from.After(p1.when) {
+			tdiff2 := from.Sub(p1.when)
+			vf := interpolate(p1, p2, tdiff2)
+			p1 = &availMeasurement{when: from, value: vf}
+			tdiff = p2.when.Sub(from)
+		}
+
+		if to.Before(p2.when) {
+			tdiff2 := p2.when.Sub(to)
+			vt := interpolate(p1, p2, tdiff2)
+			p2 = &availMeasurement{when: to, value: vt}
+			tdiff = p2.when.Sub(p1.when)
+		}
+
+		if max(p1.value, p2.value) <= classes[0].value {
+			results[0] += tdiff
+			continue
+		}
+		if min(p1.value, p2.value) > classes[len(classes)-1].value {
+			results[len(results)-2] += tdiff
+			continue
+		}
+
+		// TODO: Do the real classes.
+	}
+
+	return results
+}
+
 func bottleneckAvailabilty(
 	_ interface{},
 	req *http.Request,
@@ -95,21 +197,16 @@
 		return
 	}
 
-	type referenceValue struct {
-		level int
-		value int
-	}
-
 	ctx := req.Context()
 
-	loadReferenceValues := func() ([]referenceValue, error) {
+	loadReferenceValues := func() ([]availReferenceValue, error) {
 		rows, err := conn.QueryContext(ctx, selectGaugeLevelsSQL, bn)
 		if err != nil {
 			return nil, err
 		}
 		defer rows.Close()
 
-		var levels []referenceValue
+		var levels []availReferenceValue
 
 	loop:
 		for rows.Next() {
@@ -135,7 +232,7 @@
 					continue loop
 				}
 			}
-			levels = append(levels, referenceValue{level: level, value: value})
+			levels = append(levels, availReferenceValue{level: level, value: value})
 		}
 
 		if err := rows.Err(); err != nil {
@@ -147,7 +244,7 @@
 		return levels, nil
 	}
 
-	var refVals []referenceValue
+	var refVals []availReferenceValue
 	if refVals, err = loadReferenceValues(); err != nil {
 		return
 	}
@@ -206,13 +303,7 @@
 		los = 1
 	}
 
-	type measurement struct {
-		when  time.Time
-		depth int
-		level int
-	}
-
-	loadDepthValues := func() ([]measurement, error) {
+	loadDepthValues := func() ([]availMeasurement, error) {
 
 		rows, err := conn.QueryContext(
 			ctx, selectAvailableDepthSQL, bn, los, from, to)
@@ -221,13 +312,14 @@
 		}
 		defer rows.Close()
 
-		var ms []measurement
+		var ms []availMeasurement
 
 		for rows.Next() {
-			var m measurement
-			if err := rows.Scan(&m.when, &m.depth, &m.level); err != nil {
+			var m availMeasurement
+			if err := rows.Scan(&m.when, &m.depth, &m.value); err != nil {
 				return nil, err
 			}
+			m.when = m.when.UTC()
 			ms = append(ms, m)
 		}
 
@@ -238,7 +330,7 @@
 		return ms, nil
 	}
 
-	var ms []measurement
+	var ms []availMeasurement
 
 	if ms, err = loadDepthValues(); err != nil {
 		return