element.
+ * @property {JQuery element with id `streamerstatus${index}`, with index as two digit number, e.g. 01, 02, 03
+ status: $(`#streamerstatus${index.toString().padStart(2, '0')}`),
+ // vu is element wiht id `streamertitle${index}`, with index as two digit number, e.g. 01, 02, 03
+ title: $(`#streamertitle${index.toString().padStart(2, '0')}`),
+ // ip is
element with id `streamerip${index}`, with index as two digit number, e.g. 01, 02, 03
+ ip: $(`#streamerip${index.toString().padStart(2, '0')}`),
+ // buffer is
element with id `streamerbuffer${index}`, with index as two digit number, e.g. 01, 02, 03
+ buffer: $(`#streamerbuffer${index.toString().padStart(2, '0')}`),
+ // status is
+ `);
+ let $addedrow = $('#pagingqueuetable tr:last');
+ $addedrow.off('click').on('click', function () {
+ if (window.selectedpagingrow) {
+ window.selectedpagingrow.find('td').css('background-color', '');
+ if (window.selectedpagingrow.is($(this))) {
+ window.selectedpagingrow = null;
+ $('#removepagingqueue').prop('disabled', true);
+ return;
+ }
+ }
+ window.selectedpagingrow = $(this);
+ window.selectedpagingrow.find('td').css('background-color', 'lightblue');
+ $('#removepagingqueue').prop('disabled', false);
+ });
+ });
+}
+
+/**
+ * Fills the automatic queue table body with the provided data.
+ * @param {QueueTable[]} vv array of QueueTable objects
+ * @returns
+ */
+function fill_automaticqueuetablebody(vv) {
+ $('#automaticqueuetable').empty();
+ if (!Array.isArray(vv) || vv.length === 0) return;
+ vv.forEach(item => {
+ // fill index and description columns using item properties
+ let description = `${item.Date_Time}_${item.Source}_${item.Type}_${item.Message}_${item.BroadcastZones}`;
+ $('#automaticqueuetable').append(`${item.index}
+ ${description}
+
+ `);
+ let $addedrow = $('#automaticqueuetable tr:last');
+ $addedrow.off('click').on('click', function () {
+ if (window.selectedautomaticrow) {
+ window.selectedautomaticrow.find('td').css('background-color', '');
+ if (window.selectedautomaticrow.is($(this))) {
+ window.selectedautomaticrow = null;
+ $('#removeautomatictable').prop('disabled', true);
+ return;
+ }
+ }
+ window.selectedautomaticrow = $(this);
+ window.selectedautomaticrow.find('td').css('background-color', 'lightblue');
+ $('#removeautomatictable').prop('disabled', false);
+ });
+ });
+}
+
+function reloadPagingQueue(APIURL = "QueuePaging/") {
+ window.PagingQueue = [];
+ fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
+ if (Array.isArray(okdata) && okdata.length > 0) {
+ window.PagingQueue.push(...okdata);
+ fill_pagingqueuetablebody(window.PagingQueue);
+ } else {
+ console.log("reloadPagingQueue: okdata is not array");
+ }
+ }, (errdata) => {
+ console.log("reloadPagingQueue: errdata", errdata);
+ });
+}
+
+function reloadAutomaticQueue(APIURL = "QueueTable/") {
+ window.QueueTable = [];
+ fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => {
+ if (Array.isArray(okdata) && okdata.length > 0) {
+ window.QueueTable.push(...okdata);
+ fill_automaticqueuetablebody(window.QueueTable);
+ } else {
+ console.log("reloadAutomaticQueue: okdata is not array");
+ }
+ }, (errdata) => {
+ console.log("reloadAutomaticQueue: errdata", errdata);
+ });
+}
+
+function RemovePagingQueueByIndex(index, APIURL = "QueuePaging/") {
+ fetchAPI(APIURL + "DeleteByIndex/" + index, "DELETE", {}, null, (okdata) => {
+ console.log("RemovePagingQueueByIndex: okdata", okdata);
+ reloadPagingQueue(APIURL);
+ }, (errdata) => {
+ console.log("RemovePagingQueueByIndex: errdata", errdata);
+ });
+}
+
+function RemoveAutomaticQueueByIndex(index, APIURL = "QueueTable/") {
+ fetchAPI(APIURL + "DeleteByIndex/" + index, "DELETE", {}, null, (okdata) => {
+ console.log("RemoveAutomaticQueueByIndex: okdata", okdata);
+ reloadAutomaticQueue(APIURL);
+ }, (errdata) => {
+ console.log("RemoveAutomaticQueueByIndex: errdata", errdata);
+ });
+}
+
+$(document).ready(function () {
+ console.log("overview.js loaded");
+
+
+
+ $('#clearpagingqueue').off('click').on('click', function () {
+ DoClear("QueuePaging/", "Paging Queue", (okdata) => {
+ reloadPagingQueue();
+ alert("Success clear Paging Queue: " + okdata.message);
+ }, (errdata) => {
+ alert("Error clear Paging Queue: " + errdata.message);
+ });
+ });
+ $('#removepagingqueue').off('click').on('click', function () {
+ if (window.selectedpagingrow) {
+ let cells = window.selectedpagingrow.find('td');
+ let index = Number(cells.eq(0).text());
+ let description = cells.eq(1).text();
+ if (!isNaN(index) && description && description.length > 0) {
+ if (confirm(`Are you sure to remove Paging Queue Index: ${index} Description: ${description} ?`)) {
+ RemovePagingQueueByIndex(index);
+ window.selectedpagingrow = null;
+ $('#removepagingqueue').prop('disabled', true);
+ }
+ }
+ }
+ });
+
+
+
+ $('#clearautomatictable').off('click').on('click', function () {
+ DoClear("QueueTable/", "Automatic Queue", (okdata) => {
+ reloadAutomaticQueue();
+ alert("Success clear Automatic Queue: " + okdata.message);
+ }, (errdata) => {
+ alert("Error clear Automatic Queue: " + errdata.message);
+ });
+ });
+ $('#removeautomatictable').off('click').on('click', function () {
+ if (window.selectedautomaticrow) {
+ let cells = window.selectedautomaticrow.find('td');
+ let index = Number(cells.eq(0).text());
+ let description = cells.eq(1).text();
+ if (!isNaN(index) && description && description.length > 0) {
+ if (confirm(`Are you sure to remove Automatic Queue Index: ${index} Description: ${description} ?`)) {
+ RemoveAutomaticQueueByIndex(index);
+ window.selectedautomaticrow = null;
+ $('#removeautomatictable').prop('disabled', true);
+ }
+ }
+ }
+ });
+
+ let intervaljob = null;
+ function runIntervalJob() {
+ if (intervaljob) clearInterval(intervaljob);
+ intervaljob = setInterval(() => {
+ sendCommand("getPagingQueue", "");
+ sendCommand("getAASQueue", "");
+ sendCommand("getStreamerOutputs", "");
+ }, 1000);
+ console.log("overview.js interval job started");
+ }
+
+ runIntervalJob();
+
+ window.addEventListener('ws_connected', () => {
+ console.log("overview.js ws_connected event triggered");
+ runIntervalJob();
+ });
+
+ window.addEventListener('ws_disconnected', () => {
+ console.log("overview.js ws_disconnected event triggered");
+ if (intervaljob) clearInterval(intervaljob);
+ intervaljob = null;
+ });
+ window.addEventListener('ws_message', (event) => {
+ let rep = event.detail;
+ let cmd = rep.reply;
+ let data = rep.data;
+ if (cmd && cmd.length > 0) {
+ switch (cmd) {
+ case "getPagingQueue":
+ let pq = JSON.parse(data);
+ //console.log("getPagingQueue:", pq);
+ if (Array.isArray(pq) && pq.length > 0) {
+ window.PagingQueue = [];
+ window.PagingQueue.push(...pq);
+ console.log(`PagingQueue length: ${window.PagingQueue.length}`);
+ fill_pagingqueuetablebody(window.PagingQueue);
+ }
+ break;
+ case "getAASQueue":
+ let aq = JSON.parse(data);
+ //console.log("getAASQueue:", aq);
+ if (Array.isArray(aq) && aq.length > 0) {
+ window.QueueTable = [];
+ window.QueueTable.push(...aq);
+ console.log(`QueueTable length: ${window.QueueTable.length}`);
+ fill_automaticqueuetablebody(window.QueueTable);
+ }
+ break;
+ case "getStreamerOutputs":
+ /**
+ * @type {StreamerOutputData[]}
+ */
+ let so = JSON.parse(data);
+ UpdateStreamerCard(so);
+ break;
+ }
+ }
+ });
+
+
+
+
+
+ $(window).on('beforeunload', function () {
+ console.log("overview.js beforeunload event triggered");
+ clearInterval(intervaljob);
+ intervaljob = null;
+ });
+});
diff --git a/html/webpage/assets/js/script.js b/html/webpage/assets/js/script.js
index d293996..cb299eb 100644
--- a/html/webpage/assets/js/script.js
+++ b/html/webpage/assets/js/script.js
@@ -69,10 +69,10 @@ function fetchAPI(endpoint, method, headers = {}, body = null, cbOK, cbError) {
}
}
fetch(url, options)
- .then(async(response) => {
+ .then(async (response) => {
if (!response.ok) {
- let msg ;
- try{
+ let msg;
+ try {
let _xxx = await response.json();
msg = _xxx.message || response.statusText;
} catch {
@@ -281,10 +281,10 @@ window.redcircle = null;
*/
$(document).ready(function () {
document.title = "Automatic Announcement System"
- if (window.greencircle === null){
+ if (window.greencircle === null) {
fetchImg('green_circle.png', (url) => { window.greencircle = url; }, (err) => { console.error("Error loading green_circle.png : ", err); });
}
- if (window.redcircle === null){
+ if (window.redcircle === null) {
fetchImg('red_circle.png', (url) => { window.redcircle = url; }, (err) => { console.error("Error loading red_circle.png : ", err); });
}
const wsURL = window.location.pathname + '/ws'
@@ -292,8 +292,8 @@ $(document).ready(function () {
alert("Runtime error: " + chrome.runtime.lastError.message);
return;
}
-
-
+
+
// reset status indicators
function resetStatusIndicators() {
@@ -311,64 +311,83 @@ $(document).ready(function () {
getCategories();
getLanguages();
getScheduledDays();
-
- // Initialize WebSocket connection
- window.ws = new WebSocket(wsURL);
- window.ws.onopen = () => {
- console.log('WebSocket connection established');
- $('#onlineindicator').attr('src', window.greencircle);
- };
- window.ws.onmessage = (event) => {
- if ($('#onlineindicator').attr('src') !== window.greencircle) {
+ // reconnect handle
+ let ws_reconnect;
+
+ function reconnect() {
+ if (window.ws && window.ws.readyState === WebSocket.OPEN) return;
+ const s = new WebSocket(wsURL);
+ s.addEventListener('open', () => {
+ console.log('WebSocket connection established');
$('#onlineindicator').attr('src', window.greencircle);
- }
- let rep = JSON.parse(event.data);
- let cmd = rep.reply
- let data = rep.data;
- if (cmd && cmd.length > 0) {
- switch (cmd) {
- case "getCPUStatus":
- $('#cpustatus').text("CPU : " + data)
- break;
- case "getMemoryStatus":
- $('#ramstatus').text("RAM : " + data)
- break;
- case "getDiskStatus":
- $('#diskstatus').text("Disk : " + data)
- break;
- case "getNetworkStatus":
- //console.log("Network status: ", data);
- let result = "";
- let json = JSON.parse(data);
- if (Array.isArray(json) && json.length> 0){
- json.forEach((net)=>{
- //console.log("Network interface: ", net);
- if (result.length>0) result+="\n"
- result+=`${net.displayName} (${net.ipV4addr.join(";")}) TX:${(net.txSpeed/1024).toFixed(1)} KB/s RX:${(net.rxSpeed/1024).toFixed(1)} KB/s; `
- })
- } else result = "N/A";
- $('#networkstatus').text(result)
- break;
- case "getSystemTime":
- $('#datetimetext').text(data)
- break;
+ if (ws_reconnect) {
+ // stop reconnect attempts
+ clearTimeout(ws_reconnect);
+ ws_reconnect = null;
}
+ window.dispatchEvent(new Event('ws_connected'));
+ });
+ s.addEventListener('close', () => {
+ console.log('WebSocket connection closed');
+ window.dispatchEvent(new Event('ws_disconnected'));
+ resetStatusIndicators();
+ if (!ws_reconnect) {
+ clearTimeout(ws_reconnect);
+ ws_reconnect = null;
+ }
+ ws_reconnect = setTimeout(reconnect, 5000); // try to reconnect every 5 seconds
+ });
+ s.addEventListener('message', (event) => {
+ if ($('#onlineindicator').attr('src') !== window.greencircle) {
+ $('#onlineindicator').attr('src', window.greencircle);
+ }
+ let rep = JSON.parse(event.data);
+ window.dispatchEvent(new CustomEvent('ws_message', { detail: rep }));
+ let cmd = rep.reply
+ let data = rep.data;
+ if (cmd && cmd.length > 0) {
+ switch (cmd) {
+ case "getCPUStatus":
+ $('#cpustatus').text("CPU : " + data)
+ break;
+ case "getMemoryStatus":
+ $('#ramstatus').text("RAM : " + data)
+ break;
+ case "getDiskStatus":
+ $('#diskstatus').text("Disk : " + data)
+ break;
+ case "getNetworkStatus":
+ let result = "";
+ let json = JSON.parse(data);
+ if (Array.isArray(json) && json.length > 0) {
+ json.forEach((net) => {
+ if (result.length > 0) result += "\n"
+ result += `${net.displayName} (${net.ipV4addr.join(";")}) TX:${(net.txSpeed / 1024).toFixed(1)} KB/s RX:${(net.rxSpeed / 1024).toFixed(1)} KB/s`
+ })
+ } else result = "N/A";
+ $('#networkstatus').text(result)
+ break;
+ case "getSystemTime":
+ $('#datetimetext').text(data)
+ break;
+
+ }
+ }
+ });
+ window.ws = s;
+ }
+
+ reconnect();
+ window.addEventListener('beforeunload', () => {
+ try{
+ window.ws?.close(1000, "Client closed connection");
+ } catch (error) {
+ console.error("Error closing WebSocket connection:", error);
}
- };
- window.ws.onclose = () => {
- console.log('WebSocket connection closed');
- resetStatusIndicators();
-
- };
-
- // window.ws.onerror = (error) => {
- // console.error('WebSocket error:', error);
- // };
-
-
+ });
setInterval(() => {
sendCommand("getCPUStatus", "")
@@ -395,7 +414,7 @@ $(document).ready(function () {
if (status === "success") {
console.log("Soundbank content loaded successfully");
// pindah soundbank.js
-
+
} else {
console.error("Error loading soundbank content : ", xhr.status, xhr.statusText);
}
@@ -408,7 +427,7 @@ $(document).ready(function () {
if (status === "success") {
console.log("Messagebank content loaded successfully");
// pindah messagebank.js
-
+
} else {
console.error("Error loading messagebank content : ", xhr.status, xhr.statusText);
}
@@ -421,7 +440,7 @@ $(document).ready(function () {
if (status === "success") {
console.log("Language content loaded successfully");
// pindah languagelink.js
-
+
} else {
console.error("Error loading language content : ", xhr.status, xhr.statusText);
}
@@ -445,7 +464,7 @@ $(document).ready(function () {
if (status === "success") {
console.log("Timer content loaded successfully");
// pindah ke schedulebank.js
-
+
} else {
console.error("Error loading timer content : ", xhr.status, xhr.statusText);
}
@@ -469,7 +488,7 @@ $(document).ready(function () {
if (status === "success") {
console.log("User Management content loaded successfully");
// pindah ke usermanagement.js
-
+
} else {
console.error("Error loading user management content:", xhr.status, xhr.statusText);
}
diff --git a/html/webpage/overview.html b/html/webpage/overview.html
index ff8c347..ab5c9b1 100644
--- a/html/webpage/overview.html
+++ b/html/webpage/overview.html
@@ -1324,6 +1324,7 @@
+
${item.index}
+ ${description}
+