Update the Javascript to include support for the new features; range widget, select widget, individual hex/dec display & copy section.
//
// File .......... uptime.js
// Author ........ Steve Haywood
// Website ....... http://www.spacewire.co.uk
// Project ....... SpaceWire UK Tutorial
// Version ....... 1.6
// Conception .... 17 January 2022
// Standard ...... ECMA-262
// Description ...
// Javascript functions for the dynamic webpage.
//
// Constants
const c_uns = 0; // Unsigned string
const c_hex = 1; // Hexadecimal string
// Requests
var timer_uptime;
var timer_peek_all;
// Load PL Firmware
function loadfirmware(filename) {
var url = "/cgi-bin/loadfirmware?" + filename;
if (window.XMLHttpRequest) {
var ajaxReq = new XMLHttpRequest();
ajaxReq.onreadystatechange = function() {
if (ajaxReq.readyState == 4 && ajaxReq.status == 200) {
var respText = ajaxReq.responseText;
var img_obj = document.getElementById("fm_" + filename);
// Unique number is added to image to avoid caching issues on separate animations
const now = Date.now();
if (respText.substr(0,6) == "Error:") {
img_obj.src = "../red.png?" + now;
img_obj.title = "Last loadfirmware failed : " + respText.substr(7);
} else {
img_obj.src = "../green.gif?" + now;
img_obj.title = "Last loadfirmware successful";
}
}
}
ajaxReq.open("POST", url, true);
ajaxReq.send(null);
}
}
// Download OS information file & display result
async function read_os_ids() {
let response = await fetch("/project.txt");
if (response.status == 200) {
let ids = await response.text();
fields = ids.split(/\r?\n/);
}
for (var i = 0; i < 6; i++) {
const now = Date.now();
const txt_obj = document.getElementById("oid_" + i);
const img_obj = document.getElementById("osid_" + i);
if (response.status == 200) {
if (i < fields.length && fields[i] != "") {
img_obj.src = "../green.gif?" + now;
img_obj.title = "Last file fetch successful";
txt_obj.innerHTML = fields[i];
} else {
img_obj.src = "../red.gif?" + now;
img_obj.title = "Missing field information";
txt_obj.innerHTML = "Unknown";
}
} else {
img_obj.src = "../red.gif?" + now;
img_obj.title = "Last file fetch failed";
txt_obj.innerHTML = "Unknown";
}
}
}
// Peek all strings
function read_ids() {
read_id(0x000, 0);
read_id(0x080, 1);
read_id(0x0C0, 2);
read_id(0x100, 3);
read_id(0x120, 4);
read_id(0x140, 5);
}
// Peek string & display result
function read_id(offset, reg) {
var url = "/cgi-bin/peekstring?0x40000000&4096&" + offset + "&128";
if (window.XMLHttpRequest) {
var ajaxReq = new XMLHttpRequest();
ajaxReq.onreadystatechange = function() {
if (ajaxReq.readyState == 4 && ajaxReq.status == 200) {
var respText = ajaxReq.responseText;
var img_obj = document.getElementById("sid_" + reg);
// Unique number is added to image to avoid caching issues on separate animations
const now = Date.now();
if (respText.substr(0,6) == "Error:") {
img_obj.src = "../red.png?" + now;
img_obj.title = "Last peekstring failed : " + respText.substr(7);
} else {
const now = Date.now();
img_obj.src = "../green.gif?" + now;
img_obj.title = "Last peekstring successful";
document.getElementById("id_" + reg).innerHTML = respText;
}
}
}
ajaxReq.open("POST", url, true);
ajaxReq.send(null);
}
}
// Get uptime
function get_uptime(reg) {
var url = "/cgi-bin/uptime.cgi";
if (window.XMLHttpRequest) {
var ajaxReq = new XMLHttpRequest();
ajaxReq.onreadystatechange = function() {
if (ajaxReq.readyState == 4 && ajaxReq.status == 200) {
var respText = ajaxReq.responseText;
txtObj = document.getElementById("uptime_text");
if (txtObj) {
txtObj.innerHTML = respText;
}
}
}
ajaxReq.open("POST", url, true);
ajaxReq.send(null);
}
}
// Update uptime timer
function uptime() {
clearInterval(timer_uptime);
var uptime = document.getElementById("uptime");
var interval = uptime.value;
if (interval > 0) {
timer_uptime = setInterval("get_uptime()", 1000 * interval);
}
}
// Update peek_all timer
function timer() {
clearInterval(timer_peek_all);
var timer = document.getElementById("timer");
interval = timer.value;
if (interval > 0) {
timer_peek_all = setInterval("peek_all()", 1000 * interval);
}
}
// Convert unsigned/hexadecimal number string into a specified number string.
function fmtUnsignedLong(string, format) {
var hexStr;
const value = parseInt(string);
switch(format) {
case c_uns:
hexStr = value.toString(10);
break;
case c_hex:
hexStr = value.toString(16).toUpperCase();
hexStr = "0x" + "00000000".substr(0, 8 - hexStr.length) + hexStr;
break;
default:
break;
}
return hexStr;
}
// Call fmtUnsignedLong with type setting from row in peek/poke address table.
function fmtUnsignedLongReg(reg, value) {
const disp_obj = document.getElementById("display_" + reg);
var hexStr = "0xNaN";
if (disp_obj) {
const format = (disp_obj.checked) ? (c_hex) : (c_uns);
hexStr = fmtUnsignedLong(value, format);
}
return hexStr;
}
// Copy peek to poke & update any associated widgets
function copy(reg) {
const peek_obj = document.getElementById("peek_" + reg);
if (peek_obj) {
const poke_obj = document.getElementById("poke_" + reg);
if (poke_obj) {
poke_obj.value = peek_obj.value;
}
const range_obj = document.getElementById("poke_range_" + reg);
if (range_obj) {
range_obj.value = parseInt(peek_obj.value);
}
const select_obj = document.getElementById("poke_select_" + reg);
if (select_obj) {
select_obj.value = peek_obj.value;
}
}
}
// Copy selected peek to poke in section
function copy_section(row) {
var obj_sel;
do {
row++;
obj_sel = document.getElementById("peek_sel_" + row);
if (obj_sel) {
if (obj_sel.checked) {
copy(row);
}
}
} while (obj_sel);
}
// Copy all peek to poke
function copy_all() {
var table = document.getElementById("registers");
var rows = table.rows.length - 1;
for (var index = 0; index < rows; index++) {
copy(index);
}
}
// Peek address & display result
function peek(reg) {
var url = "/cgi-bin/peek?" + document.getElementById("addr_" + reg).value;
if (window.XMLHttpRequest) {
var ajaxReq = new XMLHttpRequest();
ajaxReq.onreadystatechange = function() {
if (ajaxReq.readyState == 4 && ajaxReq.status == 200) {
var respText = ajaxReq.responseText;
var img_obj = document.getElementById("speek_" + reg);
// Unique number is added to image to avoid caching issues on separate animations
const now = Date.now();
if (respText.substr(0,6) == "Error:") {
img_obj.src = "../red.gif?" + now;
img_obj.title = "Last peek failed : " + respText.substr(7);
} else {
img_obj.src = "../green.gif?" + now;
img_obj.title = "Last peek successful";
document.getElementById("peek_" + reg).value = fmtUnsignedLongReg(reg, respText);
}
}
}
ajaxReq.open("POST", url, true);
ajaxReq.send(null);
}
}
// Peek all selected addresses in section
function peek_section(row) {
var obj_sel;
do {
row++;
obj_sel = document.getElementById("peek_sel_" + row);
if (obj_sel) {
if (obj_sel.checked) {
peek(row);
}
}
} while (obj_sel);
}
// Peek all selected addresses in table
function peek_all() {
var table = document.getElementById("registers");
var rows = table.rows.length - 1;
for (var index = 0; index < rows; index++) {
const obj_sel = document.getElementById("peek_sel_" + index);
if (obj_sel) {
if (obj_sel.checked) {
peek(index);
}
}
}
}
// Poke address & display result
function poke(reg) {
var url = "/cgi-bin/poke?" + document.getElementById("addr_" + reg).value + "&" + document.getElementById("poke_" + reg).value;
if (window.XMLHttpRequest) {
var ajaxReq = new XMLHttpRequest();
ajaxReq.onreadystatechange = function() {
if (ajaxReq.readyState == 4 && ajaxReq.status == 200) {
var respText = ajaxReq.responseText;
var img_obj = document.getElementById("spoke_" + reg);
// Unique number is added to image to avoid caching issues on separate animations
const now = Date.now();
if (respText.substr(0,6) == "Error:") {
img_obj.src = "../red.gif?" + now;
img_obj.title = "Last poke failed : " + respText.substr(7);
} else {
img_obj.src = "../green.gif?" + now;
img_obj.title = "Last poke successful";
}
}
}
ajaxReq.open("POST", url, true);
ajaxReq.send(null);
}
}
// Poke value from input widget & update any associated widgets
function poke_widget(reg) {
const poke_obj = document.getElementById("poke_" + reg);
if (poke_obj) {
const range_obj = document.getElementById("poke_range_" + reg);
if (range_obj) {
range_obj.value = parseInt(poke_obj.value);
}
const select_obj = document.getElementById("poke_select_" + reg);
if (select_obj) {
select_obj.value = poke_obj.value;
}
}
poke(reg);
}
// Poke value on Enter key from within input widget
// Holding down Enter keeps on poking which is not desired - needs improvement!
function poke_key(reg) {
if (event.key === 'Enter') {
poke_widget(reg);
}
}
// Update radix for peek & poke input widgets
function update_radix(reg) {
const poke_obj = document.getElementById("poke_" + reg);
if (poke_obj) {
poke_obj.value = fmtUnsignedLongReg(reg, poke_obj.value);
}
const peek_obj = document.getElementById("peek_" + reg);
if (peek_obj) {
peek_obj.value = fmtUnsignedLongReg(reg, peek_obj.value);
}
}
// Poke value from range widget & update any associated widgets
function poke_range(reg) {
const range_obj = document.getElementById("poke_range_" + reg);
if (range_obj) {
const poke_obj = document.getElementById("poke_" + reg);
if (poke_obj) {
poke_obj.value = fmtUnsignedLongReg(reg, range_obj.value);
}
}
poke(reg);
}
// Poke value from select widget & update any associated widgets
function poke_select(reg) {
const select_obj = document.getElementById("poke_select_" + reg);
if (select_obj) {
const poke_obj = document.getElementById("poke_" + reg);
if (poke_obj) {
poke_obj.value = fmtUnsignedLongReg(reg, select_obj.value);
}
}
poke(reg);
}
// Poke all selected addresses in section
function poke_section(row) {
var obj_sel;
do {
row++;
obj_sel = document.getElementById("poke_sel_" + row);
if (obj_sel) {
if (obj_sel.checked) {
poke_widget(row);
}
}
} while (obj_sel);
}
// Poke all selected addresses in table
function poke_all() {
var table = document.getElementById("registers");
var rows = table.rows.length - 1;
for (var index = 0; index < rows; index++) {
const obj_sel = document.getElementById("poke_sel_" + index);
if (obj_sel) {
if (obj_sel.checked) {
poke_widget(index);
}
}
}
}
// Add row to table
function add_row() {
const obj_type = document.getElementById("type");
const row_type = obj_type.value;
switch(row_type) {
case "0":
add_register();
break;
case "1":
add_section();
break;
default:
break;
}
}
// Add peek/poke row to table
function add_register(reg) {
const table = document.getElementById("registers");
const next = table.rows.length - 1;
var addr = 0x40010000;
if (next > 0) {
const obj_addr = document.getElementById("addr_" + (next - 1));
if (obj_addr) {
addr = parseInt(obj_addr.value) + 4;
}
}
const fields = ["reg", addr.toString(), "true", "true", "0x00000000", "true", "Register @ " + fmtUnsignedLong(addr.toString(), c_hex)];
add_row_raw(fields);
}
// Add section row to table
function add_section(reg) {
const fields = ["sec", "Section Description"];
add_row_raw(fields);
}
// Add row to table
function add_row_raw(fields) {
const table = document.getElementById("registers");
const next = table.rows.length - 1;
const row = table.insertRow(-1);
var newcell;
// Separate out fields
var type = fields[0];
if (type == "sec") {
var description = fields[1];
} else {
var address = fields[1];
var display_type = fields[2];
var peek_select = fields[3];
var options = fields[4].split("#");
var poke_select = fields[5];
var description = fields[6];
// Decode fields
var disp_checked = (display_type == "true") ? ("checked") : ("");
var format = (display_type == "true") ? (c_hex) : (c_uns);
var peek_checked = (peek_select == "true") ? ("checked") : ("");
var poke_checked = (poke_select == "true") ? ("checked") : ("");
}
newcell = row.insertCell(-1);
if (type == "sec") {
newcell.colSpan = "4";
newcell.innerHTML = "--- Section ---";
} else {
newcell.innerHTML = '<input title="Address to peek/poke" type="text" id="addr_' + next + '" value="' + fmtUnsignedLong(address, c_hex) + '" size="10">';
newcell = row.insertCell(-1);
newcell.innerHTML = '<input title="Select peak/poke display type (unsigned/hexadecimal)" type="checkbox" id="display_' + next + '" ' + disp_checked + ' onchange="update_radix(' + next + ')">';
newcell = row.insertCell(-1);
newcell.innerHTML = '<input title="Value peeked at address" type="text" id="peek_' + next + '" value="'+ fmtUnsignedLong("0x0", format) +'" size="10" readonly="readonly">';
newcell = row.insertCell(-1);
newcell.innerHTML = '<input title="Select address for peeking" type="checkbox" id="peek_sel_' + next + '" ' + peek_checked + '>';
}
newcell = row.insertCell(-1);
if (type == "sec") {
newcell.innerHTML = '<input title="Peek all selected addresses in section" type="submit" value="Peek" onclick="peek_section(' + next + ')">';
} else {
newcell.innerHTML = '<input title="Peek address" type="submit" value="Peek" onclick="peek(' + next + ')">';
}
newcell = row.insertCell(-1);
if (type == "sec") {
newcell.innerHTML = '<img style="vertical-align:middle" src="../amber.gif" alt="Missing Image!">';
} else {
newcell.innerHTML = '<img title="Peek status" id="speek_' + next + '" style="vertical-align:middle" src="../amber.gif" alt="Missing Image!">';
}
newcell = row.insertCell(-1);
if (type == "sec") {
newcell.innerHTML = '<input title="Copy all selected peek values into poke values in section" type="submit" value=">>" onclick="copy_section(' + next + ')">';
} else {
newcell.innerHTML = '<input title="Copy peek value into poke value" type="submit" value=">>" onclick="copy(' + next + ')">';
}
newcell = row.insertCell(-1);
switch (type) {
case "sec":
newcell.colSpan = "3";
newcell.innerHTML = "--- Section ---";
break;
case "reg":
const poke_value = fields[4];
newcell.innerHTML = '<input title="Value to poke at address" type="text" id="poke_' + next + '" value="'+ fmtUnsignedLong(poke_value, format) +'" size="10" onkeydown="poke_key(' + next + ')">';
newcell = row.insertCell(-1);
newcell.style.padding = "0";
break;
case "range":
var range_value = options[0];
var range_min = options[1];
var range_max = options[2];
newcell.innerHTML = '<input title="Value to poke at address" type="text" id="poke_' + next + '" value="'+ fmtUnsignedLong(range_value, format) +'" size="10" onkeydown="poke_key(' + next + ')">';
newcell = row.insertCell(-1);
newcell.innerHTML = '<input title="Value to poke at address" style="width:100%" type="range" min="'+ range_min +'" max="'+ range_max +'" value="'+ range_value +'" id="poke_range_' + next + '" oninput="poke_range(' + next + ')">';
break;
case "select":
var opt_str = "";
var opt_sel;
var opt_pair;
const sel = options[0];
for (var index = 1; index < options.length; index++) {
opt_pair = options[index].split("^");
opt_sel = (sel == index - 1) ? (" selected") : ("");
opt_str = opt_str.concat('<option value="', opt_pair[0], '"', opt_sel + '>', opt_pair[1], '</option>');
}
newcell.innerHTML = '<input title="Value to poke at address" type="text" id="poke_' + next + '" value="'+ fmtUnsignedLong(sel, format) +'" size="10" onkeydown="poke_key(this, ' + next + ')">';
newcell = row.insertCell(-1);
newcell.innerHTML = '<select title="Value to poke at address" style="width:100%" id="poke_select_' + next + '" onchange="poke_select(' + next + ')">' + opt_str + '</select>';
break;
default:
break;
}
if (type == "sec") {
newcell = row.insertCell(-1);
newcell.innerHTML = '<input title="Poke all selected addresses in section" type="submit" value="Poke" onclick="poke_section(' + next + ')">';
newcell = row.insertCell(-1);
newcell.innerHTML = '<img style="vertical-align:middle" src="../amber.gif" alt="Missing Image!">';
newcell = row.insertCell(-1);
newcell.innerHTML = '<input title="Description of section" type="text" id="name_' + next + '" value="' + description + '" size="40">';
} else {
newcell = row.insertCell(-1);
newcell.innerHTML = '<input title="Select address for poking" type="checkbox" id="poke_sel_' + next + '" ' + poke_checked + '>';
newcell = row.insertCell(-1);
newcell.innerHTML = '<input title="Poke address" type="submit" value="Poke" onclick="poke_widget(' + next + ')">';
newcell = row.insertCell(-1);
newcell.innerHTML = '<img title="Poke status" id="spoke_' + next + '" style="vertical-align:middle" src="../amber.gif" alt="Missing Image!">';
newcell = row.insertCell(-1);
newcell.innerHTML = '<input title="Description of address" type="text" id="name_' + next + '" value="' + description + '" size="40">';
}
}
// Remove row from table
function rem_register(reg) {
var table = document.getElementById("registers");
if (table.rows.length > 1) {
table.deleteRow(-1);
}
}
// Remove all rows from table
function remove_all() {
var table = document.getElementById("registers");
var rows = table.rows.length - 1;
for (var index = 0; index < rows; index++) {
table.deleteRow(-1);
}
}
// Note: browser file access is made difficult for security reasons - there maybe a better way of doing file read & write.
var config_file = null;
// Create virtual configuration file from address table for user to download
function create_config() {
var text = "";
var table = document.getElementById("registers");
const rows = table.rows.length - 1;
for (var index = 0; index < rows; index++) {
const obj_addr = document.getElementById("addr_" + index);
if (obj_addr) { // Register type
var addr = document.getElementById("addr_" + index).value;
var display = document.getElementById("display_" + index).checked;
var peek_sel = document.getElementById("peek_sel_" + index).checked;
var poke = document.getElementById("poke_" + index).value;
var poke_sel = document.getElementById("poke_sel_" + index).checked;
var name = document.getElementById("name_" + index).value;
text += "reg" + "|" + addr + "|" + display + "|" + peek_sel + "|" + poke + "|" + poke_sel + "|"+ name + "\n";
} else { // Section type
var name = document.getElementById("name_" + index).value;
text += "sec" + "|" + name + "\n";
}
}
const data = new Blob([text], {type: 'text/plain'});
if (config_file !== null) {
URL.revokeObjectURL(config_file);
}
config_file = URL.createObjectURL(data);
var link = document.getElementById('download');
link.href = config_file;
link.style.display = 'inline';
}
// Read configuration file and update address table
function load_config(input) {
var file = input.target.files[0];
if (file) {
var reader = new FileReader();
reader.onload = function(input) {
var contents = input.target.result;
const lines = contents.split(/\r\n|\n/);
remove_all();
lines.forEach((line) => {
if (line.length > 0) {
var table = document.getElementById("registers");
var next = table.rows.length - 1;
const values = line.split("|");
switch(values[0]) {
case "reg":
case "sec":
case "range":
case "select":
add_row_raw(values);
break;
default:
alert("Error: Unrecognized table type found (" + values[0] + "), ignoring.");
}
}
});
};
reader.readAsText(file);
}
}
document.getElementById('load_config').addEventListener('change', load_config, false);
Check out the changes.