Compare commits
10 Commits
1563e233d6
...
8bd57f216a
| Author | SHA1 | Date | |
|---|---|---|---|
| 8bd57f216a | |||
| 8978a0986d | |||
| bc3f5c5691 | |||
| e5d8d8059e | |||
| 1b84ec133b | |||
| 1a6b7de6ec | |||
| 4da5a2fb05 | |||
| 2ca7004b70 | |||
| 2fe4a46e3e | |||
| 4d3dc538bd |
@@ -121,18 +121,33 @@ $(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();
|
||||
|
||||
359
html/webpage/assets/js/overview.js
Normal file
359
html/webpage/assets/js/overview.js
Normal file
@@ -0,0 +1,359 @@
|
||||
/**
|
||||
* @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<HTMLElement> | null} title - The jQuery result should be <h4> element.
|
||||
* @property {JQuery<HTMLElement> | null} ip - The jQuery result should be <h6> element.
|
||||
* @property {JQuery<HTMLElement> | null} buffer - The jQuery result should be <h6> element.
|
||||
* @property {JQuery<HTMLElement> | null} status - The jQuery result should be <p> element.
|
||||
* @property {JQuery<HTMLElement> | null} vu - The jQuery result should be <progress-bar> element.
|
||||
*/
|
||||
|
||||
function getCardByIndex(index) {
|
||||
let obj = {
|
||||
// title is <h4> 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 <h6> 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 <h6> 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 <p> 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 <progress-bar> 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(index, $bar, value, max = 100) {
|
||||
const v = Number(value ?? 0);
|
||||
const pct = Math.max(0, Math.min(100, Math.round((v / max) * 100)));
|
||||
//if (index!==1) return; // only update index 1 for testing
|
||||
//console.log(`setProgress: index=${index}, value=${v}, pct=${pct}`);
|
||||
$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(i, 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(i, card.vu, 0, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {PagingQueue[]}
|
||||
*/
|
||||
window.PagingQueue = [];
|
||||
/**
|
||||
* @type {JQuery<HTMLElement> | 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<HTMLElement> | 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
|
||||
$('#pagingqueuetable').append(`<tr>
|
||||
<td>${item.index}</td>
|
||||
<td>${item.date_Time}</td>
|
||||
<td>${item.source}</td>
|
||||
<td>${item.type}</td>
|
||||
<td>${item.message}</td>
|
||||
<td>${item.broadcastZones}</td>
|
||||
</tr>`);
|
||||
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
|
||||
//console.log("fill_automaticqueuetablebody: item", item);
|
||||
$('#automaticqueuetable').append(`<tr>
|
||||
<td>${item.index}</td>
|
||||
<td>${item.date_Time}</td>
|
||||
<td>${item.source}</td>
|
||||
<td>${item.type}</td>
|
||||
<td>${item.message}</td>
|
||||
<td>${item.broadcastZones}</td>
|
||||
</tr>`);
|
||||
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 intervaljob1 = null;
|
||||
let intervaljob2 = null;
|
||||
function runIntervalJob() {
|
||||
if (intervaljob1) clearInterval(intervaljob1);
|
||||
intervaljob1 = setInterval(() => {
|
||||
sendCommand("getStreamerOutputs", "");
|
||||
}, 100);
|
||||
if (intervaljob2) clearInterval(intervaljob2);
|
||||
intervaljob2 = setInterval(() => {
|
||||
sendCommand("getPagingQueue", "");
|
||||
sendCommand("getAASQueue", "");
|
||||
}, 2000);
|
||||
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);
|
||||
window.PagingQueue = [];
|
||||
if (Array.isArray(pq) && pq.length > 0) {
|
||||
window.PagingQueue.push(...pq);
|
||||
}
|
||||
fill_pagingqueuetablebody(window.PagingQueue);
|
||||
break;
|
||||
case "getAASQueue":
|
||||
let aq = JSON.parse(data);
|
||||
//console.log("getAASQueue:", aq);
|
||||
window.QueueTable = [];
|
||||
if (Array.isArray(aq) && aq.length > 0) {
|
||||
window.QueueTable.push(...aq);
|
||||
}
|
||||
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(intervaljob1);
|
||||
clearInterval(intervaljob2);
|
||||
intervaljob1 = null;
|
||||
intervaljob2 = null;
|
||||
});
|
||||
});
|
||||
@@ -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'
|
||||
@@ -313,52 +313,81 @@ $(document).ready(function () {
|
||||
getScheduledDays();
|
||||
|
||||
|
||||
// Initialize WebSocket connection
|
||||
window.ws = new WebSocket(wsURL);
|
||||
// reconnect handle
|
||||
let ws_reconnect;
|
||||
|
||||
window.ws.onopen = () => {
|
||||
console.log('WebSocket connection established');
|
||||
$('#onlineindicator').attr('src', window.greencircle);
|
||||
};
|
||||
window.ws.onmessage = (event) => {
|
||||
if ($('#onlineindicator').attr('src') !== window.greencircle) {
|
||||
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", "")
|
||||
|
||||
@@ -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[]}
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
</div>
|
||||
<div class="accordion" role="tablist" id="accordion-1">
|
||||
<div class="accordion-item pad-accordion">
|
||||
<h2 class="accordion-header" role="tab"><button class="accordion-button bg-heading1" type="button" data-bs-toggle="collapse" data-bs-target="#accordion-1 .item-1" aria-expanded="true" aria-controls="accordion-1 .item-1">Channel Status</button></h2>
|
||||
<div class="accordion-collapse collapse show item-1 bg-accordion" role="tabpanel" data-bs-parent="#accordion-1">
|
||||
<h2 class="accordion-header" role="tab"><button class="accordion-button collapsed bg-heading1" type="button" data-bs-toggle="collapse" data-bs-target="#accordion-1 .item-1" aria-expanded="false" aria-controls="accordion-1 .item-1">Channel Status</button></h2>
|
||||
<div class="accordion-collapse collapse item-1 bg-accordion" role="tabpanel" data-bs-parent="#accordion-1">
|
||||
<div class="accordion-body">
|
||||
<div class="row">
|
||||
<div class="col-12 col-sm-6 col-md-3 col-lg-3 col-xl-3 pad-card">
|
||||
@@ -1263,7 +1263,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<p class="card-text" id="streamerstatus64">Status : Idle</p>
|
||||
<div class="progress" id="streamervu-63">
|
||||
<div class="progress" id="streamervu64">
|
||||
<div class="progress-bar" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100" style="width: 50%;">50%</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1274,16 +1274,20 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item pad-accordion pad-2">
|
||||
<h2 class="accordion-header" role="tab"><button class="accordion-button collapsed bg-heading2" type="button" data-bs-toggle="collapse" data-bs-target="#accordion-1 .item-2" aria-expanded="false" aria-controls="accordion-1 .item-2">Paging Queue</button></h2>
|
||||
<div class="accordion-collapse collapse item-2 pad-2" role="tabpanel" data-bs-parent="#accordion-1">
|
||||
<h2 class="accordion-header" role="tab"><button class="accordion-button bg-heading2" type="button" data-bs-toggle="collapse" data-bs-target="#accordion-1 .item-2" aria-expanded="true" aria-controls="accordion-1 .item-2">Paging Queue</button></h2>
|
||||
<div class="accordion-collapse collapse show item-2 pad-2" role="tabpanel" data-bs-parent="#accordion-1">
|
||||
<div class="accordion-body">
|
||||
<div class="row">
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Index</th>
|
||||
<th>Description</th>
|
||||
<th class="col-sm-1">Index</th>
|
||||
<th class="col-sm-2">Date Time</th>
|
||||
<th class="col-sm-1">Source</th>
|
||||
<th class="col-sm-1">Type</th>
|
||||
<th class="col-sm-3">Message</th>
|
||||
<th class="col-sm-4">Broadcast Zones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="pagingqueuetable"></tbody>
|
||||
@@ -1306,8 +1310,12 @@
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Index</th>
|
||||
<th>Description</th>
|
||||
<th class="col-sm-1">Index</th>
|
||||
<th class="col-sm-2">Date Time</th>
|
||||
<th class="col-sm-1">Source</th>
|
||||
<th class="col-sm-1">Type</th>
|
||||
<th class="col-sm-3">Message</th>
|
||||
<th class="col-sm-4">Broadcast Zones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="automaticqueuetable"></tbody>
|
||||
@@ -1324,6 +1332,7 @@
|
||||
</div>
|
||||
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="assets/js/bs-init.js"></script>
|
||||
<script src="assets/js/overview.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -31,7 +31,7 @@ lateinit var audioPlayer: AudioPlayer
|
||||
val StreamerOutputs: MutableMap<String, BarixConnection> = HashMap()
|
||||
lateinit var udpreceiver: UDPReceiver
|
||||
lateinit var tcpreceiver: TCPReceiver
|
||||
const val version = "0.0.6 (14/10/2025)"
|
||||
const val version = "0.0.8 (16/10/2025)"
|
||||
// AAS 64 channels
|
||||
const val max_channel = 64
|
||||
|
||||
@@ -167,7 +167,6 @@ fun main() {
|
||||
|
||||
val barixserver = TCP_Barix_Command_Server()
|
||||
barixserver.StartTcpServer { cmd ->
|
||||
//Logger.info { cmd }
|
||||
val _tcp = barixserver.getSocket(cmd.ipaddress)
|
||||
val _streamer = StreamerOutputs[cmd.ipaddress]
|
||||
val _sc = db.soundchannelDB.List.find { it.ip == cmd.ipaddress }
|
||||
@@ -177,7 +176,8 @@ fun main() {
|
||||
if (_sc != null) {
|
||||
|
||||
val _bc = BarixConnection(_sc.index, _sc.channel, cmd.ipaddress)
|
||||
_bc.vu = cmd.vu
|
||||
// cmd.vu 0 - 32767, kita convert ke 0 - 100
|
||||
_bc.vu = ((1.0 * cmd.vu / 32767.0)* 100.0).toInt()
|
||||
_bc.bufferRemain = cmd.buffremain
|
||||
_bc.statusData = cmd.statusdata
|
||||
_bc.commandsocket = _tcp
|
||||
@@ -191,7 +191,8 @@ fun main() {
|
||||
if (_sc != null && _sc.channel != _streamer.channel) {
|
||||
_streamer.channel = _sc.channel
|
||||
}
|
||||
_streamer.vu = cmd.vu
|
||||
// cmd.vu 0 - 32767, kita convert ke 0 - 100
|
||||
_streamer.vu = ((1.0 * cmd.vu / 32767.0)* 100.0).toInt()
|
||||
_streamer.bufferRemain = cmd.buffremain
|
||||
_streamer.statusData = cmd.statusdata
|
||||
|
||||
|
||||
@@ -561,7 +561,7 @@ class MainExtension01 {
|
||||
.filter { it.Type=="PAGING" }
|
||||
|
||||
list.forEach { qp ->
|
||||
println("Processing $qp")
|
||||
//println("Processing $qp")
|
||||
if (qp.BroadcastZones.isNotBlank()){
|
||||
if (ValidFile(qp.Message)){
|
||||
val zz = qp.BroadcastZones.split(";")
|
||||
@@ -883,7 +883,7 @@ class MainExtension01 {
|
||||
db.queuetableDB.Get()
|
||||
val list = db.queuetableDB.List.filter { it.Type=="SOUNDBANK" }
|
||||
list.forEach { qa ->
|
||||
println("Processing $qa")
|
||||
//println("Processing $qa")
|
||||
if (qa.BroadcastZones.isNotEmpty()){
|
||||
val zz = qa.BroadcastZones.split(";")
|
||||
if (AllBroadcastZonesValid(zz)){
|
||||
|
||||
@@ -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<String, NetworkInformation>()
|
||||
|
||||
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<List<NetworkInformation>>) {
|
||||
val networks: List<NetworkIF> = 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())
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -69,7 +69,7 @@ class TCP_Android_Command_Server {
|
||||
din.read(bb)
|
||||
// B4A format, 4 bytes di depan adalah size
|
||||
val str = String(bb, 4, bb.size - 4)
|
||||
println("Received command from $key : $str")
|
||||
//println("Received command from $key : $str")
|
||||
str.split("@").map { it.trim() }.filter { ValidString(it) }
|
||||
.forEach {
|
||||
process_command(key,it) { reply ->
|
||||
@@ -308,7 +308,7 @@ class TCP_Android_Command_Server {
|
||||
if (userlogin != null){
|
||||
val userdb = db.userDB.List.find { it.username == username }
|
||||
if (userdb != null){
|
||||
println("Sending initialization data to $key with username $username")
|
||||
//println("Sending initialization data to $key with username $username")
|
||||
val result = StringBuilder()
|
||||
// kirim Zone
|
||||
result.append("ZONE")
|
||||
|
||||
15
src/content/NetworkInformation.kt
Normal file
15
src/content/NetworkInformation.kt
Normal file
@@ -0,0 +1,15 @@
|
||||
package content
|
||||
|
||||
class NetworkInformation(val name: String, val displayName: String, val macAddress: String) {
|
||||
var ipV4addr: MutableList<String> = mutableListOf()
|
||||
var ipV6addr: MutableList<String> = 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
|
||||
|
||||
}
|
||||
@@ -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')"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
package database
|
||||
|
||||
data class BroadcastZonesHtml(var index: UInt, var description: String, var SoundChannel: String, var Box: String, var Relay: String)
|
||||
@@ -1,4 +0,0 @@
|
||||
package database
|
||||
|
||||
@Suppress("unused")
|
||||
data class IpZones(var index:UInt, var description: String, var ip: String)
|
||||
@@ -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')"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1411,9 +1411,9 @@ class MariaDB(
|
||||
// use a temporary table to reorder the index
|
||||
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}")
|
||||
statement?.executeUpdate("TRUNCATE TABLE $tempdb_name")
|
||||
statement?.executeUpdate("INSERT INTO $tempdb_name (Date_Time, Source, Type, Message, SB_TAGS) SELECT Date_Time, Source, Type, Message, SB_TAGS FROM ${super.dbName} ORDER BY `index` ")
|
||||
statement?.executeUpdate("INSERT INTO $tempdb_name (Date_Time, Source, Type, Message, BroadcastZones) SELECT Date_Time, Source, Type, Message, BroadcastZones FROM ${super.dbName} ")
|
||||
statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}")
|
||||
statement?.executeUpdate("INSERT INTO ${super.dbName} (Date_Time, Source, Type, Message, SB_TAGS) SELECT Date_Time, Source, Type, Message, SB_TAGS FROM $tempdb_name")
|
||||
statement?.executeUpdate("INSERT INTO ${super.dbName} (Date_Time, Source, Type, Message, BroadcastZones) SELECT Date_Time, Source, Type, Message, BroadcastZones FROM $tempdb_name")
|
||||
statement?.executeUpdate("DROP TABLE $tempdb_name")
|
||||
Logger.info("${super.dbName} table resorted by index" as Any)
|
||||
// reload the local list
|
||||
@@ -1436,7 +1436,7 @@ class MariaDB(
|
||||
val workbook = XSSFWorkbook()
|
||||
val sheet = workbook.createSheet("QueuePaging")
|
||||
val headerRow = sheet.createRow(0)
|
||||
val headers = arrayOf("Index", "Date_Time", "Source", "Type", "Message", "SB_TAGS")
|
||||
val headers = arrayOf("Index", "Date_Time", "Source", "Type", "Message", "BroadcastZones")
|
||||
for ((colIndex, header) in headers.withIndex()) {
|
||||
val cell = headerRow.createCell(colIndex)
|
||||
cell.setCellValue(header)
|
||||
@@ -1449,7 +1449,7 @@ class MariaDB(
|
||||
row.createCell(2).setCellValue(resultSet.getString("Source"))
|
||||
row.createCell(3).setCellValue(resultSet.getString("Type"))
|
||||
row.createCell(4).setCellValue(resultSet.getString("Message"))
|
||||
row.createCell(5).setCellValue(resultSet.getString("SB_TAGS"))
|
||||
row.createCell(5).setCellValue(resultSet.getString("BroadcastZones"))
|
||||
}
|
||||
for (i in headers.indices) {
|
||||
sheet.autoSizeColumn(i)
|
||||
|
||||
@@ -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')"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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')"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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')"
|
||||
}
|
||||
|
||||
@@ -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')"
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.
|
||||
|
||||
9
src/web/StreamerOutputData.kt
Normal file
9
src/web/StreamerOutputData.kt
Normal file
@@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,7 +88,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
||||
}
|
||||
// Set user session
|
||||
it.sessionAttribute("user", user.first)
|
||||
println("User ${user.first} logged in")
|
||||
//println("User ${user.first} logged in")
|
||||
// Redirect to home page
|
||||
it.redirect("home.html")
|
||||
}
|
||||
@@ -98,10 +98,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
||||
before { CheckUsers(it) }
|
||||
ws("/ws") { ws ->
|
||||
// WebSocket endpoint for home
|
||||
ws.onClose { wsCloseContext ->
|
||||
// TODO Handle WebSocket close event
|
||||
println("WebSocket closed: ${wsCloseContext.session.remoteAddress}")
|
||||
}
|
||||
//
|
||||
ws.onMessage { wsMessageContext ->
|
||||
try {
|
||||
val cmd =
|
||||
@@ -130,9 +127,9 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
||||
}
|
||||
|
||||
"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 +141,8 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
||||
}
|
||||
|
||||
"getStreamerOutputs" -> {
|
||||
SendReply(wsMessageContext, cmd.command, objectmapper.writeValueAsString(StreamerOutputs.values.toList()))
|
||||
val reply : List<StreamerOutputData> = StreamerOutputs.map { so -> StreamerOutputData.fromBarixConnection(so.value) }
|
||||
SendReply(wsMessageContext, cmd.command, objectmapper.writeValueAsString(reply))
|
||||
}
|
||||
|
||||
else -> {
|
||||
@@ -156,10 +154,9 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
||||
}
|
||||
|
||||
}
|
||||
ws.onConnect { wsConnectContext ->
|
||||
// TODO Handle WebSocket connect event
|
||||
println("WebSocket connected: ${wsConnectContext.session.remoteAddress}")
|
||||
}
|
||||
// ws.onConnect { wsConnectContext ->
|
||||
// println("WebSocket connected: ${wsConnectContext.session.remoteAddress}")
|
||||
// }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -236,6 +233,23 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
||||
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 }
|
||||
@@ -573,7 +587,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
||||
val json: JsonNode = objectmapper.readTree(it.body())
|
||||
val tag = json.get("tag").asText("")
|
||||
val languages = json.get("language").asText("").split(";")
|
||||
println("Add Language Link, tag=$tag, languages=$languages")
|
||||
//println("Add Language Link, tag=$tag, languages=$languages")
|
||||
if (ValidString(tag)){
|
||||
if (languages.all { xx -> Language.entries.any { yy -> yy.name.equals(xx,true)} }){
|
||||
if (!db.languageDB.List.any { ll -> ll.TAG.equals(tag,true) }) {
|
||||
@@ -583,11 +597,11 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
||||
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
|
||||
} else {
|
||||
it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to add language link to database")))
|
||||
println("Failed to add language link to database")
|
||||
//println("Failed to add language link to database")
|
||||
}
|
||||
} else {
|
||||
it.status(400).result(objectmapper.writeValueAsString(resultMessage("TAG=$tag already exists")))
|
||||
println("TAG=$tag already exists")
|
||||
//println("TAG=$tag already exists")
|
||||
}
|
||||
} else {
|
||||
it.status(400).result(objectmapper.writeValueAsString(resultMessage("Contains unsupported language")))
|
||||
@@ -721,7 +735,8 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
||||
if (ValidString(description)){
|
||||
if (ValidString(day) && ValidScheduleDay(day)){
|
||||
if (ValidString(time) && ValidScheduleTime(time)){
|
||||
if (ValidString(soundpath) && ValidFile(soundpath)){
|
||||
//soundpath is a messagebank desc and aan_id
|
||||
if (ValidString(soundpath)) {
|
||||
if (repeat in 0u..127u){
|
||||
if (ValidString(broadcast_zones)){
|
||||
val zones = broadcast_zones.split(";")
|
||||
@@ -886,8 +901,11 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
||||
//TODO add at js file for messagebank and broadcast zones values
|
||||
get("GetMessageAndBroadcastZones") {
|
||||
val result = object {
|
||||
val messages = db.messageDB.List.filter { mb -> !mb.Message_Detail.contains("[") && !mb.Message_Detail.contains("]") }
|
||||
val broadcastzones = db.broadcastDB.List
|
||||
val messages = db.messageDB.List
|
||||
.filter { mb -> !mb.Message_Detail.contains("[") && !mb.Message_Detail.contains("]")}
|
||||
.map { mb -> "${mb.Description} [${mb.ANN_ID}]" }
|
||||
val broadcastzones = db.broadcastDB.List.map { it.description }
|
||||
|
||||
}
|
||||
it.result(objectmapper.writeValueAsString(result))
|
||||
}
|
||||
@@ -895,7 +913,9 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
||||
// Kirim list language dari Messagebank berdasarkan ANN_ID
|
||||
get("GetLanguageList/{ANN_ID}") { get1 ->
|
||||
//kirim list language dari Messagebank
|
||||
val langlist = db.messageDB.List.filter { it.ANN_ID == get1.pathParam("ANN_ID").toInt().toUInt() }.map { it.Language }.distinct()
|
||||
val langlist = db.messageDB.List
|
||||
.filter { it.ANN_ID == get1.pathParam("ANN_ID").toInt().toUInt() }
|
||||
.map { it.Language }.distinct()
|
||||
get1.result(objectmapper.writeValueAsString(langlist))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user