NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Fix MM1000 Node Info // @namespace http://tampermonkey.net/ // @version 0.5 // @description Fix the Motorola MM1000's broken Node Info page // @author N.Lewis // @match http://192.168.*.*/nodes.html // @match http://172.*.*.*/nodes.html // @match http:/10.*.*.*/nodes.html // @include /^http://192\.168\.\d+.\d+/nodes.html/ // @include /^http://172\.(16|17|18|19|2\d|30|31)\.\d+\.\d+/nodes.html/ // @include /^http://10\.\d+\.\d+\.\d+/nodes.html/ // @copyright 2021, N.Lewis (https://openuserjs.org/users/Saturn49/) // @run-at document-end // @license MIT // @grant none // ==/UserScript== // Caches responses to urls. var response_cache_ = {}; // A mapping of url to a list of functions waiting for a response. var response_queue_ = {}; function makeSetFunction(cell) { return function(text){cell.innerHTML = text;}; } function handleBitloading(bitloading, ctx, color) { if(bitloading !== null) { ctx.beginPath(); ctx.strokeStyle = "#C0C0C0"; ctx.lineWidth=1; for (var j=0;j<=10;j++) { ctx.moveTo(0, 110-j*10); ctx.lineTo(255, 110-j*10); } ctx.stroke(); ctx.beginPath(); ctx.strokeStyle = color; var width = 256/bitloading.length; for (j=0;j<bitloading.length;j++) { if (bitloading.charAt(j) == 'a') { ctx.arc(j*width,10,1,0,2*Math.PI); } else { ctx.arc(j*width,110-bitloading.charAt(j)*10,1,0,2*Math.PI); } } ctx.stroke(); } } function makeFinishFunction(row, nodeid, bonded, self, profiles) { return function(moca_version) { var cell; if (self) { cell = row.insertCell(-1); getfield_mac_async("--gen_node_status&"+nodeid, "eui", makeSetFunction(cell)); cell = row.insertCell(-1); cell.innerHTML = "Current Node"; cell.colSpan = "5"; } else { var rx_profile = 7; var tx_profile = 13; var rx_profile_second = 18; var tx_profile_second = 20; var rx_p = findProfile(profiles, nodeid, "RX Unicast NPER 100 MHz"); var tx_p = findProfile(profiles, nodeid, "TX Unicast NPER 100 MHz"); var rx_p2 = findProfile(profiles, nodeid, "RX Unicast NPER Sec-Ch 100 MHz"); var tx_p2 = findProfile(profiles, nodeid, "TX Unicast NPER Sec-Ch 100 MHz"); if(moca_version < 0x20) { rx_profile = 0; tx_profile = 3; rx_p = findProfile(profiles, nodeid, "RX Unicast 50 MHz"); tx_p = findProfile(profiles, nodeid, "TX Unicast 50 MHz"); } cell = row.insertCell(-1); cell.innerHTML = rx_p.mac_address; cell = row.insertCell(-1); getfield_num_async("--gen_node_ext_status&index&"+nodeid+"&profile_type&"+rx_profile, "avg_snr", makeSetFunction(cell)); cell = row.insertCell(-1); var span = document.createElement("SPAN"); cell.appendChild(span); span.innerHTML = rx_p.phy_rate; if(bonded) { span = document.createElement("SPAN"); cell.appendChild(span); span.innerHTML = " / "; span = document.createElement("SPAN"); cell.appendChild(span); span.innerHTML = rx_p2.phy_rate; } cell = row.insertCell(-1); span = document.createElement("SPAN"); cell.appendChild(span); span.innerHTML = tx_p.phy_rate; if(bonded) { span = document.createElement("SPAN"); cell.appendChild(span); span.innerHTML = " / "; span = document.createElement("SPAN"); cell.appendChild(span); span.innerHTML = tx_p2.phy_rate; } cell = row.insertCell(-1); getfield_num_async("--gen_node_ext_status&index&"+nodeid+"&profile_type&"+rx_profile, "rx_power", makeSetFunction(cell)); cell = row.insertCell(-1); getfield_num_async("--gen_node_ext_status&index&"+nodeid+"&profile_type&"+tx_profile, "tx_power", makeSetFunction(cell)); let per_cell = row.insertCell(-1); let num_numerator = 4; let num_denominator = 1; let numerator = 0; let denominator = 0; function maybeSetVal() { if(num_numerator === 0 && num_denominator === 0) { if(denominator === 0) { per_cell.innerHTML = "0"; } var per = Number(numerator) / Number(denominator); per_cell.innerHTML = per.toExponential(); } } function setNumerator(val){ num_numerator--; numerator += val; maybeSetVal(); } function setDenominator(val){ num_denominator--; denominator += val; maybeSetVal(); } getfield_num_async("--node_stats&index&"+nodeid, "rx_packets", setDenominator); getfield_num_async("--node_stats_ext&index&"+nodeid, "rx_bc_crc_error", setNumerator); getfield_num_async("--node_stats_ext&index&"+nodeid, "rx_uc_crc_error", setNumerator); getfield_num_async("--node_stats_ext&index&"+nodeid, "rx_uc_timeout_error", setNumerator); getfield_num_async("--node_stats_ext&index&"+nodeid, "rx_bc_timeout_error", setNumerator); cell = row.insertCell(-1); cell.align = "center"; cell.innerHTML = "<canvas id=\"bitloading"+nodeid+"\" width=\"256\" height=\"110\"" + "style=\"border:1px solid #000000;\" >" + "</canvas><br>"; span = document.createElement("SPAN"); cell.appendChild(span); var span2 = document.createElement("SPAN"); cell.appendChild(span2); span2.innerHTML = "Mhz"; var c = document.getElementById("bitloading"+nodeid); let ctx = c.getContext("2d"); getfield_num_async("--gen_node_ext_status&index&"+nodeid+"&profile_type&"+rx_profile, "central_freq", makeSetFunction(span)); handleBitloading(rx_p.bitloading, ctx, "rgba(255, 0, 0, 0.5)"); handleBitloading(tx_p.bitloading, ctx, "rgba(0, 0, 255, 0.5)"); cell = row.insertCell(-1); cell.align = "center"; if(bonded) { cell.innerHTML = "<canvas id=\"bitloading2"+nodeid+"\" width=\"256\" height=\"110\"" + "style=\"border:1px solid #000000;\" >" + "</canvas><br>"; span = document.createElement("SPAN"); cell.appendChild(span); span2 = document.createElement("SPAN"); cell.appendChild(span2); span2.innerHTML = "Mhz"; c = document.getElementById("bitloading2"+nodeid); let ctx2 = c.getContext("2d"); getfield_num_async("--gen_node_ext_status&index&"+nodeid+"&profile_type&"+rx_profile_second, "central_freq", makeSetFunction(span)); handleBitloading(rx_p2.bitloading, ctx2, "rgba(255, 0, 0, 0.5)"); handleBitloading(tx_p2.bitloading, ctx2, "rgba(0, 0, 255, 0.5)"); } } }; } function fillfield2() { var tbl = document.getElementById('nodeinfo').tBodies[0]; var hd_cell = tbl.rows[0].insertCell(5); hd_cell.className = "hd"; hd_cell.align = "center"; hd_cell.innerHTML = "TX Phy Rate<br>Mbps"; var bl_cell = tbl.rows[0].cells[9]; bl_cell.innerHTML += " (<span style=\"color:red\">RX</span>/<span style=\"color:blue\">TX</span>)"; var bl2_cell = tbl.rows[0].insertCell(-1); bl2_cell.className = "hd"; bl2_cell.align = "center"; bl2_cell.innerHTML = "Bit Loading 2nd (<span style=\"color:red\">RX</span>/<span style=\"color:blue\">TX</span>)"; var nodemask = getfield_num2("--net", "nodes_usable_bitmask"); var nc = getfield_num2("--net", "nc_node_id"); var nodeid = getfield_num2("--net", "node_id"); var bonded_nodes_bitmask = getfield_num2("--net", "bonded_nodes_bitmask"); var i = 0; var table = document.getElementById("nodeinfo"); var profiles = parseGCAP39( getUrl("/cmd.sh?GCAP.39")); nodemask |= (1<<nodeid); while (nodemask > 0) { if (nodemask & 1 !== 0) { let row = table.insertRow(); var cell = row.insertCell(-1); if (i == nc) { cell.innerHTML = "*"; } cell = row.insertCell(-1); cell.innerHTML = i; var bonded = (((1 << i) & bonded_nodes_bitmask) != 0); var self = (i == nodeid); getfield_num_async("--gen_node_status&"+i, "active_moca_version", makeFinishFunction(row, i, bonded, self, profiles)); } nodemask = nodemask >> 1; i++; } } function getUrl(url) { if(url in response_cache_) { return response_cache_[url]; } var xmlhttp; if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari xmlhttp = new XMLHttpRequest(); } else { // code for IE6, IE5 xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } xmlhttp.open("GET", url, false); xmlhttp.send(); var str = xmlhttp.responseText; response_cache_[url] = str; return response_cache_[url]; } function getUrlAsync(url, fn) { if(url in response_queue_){ response_queue_[url].push(fn); return; } if(url in response_cache_) { fn(response_cache_[url]); return; } response_queue_[url] = []; response_queue_[url].push(fn); var xmlhttp; if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari xmlhttp = new XMLHttpRequest(); } else { // code for IE6, IE5 xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } xmlhttp.open("GET", url, true); xmlhttp.onload = function(e) { response_cache_[url] = xmlhttp.responseText; while(response_queue_[url].length > 0) { response_queue_[url].pop()(xmlhttp.responseText); } delete response_queue_[url]; }; xmlhttp.send(); } function getfield_num2(cmdname, field) { var xmlttp; var str; var url = new String("/cmd.sh?mocap&get&"+cmdname); str = getUrl(url); var re = new RegExp (field+"\\s*:\\s*(-?\\d*)", "g"); var result = re.exec(str); if(result == null) { return ""; } return parseInt(result[1]); } function matchOrDefault(regex, input, group, def){ var result = regex.exec(input); if(result == null) { return def; } return result[group]; } function findProfile(profiles, node, name) { for(var i=0; i<profiles.length; ++i){ var profile = profiles[i]; if(profile.nodeId == node && profile.name == name) { return profile; } } return null; } function parseGCAP39(response) { var results = []; var profiles = response.split("\n\n"); profiles.forEach(function(profile){ var result = {}; result.name = matchOrDefault(/(TX|RX) (Broadcast|Unicast).*/, profile, 0, ""); result.nodeId = parseInt(matchOrDefault(/NodeID *: *(\d+)/, profile, 1, "-1")); result.mac_address = matchOrDefault(/MAC Address *: *(..:..:..:..:..:..)/, profile, 1, ""); result.phy_rate = parseInt(matchOrDefault(/PHY Rate *: *(\d+)/, profile, 1, "-1")); result.nbas = parseInt(matchOrDefault(/NBAS *: *(\d)/, profile, 1, "-1")); result.cp = parseInt(matchOrDefault(/CP *: *(\d)/, profile, 1, "-1")); result.tx_power_adjust = parseInt(matchOrDefault(/CP *: *(\d)/, profile, 1, "-1")); var bl = new RegExp ("Subcarriers\\n*(\\n\\s*(\\d+)\\s*-\\s*(\\d+)\\s*:\\s*(.*))+", "gm"); var bl_split = new RegExp("(\\d+)\\s*-\\s*(\\d+)\\s*:\\s*(.*)", "gm"); var m = bl.exec(profile); if(m == null) { result.bitloading = null; } else { var all = m[0]; var out = ""; var vals = []; while((m = bl_split.exec(all)) !== null) { var left = parseInt(m[1]); var right = parseInt(m[2]); if(left < right) { vals.push( { start: left, val: m[3] }); } else { vals.push( { start: right, val: m[3].split("").reverse().join("") }); } } vals.sort(function(a, b) { return a.start - b.start; }); vals.forEach(function(a) { out += a.val; }); result.bitloading = out; } results.push(result); }); return results; } function getfield_mac_async(cmdname, field, fn) { var url = new String("/cmd.sh?mocap&get&"+cmdname); function handleResult(str) { var re = new RegExp (field+"\\s*:\\s*(..:..:..:..:..:..)", "g"); var result = re.exec(str); if(result == null) { fn(""); return; } fn(result[1]); }; getUrlAsync(url, handleResult); } function getfield_num_async(cmdname, field, fn) { var url = new String("/cmd.sh?mocap&get&"+cmdname); var re = new RegExp (field+"\\s*:\\s*(-?\\d*)", "g"); function handleResult(str) { var result = re.exec(str); if(result == null) { fn(""); return; } fn(parseInt(result[1])); }; getUrlAsync(url, handleResult); } function getfield_bitloading_async(cmdname,field, ctx, color) { var xmlttp; var str; var url = new String("/cmd.sh?mocap&get&"+cmdname); var re = new RegExp (field+":\\n*(\\s*(\\d+)\\s*-\\s*(\\d+)\\s*:\\s*(.*)\\n)+", "gm"); var re_split = new RegExp("(\\d+)\\s*-\\s*(\\d+)\\s*:\\s*(.*)", "gm"); function handleResult(str) { var m = re.exec(str); if(m == null) { handleBitloading(null, ctx); return; } var all = m[0]; var out = ""; var vals = []; while((m = re_split.exec(all)) !== null) { var left = parseInt(m[1]); var right = parseInt(m[2]); if(left < right) { vals.push( { start: left, val: m[3] }); } else { vals.push( { start: right, val: m[3].split("").reverse().join("") }); } } vals.sort(function(a, b) { return a.start - b.start; }); vals.forEach(function(a) { out += a.val; }); handleBitloading(out, ctx, color); }; getUrlAsync(url, handleResult); } (function() { 'use strict'; window.onload = function go(){ fillfield2(); } // Your code here... })();