diff --git a/html/webpage/assets/js/messagebank.js b/html/webpage/assets/js/messagebank.js index 5de0112..7022166 100644 --- a/html/webpage/assets/js/messagebank.js +++ b/html/webpage/assets/js/messagebank.js @@ -121,17 +121,32 @@ $(document).ready(function () { function refill_messageavailablevariables() { $messageavailablevariables.empty(); categories.forEach(cat => { - $messageavailablevariables.append(ListItem(`{${cat}}`)); + $messageavailablevariables.append(ListItem(`[${cat}]`)); }); - if ($messagelanguage.val() && $messagevoicetype.val()) { - soundbankdata - .filter(sb => sb.language.toLowerCase() === $messagelanguage.val().toLowerCase()) - .filter(sb => sb.voiceType.toLowerCase() === $messagevoicetype.val().toLowerCase()) - .filter(sb => sb.category.toLowerCase() === "phrase") - .forEach(sb => { - $messageavailablevariables.append(ListItem(`[${sb.Description}]`)); + let lang = $messagelanguage.val(); + let vt = $messagevoicetype.val(); + if (lang && lang.length > 0){ + console.log("Selected Language:", lang); + if (vt && vt.length > 0){ + console.log("Selected Voice Type:", vt); + + fetchAPI(`SoundBank/GetPhrases/${lang}/${vt}`, "GET", {}, null, (okdata) => { + if (Array.isArray(okdata) && okdata.length > 0) { + console.log(`Loaded ${okdata.length} phrases from soundbank for language=${lang} and voiceType=${vt}`); + console.log(JSON.stringify(okdata)); + okdata.forEach(sb => { + if (sb.description && sb.description.length > 0) { + $messageavailablevariables.append(ListItem(`${sb.description} [${sb.TAG}]`)); + } + }); + } + }, (errdata) => { + //alert("Error loading phrases from soundbank : " + errdata.message); }); + } + } + } @@ -143,7 +158,7 @@ $(document).ready(function () { $messagedescription.val(''); // fill messagelanguage options from languages[] $messagelanguage.empty(); - languages.forEach(lang => { + window.languages.forEach(lang => { $messagelanguage.append(new Option(lang, lang)); }); $messagelanguage.val(null); @@ -154,7 +169,7 @@ $(document).ready(function () { $messageannid.val(1); // fill messagevoicetype options from voiceTypes[] $messagevoicetype.empty(); - voiceTypes.forEach(vt => { + window.voiceTypes.forEach(vt => { $messagevoicetype.append(new Option(vt, vt)); }); $messagevoicetype.val(null); @@ -344,19 +359,8 @@ $(document).ready(function () { // Fill modal fields with selected messagebank data $messageindex.val(mb.index).prop('disabled', true); $messagedescription.val(mb.description); - // Fill messagelanguage options and select current - $messagelanguage.empty(); - languages.forEach(lang => { - $messagelanguage.append(new Option(lang, lang)); - }); $messagelanguage.val(mb.language); - // Fill messagevoicetype options and select current - $messagevoicetype.empty(); - voiceTypes.forEach(vt => { - $messagevoicetype.append(new Option(vt, vt)); - }); $messagevoicetype.val(mb.voice_Type); - // Set annid $messageannid.val(mb.aNN_ID); // Refill message available variables refill_messageavailablevariables(); diff --git a/html/webpage/assets/js/overview.js b/html/webpage/assets/js/overview.js new file mode 100644 index 0000000..2ea3069 --- /dev/null +++ b/html/webpage/assets/js/overview.js @@ -0,0 +1,349 @@ +/** + * @typedef {Object} StreamerOutputData + * @property {number} index - The index of the Barix connection. + * @property {string} channel - The channel name of the Barix connection. + * @property {string} ipaddress - The IP address of the Barix connection. + * @property {number} bufferRemain - The remaining buffer size of the Barix connection. + * @property {boolean} isPlaying - true = playback started, false = playback stopped + * @property {number} vu - The VU level of the Barix connection, 0 to 100. + */ + + +/** + * @typedef {Object} PagingQueue + * @property {number} index - The index of the paging queue item. + * @property {string} Date_Time - The date and time of the paging queue item. + * @property {string} Source - The source of the paging queue item. + * @property {string} Type - The type of the paging queue item. + * @property {string} Message - The message of the paging queue item. + * @property {string} BroadcastZones - The broadcast zones of the paging queue item. + */ + +/** + * @typedef {Object} StreamerCard + * @property {JQuery | null} title - The jQuery result should be

element. + * @property {JQuery | null} ip - The jQuery result should be
element. + * @property {JQuery | null} buffer - The jQuery result should be
element. + * @property {JQuery | null} status - The jQuery result should be

element. + * @property {JQuery | null} vu - The jQuery result should be element. + */ + +function getCardByIndex(index) { + let obj = { + // title 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

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 with id `streamervu${index}`, with index as two digit number, e.g. 01, 02, 03 + vu: $(`#streamervu${index.toString().padStart(2, '0')} .progress-bar`), + } + return obj; +} + +/** + * Updates the streamer card with the provided values. + * @param {StreamerOutputData[]} values + */ +function UpdateStreamerCard(values) { + if (!Array.isArray(values) || values.length === 0) return; + function setProgress($bar, value, max = 100) { + const v = Number(value ?? 0); + const pct = Math.max(0, Math.min(100, Math.round((v / max) * 100))); + $bar + .attr('aria-valuenow', v) // semantic value + .css('width', pct + '%') // visual width + .text(pct); // optional label + } + for (let i = 1; i <= 64; i++) { + let vv = values.find(v => v.index === i); + let card = getCardByIndex(i); + if (vv) { + // there is value for this index + if (card.title) card.title.text(vv.channel ? vv.channel : `Channel ${i.toString().padStart(2, '0')}`); + if (card.ip) card.ip.text(`IP Address: ${vv.ipaddress ? vv.ipaddress : 'N/A'}`); + if (card.buffer) card.buffer.text(`Buffer: ${vv.bufferRemain !== undefined && vv.bufferRemain !== null ? vv.bufferRemain.toString() : 'N/A'}`); + if (card.status) card.status.text(`Status: ${vv.isPlaying ? 'Playing' : 'Stopped'}`); + if (card.vu) { + setProgress(card.vu, vv.vu, 100); + } + } else { + // no value for this index, disable the card + if (card.title) card.title.text(`Channel ${i.toString().padStart(2, '0')}`); + if (card.ip) card.ip.text(`IP Address: N/A`); + if (card.buffer) card.buffer.text(`Buffer: N/A`); + if (card.status) card.status.text(`Status: Disconnected`); + if (card.vu) { + setProgress(card.vu, 0, 100); + } + } + } +} + +/** + * @type {PagingQueue[]} + */ +window.PagingQueue = []; +/** + * @type {JQuery | null} + */ +window.selectedpagingrow = null; + +/** + * @typedef {Object} QueueTable + * @property {number} index - The index of the automatic queue item. + * @property {string} Date_Time - The date and time of the automatic queue item. + * @property {string} Source - The source of the automatic queue item. + * @property {string} Type - The type of the automatic queue item. + * @property {string} Message - The message of the automatic queue item. + * @property {string} SB_TAGS - The SB_TAGS of the automatic queue item. + * @property {string} BroadcastZones - The broadcast zones of the automatic queue item. + * @property {number} Repeat - The repeat count of the automatic queue item. + * @property {string} Language - The language of the automatic queue item. + */ + +/** + * @type {QueueTable[]} + */ +window.QueueTable = []; +/** + * @type {JQuery | null} + */ +window.selectedautomaticrow = null; + +/** + * Fills the paging queue table body with the provided data. + * @param {PagingQueue[]} vv array of PagingQueue objects + * @returns + */ +function fill_pagingqueuetablebody(vv) { + $('#pagingqueuetable').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}`; + $('#pagingqueuetable').append(` + ${item.index} + ${description} + `); + 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 7f0b14a..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,54 +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": - $('#networkstatus').text("Network : " + data) - 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", "") @@ -385,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); } @@ -398,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); } @@ -411,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); } @@ -435,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); } @@ -459,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/assets/js/soundbank.js b/html/webpage/assets/js/soundbank.js index c0f07c9..9abf4a3 100644 --- a/html/webpage/assets/js/soundbank.js +++ b/html/webpage/assets/js/soundbank.js @@ -1,3 +1,11 @@ + + +/** + * @typedef {Object} Select2item + * @property {number} id + * @property {string} text + */ + /** * @typedef {Object} SoundBank * @property {number} index @@ -9,12 +17,6 @@ * @property {string} path */ -/** - * @typedef {Object} Select2item - * @property {number} id - * @property {string} text - */ - /** * List of Soundbank data loaded from server * @type {SoundBank[]} 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 @@ + \ No newline at end of file diff --git a/src/Main.kt b/src/Main.kt index 8707c7c..220e7b4 100644 --- a/src/Main.kt +++ b/src/Main.kt @@ -31,7 +31,7 @@ lateinit var audioPlayer: AudioPlayer val StreamerOutputs: MutableMap = HashMap() lateinit var udpreceiver: UDPReceiver lateinit var tcpreceiver: TCPReceiver -const val version = "0.0.6 (14/10/2025)" +const val version = "0.0.7 (15/10/2025)" // AAS 64 channels const val max_channel = 64 diff --git a/src/codes/Somecodes.kt b/src/codes/Somecodes.kt index a62121c..8795e2c 100644 --- a/src/codes/Somecodes.kt +++ b/src/codes/Somecodes.kt @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import content.Category import content.Language +import content.NetworkInformation import content.ScheduleDay import content.VoiceType import kotlinx.coroutines.CoroutineScope @@ -35,6 +36,7 @@ class Somecodes { val si = SystemInfo() val processor: CentralProcessor = si.hardware.processor val memory : GlobalMemory = si.hardware.memory + val NetworkInfoMap = mutableMapOf() val datetimeformat1: DateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss") val dateformat1: DateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy") @@ -278,16 +280,43 @@ class Somecodes { , (usedMemory.toDouble() / totalMemory * 100)) } - fun GetNetworkStatus(networkname: String) : String{ + fun GetNetworkStatus(cb : Consumer>) { val networks: List = si.hardware.networkIFs.toList() - //TODO cari network yang sesuai dengan networkname -// networks.forEach { net -> -// -// println(net.name+" "+net.displayName+" ") -// net.updateAttributes() -// println("upload: ${SizetoHuman(net.bytesSent)} sent, download: ${SizetoHuman(net.bytesRecv)} received") -// } - return "" + networks.forEach { net -> + + if (net.ifOperStatus==NetworkIF.IfOperStatus.UP){ + if (net.iPv4addr.size>0 || net.iPv6addr.size>0){ + var ni = NetworkInfoMap[net.name] + if (ni == null){ + ni = NetworkInformation(net.name, net.displayName, net.macaddr) + NetworkInfoMap[net.name] = ni + } + ni.ipV4addr = net.iPv4addr.toMutableList() + ni.ipV6addr = net.iPv6addr.toMutableList() + ni.speed = net.speed + ni.packetsSent = net.packetsSent + ni.packetsRecv = net.packetsRecv + if (ni.updateStamp==0L){ + ni.bytesSent = net.bytesSent + ni.bytesRecv = net.bytesRecv + ni.txSpeed = 0 + ni.rxSpeed = 0 + ni.updateStamp = System.currentTimeMillis() + } else { + // tx speed = (current bytesSent - previous bytesSent) / (current time - previous time) * 1000 + val currentTime = System.currentTimeMillis() + ni.txSpeed = ((net.bytesSent - ni.bytesSent) * 1000 / (currentTime - ni.updateStamp)) + ni.rxSpeed = ((net.bytesRecv - ni.bytesRecv) * 1000 / (currentTime - ni.updateStamp)) + ni.bytesSent = net.bytesSent + ni.bytesRecv = net.bytesRecv + ni.updateStamp = currentTime + } + } else if (NetworkInfoMap.contains(net.name)) NetworkInfoMap.remove(net.name) + } else if (NetworkInfoMap.contains(net.name)) NetworkInfoMap.remove(net.name) + + + } + cb.accept(NetworkInfoMap.values.toList()) } /** diff --git a/src/content/NetworkInformation.kt b/src/content/NetworkInformation.kt new file mode 100644 index 0000000..916a858 --- /dev/null +++ b/src/content/NetworkInformation.kt @@ -0,0 +1,15 @@ +package content + +class NetworkInformation(val name: String, val displayName: String, val macAddress: String) { + var ipV4addr: MutableList = mutableListOf() + var ipV6addr: MutableList = mutableListOf() + var bytesSent: Long = 0 + var bytesRecv: Long = 0 + var packetsSent: Long = 0 + var packetsRecv: Long = 0 + var speed: Long = 0 // in bits per second + var updateStamp : Long = 0 // epoch time in milliseconds, buat hitung speed + var txSpeed: Long = 0 + var rxSpeed: Long = 0 + +} \ No newline at end of file diff --git a/src/database/BroadcastZones.kt b/src/database/BroadcastZones.kt index b766d3c..cf2529c 100644 --- a/src/database/BroadcastZones.kt +++ b/src/database/BroadcastZones.kt @@ -1,4 +1,28 @@ package database @Suppress("unused") -data class BroadcastZones(var index: UInt, var description: String, var SoundChannel: String, var id: String, var bp: String) +data class BroadcastZones(var index: UInt, var description: String, var SoundChannel: String, var id: String, var bp: String){ + + /** + * Check if all fields are not empty + */ + fun isNotEmpty(): Boolean{ + if (description.isNotEmpty()){ + if (SoundChannel.isNotEmpty()){ + if (id.isNotEmpty()){ + if (bp.isNotEmpty()){ + return true + } + } + } + } + return false + } + + /** + * Return a string representation of the BroadcastZones object. + */ + override fun toString(): String { + return "BroadcastZones(index=$index, description='$description', SoundChannel='$SoundChannel', id='$id', bp='$bp')" + } +} diff --git a/src/database/BroadcastZonesHtml.kt b/src/database/BroadcastZonesHtml.kt deleted file mode 100644 index 8bda430..0000000 --- a/src/database/BroadcastZonesHtml.kt +++ /dev/null @@ -1,3 +0,0 @@ -package database - -data class BroadcastZonesHtml(var index: UInt, var description: String, var SoundChannel: String, var Box: String, var Relay: String) diff --git a/src/database/IpZones.kt b/src/database/IpZones.kt deleted file mode 100644 index 7debd6b..0000000 --- a/src/database/IpZones.kt +++ /dev/null @@ -1,4 +0,0 @@ -package database - -@Suppress("unused") -data class IpZones(var index:UInt, var description: String, var ip: String) diff --git a/src/database/LanguageLink.kt b/src/database/LanguageLink.kt index 540b9b4..bea931d 100644 --- a/src/database/LanguageLink.kt +++ b/src/database/LanguageLink.kt @@ -1,4 +1,22 @@ package database -@Suppress("unused") -data class LanguageLink(var index: UInt, var TAG: String, var Language: String) +data class LanguageLink(var index: UInt, var TAG: String, var Language: String){ + + /** + * Check if the LanguageLink object has non-empty TAG and Language fields. + * @return true if both TAG and Language are not empty, false otherwise. + */ + fun isNotEmpty() : Boolean { + return TAG.isNotEmpty() && Language.isNotEmpty() + + } + + /** + * Returns a string representation of the LanguageLink object. + * @return A string in the format "LanguageLink(index=..., TAG='...', Language='...')". + */ + override fun toString(): String { + return "LanguageLink(index=$index, TAG='$TAG', Language='$Language')" + } + +} diff --git a/src/database/LogSemiauto.kt b/src/database/LogSemiauto.kt index 6d24a7b..b3bf8a1 100644 --- a/src/database/LogSemiauto.kt +++ b/src/database/LogSemiauto.kt @@ -1,4 +1,9 @@ package database -@Suppress("unused") -data class LogSemiauto(val index: UInt, val date: String, val time: String, val source: String, val description: String) +data class LogSemiauto(val index: UInt, val date: String, val time: String, val source: String, val description: String){ + + + override fun toString(): String { + return "$date $time [$source] $description" + } +} diff --git a/src/database/Messagebank.kt b/src/database/Messagebank.kt index e3d5fbf..2da8a0c 100644 --- a/src/database/Messagebank.kt +++ b/src/database/Messagebank.kt @@ -8,4 +8,27 @@ data class Messagebank( var Voice_Type: String, var Message_Detail: String, var Message_TAGS: String - ) + ) { + + /** + * Check if all fields are not empty and ANN_ID > 0 + */ + fun isNotEmpty(): Boolean{ + if (Description.isNotEmpty()){ + if (Language.isNotEmpty()){ + if (Voice_Type.isNotEmpty()){ + if (Message_Detail.isNotEmpty()){ + if (Message_TAGS.isNotEmpty()){ + return true + } + } + } + } + } + return false + } + + override fun toString(): String { + return "Messagebank(index=$index, Description='$Description', Language='$Language', ANN_ID=$ANN_ID, Voice_Type='$Voice_Type', Message_Detail='$Message_Detail', Message_TAGS='$Message_TAGS')" + } +} diff --git a/src/database/QueueFids.kt b/src/database/QueueFids.kt index 5711b6c..8c27727 100644 --- a/src/database/QueueFids.kt +++ b/src/database/QueueFids.kt @@ -1,4 +1,13 @@ package database @Suppress("unused") -data class QueueFids(var index: UInt, var ALCODE: String, var FLNUM: String, var ORIGIN: String, var ETAD: String, var FREMARK: String) +data class QueueFids(var index: UInt, var ALCODE: String, var FLNUM: String, var ORIGIN: String, var ETAD: String, var FREMARK: String){ + + fun isNotEmpty(): Boolean { + return ALCODE.isNotEmpty() && FLNUM.isNotEmpty() && ORIGIN.isNotEmpty() && ETAD.isNotEmpty() && FREMARK.isNotEmpty() + } + + override fun toString(): String { + return "QueueFids(index=$index, ALCODE='$ALCODE', FLNUM='$FLNUM', ORIGIN='$ORIGIN', ETAD='$ETAD', FREMARK='$FREMARK')" + } +} diff --git a/src/database/QueuePaging.kt b/src/database/QueuePaging.kt index 38afb29..e26dfa6 100644 --- a/src/database/QueuePaging.kt +++ b/src/database/QueuePaging.kt @@ -1,6 +1,10 @@ package database data class QueuePaging(var index: UInt, var Date_Time: String, var Source: String, var Type: String, var Message: String, var BroadcastZones: String){ + fun isNotEmpty(): Boolean { + return Date_Time.isNotEmpty() && Source.isNotEmpty() && Type.isNotEmpty() && Message.isNotEmpty() && BroadcastZones.isNotEmpty() + } + override fun toString(): String { return "QueuePaging(index=$index, Date_Time='$Date_Time', Source='$Source', Type='$Type', Message='$Message', BroadcastZones='$BroadcastZones')" } diff --git a/src/database/QueueTable.kt b/src/database/QueueTable.kt index e5be756..2aae77f 100644 --- a/src/database/QueueTable.kt +++ b/src/database/QueueTable.kt @@ -2,6 +2,12 @@ package database data class QueueTable(var index: UInt, var Date_Time: String, var Source: String, var Type: String, var Message: String, var SB_TAGS: String, var BroadcastZones: String, var Repeat: UInt, var Language: String){ + /** + * Check if all fields are not empty + */ + fun isNotEmpty(): Boolean { + return Date_Time.isNotEmpty() && Source.isNotEmpty() && Type.isNotEmpty() && Message.isNotEmpty() && SB_TAGS.isNotEmpty() && BroadcastZones.isNotEmpty() && Language.isNotEmpty() + } override fun toString(): String { return "QueueTable(index=$index, Date_Time='$Date_Time', Source='$Source', Type='$Type', Message='$Message', SB_TAGS='$SB_TAGS', BroadcastZones='$BroadcastZones', Repeat=$Repeat, Language='$Language')" } diff --git a/src/database/ScheduleBank.kt b/src/database/ScheduleBank.kt index 300d89e..aa0a6c9 100644 --- a/src/database/ScheduleBank.kt +++ b/src/database/ScheduleBank.kt @@ -10,4 +10,13 @@ data class ScheduleBank( var Repeat: UByte, var Enable: Boolean, var BroadcastZones: String, - var Language: String) + var Language: String) { + + override fun toString(): String { + return "ScheduleBank(index=$index, Description='$Description', Day='$Day', Time='$Time', Soundpath='$Soundpath', Repeat=$Repeat, Enable=$Enable, BroadcastZones='$BroadcastZones', Language='$Language')" + } + + fun isNotEmpty() : Boolean{ + return Description.isNotEmpty() && Day.isNotEmpty() && Time.isNotEmpty() && Soundpath.isNotEmpty() && BroadcastZones.isNotEmpty() && Language.isNotEmpty() + } +} diff --git a/src/database/SoundChannel.kt b/src/database/SoundChannel.kt index 1e95750..6dc3cc3 100644 --- a/src/database/SoundChannel.kt +++ b/src/database/SoundChannel.kt @@ -1,4 +1,17 @@ package database -@Suppress("unused") -data class SoundChannel(val index: UInt, val channel: String, val ip: String) +data class SoundChannel(val index: UInt, val channel: String, val ip: String) { + + + override fun toString(): String { + return "SoundChannel(index=$index, channel='$channel', ip='$ip')" + } + + /** + * Check if both channel and ip are not empty + * @return true if both channel and ip are not empty, false otherwise + */ + fun isNotEmpty() : Boolean { + return channel.isNotEmpty() && ip.isNotEmpty() + } +} diff --git a/src/database/Soundbank.kt b/src/database/Soundbank.kt index 8f38c9f..67366c5 100644 --- a/src/database/Soundbank.kt +++ b/src/database/Soundbank.kt @@ -1,6 +1,5 @@ package database -import content.Category data class Soundbank( var index: UInt, @@ -16,20 +15,7 @@ data class Soundbank( * Check if all fields are not empty */ fun isNotEmpty(): Boolean{ - if (Description.isNotEmpty()){ - if (TAG.isNotEmpty()){ - if (Category.isNotEmpty()){ - if (Language.isNotEmpty()){ - if (VoiceType.isNotEmpty()){ - if (Path.isNotEmpty()){ - return true - } - } - } - } - } - } - return false + return Description.isNotEmpty() && TAG.isNotEmpty() && Category.isNotEmpty() && Language.isNotEmpty() && VoiceType.isNotEmpty() && Path.isNotEmpty() } /** diff --git a/src/database/UserDB.kt b/src/database/UserDB.kt index 991ac35..edc79b3 100644 --- a/src/database/UserDB.kt +++ b/src/database/UserDB.kt @@ -6,6 +6,10 @@ data class UserDB(var index: UInt, var username: String, var password: String, v return "UserDB(index=$index, username='$username', location='$location', airline_tags='$airline_tags', city_tags='$city_tags', messagebank_ann_id='$messagebank_ann_id', broadcastzones='$broadcastzones')" } + fun isNotEmpty() : Boolean { + return username.isNotEmpty() && password.isNotEmpty() && location.isNotEmpty() && airline_tags.isNotEmpty() && city_tags.isNotEmpty() && messagebank_ann_id.isNotEmpty() && broadcastzones.isNotEmpty() + } + /** * Compares this UserDB object with another UserDB object for equality. * Two UserDB objects are considered equal if all their properties are equal, except for the index property. diff --git a/src/web/StreamerOutputData.kt b/src/web/StreamerOutputData.kt new file mode 100644 index 0000000..7294854 --- /dev/null +++ b/src/web/StreamerOutputData.kt @@ -0,0 +1,9 @@ +package web + +class StreamerOutputData(val index: UInt, val channel: String, val ipaddress: String, val vu: Int, val bufferRemain: Int, var isPlaying: Boolean) { + companion object{ + fun fromBarixConnection(bc: barix.BarixConnection): StreamerOutputData { + return StreamerOutputData(bc.index, bc.channel, bc.ipaddress, bc.vu, bc.bufferRemain, bc.isPlaying()) + } + } +} \ No newline at end of file diff --git a/src/web/WebApp.kt b/src/web/WebApp.kt index d58dc0d..ab401d1 100644 --- a/src/web/WebApp.kt +++ b/src/web/WebApp.kt @@ -130,9 +130,9 @@ class WebApp(val listenPort: Int, val userlist: List>) { } "getNetworkStatus" -> { - // TODO Get Network status - Somecodes.GetNetworkStatus("") - SendReply(wsMessageContext, cmd.command, "OK") + Somecodes.GetNetworkStatus { nn -> + SendReply(wsMessageContext, cmd.command, objectmapper.writeValueAsString(nn)) + } } "getPagingQueue" ->{ @@ -144,7 +144,8 @@ class WebApp(val listenPort: Int, val userlist: List>) { } "getStreamerOutputs" -> { - SendReply(wsMessageContext, cmd.command, objectmapper.writeValueAsString(StreamerOutputs.values.toList())) + val reply : List = StreamerOutputs.map { so -> StreamerOutputData.fromBarixConnection(so.value) } + SendReply(wsMessageContext, cmd.command, objectmapper.writeValueAsString(reply)) } else -> { @@ -236,6 +237,23 @@ class WebApp(val listenPort: Int, val userlist: List>) { get("ListFiles") { it.result(objectmapper.writeValueAsString(ListAudioFiles(Somecodes.Soundbank_directory))) } + get("GetPhrases/{Language}/{VoiceType}"){ + val language = it.pathParam("Language") + val voiceType = it.pathParam("VoiceType") + if (ValidString(language) && Language.entries.any { lang -> lang.name == language }) { + if (ValidString(voiceType) && VoiceType.entries.any { vt -> vt.name == voiceType }) { + val phrases = db.soundDB.List + .filter { sb -> sb.Language == language } + .filter {sb -> sb.VoiceType == voiceType} + .filter { sb -> sb.Category == Category.Phrase.name} + .distinctBy { sb -> sb.TAG } + .sortedBy { sb -> sb.TAG } + it.result(objectmapper.writeValueAsString(phrases)) + } else it.status(400) + .result(objectmapper.writeValueAsString(resultMessage("Invalid VoiceType"))) + } else it.status(400) + .result(objectmapper.writeValueAsString(resultMessage("Invalid Language"))) + } get("AirlineTags") { ctx -> val value = db.soundDB.List .filter { it.Category == Category.Airplane_Name.name }