Skip to content

Commit 98a8506

Browse files
committed
implements automatic stepping if needed fix #15
1 parent e584da9 commit 98a8506

File tree

2 files changed

+49
-52
lines changed

2 files changed

+49
-52
lines changed

file.go

+38-34
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ const (
1616
headerFields = 9
1717
headerDateFormat = "02/01/2006" // 31/12/2021
1818
dataDateFormat = "2006-01-02T15:04:05-07:00" // 2021-12-31T00:30:00+01:00
19-
steppingHalfHour = 30 * time.Minute
20-
steppingHour = time.Hour
19+
noSteppingValue = -1
20+
dataStartLine = 4
21+
defaultStepping = 30 * time.Minute
2122
)
2223

2324
type CSVHeader struct {
@@ -30,6 +31,7 @@ type CSVHeader struct {
3031
type point struct {
3132
Time time.Time
3233
Value float64 // kW
34+
Conso float64 // kWh
3335
}
3436

3537
func parseFile(path string) (header CSVHeader, data []point, err error) {
@@ -49,24 +51,10 @@ func parseFile(path string) (header CSVHeader, data []point, err error) {
4951
return
5052
}
5153
// Parse data
52-
if data, err = parseData(cr); err != nil {
54+
if data, err = parseData(cr, header.Step); err != nil {
5355
err = fmt.Errorf("failed to parse data: %w", err)
5456
return
5557
}
56-
// Try to guess stepping if missing (saw in the wild)
57-
if header.Step == -1 {
58-
for index, point := range data {
59-
if point.Time.Minute() == 30 {
60-
header.Step = steppingHalfHour
61-
break
62-
}
63-
if index > 1 {
64-
// 2 records processed and 30min step not encountered, guessing it is 60min step
65-
header.Step = steppingHour
66-
break
67-
}
68-
}
69-
}
7058
return
7159
}
7260

@@ -101,25 +89,21 @@ func parseHeader(cr *csv.Reader) (header CSVHeader, err error) {
10189
return
10290
}
10391
step_str := records[8]
104-
if step_str == "" {
105-
// step empty
106-
header.Step = -1
107-
} else {
92+
if step_str != "" {
10893
var step int
10994
if step, err = strconv.Atoi(step_str); err != nil {
11095
err = fmt.Errorf("non integer stepping found: %s", err)
11196
return
112-
} else if header.Step != 30 && header.Step != 60 {
113-
err = fmt.Errorf("unexpected stepping found: %d", header.Step)
114-
return
115-
} else {
116-
header.Step = time.Duration(step) * time.Minute
11797
}
98+
header.Step = time.Duration(step) * time.Minute
99+
} else {
100+
// step empty
101+
header.Step = noSteppingValue
118102
}
119103
return
120104
}
121105

122-
func parseData(cr *csv.Reader) (data []point, err error) {
106+
func parseData(cr *csv.Reader, stepping time.Duration) (data []point, err error) {
123107
// nb of records changes for data
124108
cr.FieldsPerRecord = 2
125109
// remove data header
@@ -134,9 +118,14 @@ func parseData(cr *csv.Reader) (data []point, err error) {
134118
line int
135119
recordTime time.Time
136120
recordValue int
121+
computedkWh float64
137122
)
138-
data = make([]point, 0, 365*24*2) // most people will analyse a full year (make more sense for tempo)
139-
for line = 4; ; line++ {
123+
if stepping == noSteppingValue {
124+
data = make([]point, 0, 365*24*(time.Hour/defaultStepping))
125+
} else {
126+
data = make([]point, 0, 365*24*(time.Hour/stepping))
127+
}
128+
for line = dataStartLine; ; line++ {
140129
// read line
141130
records, err = cr.Read()
142131
if err != nil {
@@ -156,19 +145,34 @@ func parseData(cr *csv.Reader) (data []point, err error) {
156145
err = fmt.Errorf("failed to parse record value: %w", err)
157146
break
158147
}
159-
// checks
160-
if recordTime.Minute() != 30 && recordTime.Minute() != 0 {
161-
err = fmt.Errorf("minutes should always be 00 or 30: %v", recordTime)
162-
break
163-
}
148+
// check
164149
if recordTime.Second() != 0 {
165150
err = fmt.Errorf("seconds should always be 00: %v", recordTime)
166151
break
167152
}
153+
// Determine stepping if needed
154+
if stepping == noSteppingValue {
155+
if line > dataStartLine {
156+
// get previous point and compute stepping
157+
prevPoint := data[len(data)-1]
158+
stepping := recordTime.Sub(prevPoint.Time)
159+
computedkWh = float64(recordValue) / 1000 / float64(time.Hour/stepping)
160+
if line == dataStartLine+1 {
161+
// compute the first point kWh using the same stepping
162+
firstPoint := data[len(data)-1]
163+
firstPoint.Conso = firstPoint.Value / 1000 / float64(time.Hour/stepping)
164+
data[len(data)-1] = firstPoint
165+
}
166+
}
167+
// else first point, can not compute stepping without a previous point, waiting for second point to retro compute first point
168+
} else {
169+
computedkWh = float64(recordValue) / 1000 / float64(time.Hour/stepping)
170+
}
168171
// save value
169172
data = append(data, point{
170173
Time: recordTime.In(frLocation), // make sure every date time in this program is in the same loc
171174
Value: float64(recordValue) / 1000, // convert W to kW
175+
Conso: computedkWh,
172176
})
173177
}
174178
if errors.Is(err, io.EOF) {

main.go

+11-18
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"flag"
55
"fmt"
66
"log"
7-
"os"
87
"time"
98
)
109

@@ -54,7 +53,6 @@ func main() {
5453

5554
func compute(header CSVHeader, data []point, montly bool) {
5655
var (
57-
pointAdjustedConso float64
5856
totalConso, monthConso float64
5957
totalBase, monthBase, pointBase float64
6058
totalHC, monthHC, pointHC float64
@@ -64,32 +62,27 @@ func compute(header CSVHeader, data []point, montly bool) {
6462
fmt.Printf("PRM:\t\t%s\n", header.PRMID)
6563
fmt.Printf("Start:\t\t%v\n", header.Start)
6664
fmt.Printf("End:\t\t%v\n", header.End)
67-
fmt.Printf("Stepping:\t%v\n", header.Step)
65+
if header.Step == noSteppingValue {
66+
fmt.Printf("Stepping:\tnot fixed (automatically detected for each point)\n")
67+
} else {
68+
fmt.Printf("Stepping:\t%v\n", header.Step)
69+
}
6870
for index, point := range data {
6971
// Adjust time for start and not end
7072
adjustedTime := point.Time.Add(header.Step * -1)
7173
if index == 0 {
7274
refMonth = adjustedTime
7375
}
74-
// Adjust average watt consumption to kWh
75-
if header.Step == steppingHour {
76-
pointAdjustedConso = point.Value
77-
} else if header.Step == steppingHalfHour {
78-
pointAdjustedConso = point.Value / 2
79-
} else {
80-
fmt.Printf("unexpected stepping: %s", header.Step)
81-
os.Exit(1)
82-
}
8376
// Compute price for current point
84-
pointBase = pointAdjustedConso * getBasePrice(adjustedTime)
85-
pointHC = pointAdjustedConso * getHCPrice(adjustedTime)
86-
pointTempo = pointAdjustedConso * getTempoPrice(adjustedTime)
77+
pointBase = point.Conso * getBasePrice(adjustedTime)
78+
pointHC = point.Conso * getHCPrice(adjustedTime)
79+
pointTempo = point.Conso * getTempoPrice(adjustedTime)
8780
// Handle months
8881
if refMonth.Year() == adjustedTime.Year() && refMonth.Month() == adjustedTime.Month() {
8982
monthBase += pointBase
9083
monthHC += pointHC
9184
monthTempo += pointTempo
92-
monthConso += pointAdjustedConso
85+
monthConso += point.Conso
9386
} else {
9487
if montly {
9588
// Print total for previous month
@@ -104,11 +97,11 @@ func compute(header CSVHeader, data []point, montly bool) {
10497
monthBase = pointBase
10598
monthHC = pointHC
10699
monthTempo = pointTempo
107-
monthConso = pointAdjustedConso
100+
monthConso = point.Conso
108101
refMonth = adjustedTime
109102
}
110103
// Add to total
111-
totalConso += pointAdjustedConso
104+
totalConso += point.Conso
112105
totalBase += pointBase
113106
totalHC += pointHC
114107
totalTempo += pointTempo

0 commit comments

Comments
 (0)