Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions arena_studio.html
Original file line number Diff line number Diff line change
Expand Up @@ -2779,10 +2779,9 @@ <h2 id="modalTitle">Import error</h2>
</div>
<div class="cmenu-sep" title="Rig-declared controller I/O roles + power-on defaults (rig YAML io: block, #135). Applied automatically at connect; the role selectors are SESSION-LOCAL overrides — they never edit the rig YAML. Greyed roles need firmware that doesn't exist yet. Names match the board's BNC silkscreen.">Rig I/O — session roles</div>
<div class="cmenu-row">
<label title="The BNC silkscreened 'Digital IO 1 (5V)' on the controller (J3; rig io: port 1 = 0xAA channel 1)">Digital IO 1</label>
<select id="cIoRoleDio1" title="'Digital IO 1 (5V)' role — session-local override of the rig's io: declaration">
<label title="The BNC silkscreened 'Digital IO 1 (5V)' on the controller (J3; rig io: port 1 = 0xAA channel 1). It can't be the trigger input: only Digital IO 2 (5V) is wired to the panels' trigger (EINT).">Digital IO 1</label>
<select id="cIoRoleDio1" title="'Digital IO 1 (5V)' role — session-local override of the rig's io: declaration. No in_trigger here: only Digital IO 2 reaches the panels' trigger input (EINT).">
<option value="off">off</option>
<option value="in_trigger" disabled title="Needs io_ext firmware (0xC2 capability bit 5) — enables automatically when the connected controller advertises it.">in_trigger</option>
<option value="out_programmable">out_programmable</option>
<option value="out_debug_framescan" disabled title="Needs io_ext firmware (0xC2 capability bit 5) — enables automatically when the connected controller advertises it.">out_debug_framescan</option>
</select>
Expand Down Expand Up @@ -6938,7 +6937,8 @@ <h2 id="modalTitle">Import error</h2>
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.');
Expand Down Expand Up @@ -6993,6 +6993,11 @@ <h2 id="modalTitle">Import error</h2>
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 {
Expand Down
8 changes: 8 additions & 0 deletions js/plugin-registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
47 changes: 47 additions & 0 deletions tests/test-protocol-roundtrip-v3.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(/<select id="cIoRoleDio1"[\s\S]*?<\/select>/) || [''])[0];
checkTrue('N12io-c2: Studio DIO 1 select found', dio1Select.length > 0, 'select missing');
checkTrue(
'N12io-c2: Studio DIO 1 has no in_trigger option',
!/value="in_trigger"/.test(dio1Select),
dio1Select
);
checkTrue(
'N12io-c2: Studio DIO 1 tooltip says why (only Digital IO 2 reaches EINT)',
/id="cIoRoleDio1" title="[^"]*only Digital IO 2 reaches the panels' trigger input \(EINT\)/.test(
studioHtml
),
'tooltip changed'
);
checkTrue(
'N12io-c2: capability gating never enables DIO 1 in_trigger',
!/gate\('cIoRoleDio1',\s*'in_trigger'/.test(studioHtml),
'found a gate() call for cIoRoleDio1 in_trigger'
);
checkTrue(
'N12io-c2: apply path skips in_trigger on channel 1',
/if \(ch === 1 && r === 'in_trigger'\)/.test(studioHtml),
'apply guard missing'
);

// (d) value clamping.
const clamp = parseRigIo({
io: {
Expand Down
Loading