Fix TruthValue handling for different firmware versions

Some Accedian firmware reports SNMP TruthValue fields as '1'/'2'
(INTEGER) while others use 'true'/'false' (textual). Add isTrue()
helper that accepts both formats and replace all 15+ boolean checks
across the viewer (present, diagCapable, active, enabled, filter
enable flags, etc).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
sam 2026-03-06 08:30:36 -07:00
parent 4daf26b778
commit bcb179e7e4

View File

@ -734,6 +734,9 @@ function formatUptime(sec) {{
p.push(s+'s'); p.push(s+'s');
return p.join(' '); return p.join(' ');
}} }}
// SNMP TruthValue: some agents return '1'/'2', others 'true'/'false'
function isTrue(v) {{ return v === '1' || v === 'true' || v === true; }}
// Derive data ports and management port from connectors dynamically // Derive data ports and management port from connectors dynamically
function getPortLists() {{ function getPortLists() {{
const connectors = DATA.connectors || {{}}; const connectors = DATA.connectors || {{}};
@ -990,7 +993,7 @@ function renderHeader() {{
const cfgByNum = {{}}; const cfgByNum = {{}};
for (const [k,v] of Object.entries(alarmCfg)) cfgByNum[v.number] = v; for (const [k,v] of Object.entries(alarmCfg)) cfgByNum[v.number] = v;
for (const [k,a] of Object.entries(alarmStatus)) {{ for (const [k,a] of Object.entries(alarmStatus)) {{
if (a.active === '1') {{ if (isTrue(a.active)) {{
activeCount++; activeCount++;
const cfg = cfgByNum[a.number]; const cfg = cfgByNum[a.number];
if (cfg) sevCounts[cfg.severity] = (sevCounts[cfg.severity]||0) + 1; if (cfg) sevCounts[cfg.severity] = (sevCounts[cfg.severity]||0) + 1;
@ -1137,7 +1140,7 @@ function renderPanel() {{
const iface = ifaces[connIdx]; const iface = ifaces[connIdx];
if (connType(connIdx) === 'sfp') {{ if (connType(connIdx) === 'sfp') {{
const sfp = sfpInfo[connIdx]; const sfp = sfpInfo[connIdx];
const present = sfp && sfp.present === '1'; const present = sfp && isTrue(sfp.present);
if (!present) return 'empty'; if (!present) return 'empty';
if (iface && isUp(iface.ifOperStatus)) return 'present-link'; if (iface && isUp(iface.ifOperStatus)) return 'present-link';
return 'present-nolink'; return 'present-nolink';
@ -1152,7 +1155,7 @@ function renderPanel() {{
function sfpLabel(connIdx) {{ function sfpLabel(connIdx) {{
const sfp = sfpInfo[connIdx]; const sfp = sfpInfo[connIdx];
if (!sfp) return ''; if (!sfp) return '';
if (sfp.present !== '1') return 'EMPTY'; if (!isTrue(sfp.present)) return 'EMPTY';
const pn = sfp.vendorPn || ''; const pn = sfp.vendorPn || '';
if (pn.length > 8) return pn.substring(0,8); if (pn.length > 8) return pn.substring(0,8);
return pn || sfp.vendor || ''; return pn || sfp.vendor || '';
@ -1214,7 +1217,7 @@ function renderPanel() {{
// Power feeds // Power feeds
let pwrHtml = '<div class="pwr-block">'; let pwrHtml = '<div class="pwr-block">';
for (const [k,p] of Object.entries(pwr)) {{ for (const [k,p] of Object.entries(pwr)) {{
const ok = p.present === '1'; const ok = isTrue(p.present);
pwrHtml += `<div><span class="pwr-led ${{ok?'ok':'fail'}}"></span>${{esc(p.name)}} ${{ok?'OK':'ABSENT'}}</div>`; pwrHtml += `<div><span class="pwr-led ${{ok?'ok':'fail'}}"></span>${{esc(p.name)}} ${{ok?'OK':'ABSENT'}}</div>`;
}} }}
pwrHtml += '</div>'; pwrHtml += '</div>';
@ -1336,7 +1339,7 @@ function renderSfp() {{
const thresh = sfpThresh[si] || {{}}; const thresh = sfpThresh[si] || {{}};
const conn = connectors[si] || {{}}; const conn = connectors[si] || {{}};
if (!info || info.present !== '1') {{ if (!info || !isTrue(info.present)) {{
cards += `<div class="sfp-detail" id="sfp-detail-${{si}}"> cards += `<div class="sfp-detail" id="sfp-detail-${{si}}">
<div class="card-dark" style="border-left:3px solid #555"> <div class="card-dark" style="border-left:3px solid #555">
<div class="card-body" style="text-align:center;color:#555;padding:2rem"> <div class="card-body" style="text-align:center;color:#555;padding:2rem">
@ -1348,7 +1351,7 @@ function renderSfp() {{
continue; continue;
}} }}
const ddm = info.diagCapable === '1'; const ddm = isTrue(info.diagCapable);
const ddmBadge = ddm const ddmBadge = ddm
? '<span class="badge bg-success">DDM Supported</span>' ? '<span class="badge bg-success">DDM Supported</span>'
: '<span class="badge bg-secondary">DDM Not Supported</span>'; : '<span class="badge bg-secondary">DDM Not Supported</span>';
@ -1405,8 +1408,8 @@ function renderSfp() {{
<h6 style="font-size:0.8rem;color:var(--text-muted)">CAPABILITIES</h6> <h6 style="font-size:0.8rem;color:var(--text-muted)">CAPABILITIES</h6>
<div style="font-size:0.82rem"> <div style="font-size:0.82rem">
<div>DDM Capable: <strong>${{ddm ? 'Yes' : 'No'}}</strong></div> <div>DDM Capable: <strong>${{ddm ? 'Yes' : 'No'}}</strong></div>
<div>Internal Cal: <strong>${{info.internalCal==='1' ? 'Yes' : 'No'}}</strong></div> <div>Internal Cal: <strong>${{isTrue(info.internalCal) ? 'Yes' : 'No'}}</strong></div>
<div>Alarm Capable: <strong>${{info.alarmCapable==='1' ? 'Yes' : 'No'}}</strong></div> <div>Alarm Capable: <strong>${{isTrue(info.alarmCapable) ? 'Yes' : 'No'}}</strong></div>
<div>SFF-8472 Rev: <strong>${{esc(info.rev8472)}}</strong></div> <div>SFF-8472 Rev: <strong>${{esc(info.rev8472)}}</strong></div>
<div>ID Type: <strong>${{esc(info.idType)}}</strong></div> <div>ID Type: <strong>${{esc(info.idType)}}</strong></div>
<div>Ext ID: <strong>${{esc(info.extIdType)}}</strong></div> <div>Ext ID: <strong>${{esc(info.extIdType)}}</strong></div>
@ -1447,7 +1450,7 @@ function renderAlarms() {{
// Collect active alarms (when status table available) // Collect active alarms (when status table available)
const active = []; const active = [];
for (const [k,a] of Object.entries(alarmStatus)) {{ for (const [k,a] of Object.entries(alarmStatus)) {{
if (a.active === '1') {{ if (isTrue(a.active)) {{
const cfg = cfgByNum[a.number] || {{}}; const cfg = cfgByNum[a.number] || {{}};
active.push({{ ...a, ...cfg, _statusId: k }}); active.push({{ ...a, ...cfg, _statusId: k }});
}} }}
@ -1489,7 +1492,7 @@ function renderAlarms() {{
<td>${{esc(c.description)}}</td>`; <td>${{esc(c.description)}}</td>`;
if (hasSeverity) rows += ` if (hasSeverity) rows += `
<td><span class="badge ${{sevClass(c.severity)}}">${{sevLabel(c.severity)}}</span></td> <td><span class="badge ${{sevClass(c.severity)}}">${{sevLabel(c.severity)}}</span></td>
<td>${{c.enabled === '1' ? '<span style="color:var(--green)">Yes</span>' : '<span style="color:var(--text-muted)">No</span>'}}</td>`; <td>${{isTrue(c.enabled) ? '<span style="color:var(--green)">Yes</span>' : '<span style="color:var(--text-muted)">No</span>'}}</td>`;
if (hasCondition) rows += ` if (hasCondition) rows += `
<td class="mono">${{esc(c.conditionType)}}</td> <td class="mono">${{esc(c.conditionType)}}</td>
<td class="mono">${{esc(c.amoType)}}</td>`; <td class="mono">${{esc(c.amoType)}}</td>`;
@ -1636,12 +1639,12 @@ function renderFilters() {{
let rows = ''; let rows = '';
for (const [id, f] of Object.entries(filters)) {{ for (const [id, f] of Object.entries(filters)) {{
const conditions = []; const conditions = [];
if (f.macDstEn === '1') conditions.push('MAC Dst: ' + esc(f.macDst)); if (isTrue(f.macDstEn)) conditions.push('MAC Dst: ' + esc(f.macDst));
if (f.macSrcEn === '1') conditions.push('MAC Src: ' + esc(f.macSrc)); if (isTrue(f.macSrcEn)) conditions.push('MAC Src: ' + esc(f.macSrc));
if (f.etypeEn === '1') conditions.push('EType: ' + esc(f.etype)); if (isTrue(f.etypeEn)) conditions.push('EType: ' + esc(f.etype));
if (f.vlan1IdEn === '1') conditions.push('VLAN1: ' + esc(f.vlan1Id)); if (isTrue(f.vlan1IdEn)) conditions.push('VLAN1: ' + esc(f.vlan1Id));
if (f.vlan2IdEn === '1') conditions.push('VLAN2: ' + esc(f.vlan2Id)); if (isTrue(f.vlan2IdEn)) conditions.push('VLAN2: ' + esc(f.vlan2Id));
if (f.vlan1PriorEn === '1') conditions.push('PCP1: ' + esc(f.vlan1Prior)); if (isTrue(f.vlan1PriorEn)) conditions.push('PCP1: ' + esc(f.vlan1Prior));
const condStr = conditions.length ? conditions.join(', ') : '<span class="status-na">any (catchall)</span>'; const condStr = conditions.length ? conditions.join(', ') : '<span class="status-na">any (catchall)</span>';
rows += `<tr> rows += `<tr>
@ -1802,7 +1805,7 @@ function renderLldp() {{
const iface = ifaces[portIdx]; const iface = ifaces[portIdx];
if (isSfp) {{ if (isSfp) {{
const sfp = sfpInfo[portIdx]; const sfp = sfpInfo[portIdx];
const present = sfp && sfp.present === '1'; const present = sfp && isTrue(sfp.present);
if (!present) return 'empty'; if (!present) return 'empty';
if (iface && isUp(iface.ifOperStatus)) return 'present-link'; if (iface && isUp(iface.ifOperStatus)) return 'present-link';
return 'present-nolink'; return 'present-nolink';
@ -2005,7 +2008,7 @@ function renderCoverage() {{
// Check SFP DDM // Check SFP DDM
const sfpInfo = DATA.sfp_info || {{}}; const sfpInfo = DATA.sfp_info || {{}};
for (const [k,s] of Object.entries(sfpInfo)) {{ for (const [k,s] of Object.entries(sfpInfo)) {{
if (s.present === '1' && s.diagCapable !== '1') {{ if (isTrue(s.present) && !isTrue(s.diagCapable)) {{
gaps.push({{ gaps.push({{
type: 'red', type: 'red',
text: `SFP-${{k}} DDM: Not available via SNMP (diagCapable=false). NID web UI reads SFP I2C bus directly.` text: `SFP-${{k}} DDM: Not available via SNMP (diagCapable=false). NID web UI reads SFP I2C bus directly.`