Field value store and page descriptor model #58

Open
opened 2026-08-31 17:55:57 +02:00 by robert · 0 comments
robert commented 2026-08-31 17:55:57 +02:00 (Migrated from git.butzei.de)

Goal

Make pages data rather than layout code. This is the foundation that lets the phone configure screens later without rewriting the views, and it must land before any view hard-codes a layout.

Acceptance criteria

  • FieldId enum covering every metric: speed, average and max speed; distance, elapsed, clock; HR, average HR, HR zone; power, 3 s power, average and normalised power; cadence and average cadence; ETA, remaining, distance to turn; lap number, distance, time and average speed
  • Value store keyed by FieldId, updated as messages arrive, each entry carrying its last-update timestamp
  • One formatter table per field: decimal places, unit string, and unavailable rendering
  • PageDescriptor { template, fields[6] } with descriptors held in persistent storage
  • A field with no source renders -- - never a stale value, never zero
  • A page whose every field is sourceless is skipped in the carousel
  • A value older than its expected update interval renders greyed
  • Default descriptors defined in code for the three shipped pages

Files

  • watchapp/src/c/fields.c
  • watchapp/src/c/fields.h
  • watchapp/src/c/page.h

Notes

See docs/DESIGN.md sections 1 and 2. Getting this in before #9 is the whole point - a hard-coded layout makes the later phone configuration a rewrite.

Update — 2026-09-01: every field needs a producer and a wire key

The enum defined MAX_SPEED, AVG_HR, HR_ZONE, AVG_CADENCE and NORM_POWER with nothing
anywhere in the plan computing them — fields that could only ever render --. #65 now produces them
and docs/PROTOCOL.md gives them wire keys (D28).

  • Every FieldId maps to a key in docs/PROTOCOL.md, or is documented as watch-local (HR,
    CLOCK)
  • The unavailable sentinel is distinguishable from a real zero — zero is a legitimate speed,
    cadence and power
  • Keep the store and formatters free of SDK calls so they are host-testable (#68)

Update — 2026-09-02: stale and unavailable are two states

The store already carries a last-update timestamp per entry, and the criteria above ask for both
-- and greying. Those are different states and were being conflated (D44).

  • -- means there is no source. It is signalled by the phone with the type's sentinel,
    never inferred from silence
  • Grey means the producer or the link went quiet. The number was true and may still be
  • Staleness is measured against the field's group heartbeat (PROTOCOL §4) and link liveness,
    not against that key's own last arrival. Under changed-keys-only updates a constant value —
    speed of zero at a traffic light, distance while paused — stops being sent and would otherwise
    grey out while perfectly fresh
  • The 10 s full refresh is not the staleness mechanism; relying on it forces a threshold above
    10 s, far too coarse to catch a 3 s gap
## Goal Make pages data rather than layout code. This is the foundation that lets the phone configure screens later without rewriting the views, and it must land before any view hard-codes a layout. ## Acceptance criteria - [ ] `FieldId` enum covering every metric: speed, average and max speed; distance, elapsed, clock; HR, average HR, HR zone; power, 3 s power, average and normalised power; cadence and average cadence; ETA, remaining, distance to turn; lap number, distance, time and average speed - [ ] Value store keyed by `FieldId`, updated as messages arrive, each entry carrying its last-update timestamp - [ ] One formatter table per field: decimal places, unit string, and unavailable rendering - [ ] `PageDescriptor { template, fields[6] }` with descriptors held in persistent storage - [ ] **A field with no source renders `--`** - never a stale value, never zero - [ ] **A page whose every field is sourceless is skipped in the carousel** - [ ] A value older than its expected update interval renders greyed - [ ] Default descriptors defined in code for the three shipped pages ## Files - `watchapp/src/c/fields.c` - `watchapp/src/c/fields.h` - `watchapp/src/c/page.h` ## Notes See docs/DESIGN.md sections 1 and 2. Getting this in before #9 is the whole point - a hard-coded layout makes the later phone configuration a rewrite. ## Update — 2026-09-01: every field needs a producer and a wire key The enum defined `MAX_SPEED`, `AVG_HR`, `HR_ZONE`, `AVG_CADENCE` and `NORM_POWER` with nothing anywhere in the plan computing them — fields that could only ever render `--`. #65 now produces them and `docs/PROTOCOL.md` gives them wire keys (D28). - [ ] Every `FieldId` maps to a key in docs/PROTOCOL.md, or is documented as watch-local (`HR`, `CLOCK`) - [ ] The unavailable sentinel is distinguishable from a real zero — zero is a legitimate speed, cadence and power - [ ] Keep the store and formatters **free of SDK calls** so they are host-testable (#68) ## Update — 2026-09-02: stale and unavailable are two states The store already carries a last-update timestamp per entry, and the criteria above ask for both `--` and greying. Those are different states and were being conflated (D44). - [ ] **`--`** means there is no source. It is **signalled** by the phone with the type's sentinel, never inferred from silence - [ ] **Grey** means the producer or the link went quiet. The number was true and may still be - [ ] Staleness is measured against the field's **group heartbeat** (PROTOCOL §4) and link liveness, **not** against that key's own last arrival. Under changed-keys-only updates a constant value — speed of zero at a traffic light, distance while paused — stops being sent and would otherwise grey out while perfectly fresh - [ ] The 10 s full refresh is not the staleness mechanism; relying on it forces a threshold above 10 s, far too coarse to catch a 3 s gap
Sign in to join this conversation.
No description provided.