mirror of
https://github.com/logicog/RTLPlayground.git
synced 2026-08-30 14:52:51 +08:00
gui: Spanning Tree page (config + live status)
Add a Spanning Tree page: an on/off toggle driving the existing "stp" command over /cmd, and a live status section fed by a new /stp.json endpoint - the elected root bridge (priority + MAC), our path cost, whether we are the root, and the per-port STP state read live from the ASIC's MSTP register (same 2-bit encoding stp_setup() writes). Ports are reported by their physical numbers. Recovered-from: 3132319, 9365c86
This commit is contained in:
@@ -4,6 +4,7 @@ document.getElementById('sidebar').innerHTML =
|
|||||||
+ "<li><a href='stat.html' data-i18n='nav_port_stat'>Port Statistics</a></li>"
|
+ "<li><a href='stat.html' data-i18n='nav_port_stat'>Port Statistics</a></li>"
|
||||||
+ "<li><a href='vlan.html' >VLAN</a></li>"
|
+ "<li><a href='vlan.html' >VLAN</a></li>"
|
||||||
+ "<li><a href='l2.html' data-i18n='nav_l2'>L2 Configuration</a></li>"
|
+ "<li><a href='l2.html' data-i18n='nav_l2'>L2 Configuration</a></li>"
|
||||||
|
+ "<li><a href='stp.html'>Spanning Tree</a></li>"
|
||||||
+ "<li><a href='mirror.html' data-i18n='nav_mirror'>Mirroring</a></li>"
|
+ "<li><a href='mirror.html' data-i18n='nav_mirror'>Mirroring</a></li>"
|
||||||
+ "<li><a href='lag.html' data-i18n='nav_lag'>Link Aggregation</a></li>"
|
+ "<li><a href='lag.html' data-i18n='nav_lag'>Link Aggregation</a></li>"
|
||||||
+ "<li><a href='eee.html' data-i18n='nav_eee'>EEE</a></li>"
|
+ "<li><a href='eee.html' data-i18n='nav_eee'>EEE</a></li>"
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<script src="/main.js"></script>
|
||||||
|
<script src="/i18n.js"></script>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<title>FreeSwitchOS Spanning Tree</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav id="sidebar"></nav>
|
||||||
|
<div style="margin-left:16%;padding:1px 16px;height:1000px;">
|
||||||
|
<div id="ports"></div>
|
||||||
|
<h1>Spanning Tree (RSTP)</h1>
|
||||||
|
<h2>Mode: <select id="stpMode"><option value="off">Disabled</option><option value="on">Enabled</option></select>
|
||||||
|
<input style="width:15%;margin-left: 3em;" class="action" id="stp_sub" onclick="stpSub();" type="button" value="Apply"></h2>
|
||||||
|
<div id="stpStat" style="font-family:monospace;"></div>
|
||||||
|
<div id="stpPorts" style="font-family:monospace;white-space:pre;"></div>
|
||||||
|
<script src="/stp.js"></script>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
<script src="/navigation.js"></script>
|
||||||
|
</html>
|
||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
/* ---- Spanning Tree (RSTP) section ---- */
|
||||||
|
|
||||||
|
// STP port states as encoded in the ASIC's MSTP register (2 bits per port)
|
||||||
|
const STP_STATES = ["Disabled", "Blocking", "Learning", "Forwarding"];
|
||||||
|
|
||||||
|
// "user is editing" flag: while set, the periodic refresh must not overwrite
|
||||||
|
// the mode dropdown (same pattern as the LAG page - without it the 2 s refresh
|
||||||
|
// silently reverts the user's choice before Apply).
|
||||||
|
var stpDirty = false;
|
||||||
|
|
||||||
|
function fetchStp() {
|
||||||
|
var xhttp = new XMLHttpRequest();
|
||||||
|
xhttp.onreadystatechange = function() {
|
||||||
|
if (this.readyState == 4 && this.status == 200) {
|
||||||
|
const s = JSON.parse(xhttp.responseText);
|
||||||
|
// textContent throughout: rootMac comes from received BPDUs
|
||||||
|
// (remote-controlled), never render it as HTML
|
||||||
|
if (!stpDirty)
|
||||||
|
document.getElementById("stpMode").value = s.on ? "on" : "off";
|
||||||
|
document.getElementById("stpStat").textContent = s.on
|
||||||
|
? (s.weRoot
|
||||||
|
? "This switch is the root bridge (priority 0x" + s.rootPrio + ")"
|
||||||
|
: "Root bridge: 0x" + s.rootPrio + " / " + s.rootMac
|
||||||
|
+ " \u2014 path cost: 0x" + s.cost)
|
||||||
|
: "";
|
||||||
|
let t = "";
|
||||||
|
if (s.on) {
|
||||||
|
t = "port state\n";
|
||||||
|
for (const p of s.ports)
|
||||||
|
t += String(p.p).padEnd(6) + STP_STATES[p.st] + "\n";
|
||||||
|
}
|
||||||
|
document.getElementById("stpPorts").textContent = t;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhttp.open("GET", `/stp.json`, true);
|
||||||
|
sendXHTTP(xhttp);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function stpSub() {
|
||||||
|
const on = document.getElementById("stpMode").value === "on";
|
||||||
|
try {
|
||||||
|
await fetch('/cmd', { method: 'POST', body: on ? "stp on" : "stp off" });
|
||||||
|
} catch(err) {
|
||||||
|
console.error(`Error: ${err}`);
|
||||||
|
}
|
||||||
|
stpDirty = false; // editing done - let the refresh show the truth
|
||||||
|
fetchStp();
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("load", function() {
|
||||||
|
document.getElementById("stpMode")
|
||||||
|
.addEventListener("change", () => { stpDirty = true; });
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("load", function() {
|
||||||
|
update( () => {
|
||||||
|
fetchStp();
|
||||||
|
const interval = setInterval(update, 2000);
|
||||||
|
const stpInt = setInterval(fetchStp, 2000);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -680,6 +680,8 @@ void httpd_appcall(void)
|
|||||||
send_mtu();
|
send_mtu();
|
||||||
} else if (is_word(q, "/lag.json")) {
|
} else if (is_word(q, "/lag.json")) {
|
||||||
send_lag();
|
send_lag();
|
||||||
|
} else if (is_word(q, "/stp.json")) {
|
||||||
|
send_stp();
|
||||||
} else if (is_word(q, "/vlanlist")) {
|
} else if (is_word(q, "/vlanlist")) {
|
||||||
send_vlanlist();
|
send_vlanlist();
|
||||||
} else if (is_word(q, "/config")) {
|
} else if (is_word(q, "/config")) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "phy.h"
|
#include "phy.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "machine.h"
|
#include "machine.h"
|
||||||
|
#include "rtl837x_stp.h"
|
||||||
#include "page_impl.h"
|
#include "page_impl.h"
|
||||||
#include "syslog.h"
|
#include "syslog.h"
|
||||||
|
|
||||||
@@ -527,6 +528,56 @@ void send_lag(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* STP status for the L2 page ("/stp.json"): enable state, the elected root
|
||||||
|
* bridge (priority+MAC) and our path cost to it, whether we are the root, and
|
||||||
|
* the live per-port STP state read from the ASIC's MSTP register (2 bits per
|
||||||
|
* port: 0 disable, 1 blocking, 2 learning, 3 forwarding - same encoding
|
||||||
|
* stp_setup() writes). Ports are reported by their physical number. */
|
||||||
|
/* Scratch for send_stp(): a plain local would land in the near-full 8051
|
||||||
|
* internal-RAM overlay (OSEG). */
|
||||||
|
__xdata uint8_t stp_we_root;
|
||||||
|
|
||||||
|
void send_stp(void)
|
||||||
|
{
|
||||||
|
dbg_string("send_stp called\n");
|
||||||
|
slen = strtox(outbuf, HTTP_RESPONCE_JSON);
|
||||||
|
|
||||||
|
slen += strtox(outbuf + slen, "{\"on\":");
|
||||||
|
bool_to_html(stpEnabled);
|
||||||
|
slen += strtox(outbuf + slen, ",\"rootPrio\":\"");
|
||||||
|
byte_to_html(root_bridge.prio);
|
||||||
|
byte_to_html(root_bridge.ext);
|
||||||
|
slen += strtox(outbuf + slen, "\",\"rootMac\":\"");
|
||||||
|
for (uint8_t j = 0; j < 6; j++)
|
||||||
|
byte_to_html(root_bridge.mac[j]);
|
||||||
|
slen += strtox(outbuf + slen, "\",\"cost\":\"");
|
||||||
|
byte_to_html(root_bridge_cost >> 24);
|
||||||
|
byte_to_html(root_bridge_cost >> 16);
|
||||||
|
byte_to_html(root_bridge_cost >> 8);
|
||||||
|
byte_to_html(root_bridge_cost);
|
||||||
|
/* are we the elected root? (our MAC == root MAC) */
|
||||||
|
stp_we_root = 1;
|
||||||
|
for (uint8_t j = 0; j < 6; j++) {
|
||||||
|
if (root_bridge.mac[j] != uip_ethaddr.addr[j])
|
||||||
|
stp_we_root = 0;
|
||||||
|
}
|
||||||
|
slen += strtox(outbuf + slen, "\",\"weRoot\":");
|
||||||
|
bool_to_html(stp_we_root);
|
||||||
|
slen += strtox(outbuf + slen, ",\"ports\":[");
|
||||||
|
reg_read_m(RTL837X_MSTP_STATES);
|
||||||
|
for (uint8_t i = machine.min_port; i <= machine.max_port; i++) {
|
||||||
|
slen += strtox(outbuf + slen, "{\"p\":");
|
||||||
|
itoa_html(machine.log_to_phys_port[i]);
|
||||||
|
slen += strtox(outbuf + slen, ",\"st\":");
|
||||||
|
/* 2-bit field per logical port, packed from byte 3 up (cf. stp_setup) */
|
||||||
|
itoa_html((sfr_data[3 - (i >> 2)] >> ((i << 1) & 0x7)) & 0x3);
|
||||||
|
slen += strtox(outbuf + slen, "},");
|
||||||
|
}
|
||||||
|
slen -= 1; // remove comma
|
||||||
|
slen += strtox(outbuf + slen, "]}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void send_eee(void)
|
void send_eee(void)
|
||||||
{
|
{
|
||||||
dbg_string("send_eee called\nsending EEE status\n");
|
dbg_string("send_eee called\nsending EEE status\n");
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ void send_mtu(void);
|
|||||||
void send_config(void);
|
void send_config(void);
|
||||||
void send_cmd_log(void);
|
void send_cmd_log(void);
|
||||||
void send_lag(void);
|
void send_lag(void);
|
||||||
|
void send_stp(void);
|
||||||
void send_vlanlist(void);
|
void send_vlanlist(void);
|
||||||
|
|
||||||
/* Convert only the lower nibble to ascii HEX char.
|
/* Convert only the lower nibble to ascii HEX char.
|
||||||
|
|||||||
Reference in New Issue
Block a user