Add the field value store and page descriptor model (#58) #81
No reviewers
Labels
No labels
area:companion
area:docs
area:shared
area:tooling
area:watchapp
blocker
kind:chore
kind:feature
kind:spike
kind:test
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
robert/PedalPebble!81
Loading…
Reference in a new issue
No description provided.
Delete branch "area/field-store-page-descriptors"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Implements #58 (Field value store and page descriptor model).
What this adds
watchapp/src/c/fields.h/fields.c— theFieldIdenum (every metric in the issue, oneentry per DESIGN.md section 2 row), a value store keyed by it, typed setters
(
field_store_set_u8/u16/u32) that recognise each wire width's PROTOCOL.md rule-3 sentinel and markthe field unavailable automatically — a call site can't forget to check for it — group-heartbeat
staleness aging (D44), and one formatter table (decimal places, unit string, unavailable text).
Zero Pebble SDK calls anywhere in this file; verified by compiling it standalone with plain host
gccagainst a small smoke-test harness (sentinel handling, zero-is-not-absent, the D44constant-value-stays-fresh scenario, staleness-vs-unavailable). That test isn't checked in — #68 owns
the actual harness — but the file is proven host-buildable as written.
watchapp/src/c/page.h/page.c—PageTemplate,PageDescriptor{template, fields[6]},page_has_live_field()(the "does this descriptor have at least one live field" primitive #60'scarousel needs), wire/persisted packing that matches PROTOCOL.md 2.6's
CONFIG_PAGESbyte layoutexactly (1 byte template + 6 bytes fields), and the three default pages (Ride/Effort/Progress) from
DESIGN.md section 4. Persistence uses
persist_read_data/persist_write_data, confirmed against theinstalled SDK 4.33.1 headers (256-byte-per-key limit,
uint32_tkeys). Onlypage_store_load/savetouch the SDK — the rest of the file is pure.
pebble buildis clean foremery,gabbroandbasalt.Design calls made without an explicit spec (flagging for review)
ride and nav groups grey at 6 s (2x PROTOCOL's stated 3 s heartbeat — the same "two missed
heartbeats" point PROTOCOL.md §4 itself uses to call the whole link down). Laps never age by time —
PROTOCOL.md §4 gives them no heartbeat at all ("on lap events only"), so there's nothing periodic to
compare against; only availability matters. The clock never ages (the app is its own source). HR is
watch-local with no PROTOCOL heartbeat to anchor to, so it provisionally reuses the ride threshold
pending whatever sample period a future HR-producer issue actually requests.
M:SS/H:MM:SS, matchingELAPSED/LAP_TIME) rather than aclock-of-day.
NAV_ETA_Sis "seconds at rolling average speed", which reads as a duration, and aclock-of-day render would need timezone/localtime handling that would compromise this file's
host-testability.
persist_*key (PERSIST_KEY_PAGE_DESCRIPTORS = 1), reusing PROTOCOL.md'sCONFIG_PAGESwireformat byte-for-byte rather than inventing a separate on-flash shape. This is sized for exactly the
three shipped pages; #61 (phone-driven
CONFIG_PAGES) will need to generalise it to a phone-suppliedcount.
Deliberately not in scope here
field_store_set_*— it needsproto.h's generatedkeys, which is #7 and hasn't landed yet. This PR only builds the setters it will call.
fields.c/fields.hare proven host-buildable (see above) butpage.cmixes pure logic with the twopersist_*calls, so a clean host build of it needs either astub or a file split — that's a harness-design decision I left for #68 rather than pre-empting it
here, per the issue's own guidance not to force it if it doesn't fit naturally.
Claude-Session: https://claude.ai/code/session_01DAoXbRmJUf2uxNYBfdAXPt
FieldId enum, a value store keyed by it, one formatter table per field, and PageDescriptor{template, fields[6]} persisted on the watch — docs/DESIGN.md sections 1-2, docs/PROTOCOL.md section 2. - fields.h/fields.c: FieldId, the value store (available flag distinct from a real zero, per-entry last-update timestamp), typed setters that recognise each wire width's sentinel automatically, group-heartbeat staleness (D44: aged against the group, never a key's own arrival), and one formatter table (decimal places, unit, unavailable text). Free of Pebble SDK calls throughout, verified with a standalone host gcc build against a small smoke-test harness covering the sentinel, zero-is-not-absent, staleness-vs-unavailable and D44's constant-value-stays-fresh scenarios. - page.h/page.c: PageTemplate, PageDescriptor, page_has_live_field() (the carousel primitive #60 needs), wire/persisted packing matching PROTOCOL.md 2.6's byte layout exactly, and the three default pages (Ride/Effort/Progress) from DESIGN.md section 4. Persistence uses persist_read_data/persist_write_data (confirmed against the installed 4.33.1 SDK headers); only page_store_load/save touch the SDK, so the pure logic in this file stays separable for #68. `pebble build` verified clean for emery, gabbro and basalt. Design calls made without an explicit spec (flagged for review): - Staleness thresholds: 6s (2x the 3s PROTOCOL heartbeat) for the ride and nav groups; laps never age by time (event-only, no heartbeat exists to compare against); the clock never ages (it's the app's own RTC); HR provisionally reuses the ride threshold pending a real sample-period decision elsewhere. - ETA formats as a duration (matching ELAPSED/LAP_TIME) rather than a clock-of-day, to avoid pulling timezone/localtime handling into a file that must stay host-testable. - Persistent storage packs all three default descriptors as one 21-byte blob under a single persist key, reusing PROTOCOL.md's CONFIG_PAGES wire format byte-for-byte; #61 will need to generalise this when the phone can configure more than three pages. Not built here: the AppMessage receive handler that will call these setters (blocked on #7's key generation) and #68's host-test harness (left clean for that issue — page.c's SDK-touching functions are isolated but not split out). Claude-Session: https://claude.ai/code/session_01DAoXbRmJUf2uxNYBfdAXPt