Skip to content

refactor: extract DB records from compound state tuples: simple atoms (Phase 5 final)#5259

Open
brianmay wants to merge 6 commits into
mainfrom
refactor/extract-records-from-states
Open

refactor: extract DB records from compound state tuples: simple atoms (Phase 5 final)#5259
brianmay wants to merge 6 commits into
mainfrom
refactor/extract-records-from-states

Conversation

@brianmay
Copy link
Copy Markdown
Collaborator

@brianmay brianmay commented Apr 5, 2026

Summary

This PR completes the state machine simplification by replacing compound state tuples like {:driving, status, %Log.Drive{}}, {:charging, %Log.ChargingProcess{}}, and {:updating, %Log.Update{}} with simple atoms (:driving, :charging, :updating). DB records and sub-states are stored in Data struct fields.

Changes (4 commits)

Commit 1: Add preparatory fields to Data struct

Adds current_drive, current_charging_process, current_update, and driving_status fields.

Commit 2: Populate Data struct fields in parallel with compound state tuples

Every transition into a compound state now also updates the corresponding struct fields.

Commit 3: Migrate compound state reads to struct field guards

Replaces pattern-match reads with guards on the struct fields.

Commit 4: Replace compound state tuples with simple atoms throughout state machine

This is the main simplification commit. After this:

  • The GenState state is always a simple atom
  • {:driving, status, drive}:driving (status in data.driving_status, drive in data.current_drive)
  • {:charging, cproc}:charging (cproc in data.current_charging_process)
  • {:updating, update}:updating (update in data.current_update)

Key changes in this commit:

  • All 12 next_state positions now use simple atoms
  • Driving sub-state machine (available → unavailable → offline) is tracked in data.driving_status instead of the state tuple's second element
  • All driving handlers match :driving with guards on driving_status
  • suspend_logging, fetch/can_fall_asleep, and fetch/reachable all use simple atom matching
  • Summary module updated to handle the new state format

Refactoring sequence (complete!)

  1. ✅ Replace fake_online_state integers with pre_online_check atoms
  2. ✅ Add struct fields + populate them + migrate reads
  3. This PR: Replace compound states with simple atoms

Before vs After

Before (hard to read):

def handle_event(:internal, {:update, {:offline, _}}, {:driving, {:unavailable, n}, drv}, ...)
def handle_event(:internal, {:update, {:offline, _}}, {:driving, {:offline, last}, drive}, ...)
def handle_event(:info, {:stream, %Stream.Data{} = stream_data}, {:driving, status, drv}, ...)

After (clean):

def handle_event(:internal, {:update, {:offline, _}}, :driving, %Data{driving_status: {:unavailable, n}}) do ...
def handle_event(:internal, {:update, {:offline, _}}, :driving, %Data{driving_status: {:offline, last}}) do ...
def handle_event(:info, {:stream, %Stream.Data{}}, :driving, %Data{driving_status: :available, current_drive: drv}) do ...

Testing

All commits are no-behaviour-change mechanical refactors. Existing tests pass.

@brianmay brianmay changed the title Add preparatory fields to Data struct for extracting DB records Extract DB records from compound state tuples: preparatory Data struct changes Apr 5, 2026
@brianmay
Copy link
Copy Markdown
Collaborator Author

brianmay commented Apr 5, 2026

This is only the first phase so far, we haven't seen the simplification to the compound state tuples yet. That will come later.

@brianmay
Copy link
Copy Markdown
Collaborator Author

brianmay commented Apr 5, 2026

As before, this was done with AI. It was going to make one big pull request, then we agreed that breaking it up might be better.

@swiffer swiffer force-pushed the mcu2-upgraded-cars branch 2 times, most recently from c7b2015 to 5ccde37 Compare April 6, 2026 17:04
@brianmay brianmay force-pushed the mcu2-upgraded-cars branch from 5ccde37 to ba33348 Compare April 9, 2026 04:58
@brianmay brianmay changed the title Extract DB records from compound state tuples: preparatory Data struct changes Extract DB records from compound state tuples: struct fields + read migration Apr 11, 2026
@brianmay brianmay changed the title Extract DB records from compound state tuples: struct fields + read migration Extract DB records from compound state tuples: simple atoms (Phase 5 final) Apr 11, 2026
@brianmay
Copy link
Copy Markdown
Collaborator Author

brianmay commented Apr 11, 2026

I decided to do the full refactor in one pull request. While this the PR more complicated, don't think you can really review this probably unless you can see the entire picture.

The steps are still there, but as separate commits. Which probably should stay as separate commits.

Key questions before merging:

  1. Does this make things simpler and easier to understand?

I think by making the state an enum only and putting state data into the data variable we have made it easier to understand. If anyone disagrees, feel free to speak out :-)

  1. Is it correct?

It looks OK to me... Although certainly is scope for errors to creep in. Guess next step will be to test this.

@brianmay brianmay force-pushed the refactor/extract-records-from-states branch from f2d2ed5 to 2f1f3fe Compare April 11, 2026 04:18
@brianmay
Copy link
Copy Markdown
Collaborator Author

I spotted a place where the logic had been changed and was more complicated then required. Fixed now.

@brianmay brianmay force-pushed the refactor/extract-records-from-states branch 2 times, most recently from 96e4597 to 5317d27 Compare April 14, 2026 05:58
@brianmay
Copy link
Copy Markdown
Collaborator Author

All tests are expected to pass.

Still need to manually review the "Fix compound state tuple handling after dce479c refactoring" commit - haven't done that yet.

@brianmay
Copy link
Copy Markdown
Collaborator Author

Might make sense to merge the last three commits into the "Migrate compound state reads to struct field guards" commit, seeing as all they do is fix problems caused by this commit.

Will decide when more awake :-)

@brianmay brianmay force-pushed the refactor/extract-records-from-states branch from 5317d27 to 089956b Compare April 14, 2026 06:13
@JakobLichterfeld JakobLichterfeld added enhancement New feature or request area:teslamate Related to TeslaMate core labels May 1, 2026
@JakobLichterfeld JakobLichterfeld changed the title Extract DB records from compound state tuples: simple atoms (Phase 5 final) refactor: extract DB records from compound state tuples: simple atoms (Phase 5 final) May 1, 2026
brianmay added 6 commits May 5, 2026 11:22
…compound states

These fields prepare for the next refactoring step where we'll move DB records
out of compound state tuples like {:driving, status, %Log.Drive{}} into dedicated
Data struct fields.

This commit only adds the fields without using them yet, keeping the change
minimal and focused. Future commits will:
1. Update state transitions to populate these fields
2. Update pattern matches to read from fields instead of state tuples
3. Remove compound states in favor of simple atoms

No behavior changes in this commit.
Each transition into {:driving, ...}, {:charging, ...}, or {:updating, ...}
now also updates the corresponding Data struct fields:

  - current_drive / driving_status   (all 9 driving transitions)
  - current_charging_process         (both charging entry points)
  - current_update                   (single update entry)

The compound state tuples are unchanged so all existing pattern matches
continue to work. The struct fields mirror the tuple contents and will be
used for reads in a future PR, at which point the compound tuples can be
replaced with simple atoms.

No behaviour changes.
This PR migrates all pattern-match reads of compound state tuples
({:driving, status, drive}, {:charging, cproc}, {:updating, update})
to use the corresponding Data struct fields instead.

Changes:
- vehicle_in_service driving case: guard on data.current_drive != nil,
  read drive from data.current_drive, clear both current_drive and
  driving_status on exit from driving state
- remaining_distance interval: use {current_drive, current_charging_process}
  tuple instead of compound state pattern match
- asleep handler: guard on driving_status != nil && driving_status != :available
  to detect offline sub-states, clear driving_status on :start exit
- fetch can_fall_asleep?: add guards on current_drive/update/charging_process
- fetch reachable: same guards added
- All 6 transitions to :start from driving state now clear driving_status: nil

The compound state tuples remain in next_state positions (handled in prior
PR), so this is purely a read migration. No behaviour changes expected.

No behaviour changes - all guards are guaranteed true at runtime given
correct dual-write population from prior PR.
The dce479c commit converted :driving, :charging, :updating states to simple atoms but left :asleep, :offline, :suspended as compound tuples, causing Summary.format_state and various handlers to fail.

Fixes:
- Summary.format_state: Handle compound tuples like {:asleep, interval}, {:offline, interval}
- driving: Add handler for {:asleep, _} in :driving state (was only matching {:offline, _})
- driving offline: Fix offline handler to not overwrite {:offline, _} without checking timeout
- charging: Convert {:charging, cproc} to simple :charging atom
- updating: Convert {:updating, _update_id} to simple :updating atom
- :summary call: Include driving_status in attrs map for Summary.into
- vehicle_in_service handler: Use :driving instead of {:driving, _, _}

All 91 vehicle tests and 333 total tests pass.
The interval calculation for API errors was unnecessarily restructured
during the read-migration phase. The original case on state was still
correct at that point and should have remained unchanged.

With simple atoms now in place, the correct form is restored:
  :driving -> 10
  :charging -> 15
  :online -> 20
  _ -> 30

This is a 4-line case expression instead of the 14-line restructure
that was added during read migration.
The guards 'when data.current_drive != nil' etc. on the simple atom
patterns ':driving', ':updating', ':charging' are unnecessary. The state
atom itself is the source of truth - if we're in ':driving', the
dual-write invariant guarantees current_drive is populated. The guards
added nothing over the atom pattern match.
@brianmay brianmay force-pushed the refactor/extract-records-from-states branch from 089956b to d1e6599 Compare May 5, 2026 01:23
@brianmay brianmay changed the base branch from mcu2-upgraded-cars to main May 5, 2026 01:24
@brianmay
Copy link
Copy Markdown
Collaborator Author

brianmay commented May 5, 2026

I rebased this against the main branch, now that mcu2-upgraded-cars has been merged into main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:teslamate Related to TeslaMate core enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants