Skip to content

Commit b1a47e4

Browse files
authored
Merge pull request #13 from seigel/claude/magical-hodgkin
Implement full EFP2 command set and a demo.
2 parents 5bfcb9b + be6d90f commit b1a47e4

42 files changed

Lines changed: 2318 additions & 171 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CLAUDE.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
Cyrano is a Node.js service implementing the **EFP2 (Ethernet Fencing Protocol 2)** — a UDP-based protocol for networked fencing scoring machines. It bridges scoring machines (Masters) with competition management software (Managers), results servers, and display clients.
8+
9+
## Commands
10+
11+
```bash
12+
npm test # Run all tests (requires --experimental-vm-modules)
13+
npm test -- tests/path/to/file.test.js # Run a single test file
14+
npm test -- --testNamePattern="pattern" # Run tests matching a name
15+
npm run coverage # Run tests with coverage report
16+
npm run build # Bundle with esbuild → build/
17+
npm start # Run built app
18+
npm run clean # Remove node_modules and reinstall
19+
```
20+
21+
## Architecture
22+
23+
**Entry point:** `index.js` — UDP server on port 50100
24+
25+
**Protocol pipeline:** `cylex.js` (tokenizer) → `cyrano.js` (processor) → `commands/` (handlers)
26+
27+
- `src/protocol/cyranoTokens.js` — Protocol constants (`EFP2`, `|` separator)
28+
- `src/protocol/cylex.js` — Splits raw messages by `|` into token arrays
29+
- `src/protocol/cyrano.js` — Validates EFP2 header, dispatches to command handlers
30+
- `src/commands/index.js` — Auto-loads all command handlers from the `commands/` directory
31+
- `src/commands/*.js` — Individual command handlers (HELLO, PING, STOP, MSG, DISP)
32+
33+
## EFP2 Protocol Reference
34+
35+
**Message format:** `|EFP2|COMMAND|param1|param2|...|` (ASCII, fields delimited by `|`)
36+
37+
### Network Topology
38+
39+
| Node | IP | Ports |
40+
|------|----|-------|
41+
| Scoring machines Server | 172.20.0.1 | 50100 (from Masters), 50103 (from Managers) |
42+
| Backup Scoring machines Server | 172.20.0.2 | same as primary |
43+
| Master (piste x) | 172.20.x.1 (x=1..60) | 50100 (from Servers), 50101 (from Additional devices) |
44+
| Additional devices (piste x) | 172.20.x.y (y=1..255) | any |
45+
| Manager | 172.20.0.y (y=9..32) | any |
46+
| Server of the Results | 172.20.0.8 | 50100 (from Servers), 50103 (from Managers), 50104 (from Clients) |
47+
| Clients | 172.20.129–254.x | any |
48+
49+
### All Commands
50+
51+
| Command | Description | Params |
52+
|---------|-------------|--------|
53+
| `HELLO` | Node is online | 0–1 (optional piste code) |
54+
| `PING` | Check node presence; recipient must reply `\|EFP2\|HELLO\|\|` | 0 |
55+
| `STOP` | Disconnect / stop receiving | 0 |
56+
| `DISP` | Set new bout on piste | 18 (see below) |
57+
| `INFO` | Piste state (score, time, lights, cards, etc.) | ~40 fields |
58+
| `ACK` | Bout result accepted | 0 |
59+
| `NAK` | Bout result rejected | 0 |
60+
| `NEXT` | Referee requests next match | 1 (piste code) |
61+
| `PREV` | Referee requests previous match | 1 (piste code) |
62+
| `TEAM` | Team member list for one side | 19 (piste, side, 3 members + reserve, 9 round assignments, unique ID) |
63+
| `GETTEAM` | Request team list from Server of Results | 2 (piste code, side) |
64+
| `REPLACE` | Team member substitution | 3 (piste code, side, fencer number 1–3) |
65+
| `BOUTSTOP` | Cancel current DISP / clear piste | 1 (piste code) |
66+
| `MSG` | Text message to display | 2 (piste code or `ALL`, message text ≤128 chars) |
67+
| `STANDBY` | Switch apparatus to sleep mode | 1 (piste code) |
68+
| `BROKEN` | Lost contact with piste | 1 (piste code) |
69+
| `DENY` | Deny a request | reason string |
70+
| `UPDATED` | XML competition data updated | 2 (event ID, competition code) |
71+
72+
### TEAM structure (20 tokens)
73+
`members` is an array of 4: indices 0–2 are the active fencers, index 3 is the reserve.
74+
`rounds` is a 9-element array of fencer numbers (1, 2, or 3) indicating who fences each round.
75+
76+
### DISP fields (fields 3–20)
77+
PisteCode, EventID, CompetitionCode, Phase, Order, BoutID, TimeBegin, Stopwatch,
78+
Right ID, Right Name, Right Nation, Right MemberID, Right MemberName,
79+
Left ID, Left Name, Left Nation, Left MemberID, Left MemberName
80+
81+
### Piste codes
82+
Numeric `1``59` for standard pistes; named `BLUE`, `YELLOW`, `GREEN`, `RED`, `FINAL` for final area pistes.
83+
84+
### Competition codes
85+
`EIM`/`EIW`/`ETM`/`ETW` (épée), `FIM`/`FIW`/`FTM`/`FTW` (foil), `SIM`/`SIW`/`STM`/`STW` (sabre), `MIX`
86+
87+
### Master state machine
88+
States: `Standalone``Indefinite``Not active``Halt`/`Fencing`/`Pause`/`Waiting`/`Ending`
89+
- Enters `Not active` only on receiving `DISP`
90+
- Enters `Ending` on manual bout deactivation; sends `INFO` every second for up to 4s awaiting `ACK`/`NAK`
91+
- `ACK``Not active`; `NAK` or timeout → `Waiting`
92+
93+
## Testing
94+
95+
Tests mirror the `src/` structure under `tests/`. Jest runs with ES module support (`--experimental-vm-modules`). No transpilation is needed for tests — the project uses native ES modules.

0 commit comments

Comments
 (0)