docs(series4): the schedule record format, named by the firmware itself
A debug printf in the scheduler names the record's fields outright: SCHEDULER : _ReadRecord(%d) -> %s (Action=%u, 1/2h=%u, Day=%u, Setup="%s") [WDAY=%d] They fit the captured record, and the name-length byte anchors the alignment -- it reads 40 for the 40-character name pulled off the unit and 9 for TEST1.mmb written back, same position, both directions: [0] Action = 3 [1:4] zero (padding, or Action is a uint32) [4] 1/2h = 15 -> 30-minute resolution, 48 slots/day [5] Day = 3 [6] unidentified (WDAY?) [7] name length -- 0x28=40 read, 0x09=9 written <-- confirms the layout [8:] setup name [264] trailer 27 03 10 Slot 15 would be 07:30 counted from midnight; flagged unconfirmed because the schedule's actual time was not recorded with the capture. Six duty-cycle actions, not the five previously recorded -- there is also DUTYCYCLE_START_MONITOR_WITH_SETUP_STOP_COMPLETE. THOR exposes four. Two start variants it never offers, one needing no setup file. Strengthened the setup-less-action finding and ruled out an alternative explanation I had not considered: the Micromate has a separate Timer Mode (MODE_TIMER, Monitor Once Only, under Special Setup), so START_MONITOR could have belonged to that path. It does not -- it is a case in _PSA(), the scheduler's own dispatcher for _ReadRecord's Action field, and `_PSA() send ->> CMD_DUTYCYCLE_ START_MONITOR` shows it is live code sending a real message, not a dead case. Also: SysPref.bMonitorScheduler places the scheduler enable in system preferences, which confirms from the other side why the 0x71 block was byte-identical when the scheduler was switched on -- the flag was never going to be in the compliance config. It also suggests 0x47 is a SysPref get/set rather than anything scheduler-specific, which would explain its params[7] selector and its response shape matching 0x48's page-0 descriptor. Still a hypothesis. Names the one capture that would settle the rest: a schedule with TWO entries at different times with different actions. That yields the record stride, the action code values, and the time encoding at once. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ru8Lg9HkkYvX9VWWo65SmL
This commit is contained in:
@@ -1490,6 +1490,88 @@ other schedules — or the operator's own saved work — depend on. Nothing in
|
||||
protocol or the ack reports that this happened. Validating the reference
|
||||
instead of rewriting the referent avoids the whole class of problem.
|
||||
|
||||
### The schedule record format, from the firmware's own debug printf
|
||||
|
||||
```
|
||||
SCHEDULER : _ReadRecord(%d) -> %s (Action=%u, 1/2h=%u, Day=%u, Setup="%s") [WDAY=%d]
|
||||
```
|
||||
|
||||
That names the fields outright, and they fit the captured record:
|
||||
|
||||
| offset | field | read capture | write capture |
|
||||
|---|---|---|---|
|
||||
| `[0]` | **Action** | `03` | `03` |
|
||||
| `[1:4]` | zero — padding, or `Action` is a uint32 | `00 00 00` | `00 00 00` |
|
||||
| `[4]` | **1/2h** — half-hour slot | `0f` = 15 | `0f` = 15 |
|
||||
| `[5]` | **Day** | `03` | `03` |
|
||||
| `[6]` | unidentified (`WDAY`?) | `02` | `02` |
|
||||
| `[7]` | **name length** | `0x28` = **40** | `0x09` = **9** |
|
||||
| `[8:]` | **Setup** name | 40-char name | `TEST1.mmb` |
|
||||
| `[264]` | trailer | `27 03 10` | `27 03 10` |
|
||||
|
||||
**The length byte is what anchors this** — it reads 40 for the 40-character name
|
||||
and 9 for `TEST1.mmb`, in the same position, in both directions. The layout is
|
||||
not a guess.
|
||||
|
||||
`1/2h` implies **30-minute resolution, 48 slots per day**. Slot 15 would be
|
||||
07:30 if counted from midnight — plausible but ⚠ **unconfirmed**, since the
|
||||
schedule's actual time was not recorded alongside the capture. One question to
|
||||
the operator settles it.
|
||||
|
||||
⚠ Still unknown, and all answerable with **one schedule containing two entries**:
|
||||
|
||||
- the **record stride** and how many records the file holds (one record was
|
||||
captured; `27 03 10` at `[264]` may be a trailer or a second record's head)
|
||||
- whether `Action` is one byte or a uint32
|
||||
- the **action code values** — `03` is presumably
|
||||
`DUTYCYCLE_START_MONITOR_WITH_SETUP` but the enum's base is unknown
|
||||
- `[6]`, and how `Day` and `WDAY` divide the work
|
||||
|
||||
### Six duty-cycle actions, not five
|
||||
|
||||
The full `_PSA()` dispatch — the scheduler's own action processor, reading records
|
||||
via `_ReadRecord`:
|
||||
|
||||
```
|
||||
DUTYCYCLE_START_MONITOR ← no setup
|
||||
DUTYCYCLE_START_MONITOR_WITH_SETUP
|
||||
DUTYCYCLE_START_MONITOR_WITH_SETUP_STOP_COMPLETE ← a third start variant
|
||||
DUTYCYCLE_STOP_MONITOR
|
||||
DUTYCYCLE_CALLHOME
|
||||
DUTYCYCLE_SELF_CHECK
|
||||
```
|
||||
|
||||
**THOR exposes four.** Two start variants it never offers, one of which needs no
|
||||
setup file at all.
|
||||
|
||||
`_PSA() send ->> CMD_DUTYCYCLE_START_MONITOR` shows the setup-less action is live
|
||||
code that the scheduler genuinely dispatches, not a dead case — it sends a real
|
||||
message. And since every one of these cases sits in the function that consumes
|
||||
`_ReadRecord`'s `Action` field, **a schedule record can carry it.**
|
||||
|
||||
An alternative explanation was checked and ruled out: the Micromate does have a
|
||||
separate *Timer Mode* (`MODE_TIMER`, `MODE_MONITOR_TIMER`, `Monitor Once Only`,
|
||||
under Special Setup), so `START_MONITOR` could have belonged to that path
|
||||
instead. It does not — it is in `_PSA()`, the scheduler's dispatcher.
|
||||
|
||||
### The scheduler enable lives in SysPref, not in the setup
|
||||
|
||||
```
|
||||
SysPref.bMonitorScheduler = %s
|
||||
_Task() CMD_SET_SCHEDULE - MonitorScheduler ENABLED
|
||||
_Task() CMD_SET_SCHEDULE - ! MonitorScheduler NOT Enabled
|
||||
```
|
||||
|
||||
This **confirms the failed prediction above from the other side**: the
|
||||
`Scheduler On/Off` switch is a *system preference*, which is why the `0x71`
|
||||
compliance block was byte-identical when the scheduler was turned on. It was
|
||||
never going to be in there.
|
||||
|
||||
It also suggests `0x47` — the two bare frames at the end of the capture — is a
|
||||
SysPref get/set rather than anything scheduler-specific, which would explain its
|
||||
`params[7]` selector and its 15-byte response shape being shared with `0x48`'s
|
||||
page-0 descriptor. ⚠ Still a hypothesis; the disable/enable capture settles it.
|
||||
|
||||
#### The schedule has a setup-less start action — Thor just never uses it
|
||||
|
||||
The coupling above looked like it might be a workaround for the unit crashing on
|
||||
|
||||
Reference in New Issue
Block a user