From fe4e73ff988c4122f6af26f3cdc105d47a59d761 Mon Sep 17 00:00:00 2001 From: rdkartono Date: Thu, 23 Oct 2025 16:50:02 +0700 Subject: [PATCH] commit 23/10/2025 --- config.properties | 7 +- html/webpage/assets/js/script.js | 14 ++- html/webpage/assets/js/setting.js | 160 ++++++++++++++++++++++++++++++ html/webpage/setting.html | 7 +- src/Main.kt | 11 +- src/MainExtension01.kt | 13 ++- src/codes/configFile.kt | 7 +- src/codes/configKeys.kt | 7 +- src/web/WebApp.kt | 104 ++++++++++++++++++- 9 files changed, 312 insertions(+), 18 deletions(-) create mode 100644 html/webpage/assets/js/setting.js diff --git a/config.properties b/config.properties index 61631d0..7d23ab5 100644 --- a/config.properties +++ b/config.properties @@ -1,5 +1,5 @@ #Configuration file -#Tue Oct 21 17:17:50 WIB 2025 +#Thu Oct 23 15:01:47 WIB 2025 database.host=localhost database.name=aas database.password=admin @@ -10,3 +10,8 @@ remark.GBD= remark.GFC= remark.GOP= soundbank.directory=C\:\\Users\\rdkar\\OneDrive\\Documents\\IntelliJ Project\\AAS_NewGen\\soundbank +webapp.admin.password=password +webapp.admin.username=admin +webapp.port=3030 +webapp.viewer.password=password +webapp.viewer.username=viewer diff --git a/html/webpage/assets/js/script.js b/html/webpage/assets/js/script.js index 2122c4c..aa0b862 100644 --- a/html/webpage/assets/js/script.js +++ b/html/webpage/assets/js/script.js @@ -60,6 +60,7 @@ window.messagebankdata ??= []; function reloadMessageBank(APIURL = "MessageBank/", cbOK = null) { window.messagebankdata ??= []; fetchAPI(APIURL + "List", "GET", {}, null, (okdata) => { + //console.log("Message bank data loaded : ", okdata); if (Array.isArray(okdata)) { window.messagebankdata.push(...okdata); window.selectedmessagerow = null; @@ -67,6 +68,7 @@ function reloadMessageBank(APIURL = "MessageBank/", cbOK = null) { if (cbOK) cbOK(); } }, (errdata) => { + //console.error("Error loading messagebank : ", errdata); alert("Error loading messagebank : " + errdata.message); }); } @@ -186,14 +188,16 @@ function fetchImg(url, cbOK, cbError) { /** * Reload voice types from server + * @param {Function|null} cbOK callback on success, default null */ -function getVoiceTypes() { +function getVoiceTypes(cbOK = null) { window.voiceTypes = []; fetchAPI("VoiceType", "GET", {}, null, (okdata) => { // okdata is a string contains elements separated by semicolon ; if (Array.isArray(okdata)) { window.voiceTypes = okdata.filter(item => item.trim().length > 0); //console.log("Loaded " + voiceTypes.length + " voice types : " + voiceTypes.join(", ")); + if (cbOK) cbOK(); } else console.log("getVoiceTypes: okdata is not array"); }, (errdata) => { alert("Error loading voice types : " + errdata.message); @@ -202,14 +206,16 @@ function getVoiceTypes() { /** * Reload categories from server + * @param {Function|null} cbOK callback on success, default null */ -function getCategories() { +function getCategories(cbOK = null) { window.categories = []; fetchAPI("Category", "GET", {}, null, (okdata) => { // okdata is a string contains elements separated by semicolon ; if (Array.isArray(okdata)) { window.categories = okdata.filter(item => item.trim().length > 0); //console.log("Loaded " + categories.length + " categories : " + categories.join(", ")); + if (cbOK) cbOK(); } else console.log("getCategories: okdata is not array"); }, (errdata) => { alert("Error loading categories : " + errdata.message); @@ -218,14 +224,16 @@ function getCategories() { /** * Reload languages from server + * @param {Function|null} cbOK callback on success, default null */ -function getLanguages() { +function getLanguages(cbOK = null) { window.languages = []; fetchAPI("Language", "GET", {}, null, (okdata) => { // okdata is a string contains elements separated by semicolon ; if (Array.isArray(okdata)) { window.languages = okdata.filter(item => item.trim().length > 0); //console.log("Loaded " + languages.length + " languages : " + languages.join(", ") ); + if (cbOK) cbOK(); } else console.log("getLanguages: okdata is not array"); }, (errdata) => { alert("Error loading languages : " + errdata.message); diff --git a/html/webpage/assets/js/setting.js b/html/webpage/assets/js/setting.js new file mode 100644 index 0000000..da26ff0 --- /dev/null +++ b/html/webpage/assets/js/setting.js @@ -0,0 +1,160 @@ +let selected_language = null; +let selected_category = null; +let selected_voice = null; + +/** + * Load setting language dropdown + */ +function load_setting_language(){ + selected_language = null; + $("#setting_language").empty().off('change'); + + getLanguages( () => { + window.languages.forEach( (lang) => { + let $option = $("").attr("value", lang).text(lang); + $("#setting_language").append($option); + }); + $("#setting_language").on('change', function() { + selected_language = $(this).val(); + change_droparea_enable(); + }); + }); +} + +/** + * Load setting category dropdown + */ +function load_setting_category(){ + selected_category = null; + $("#setting_category").empty().off('change'); + getCategories( () => { + window.categories.forEach( (cat) => { + let $option = $("").attr("value", cat).text(cat); + $("#setting_category").append($option); + }); + $("#setting_category").on('change', function() { + selected_category = $(this).val(); + change_droparea_enable(); + }); + }); +} + +/** + * Load setting voice dropdown + */ +function load_setting_voice(){ + selected_voice = null; + $("#setting_voice").empty().off('change'); + getVoiceTypes( () => { + window.voiceTypes.forEach( (voice) => { + let $option = $("").attr("value", voice).text(voice); + $("#setting_voice").append($option); + }); + $("#setting_voice").on('change', function() { + selected_voice = $(this).val(); + change_droparea_enable(); + }); + }); +} + +function get_soundbank_path(){ + fetchAPI("Settings/SoundbankDirectory", "GET", {}, null, (okdata) => { + console.log("Soundbank path : " + okdata); + $("#setting_path").val(okdata); + }, (errdata) => { + alert("Error getting soundbank path : " + errdata.message); + }); +} + +/** + * Enable or disable drop area based on selections + */ +function change_droparea_enable(){ + if (selected_category && selected_language && selected_voice) { + $("#drop-area").removeClass("disabled"); + } else { + $("#drop-area").addClass("disabled"); + } +} + +function load_messagebank(cbOK = null){ + $("#input_GOP").empty(); + $("#input_GBD").empty(); + $("#input_GFC").empty(); + $("#input_FLD").empty(); + + // get messagebank data from server + reloadMessageBank(()=>{ + console.log("Will load " + window.messagebankdata.length + " message bank items into selection."); + window.messagebankdata.forEach((item)=>{ + let opt = `${item.description} [${item.aNN_ID}]`; + console.log("Adding option: " + opt); + $("#input_GOP").append($("").attr("value", opt).text(opt)); + $("#input_GBD").append($("").attr("value", opt).text(opt)); + $("#input_GFC").append($("").attr("value", opt).text(opt)); + $("#input_FLD").append($("").attr("value", opt).text(opt)); + }); + if (window.messagebankdata.length > 0) { + if (cbOK) cbOK(); + } + }); +} + +function load_remark_selection(){ + fetchAPI("Settings/FISCode", "GET", {}, null, (okdata) => { + $("#input_GOP").val(okdata.GOP); + $("#input_GBD").val(okdata.GBD); + $("#input_GFC").val(okdata.GFC); + $("#input_FLD").val(okdata.FLD); + }, (errdata) => { + alert("Error getting FIS codes : " + errdata.message); + }); +} + +$(document).ready(function() { + console.log("setting.js loaded"); + + + $("#save_directory").off('click').on('click', function() { + let new_path = $("#setting_path").val(); + if (new_path && new_path.trim().length > 0) { + fetchAPI("Settings/SoundbankDirectory", "POST", {}, { directory: new_path }, (okdata) => { + alert("Soundbank directory path saved successfully."); + }, (errdata) => { + alert("Error saving soundbank directory path : " + errdata.message); + }); + } else { + alert("Please enter a valid soundbank directory path."); + } + }); + + + // get_soundbank_path(); + load_setting_category(); + load_setting_language(); + load_setting_voice(); + load_messagebank(()=> load_remark_selection()); + $("#fiscodesave").off('click').on('click', function() { + let gop = $("#input_GOP").val(); + let gbd = $("#input_GBD").val(); + let gfc = $("#input_GFC").val(); + let fld = $("#input_FLD").val(); + if (gop && gbd && gfc && fld) { + let data = { + GOP: gop, + GBD: gbd, + GFC: gfc, + FLD: fld + }; + fetchAPI("Settings/FISCode", "POST", {}, data, (okdata) => { + alert("FIS codes saved successfully."); + }, (errdata) => { + alert("Error saving FIS codes : " + errdata.message); + }); + } else { + alert("Please select all FIS codes (GOP, GBD, GFC, FLD) before saving."); + } + + }); + +}); \ No newline at end of file diff --git a/html/webpage/setting.html b/html/webpage/setting.html index 006b5b5..50a1a05 100644 --- a/html/webpage/setting.html +++ b/html/webpage/setting.html @@ -59,8 +59,8 @@
-
-
+
+
@@ -72,7 +72,7 @@
-
+
@@ -81,6 +81,7 @@ + \ No newline at end of file diff --git a/src/Main.kt b/src/Main.kt index 38a068a..30063de 100644 --- a/src/Main.kt +++ b/src/Main.kt @@ -97,7 +97,7 @@ fun files_preparation(){ } -lateinit var config : configFile; +lateinit var config : configFile // Application start here fun main() { @@ -135,6 +135,7 @@ fun main() { subcode01.Read_Queue_Soundbank() } } + // Coroutine untuk cek Schedulebank tiap menit saat detik 00 CoroutineScope(Dispatchers.Default).launch { while (isActive) { @@ -144,11 +145,11 @@ fun main() { } val web = WebApp( - 3030, + config.Get(configKeys.WEBAPP_PORT.key).toInt(), listOf( - Pair("admin", "password"), - Pair("user", "password") - )) + Pair(config.Get(configKeys.WEBAPP_ADMIN_USERNAME.key), config.Get(configKeys.WEBAPP_ADMIN_PASSWORD.key)), + Pair(config.Get(configKeys.WEBAPP_VIEWER_USERNAME.key), config.Get(configKeys.WEBAPP_VIEWER_PASSWORD.key)) + ), config) web.Start() udpreceiver = UDPReceiver() diff --git a/src/MainExtension01.kt b/src/MainExtension01.kt index b07618b..e738e64 100644 --- a/src/MainExtension01.kt +++ b/src/MainExtension01.kt @@ -12,6 +12,7 @@ import codes.Somecodes.Companion.ValidString import codes.Somecodes.Companion.dateformat1 import codes.Somecodes.Companion.datetimeformat1 import codes.Somecodes.Companion.timeformat2 +import codes.configKeys import content.Category import content.Language import content.ScheduleDay @@ -892,23 +893,31 @@ class MainExtension01 { val variables = Get_Soundbank_Data(qa.SB_TAGS) val languages = qa.Language.split(";") // cek apakah ANN_ID ada di SB_TAGS - val ann_id = variables?.get("ANN_ID")?.toIntOrNull() ?: 0 + var ann_id = variables?.get("ANN_ID")?.toIntOrNull() ?: 0 if (ann_id==0){ // not available from variables, try to get from Message column // ada ini, karena protokol FIS dulu tidak ada ANN_ID tapi pake Remark val remark = variables?.get("REMARK").orEmpty() when(remark){ "GOP" -> { - //TODO Combobox First_Call_Message_Chooser + //TODO Combobox First_Call_Message_Chooser. + val remarkMsg = config.Get(configKeys.REMARK_GOP.toString()) + ann_id = Regex("\\[(\\d+)]").find(remarkMsg)?.value?.toIntOrNull() ?: 0 } "GBD" ->{ // TODO Combobox Second_Call_Message_Chooser + val remarkMsg = config.Get(configKeys.REMARK_GBD.toString()) + ann_id = Regex("\\[(\\d+)]").find(remarkMsg)?.value?.toIntOrNull() ?: 0 } "GFC" ->{ // TODO Combobox Final_Call_Message_Chooser + val remarkMsg = config.Get(configKeys.REMARK_GFC.toString()) + ann_id = Regex("\\[(\\d+)]").find(remarkMsg)?.value?.toIntOrNull() ?: 0 } "FLD" ->{ // TODO Combobox Landed_Message_Chooser + val remarkMsg = config.Get(configKeys.REMARK_FLD.toString()) + ann_id = Regex("\\[(\\d+)]").find(remarkMsg)?.value?.toIntOrNull() ?: 0 } } } diff --git a/src/codes/configFile.kt b/src/codes/configFile.kt index f1c0e71..6aa7a5a 100644 --- a/src/codes/configFile.kt +++ b/src/codes/configFile.kt @@ -44,7 +44,7 @@ class configFile { } private fun HaveAllKeys() : Boolean{ - return configKeys.values().all { config.containsKey(it.key) } + return configKeys.entries.all { config.containsKey(it.key) } } @@ -61,6 +61,11 @@ class configFile { config[configKeys.REMARK_GBD.key] = "" config[configKeys.REMARK_GFC.key] = "" config[configKeys.REMARK_FLD.key] = "" + config[configKeys.WEBAPP_ADMIN_USERNAME.key] = "admin" + config[configKeys.WEBAPP_ADMIN_PASSWORD.key] = "password" + config[configKeys.WEBAPP_VIEWER_USERNAME.key] = "viewer" + config[configKeys.WEBAPP_VIEWER_PASSWORD.key] = "password" + config[configKeys.WEBAPP_PORT.key] = "3030" Save() } } \ No newline at end of file diff --git a/src/codes/configKeys.kt b/src/codes/configKeys.kt index 26bab7a..2ce3e5c 100644 --- a/src/codes/configKeys.kt +++ b/src/codes/configKeys.kt @@ -10,5 +10,10 @@ enum class configKeys(val key: String) { REMARK_GOP("remark.GOP"), REMARK_GBD("remark.GBD"), REMARK_GFC("remark.GFC"), - REMARK_FLD("remark.FLD") + REMARK_FLD("remark.FLD"), + WEBAPP_ADMIN_USERNAME("webapp.admin.username"), + WEBAPP_ADMIN_PASSWORD("webapp.admin.password"), + WEBAPP_VIEWER_USERNAME("webapp.viewer.username"), + WEBAPP_VIEWER_PASSWORD("webapp.viewer.password"), + WEBAPP_PORT("webapp.port") } \ No newline at end of file diff --git a/src/web/WebApp.kt b/src/web/WebApp.kt index e72e9ff..d765697 100644 --- a/src/web/WebApp.kt +++ b/src/web/WebApp.kt @@ -12,6 +12,7 @@ import codes.Somecodes.Companion.ValidScheduleDay import codes.Somecodes.Companion.ValidScheduleTime import codes.Somecodes.Companion.ValidString import codes.Somecodes.Companion.ValidStrings +import codes.configFile import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import content.Category @@ -41,9 +42,13 @@ import io.javalin.websocket.WsMessageContext import org.apache.poi.xssf.usermodel.XSSFWorkbook import java.nio.file.Files import java.time.LocalDateTime +import codes.configKeys +import org.tinylog.Logger +//import com.sun.security.auth.login.ConfigFile + @Suppress("unused") -class WebApp(val listenPort: Int, val userlist: List>) { +class WebApp(val listenPort: Int, val userlist: List>, val _config: configFile) { var app: Javalin? = null val objectmapper = jacksonObjectMapper() @@ -1448,7 +1453,6 @@ class WebApp(val listenPort: Int, val userlist: List>) { } - path("QueueTable"){ get("List"){ it.result(MariaDB.ArrayListtoString(db.queuetableDB.List)) @@ -1472,6 +1476,7 @@ class WebApp(val listenPort: Int, val userlist: List>) { db.queuetableDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) db.Add_Log("AAS", "Deleted queue sound with index $index") + } else { it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to delete queue sound with index $index"))) } @@ -1480,12 +1485,107 @@ class WebApp(val listenPort: Int, val userlist: List>) { } path("Settings"){ + get("SoundbankDirectory"){ + val dir = _config.Get(configKeys.SOUNDBANK_DIRECTORY.key) + it.result(objectmapper.writeValueAsString(resultMessage(dir))) + } + post("SoundbankDirectory"){ + val json : JsonNode = objectmapper.readTree(it.body()) + val newdir = json.get("directory").asText("") + if (ValidString(newdir)){ + _config.Set(configKeys.SOUNDBANK_DIRECTORY.key, newdir) + _config.Save() + Logger.info { "Changed Soundbank Directory to $newdir" } + it.result(objectmapper.writeValueAsString(resultMessage("OK"))) + } else { + it.status(400) + .result(objectmapper.writeValueAsString(resultMessage("Invalid directory value"))) + } + } + + + get("SoundbankResultList"){ + it.result(objectmapper.writeValueAsString(ListAudioFiles(Somecodes.SoundbankResult_directory))) + } + + get("SoundbankResultFile/{filename}"){ + + //TODO recheck bener/gak cara downloadnya + val filename = it.pathParam("filename") + + val filepath = Somecodes.SoundbankResult_directory.resolve(filename) + if (ValidFile(filename) && Files.isRegularFile(filepath)){ + it.header( + "Content-Disposition", + "attachment; filename=\"$filename\"") + it.outputStream().use { out -> + Files.newInputStream(filepath).use { inp -> + inp.copyTo(out) + } + } + } else { + it.status(404).result(objectmapper.writeValueAsString(resultMessage("File not found"))) + } + } + + get("PagingResultList"){ + it.result(objectmapper.writeValueAsString(ListAudioFiles(Somecodes.PagingResult_directory))) + } + + get ("PagingResultFile/{filename}"){ + val filename = it.pathParam("filename") + val filepath = Somecodes.PagingResult_directory.resolve(filename) + if (ValidFile(filename) && Files.isRegularFile(filepath)){ + it.header("Content-Disposition", "attachment; filename=\"$filename\"") + it.outputStream().use { out -> + Files.newInputStream(filepath).use { inp -> + inp.copyTo(out) + } + } + } else { + it.status(404).result(objectmapper.writeValueAsString(resultMessage("File not found"))) + } + } + get("Messages"){ val messages = db.messageDB.List it.result(objectmapper.writeValueAsString(messages)) } + + get("FISCode"){ + //TODO get FIS code + val value = object { + //get from config file + val GOP = _config.Get(configKeys.REMARK_GOP.key) + val GBD = _config.Get(configKeys.REMARK_GBD.key) + val GFC = _config.Get(configKeys.REMARK_GFC.key) + val FLD = _config.Get(configKeys.REMARK_FLD.key) + } + + it.result(objectmapper.writeValueAsString(value)) + } + post ("FISCode") { //TODO set FIS code + + val json : JsonNode = objectmapper.readTree(it.body()) + val _gop = json.get("GOP").asText("") + val _gbd = json.get("GBD").asText("") + val _gfc = json.get("GFC").asText("") + val _fld = json.get("FLD").asText("") + if (ValidString(_gop) && ValidString(_gbd) && ValidString(_gfc) && ValidString(_fld)){ + // save to config file + _config.Set(configKeys.REMARK_GOP.key, _gop) + _config.Set(configKeys.REMARK_GBD.key, _gbd) + _config.Set(configKeys.REMARK_GFC.key, _gfc) + _config.Set(configKeys.REMARK_FLD.key, _fld) + _config.Save() + Logger.info { "Changed FIS Codes" } + it.result(objectmapper.writeValueAsString(resultMessage("OK"))) + } else { + it.status(400) + .result(objectmapper.writeValueAsString(resultMessage("Invalid FIS code value"))) + } } } }