# JMK RB RaceController English README. Swedish version: [`README.sv.md`](README.sv.md) JMK RB RaceController is an RC timing and race-control system with support for sponsor events with shared cars/transponders, AMMC WebSocket input, and local persistence through SQLite. ## Architecture - JavaScript module overview: `docs/javascript-architecture.md` - Swedish operational guide: `README.sv.md` ## Screenshots ### Overview ![Overview](docs/screenshots/overview.jpg) ### Timing ![Timing](docs/screenshots/timing.jpg) ### Overlay ![Overlay](docs/screenshots/overlay.jpg) ### Event ![Event](docs/screenshots/event.jpg) ### Race Setup ![Race Setup](docs/screenshots/race-setup.jpg) ### Team Overlay ![Team Overlay](docs/screenshots/team-overlay.jpg) ### Judging ![Team Overlay](docs/screenshots/judging.jpg) ## Public Overlay And OBS The app now supports a dedicated public overlay route so you do not need to expose the full admin UI externally. - Public overlay route: `/public-overlay/` - OBS mode: `/public-overlay/obs` - OBS overlay can be configured from the `Overlay` menu: - rows - layout: `leaderboard`, `grid`, `lowerthird` - theme: `panel`, `transparent`, `chroma` - visible columns and blocks - Use `Copy OBS URL` to generate a ready-to-paste browser-source link for OBS. If you want simple protection for public overlays, set this environment variable on the server: ```bash PUBLIC_OVERLAY_TOKEN=your-secret-token ``` Then the public overlay URL must include: ```text /public-overlay/obs?token=your-secret-token ``` Recommended deployment: - publish only `/public-overlay/*` through your reverse proxy - keep the main app/admin UI internal ## Features - Event modes: - `Race (driver transponders)` - `Track Event (shared cars)` - UI separation: - `Event` = sponsor events with shared cars/transponders - `Race Setup` = competition races with personal driver transponders - Storage/import tools: - full race-data export/import for backup or moving to another machine - directory export/import for only classes, drivers and cars - CSV export for drivers and cars when you want simple spreadsheet lists - single race-package export/import from `Race Setup -> Manage -> Race Actions` - Race Setup includes: - grouped `Participants` and `Teams` sections in `Manage` - a four-step `Create Race Wizard` for new races - `Manage` split into `Setup`, `Format`, `Generation`, and `Live / results` - Basic mode now reacts to the selected preset, including a cleaner endurance view - `Basic / Advanced` mode for `Race Format` - a right-side race summary card - a preset-aware summary card that changes between endurance and normal race focus - status badges for `Setup`, `Format`, `Generation`, and `Live / results` - summary warnings when participants, sessions, teams, or lap-window settings are missing - clickable summary warnings that jump to the relevant Manage section - clickable step cards that act as quick navigation inside `Manage` - compact step cards with live counters such as drivers, teams, sessions, and active/results state - separate `Race actions` for generation, reseeding and bump-up - reorganized in-app `Guide` with overview cards and two-column sections - explicit race participant selection - practice standings - qualifying standings with `points` or `best result` - built-in step-by-step guide - inline descriptions for all `Race Format` fields - `Follow-up time` after scheduled race time before a heat fully closes - `Min lap time` and `Max lap time` filtering for shortcuts/false hits and better stint/stat logic - seeding modes for practice/qualifying: best N laps total, average, or consecutive - selectable qualifying points tables and tie-break rules - `Race Format` presets for short technical tracks, club races, IFMAR-style layouts, and endurance - schedule drift on `Dashboard` between planned and actual runtime - session type `Free Practice` for rolling lap-time display without seeding - automatic qualifying heat generation from practice standings or class list - re-seeding of upcoming qualifying heats from current standings - automatic `A/B/C...` finals generation from rankings - persistent manual grid per session with drag-and-drop grid editor - auto-reseed skips heats with locked manual grid - explicit `Lock/Unlock grid` per session - multi-leg final standings with counted final legs - optional bump-up between finals - reserved bump-up slots in higher finals - visual finals matrix with reserved bump slots - drag-and-drop grid editor for position starts - print/export of heat sheets per qualifying/final - overlay modes for external display screens - multiple overlay modes: leaderboard, speaker, results, TV, team - speaker overlay with event markers and separate speaker cues - live speaker panel in `Timing` - speaker cues and club/PDF branding from `Settings` - overlay logo upload from `Settings` - per-event/per-race branding in `Manage` - extra speaker cues for `session start`, `new best lap`, and `top 3 change` - selectable PDF themes: `classic`, `minimal`, `motorsport` - branded print and PDF exports - server-generated PDF exports with embedded logo - generated qualifying/finals inherit duration and start mode from race format - finish sound uses a siren instead of browser TTS - Sessions: - `practice` - `qualification` - `heat` - `final` - `free_practice` - `open_practice` - `team_race` - Sponsor event tools: - automatic round creation (`qualification`, `heat`, `final`) - auto-assign driver -> car per session - Live timing from AMMC WebSocket (`msg: "PASSING"`) - Managed AMMC from the web UI: - backend can start/stop local `ammc-amb` on Windows, Linux, and macOS - bundled binaries from `AMMC/windows64`, `AMMC/linux_x86-64`, `AMMC/apple_m` - Editing in UI: - classes, event names/dates, drivers, and cars can be edited directly - drivers and cars support optional `brand` fields for team, sponsor, manufacturer, or model data - `Drivers` and `Cars` can be searched by name, transponder, or brand directly in the UI - race start lists and heat sheets include driver brand in print, CSV, and PDF exports - Live race control: - countdown during timed sessions - auto-finish at end of session time with `Race is finished` - leaderboard sorting by laps first, then nearest to target time when applicable - browser audio for passings (`beep` or driver name) and finish - session start types: `Mass start`, `Position start`, `Staggered` - `Timing` shows grid/order for the active `Position start` session - leaderboard shows `gap to leader`, `gap ahead`, and `own delta` - `Recent Passings` shows invalid laps such as `Short lap` and `Over max lap` - manual corrections in `Timing -> Details`: `+1/-1 lap`, `+1/+5/-1 sec`, reset - practice/qualifying seeding on best `2` or `3` laps - Persistence: - frontend state in browser `localStorage` - same state and passings persisted to local SQLite through the Node backend - Built-in `Guide` page in the app covering: - sponsor events (for example 10 drivers / 4 cars) - standard race setup - invalid laps, follow-up, and manual corrections - AMMC + npm setup on Windows and Linux - UI language selection: - `SV` - `EN` ## Training And Rewards The `Training` menu provides a non-race progression view for registered drivers: The default view is intentionally simple: four key statistics, the next lap target, today's 20-minute plan and active rewards. Goal administration, mobile sharing, detailed history and the club comparison remain available in collapsed sections when needed. - durable lifetime valid-lap ledger synchronized from stored session results - practice days, recorded sessions, personal best, overall/recent averages, controlled-lap streak, and recent consistency spread - daily history for the latest practice days and recent personal-best improvement - automatic progression levels at useful lap totals without manual setup - a 20-minute recommendation split into maximum-focus and controlled 80-90% blocks - custom lap milestones with quick targets at `500`, `1000`, and `1500` laps - optional reward text and a delivered/redeemed state - responsive layout for opening the same server from a phone on the local network Direct mobile/bookmark link: `http://:8081/?view=training`. Selecting a driver adds its ID to the URL so that driver's page can be bookmarked. Training statistics currently include only valid laps linked to a registered driver. Anonymous Open Practice transponders are intentionally excluded until they are assigned to a driver. Judging corrections update ledger laps while the source session still exists. Once a session is deleted, its synchronized training laps remain in the ledger so lifetime progression is not lost. Full data export includes the ledger and reward goals. ### Read-only mobile profile Each registered driver can have an optional personal mobile link: 1. Open `Training` and select the driver. 2. Open `Goals and mobile profile`, then click `Enable mobile profile`. 3. Copy or open the generated link. 4. Use `Create new link` to revoke the previous URL, or `Disable profile` to turn sharing off. The URL uses a 192-bit random bearer token rather than a driver ID. The public API returns only that driver's display name, class/brand, aggregate practice statistics, daily summaries, goals and reward text. It does not return transponder IDs, other driver names, race setup or admin state. The mobile page also includes a local 20-minute practice timer: 10 minutes maximum focus followed by 10 minutes controlled 80-90% driving. While a shared Free Practice session keeps running, the card shows that driver's lap count, best lap and average since the personal timer started. Timer state stays in that phone's browser and does not modify race data. For internet publishing, proxy only these paths: ```text /public-training/* /src/public_training.js /src/public_training.css ``` Do not expose `/api/state` or the full admin application. Static serving is restricted to `/src` and `/docs`; `data/`, server scripts and bundled binaries are not public static files. ## Create Race Wizard - `Race Setup` now starts with a four-step wizard for new races: - `Basics` - `Participants` - `Session plan` - `Confirm` - The wizard is intended to create the first race structure quickly: - choose class and preset - scope valid race drivers - create practice, qualifying, and/or team-race sessions automatically - Finals are still generated later from `Race Actions` after practice or qualifying is complete. ## Manage workflow - After the race is created, `Manage` is split into four clear stages: - `Setup` - `Format` - `Generation` - `Live / results` - `Setup` handles race participants and teams. The top step cards also act as quick navigation between the four blocks. - `Format` handles practice/qualifying, finals, validation, presets, and advanced settings. In Basic mode, endurance hides most qualifying/finals fields until Advanced is opened. - `Generation` is kept separate so actions like qualifying generation, reseeding, finals generation, and bump-up do not clutter the format form. - `Live / results` is where grid, standings, print, PDF, and finals matrix are used once the structure is ready. ## Export / Import - `Settings -> Storage` now supports: - full JSON export/import for the complete competition database - directory JSON export/import for only classes, drivers, and cars - CSV export for drivers and cars - `Race Setup -> Manage -> Race Actions` now supports single race packages: - export one race with its sessions, results, and referenced classes/drivers/cars - import that package into another installation as a new race entry - JSON export files now include export metadata and a schema version field so future imports can be handled more safely. ## Race features ### Follow-up time - Configure it in `Race Setup -> Manage -> Race Format` - It can also be set per session when creating or editing a session - When scheduled time ends, the session enters follow-up first - When follow-up ends, the session closes automatically - Generated qualifying/finals inherit follow-up from race format ### Min / Max lap time - Configure it in `Race Setup -> Manage -> Race Format` - `Min lap time`: - laps faster than the threshold are ignored as shortcuts or false hits - `Max lap time`: - laps slower than the threshold are not counted as valid laps - also used to split stints and improve statistics Example: - track around `16 sec/lap` - `Min lap time = 11 sec` - `Max lap time = 60 sec` ### Invalid passings - `Timing -> Recent Passings` shows both valid and invalid passings - displayed statuses include: - `Short lap` - `Over max lap` - invalid passings are visually highlighted ### Manual corrections - open `Timing` - click `Details` on a driver or team - available actions: - `+1 lap` - `-1 lap` - `+1 sec` - `+5 sec` - `-1 sec` - `Reset correction` - leaderboard and results update immediately ### Best laps / average / consecutive - Configure per session with `Seed Method` when `Best laps for seeding` is greater than `0` - available modes: - `Best N laps, total` - `Best N laps, average` - `Best N consecutive laps` - leaderboard and standings use the correct format: - total: `3/00:48.321` - average: `3 avg 16.107` - consecutive: `3 con 00:49.005` ### Points tables and tie-break - Configure in `Race Setup -> Manage -> Race Format` - qualifying points tables: - `Placement values (1,2,3...)` - `Descending by field size` - `10-9-8-7-6-5-4-3-2-1` - qualifying tie-break options: - `Compare counted rounds` - `Best single lap` - `Best round / heat result` - qualifying standings also show an inline tie-break note in the table ### Race format presets - `Race Setup -> Manage -> Race Format` - choose a preset and click `Apply preset` - built-in presets: - `Short technical track 16s` - `Club race qual + finals` - `IFMAR-style qual/finals` - `Endurance / team race` - after applying, all fields can still be adjusted manually and saved as usual - `Save club preset` stores your own local presets in app state - `Delete club preset` removes a local preset - `Settings -> Club presets` can export/import the preset library as JSON between installations ### Schedule drift on Dashboard - `Dashboard` shows whether the day is ahead of or behind schedule - planned time includes: - session duration - follow-up time - actual time includes: - real time from start to stop - or live runtime if the session is still running ### Invalid-lap marker in leaderboard - if the latest passing for a driver or team is invalid, the row is marked in both leaderboard and overlay - this makes it easier to see problems without staying in `Recent Passings` ### Manual invalidate / restore last lap - `Timing -> Details` - `Invalidate last lap` marks the latest counted lap as manually invalid - leaderboard, overlay, and lap history update immediately - `Restore latest manually invalidated lap` puts the latest manually invalidated lap back if needed ### Judging view - separate `Judging` menu - shows active session, leaderboard, lap history, and judging log in one view - same corrections as `Timing -> Details` - row filters: - `All` - `Invalid` - `Corrected` - `Team race` - judging log can be exported per session as JSON - supports `Undo latest` and per-log-row undo for multiple recent manual actions ## Windows installation Use 64-bit Windows 11 and Node.js 22 or 24 LTS. The easiest supported setup is: 1. Clone or extract the complete repository, preferably to `C:\\Live_RC`. 2. Double-click `windows\\install.bat`. 3. The installer runs `npm.cmd restart` and leaves the backend running. 4. Open `http://localhost:8081`. The setup script can install Node.js LTS through `winget`, runs locked dependency installation with `npm.cmd ci`, verifies SQLite/Express, and checks the bundled AMMC runtime. Full instructions and troubleshooting: [`windows/README.md`](windows/README.md). Common commands: ```powershell npm.cmd start npm.cmd stop npm.cmd restart npm.cmd run status npm.cmd run start:fg ``` - `npm.cmd start` starts the backend in the background - `npm.cmd stop` stops the process via `data/server.pid` - `npm.cmd restart` restarts the backend - `npm.cmd run status` shows whether the backend is running - `npm.cmd run start:fg` runs in foreground for troubleshooting ### Windows scripts The `windows/` folder includes: - `windows\\install.bat` installs dependencies and runs `npm.cmd restart` - `windows\\start_ammc.bat` starts AMMC in the background - `windows\\start_backend.bat` starts `npm.cmd start` in the background - `windows\\restart_backend.bat` runs `npm.cmd restart` after updates - `windows\\start_all.bat` starts both AMMC + backend and opens the web page - `windows\\stop_all.bat` stops the managed backend and `ammc-amb.exe` Backend logs: - `logs\\server.out.log` - `logs\\server.err.log` SQLite database file: - `data\\rc_timing.sqlite` ## Connect to AMMC There are two supported modes. ### A. Managed AMMC from the web UI Important: - AMMC runs on the same host where the backend was started with `npm.cmd start` - if you only browse from another laptop, no process starts there - the `AMMC executable` field in `Settings` is a path on the backend host, not the client browser machine 1. Place AMMC binaries in the `AMMC/` project folder. 2. Open `Settings`. 3. Enable `Managed AMMC`. 4. Set `Decoder IP / host`, verify port `9000`, save. 5. Click `Start AMMC`. 6. Click `Use server WS URL` so the client uses for example `ws://:9000`. Default binaries: - Linux host: `AMMC/linux_x86-64/ammc-amb` - Windows host: `AMMC/windows64/ammc-amb.exe` - macOS host: `AMMC/apple_m/ammc-amb` ### B. Manual start Example: ```powershell ammc-amb.exe -w 9000 192.168.1.11 ``` In the app: 1. Open `Settings` 2. Set `WebSocket URL`, for example `ws://127.0.0.1:9000` 3. Set `Backend URL` to `http://127.0.0.1:8081` 4. Click `Test Backend` 5. Go to `Timing` and click `Connect Decoder` If Linux firewall/UFW is enabled: ```bash sudo ufw allow 8081/tcp ``` ## Auto reload during development - The server watches `index.html`, `src/app.js`, and `src/styles.css` - When files in `Live_RC` change, the client reloads automatically - If backend code changes on Windows, run `npm.cmd restart` ## Verify SQLite persistence Latest passings via API: - `http://localhost:8081/api/passings` Saved app state via API: - `http://localhost:8081/api/state` ## Important rule for sponsor events - In the same running session, every active car must have a unique transponder ID - The same transponder ID can be reused in the next session (`Heat 1 -> Heat 2 -> Heat 3 -> Final 1 ...`) ## AMMC JSON reference Official quick start: - https://www.ammconverter.eu/docs/intro/quick-start/ Example message: ```json { "msg": "PASSING", "passing_number": 1, "transponder": 232323, "rtc_time": "2022-10-11T22:57:36.099+02:00", "strength": 0.0, "resend": false, "tran_code": "ID:232323", "loop_id": "55" } ```