diff --git a/arena_studio.html b/arena_studio.html index 126d66e1..902fa506 100644 --- a/arena_studio.html +++ b/arena_studio.html @@ -2779,10 +2779,9 @@

Import error

Rig I/O — session roles
- - - @@ -6938,7 +6937,8 @@

Import error

opt.title = on ? title : 'Needs io_ext firmware (0xC2 capability bit 5) — connect a controller running feat/dio-roles-ao-modes or later.'; }; - gate('cIoRoleDio1', 'in_trigger', 'Input role — on Digital IO 2 this is the EINT trigger route (J30); pulse-semantics bench validation pending.'); + // Digital IO 1 has no in_trigger option at all: that BNC has no route to the panels' EINT net + // (#168). Only Digital IO 2 can be the trigger input. gate('cIoRoleDio2', 'in_trigger', 'Input role — the EINT trigger route to the panels (J30 shunt); pulse-semantics bench validation pending.'); gate('cIoRoleDio1', 'out_debug_framescan', 'Scope envelope: HIGH exactly while each frame\'s SPI transmission runs (bench-verified).'); gate('cIoRoleDio2', 'out_debug_framescan', 'Scope envelope: HIGH exactly while each frame\'s SPI transmission runs. NOTE: with the J30 shunt installed this also pulses the panels\' EINT net.'); @@ -6993,6 +6993,11 @@

Import error

const r = roleOf(d.port === 1 ? 'cIoRoleDio1' : 'cIoRoleDio2', d.role); const ch = d.port; // io: port == wire channel == board "Digital IO n (5V)" label if (r === 'off') continue; // off = DON'T TOUCH (keeps fw boot roles, e.g. port 2 in_trigger) + if (ch === 1 && r === 'in_trigger') { + // Digital IO 1 has no EINT route: the trigger would be silently dead (#168). + clog('rig io: Digital IO 1 cannot be the trigger input (only Digital IO 2 reaches the panels\' EINT) — not applied', 'warn'); + continue; + } if (ext) { // Explicit role first (SET_DIO_ROLE 0xAC), then the level for outputs. try { diff --git a/js/plugin-registry.js b/js/plugin-registry.js index 4001884e..50f75c81 100644 --- a/js/plugin-registry.js +++ b/js/plugin-registry.js @@ -1253,6 +1253,14 @@ function parseRigIo(rigData) { } var slot = result.dio[port - 1]; slot.role = _rigIoRole(entry.role, RIG_IO_ROLES.dio, 'dio port ' + port, result.warnings); + // Only "Digital IO 2 (5V)" is wired to the panels' EINT trigger net (U3 + J30 shunt). + // Digital IO 1 has no route, so in_trigger there would be a silently dead trigger (#168). + if (port === 1 && slot.role === 'in_trigger') { + result.warnings.push( + 'dio port 1 cannot be in_trigger: only "Digital IO 2 (5V)" reaches the panels\' trigger input (EINT); treated as off' + ); + slot.role = 'off'; + } // default is outputs-only; clamp anything truthy-numeric/boolean to 0|1. if (entry.default != null) slot.default = entry.default === true || Number(entry.default) === 1 ? 1 : 0; diff --git a/tests/test-protocol-roundtrip-v3.js b/tests/test-protocol-roundtrip-v3.js index 702cecab..5bc8f657 100644 --- a/tests/test-protocol-roundtrip-v3.js +++ b/tests/test-protocol-roundtrip-v3.js @@ -3846,6 +3846,53 @@ console.log('\n--- Suite N12io: rig io: block (#135) ---'); bad.warnings.join(' | ') ); + // (c2) #168: Digital IO 1 has no route to the panels' EINT trigger net — in_trigger on + // port 1 degrades to off with a warning naming Digital IO 2; port 2 keeps it. + const trig = parseRigIo({ + io: { + dio: [ + { port: 1, role: 'in_trigger' }, + { port: 2, role: 'in_trigger' } + ] + } + }); + check('N12io-c2: port 1 in_trigger → off (no EINT route)', trig.dio[0].role, 'off'); + check('N12io-c2: port 2 in_trigger kept (the EINT route)', trig.dio[1].role, 'in_trigger'); + checkTrue( + 'N12io-c2: one warning, naming Digital IO 2', + trig.warnings.length === 1 && + /port 1 cannot be in_trigger/.test(trig.warnings[0]) && + /Digital IO 2/.test(trig.warnings[0]), + trig.warnings.join(' | ') + ); + // The Studio side: Digital IO 1 offers no in_trigger option at all (the reason is in its + // tooltip), capability gating never adds it back, and the apply path refuses to send it. + const studioHtml = fs.readFileSync(path.join(__dirname, '..', 'arena_studio.html'), 'utf8'); + const dio1Select = (studioHtml.match(/