Documentation
¶
Overview ¶
Package repair holds one-off data repairs run from the rivolt CLI.
Fragments (repair-charge-fragments): a single physical charging session that the recorder stored as several back-to-back rows, each carrying the session's RUNNING energy total, so the ledger counts the energy once per piece (2026-10-10: 11 pieces, 496.6 kWh counted for ~82 kWh). Cause: a replayed stale Parallax gear frame made the recorder close and reopen the charge on every resubscribe. This package finds those runs and merges each into one row.
Everything here is dry-run first and reversible: the caller writes a CSV backup of every touched row before Apply.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Apply ¶
Apply merges one group in a single transaction: the keeper takes the session's start, start SoC, end, final state, peak power and active seconds, and the other pieces are deleted. Every statement is scoped by user_id and guarded by the rows still being what Load saw.
func FindGroups ¶
FindGroups returns the fragment runs in ps and the runs it refuses.
Pieces chain when they are live charges of one user and vehicle, the next starts within maxGap of the previous end (stale-gear flaps leave ~3 s gaps and ~178 s gaps, one resubscribe TTL), the previous was closed 'abandoned', no drive happened in between, and the next piece is a RUNNING TOTAL rather than an independent session:
- its energy is not below the previous energy, and
- it claims more than its own SoC gain can explain (energy > 1.3 x own gain + 1 kWh), or it repeats the previous energy while its own SoC gain is ~0 (a stale duplicate).
A piece whose SoC gain explains its energy is its own session (a real unplug and re-plug) and ends the run. Missing SoC data means "cannot verify": the run ends. Null-energy pieces ride along (they are removed, never kept). A group needs two or more pieces and at least one with energy. A group whose removed pieces carry a user price or a restamp is returned as skipped, not merged.
Types ¶
type Group ¶
type Group struct{ Pieces []Piece }
Group is a run of two or more pieces of one session, in time order. The keeper is the LAST piece that has an energy value; every other piece (including null-energy ones) is removed.
func (Group) Keeper ¶
Keeper is the row that survives (the zero Piece if no piece has energy; FindGroups never returns such a group).
type Merged ¶
type Merged struct {
StartedAt time.Time
EndedAt time.Time
FinalState string
StartSoC *float64
MaxPowerKW *float64
ActiveSeconds *float64
EnergyKWh *float64
Cost *float64
// CountedKWh / CountedCost are what the pieces contribute today.
CountedKWh float64
CountedCost float64
}
Merged is what the keeper becomes.
type Piece ¶
type Piece struct {
ID uuid.UUID
ExternalID string
UserID uuid.UUID
Username string
VehicleID uuid.UUID
RivianID string
StartedAt time.Time
EndedAt time.Time
StartSoC *float64
EndSoC *float64
EnergyKWh *float64
MaxPowerKW *float64
ActiveSeconds *float64
Cost *float64
Currency string
FinalState string
CostSource string
Source string
// PackKWh is the vehicle's pack capacity (nil = unknown).
PackKWh *float64
// DriveBefore is true when a drive of the same vehicle falls
// between the previous live charge's end and this piece's start.
DriveBefore bool
// HasRestamp is true when a charge_restamps row (a user's place
// correction) points at this piece.
HasRestamp bool
}
Piece is one charges row considered for merging.
func Load ¶
func Load(ctx context.Context, d *sql.DB, user string, since, until time.Time, maxGap time.Duration) ([]Piece, error)
Load reads the candidate pieces: live charges within maxGap of another live charge of the same vehicle. user ("" = all) filters by username; since/until (zero = open) bound started_at.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package phantom is the repair for phantom (stale-copy) live charges: rows the recorder fabricated from a replayed previous-session total, with no power and no SoC change behind them (architect's scratchpad/phantom-charges.md; devils-advocate's audit).
|
Package phantom is the repair for phantom (stale-copy) live charges: rows the recorder fabricated from a replayed previous-session total, with no power and no SoC change behind them (architect's scratchpad/phantom-charges.md; devils-advocate's audit). |
|
Package r2 rebuilds the drives and charges of a vehicle whose vehicleState gear was unusable (R2 before #214: the gear stayed at the last D/R for days, so drives never closed and no charge opened).
|
Package r2 rebuilds the drives and charges of a vehicle whose vehicleState gear was unusable (R2 before #214: the gear stayed at the last D/R for days, so drives never closed and no charge opened). |