commit 26/08/2025

This commit is contained in:
2025-08-26 08:24:31 +07:00
parent 0c84449b77
commit 4743b47a89
16 changed files with 534 additions and 77 deletions

13
.idea/deviceManager.xml generated Normal file
View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DeviceTable">
<option name="columnSorters">
<list>
<ColumnSorterState>
<option name="column" value="Name" />
<option name="order" value="ASCENDING" />
</ColumnSorterState>
</list>
</option>
</component>
</project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -1,5 +1,104 @@
$(document).ready(function() { $(document).ready(function () {
document.title = "Automatic Announcement System" document.title = "Automatic Announcement System"
$('#onlineindicator').attr('src', '/assets/img/red_circle.png');
let soundbankdata = [];
let messagebankdata = [];
let languagebankdata = [];
let schedulebankdata = [];
let logdata = [];
/**
* Fill soundbank table body with values
* @param {SoundBank[]} vv values to fill
*/
function fill_soundbanktablebody(vv) {
$('#soundbanktablebody').empty();
vv.forEach(item => {
const row = `<tr>
<td>${item.index}</td>
<td>${item.description}</td>
<td>${item.tag}</td>
<td>${item.category}</td>
<td>${item.language}</td>
<td>${item.voiceType}</td>
<td>${item.path}</td>
</tr>`;
$('#soundbanktablebody').append(row);
});
$('#tablesize').text("Table Size: " + vv.length);
}
/**
* Fill messagebank table body with values
* @param {MessageBank[]} vv values to fill
*/
function fill_messagebanktablebody(vv) {
$('#messagebanktablebody').empty();
vv.forEach(item => {
const row = `<tr>
<td>${item.index}</td>
<td>${item.description}</td>
<td>${item.language}</td>
<td>${item.aNN_ID}</td>
<td>${item.voice_Type}</td>
<td>${item.message_Detail}</td>
<td>${item.message_TAGS}</td>
</tr>`;
$('#messagebanktablebody').append(row);
});
console.log("loaded " + vv.length + " messagebank items");
}
function fill_languagebanktablebody(vv) {
$('#languagebanktablebody').empty();
vv.forEach(item => {
//TODO examine JSON structure
const row = `<tr>
<td>${item.index}</td>
<td>${item.description}</td>
<td>${item.language}</td>
<td>${item.aNN_ID}</td>
<td>${item.voice_Type}</td>
<td>${item.message_Detail}</td>
<td>${item.message_TAGS}</td>
</tr>`;
$('#languagebanktablebody').append(row);
});
console.log("loaded " + vv.length + " languagebank items");
}
function fill_schedulebanktablebody(vv) {
$('#schedulebanktablebody').empty();
//TODO examine JSON structure
vv.forEach(item => {
const row = `<tr>
<td>${item.index}</td>
<td>${item.description}</td>
<td>${item.language}</td>
<td>${item.aNN_ID}</td>
<td>${item.voice_Type}</td>
<td>${item.message_Detail}</td>
<td>${item.message_TAGS}</td>
</tr>`;
$('#schedulebanktablebody').append(row);
});
console.log("loaded " + vv.length + " schedulebank items");
}
function fill_logtablebody(vv) {
$('#logtablebody').empty();
vv.forEach(item => {
const row = `<tr>
<td>${item.index}</td>
<td>${item.date}</td>
<td>${item.time}</td>
<td>${item.description}</td>
</tr>`;
$('#logtablebody').append(row);
});
console.log("loaded " + vv.length + " log items");
}
const ws = new WebSocket(window.location.pathname + '/ws'); const ws = new WebSocket(window.location.pathname + '/ws');
@@ -7,32 +106,104 @@ $(document).ready(function() {
ws.onopen = () => { ws.onopen = () => {
console.log('WebSocket connection established'); console.log('WebSocket connection established');
$('#onlineindicator').attr('src', '/assets/img/green_circle.png');
}; };
ws.onmessage = (event) => { ws.onmessage = (event) => {
let rep = JSON.parse(event.data); let rep = JSON.parse(event.data);
let cmd = rep.reply let cmd = rep.reply
let data = rep.data; let data = rep.data;
if (cmd && cmd.length > 0){ if (cmd && cmd.length > 0) {
// console.log('Command:', cmd); switch (cmd) {
// console.log('Data:', data); case "getCPUStatus":
switch(cmd){
case "getCPUStatus" :
$('#cpustatus').text("CPU Usage: " + data) $('#cpustatus').text("CPU Usage: " + data)
break; break;
case "getMemoryStatus" : case "getMemoryStatus":
$('#ramstatus').text("Memory Usage: " + data) $('#ramstatus').text("Memory Usage: " + data)
break; break;
case "getDiskStatus" : case "getDiskStatus":
$('#diskstatus').text("Disk Usage: " + data) $('#diskstatus').text("Disk Usage: " + data)
break; break;
case "getNetworkStatus" : case "getNetworkStatus":
$('#networkstatus').text("Network Usage: " + data) $('#networkstatus').text("Network Usage: " + data)
break; break;
case "getSystemTime":
$('#datetimetext').text(data)
break;
case "getSoundBankList":
let $soundbankfilter = $('#findsoundbank');
$soundbankfilter.empty();
soundbankdata = [];
fill_soundbanktablebody(soundbankdata)
let xx = JSON.parse(data);
if (Array.isArray(xx) && xx.length > 0) {
soundbankdata = xx;
fill_soundbanktablebody(soundbankdata);
$soundbankfilter.prop('disabled', false);
$soundbankfilter.off('input').on('input', function () {
const filterText = $(this).val().toLowerCase();
if (filterText.length === 0) {
fill_soundbanktablebody(soundbankdata);
return;
} else {
const filtered = soundbankdata.filter(item =>
item.description && item.description.toLowerCase().startsWith(filterText)
);
fill_soundbanktablebody(filtered);
}
});
} else {
$soundbankfilter.prop('disabled', true);
alert("No soundbank data available");
}
break;
case "getMessageBankList":
messagebankdata = [];
fill_messagebanktablebody(messagebankdata);
let yy = JSON.parse(data);
if (Array.isArray(yy) && yy.length > 0) {
messagebankdata = yy;
fill_messagebanktablebody(messagebankdata);
} else alert("No messagebank data available");
break;
case "getLanguageList":
languagebankdata = []
fill_languagebanktablebody(languagebankdata);
let zz = JSON.parse(data);
if (Array.isArray(zz) && zz.length > 0) {
languagebankdata = zz;
fill_languagebanktablebody(languagebankdata);
} else alert("No language data available");
break;
case "getTimerList":
schedulebankdata = []
fill_schedulebanktablebody(schedulebankdata);
let aa = JSON.parse(data);
if (Array.isArray(aa) && aa.length > 0) {
schedulebankdata = aa;
fill_schedulebanktablebody(schedulebankdata);
} else alert("No schedule data available");
break;
case "getLog":
let $logfilter = $('#searchfilter');
logdata = [];
fill_logtablebody(logdata);
let bb = JSON.parse(data);
if (Array.isArray(bb) && bb.length > 0) {
logdata = bb;
fill_logtablebody(logdata);
} else alert("No log data available");
break;
case "getSetting":
console.log("Setting:");
console.log(data);
break;
} }
} }
}; };
ws.onclose = () => { ws.onclose = () => {
console.log('WebSocket connection closed'); console.log('WebSocket connection closed');
$('#onlineindicator').attr('src', '/assets/img/red_circle.png');
}; };
// ws.onerror = (error) => { // ws.onerror = (error) => {
// console.error('WebSocket error:', error); // console.error('WebSocket error:', error);
@@ -43,7 +214,7 @@ $(document).ready(function() {
* @param {String} command command to send * @param {String} command command to send
* @param {String} data data to send * @param {String} data data to send
*/ */
function sendCommand(command, data){ function sendCommand(command, data) {
if (ws.readyState === WebSocket.OPEN) { if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ command, data })); ws.send(JSON.stringify({ command, data }));
} else { } else {
@@ -51,43 +222,133 @@ $(document).ready(function() {
} }
} }
setInterval(()=>{ setInterval(() => {
$('#datetimetext').text(new Date().toLocaleString());
sendCommand("getCPUStatus", "") sendCommand("getCPUStatus", "")
sendCommand("getMemoryStatus", "") sendCommand("getMemoryStatus", "")
sendCommand("getDiskStatus", "") sendCommand("getDiskStatus", "")
sendCommand("getNetworkStatus", "") sendCommand("getNetworkStatus", "")
sendCommand("getSystemTime", "")
}, 1000) }, 1000)
let sidemenu = new bootstrap.Offcanvas('#offcanvas-menu'); let sidemenu = new bootstrap.Offcanvas('#offcanvas-menu');
$('#showmenu').click(()=>{ $('#showmenu').click(() => {
sidemenu.show(); sidemenu.show();
}) })
$('#soundbanklink').click(()=>{ $('#soundbanklink').click(() => {
sidemenu.hide(); sidemenu.hide();
$('#content').load('soundbank.html'); $('#content').load('soundbank.html', function (response, status, xhr) {
if (status === "success") {
console.log("Soundbank content loaded successfully");
sendCommand("getSoundBankList", "");
} else {
console.error("Error loading soundbank content:", xhr.status, xhr.statusText);
}
});
}) })
$('#messagebanklink').click(()=>{ $('#messagebanklink').click(() => {
sidemenu.hide(); sidemenu.hide();
$('#content').load('messagebank.html'); $('#content').load('messagebank.html', function (response, status, xhr) {
if (status === "success") {
console.log("Messagebank content loaded successfully");
sendCommand("getMessageBankList", "");
} else {
console.error("Error loading messagebank content:", xhr.status, xhr.statusText);
}
});
}) })
$('#languagelink').click(()=>{ $('#languagelink').click(() => {
sidemenu.hide(); sidemenu.hide();
$('#content').load('language.html'); $('#content').load('language.html', function (response, status, xhr) {
if (status === "success") {
console.log("Language content loaded successfully");
sendCommand("getLanguageList", "");
} else {
console.error("Error loading language content:", xhr.status, xhr.statusText);
}
});
}) })
$('#timerlink').click(()=>{ $('#timerlink').click(() => {
sidemenu.hide(); sidemenu.hide();
$('#content').load('timer.html'); $('#content').load('timer.html', function (response, status, xhr) {
if (status === "success") {
console.log("Timer content loaded successfully");
sendCommand("getTimerList", "");
} else {
console.error("Error loading timer content:", xhr.status, xhr.statusText);
}
});
}) })
$('#loglink').click(()=>{ $('#loglink').click(() => {
sidemenu.hide(); sidemenu.hide();
$('#content').load('log.html'); $('#content').load('log.html', function (response, status, xhr) {
if (status === "success") {
console.log("Log content loaded successfully");
const $logdate = $('#logdate');
const $searchfilter = $('#searchfilter');
if (!$logdate.val()) {
const today = new Date();
const dd = String(today.getDate()).padStart(2, '0');
const mm = String(today.getMonth() + 1).padStart(2, '0');
const yyyy = today.getFullYear();
$logdate.val(`${yyyy}-${mm}-${dd}`);
}
$logdate.off('change').on('change', function () {
const selected = $(this).val();
if (selected) {
const [year, month, day] = selected.split('-');
const formatted = `${day}/${month}/${year}`;
sendCommand("getLog", logRequstData(formatted, $searchfilter.val()));
}
});
const selected = $logdate.val();
if (selected) {
const [year, month, day] = selected.split('-');
const formatted = `${day}/${month}/${year}`;
sendCommand("getLog", logRequstData(formatted, $searchfilter.val()));
}
} else {
console.error("Error loading log content:", xhr.status, xhr.statusText);
}
});
}) })
$('#settinglink').click(()=>{ $('#settinglink').click(() => {
sidemenu.hide(); sidemenu.hide();
$('#content').load('setting.html'); $('#content').load('setting.html', function (response, status, xhr) {
if (status === "success") {
console.log("Setting content loaded successfully");
sendCommand("getSetting", "");
} else {
console.error("Error loading setting content:", xhr.status, xhr.statusText);
}
});
}) })
$('#logoutlink').click(()=>{ $('#logoutlink').click(() => {
window.location.href = "login.html" window.location.href = "login.html"
}) })
/**
* Create log Request Data
* @param {String} logdate in format dd/mm/yyyy
* @param {String} logfilter
* @returns JSON string of log Request data
*/
function logRequstData(logdate, logfilter) {
if (logdate && logdate.length > 0) {
const dateRegex = /^(0[1-9]|[12][0-9]|3[01])\/(0[1-9]|1[0-2])\/\d{4}$/;
if (dateRegex.test(logdate)) {
// logdate is valid
return JSON.stringify({
date: logdate,
filter: logfilter
})
}
}
return ""
}
}); });

View File

@@ -60,7 +60,15 @@
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-1 border border-dark"><button class="btn btn-primary w-100 h-100" id="showmenu" type="button">Open Menu</button></div> <div class="col-2 border border-dark">
<div class="row">
<div class="col"><button class="btn btn-primary h-100" id="showmenu" type="button"><svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 24 24" width="1em" fill="currentColor" style="width: 32px;height: 32px;">
<path d="M0 0h24v24H0z" fill="none"></path>
<path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"></path>
</svg></button></div>
<div class="col"><img class="img-fluid" id="onlineindicator" src="assets/img/red_circle.png" width="32px" height="32px"></div>
</div>
</div>
<div class="col border border-dark"> <div class="col border border-dark">
<p class="w-100 h-100" id="cpustatus">CPU Status</p> <p class="w-100 h-100" id="cpustatus">CPU Status</p>
</div> </div>

View File

@@ -29,7 +29,7 @@
<th class="col-sm-3">Filename</th> <th class="col-sm-3">Filename</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody id="languagebanktablebody">
<tr> <tr>
<td>Cell 1</td> <td>Cell 1</td>
<td>Cell 2</td> <td>Cell 2</td>

View File

@@ -44,7 +44,7 @@
<th>Description</th> <th>Description</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody id="logtablebody">
<tr> <tr>
<td>Cell 1</td> <td>Cell 1</td>
<td>Cell 2</td> <td>Cell 2</td>

View File

@@ -29,7 +29,7 @@
<th class="col-sm-3">Message Tags</th> <th class="col-sm-3">Message Tags</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody id="messagebanktablebody">
<tr> <tr>
<td>Cell 1</td> <td>Cell 1</td>
<td>Cell 2</td> <td>Cell 2</td>

View File

@@ -15,6 +15,25 @@
<h1 style="text-align: center;">Sound Bank</h1> <h1 style="text-align: center;">Sound Bank</h1>
</div> </div>
</div> </div>
<div class="row">
<div class="col-2">
<p>Search</p>
</div>
<div class="col"><input class="w-100" type="text" id="findsoundbank" placeholder="Search keyword" name="findsoundbank"></div>
</div>
<div class="row">
<div class="col">
<p id="tablesize" style="text-align: center;">Table Length : N/A</p>
</div>
</div>
<div class="row">
<div class="col"><button class="btn btn-primary w-100" id="btnClear" type="button">Clear</button></div>
<div class="col"><button class="btn btn-primary w-100" id="btnAdd" type="button">Add</button></div>
<div class="col"><button class="btn btn-primary w-100" id="btnRemove" type="button">Remove</button></div>
<div class="col"><button class="btn btn-primary w-100" id="btnEdit" type="button">Edit</button></div>
<div class="col"><button class="btn btn-primary w-100" id="btnExport" type="button">Export</button></div>
<div class="col"><button class="btn btn-primary w-100" id="btnImport" type="button">Import</button></div>
</div>
<div class="row"> <div class="row">
<div class="table-responsive"> <div class="table-responsive">
<table class="table"> <table class="table">
@@ -29,7 +48,7 @@
<th class="col-sm-3">Filename</th> <th class="col-sm-3">Filename</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody id="soundbanktablebody">
<tr> <tr>
<td>Cell 1</td> <td>Cell 1</td>
<td>Cell 2</td> <td>Cell 2</td>
@@ -52,20 +71,6 @@
</table> </table>
</div> </div>
</div> </div>
<div class="row">
<div class="col-2">
<p>Search</p>
</div>
<div class="col"><input class="w-100" type="text" id="findsoundbank" placeholder="Search keyword" name="findsoundbank"></div>
</div>
<div class="row">
<div class="col"><button class="btn btn-primary w-100" id="btnClear" type="button">Clear</button></div>
<div class="col"><button class="btn btn-primary w-100" id="btnAdd" type="button">Add</button></div>
<div class="col"><button class="btn btn-primary w-100" id="btnRemove" type="button">Remove</button></div>
<div class="col"><button class="btn btn-primary w-100" id="btnEdit" type="button">Edit</button></div>
<div class="col"><button class="btn btn-primary w-100" id="btnExport" type="button">Export</button></div>
<div class="col"><button class="btn btn-primary w-100" id="btnImport" type="button">Import</button></div>
</div>
<script src="assets/bootstrap/js/bootstrap.min.js"></script> <script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/jquery-3.7.1.min.js"></script> <script src="assets/js/jquery-3.7.1.min.js"></script>
</body> </body>

View File

@@ -29,7 +29,7 @@
<th class="col-sm-3">Filename</th> <th class="col-sm-3">Filename</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody id="schedulebanktablebody">
<tr> <tr>
<td>Cell 1</td> <td>Cell 1</td>
<td>Cell 2</td> <td>Cell 2</td>

View File

@@ -1,20 +1,31 @@
import audio.AudioPlayer import audio.AudioPlayer
import com.sun.jna.Platform
import content.ContentCache import content.ContentCache
import database.MariaDB
import org.tinylog.Logger import org.tinylog.Logger
import oshi.util.GlobalConfig
import web.WebApp import web.WebApp
fun main() { fun main() {
if (Platform.isWindows()){
// supaya OSHI bisa mendapatkan CPU usage di Windows seperti di Task Manager
GlobalConfig.set(GlobalConfig.OSHI_OS_WINDOWS_CPU_UTILITY,true)
}
Logger.info("Application started" as Any) Logger.info("Application started" as Any)
val audioPlayer = AudioPlayer(44100) // 44100 Hz sampling rate val audioPlayer = AudioPlayer(44100) // 44100 Hz sampling rate
audioPlayer.InitAudio(1) audioPlayer.InitAudio(1)
val content = ContentCache() val content = ContentCache()
val db = MariaDB()
val web = WebApp(3030, val web = WebApp(3030,
listOf( listOf(
Pair("admin", "password"), Pair("admin", "password"),
Pair("user", "password") Pair("user", "password")
) ), db
) )
web.Start() web.Start()
} }

View File

@@ -1,20 +1,26 @@
package codes package codes
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import oshi.SystemInfo import oshi.SystemInfo
import oshi.hardware.CentralProcessor import oshi.hardware.CentralProcessor
import oshi.hardware.GlobalMemory import oshi.hardware.GlobalMemory
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.time.format.DateTimeFormatter
import java.util.function.Consumer
@Suppress("unused") @Suppress("unused")
class Somecodes { class Somecodes {
companion object { companion object {
val current_directory : String = System.getProperty("user.dir")
val si = SystemInfo() val si = SystemInfo()
val processor: CentralProcessor = si.hardware.processor val processor: CentralProcessor = si.hardware.processor
val memory : GlobalMemory = si.hardware.memory val memory : GlobalMemory = si.hardware.memory
val datetimeformat1: DateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss")
const val KB_threshold = 1024.0 const val KB_threshold = 1024.0
const val MB_threshold = KB_threshold * 1024.0 const val MB_threshold = KB_threshold * 1024.0
@@ -36,8 +42,59 @@ class Somecodes {
} }
} }
/**
* Get Disk usage using OSHI library.
* @param path The path to check disk usage, defaults to the current working directory.
* @return A string representing the disk usage of the file system in a human-readable format.
*/
fun getDiskUsage(path: String = current_directory) : String {
return try{
val p = Path.of(path).toFile()
if (p.exists() && p.isDirectory){
val total = p.totalSpace
val free = p.freeSpace
val used = total - free
String.format("Total: %s, Used: %s, Free: %s, Usage: %.2f%%",
SizetoHuman(total),
SizetoHuman(used),
SizetoHuman(free),
(used.toDouble() / total * 100)
)
} else throw Exception()
} catch (_ : Exception){
"N/A"
}
}
fun getCPUUsage(cb : Consumer<String>){
CoroutineScope(Dispatchers.Default).launch {
val prev = processor.systemCpuLoadTicks
delay(1000)
val current = processor.systemCpuLoadTicks
fun delta(t: CentralProcessor.TickType) = current[t.index] - prev[t.index]
val idle = delta(CentralProcessor.TickType.IDLE) + delta(CentralProcessor.TickType.IOWAIT)
val busy = delta(CentralProcessor.TickType.USER) + delta(CentralProcessor.TickType.SYSTEM) +
delta(CentralProcessor.TickType.NICE) + delta(CentralProcessor.TickType.IRQ) +
delta(CentralProcessor.TickType.SOFTIRQ)+ delta(CentralProcessor.TickType.STEAL)
val total = idle + busy
val usage = if (total > 0) {
(busy.toDouble() / total) * 100
} else {
0.0
}
cb.accept(String.format("%.2f%%", usage))
}
}
/**
* Get RAM usage using OSHI library.
* @return A string representing the total, used, and available memory in a human-readable format.
*/
fun getMemoryUsage() : String{ fun getMemoryUsage() : String{
val totalMemory = memory.total val totalMemory = memory.total
val availableMemory = memory.available val availableMemory = memory.available

View File

@@ -0,0 +1,4 @@
package database
@Suppress("unused")
data class LanguageLink(val index: UInt, val TAG: String, val Language: String)

View File

@@ -1,5 +1,6 @@
package database package database
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@@ -29,6 +30,8 @@ class MariaDB (
var connected : Boolean = false var connected : Boolean = false
var SoundbankList : ArrayList<Soundbank> = ArrayList() var SoundbankList : ArrayList<Soundbank> = ArrayList()
var MessagebankList : ArrayList<Messagebank> = ArrayList() var MessagebankList : ArrayList<Messagebank> = ArrayList()
var LanguageLinkList : ArrayList<LanguageLink> = ArrayList()
var SchedulebankList : ArrayList<ScheduleBank> = ArrayList()
companion object { companion object {
fun ValidDate(date: String): Boolean { fun ValidDate(date: String): Boolean {
@@ -41,6 +44,22 @@ class MariaDB (
val regex = Regex("""^\d{2}:\d{2}:\d{2}$""") val regex = Regex("""^\d{2}:\d{2}:\d{2}$""")
return regex.matches(time) return regex.matches(time)
} }
private val objectMapper = jacksonObjectMapper()
/**
* Convert SoundbankList, MessagebankList, LanguageLinkList, or SchedulebankList to a JSON String.
* @param list The ArrayList to convert to a String.
* @return A JSON String representation of the ArrayList.
*/
fun <T> ArrayListtoString(list: ArrayList<T>) : String{
return try {
objectMapper.writeValueAsString(list.toArray())
} catch (e: Exception) {
Logger.error("Error converting list to JSON: ${e.message}" as Any)
"[]"
}
}
} }
init { init {
@@ -52,9 +71,9 @@ class MariaDB (
runBlocking { runBlocking {
withContext(Dispatchers.IO){ withContext(Dispatchers.IO){
Reload_Messagebank() Reload_Messagebank()
Logger.info { "Messagebank loaded" }
Reload_Soundbank() Reload_Soundbank()
Logger.info { "Soundbank loaded" } Reload_LanguageLink()
Reload_Schedulebank()
} }
} }
@@ -63,6 +82,8 @@ class MariaDB (
Logger.info { "Loading MariaDB completed" } Logger.info { "Loading MariaDB completed" }
Logger.info { "Soundbank count: ${SoundbankList.size}" } Logger.info { "Soundbank count: ${SoundbankList.size}" }
Logger.info { "Messagebank count: ${MessagebankList.size}" } Logger.info { "Messagebank count: ${MessagebankList.size}" }
Logger.info { "LanguageLink count: ${LanguageLinkList.size}" }
Logger.info { "Schedulebank count: ${SchedulebankList.size}" }
} catch (e : Exception) { } catch (e : Exception) {
@@ -165,6 +186,54 @@ class MariaDB (
consumer.accept(logList) consumer.accept(logList)
} }
/**
* Reloads the ScheduleBank list from the database.
*/
private fun Reload_Schedulebank() {
SchedulebankList.clear()
try {
val statement = connection?.createStatement()
val resultSet = statement?.executeQuery("SELECT * FROM schedulebank")
while (resultSet?.next() == true) {
val schedulebank = ScheduleBank(
resultSet.getLong("index").toUInt(),
resultSet.getString("Description"),
resultSet.getString("Day"),
resultSet.getString("Time"),
resultSet.getString("Soundpath"),
resultSet.getInt("Repeat").toUByte(),
resultSet.getBoolean("Enable"),
resultSet.getString("BroadcastZones"),
resultSet.getString("Language")
)
SchedulebankList.add(schedulebank)
}
} catch (e : Exception) {
Logger.error("Error fetching schedulebanks: ${e.message}" as Any)
}
}
/**
* Reloads the language link list from the database.
*/
private fun Reload_LanguageLink() {
LanguageLinkList.clear()
try {
val statement = connection?.createStatement()
val resultSet = statement?.executeQuery("SELECT * FROM languagelink")
while (resultSet?.next() == true) {
val languageLink = LanguageLink(
resultSet.getLong("index").toUInt(),
resultSet.getString("TAG"),
resultSet.getString("Language")
)
LanguageLinkList.add(languageLink)
}
} catch (e : Exception) {
Logger.error("Error fetching language links: ${e.message}" as Any)
}
}
/** /**
* Reloads the soundbank list from the database. * Reloads the soundbank list from the database.
*/ */

View File

@@ -0,0 +1,4 @@
package database
@Suppress("unused")
data class ScheduleBank(val index: UInt, val Description: String, val Day: String, val Time: String, val Soundpath: String, val Repeat: UByte, val Enable: Boolean, val BroadcastZones: String, val Language: String)

View File

@@ -2,6 +2,7 @@ package web
import codes.Somecodes import codes.Somecodes
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import database.MariaDB
import io.javalin.Javalin import io.javalin.Javalin
import io.javalin.apibuilder.ApiBuilder.before import io.javalin.apibuilder.ApiBuilder.before
import io.javalin.apibuilder.ApiBuilder.get import io.javalin.apibuilder.ApiBuilder.get
@@ -9,13 +10,13 @@ import io.javalin.apibuilder.ApiBuilder.path
import io.javalin.apibuilder.ApiBuilder.post import io.javalin.apibuilder.ApiBuilder.post
import io.javalin.apibuilder.ApiBuilder.ws import io.javalin.apibuilder.ApiBuilder.ws
import io.javalin.http.Context import io.javalin.http.Context
import java.time.LocalDateTime
@Suppress("unused") @Suppress("unused")
class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) { class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>, val db: MariaDB) {
var app : Javalin? = null var app : Javalin? = null
private val objectmapper = jacksonObjectMapper() val objectmapper = jacksonObjectMapper()
fun Start() { fun Start() {
app = Javalin.create { app = Javalin.create {
config -> config ->
@@ -55,35 +56,58 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
path("home.html") { path("home.html") {
before { CheckUsers(it) } before { CheckUsers(it) }
ws("/ws") { it -> ws("/ws") { ws ->
// WebSocket endpoint for home // WebSocket endpoint for home
it.onClose { ws.onClose { wsCloseContext ->
// TODO Handle WebSocket close event // TODO Handle WebSocket close event
println("WebSocket closed: ${it.session.remoteAddress}") println("WebSocket closed: ${wsCloseContext.session.remoteAddress}")
} }
it.onMessage { ws.onMessage { wsMessageContext ->
try{ try{
val cmd = objectmapper.readValue(it.message(), WebsocketCommand::class.java) val cmd = objectmapper.readValue(wsMessageContext.message(), WebsocketCommand::class.java)
when (cmd.command) { when (cmd.command) {
"getSystemTime" ->{
wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, LocalDateTime.now().format(Somecodes.datetimeformat1))))
}
"getCPUStatus" ->{ "getCPUStatus" ->{
//TODO Get CPU status Somecodes.getCPUUsage { vv ->
wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, vv)))
it.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command,"OK"))) }
} }
"getMemoryStatus" ->{ "getMemoryStatus" ->{
// TODO Get Memory status wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, Somecodes.getMemoryUsage())))
it.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, Somecodes.getMemoryUsage())))
} }
"getDiskStatus" ->{ "getDiskStatus" ->{
// TODO Get Disk status wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, Somecodes.getDiskUsage())))
it.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command,"OK")))
} }
"getNetworkStatus" ->{ "getNetworkStatus" ->{
// TODO Get Network status // TODO Get Network status
it.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command,"OK"))) wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command,"OK")))
}
"getSoundBankList" ->{
println("getSoundBankList command received")
wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, MariaDB.ArrayListtoString(db.SoundbankList))))
}
"getMessageBankList"->{
println("getMessageBankList command received")
wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, MariaDB.ArrayListtoString(db.MessagebankList))) )
}
"getLanguageList"->{
println("getLanguageList command received")
wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, MariaDB.ArrayListtoString(db.LanguageLinkList))))
}
"getTimerList"->{
println("getTimerList command received")
wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, MariaDB.ArrayListtoString(db.SchedulebankList))))
}
"getLog" ->{
println("getLog command received")
}
"getSetting" ->{
println("getSetting command received")
} }
else -> { else -> {
it.send(objectmapper.writeValueAsString(WebsocketReply("error", "Unknown command: ${cmd.command}"))) wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply("error", "Unknown command: ${cmd.command}")))
} }
} }
} catch (e: Exception){ } catch (e: Exception){
@@ -91,30 +115,37 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
} }
} }
it.onConnect { ws.onConnect { wsConnectContext ->
// TODO Handle WebSocket connect event // TODO Handle WebSocket connect event
println("WebSocket connected: ${it.session.remoteAddress}") println("WebSocket connected: ${wsConnectContext.session.remoteAddress}")
} }
} }
} }
path("soundbank.html") { path("soundbank.html") {
before {CheckUsers(it)} before {CheckUsers(it)}
} }
path("messagebank.html") { path("messagebank.html") {
before { CheckUsers(it) } before { CheckUsers(it) }
} }
path("language.html") { path("language.html") {
before { CheckUsers(it) } before { CheckUsers(it) }
} }
path("log.html") { path("log.html") {
before { CheckUsers(it) } before { CheckUsers(it) }
} }
path("setting.html") { path("setting.html") {
before { CheckUsers(it) } before { CheckUsers(it) }
} }
path("timer.html") { path("timer.html") {
before { CheckUsers(it) } before { CheckUsers(it) }
} }
} }
}.start(listenPort) }.start(listenPort)
@@ -122,19 +153,13 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
} }
fun CheckUsers(ctx: Context){ fun CheckUsers(ctx: Context){
println("Checking user session at ${ctx.req().requestURI}")
val user = ctx.sessionAttribute<String?>("user") val user = ctx.sessionAttribute<String?>("user")
if (user == null) { if (user == null) {
println("User not logged in, redirecting to login page")
ctx.redirect("login.html") ctx.redirect("login.html")
} }
println("User is logged in: $user")
val foundUser = userlist.find { it.first == user } val foundUser = userlist.find { it.first == user }
if (foundUser==null) { if (foundUser==null) {
println("User not found in user list, redirecting to login page")
ctx.redirect("login.html") ctx.redirect("login.html")
} else {
println("User found: $user")
} }
} }