fix: restore live V2X signal and vehicle fidelity in Cesium preview
The ported overlay used the correct REST paths but lost the data handling
from the source dashboard's live-intersection view (HologramCross), so
signals rendered permanently red and vehicles often never appeared.
- Lamp status codes now follow the dashboard dictionary (11/21/22/23/31).
The previous 2/3 reading made every real push fall through to red. The
dictionary lives only in the overlay; the preview consumes normalized
{nodeKeys, color, countDown} entries so the two copies cannot drift.
- Bind V2X phases to native signal heads geometrically. The runtime
document has no phaseNo, so the old lookup fell back to signal.id and
never matched, leaving the dynamic assembly dark. Travel heading is
recovered as faceHeadingDegrees + 180, per the generator's
mast = travel - 90 / face = travel + 180. Verified 7/7 exact matches
against the fengshu-er-road runtime document.
- A phase now lights every approach it drives; the phase -> single entity
map silently overwrote all but the last.
- Drive the countdown assets from the push's countDown field.
- All three sockets heartbeat every 30s and reconnect with backoff,
replaying their subscription frame. Without this the service dropped
the connection and the scene emptied after about a minute.
- The OBU socket sends its bounds frame on connect and on camera move;
it previously sent nothing at all.
- Vehicles are swept when a push goes stale and their slots reused, so
they no longer accumulate as ghosts. Models follow the dashboard's
car_obu.glb / ${type}${subType}.glb naming.
- Parse vehicle pushes leniently, since the dashboard uses saferEval and
the payload is not guaranteed to be strict JSON. Failures are counted
and surfaced rather than dropped; no eval is introduced.
- Drop FlowTravelRatio/queryListWeek, which is not part of this view.
Also corrects a stale spec rule that required vehicleModelNames to be
empty. Live V2X vehicles need packaged models; the real invariant is no
generated routes or traffic simulation.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,28 @@ not commit a private upstream address, an account, a token, or an AMap key.
|
||||
`crossCode` is deployment configuration for the selected intersection. It is
|
||||
never shown or editable in the browser login panel.
|
||||
|
||||
### Optional phase binding overrides
|
||||
|
||||
`traffic-signals.json` carries no V2X phase number, so live phases are bound to
|
||||
native signal heads geometrically (see *Phase Binding* below). Three optional
|
||||
keys tune or replace that binding:
|
||||
|
||||
| Key | Default | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `maxDistanceMeters` | `30` | Stop-line proximity tolerance. |
|
||||
| `maxHeadingDeltaDegrees` | `45` | Approach heading tolerance. |
|
||||
| `phaseSignalMap` | none | Explicit `{"<phaseNo>": ["<nodeKey>", …]}`; bypasses geometry for the listed phases. |
|
||||
|
||||
Use `phaseSignalMap` when the V2X link geometry cannot be matched — the V2X
|
||||
panel reports how many phases bound and how many did not.
|
||||
|
||||
The page always starts at the login gate. After authentication it resolves the
|
||||
current intersection with the same source-dashboard rule: request the HD-map
|
||||
cross list and cross-device configuration list, prefer a configured cross, then
|
||||
use the first available HD-map cross. Only after that resolution succeeds does
|
||||
it issue the operational REST requests or open signal, OBU, and target-vehicle
|
||||
WebSockets.
|
||||
|
||||
The browser sends `POST {apiBaseUrl}/facilities/api/sys/login` with `userName`
|
||||
and an MD5-hashed password, matching the source V2X dashboard. It holds the
|
||||
returned token in `sessionStorage` only, sends it as the raw `Authorization`
|
||||
@@ -36,12 +58,48 @@ signal WebSocket. Closing the tab clears the session token.
|
||||
## Data Sources
|
||||
|
||||
After sign-in the preview reads the selected intersection's links, pole
|
||||
configuration, bound devices, device configuration, weekly traffic flow ratio,
|
||||
and `/network/ws/network/signal` phase updates. It also subscribes to
|
||||
configuration, bound devices, and device configuration, and subscribes to
|
||||
`/network/ws/network/signal` for phase updates. It also subscribes to
|
||||
`/network/ws/network/obuPosition` and `/network/ws/network/targetPosition` for
|
||||
the only vehicle and vehicle-line sources. Failures are shown in the V2X panel;
|
||||
the static Cesium scene stays available and the vehicle layer stays empty.
|
||||
|
||||
All three sockets mirror the source dashboard's connection behaviour: a
|
||||
`{"heartBeat":"ping"}` frame every 30 s, automatic reconnect with backoff, and
|
||||
the subscription frame replayed on every reconnect. Without the heartbeat the
|
||||
service drops the connection and the scene silently empties after a minute.
|
||||
|
||||
Subscription frames match the dashboard exactly:
|
||||
|
||||
| Socket | Frame on every (re)connect |
|
||||
| --- | --- |
|
||||
| `signal` | `{"junctionId":"<crossCode>"}` |
|
||||
| `obuPosition` | `{"bounds":"<four GCJ-02 corners>"}`, or an empty frame when the camera rectangle is unavailable (no viewport filter) |
|
||||
| `targetPosition` | `{"deviceId":"<ids>"}`, or `{"deviceId":null}` when no targets are configured |
|
||||
|
||||
## Phase Binding
|
||||
|
||||
The runtime traffic-signal document has no V2X `phaseNo`, so phases are bound to
|
||||
native signal heads by geometry: the V2X link's last segment ends at the stop
|
||||
line, and each native signal records its own stop-line point and heading. The
|
||||
generator in `scripts/lib/traffic-signals.js` derives `mast = travel - 90` and
|
||||
`face = travel + 180` from the approach travel direction, so the travel heading
|
||||
is recovered as `faceHeadingDegrees + 180`.
|
||||
|
||||
One link may bind several heads on the same approach, and several links may
|
||||
share one phase; both are unions, so a phase lights every approach it drives.
|
||||
Unbound phases are counted in the panel — override them with `phaseSignalMap`.
|
||||
|
||||
## Vehicle Lifecycle
|
||||
|
||||
Vehicles follow the dashboard's rules: the push `interval` (`0` falls back to
|
||||
500 ms) sets the animation duration, a vehicle not updated within
|
||||
`interval * 1.5` is hidden, and hidden entries stay as reusable slots rather
|
||||
than being removed. Models are named as the dashboard names them —
|
||||
`car_obu.glb` for OBU vehicles and `${type}${subType}.glb` for recognized
|
||||
targets — falling back to the first packaged model, with the shortfall reported
|
||||
in the panel.
|
||||
|
||||
## Coordinate Contract
|
||||
|
||||
| Data | Coordinate system | Preview handling |
|
||||
@@ -56,9 +114,19 @@ directly to Cesium. Either error produces a visible intersection offset.
|
||||
## Proxy Requirements
|
||||
|
||||
Use an HTTPS reverse proxy which forwards the configured REST prefix and supports
|
||||
WebSocket upgrade for the configured WS prefix. The generated preview is static,
|
||||
WebSocket upgrade for the configured WS prefix. For development or a simple host
|
||||
without Nginx, use the repository's proxy static server instead of
|
||||
`python3 -m http.server`:
|
||||
|
||||
```bash
|
||||
V2X_UPSTREAM=http://<v2x-api-host>:<port> \
|
||||
npm run serve:v2x-preview -- --root outputs/fengshu-er-road --host 0.0.0.0 --port 7862
|
||||
```
|
||||
|
||||
It rewrites `/api/*` and `/websocket/*` before proxying, matching the source
|
||||
dashboard's Vite proxy. The upstream address stays an environment value, not a
|
||||
browser configuration or committed area file. The generated preview is static,
|
||||
so direct cross-origin calls are likely to fail CORS or expose an internal origin.
|
||||
The V2X panel reports that condition while leaving native preview controls usable.
|
||||
|
||||
## Rollback
|
||||
|
||||
|
||||
Reference in New Issue
Block a user