docs(series4): CORRECT the schedule record -- [6] is the Action bitmask, not [0]
A five-entry schedule (start/stop/self-check/start/ACH) overturns the two
previous readings of this record, and the operator supplied a Thor screenshot of
the schedule as ground truth.
[6] is the Action, and the values are powers of two:
2 = Start Monitoring 4 = Stop Monitoring
8 = Self Check 16 = Auto Call Home
Bits 1-4 of a bitmask; bit 0 (value 1) is unobserved -- a natural home for the
setup-less DUTYCYCLE_START_MONITOR, but that is a guess.
Two retractions:
* [6] was recorded as "the one unidentified field" and predicted to be the
Repeat flag. It is the Action.
* [0] was labelled Action, then "Action with repeat folded in". Both wrong.
[0] is non-zero only on record 0 -- records 0 and 3 here are the SAME action
with different [0] values. It is a schedule-level field carried in the first
record, holding Repeat: 3 on, 2 off, matching "Repeat Daily: Disabled" on the
Thor screen.
The earlier repeat capture was consistent with both readings because it had one
start entry and moved one byte. A single-variable test is not always enough; it
took four distinct actions to separate the fields.
SUB 0x47 is confirmed as the scheduler enable. Previously recorded as "genuinely
undetermined" whether it sets or reads -- Thor's notification pane timestamps it:
schedule write completes 00:51:29, "successfully SENT" 00:51:31, the 0x47 pair at
00:51:31 and 00:51:33, "successfully ENABLED" 00:51:35. Nothing else sits between
the two notifications. params[7] in {1,3} is still open and is more likely a
selector than a value, since a lone params[7]=3 also appears at session start.
It must be DLE-escaped -- a bare 0x03 truncates the frame.
SECOND RETRACTION: setups ARE written as raw .MMB files. This document twice
said they are not. Thor used both paths in one session, choosing by whether the
setup is active:
TEST1.mmb (active) 0xDA -> 0x68/0x73 -> 0x82/0x83 -> 0x71/0x72
test2.mmb (not active) 0x8D \system\setups\test2.mmb -> 0x8E (2192 B)
The .MMB file is nearly the compliance block -- 1968/2086 bytes equal (94.3%) at
a 4-byte shift, 102 bytes longer, name at offset 38 vs 42. Same structure,
different framing. Writing a setup as a file is the cleaner path for SFM: two
frames, no 0xDA/0x68/0x82 ritual, and it does not disturb the active setup.
Also: file writes are chunked. The schedule's 1,304 bytes went as 1,024 + 280
with each offset = that chunk's length, but the 2,192-byte setup went in one
frame, so 1,024 is not a hard ceiling. Rule unexplained, recorded as observed.
Capture provenance: the seismo_lab bins were empty (capture not stopped), and the
session was recovered from the socat relay log again -- 38 frames each way, 0 bad
checksums. That fallback has now saved two captures.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ru8Lg9HkkYvX9VWWo65SmL
This commit is contained in:
@@ -1502,16 +1502,19 @@ The captured file is **two 260-byte records plus four zero bytes** — 524 bytes
|
||||
exactly. Every non-zero byte in the file falls inside those two records.
|
||||
|
||||
```
|
||||
offset field size notes
|
||||
[0] Action 1 (or uint32 LE at [0:4] — both are zero-extended here)
|
||||
[1:4] padding 3 zero in both records
|
||||
[4] 1/2h 1 half-hour slot, 0–47
|
||||
[5] Day 1 3 in both records
|
||||
[6] ?? 1 the one unidentified field
|
||||
[7] name length 1
|
||||
[8:260] Setup name 252 null-padded
|
||||
offset field size notes
|
||||
[0] schedule flags 1 record 0 ONLY — 3 = repeat on, 2 = repeat off
|
||||
[1:4] padding 3 zero in every record observed
|
||||
[4] 1/2h 1 half-hour slot, 0–47
|
||||
[5] Day 1 3 in every record observed — undetermined
|
||||
[6] Action 1 bitmask: 2 start, 4 stop, 8 self-check, 16 ACH
|
||||
[7] name length 1
|
||||
[8:260] Setup name 252 null-padded; empty for actions that take no setup
|
||||
```
|
||||
|
||||
⚠ `[0]` and `[6]` were **mislabelled twice** before a five-entry schedule
|
||||
separated them — see *CORRECTED* below.
|
||||
|
||||
Decoded against the operator's own description of what they entered —
|
||||
*"start monitoring TEST1 at 7:30 AM, Auto Call Home at 7:30 PM"*:
|
||||
|
||||
@@ -1546,56 +1549,110 @@ Action codes, so far:
|
||||
| `0` | **Auto Call Home** |
|
||||
| `3` | **Start monitoring, with setup** |
|
||||
|
||||
### The Repeat flag is folded into `Action` (2026-09-25)
|
||||
### ⚠ CORRECTED — `[6]` is the Action, and it is a bitmask (2026-09-25)
|
||||
|
||||
A capture that changed **only** the *Repeat Daily* checkbox — schedule pulled,
|
||||
repeat disabled, schedule pushed — moved **exactly one byte in the whole
|
||||
session**:
|
||||
A five-entry schedule settled this, and it **overturns the two previous
|
||||
readings of this record.** The operator's Thor screen, captured alongside:
|
||||
|
||||
```
|
||||
record @0 Action 0x03 → 0x02 (repeat ON → repeat OFF)
|
||||
actioncheck — Micromate — Repeat Daily: Disabled
|
||||
7:30 AM Start Monitoring TEST1
|
||||
8:00 AM Stop Monitoring
|
||||
8:30 AM Self Check
|
||||
9:00 AM Start Monitoring test2
|
||||
7:30 PM Auto Call Home
|
||||
```
|
||||
|
||||
Nothing else moved. Record 2 unchanged, `[6]` unchanged, and `0xDA`, `0x68`,
|
||||
`0x82`, `0x71`, `0x94`, `0x8D` all byte-identical.
|
||||
Five entries, and the file holds exactly five 260-byte records:
|
||||
|
||||
⚠ So **`[6]` is NOT the repeat flag** — the field predicted for that test is still
|
||||
unidentified. And the earlier label on `[0]` was too simple: it is not a plain
|
||||
action index, it carries the repeat behaviour too.
|
||||
| rec | `[0]` | `1/2h` | `Day` | **`[6]`** | len | name |
|
||||
|---|---|---|---|---|---|---|
|
||||
| @0 | **2** | 15 = 07:30 | 3 | **2** | 9 | `TEST1.mmb` |
|
||||
| @260 | 0 | 16 = 08:00 | 3 | **4** | 0 | |
|
||||
| @520 | 0 | 17 = 08:30 | 3 | **8** | 0 | |
|
||||
| @780 | 0 | 18 = 09:00 | 3 | **2** | 9 | `test2.mmb` |
|
||||
| @1040 | 0 | 39 = 19:30 | 3 | **16** | 0 | |
|
||||
|
||||
**Leading hypothesis** — `2` and `3` are the two *setup-bearing start* actions the
|
||||
firmware names, and repeat selects between them:
|
||||
**`[6]` tracks the action exactly, and the values are powers of two:**
|
||||
|
||||
| value | action | why |
|
||||
|---|---|---|
|
||||
| `0` | `DUTYCYCLE_CALLHOME` | the 19:30 entry, no setup name |
|
||||
| `2` | `DUTYCYCLE_START_MONITOR_WITH_SETUP` | repeat **off** |
|
||||
| `3` | `DUTYCYCLE_START_MONITOR_WITH_SETUP_STOP_COMPLETE` | repeat **on** |
|
||||
| `[6]` | action |
|
||||
|---|---|
|
||||
| `2` | Start Monitoring |
|
||||
| `4` | Stop Monitoring |
|
||||
| `8` | Self Check |
|
||||
| `16` | Auto Call Home |
|
||||
|
||||
That reading is supported by the THOR manual's description of what a *repeating*
|
||||
schedule does on hitting a Start Monitoring event while already monitoring:
|
||||
Bits 1–4 of a bitmask. **Bit 0 (value `1`) is unobserved** — a natural home for
|
||||
the setup-less `DUTYCYCLE_START_MONITOR`, but that is a guess, not a finding.
|
||||
|
||||
> *"the unit will stop the current monitoring session, run any Auto Call Home
|
||||
> actions, load the compliance setup and continue monitoring."*
|
||||
**Two retractions:**
|
||||
|
||||
Which is precisely what a name ending `_STOP_COMPLETE` would mean: a repeating
|
||||
start has to terminate the in-progress session first, a one-shot start does not.
|
||||
The firmware action name, the manual's behaviour, and the single byte that moved
|
||||
all agree.
|
||||
1. `[6]` was recorded as *"the one unidentified field"* and predicted to be the
|
||||
Repeat flag. It is the **Action**.
|
||||
2. `[0]` was labelled **Action**, then *"Action with repeat folded in"*. Both
|
||||
wrong. `[0]` is **non-zero only on record 0** — including here, where record
|
||||
0 and record 3 are the same action with different `[0]` values. It is a
|
||||
**schedule-level field carried in the first record**, and it holds the Repeat
|
||||
flag: `3` repeat on, `2` repeat off, matching *"Repeat Daily: Disabled"* on
|
||||
the screen.
|
||||
|
||||
⚠ **The enum's ordering is NOT known.** The action names were recovered with
|
||||
`strings | sort`, so their source order is lost — do **not** infer that `1` is
|
||||
`DUTYCYCLE_START_MONITOR` merely because it sits between the two known values,
|
||||
tempting as that is. Three of six codes are observed (`0`, `2`, `3`); the
|
||||
remaining three need entries that use those actions.
|
||||
The earlier repeat capture was consistent with both readings because it had one
|
||||
start entry and changed one byte. A single-variable test is not always enough —
|
||||
it took a schedule with four distinct actions to separate the two fields.
|
||||
|
||||
⚠ Note also that Thor re-pushed the **entire 2,090-byte config block** for a
|
||||
one-byte schedule change — a third instance of the coupling described in
|
||||
*The schedule↔config coupling is Thor's, not the protocol's*.
|
||||
`Day` is `3` on all five records across every capture. Still undetermined.
|
||||
|
||||
⚠ Also still unknown: whether the file can hold more than the records in use
|
||||
(both captures had every used record contiguous from offset 0), and the remaining
|
||||
four action codes.
|
||||
### ✅ `SUB 0x47` is the scheduler enable — confirmed by Thor's own notifications
|
||||
|
||||
Previously recorded as *"genuinely undetermined"* whether `0x47` sets or reads.
|
||||
Thor's notification pane timestamps it:
|
||||
|
||||
```
|
||||
00:51:29 schedule write (0x8E) completes
|
||||
00:51:31 Thor: "actioncheck has been successfully SENT to ... UM12947"
|
||||
00:51:31 → 0x47 params[7] = 0x01
|
||||
00:51:33 → 0x47 params[7] = 0x03 (on the wire as 10 03 — DLE-escaped)
|
||||
00:51:35 Thor: "actioncheck was successfully ENABLED on ... UM12947"
|
||||
```
|
||||
|
||||
The only frames between *sent* and *enabled* are the `0x47` pair. **`0x47`
|
||||
performs the enable.**
|
||||
|
||||
⚠ The meaning of `params[7]` ∈ {1, 3} is still open — a single `0x47` with
|
||||
`params[7] = 3` also appears at *session start*, where nothing is being enabled,
|
||||
so the byte is more likely a selector than a value. Note it must be
|
||||
**DLE-escaped**: a bare `0x03` in params truncates the frame at what the device
|
||||
reads as ETX.
|
||||
|
||||
### 🔑 Setups ARE written as raw `.MMB` files — second retraction
|
||||
|
||||
This document twice stated that setups are *not* pushed as raw `.MMB` blobs and
|
||||
go only through `0xDA` + the config block. **Wrong.** In this session Thor did
|
||||
both, choosing by whether the setup is the active one:
|
||||
|
||||
| setup | how it was written |
|
||||
|---|---|
|
||||
| `TEST1.mmb` (**active**) | `0xDA` → `0x68`/`0x73` → `0x82`/`0x83` → `0x71`/`0x72` |
|
||||
| `test2.mmb` (not active) | `0x8D` `\system\setups\test2.mmb` → `0x8E` (2,192 B) |
|
||||
|
||||
So **`\system\setups\<name>.mmb`** is the setup directory, and the generic file
|
||||
transfer writes there directly.
|
||||
|
||||
The `.MMB` file is *nearly* the compliance block: aligning the two gives
|
||||
**1,968 / 2,086 bytes equal (94.3%) at a 4-byte shift**. The file is 102 bytes
|
||||
longer (2,192 vs 2,090) and puts the setup name at offset 38 where the `0x71`
|
||||
block has it at 42. So the on-disk setup and the wire compliance block are the
|
||||
same structure with different framing — not two formats.
|
||||
|
||||
**This is the cleaner path for SFM.** Writing a setup as a file needs two frames
|
||||
and no `0xDA`/`0x68`/`0x82` ritual, and it does not disturb the active setup.
|
||||
|
||||
### File writes are chunked
|
||||
|
||||
The schedule's 1,304 bytes went as **two `0x8E` frames, 1,024 + 280**, each with
|
||||
`offset` = that chunk's own length. The 2,192-byte setup file went in **one**
|
||||
frame, so 1,024 is not a hard ceiling — the chunking rule is unexplained and
|
||||
recorded as observed.
|
||||
|
||||
### Six duty-cycle actions, not five
|
||||
|
||||
|
||||
Reference in New Issue
Block a user