Keep the colleague's microbu-esp32c5 tree in this repository
obu-firmware builds against vanetza-idf from microbu-esp32c5/external, but that tree was gitignored, so a clone of this repository could not build the firmware it ships. It is now committed here as ordinary files in its own folder, microbu-esp32c5/: the colleague's commit cf4b99f plus the V2X2MAP bridge's signature verification (--trust) used on the bench. Nothing is fetched from or pushed to the colleague's repository; this repository and its remotes carry everything. The folder's own .gitignore keeps build output, downloaded components and private key material out, as it did there; the committed file set is identical to that repository's tracked files. The ESP32-C5 is still flashed from obu-firmware/, which only takes vanetza-idf from microbu-esp32c5/, so the two stay separate folders. FLASHING.md says how to take a newer version of the colleague's tree (copy it over the folder, rebuild, test, commit).
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
// TEST-V2X2MAP-VAM-002: exercise the dashboard's actual rendering functions.
|
||||
const fs = require('node:fs');
|
||||
const vm = require('node:vm');
|
||||
const assert = require('node:assert/strict');
|
||||
const html = fs.readFileSync(require('node:path').join(__dirname, '../dashboard.html'), 'utf8');
|
||||
const script = html.split('<script>')[1].split('</script>')[0];
|
||||
new vm.Script(script); // Syntax-check the entire dashboard, not only VAM code.
|
||||
const slice = (a, b) => script.slice(script.indexOf(a), script.indexOf(b));
|
||||
const context = vm.createContext({assert, console});
|
||||
vm.runInContext(slice('const MSG =', '// ─── Map') +
|
||||
slice('const vehicles ', 'setInterval(()=>{') +
|
||||
slice('const filterActive=', 'document.querySelectorAll(\'.fchip\')') +
|
||||
slice('function buildSummary(', 'function etCls('), context);
|
||||
vm.runInContext(`
|
||||
const map = {setView() {}};
|
||||
const L = {
|
||||
divIcon: x => x,
|
||||
marker: () => ({addTo() {return this}, bindPopup() {}, on() {},
|
||||
setLatLng() {}, setIcon() {}, isPopupOpen: () => false})
|
||||
};
|
||||
const frame = {msg_type:'VAM', station_id:'1234', lat:53.9, lon:10,
|
||||
speed_mps:5, heading_deg:90, station_type_label:'cyclist',
|
||||
vam:{station_id:42, station_type:2, generation_delta_time:65535,
|
||||
speed_mps:5, altitude_m:12.3, longitudinal_acceleration_mps2:-0.5,
|
||||
containers:{test:'<img src=x onerror=alert(1)>'}}};
|
||||
onPositionFrame(frame);
|
||||
onPositionFrame({...frame, lat:54});
|
||||
assert.equal(vehicles.size, 1);
|
||||
const marker = [...vehicles.values()][0];
|
||||
assert.equal(marker.count, 2);
|
||||
const popup = popupHtml(marker);
|
||||
for (const text of ['Vulnerable road user (VAM)', '65535', '18.0 km/h', '12.3 m', '-0.5 m/s²', 'VAM containers'])
|
||||
assert.ok(popup.includes(text), text);
|
||||
assert.ok(!popup.includes('<img'));
|
||||
assert.ok(popup.includes('<img'));
|
||||
const noValues = popupHtml({lastFrame:{...frame, vam:{station_id:0,station_type:2,generation_delta_time:0}},count:1,lastMs:0});
|
||||
assert.ok(!noValues.includes('undefined'));
|
||||
assert.ok(!noValues.includes('NaN'));
|
||||
assert.ok(isVisible('VAM'));
|
||||
filterActive.delete('VAM');
|
||||
assert.ok(!isVisible('VAM'));
|
||||
assert.ok(isVisible('CAM'));
|
||||
assert.ok(buildSummary({lastFrame:frame,msgType:'VAM',frameCount:1}).includes('65535'));
|
||||
assert.ok(buildSummary({lastFrame:{vam_status:'invalid VAM'},msgType:'VAM',frameCount:1}).includes('invalid VAM'));
|
||||
console.log('PASS: VAM map updates, popups, unavailable fields, escaping, filters and log');
|
||||
`, context);
|
||||
@@ -0,0 +1,46 @@
|
||||
[
|
||||
{
|
||||
"fields": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"uper": "03100000000100000009ad2748035a4e9007ffffff08eddd0f8000007e0003f50730"
|
||||
},
|
||||
{
|
||||
"fields": [
|
||||
4294967295,
|
||||
539000000,
|
||||
100000000,
|
||||
65535,
|
||||
500,
|
||||
900
|
||||
],
|
||||
"uper": "0310ffffffffffff000aae2b2e0389fd9807ffffff08eddd0f8001c27e07d3f50730"
|
||||
},
|
||||
{
|
||||
"fields": [
|
||||
42,
|
||||
-900000000,
|
||||
-1800000000,
|
||||
1,
|
||||
16383,
|
||||
3601
|
||||
],
|
||||
"uper": "03100000002a000100080000000000000007ffffff08eddd0f800708fefffff50730"
|
||||
},
|
||||
{
|
||||
"fields": [
|
||||
9,
|
||||
900000000,
|
||||
1800000000,
|
||||
32768,
|
||||
1,
|
||||
3599
|
||||
],
|
||||
"uper": "0310000000098000000b5a4e9006b49d2007ffffff08eddd0f800707fe0007f50730"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,75 @@
|
||||
2026-09-09 host verification
|
||||
|
||||
COMMAND: C:\bachelorthesis_micrOBU\.venv\Scripts\python.exe -W ignore::DeprecationWarning -m unittest discover -s C:\bachelorthesis_micrOBU\implementation\v2x2map-0.3.0\bridge -p test_vam.py -v
|
||||
test_all_truncations_and_trailing_data (test_vam.VamTests.test_all_truncations_and_trailing_data) ... ok
|
||||
test_independent_asn1c_vectors (test_vam.VamTests.test_independent_asn1c_vectors) ... ok
|
||||
test_malformed_never_falls_back_to_gn_position (test_vam.VamTests.test_malformed_never_falls_back_to_gn_position) ... ok
|
||||
test_optional_containers_and_json (test_vam.VamTests.test_optional_containers_and_json) ... ok
|
||||
test_secured_header_is_not_guessed (test_vam.VamTests.test_secured_header_is_not_guessed) ... ok
|
||||
test_transport_and_map_fields (test_vam.VamTests.test_transport_and_map_fields) ... ok
|
||||
test_unavailable_coordinates_and_real_zero (test_vam.VamTests.test_unavailable_coordinates_and_real_zero) ... ok
|
||||
|
||||
----------------------------------------------------------------------
|
||||
Ran 7 tests in 0.709s
|
||||
|
||||
OK
|
||||
|
||||
COMMAND: node C:\bachelorthesis_micrOBU\implementation\v2x2map-0.3.0\bridge\tests\dashboard-vam.cjs
|
||||
PASS: VAM map updates, popups, unavailable fields, escaping, filters and log
|
||||
|
||||
COMMAND: C:\bachelorthesis_micrOBU\.venv\Scripts\python.exe C:\bachelorthesis_micrOBU\implementation\v2x2map-0.3.0\bridge\its_g5_bridge.py --help
|
||||
usage: its_g5_bridge.py [-h] [--port PORT] [--list-ports] [--baud BAUD]
|
||||
[--node-id NODE_ID] [--broker URL] [--no-mqtt]
|
||||
[--reset-on-start] [--reconnect-delay RECONNECT_DELAY]
|
||||
[--exit-after EXIT_AFTER]
|
||||
[--dashboard-port DASHBOARD_PORT] [--open-browser]
|
||||
[--no-browser] [--pcap-out PCAP_OUT] [--verbose]
|
||||
|
||||
ITS-G5 USB->MQTT bridge (V2X2MAP)
|
||||
Config: C:\bachelorthesis_micrOBU\implementation\v2x2map-0.3.0\bridge\config\v2x2map.cfg
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--port PORT serial port (e.g. COM7); omit to auto-detect or use
|
||||
config
|
||||
--list-ports print available serial ports with VID/PID and exit
|
||||
--baud BAUD
|
||||
--node-id NODE_ID device ID, e.g. V2X2MAP:d2cf13ed6293 (read from config
|
||||
if omitted)
|
||||
--broker URL MQTT broker URL (mqtts://host:port or
|
||||
mqtt://host:port). Can be specified multiple times.
|
||||
Default from config or
|
||||
mqtts://cits1.opentrafficmap.org:8883
|
||||
--no-mqtt parse only, don't publish (for testing)
|
||||
--reset-on-start pulse RTS to reboot the device on first connect
|
||||
--reconnect-delay RECONNECT_DELAY
|
||||
seconds to wait before reconnecting after USB
|
||||
disconnect (0 = exit)
|
||||
--exit-after EXIT_AFTER
|
||||
exit after N seconds (0 = run forever)
|
||||
--dashboard-port DASHBOARD_PORT
|
||||
local HTTP port for the live dashboard (0 to disable)
|
||||
--open-browser open the dashboard URL in the default browser on start
|
||||
(default: on when running as .exe)
|
||||
--no-browser suppress automatic browser opening
|
||||
--pcap-out PCAP_OUT write all captured frames to a .pcap file
|
||||
(DLT_IEEE802_11)
|
||||
--verbose, -v
|
||||
|
||||
PASS: Python/spec syntax, bridge CLI, Debian archive includes decoder, schemas, requirements and notices. Debian install and Windows EXE build not run.
|
||||
|
||||
2026-09-14 host verification (signed VAMs)
|
||||
|
||||
Change: bridge/secured.py unwraps TS 103 097 signed GeoNetworking packets (no signature
|
||||
verification); its_g5_bridge.decode_itsg5_full continues on the unsecured payload and reports
|
||||
security = {psid, hash, signer, signer_id, generation_time_ms, verified: false}.
|
||||
|
||||
COMMAND: C:\bachelorthesis_micrOBU\.venv\Scripts\python.exe -W ignore::DeprecationWarning -m unittest -v test_vam
|
||||
Ran 8 tests ... OK (new: test_signed_vam_is_unwrapped_but_not_verified, vector = frame 1 of
|
||||
recordings/its5_20260914_202525.pcap, signer HashedId8 ba1862d0e89fe341 = the micrOBU's ticket)
|
||||
|
||||
COMMAND: node tests/dashboard-vam.cjs
|
||||
PASS
|
||||
|
||||
Live: bridge on COM20 (V2X2MAP receiver firmware) with the micrOBU transmitting signed VAMs on
|
||||
COM11: /events carries msg_type VAM, vam_status decoded, security.signer certificate/digest.
|
||||
Reference in New Issue
Block a user