Dynamic port rendering for multi-port NID devices
Replace hardcoded 4-port loops in renderPanel(), renderSfp(), and renderLldp() with dynamic iteration over connectors data. Devices like the AMO-10000-LT-S with more than 4 ports now render all ports automatically. Management port detected by connector name. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d6bf394297
commit
df8c74627b
@ -734,6 +734,23 @@ function formatUptime(sec) {{
|
|||||||
p.push(s+'s');
|
p.push(s+'s');
|
||||||
return p.join(' ');
|
return p.join(' ');
|
||||||
}}
|
}}
|
||||||
|
// Derive data ports and management port from connectors dynamically
|
||||||
|
function getPortLists() {{
|
||||||
|
const connectors = DATA.connectors || {{}};
|
||||||
|
const keys = Object.keys(connectors).sort((a,b) => parseInt(a) - parseInt(b));
|
||||||
|
const dataPorts = [];
|
||||||
|
let mgmtPort = null;
|
||||||
|
for (const k of keys) {{
|
||||||
|
const c = connectors[k];
|
||||||
|
if (c.name && c.name.toLowerCase() === 'management') {{
|
||||||
|
mgmtPort = k;
|
||||||
|
}} else {{
|
||||||
|
dataPorts.push(k);
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
return {{ dataPorts, mgmtPort }};
|
||||||
|
}}
|
||||||
|
|
||||||
function sevLabel(s) {{
|
function sevLabel(s) {{
|
||||||
return {{'0':'INFO','1':'MINOR','2':'MAJOR','3':'CRITICAL'}}[s] || s;
|
return {{'0':'INFO','1':'MINOR','2':'MAJOR','3':'CRITICAL'}}[s] || s;
|
||||||
}}
|
}}
|
||||||
@ -1150,15 +1167,15 @@ function renderPanel() {{
|
|||||||
return {{ label, alias }};
|
return {{ label, alias }};
|
||||||
}}
|
}}
|
||||||
|
|
||||||
// Build port slots dynamically from connectors 1-4
|
// Build port slots dynamically from connectors
|
||||||
|
const {{ dataPorts, mgmtPort }} = getPortLists();
|
||||||
let slots = '';
|
let slots = '';
|
||||||
for (let i = 1; i <= 4; i++) {{
|
for (const idx of dataPorts) {{
|
||||||
const idx = String(i);
|
|
||||||
const conn = connectors[idx] || {{}};
|
const conn = connectors[idx] || {{}};
|
||||||
const isSfp = connType(idx) === 'sfp';
|
const isSfp = connType(idx) === 'sfp';
|
||||||
const state = slotState(idx);
|
const state = slotState(idx);
|
||||||
const pi = portInfo(idx);
|
const pi = portInfo(idx);
|
||||||
const slotName = conn.name || (isSfp ? `SFP-${{i}}` : `RJ45-${{i}}`);
|
const slotName = conn.name || (isSfp ? `SFP-${{idx}}` : `RJ45-${{idx}}`);
|
||||||
|
|
||||||
let icon, detail;
|
let icon, detail;
|
||||||
if (isSfp) {{
|
if (isSfp) {{
|
||||||
@ -1178,7 +1195,7 @@ function renderPanel() {{
|
|||||||
? `<div class="port-label" title="${{esc(pi.label + (pi.alias ? ' / ' + pi.alias : ''))}}">${{labelParts.join('<br>')}}</div>`
|
? `<div class="port-label" title="${{esc(pi.label + (pi.alias ? ' / ' + pi.alias : ''))}}">${{labelParts.join('<br>')}}</div>`
|
||||||
: '';
|
: '';
|
||||||
slots += `<div class="sfp-slot-group">
|
slots += `<div class="sfp-slot-group">
|
||||||
<div class="sfp-slot ${{state}}" data-sfp="${{i}}" onclick="selectSfp(${{i}})">
|
<div class="sfp-slot ${{state}}" data-sfp="${{idx}}" onclick="selectSfp(${{idx}})">
|
||||||
${{icon}}<span class="slot-label">${{esc(slotName)}}</span>
|
${{icon}}<span class="slot-label">${{esc(slotName)}}</span>
|
||||||
<span style="font-size:0.5rem;max-width:52px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${{esc(detail)}}</span>
|
<span style="font-size:0.5rem;max-width:52px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${{esc(detail)}}</span>
|
||||||
</div>
|
</div>
|
||||||
@ -1187,7 +1204,7 @@ function renderPanel() {{
|
|||||||
}}
|
}}
|
||||||
|
|
||||||
// Management port
|
// Management port
|
||||||
const mgmtIf = ifaces['5'];
|
const mgmtIf = mgmtPort ? ifaces[mgmtPort] : null;
|
||||||
const mgmtUp = mgmtIf && isUp(mgmtIf.ifOperStatus);
|
const mgmtUp = mgmtIf && isUp(mgmtIf.ifOperStatus);
|
||||||
const mgmtSlot = `<div class="mgmt-port ${{mgmtUp ? 'link-up' : 'link-down'}}">
|
const mgmtSlot = `<div class="mgmt-port ${{mgmtUp ? 'link-up' : 'link-down'}}">
|
||||||
<i class="bi bi-ethernet"></i><span style="font-size:0.55rem">MGMT</span>
|
<i class="bi bi-ethernet"></i><span style="font-size:0.55rem">MGMT</span>
|
||||||
@ -1311,20 +1328,20 @@ function renderSfp() {{
|
|||||||
const sfpDiag = DATA.sfp_diagnostics || {{}};
|
const sfpDiag = DATA.sfp_diagnostics || {{}};
|
||||||
const sfpThresh = DATA.sfp_thresholds || {{}};
|
const sfpThresh = DATA.sfp_thresholds || {{}};
|
||||||
|
|
||||||
|
const {{ dataPorts }} = getPortLists();
|
||||||
let cards = '';
|
let cards = '';
|
||||||
for (let i = 1; i <= 4; i++) {{
|
for (const si of dataPorts) {{
|
||||||
const si = String(i);
|
|
||||||
const info = sfpInfo[si];
|
const info = sfpInfo[si];
|
||||||
const diag = sfpDiag[si] || {{}};
|
const diag = sfpDiag[si] || {{}};
|
||||||
const thresh = sfpThresh[si] || {{}};
|
const thresh = sfpThresh[si] || {{}};
|
||||||
const conn = connectors[si] || {{}};
|
const conn = connectors[si] || {{}};
|
||||||
|
|
||||||
if (!info || info.present !== '1') {{
|
if (!info || info.present !== '1') {{
|
||||||
cards += `<div class="sfp-detail" id="sfp-detail-${{i}}">
|
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">
|
||||||
<i class="bi bi-slash-circle" style="font-size:2rem"></i>
|
<i class="bi bi-slash-circle" style="font-size:2rem"></i>
|
||||||
<div>SFP-${{i}} — Not Present</div>
|
<div>${{esc(conn.name || 'Port ' + si)}} — Not Present</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
@ -1365,10 +1382,10 @@ function renderSfp() {{
|
|||||||
const wl = info.wavelength && info.wavelength !== '0' ? info.wavelength + ' nm' : 'N/A (copper)';
|
const wl = info.wavelength && info.wavelength !== '0' ? info.wavelength + ' nm' : 'N/A (copper)';
|
||||||
const mfgDate = [info.mfgYear, String(info.mfgMonth||'').padStart(2,'0'), String(info.mfgDay||'').padStart(2,'0')].join('-');
|
const mfgDate = [info.mfgYear, String(info.mfgMonth||'').padStart(2,'0'), String(info.mfgDay||'').padStart(2,'0')].join('-');
|
||||||
|
|
||||||
cards += `<div class="sfp-detail ${{i===1?'active':''}}" id="sfp-detail-${{i}}">
|
cards += `<div class="sfp-detail ${{si===dataPorts[0]?'active':''}}" id="sfp-detail-${{si}}">
|
||||||
<div class="card-dark" style="border-left:3px solid var(--accent)">
|
<div class="card-dark" style="border-left:3px solid var(--accent)">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<i class="bi bi-lightning-charge"></i> SFP-${{i}}: ${{esc(info.vendor)}} ${{esc(info.vendorPn)}}
|
<i class="bi bi-lightning-charge"></i> ${{esc(conn.name || 'Port ' + si)}}: ${{esc(info.vendor)}} ${{esc(info.vendorPn)}}
|
||||||
<span class="ms-auto">${{ddmBadge}}</span>
|
<span class="ms-auto">${{ddmBadge}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@ -1409,7 +1426,7 @@ function renderSfp() {{
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div style="font-size:0.78rem;color:var(--text-muted);margin-bottom:0.5rem">
|
<div style="font-size:0.78rem;color:var(--text-muted);margin-bottom:0.5rem">
|
||||||
Click an SFP slot in the front panel above, or select below:
|
Click an SFP slot in the front panel above, or select below:
|
||||||
${{[1,2,3,4].map(i => `<button class="btn btn-sm btn-outline-secondary ms-1" onclick="selectSfp(${{i}})">SFP-${{i}}</button>`).join('')}}
|
${{dataPorts.map(k => `<button class="btn btn-sm btn-outline-secondary ms-1" onclick="selectSfp(${{k}})">${{esc((connectors[k]||{{}}).name || 'Port '+k)}}</button>`).join('')}}
|
||||||
</div>
|
</div>
|
||||||
${{cards}}
|
${{cards}}
|
||||||
</div>
|
</div>
|
||||||
@ -1818,23 +1835,23 @@ function renderLldp() {{
|
|||||||
</div>`;
|
</div>`;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
// Build vertical columns for ports 1-4
|
// Build vertical columns for data ports
|
||||||
|
const {{ dataPorts, mgmtPort }} = getPortLists();
|
||||||
let colsHtml = '';
|
let colsHtml = '';
|
||||||
for (let i = 1; i <= 4; i++) {{
|
for (const portKey of dataPorts) {{
|
||||||
const portKey = String(i);
|
|
||||||
const conn = connectors[portKey] || {{}};
|
const conn = connectors[portKey] || {{}};
|
||||||
const isSfp = conn.type === '14';
|
const isSfp = conn.type === '14';
|
||||||
const nbr = neighborByPort[portKey];
|
const nbr = neighborByPort[portKey];
|
||||||
const state = slotState(portKey);
|
const state = slotState(portKey);
|
||||||
const iface = ifaces[portKey] || {{}};
|
const iface = ifaces[portKey] || {{}};
|
||||||
const localUp = isUp(iface.ifOperStatus);
|
const localUp = isUp(iface.ifOperStatus);
|
||||||
const slotName = conn.name || (isSfp ? `SFP-${{i}}` : `RJ45-${{i}}`);
|
const slotName = conn.name || (isSfp ? `SFP-${{portKey}}` : `RJ45-${{portKey}}`);
|
||||||
const icon = isSfp
|
const icon = isSfp
|
||||||
? (state === 'empty' ? '<i class="bi bi-dash"></i>' :
|
? (state === 'empty' ? '<i class="bi bi-dash"></i>' :
|
||||||
state === 'present-link' ? '<i class="bi bi-arrow-left-right"></i>' :
|
state === 'present-link' ? '<i class="bi bi-arrow-left-right"></i>' :
|
||||||
'<i class="bi bi-plug"></i>')
|
'<i class="bi bi-plug"></i>')
|
||||||
: '<i class="bi bi-ethernet"></i>';
|
: '<i class="bi bi-ethernet"></i>';
|
||||||
const portLabel = iface.ifName || 'Port ' + i;
|
const portLabel = iface.ifName || 'Port ' + portKey;
|
||||||
|
|
||||||
if (nbr) {{
|
if (nbr) {{
|
||||||
const linkClass = localUp ? 'up' : 'down';
|
const linkClass = localUp ? 'up' : 'down';
|
||||||
@ -1861,10 +1878,10 @@ function renderLldp() {{
|
|||||||
}}
|
}}
|
||||||
}}
|
}}
|
||||||
|
|
||||||
// MGMT column (port 5)
|
// MGMT column
|
||||||
const mgmtNbr = neighborByPort['5'];
|
const mgmtNbr = mgmtPort ? neighborByPort[mgmtPort] : null;
|
||||||
if (mgmtNbr) {{
|
if (mgmtNbr) {{
|
||||||
const mgmtIface = ifaces['5'] || {{}};
|
const mgmtIface = ifaces[mgmtPort] || {{}};
|
||||||
const mgmtUp = isUp(mgmtIface.ifOperStatus);
|
const mgmtUp = isUp(mgmtIface.ifOperStatus);
|
||||||
colsHtml += `<div class="lldp-col-divider"></div>`;
|
colsHtml += `<div class="lldp-col-divider"></div>`;
|
||||||
colsHtml += `
|
colsHtml += `
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user