Skip to content

Commit 0d203b4

Browse files
committed
fix: data integration retries
1 parent 22c96ba commit 0d203b4

3 files changed

Lines changed: 338 additions & 12 deletions

File tree

‎scheduler/data.go‎

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -82,16 +82,28 @@ type IntegratedData struct {
8282
evdcChargePower float64
8383
loadPower float64
8484
batterySoc float64
85+
timestamp time.Time
86+
sampleCount int // Number of samples integrated
8587
}
8688

87-
// IntegrateSamples computes integrated power values from collected samples.
88-
func (d *DataSamples) IntegrateSamples(pollInterval time.Duration) IntegratedData {
89+
// IntegrateSamples computes integrated power values from collected samples up to the specified cutoff time.
90+
// Only samples with timestamp <= cutoffTime are integrated.
91+
// The cutoffTime represents the end of the integration period and is used as the result timestamp.
92+
// Samples are preserved and must be cleared explicitly using ClearBefore() after successful processing.
93+
func (d *DataSamples) IntegrateSamples(pollInterval time.Duration, cutoffTime time.Time) IntegratedData {
8994
d.mu.Lock()
9095
defer d.mu.Unlock()
9196

9297
var result IntegratedData
98+
result.timestamp = cutoffTime
9399

94100
for _, sample := range d.samples {
101+
// Only integrate samples that belong to this period
102+
if sample.ts.After(cutoffTime) {
103+
continue
104+
}
105+
106+
result.sampleCount++
95107
energyKWh := pollInterval.Seconds() / 3600.0 // Convert to hours
96108

97109
result.pvTotalPower += sample.pvPower * energyKWh
@@ -121,10 +133,25 @@ func (d *DataSamples) IntegrateSamples(pollInterval time.Duration) IntegratedDat
121133
result.loadPower = result.pvTotalPower + result.batteryDischargePower + result.gridImportPower -
122134
result.batteryChargePower - result.gridExportPower - result.evdcChargePower
123135

124-
d.samples = d.samples[:0]
125136
return result
126137
}
127138

139+
// ClearBefore removes all samples with timestamp <= cutoffTime from the collection.
140+
// Should only be called after samples have been successfully processed for that period.
141+
func (d *DataSamples) ClearBefore(cutoffTime time.Time) {
142+
d.mu.Lock()
143+
defer d.mu.Unlock()
144+
145+
// Filter out samples that should be cleared
146+
filteredSamples := make([]DataSample, 0, len(d.samples))
147+
for _, sample := range d.samples {
148+
if sample.ts.After(cutoffTime) {
149+
filteredSamples = append(filteredSamples, sample)
150+
}
151+
}
152+
d.samples = filteredSamples
153+
}
154+
128155
// IsEmpty returns true if there are no samples collected.
129156
func (d *DataSamples) IsEmpty() bool {
130157
d.mu.Lock()
@@ -169,15 +196,27 @@ func (s *MinerScheduler) runDataPoll(samples *DataSamples) error {
169196
}
170197

171198
func (s *MinerScheduler) runDataIntegration(samples *DataSamples, pollInterval time.Duration, dataDB *sql.DB, deviceID int, dryRun bool) error {
172-
if samples.IsEmpty() {
173-
s.logger.Printf("Data integration: no samples collected in period")
199+
// Calculate the period boundary timestamp (end of current integration period)
200+
// This ensures samples are grouped by their integration period
201+
config := s.GetConfig()
202+
now := time.Now()
203+
periodEndTime := now.Truncate(config.PVIntegrationPeriod)
204+
if periodEndTime.Before(now.Add(-config.PVIntegrationPeriod)) {
205+
periodEndTime = periodEndTime.Add(config.PVIntegrationPeriod)
206+
}
207+
208+
// Integrate only samples up to the period boundary
209+
data := samples.IntegrateSamples(pollInterval, periodEndTime)
210+
211+
if data.sampleCount == 0 {
212+
s.logger.Printf("Data integration: no samples collected in period ending at %s", periodEndTime.Format(time.RFC3339))
174213
return nil
175214
}
176215

177-
data := samples.IntegrateSamples(pollInterval)
178-
timestamp := time.Now()
216+
timestamp := data.timestamp
179217

180218
if dataDB == nil {
219+
samples.ClearBefore(periodEndTime)
181220
return nil
182221
}
183222

@@ -193,7 +232,6 @@ func (s *MinerScheduler) runDataIntegration(samples *DataSamples, pollInterval t
193232
}
194233

195234
// Calculate costs using current energy prices
196-
config := s.GetConfig()
197235

198236
// Get current spot price for cost calculations
199237
var gridImportCost, gridExportCost float64
@@ -216,7 +254,8 @@ func (s *MinerScheduler) runDataIntegration(samples *DataSamples, pollInterval t
216254

217255
if dryRun {
218256
// DRY-RUN MODE: Log the action without saving to database
219-
s.logger.Printf("Data integration [DRY-RUN]: would save metrics for device_id=%d at %s", deviceID, timestamp.Format(time.RFC3339))
257+
s.logger.Printf("Data integration [DRY-RUN]: would save metrics for device_id=%d at %s (samples: %d)",
258+
deviceID, timestamp.Format(time.RFC3339), data.sampleCount)
220259
s.logger.Printf(" PV: %.3f kWh, Grid Import: %.3f kWh (%.3f), Grid Export: %.3f kWh (%.3f)",
221260
data.pvTotalPower, data.gridImportPower, gridImportCost, data.gridExportPower, gridExportCost)
222261
s.logger.Printf(" Battery Charge: %.3f kWh, Battery Discharge: %.3f kWh, SOC: %.1f%%",
@@ -228,6 +267,7 @@ func (s *MinerScheduler) runDataIntegration(samples *DataSamples, pollInterval t
228267
if weatherSymbol != nil {
229268
s.logger.Printf(" Weather: %s", *weatherSymbol)
230269
}
270+
samples.ClearBefore(periodEndTime)
231271
} else {
232272
// Insert comprehensive energy flow data
233273
_, err = dataDB.Exec(
@@ -248,10 +288,14 @@ func (s *MinerScheduler) runDataIntegration(samples *DataSamples, pollInterval t
248288
)
249289
if err != nil {
250290
s.logger.Printf("Data integration: failed to insert metrics: %v", err)
251-
return nil
291+
return err
252292
}
253293

254-
s.logger.Printf("Data integration: saved metrics for device_id=%d at %s", deviceID, timestamp.Format(time.RFC3339))
294+
// Only clear samples for this period after successful DB insertion
295+
samples.ClearBefore(periodEndTime)
296+
297+
s.logger.Printf("Data integration: saved metrics for device_id=%d at %s (samples: %d)",
298+
deviceID, timestamp.Format(time.RFC3339), data.sampleCount)
255299
s.logger.Printf(" PV: %.3f kWh, Grid Import: %.3f kWh (%.3f), Grid Export: %.3f kWh (%.3f)",
256300
data.pvTotalPower, data.gridImportPower, gridImportCost, data.gridExportPower, gridExportCost)
257301
s.logger.Printf(" Battery Charge: %.3f kWh, Battery Discharge: %.3f kWh, SOC: %.1f%%",

0 commit comments

Comments
 (0)