Auto: focas-f3a — cnc_rdalmhistry alarm-history extension
Adds FocasAlarmProjection with two modes (ActiveOnly default, ActivePlusHistory) that polls cnc_rdalmhistry on connect + on a configurable cadence (5 min default, HistoryDepth=100 capped at 250). Emits historic events via IAlarmSource with SourceTimestampUtc set from the CNC's reported timestamp; dedup keyed on (OccurrenceTime, AlarmNumber, AlarmType). Ships the ODBALMHIS packed-buffer decoder + encoder in Wire/FocasAlarmHistoryDecoder.cs and threads ReadAlarmHistoryAsync through IFocasClient (default no-op so existing transport variants stay back-compat). FocasDriver now implements IAlarmSource. 13 new unit tests cover: mode switch, dedup, distinct-timestamp emission, type-as-key behaviour, OccurrenceTime passthrough (not Now), HistoryDepth clamp/fallback, and decoder round-trip. All 341 FOCAS unit tests still pass. Docs: docs/drivers/FOCAS.md (new), docs/v2/focas-deployment.md (new), docs/v2/implementation/focas-wire-protocol.md (new), docs/v2/implementation/focas-simulator-plan.md (new), docs/drivers/FOCAS-Test-Fixture.md (alarm-history bullet appended). Closes #267
This commit is contained in:
76
docs/v2/implementation/focas-wire-protocol.md
Normal file
76
docs/v2/implementation/focas-wire-protocol.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# FOCAS wire protocol — packed-buffer surface
|
||||
|
||||
Notes on the language-neutral packed-buffer encoding the FOCAS driver +
|
||||
focas-mock simulator share. This format is **not** the FWLIB native struct
|
||||
layout — Tier-C Fwlib32 backends marshal directly from the FANUC C struct.
|
||||
The packed surface exists so the simulator (Python / FastAPI) and the .NET
|
||||
wire client can speak a common format over IPC without piping a Win32 DLL
|
||||
through both ends.
|
||||
|
||||
## Command id table
|
||||
|
||||
Each FOCAS-equivalent call gets a stable wire-protocol command id. Ids are
|
||||
**append-only** — never renumber, never reuse.
|
||||
|
||||
| Id | FOCAS API | Surface |
|
||||
| --- | --- | --- |
|
||||
| `0x0001` | `cnc_rdcncstat` | ODBST 9-field status struct |
|
||||
| `0x0002` | `cnc_rdparam` | parameter value (one number) |
|
||||
| `0x0003` | `cnc_rdmacro` | macro variable value |
|
||||
| `0x0004` | `cnc_rddiag` | diagnostic value |
|
||||
| ... | ... | ... |
|
||||
| `0x0F1A` | **`cnc_rdalmhistry`** | **ODBALMHIS alarm-history ring-buffer dump (issue #267, plan PR F3-a)** |
|
||||
|
||||
## ODBALMHIS — alarm history (`cnc_rdalmhistry`, command `0x0F1A`)
|
||||
|
||||
Issued by `FocasAlarmProjection` when
|
||||
`FocasDriverOptions.AlarmProjection.Mode == ActivePlusHistory`. Returns up
|
||||
to `depth` most-recent ring-buffer entries.
|
||||
|
||||
### Request
|
||||
|
||||
| Offset | Width | Field | Notes |
|
||||
| --- | --- | --- | --- |
|
||||
| 0 | `int16 LE` | `depth` | clamped client-side to `[1..250]` (`FocasAlarmProjectionOptions.MaxHistoryDepth`) |
|
||||
|
||||
### Response (packed buffer, little-endian)
|
||||
|
||||
| Offset | Width | Field |
|
||||
| --- | --- | --- |
|
||||
| 0 | `int16 LE` | `num_alm` — number of entries that follow. `< 0` indicates CNC error. |
|
||||
| 2 | repeated | `ALMHIS_data alm[num_alm]` (see below) |
|
||||
|
||||
Each entry block:
|
||||
|
||||
| Offset (rel.) | Width | Field |
|
||||
| --- | --- | --- |
|
||||
| 0 | `int16 LE` | `year` |
|
||||
| 2 | `int16 LE` | `month` |
|
||||
| 4 | `int16 LE` | `day` |
|
||||
| 6 | `int16 LE` | `hour` |
|
||||
| 8 | `int16 LE` | `minute` |
|
||||
| 10 | `int16 LE` | `second` |
|
||||
| 12 | `int16 LE` | `axis_no` (1-based; 0 = whole-CNC) |
|
||||
| 14 | `int16 LE` | `alm_type` (P/S/OT/SV/SR/MC/SP/PW/IO encoded numerically) |
|
||||
| 16 | `int16 LE` | `alm_no` |
|
||||
| 18 | `int16 LE` | `msg_len` (0..32 typical) |
|
||||
| 20 | `msg_len` | ASCII message (no null terminator) |
|
||||
| `20 + msg_len` | 0..3 | pad to 4-byte boundary so per-entry blocks stay self-delimiting |
|
||||
|
||||
The CNC stamps `year..second` in **its own local time**. The deployment
|
||||
guide instructs operators to keep CNC clocks on UTC so the projection's
|
||||
dedup key `(OccurrenceTime, AlarmNumber, AlarmType)` stays stable across
|
||||
DST transitions. The .NET decoder
|
||||
(`Wire/FocasAlarmHistoryDecoder.Decode`) constructs each
|
||||
`DateTimeOffset` with `TimeSpan.Zero` (UTC) on that assumption.
|
||||
|
||||
### Error handling
|
||||
|
||||
- A negative `num_alm` short-circuits decode to an empty list — the
|
||||
projection treats it as "no history this tick" and the next poll
|
||||
retries.
|
||||
- Malformed timestamps (e.g. month=0) are skipped per-entry instead of
|
||||
faulting the whole decode; the dedup key for malformed entries would be
|
||||
unstable anyway.
|
||||
- `msg_len` overrunning the payload truncates the entry list at the
|
||||
malformed entry rather than throwing.
|
||||
Reference in New Issue
Block a user