diff --git a/src/Main.kt b/src/Main.kt index 20fd374..1b3ce4c 100644 --- a/src/Main.kt +++ b/src/Main.kt @@ -14,7 +14,7 @@ import commandServer.TCP_Android_Command_Server import content.Category import content.Language import content.VoiceType -import database.Log +import database.data.Log import database.MariaDB import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -45,14 +45,7 @@ const val max_channel = 64 val apptick : Long = System.currentTimeMillis() // dipakai untuk ambil messagebank berdasarkan id -val urutan_bahasa = listOf( - Language.INDONESIA.name, - Language.LOCAL.name, - Language.ENGLISH.name, - Language.CHINESE.name, - Language.JAPANESE.name, - Language.ARABIC.name -) + val contentCache = ContentCache() diff --git a/src/MainExtension01.kt b/src/MainExtension01.kt index 8345036..b66e05b 100644 --- a/src/MainExtension01.kt +++ b/src/MainExtension01.kt @@ -17,9 +17,9 @@ import content.Category import content.Language import content.ScheduleDay import content.VoiceType -import database.Messagebank -import database.QueueTable -import database.Soundbank +import database.data.Messagebank +import database.data.QueueTable +import database.data.Soundbank import org.tinylog.Logger import java.time.DayOfWeek @@ -163,7 +163,7 @@ class MainExtension01 { * @param languages List of language yang diinginkan, default urutan_bahasa * @return List of Messagebank */ - fun Get_MessageBank_by_id(id: Int, languages: List = urutan_bahasa): ArrayList { + fun Get_MessageBank_by_id(id: Int, languages: List = Language.LanguageOrder()): ArrayList { val mb_list = ArrayList() var selected_voice = config.Get(configKeys.DEFAULT_VOICE_TYPE.key) if (selected_voice.isEmpty()) selected_voice = VoiceType.VOICE_1.name diff --git a/src/commandServer/TCP_Android_Command_Server.kt b/src/commandServer/TCP_Android_Command_Server.kt index d18e0f1..4928291 100644 --- a/src/commandServer/TCP_Android_Command_Server.kt +++ b/src/commandServer/TCP_Android_Command_Server.kt @@ -3,12 +3,11 @@ package commandServer import audioPlayer import codes.Somecodes.Companion.ValidString import codes.Somecodes.Companion.datetimeformat1 -import content.Category import content.Language -import database.Messagebank -import database.QueuePaging -import database.QueueTable -import database.Soundbank +import database.data.Messagebank +import database.data.QueuePaging +import database.data.QueueTable +import database.data.Soundbank import db import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -367,11 +366,7 @@ class TCP_Android_Command_Server { .map { it.trim() } .filter { it.isNotBlank() } .forEach { al -> - val sb = db.soundDB.List - .filter { it.Category.equals(Category.Airplane_Name.name, true) } - .filter { it.TAG.equals(al, true)} - .distinctBy { it.TAG } - VARAPTOTAL.addAll(sb) + VARAPTOTAL.addAll(db.soundDB.Find_AirlineName_By_TAG(al)) } result.append(VARAPTOTAL.size).append("@") cb.accept(result.toString()) @@ -385,11 +380,7 @@ class TCP_Android_Command_Server { .map { it.trim() } .filter { it.isNotBlank() } .forEach { ct -> - val sb = db.soundDB.List - .filter { it.Category.equals(Category.City.name, true) } - .filter { it.TAG.equals(ct, true)} - .distinctBy { it.TAG } - VARCITYTOTAL.addAll(sb) + VARCITYTOTAL.addAll(db.soundDB.Find_City_By_TAG(ct)) } result.append(VARCITYTOTAL.size).append("@") cb.accept(result.toString()) @@ -397,104 +388,56 @@ class TCP_Android_Command_Server { // kirim VARPLACESTOTAL result.clear() result.append("VARPLACESTOTAL;") - val VARPLACESTOTAL = mutableListOf() - db.soundDB.List - .filter { it.Category.equals(Category.Places.name, true) } - .distinctBy { it.TAG } - .forEach { - VARPLACESTOTAL.add(it) - } + val VARPLACESTOTAL = db.soundDB.Get_Places() result.append(VARPLACESTOTAL.size).append("@") cb.accept(result.toString()) // kirim VARSHALATTOTAL result.clear() result.append("VARSHALATTOTAL;") - val VARSHALATTOTAL = mutableListOf() - db.soundDB.List - .filter { it.Category.equals(Category.Shalat.name, true) } - .distinctBy { it.TAG } - .forEach { - VARSHALATTOTAL.add(it) - } + val VARSHALATTOTAL = db.soundDB.Get_Shalat() result.append(VARSHALATTOTAL.size).append("@") cb.accept(result.toString()) // kirim VARSEQUENCETOTAL result.clear() result.append("VARSEQUENCETOTAL;") - val VARSEQUENCETOTAL = mutableListOf() - db.soundDB.List - .filter { it.Category.equals(Category.Sequence.name, true) } - .distinctBy { it.TAG } - .forEach { - VARSEQUENCETOTAL.add(it) - } + val VARSEQUENCETOTAL = db.soundDB.Get_Sequences() result.append(VARSEQUENCETOTAL.size).append("@") cb.accept(result.toString()) // kirim VARREASONTOTAL result.clear() result.append("VARREASONTOTAL;") - val VARREASONTOTAL = mutableListOf() - db.soundDB.List - .filter { it.Category.equals(Category.Reason.name, true) } - .distinctBy { it.TAG } - .forEach { - VARREASONTOTAL.add(it) - } + val VARREASONTOTAL = db.soundDB.Get_Reasons() result.append(VARREASONTOTAL.size).append("@") cb.accept(result.toString()) // kirim VARPROCEDURETOTAL - val VARPROCEDURETOTAL = mutableListOf() result.clear() result.append("VARPROCEDURETOTAL;") - db.soundDB.List - .filter { it.Category.equals(Category.Procedure.name, true) } - .distinctBy { it.TAG } - .forEach { - VARPROCEDURETOTAL.add(it) - } + val VARPROCEDURETOTAL = db.soundDB.Get_Procedures() result.append(VARPROCEDURETOTAL.size).append("@") cb.accept(result.toString()) // kirim VARGATETOTAL - val VARGATETOTAL = mutableListOf() result.clear() result.append("VARGATETOTAL;") - db.soundDB.List - .filter { it.Category.equals(Category.Gate.name, true) } - .distinctBy { it.TAG } - .forEach { - VARGATETOTAL.add(it) - } + val VARGATETOTAL = db.soundDB.Get_Gates() result.append(VARGATETOTAL.size).append("@") cb.accept(result.toString()) // kirim VARCOMPENSATIONTOTAL result.clear() result.append("VARCOMPENSATIONTOTAL;") - val VARCOMPENSATIONTOTAL = mutableListOf() - db.soundDB.List - .filter { it.Category.equals(Category.Compensation.name, true) } - .distinctBy { it.TAG } - .forEach { - VARCOMPENSATIONTOTAL.add(it) - } + val VARCOMPENSATIONTOTAL = db.soundDB.Get_Compensation() result.append(VARCOMPENSATIONTOTAL.size).append("@") cb.accept(result.toString()) // kirim VARGREETINGTOTAL result.clear() result.append("VARGREETINGTOTAL;") - val VARGREETINGTOTAL = mutableListOf() - db.soundDB.List - .filter { it.Category.equals(Category.Greeting.name, true) } - .distinctBy { it.TAG } - .forEach { - VARGREETINGTOTAL.add(it) - } + val VARGREETINGTOTAL = db.soundDB.Get_Greeting() result.append(VARGREETINGTOTAL.size).append("@") cb.accept(result.toString()) diff --git a/src/content/Language.kt b/src/content/Language.kt index 85b4bd9..3d42603 100644 --- a/src/content/Language.kt +++ b/src/content/Language.kt @@ -12,9 +12,30 @@ enum class Language(name: String) { LOCAL("LOCAL"), JAPANESE("JAPANESE"), CHINESE("CHINESE"), - ARABIC("ARABIC"); + ARABIC("ARABIC"), + DEFAULT(INDONESIA.name); // default language companion object{ + /** + * Default language link string + */ + fun DefaultLanguageLink() : String { + return DEFAULT.name+";"+ENGLISH.name + } + + /** + * Default language order + */ + fun LanguageOrder() : List { + return listOf( + INDONESIA.name, + LOCAL.name, + ENGLISH.name, + CHINESE.name, + JAPANESE.name, + ARABIC.name + ) + } fun from_GoogleTTSLanguage(lang: google.GoogleTTSLanguage) : Language { return when(lang) { google.GoogleTTSLanguage.Indonesia -> INDONESIA diff --git a/src/database/MariaDB.kt b/src/database/MariaDB.kt index 5d035fc..9e8580d 100644 --- a/src/database/MariaDB.kt +++ b/src/database/MariaDB.kt @@ -1,19 +1,25 @@ package database -import codes.Somecodes.Companion.ValidScheduleDay import codes.Somecodes.Companion.toJsonString import config -import content.Category import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext -import max_channel -import org.apache.poi.xssf.usermodel.XSSFWorkbook import org.tinylog.Logger import java.sql.Connection import java.sql.DriverManager -import java.util.function.Consumer import codes.configKeys +import database.table.Table_BroadcastZones +import database.table.Table_LanguageLink +import database.table.Table_LogSemiAuto +import database.table.Table_Logs +import database.table.Table_Messagebank +import database.table.Table_QueuePaging +import database.table.Table_QueueSoundbank +import database.table.Table_Schedule +import database.table.Table_SoundChannel +import database.table.Table_Soundbank +import database.table.Table_Users /** @@ -25,7 +31,6 @@ import codes.configKeys * @property username The username for the database connection. * @property password The password for the database connection. */ -@Suppress("unused", "SqlSourceToSinkFlow") class MariaDB( address: String = config.Get(configKeys.DATABASE_HOST.key), port: Int = config.Get(configKeys.DATABASE_PORT.key).toInt(), @@ -34,19 +39,17 @@ class MariaDB( password: String = config.Get(configKeys.DATABASE_PASSWORD.key) ) { var connected: Boolean = false - - lateinit var connection: Connection - lateinit var soundDB: dbFunctions - lateinit var messageDB: dbFunctions - lateinit var languageDB: dbFunctions - lateinit var scheduleDB: dbFunctions - lateinit var broadcastDB: dbFunctions - lateinit var queuetableDB: dbFunctions - lateinit var queuepagingDB: dbFunctions - lateinit var soundchannelDB: dbFunctions + lateinit var soundDB: Table_Soundbank + lateinit var messageDB: Table_Messagebank + lateinit var languageDB: Table_LanguageLink + lateinit var scheduleDB: Table_Schedule + lateinit var broadcastDB: Table_BroadcastZones + lateinit var queuetableDB: Table_QueueSoundbank + lateinit var queuepagingDB: Table_QueuePaging + lateinit var soundchannelDB: Table_SoundChannel lateinit var logDB: Table_Logs - lateinit var userDB: dbFunctions + lateinit var userDB: Table_Users lateinit var logSemiAuto: Table_LogSemiAuto companion object { @@ -91,1916 +94,17 @@ class MariaDB( statement.executeUpdate("CREATE DATABASE IF NOT EXISTS aas") statement.executeUpdate("USE aas") - - soundDB = object : dbFunctions("soundbank", connection, listOf("index", "Description", "TAG", "Category", "Language", "VoiceType", "Path")) { - - override fun Create() { - val tabledefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" + - "`index` INT AUTO_INCREMENT PRIMARY KEY," + - "Description VARCHAR(1024) NOT NULL," + // Description of the soundbank - "TAG VARCHAR(45) NOT NULL," + // TAG of the soundbank - "Category VARCHAR(45) NOT NULL," + // Category of the soundbank - "Language VARCHAR(45) NOT NULL," + // Language of the soundbank - "VoiceType VARCHAR(45) NOT NULL," + // VoiceType of the soundbank - "Path VARCHAR(1024) NOT NULL" + // Path to the sound file - ")" - super.Create(tabledefinition) - } - - override fun Get(cbOK: Consumer?, cbFail: Consumer?) { - List.clear() - try { - val statement = connection.createStatement() - val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName} ORDER BY Category, Language, VoiceType, TAG") - while (resultSet?.next() == true) { - val soundbank = Soundbank( - resultSet.getLong("index").toUInt(), - resultSet.getString("Description"), - resultSet.getString("TAG"), - resultSet.getString("Category"), - resultSet.getString("Language"), - resultSet.getString("VoiceType"), - resultSet.getString("Path") - ) - List.add(soundbank) - } - cbOK?.accept(Unit) - } catch (e: Exception) { - Logger.error("Error fetching soundbanks: ${e.message}" as Any) - cbFail?.accept("Error fetching ${super.dbName} : ${e.message}") - } - } - - fun Get_By_Index(index: Int): Soundbank? { - try { - val statement = connection.createStatement() - val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName} WHERE `index` = $index") - if (resultSet?.next() == true) { - return Soundbank( - resultSet.getLong("index").toUInt(), - resultSet.getString("Description"), - resultSet.getString("TAG"), - resultSet.getString("Category"), - resultSet.getString("Language"), - resultSet.getString("VoiceType"), - resultSet.getString("Path") - ) - } - } catch (_: Exception) { - Logger.error("Error finding ${super.dbName} with index $index" as Any) - } - return null - } - - override fun Add(data: Soundbank): Boolean { - try { - val statement = - connection.prepareStatement("INSERT INTO ${super.dbName} (Description, TAG, Category, Language, VoiceType, Path) VALUES (?, ?, ?, ?, ?, ?)") - statement?.setString(1, data.Description) - statement?.setString(2, data.TAG) - statement?.setString(3, data.Category) - statement?.setString(4, data.Language) - statement?.setString(5, data.VoiceType) - statement?.setString(6, data.Path) - val rowsAffected = statement?.executeUpdate() - if (rowsAffected != null && rowsAffected > 0) { - logDB.Add("AAS", "Soundbank added: ${data.Description}; TAG: ${data.TAG}; Category: ${data.Category}; Language: ${data.Language}; VoiceType: ${data.VoiceType}; Path: ${data.Path}") - Logger.info("Soundbank added: ${data.Description}" as Any) - return true - } else { - logDB.Add("AAS", "No soundbank entry added for: ${data.Description}") - Logger.warn("No soundbank entry added for: ${data.Description}" as Any) - } - } catch (e: Exception) { - logDB.Add("AAS", "Failed to add Soundbank: ${data.Description}. Error: ${e.message}") - Logger.error("Error adding soundbank entry: ${e.message}" as Any) - } - return false - } - - override fun AddAll(data: ArrayList): Boolean { - // use mysql bulk insert - try { - connection.autoCommit = false - val sql = - "INSERT INTO ${super.dbName} (Description, TAG, Category, Language, VoiceType, Path) VALUES (?, ?, ?, ?, ?, ?)" - val statement = connection.prepareStatement(sql) - for (sb in data) { - statement.setString(1, sb.Description) - statement.setString(2, sb.TAG) - statement.setString(3, sb.Category) - statement.setString(4, sb.Language) - statement.setString(5, sb.VoiceType) - statement.setString(6, sb.Path) - statement.addBatch() - } - statement.executeBatch() - connection.commit() - Logger.info("Bulk soundbank insert successful: ${data.size} entries" as Any) - logDB.Add("AAS","Successfully Import Sound Bank Table: ${data.size} entries.") - connection.autoCommit = true - return true - } catch (e: Exception) { - logDB.Add("AAS","Failed to Import Sound Bank. Error: ${e.message}") - Logger.error("Error adding soundbank entries: ${e.message}" as Any) - } - return false - } - - override fun UpdateByIndex(index: Int, data: Soundbank): Boolean { - try { - val statement = - connection.prepareStatement("UPDATE ${super.dbName} SET Description = ?, TAG = ?, Category = ?, Language = ?, VoiceType = ?, Path = ? WHERE `index` = ?") - statement?.setString(1, data.Description) - statement?.setString(2, data.TAG) - statement?.setString(3, data.Category) - statement?.setString(4, data.Language) - statement?.setString(5, data.VoiceType) - statement?.setString(6, data.Path) - statement?.setLong(7, index.toLong()) - val rowsAffected = statement?.executeUpdate() - if (rowsAffected != null && rowsAffected > 0) { - Logger.info("Soundbank updated at index $index: ${data.Description}" as Any) - logDB.Add("AAS", "Soundbank updated at index $index: ${data.Description}; TAG: ${data.TAG}; Category: ${data.Category}; Language: ${data.Language}; VoiceType: ${data.VoiceType}; Path: ${data.Path}") - return true - } else { - logDB.Add("AAS", "Failed updating Sound Bank at index $index for: ${data.Description}") - Logger.warn("No soundbank entry updated at index $index for: ${data.Description}" as Any) - } - } catch (e: Exception) { - logDB.Add("AAS", "Failed updating Sound Bank at index $index. Error: ${e.message}") - Logger.error("Error updating soundbank entry at index $index: ${e.message}" as Any) - } - return false - } - - override fun Resort(): Boolean { - try { - val statement = connection.createStatement() - val tempdb_name = "temp_${super.dbName}" - - // 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 (Description, TAG, Category, Language, VoiceType, Path) SELECT Description, TAG, Category, Language, VoiceType, Path FROM ${super.dbName} ORDER BY Category, Language, VoiceType, TAG ") - statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") - statement?.executeUpdate("INSERT INTO ${super.dbName} (Description, TAG, Category, Language, VoiceType, Path) SELECT Description, TAG, Category, Language, VoiceType, Path FROM $tempdb_name") - statement?.executeUpdate("DROP TABLE $tempdb_name") - Logger.info("${super.dbName} table resorted by Description" as Any) - // reload the local list - Get() - return true - } catch (e: Exception) { - Logger.error("Error resorting ${super.dbName} table by Description: ${e.message}" as Any) - } - return false - } - - override fun Import_XLSX(workbook: XSSFWorkbook): Boolean { - try { - // check if there is sheet named "Soundbank" - val sheet = - workbook.getSheet("Soundbank") ?: throw Exception("No sheet named 'Soundbank' found") - // check if the sheet contains header named "index", "Description", "TAG", "Category", "Language", "VoiceType", "Path" - val headerRow = sheet.getRow(0) ?: throw Exception("No header row found") - val headers = - arrayOf("Index", "Description", "TAG", "Category", "Language", "VoiceType", "Path") - for ((colIndex, header) in headers.withIndex()) { - val cell = headerRow.getCell(colIndex) ?: throw Exception("Header '$header' not found") - if (cell.stringCellValue != header) throw Exception("Header '$header' not found") - } - // clear existing soundbank - Clear() - // read each row and insert into database - val _soundbankList = ArrayList() - for (rowIndex in 1..sheet.lastRowNum) { - val row = sheet.getRow(rowIndex) ?: continue - val description = row.getCell(1)?.stringCellValue ?: continue - val tag = row.getCell(2)?.stringCellValue ?: continue - val category = row.getCell(3)?.stringCellValue ?: continue - val language = row.getCell(4)?.stringCellValue ?: continue - val voiceType = row.getCell(5)?.stringCellValue ?: continue - val path = row.getCell(6)?.stringCellValue ?: continue - val soundbank = Soundbank(0u, description, tag, category, language, voiceType, path) - _soundbankList.add(soundbank) - } - return AddAll(_soundbankList) - } catch (e: Exception) { - Logger.error { "Error importing Soundbank, Msg: ${e.message}" } - } - return false - } - - override fun Export_XLSX(): XSSFWorkbook? { - try { - val statement = connection.createStatement() - val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") - val workbook = XSSFWorkbook() - val sheet = workbook.createSheet("Soundbank") - val headerRow = sheet.createRow(0) - val headers = - arrayOf("Index", "Description", "TAG", "Category", "Language", "VoiceType", "Path") - for ((colIndex, header) in headers.withIndex()) { - val cell = headerRow.createCell(colIndex) - cell.setCellValue(header) - } - var rowIndex = 1 - while (resultSet?.next() == true) { - val row = sheet.createRow(rowIndex++) - row.createCell(0).setCellValue(resultSet.getString("index")) - row.createCell(1).setCellValue(resultSet.getString("Description")) - row.createCell(2).setCellValue(resultSet.getString("TAG")) - row.createCell(3).setCellValue(resultSet.getString("Category")) - row.createCell(4).setCellValue(resultSet.getString("Language")) - row.createCell(5).setCellValue(resultSet.getString("VoiceType")) - row.createCell(6).setCellValue(resultSet.getString("Path")) - } - for (i in 0 until headers.size) { - sheet.autoSizeColumn(i) - } - return workbook - } catch (e: Exception) { - Logger.error { "Error exporting Soundbank, Msg: ${e.message}" } - } - return null - } - } - - messageDB = object : dbFunctions("messagebank", connection, listOf("index", "Description", "Language", "ANN_ID", "Voice_Type", "Message_Detail", "Message_TAGS")) { - override fun Create() { - val tabledefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" + - "`index` INT AUTO_INCREMENT PRIMARY KEY," + - "Description VARCHAR(512) NOT NULL," + // Description of the message - "Language VARCHAR(45) NOT NULL," + // Language of the message - "ANN_ID INT NOT NULL," + // ANN ID of the message - "Voice_Type VARCHAR(45) NOT NULL," + // Voice type of the message - "Message_Detail VARCHAR(1024) NOT NULL," + // Full message text - "Message_TAGS VARCHAR(1024)" + // Comma-separated tags for the message - ")" - super.Create(tabledefinition) - } - - override fun Get(cbOK: Consumer?, cbFail: Consumer?) { - List.clear() - try { - val statement = connection.createStatement() - val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") - while (resultSet?.next() == true) { - val messagebank = Messagebank( - resultSet.getLong("index").toUInt(), - resultSet.getString("Description"), - resultSet.getString("Language"), - resultSet.getInt("ANN_ID").toUInt(), - resultSet.getString("Voice_Type"), - resultSet.getString("Message_Detail"), - resultSet.getString("Message_TAGS") - ) - - List.add(messagebank) - } - cbOK?.accept(Unit) - } catch (e: Exception) { - Logger.error("Error fetching ${super.dbName} : ${e.message}" as Any) - cbFail?.accept("Error fetching ${super.dbName} : ${e.message}") - } - } - - override fun Add(data: Messagebank): Boolean { - try { - val statement = - connection.prepareStatement("INSERT INTO ${super.dbName} (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) VALUES (?, ?, ?, ?, ?, ?)") - statement?.setString(1, data.Description) - statement?.setString(2, data.Language) - statement?.setInt(3, data.ANN_ID.toInt()) - statement?.setString(4, data.Voice_Type) - statement?.setString(5, data.Message_Detail) - statement?.setString(6, data.Message_TAGS) - val rowsAffected = statement?.executeUpdate() - if (rowsAffected != null && rowsAffected > 0) { - Logger.info("Messagebank added: ${data.Description}" as Any) - logDB.Add("AAS", "Messagebank added: ${data.Description}; Language: ${data.Language}; ANN_ID: ${data.ANN_ID}; Voice_Type: ${data.Voice_Type}") - return true - } else { - logDB.Add("AAS","Failed adding Message Bank for: ${data.Description}") - Logger.warn("No messagebank entry added for: ${data.Description}" as Any) - } - } catch (e: Exception) { - logDB.Add("AAS","Failed adding Message Bank for: ${data.Description}. Error: ${e.message}") - Logger.error("Error adding messagebank entry: ${e.message}" as Any) - } - return false - } - - override fun AddAll(data: ArrayList): Boolean { - try { - connection.autoCommit = false - val sql = - "INSERT INTO ${super.dbName} (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) VALUES (?, ?, ?, ?, ?, ?)" - val statement = connection.prepareStatement(sql) - for (mb in data) { - statement.setString(1, mb.Description) - statement.setString(2, mb.Language) - statement.setInt(3, mb.ANN_ID.toInt()) - statement.setString(4, mb.Voice_Type) - statement.setString(5, mb.Message_Detail) - statement.setString(6, mb.Message_TAGS) - statement.addBatch() - } - statement.executeBatch() - connection.commit() - Logger.info("Bulk messagebank insert successful: ${data.size} entries" as Any) - logDB.Add("AAS","Successfully Import Message Bank Table: ${data.size} entries.") - connection.autoCommit = true - return true - } catch (e: Exception) { - logDB.Add("AAS","Failed to Import Message Bank. Error: ${e.message}") - Logger.error("Error adding messagebank entries: ${e.message}" as Any) - } - return false - } - - override fun UpdateByIndex(index: Int, data: Messagebank): Boolean { - try { - val statement = - connection.prepareStatement("UPDATE ${super.dbName} SET Description = ?, Language = ?, ANN_ID = ?, Voice_Type = ?, Message_Detail = ?, Message_TAGS = ? WHERE `index` = ?") - statement?.setString(1, data.Description) - statement?.setString(2, data.Language) - statement?.setInt(3, data.ANN_ID.toInt()) - statement?.setString(4, data.Voice_Type) - statement?.setString(5, data.Message_Detail) - statement?.setString(6, data.Message_TAGS) - statement?.setLong(7, index.toLong()) - val rowsAffected = statement?.executeUpdate() - if (rowsAffected != null && rowsAffected > 0) { - Logger.info("Messagebank updated at index $index: ${data.Description}" as Any) - return true - } else { - Logger.warn("No messagebank entry updated at index $index for: ${data.Description}" as Any) - } - } catch (e: Exception) { - Logger.error("Error updating messagebank entry at index $index: ${e.message}" as Any) - } - return false - } - - override fun Resort(): Boolean { - try { - val statement = connection.createStatement() - val tempdb_name = "temp_${super.dbName}" - // 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 (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) SELECT Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS FROM ${super.dbName} ORDER BY ANN_ID ") - statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") - statement?.executeUpdate("INSERT INTO ${super.dbName} (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) SELECT Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS FROM $tempdb_name") - statement?.executeUpdate("DROP TABLE $tempdb_name") - Logger.info("${super.dbName} table resorted by Description" as Any) - // reload the local list - Get() - return true - } catch (e: Exception) { - Logger.error("Error resorting ${super.dbName} table by Description: ${e.message}" as Any) - } - return false - } - - override fun Import_XLSX(workbook: XSSFWorkbook): Boolean { - try { - // check if there is sheet named "Messagebank" - val sheet = - workbook.getSheet("Messagebank") ?: throw Exception("No sheet named 'Messagebank' found") - // check if the sheet contains header named "Index", "Description", "Language", "ANN_ID", "Voice_Type", "Message_Detail", "Message_TAGS" - val headerRow = sheet.getRow(0) ?: throw Exception("No header row found") - val headers = - arrayOf( - "Index", - "Description", - "Language", - "ANN_ID", - "Voice_Type", - "Message_Detail", - "Message_TAGS" - ) - for ((colIndex, header) in headers.withIndex()) { - val cell = headerRow.getCell(colIndex) ?: throw Exception("Header '$header' not found") - if (cell.stringCellValue != header) throw Exception("Header '$header' not found") - } - // clear existing messagebank - Clear() - // read each row and insert into database - val _messagebankList = ArrayList() - for (rowIndex in 1..sheet.lastRowNum) { - val row = sheet.getRow(rowIndex) ?: continue - val description = row.getCell(1)?.stringCellValue ?: continue - val language = row.getCell(2)?.stringCellValue ?: continue - val annId = row.getCell(3)?.stringCellValue?.toUIntOrNull() ?: continue - val voiceType = row.getCell(4)?.stringCellValue ?: continue - val messageDetail = row.getCell(5)?.stringCellValue ?: continue - val messageTags = row.getCell(6)?.stringCellValue ?: continue - val messagebank = - Messagebank(0u, description, language, annId, voiceType, messageDetail, messageTags) - _messagebankList.add(messagebank) - } - return AddAll(_messagebankList) - } catch (e: Exception) { - Logger.error { "Error importing Messagebank, Msg: ${e.message}" } - } - return false - } - - override fun Export_XLSX(): XSSFWorkbook? { - try { - val statement = connection.createStatement() - val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") - val workbook = XSSFWorkbook() - val sheet = workbook.createSheet("Messagebank") - val headerRow = sheet.createRow(0) - val headers = - arrayOf( - "Index", - "Description", - "Language", - "ANN_ID", - "Voice_Type", - "Message_Detail", - "Message_TAGS" - ) - for ((colIndex, header) in headers.withIndex()) { - val cell = headerRow.createCell(colIndex) - cell.setCellValue(header) - } - var rowIndex = 1 - while (resultSet?.next() == true) { - val row = sheet.createRow(rowIndex++) - row.createCell(0).setCellValue(resultSet.getString("index")) - row.createCell(1).setCellValue(resultSet.getString("Description")) - row.createCell(2).setCellValue(resultSet.getString("Language")) - row.createCell(3).setCellValue(resultSet.getString("ANN_ID")) - row.createCell(4).setCellValue(resultSet.getString("Voice_Type")) - row.createCell(5).setCellValue(resultSet.getString("Message_Detail")) - row.createCell(6).setCellValue(resultSet.getString("Message_TAGS")) - } - for (i in headers.indices) { - sheet.autoSizeColumn(i) - } - return workbook - } catch (e: Exception) { - Logger.error { "Error exporting Messagebank, Msg: ${e.message}" } - } - return null - } - - } - - languageDB = object : dbFunctions("languagelinking", connection, listOf("index", "TAG", "Language")) { - override fun Create() { - val tabledefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" + - "`index` INT AUTO_INCREMENT PRIMARY KEY," + - "TAG VARCHAR(45) NOT NULL," + // Language tag (e.g., EN, FR) - "Language VARCHAR(128) NOT NULL" + // Full language name (e.g., English, French) - ")" - - super.Create(tabledefinition) - } - - override fun Get(cbOK: Consumer?, cbFail: Consumer?) { - List.clear() - try { - val statement = connection.createStatement() - val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") - while (resultSet?.next() == true) { - val languageLink = LanguageLink( - resultSet.getLong("index").toUInt(), - resultSet.getString("TAG"), - resultSet.getString("Language") - ) - List.add(languageLink) - } - cbOK?.accept(Unit) - } catch (e: Exception) { - Logger.error("Error fetching ${super.dbName} : ${e.message}" as Any) - cbFail?.accept("Error fetching ${super.dbName} : ${e.message}" ) - } - } - - override fun Add(data: LanguageLink): Boolean { - try { - val statement = connection.prepareStatement("INSERT INTO ${super.dbName} (TAG, Language) VALUES (?, ?)") - statement.setString(1, data.TAG) - statement.setString(2, data.Language) - val rowsAffected = statement.executeUpdate() - if (rowsAffected > 0) { - Logger.info("Language link added: ${data.TAG} -> ${data.Language}" as Any) - logDB.Add("AAS", "Language link added: ${data.TAG} -> ${data.Language}") - return true - } else { - logDB.Add("AAS","Failed adding Language Link for: ${data.TAG} -> ${data.Language}.") - Logger.warn("No language link entry added for: ${data.TAG} -> ${data.Language}" as Any) - } - } catch (e: Exception) { - logDB.Add("AAS","Failed adding Language Link for: ${data.TAG} -> ${data.Language}. Error: ${e.message}") - Logger.error("Error adding language link entry: ${e.message}" as Any) - } - return false - } - - override fun AddAll(data: ArrayList): Boolean { - try { - connection.autoCommit = false - val sql = "INSERT INTO ${super.dbName} (TAG, Language) VALUES (?, ?)" - val statement = connection.prepareStatement(sql) - - //for (ll in List) { - for (ll in data) { - statement.setString(1, ll.TAG) - statement.setString(2, ll.Language) - statement.addBatch() - } - statement.executeBatch() - connection.commit() - Logger.info("Bulk languagelinking insert successful: ${List.size} entries" as Any) - logDB.Add("AAS","Successfully Import Language Link Table: ${data.size} entries.") - connection.autoCommit = true - return true - } catch (e: Exception) { - logDB.Add("AAS","Failed to Import Language Link. Error: ${e.message}") - Logger.error("Error adding languagelinking entries: ${e.message}" as Any) - } - return false - } - - override fun UpdateByIndex(index: Int, data: LanguageLink): Boolean { - try { - val statement = - connection.prepareStatement("UPDATE ${super.dbName} SET TAG = ?, Language = ? WHERE `index` = ?") - statement?.setString(1, data.TAG) - statement?.setString(2, data.Language) - statement?.setLong(3, index.toLong()) - val rowsAffected = statement?.executeUpdate() - if (rowsAffected != null && rowsAffected > 0) { - Logger.info("Language link updated at index $index: ${data.TAG} -> ${data.Language}" as Any) - - logDB.Add("AAS", "Language link updated at index $index: ${data.TAG} -> ${data.Language}") - return true - } else { - logDB.Add("AAS", "Failed updating Language Link at index $index for: ${data.TAG} -> ${data.Language}") - Logger.warn("No language link entry updated at index $index for: ${data.TAG} -> ${data.Language}" as Any) - } - } catch (e: Exception) { - logDB.Add("AAS", "Failed updating Language Link at index $index. Error: ${e.message}") - Logger.error("Error updating language link entry at index $index: ${e.message}" as Any) - } - return false - } - - override fun Resort(): Boolean { - try { - val statement = connection.createStatement() - val tempdb_name = "temp_${super.dbName}" - // 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 (TAG, Language) SELECT TAG, Language FROM ${super.dbName} ORDER BY TAG") - statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") - statement?.executeUpdate("INSERT INTO ${super.dbName} (TAG, Language) SELECT TAG, Language FROM $tempdb_name") - statement?.executeUpdate("DROP TABLE $tempdb_name") - Logger.info("${super.dbName} table resorted by TAG" as Any) - // reload the local list - Get() - return true - } catch (e: Exception) { - Logger.error("Error resorting ${super.dbName} table by TAG: ${e.message}" as Any) - } - return false - } - - override fun Import_XLSX(workbook: XSSFWorkbook): Boolean { - try { - val sheet = - workbook.getSheet("LanguageLink") ?: throw Exception("No sheet named 'LanguageLink' found") - val headerRow = sheet.getRow(0) ?: throw Exception("No header row found") - val headers = arrayOf("Index", "TAG", "Language") - for ((colIndex, header) in headers.withIndex()) { - val cell = headerRow.getCell(colIndex) ?: throw Exception("Header '$header' not found") - if (cell.stringCellValue != header) throw Exception("Header '$header' not found") - } - // clear existing languagelink - Clear() - // read each row and insert into database - val _languageLinkList = ArrayList() - for (rowIndex in 1..sheet.lastRowNum) { - val row = sheet.getRow(rowIndex) ?: continue - val tag = row.getCell(1)?.stringCellValue ?: continue - val language = row.getCell(2)?.stringCellValue ?: continue - val languageLink = LanguageLink(0u, tag, language) - _languageLinkList.add(languageLink) - } - return AddAll(_languageLinkList) - } catch (e: Exception) { - Logger.error { "Error importing LanguageLink, Msg: ${e.message}" } - } - return false - } - - override fun Export_XLSX(): XSSFWorkbook? { - try { - val statement = connection.createStatement() - val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") - val workbook = XSSFWorkbook() - val sheet = workbook.createSheet("LanguageLink") - val headerRow = sheet.createRow(0) - val headers = arrayOf("Index", "TAG", "Language") - for ((colIndex, header) in headers.withIndex()) { - val cell = headerRow.createCell(colIndex) - cell.setCellValue(header) - } - var rowIndex = 1 - while (resultSet?.next() == true) { - val row = sheet.createRow(rowIndex++) - row.createCell(0).setCellValue(resultSet.getString("index")) - row.createCell(1).setCellValue(resultSet.getString("TAG")) - row.createCell(2).setCellValue(resultSet.getString("Language")) - } - for (i in headers.indices) { - sheet.autoSizeColumn(i) - } - return workbook - } catch (e: Exception) { - Logger.error { "Error exporting languagelinking, Msg: ${e.message}" } - } - return null - } - - } - - scheduleDB = object : dbFunctions("schedulebank", connection, listOf("index", "Description", "Day", "Time", "Soundpath", "Repeat", "Enable", "BroadcastZones", "Language")) { - override fun Create() { - val tabledefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" + - "`index` INT AUTO_INCREMENT PRIMARY KEY," + - "Description VARCHAR(128) NOT NULL," + // Description of the schedule - "Day VARCHAR(255) NOT NULL," + // Day in format DD/MM/YYYY - "Time VARCHAR(20) NOT NULL," + // Time in format HH:MM:SS - "Soundpath VARCHAR(512) NOT NULL," + // Path to the sound file - "`Repeat` TINYINT UNSIGNED NOT NULL," + // Repeat type (0=Once, 1=Daily, 2=Weekly, 3=Monthly, 4=Yearly) - "Enable BOOLEAN NOT NULL," + // Enable or disable the schedule - "BroadcastZones TEXT NOT NULL," + // Comma-separated list of broadcast zones - "Language VARCHAR(45) NOT NULL" + // Language code (e.g., EN, FR) - ")" - super.Create(tabledefinition) - } - - override fun Get(cbOK: Consumer?, cbFail: Consumer?) { - List.clear() - try { - val statement = connection.createStatement() - val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") - 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") - ) - List.add(schedulebank) - } - cbOK?.accept(Unit) - } catch (e: Exception) { - Logger.error("Error fetching ${super.dbName}: ${e.message}" as Any) - cbFail?.accept("Error fetching ${super.dbName}: ${e.message}" ) - } - } - - override fun Add(data: ScheduleBank): Boolean { - if (!ValidScheduleDay(data.Day)) { - Logger.error("Error adding schedulebank entry: Invalid date format ${data.Day}" as Any) - return false - } - if (!ValidTime(data.Time)) { - Logger.error("Error adding schedulebank entry: Invalid time format ${data.Time}" as Any) - return false - } - try { - val statement = - connection.prepareStatement("INSERT INTO ${super.dbName} (Description, Day, Time, Soundpath, `Repeat`, Enable, BroadcastZones, Language) VALUES (?, ?, ?, ?, ?, ?, ?, ?)") - statement?.setString(1, data.Description) - statement?.setString(2, data.Day) - statement?.setString(3, data.Time) - statement?.setString(4, data.Soundpath) - statement?.setInt(5, data.Repeat.toInt()) - statement?.setBoolean(6, data.Enable) - statement?.setString(7, data.BroadcastZones) - statement?.setString(8, data.Language) - val rowsAffected = statement?.executeUpdate() - if (rowsAffected != null && rowsAffected > 0) { - Logger.info("Schedulebank added: ${data.Description}" as Any) - logDB.Add("AAS", "Schedulebank added: ${data.Description}; Day: ${data.Day}; Time: ${data.Time}; Soundpath: ${data.Soundpath}; Repeat: ${data.Repeat}; Enable: ${data.Enable}; BroadcastZones: ${data.BroadcastZones}; Language: ${data.Language}") - return true - } else { - logDB.Add("AAS","Failed adding Schedule Bank for: ${data.Description}.") - Logger.warn("No schedulebank entry added for: ${data.Description}" as Any) - } - } catch (e: Exception) { - logDB.Add("AAS","Failed adding Schedule Bank for: ${data.Description}. Error: ${e.message}") - Logger.error("Error adding schedulebank entry: ${e.message}" as Any) - } - return false - } - - override fun AddAll(data: ArrayList): Boolean { - try { - connection.autoCommit = false - val sql = - "INSERT INTO ${super.dbName} (Description, Day, Time, Soundpath, `Repeat`, Enable, BroadcastZones, Language) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" - val statement = connection.prepareStatement(sql) - for (sb in data) { - if (!ValidScheduleDay(sb.Day) || !ValidTime(sb.Time)) { - Logger.error("Invalid date or time format for schedulebank: ${sb.Description}" as Any) - continue - } - statement.setString(1, sb.Description) - statement.setString(2, sb.Day) - statement.setString(3, sb.Time) - statement.setString(4, sb.Soundpath) - statement.setInt(5, sb.Repeat.toInt()) - statement.setBoolean(6, sb.Enable) - statement.setString(7, sb.BroadcastZones) - statement.setString(8, sb.Language) - statement.addBatch() - } - statement.executeBatch() - connection.commit() - Logger.info("Bulk schedulebank insert successful: ${data.size} entries" as Any) - logDB.Add("AAS","Successfully Import Schedule Bank Table: ${data.size} entries.") - connection.autoCommit = true - return true - } catch (e: Exception) { - logDB.Add("AAS","Failed to Import Schedule Bank. Error: ${e.message}") - Logger.error("Error adding schedulebank entries: ${e.message}" as Any) - } - return false - } - - - override fun UpdateByIndex(index: Int, data: ScheduleBank): Boolean { - if (!ValidScheduleDay(data.Day)) { - Logger.error("Error updating schedulebank entry: Invalid date format ${data.Day}" as Any) - return false - } - if (!ValidTime(data.Time)) { - Logger.error("Error updating schedulebank entry: Invalid time format ${data.Time}" as Any) - return false - } - try { - val statement = - connection.prepareStatement("UPDATE ${super.dbName} SET Description = ?, Day = ?, Time = ?, Soundpath = ?, `Repeat` = ?, Enable = ?, BroadcastZones = ?, Language = ? WHERE `index` = ?") - statement?.setString(1, data.Description) - statement?.setString(2, data.Day) - statement?.setString(3, data.Time) - statement?.setString(4, data.Soundpath) - statement?.setInt(5, data.Repeat.toInt()) - statement?.setBoolean(6, data.Enable) - statement?.setString(7, data.BroadcastZones) - statement?.setString(8, data.Language) - statement?.setLong(9, index.toLong()) - val rowsAffected = statement?.executeUpdate() - if (rowsAffected != null && rowsAffected > 0) { - Logger.info("Schedulebank updated at index $index: ${data.Description}" as Any) - logDB.Add("AAS", "Schedulebank updated at index $index: ${data.Description}; Day: ${data.Day}; Time: ${data.Time}; Soundpath: ${data.Soundpath}; Repeat: ${data.Repeat}; Enable: ${data.Enable}; BroadcastZones: ${data.BroadcastZones}; Language: ${data.Language}") - return true - } else { - logDB.Add("AAS", "Failed updating Schedule Bank at index $index for: ${data.Description}.") - Logger.warn("No schedulebank entry updated at index $index for: ${data.Description}" as Any) - } - } catch (e: Exception) { - logDB.Add("AAS", "Failed updating Schedule Bank at index $index. Error: ${e.message}") - Logger.error("Error updating schedulebank entry at index $index: ${e.message}" as Any) - } - return false - } - - override fun Resort(): Boolean { - try { - val statement = connection.createStatement() - val tempdb_name = "temp_${super.dbName}" - // 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 (Description, Day, Time, Soundpath, `Repeat`, Enable, BroadcastZones, Language) SELECT Description, Day, Time, Soundpath, `Repeat`, Enable, BroadcastZones, Language FROM ${super.dbName} ORDER BY Day , Time ") - statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") - statement?.executeUpdate("INSERT INTO ${super.dbName} (Description, Day, Time, Soundpath, `Repeat`, Enable, BroadcastZones, Language) SELECT Description, Day, Time, Soundpath, `Repeat`, Enable, BroadcastZones, Language FROM $tempdb_name") - statement?.executeUpdate("DROP TABLE $tempdb_name") - Logger.info("${super.dbName} table resorted by Day and Time" as Any) - // reload the local list - Get() - return true - } catch (e: Exception) { - Logger.error("Error resorting ${super.dbName} table by Day and Time: ${e.message}" as Any) - } - return false - } - - override fun Import_XLSX(workbook: XSSFWorkbook): Boolean { - try { - val sheet = - workbook.getSheet("Schedulebank") ?: throw Exception("No sheet named 'Schedulebank' found") - val headerRow = sheet.getRow(0) ?: throw Exception("No header row found") - val headers = arrayOf( - "Index", - "Description", - "Day", - "Time", - "Soundpath", - "Repeat", - "Enable", - "BroadcastZones", - "Language" - ) - for ((colIndex, header) in headers.withIndex()) { - val cell = headerRow.getCell(colIndex) ?: throw Exception("Header '$header' not found") - if (cell.stringCellValue != header) throw Exception("Header '$header' not found") - } - // clear existing schedulebank - Clear() - // read each row and insert into database - val _schedulebankList = ArrayList() - //Logger.info{"Sheet last row num: ${sheet.lastRowNum}"} - for (rowIndex in 1..sheet.lastRowNum) { - val row = sheet.getRow(rowIndex) ?: continue - //println(row) - val description = row.getCell(1)?.stringCellValue ?: continue - //println(description.toString()) - val day = row.getCell(2)?.stringCellValue ?: continue - //println(day.toString()) - val time = row.getCell(3)?.stringCellValue ?: continue - //println(time.toString()) - val soundpath = row.getCell(4)?.stringCellValue ?: continue - //println(soundpath.toString()) - val repeat = row.getCell(5)?.stringCellValue?.toUByteOrNull() ?: continue - // println(repeat.toString()) - //val enable = row.getCell(6)?.stringCellValue?.toBooleanStrictOrNull() ?: continue - val enable = row.getCell(6)?.stringCellValue?.toBoolean() ?: continue - //println(enable.toString()) - val broadcastZones = row.getCell(7)?.stringCellValue ?: continue - //println(broadcastZones.toString()) - val language = row.getCell(8)?.stringCellValue ?: continue - //println(language.toString()) - val schedulebank = - ScheduleBank( - 0u, - description, - day, - time, - soundpath, - repeat, - enable, - broadcastZones, - language - ) - Logger.info{"SchedulebankList added 1"} - - _schedulebankList.add(schedulebank) - } - return scheduleDB.AddAll(_schedulebankList) - } catch (e: Exception) { - Logger.error { "Error importing Schedulebank, Msg: ${e.message}" } - } - return false - } - - override fun Export_XLSX(): XSSFWorkbook? { - try { - val statement = connection.createStatement() - val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") - val workbook = XSSFWorkbook() - val sheet = workbook.createSheet("Schedulebank") - val headerRow = sheet.createRow(0) - val headers = arrayOf( - "Index", - "Description", - "Day", - "Time", - "Soundpath", - "Repeat", - "Enable", - "BroadcastZones", - "Language" - ) - for ((colIndex, header) in headers.withIndex()) { - val cell = headerRow.createCell(colIndex) - cell.setCellValue(header) - } - var rowIndex = 1 - while (resultSet?.next() == true) { - val row = sheet.createRow(rowIndex++) - row.createCell(0).setCellValue(resultSet.getString("index")) - row.createCell(1).setCellValue(resultSet.getString("Description")) - row.createCell(2).setCellValue(resultSet.getString("Day")) - row.createCell(3).setCellValue(resultSet.getString("Time")) - row.createCell(4).setCellValue(resultSet.getString("Soundpath")) - row.createCell(5).setCellValue(resultSet.getString("Repeat")) - row.createCell(6).setCellValue(resultSet.getString("Enable")) - row.createCell(7).setCellValue(resultSet.getString("BroadcastZones")) - row.createCell(8).setCellValue(resultSet.getString("Language")) - } - for (i in headers.indices) { - sheet.autoSizeColumn(i) - } - return workbook - } catch (e: Exception) { - Logger.error { "Error exporting Schedulebank, Msg: ${e.message}" } - } - return null - } - - } - - broadcastDB = object : dbFunctions("broadcastzones", connection, listOf("index", "description", "SoundChannel", "id", "bp")) { - override fun Create() { - val tabledefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" + - "`index` INT AUTO_INCREMENT PRIMARY KEY," + - "description VARCHAR(512) NOT NULL," + // Description of the broadcast zone - "SoundChannel VARCHAR(45) NOT NULL," + // Sound channel of the broadcast zone - "id VARCHAR(45) NOT NULL," + // Box of the broadcast zone - "bp VARCHAR(45) NOT NULL" + // Relay of the broadcast zone - ")" - super.Create(tabledefinition) - } - - override fun Get(cbOK: Consumer?, cbFail: Consumer?) { - List.clear() - try { - val statement = connection.createStatement() - val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") - while (resultSet?.next() == true) { - val zone = BroadcastZones( - resultSet.getLong("index").toUInt(), - resultSet.getString("description"), - resultSet.getString("SoundChannel"), - resultSet.getString("id"), - resultSet.getString("bp") - ) - List.add(zone) - } - cbOK?.accept(Unit) - } catch (e: Exception) { - Logger.error("Error fetching ${super.dbName} : ${e.message}" as Any) - cbFail?.accept("Error fetching ${super.dbName} : ${e.message}" ) - } - } - - override fun Add(data: BroadcastZones): Boolean { - try { - val statement = - connection.prepareStatement("INSERT INTO ${super.dbName} (description, SoundChannel, id, bp) VALUES (?, ?, ?, ?)") - statement?.setString(1, data.description) - statement?.setString(2, data.SoundChannel) - statement?.setString(3, data.id) - statement?.setString(4, data.bp) - val rowsAffected = statement?.executeUpdate() - if (rowsAffected != null && rowsAffected > 0) { - Logger.info("Broadcast zone added: ${data.description}" as Any) - logDB.Add("AAS", "Broadcast zone added: ${data.description}; SoundChannel: ${data.SoundChannel}; id: ${data.id}; bp: ${data.bp}") - return true - } else { - logDB.Add("AAS","Failed adding Broadcast Zone for: ${data.description}.") - Logger.warn("No broadcast zone entry added for: ${data.description}" as Any) - } - } catch (e: Exception) { - logDB.Add("AAS","Failed adding Broadcast Zone for: ${data.description}. Error: ${e.message}") - Logger.error("Error adding broadcast zone entry: ${e.message}" as Any) - } - return false - } - - override fun AddAll(data: ArrayList): Boolean { - try { - connection.autoCommit = false - val sql = - "INSERT INTO ${super.dbName} (description, SoundChannel, id, bp) VALUES (?, ?, ?, ?)" - val statement = connection.prepareStatement(sql) - for (bz in data) { - statement.setString(1, bz.description) - statement.setString(2, bz.SoundChannel) - statement.setString(3, bz.id) - statement.setString(4, bz.bp) - statement.addBatch() - } - statement.executeBatch() - connection.commit() - Logger.info("Bulk ${super.dbName} insert successful: ${data.size} entries" as Any) - logDB.Add("AAS","Successfully Import Broadcast Zones Table: ${data.size} entries.") - connection.autoCommit = true - return true - } catch (e: Exception) { - logDB.Add("AAS","Failed to Import Broadcast Zones. Error: ${e.message}") - Logger.error("Error adding ${super.dbName} entries: ${e.message}" as Any) - } - return false - } - - override fun UpdateByIndex(index: Int, data: BroadcastZones): Boolean { - try { - val statement = - connection.prepareStatement("UPDATE ${super.dbName} SET description = ?, SoundChannel = ?, id = ?, bp = ? WHERE `index` = ?") - statement?.setString(1, data.description) - statement?.setString(2, data.SoundChannel) - statement?.setString(3, data.id) - statement?.setString(4, data.bp) - statement?.setLong(5, index.toLong()) - val rowsAffected = statement?.executeUpdate() - if (rowsAffected != null && rowsAffected > 0) { - Logger.info("Broadcast zone updated at index $index: ${data.description}" as Any) - logDB.Add("AAS", "Broadcast zone updated at index $index: ${data.description}; SoundChannel: ${data.SoundChannel}; id: ${data.id}; bp: ${data.bp}") - return true - } else { - logDB.Add("AAS", "Failed updating Broadcast Zone at index $index for: ${data.description}.") - Logger.warn("No broadcast zone entry updated at index $index for: ${data.description}" as Any) - } - } catch (e: Exception) { - logDB.Add("AAS", "Failed updating Broadcast Zone at index $index. Error: ${e.message}") - Logger.error("Error updating broadcast zone entry at index $index: ${e.message}" as Any) - } - return false - } - - override fun Resort(): Boolean { - try { - val statement = connection.createStatement() - val tempdb_name = "temp_${super.dbName}" - // 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 (description, SoundChannel, id, bp) SELECT description, SoundChannel, id, bp FROM ${super.dbName} ORDER BY description ") - statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") - statement?.executeUpdate("INSERT INTO ${super.dbName} (description, SoundChannel, id, bp) SELECT description, SoundChannel, id, bp FROM $tempdb_name") - statement?.executeUpdate("DROP TABLE $tempdb_name") - Logger.info("${super.dbName} table resorted by description" as Any) - // reload the local list - Get() - return true - } catch (e: Exception) { - Logger.error("Error resorting ${super.dbName} table by description: ${e.message}" as Any) - } - return false - } - - override fun Import_XLSX(workbook: XSSFWorkbook): Boolean { - try { - val sheet = workbook.getSheet("BroadcastZones") - ?: throw Exception("No sheet named 'BroadcastZones' found") - val headerRow = sheet.getRow(0) ?: throw Exception("No header row found") - val headers = arrayOf("Index", "description", "SoundChannel", "id", "bp") - for ((colIndex, header) in headers.withIndex()) { - val cell = headerRow.getCell(colIndex) ?: throw Exception("Header '$header' not found") - if (cell.stringCellValue != header) throw Exception("Header '$header' not found") - } - // clear existing broadcast_zones - Clear() - // read each row and insert into database - val _broadcastZonesList = ArrayList() - for (rowIndex in 1..sheet.lastRowNum) { - val row = sheet.getRow(rowIndex) ?: continue - val description = row.getCell(1)?.stringCellValue ?: continue - val soundChannel = row.getCell(2)?.stringCellValue ?: continue - val id = row.getCell(3)?.stringCellValue ?: continue - val bp = row.getCell(4)?.stringCellValue ?: continue - val broadcastZone = BroadcastZones(0u, description, soundChannel, id, bp) - _broadcastZonesList.add(broadcastZone) - } - return AddAll(_broadcastZonesList) - } catch (e: Exception) { - Logger.error { "Error importing BroadcastZones, Msg: ${e.message}" } - } - return false - } - - override fun Export_XLSX(): XSSFWorkbook? { - try { - val statement = connection.createStatement() - val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") - val workbook = XSSFWorkbook() - val sheet = workbook.createSheet("BroadcastZones") - val headerRow = sheet.createRow(0) - val headers = arrayOf("Index", "description", "SoundChannel", "id", "bp") - for ((colIndex, header) in headers.withIndex()) { - val cell = headerRow.createCell(colIndex) - cell.setCellValue(header) - } - var rowIndex = 1 - while (resultSet?.next() == true) { - val row = sheet.createRow(rowIndex++) - row.createCell(0).setCellValue(resultSet.getString("index")) - row.createCell(1).setCellValue(resultSet.getString("description")) - row.createCell(2).setCellValue(resultSet.getString("SoundChannel")) - row.createCell(3).setCellValue(resultSet.getString("id")) - row.createCell(4).setCellValue(resultSet.getString("bp")) - } - for (i in headers.indices) { - sheet.autoSizeColumn(i) - } - return workbook - } catch (e: Exception) { - Logger.error { "Error exporting BroadcastZones, Msg: ${e.message}" } - } - return null - } - - } - - queuetableDB = object : dbFunctions("queue_table", connection, listOf("index", "Date_Time", "Source", "Type", "Message", "SB_TAGS", "BroadcastZones", "Repeat", "Language")) { - override fun Create() { - val tabledefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" + - "`index` INT AUTO_INCREMENT PRIMARY KEY," + - "Date_Time VARCHAR(45) NOT NULL," + // Date and time of the entry - "Source VARCHAR(45) NOT NULL," + // Source of the entry - "Type VARCHAR(45) NOT NULL," + // Type of the entry - "Message VARCHAR(1024) NOT NULL," + // Message content - "SB_TAGS VARCHAR(1024)," + // Comma-separated soundbank tags - "BroadcastZones VARCHAR(1024) NOT NULL," + // Comma-separated broadcast zones - "`Repeat` INT NOT NULL," + // Number of repeats - "Language VARCHAR(100) NOT NULL" + // Language of the message - ")" - super.Create(tabledefinition) - } - - override fun Get(cbOK: Consumer?, cbFail: Consumer?) { - List.clear() - val queueList = ArrayList() - try { - val statement = connection.createStatement() - val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") - while (resultSet?.next() == true) { - val queueTable = QueueTable( - resultSet.getLong("index").toUInt(), - resultSet.getString("Date_Time"), - resultSet.getString("Source"), - resultSet.getString("Type"), - resultSet.getString("Message"), - resultSet.getString("SB_TAGS"), - resultSet.getString("BroadcastZones"), - resultSet.getInt("Repeat").toUInt(), - resultSet.getString("Language") - ) - queueList.add(queueTable) - List.add(queueTable) - } - cbOK?.accept(Unit) - } catch (e: Exception) { - Logger.error("Error fetching ${super.dbName} : ${e.message}" as Any) - cbFail?.accept("Error fetching ${super.dbName} : ${e.message}" ) - } - } - - override fun Add(data: QueueTable): Boolean { - try { - val statement = connection.prepareStatement( - "INSERT INTO ${super.dbName} (`Date_Time`, `Source`, `Type`, `Message`, `SB_TAGS`, `BroadcastZones`, `Repeat`, `Language`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" - ) - statement?.setString(1, data.Date_Time) - statement?.setString(2, data.Source) - statement?.setString(3, data.Type) - statement?.setString(4, data.Message) - statement?.setString(5, data.SB_TAGS) - statement?.setString(6, data.BroadcastZones) - statement?.setInt(7, data.Repeat.toInt()) - statement?.setString(8, data.Language) - val rowsAffected = statement?.executeUpdate() - if (rowsAffected != null && rowsAffected > 0) { - Logger.info("QueueTable added Source=${data.Source} Type=${data.Type} Message=${data.Message}, Languages=${data.Language} Variables=${data.SB_TAGS}, BroadcastZones=${data.BroadcastZones}" as Any) - return true - } else { - Logger.warn("No QueueTable entry added for: ${data.Message}" as Any) - } - } catch (e: Exception) { - Logger.error("Error adding QueueTable entry: ${e.message}" as Any) - } - return false - } - - override fun AddAll(data: ArrayList): Boolean { - try { - connection.autoCommit = false - val sql = - "INSERT INTO ${super.dbName} (`Date_Time`, `Source`, `Type`, `Message`, `SB_TAGS`, `BroadcastZones`, `Repeat`, `Language`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" - val statement = connection.prepareStatement(sql) - for (qt in data) { - statement.setString(1, qt.Date_Time) - statement.setString(2, qt.Source) - statement.setString(3, qt.Type) - statement.setString(4, qt.Message) - statement.setString(5, qt.SB_TAGS) - statement.setString(6, qt.BroadcastZones) - statement.setInt(7, qt.Repeat.toInt()) - statement.setString(8, qt.Language) - statement.addBatch() - } - statement.executeBatch() - connection.commit() - Logger.info("Bulk QueueTable insert successful: ${data.size} entries" as Any) - logDB.Add("AAS","Successfully Import Queue Table: ${data.size} entries.") - connection.autoCommit = true - return true - } catch (e: Exception) { - logDB.Add("AAS","Failed to Import Queue Table. Error: ${e.message}") - Logger.error("Error adding QueueTable entries: ${e.message}" as Any) - } - return false - } - - override fun UpdateByIndex(index: Int, data: QueueTable): Boolean { - throw Exception("Update not supported") - } - - override fun Resort(): Boolean { - try { - val statement = connection.createStatement() - val tempdb_name = "temp_${super.dbName}" - // 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, BroadcastZones, `Repeat`, Language) SELECT Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, `Repeat`, Language FROM ${super.dbName} ORDER BY `index` ") - statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") - statement?.executeUpdate("INSERT INTO ${super.dbName} (Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, `Repeat`, Language) SELECT Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, `Repeat`, Language FROM $tempdb_name") - statement?.executeUpdate("DROP TABLE $tempdb_name") - Logger.info("${super.dbName} table resorted by index" as Any) - // reload the local list - Get() - return true - } catch (e: Exception) { - Logger.error("Error resorting ${super.dbName} table by index: ${e.message}" as Any) - } - return false - } - - override fun Import_XLSX(workbook: XSSFWorkbook): Boolean { - throw Exception("Import XLSX not supported for QueueTable") - } - - override fun Export_XLSX(): XSSFWorkbook? { - try { - val statement = connection.createStatement() - val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") - val workbook = XSSFWorkbook() - val sheet = workbook.createSheet("QueueTable") - val headerRow = sheet.createRow(0) - val headers = arrayOf( - "Index", - "Date_Time", - "Source", - "Type", - "Message", - "SB_TAGS", - "BroadcastZones", - "Repeat", - "Language" - ) - for ((colIndex, header) in headers.withIndex()) { - val cell = headerRow.createCell(colIndex) - cell.setCellValue(header) - } - var rowIndex = 1 - while (resultSet?.next() == true) { - val row = sheet.createRow(rowIndex++) - row.createCell(0).setCellValue(resultSet.getString("index")) - row.createCell(1).setCellValue(resultSet.getString("Date_Time")) - 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(6).setCellValue(resultSet.getString("BroadcastZones")) - row.createCell(7).setCellValue(resultSet.getString("Repeat")) - row.createCell(8).setCellValue(resultSet.getString("Language")) - } - for (i in headers.indices) { - sheet.autoSizeColumn(i) - } - return workbook - } catch (e: Exception) { - Logger.error { "Error exporting QueueTable, Msg: ${e.message}" } - } - return null - } - - } - - queuepagingDB = object : dbFunctions("queue_paging", connection, listOf("index", "Date_Time", "Source", "Type", "Message", "BroadcastZones")) { - override fun Create() { - val tabledefinition ="CREATE TABLE IF NOT EXISTS ${super.dbName} (" + - "`index` INT AUTO_INCREMENT PRIMARY KEY," + - "Date_Time VARCHAR(45) NOT NULL," + // Date and time of the entry - "Source VARCHAR(45) NOT NULL," + // Source of the entry - "Type VARCHAR(45) NOT NULL," + // Type of the entry - "Message VARCHAR(1024) NOT NULL," + // Message content - "BroadcastZones VARCHAR(1024)" + // Comma-separated soundbank tags - ")" - super.Create(tabledefinition) - } - - override fun Get(cbOK: Consumer?, cbFail: Consumer?) { - List.clear() - val queueList = ArrayList() - try { - val statement = connection.createStatement() - val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") - while (resultSet?.next() == true) { - val queuePaging = QueuePaging( - resultSet.getLong("index").toUInt(), - resultSet.getString("Date_Time"), - resultSet.getString("Source"), - resultSet.getString("Type"), - resultSet.getString("Message"), - resultSet.getString("BroadcastZones"), - ) - queueList.add(queuePaging) - List.add(queuePaging) - } - cbOK?.accept(Unit) - } catch (e: Exception) { - Logger.error("Error fetching ${super.dbName} : ${e.message}" as Any) - cbFail?.accept("Error fetching ${super.dbName} : ${e.message}" ) - } - } - - override fun Add(data: QueuePaging): Boolean { - try { - val statement = connection.prepareStatement( - "INSERT INTO ${super.dbName} (Date_Time, Source, Type, Message, BroadcastZones) VALUES (?, ?, ?, ?, ?)" - ) - statement?.setString(1, data.Date_Time) - statement?.setString(2, data.Source) - statement?.setString(3, data.Type) - statement?.setString(4, data.Message) - statement?.setString(5, data.BroadcastZones) - val rowsAffected = statement?.executeUpdate() - if (rowsAffected != null && rowsAffected > 0) { - Logger.info("QueuePaging added: ${data.Message}" as Any) - return true - } else { - Logger.warn("No QueuePaging entry added for: ${data.Message}" as Any) - } - } catch (e: Exception) { - Logger.error("Error adding QueuePaging entry: ${e.message}" as Any) - } - return false - } - - override fun AddAll(data: ArrayList): Boolean { - return try { - connection.autoCommit = false - val sql = - "INSERT INTO ${super.dbName} (Date_Time, Source, Type, Message, BroadcastZones) VALUES (?, ?, ?, ?, ?)" - val statement = connection.prepareStatement(sql) - for (qp in data) { - statement.setString(1, qp.Date_Time) - statement.setString(2, qp.Source) - statement.setString(3, qp.Type) - statement.setString(4, qp.Message) - statement.setString(5, qp.BroadcastZones) - statement.addBatch() - } - statement.executeBatch() - connection.commit() - Logger.info("Bulk QueuePaging insert successful: ${data.size} entries" as Any) - logDB.Add("AAS","Successfully Import Queue Paging Table: ${data.size} entries.") - connection.autoCommit = true - true - } catch (e: Exception) { - logDB.Add("AAS","Failed to Import Queue Paging Table. Error: ${e.message}") - Logger.error("Error adding QueuePaging entries: ${e.message}" as Any) - false - } - } - - override fun UpdateByIndex(index: Int, data: QueuePaging): Boolean { - throw Exception("Update not supported") - } - - override fun Resort(): Boolean { - try { - val statement = connection.createStatement() - val tempdb_name = "temp_${super.dbName}" - // 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, 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, 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 - Get() - return true - } catch (e: Exception) { - Logger.error("Error resorting ${super.dbName} table by index: ${e.message}" as Any) - } - return false - } - - override fun Import_XLSX(workbook: XSSFWorkbook): Boolean { - throw Exception("Importing QueuePaging from XLSX is not supported") - } - - override fun Export_XLSX(): XSSFWorkbook? { - try { - val statement = connection.createStatement() - val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") - val workbook = XSSFWorkbook() - val sheet = workbook.createSheet("QueuePaging") - val headerRow = sheet.createRow(0) - val headers = arrayOf("Index", "Date_Time", "Source", "Type", "Message", "BroadcastZones") - for ((colIndex, header) in headers.withIndex()) { - val cell = headerRow.createCell(colIndex) - cell.setCellValue(header) - } - var rowIndex = 1 - while (resultSet?.next() == true) { - val row = sheet.createRow(rowIndex++) - row.createCell(0).setCellValue(resultSet.getString("index")) - row.createCell(1).setCellValue(resultSet.getString("Date_Time")) - 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("BroadcastZones")) - } - for (i in headers.indices) { - sheet.autoSizeColumn(i) - } - return workbook - } catch (e: Exception) { - Logger.error { "Error exporting QueuePaging, Msg: ${e.message}" } - } - return null - } - - } - - soundchannelDB = object : dbFunctions("soundchannel", connection, listOf("index", "channel", "ip")) { - override fun Create() { - val tableDefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" + - "`index` INT AUTO_INCREMENT PRIMARY KEY," + - "channel VARCHAR(45) NOT NULL," + // Channel 01 to Channel 64 - "ip VARCHAR(45) NOT NULL" + // IP address or empty string - ")" - - super.Create(tableDefinition) - - // Check if table is empty, if so, populate with 64 channels - try { - val statement = connection.createStatement() - val countResult = statement?.executeQuery("SELECT COUNT(*) AS count FROM ${super.dbName}") - if (countResult?.next() == true) { - val count = countResult.getInt("count") - if (count < max_channel) { - Logger.info("SoundChannel table is empty, populating with default channels" as Any) - Clear() - } - } - } catch (e: Exception) { - Logger.error("Error creating SoundChannel table: ${e.message}" as Any) - } - } - - override fun Get(cbOK: Consumer?, cbFail: Consumer?) { - List.clear() - try { - val statement = connection.createStatement() - val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName} ORDER BY `index` ") - while (resultSet?.next() == true) { - val channel = SoundChannel( - resultSet.getLong("index").toUInt(), - resultSet.getString("channel"), - resultSet.getString("ip") - ) - List.add(channel) - } - cbOK?.accept(Unit) - } catch (e: Exception) { - Logger.error("Error fetching sound channels: ${e.message}" as Any) - cbFail?.accept("Error fetching sound channels: ${e.message}" ) - } - } - - override fun Add(data: SoundChannel): Boolean { - try { - val statement = connection.prepareStatement("UPDATE ${super.dbName} SET ip = ? WHERE channel = ?") - statement?.setString(1, data.ip) - statement?.setString(2, data.channel) - val rowsAffected = statement?.executeUpdate() - if (rowsAffected != null && rowsAffected > 0) { - Logger.info("SoundChannel updated: ${data.channel} -> ${data.ip}" as Any) - logDB.Add("AAS", "Sound Channel updated: ${data.channel} -> ${data.ip}") - return true - } else { - logDB.Add("AAS", "Failed updating Sound Channel for: ${data.channel} -> ${data.ip}.") - Logger.warn("No SoundChannel entry updated for: ${data.channel} -> ${data.ip}" as Any) - } - } catch (e: Exception) { - logDB.Add("AAS", "Failed updating Sound Channel for: ${data.channel} -> ${data.ip}. Error: ${e.message}") - Logger.error("Error updating SoundChannel entry: ${e.message}" as Any) - } - return false - } - - override fun AddAll(data: ArrayList): Boolean { - return try { - connection.autoCommit = false - val sql = "UPDATE ${super.dbName} SET ip = ? WHERE channel = ?" - val statement = connection.prepareStatement(sql) - for (sc in data) { - statement.setString(1, sc.ip) - statement.setString(2, sc.channel) - statement.addBatch() - } - statement.executeBatch() - connection.commit() - Logger.info("Bulk SoundChannel update successful: ${data.size} entries" as Any) - logDB.Add("AAS","Successfully Import Sound Channels Table: ${data.size} entries.") - connection.autoCommit = true - true - } catch (e: Exception) { - logDB.Add("AAS","Failed to Import Sound Channels. Error: ${e.message}") - Logger.error("Error updating SoundChannel entries: ${e.message}" as Any) - false - } - } - - override fun UpdateByIndex(index: Int, data: SoundChannel): Boolean { - try { - val statement = - connection.prepareStatement("UPDATE ${super.dbName} SET channel = ?, ip = ? WHERE `index` = ?") - statement?.setString(1, data.channel) - statement?.setString(2, data.ip) - statement?.setLong(3, index.toLong()) - val rowsAffected = statement?.executeUpdate() - if (rowsAffected != null && rowsAffected > 0) { - Logger.info("SoundChannel updated at index $index: ${data.channel} -> ${data.ip}" as Any) - logDB.Add("AAS", "Sound Channel updated at index $index: ${data.channel} -> ${data.ip}") - return true - } else { - logDB.Add("AAS", "Failed updating Sound Channel at index $index for: ${data.channel} -> ${data.ip}.") - Logger.warn("No Sound Channel entry updated at index $index for: ${data.channel} -> ${data.ip}" as Any) - } - } catch (e: Exception) { - logDB.Add("AAS", "Failed updating Sound Channel at index $index. Error: ${e.message}") - Logger.error("Error updating SoundChannel entry at index $index: ${e.message}" as Any) - } - return false - } - - override fun Resort(): Boolean { - try { - val statement = connection.createStatement() - val tempdb_name = "temp_${super.dbName}" - // 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 (channel, ip) SELECT channel, ip FROM ${super.dbName} ORDER BY `index` ") - statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") - statement?.executeUpdate("INSERT INTO ${super.dbName} (channel, ip) SELECT channel, ip FROM $tempdb_name") - statement?.executeUpdate("DROP TABLE $tempdb_name") - Logger.info("${super.dbName} table resorted by index" as Any) - // reload the local list - Get() - return true - } catch (e: Exception) { - Logger.error("Error resorting ${super.dbName} table by index: ${e.message}" as Any) - } - return false - } - - override fun Clear(): Boolean { - try { - val statement = connection.createStatement() - // use TRUNCATE to reset auto increment index - statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") - Logger.info("${super.dbName} table cleared" as Any) - List.clear() - // create new rows from 1 to 64 with description "Channel 1" to "Channel 64" and empty ip - for (i in 1..max_channel) { - val channel = String.format("Channel %d", i) - val insertStatement = - connection.prepareStatement("INSERT INTO ${super.dbName} (channel, ip) VALUES (?, ?)") - insertStatement?.setString(1, channel) - insertStatement?.setString(2, "") - insertStatement?.executeUpdate() - } - return true - } catch (e: Exception) { - Logger.error("Error clearing soundchannel table: ${e.message}" as Any) - } - return false - } - - override fun Import_XLSX(workbook: XSSFWorkbook): Boolean { - try { - val sheet = - workbook.getSheet("SoundChannel") ?: throw Exception("No sheet named 'SoundChannel' found") - val headerRow = sheet.getRow(0) ?: throw Exception("No header row found") - val headers = arrayOf("Index", "channel", "ip") - for ((colIndex, header) in headers.withIndex()) { - val cell = headerRow.getCell(colIndex) ?: throw Exception("Header '$header' not found") - if (cell.stringCellValue != header) throw Exception("Header '$header' not found") - } - // clear existing soundchannel - Clear() - // read each row and insert into database - val _soundChannelList = ArrayList() - for (rowIndex in 1..sheet.lastRowNum) { - val row = sheet.getRow(rowIndex) ?: continue - val channel = row.getCell(1)?.stringCellValue ?: continue - val ip = row.getCell(2)?.stringCellValue ?: continue - val soundChannel = SoundChannel(0u, channel, ip) - _soundChannelList.add(soundChannel) - } - // Bulk update IPs for channels - var success = true - for (sc in _soundChannelList) { - if (!soundchannelDB.Add(sc)) { - success = false - } - } - return success - } catch (e: Exception) { - Logger.error { "Error importing SoundChannel, Msg: ${e.message}" } - } - return false - } - - override fun Export_XLSX(): XSSFWorkbook? { - try { - val statement = connection.createStatement() - val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") - val workbook = XSSFWorkbook() - val sheet = workbook.createSheet("SoundChannel") - val headerRow = sheet.createRow(0) - val headers = arrayOf("Index", "channel", "ip") - for ((colIndex, header) in headers.withIndex()) { - val cell = headerRow.createCell(colIndex) - cell.setCellValue(header) - } - var rowIndex = 1 - while (resultSet?.next() == true) { - val row = sheet.createRow(rowIndex++) - row.createCell(0).setCellValue(resultSet.getString("index")) - row.createCell(1).setCellValue(resultSet.getString("channel")) - row.createCell(2).setCellValue(resultSet.getString("ip")) - } - for (i in headers.indices) { - sheet.autoSizeColumn(i) - } - return workbook - } catch (e: Exception) { - Logger.error { "Error exporting SoundChannel, Msg: ${e.message}" } - } - return null - } - - /** - * Delete entry by index, but only clear the IP field - * @param index The index of the entry to delete - * @return true if successful, false otherwise - */ - override fun DeleteByIndex(index: Int): Boolean { - try { - val statement = connection.prepareStatement("UPDATE ${super.dbName} SET ip = '' WHERE `index` = ?") - statement?.setLong(1, index.toLong()) - val rowsAffected = statement?.executeUpdate() - if (rowsAffected != null && rowsAffected > 0) { - Logger.info("${super.dbName} IP cleared for index $index" as Any) - logDB.Add("AAS", "${super.dbName} IP cleared for index $index") - return true - } else { - logDB.Add("AAS","Failed clearing ${super.dbName} IP for index $index.") - Logger.warn("No ${super.dbName} entry cleared for index $index" as Any) - } - } catch (e: Exception) { - logDB.Add("AAS","Failed clearing ${super.dbName} IP for index $index. Error: ${e.message}") - Logger.error("Error clearing ${super.dbName} entry for index $index: ${e.message}" as Any) - } - return false - } - - } - + soundDB = Table_Soundbank(connection) + messageDB = Table_Messagebank(connection) + languageDB = Table_LanguageLink(connection) + scheduleDB = Table_Schedule(connection) + broadcastDB = Table_BroadcastZones(connection) + queuetableDB = Table_QueueSoundbank(connection) + queuepagingDB = Table_QueuePaging(connection) + soundchannelDB = Table_SoundChannel(connection) logSemiAuto = Table_LogSemiAuto(connection) - logDB = Table_Logs(connection) - - - userDB = object : dbFunctions("newuser", connection, listOf("index", "username", "password", "location", "airline_tags", "city_tags", "messagebank_ann_id", "broadcastzones")) { - override fun Create() { - val tableDefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" + - "`index` INT AUTO_INCREMENT PRIMARY KEY," + - "username VARCHAR(100) NOT NULL," + - "password VARCHAR(100) NOT NULL," + - "location VARCHAR(100) NOT NULL," + - "airline_tags TEXT NOT NULL,"+ // Comma-separated soundbank tags - "city_tags TEXT NOT NULL,"+ // Comma-separated soundbank tags - "messagebank_ann_id TEXT NOT NULL,"+ // Comma-separated messagebank announcement index - "broadcastzones TEXT NOT NULL"+ // Comma-separated broadcast zones - ")" - super.Create(tableDefinition) - } - - override fun Get(cbOK: Consumer?, cbFail: Consumer?) { - List.clear() - try { - val statement = connection.createStatement() - val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") - while (resultSet?.next() == true) { - val user = UserDB( - resultSet.getLong("index").toUInt(), - resultSet.getString("username"), - resultSet.getString("password"), - resultSet.getString("location"), - resultSet.getString("airline_tags"), - resultSet.getString("city_tags"), - resultSet.getString("messagebank_ann_id"), - resultSet.getString("broadcastzones") - ) - List.add(user) - } - cbOK?.accept(Unit) - } catch (e: Exception) { - Logger.error("Error fetching users: ${e.message}" as Any) - cbFail?.accept("Error fetching users: ${e.message}" ) - } - } - - override fun Add(data: UserDB): Boolean { - try { - val statement = connection.prepareStatement("INSERT INTO ${super.dbName} (username, password, location, airline_tags, city_tags, messagebank_ann_id, broadcastzones) VALUES (?, ?, ?, ?,?, ?, ?)") - statement?.setString(1, data.username) - statement?.setString(2, data.password) - statement?.setString(3, data.location) - statement?.setString(4, data.airline_tags) - statement?.setString(5, data.city_tags) - statement?.setString(6, data.messagebank_ann_id) - statement?.setString(7, data.broadcastzones) - val rowsAffected = statement?.executeUpdate() - if (rowsAffected != null && rowsAffected > 0) { - Logger.info("User added: ${data.username}" as Any) - logDB.Add("AAS", "User added: ${data.username}") - return true - } else { - logDB.Add("AAS","Failed adding User entry for: ${data.username}.") - Logger.warn("No user entry added for: ${data.username}" as Any) - } - } catch (e: Exception) { - logDB.Add("AAS","Failed adding User entry for: ${data.username}. Error: ${e.message}") - Logger.error("Error adding user entry: ${e.message}" as Any) - } - return false - } - - override fun AddAll(data: ArrayList): Boolean { - return try { - connection.autoCommit = false - val sql = "INSERT INTO ${super.dbName} (username, password, location,airline_tags,city_tags, messagebank_ann_id, broadcastzones) VALUES (?, ?, ?,?, ?, ?, ?)" - val statement = connection.prepareStatement(sql) - for (user in data) { - statement.setString(1, user.username) - statement.setString(2, user.password) - statement.setString(3, user.location) - statement.setString(4, user.airline_tags) - statement.setString(5, user.city_tags) - statement.setString(6, user.messagebank_ann_id) - statement.setString(7, user.broadcastzones) - statement.addBatch() - } - statement.executeBatch() - connection.commit() - Logger.info("Bulk user insert successful: ${data.size} entries" as Any) - logDB.Add("AAS","Successfully Import User Table: ${data.size} entries.") - connection.autoCommit = true - true - } catch (e: Exception) { - logDB.Add("AAS","Failed to Import User Table. Error: ${e.message}") - Logger.error("Error adding user entries: ${e.message}" as Any) - false - } - } - - override fun UpdateByIndex(index: Int, data: UserDB): Boolean { - try { - val statement = connection.prepareStatement("UPDATE ${super.dbName} SET username = ?, password = ?, location = ?, airline_tags = ?,city_tags=?, messagebank_ann_id = ?, broadcastzones = ? WHERE `index` = ?") - statement?.setString(1, data.username) - statement?.setString(2, data.password) - statement?.setString(3, data.location) - statement?.setString(4, data.airline_tags) - statement?.setString(5, data.city_tags) - statement?.setString(6, data.messagebank_ann_id) - statement?.setString(7, data.broadcastzones) - statement?.setLong(8, index.toLong()) - val rowsAffected = statement?.executeUpdate() - if (rowsAffected != null && rowsAffected > 0) { - Logger.info("User updated at index $index: ${data.username}" as Any) - logDB.Add("AAS", "User updated at index $index: ${data.username}") - return true - } else { - logDB.Add("AAS", "Failed updating User at index $index for: ${data.username}.") - Logger.warn("No user entry updated at index $index for: ${data.username}" as Any) - } - } catch (e: Exception) { - logDB.Add("AAS", "Failed updating User at index $index. Error: ${e.message}") - Logger.error("Error updating user entry at index $index: ${e.message}" as Any) - } - return false - } - - override fun Resort(): Boolean { - try { - val statement = connection.createStatement() - val tempdb_name = "temp_${super.dbName}" - // 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 (username, password, location, airline_tags, city_tags, messagebank_ann_id, broadcastzones) SELECT username, password, location, airline_tags, city_tags, messagebank_ann_id, broadcastzones FROM ${super.dbName} ORDER BY username ") - statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") - statement?.executeUpdate("INSERT INTO ${super.dbName} (username, password, location, airline_tags, city_tags, messagebank_ann_id, broadcastzones) SELECT username, password, location, airline_tags, city_tags, messagebank_ann_id, broadcastzones FROM $tempdb_name") - statement?.executeUpdate("DROP TABLE $tempdb_name") - Logger.info("${super.dbName} table resorted by index" as Any) - // reload the local list - Get() - return true - } catch (e: Exception) { - Logger.error("Error resorting ${super.dbName} table by index: ${e.message}" as Any) - } - return false - } - - override fun Import_XLSX(workbook: XSSFWorkbook): Boolean { - try { - val sheet = workbook.getSheet("User") ?: throw Exception("No sheet named 'User' found") - val headerRow = sheet.getRow(0) ?: throw Exception("No header row found") - val headers = arrayOf("Index", "username", "password", "location", "airline_tags", "city_tags", "messagebank_ann_id", "broadcastzones") - for ((colIndex, header) in headers.withIndex()) { - val cell = headerRow.getCell(colIndex) ?: throw Exception("Header '$header' not found") - if (cell.stringCellValue != header) throw Exception("Header '$header' not found") - } - // clear existing users - Clear() - // read each row and insert into database - val _userList = ArrayList() - for (rowIndex in 1..sheet.lastRowNum) { - val row = sheet.getRow(rowIndex) ?: continue - val username = row.getCell(1)?.stringCellValue ?: continue - val password = row.getCell(2)?.stringCellValue ?: continue - val location = row.getCell(3)?.stringCellValue ?: continue - val airline_tags = row.getCell(4)?.stringCellValue ?: continue - val city_tags = row.getCell(5)?.stringCellValue ?: continue - val messagebank_ann_id = row.getCell(6)?.stringCellValue ?: continue - val broadcastzones = row.getCell(7)?.stringCellValue ?: continue - val user = UserDB(0u, username, password, location, airline_tags,city_tags, messagebank_ann_id, broadcastzones) - _userList.add(user) - } - return AddAll(_userList) - } catch (e: Exception) { - Logger.error { "Error importing User, Msg: ${e.message}" } - } - return false - } - - override fun Export_XLSX(): XSSFWorkbook? { - try { - val statement = connection.createStatement() - val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") - val workbook = XSSFWorkbook() - val sheet = workbook.createSheet("User") - val headerRow = sheet.createRow(0) - val headers = arrayOf("Index", "username", "password", "location", "airline_tags","city_tags", "messagebank_ann_id", "broadcastzones") - for ((colIndex, header) in headers.withIndex()) { - val cell = headerRow.createCell(colIndex) - cell.setCellValue(header) - } - var rowIndex = 1 - while (resultSet?.next() == true) { - val row = sheet.createRow(rowIndex++) - row.createCell(0).setCellValue(resultSet.getString("index")) - row.createCell(1).setCellValue(resultSet.getString("username")) - row.createCell(2).setCellValue(resultSet.getString("password")) - row.createCell(3).setCellValue(resultSet.getString("location")) - row.createCell(4).setCellValue(resultSet.getString("airline_tags")) - row.createCell(5).setCellValue(resultSet.getString("city_tags")) - row.createCell(6).setCellValue(resultSet.getString("messagebank_ann_id")) - row.createCell(7).setCellValue(resultSet.getString("broadcastzones")) - } - for (i in headers.indices) { - sheet.autoSizeColumn(i) - } - return workbook - } catch (e: Exception) { - Logger.error { "Error exporting User, Msg: ${e.message}" } - } - return null - } - - } + userDB = Table_Users(connection) runBlocking { withContext(Dispatchers.IO) { @@ -2038,7 +142,6 @@ class MariaDB( Logger.info { "User count: ${userDB.List.size}" } - } catch (e: Exception) { Logger.error("Failed to connect to MariaDB: ${e.message}" as Any) } @@ -2058,192 +161,5 @@ class MariaDB( connected = false } - fun GetSemiAutoLogForHTML(date: String, consumer: Consumer>) { - val logList = ArrayList() - try { - val statement = connection.prepareStatement("SELECT * FROM logsemiauto WHERE date = ?") - statement?.setString(1, date) - val resultSet = statement?.executeQuery() - while (resultSet?.next() == true) { - val log = LogSemiauto( - resultSet.getLong("index").toULong(), - resultSet.getString("date"), - resultSet.getString("time"), - resultSet.getString("source"), - resultSet.getString("description") - ) - logList.add(log) - } - } catch (e: Exception) { - Logger.error("Error fetching logsemiauto table for date $date: ${e.message}" as Any) - } - consumer.accept(logList) - } - - /** - * Exports the log table to an XLSX workbook for a specific date and optional filter. - * @param logDate The date string in format "dd/MM/yyyy". - * @param logFilter The filter string for the description or machine. If empty, exports all logs for the date. - * @return An XSSFWorkbook containing the filtered log data, or null if an error occurred. - */ - fun Export_Log_XLSX(logDate: String, logFilter: String): XSSFWorkbook? { - try { - val statement = connection.prepareStatement( - if (logFilter.isBlank()) { - "SELECT * FROM logs WHERE datenya = ?" - } else { - "SELECT * FROM logs WHERE datenya = ? AND (description LIKE ?)" - } - ) - statement?.setString(1, logDate) - if (!logFilter.isBlank()) { - val filter = "%$logFilter%" - statement?.setString(2, filter) - } - val resultSet = statement?.executeQuery() - val workbook = XSSFWorkbook() - val sheet = workbook.createSheet("Log") - val headerRow = sheet.createRow(0) - val headers = arrayOf("Index", "datenya", "timenya", "machine", "description") - for ((colIndex, header) in headers.withIndex()) { - val cell = headerRow.createCell(colIndex) - cell.setCellValue(header) - } - var rowIndex = 1 - while (resultSet?.next() == true) { - val row = sheet.createRow(rowIndex++) - row.createCell(0).setCellValue(resultSet.getString("index")) - row.createCell(1).setCellValue(resultSet.getString("datenya")) - row.createCell(2).setCellValue(resultSet.getString("timenya")) - row.createCell(3).setCellValue(resultSet.getString("machine")) - row.createCell(4).setCellValue(resultSet.getString("description")) - } - for (i in headers.indices) { - sheet.autoSizeColumn(i) - } - return workbook - } catch (e: Exception) { - Logger.error { "Error exporting Log, Msg: ${e.message}" } - } - return null - } - - /** - * Find all city soundbank by tag - * @param tag The tags to search for - * @return a list of Soundbank with Category City and matching tag - */ - fun Find_Soundbank_City(tag: String) : List{ - return soundDB.List - .filter{ it.Category== Category.City.name } - .filter { it.TAG.equals(tag,true)} - } - - fun Find_Soundbank_AirplaneName(tag: String) : List{ - return soundDB.List - .filter{ it.Category== Category.Airplane_Name.name } - .filter { it.TAG.equals(tag,true)} - } - - fun Find_Soundbank_Places(tag: String) : List{ - return soundDB.List - .filter{ it.Category== Category.Places.name } - .filter { it.TAG.equals(tag,true)} - } - - fun Find_Soundbank_Shalat(tag: String) : List{ - val lowerTag = tag.lowercase() - return soundDB.List - .filter{ it.Category== Category.Shalat.name } - .filter { it.TAG.lowercase()==lowerTag} - } - - fun Find_Soundbank_Sequence(tag: String) : List{ - return soundDB.List - .filter{ it.Category== Category.Sequence.name } - .filter { it.TAG.equals(tag,true)} - } - - fun Find_Soundbank_Reason(tag: String) : List{ - return soundDB.List - .filter{ it.Category== Category.Reason.name } - .filter { it.TAG.equals(tag,true)} - } - - fun Find_Soundbank_Procedure(tag: String) : List { - return soundDB.List - .filter { it.Category == Category.Procedure.name } - .filter { it.TAG.equals(tag,true) } - } - - /** - * Get all distinct airline code tags from soundbank - * @return a list of distinct airline code tags sorted alphabetically - */ - fun Get_AirlineCode_Tags(): List { - - return soundDB.List - .filter { it.Category.equals(Category.Airline_Code.name,true) } - .distinctBy { it.TAG } - .map { it.TAG } - .sorted() - } - - /** - * Get all distinct city tags from soundbank - * @return a list of distinct city tags sorted alphabetically - */ - fun Get_City_Tags(): List { - return soundDB.List - .filter { it.Category.equals(Category.City.name,true) } - .distinctBy { it.TAG } - .map { it.TAG } - .sorted() - } - - /** - * Get all distinct message ID from messagebank - * @return a list of distinct ANN_ID sorted numerically - */ - fun Get_MessageID_List(): List { - return messageDB.List - .distinctBy { it.ANN_ID } - .map { it.ANN_ID } - .sorted() - } - - /** - * Get all distinct broadcast zone descriptions from broadcastDB - * @return a list of distinct broadcast zone descriptions sorted alphabetically - */ - fun Get_BroadcastZone_List(): List { - return broadcastDB.List - .distinctBy { it.description } - .map { it.description } - .sorted() - } - - /** - * Get all distinct sound channel from soundchannelDB - * @return a list of distinct sound channel sorted alphabetically - */ - fun Get_SoundChannel_List(): List { - return soundchannelDB.List - .distinctBy { it.channel } - .map { it.channel } - .sorted() - } - - /** - * Check if a username already exists in the userDB (case-insensitive) - */ - fun Username_exists(username: String): Boolean { - return userDB.List.any { it.username.equals(username, ignoreCase = true) } - } - - - - - } \ No newline at end of file diff --git a/src/database/BroadcastZones.kt b/src/database/data/BroadcastZones.kt similarity index 98% rename from src/database/BroadcastZones.kt rename to src/database/data/BroadcastZones.kt index f98711e..e85063f 100644 --- a/src/database/BroadcastZones.kt +++ b/src/database/data/BroadcastZones.kt @@ -1,4 +1,4 @@ -package database +package database.data @Suppress("unused") data class BroadcastZones(var index: UInt, var description: String, var SoundChannel: String, var id: String, var bp: String){ @@ -49,4 +49,4 @@ data class BroadcastZones(var index: UInt, var description: String, var SoundCha return result } } -} +} \ No newline at end of file diff --git a/src/database/LanguageLink.kt b/src/database/data/LanguageLink.kt similarity index 96% rename from src/database/LanguageLink.kt rename to src/database/data/LanguageLink.kt index bea931d..a5f0498 100644 --- a/src/database/LanguageLink.kt +++ b/src/database/data/LanguageLink.kt @@ -1,4 +1,4 @@ -package database +package database.data data class LanguageLink(var index: UInt, var TAG: String, var Language: String){ @@ -19,4 +19,4 @@ data class LanguageLink(var index: UInt, var TAG: String, var Language: String){ return "LanguageLink(index=$index, TAG='$TAG', Language='$Language')" } -} +} \ No newline at end of file diff --git a/src/database/Log.kt b/src/database/data/Log.kt similarity index 97% rename from src/database/Log.kt rename to src/database/data/Log.kt index 6b69ea1..f123569 100644 --- a/src/database/Log.kt +++ b/src/database/data/Log.kt @@ -1,4 +1,4 @@ -package database +package database.data import java.time.LocalDate import java.time.LocalTime @@ -30,4 +30,4 @@ data class Log( override fun toString() : String { return "$datenya $timenya [$machine] $description" } -} +} \ No newline at end of file diff --git a/src/database/LogSemiauto.kt b/src/database/data/LogSemiauto.kt similarity index 96% rename from src/database/LogSemiauto.kt rename to src/database/data/LogSemiauto.kt index 36b7abc..f23f2eb 100644 --- a/src/database/LogSemiauto.kt +++ b/src/database/data/LogSemiauto.kt @@ -1,4 +1,4 @@ -package database +package database.data import java.time.LocalDate import java.time.LocalTime @@ -15,4 +15,4 @@ data class LogSemiauto(val index: ULong, val date: String, val time: String, val return LogSemiauto(0u, date, time, source, description) } } -} +} \ No newline at end of file diff --git a/src/database/Messagebank.kt b/src/database/data/Messagebank.kt similarity index 97% rename from src/database/Messagebank.kt rename to src/database/data/Messagebank.kt index 2da8a0c..ed39a47 100644 --- a/src/database/Messagebank.kt +++ b/src/database/data/Messagebank.kt @@ -1,4 +1,4 @@ -package database +package database.data data class Messagebank( var index : UInt, @@ -31,4 +31,4 @@ data class Messagebank( 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')" } -} +} \ No newline at end of file diff --git a/src/database/QueueFids.kt b/src/database/data/QueueFids.kt similarity index 95% rename from src/database/QueueFids.kt rename to src/database/data/QueueFids.kt index 8c27727..509a178 100644 --- a/src/database/QueueFids.kt +++ b/src/database/data/QueueFids.kt @@ -1,4 +1,4 @@ -package database +package database.data @Suppress("unused") data class QueueFids(var index: UInt, var ALCODE: String, var FLNUM: String, var ORIGIN: String, var ETAD: String, var FREMARK: String){ diff --git a/src/database/QueuePaging.kt b/src/database/data/QueuePaging.kt similarity index 78% rename from src/database/QueuePaging.kt rename to src/database/data/QueuePaging.kt index 9b9fb97..7a032b9 100644 --- a/src/database/QueuePaging.kt +++ b/src/database/data/QueuePaging.kt @@ -1,4 +1,8 @@ -package database +package database.data + +import codes.Somecodes +import java.time.Duration +import java.time.LocalDateTime 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 { @@ -18,8 +22,8 @@ data class QueuePaging(var index: UInt, var Date_Time: String, var Source: Strin */ fun isExpired(qt: QueuePaging) : Boolean{ try{ - val t1 = java.time.LocalDateTime.parse(qt.Date_Time, codes.Somecodes.datetimeformat1) - val delta = java.time.Duration.between(t1, java.time.LocalDateTime.now()) + val t1 = LocalDateTime.parse(qt.Date_Time, Somecodes.datetimeformat1) + val delta = Duration.between(t1, LocalDateTime.now()) return delta.seconds > 5 } catch (_: Exception){ return false diff --git a/src/database/QueueTable.kt b/src/database/data/QueueTable.kt similarity index 92% rename from src/database/QueueTable.kt rename to src/database/data/QueueTable.kt index 5a82d5b..ba4f4c5 100644 --- a/src/database/QueueTable.kt +++ b/src/database/data/QueueTable.kt @@ -1,6 +1,7 @@ -package database +package database.data import codes.Somecodes.Companion.datetimeformat1 +import java.time.Duration import java.time.LocalDateTime 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){ @@ -25,7 +26,7 @@ data class QueueTable(var index: UInt, var Date_Time: String, var Source: String fun isExpired(qt: QueueTable) : Boolean{ try{ val t1 = LocalDateTime.parse(qt.Date_Time, datetimeformat1) - val delta = java.time.Duration.between(t1, LocalDateTime.now()) + val delta = Duration.between(t1, LocalDateTime.now()) // expired if more than 5 seconds return delta.seconds > 5 } catch (_: Exception){ diff --git a/src/database/ScheduleBank.kt b/src/database/data/ScheduleBank.kt similarity index 98% rename from src/database/ScheduleBank.kt rename to src/database/data/ScheduleBank.kt index d2e9ec6..7066969 100644 --- a/src/database/ScheduleBank.kt +++ b/src/database/data/ScheduleBank.kt @@ -1,4 +1,4 @@ -package database +package database.data @Suppress("unused") data class ScheduleBank( @@ -36,4 +36,4 @@ data class ScheduleBank( BroadcastZones == other.BroadcastZones && Language == other.Language } -} +} \ No newline at end of file diff --git a/src/database/SoundChannel.kt b/src/database/data/SoundChannel.kt similarity index 94% rename from src/database/SoundChannel.kt rename to src/database/data/SoundChannel.kt index 6dc3cc3..cc852e6 100644 --- a/src/database/SoundChannel.kt +++ b/src/database/data/SoundChannel.kt @@ -1,4 +1,4 @@ -package database +package database.data data class SoundChannel(val index: UInt, val channel: String, val ip: String) { @@ -14,4 +14,4 @@ data class SoundChannel(val index: UInt, val channel: String, val ip: String) { fun isNotEmpty() : Boolean { return channel.isNotEmpty() && ip.isNotEmpty() } -} +} \ No newline at end of file diff --git a/src/database/Soundbank.kt b/src/database/data/Soundbank.kt similarity index 97% rename from src/database/Soundbank.kt rename to src/database/data/Soundbank.kt index 67366c5..397fedd 100644 --- a/src/database/Soundbank.kt +++ b/src/database/data/Soundbank.kt @@ -1,5 +1,4 @@ -package database - +package database.data data class Soundbank( var index: UInt, @@ -37,4 +36,4 @@ data class Soundbank( fun ValidLanguage() : Boolean{ return content.Language.entries.any{ it.name == this.Language} } -} +} \ No newline at end of file diff --git a/src/database/UserDB.kt b/src/database/data/UserDB.kt similarity index 98% rename from src/database/UserDB.kt rename to src/database/data/UserDB.kt index edc79b3..84870fc 100644 --- a/src/database/UserDB.kt +++ b/src/database/data/UserDB.kt @@ -1,4 +1,4 @@ -package database +package database.data @Suppress("unused") data class UserDB(var index: UInt, var username: String, var password: String, var location: String, var airline_tags: String, var city_tags: String, var messagebank_ann_id: String, var broadcastzones: String){ @@ -25,7 +25,4 @@ data class UserDB(var index: UInt, var username: String, var password: String, v this.broadcastzones == other.broadcastzones } -} - - - +} \ No newline at end of file diff --git a/src/database/table/Table_BroadcastZones.kt b/src/database/table/Table_BroadcastZones.kt new file mode 100644 index 0000000..f989c14 --- /dev/null +++ b/src/database/table/Table_BroadcastZones.kt @@ -0,0 +1,203 @@ +package database.table + +import database.data.BroadcastZones +import database.dbFunctions +import org.apache.poi.xssf.usermodel.XSSFWorkbook +import org.tinylog.Logger +import java.sql.Connection +import java.util.function.Consumer + +class Table_BroadcastZones(connection: Connection) : dbFunctions("broadcastzones", connection, listOf("index", "description", "SoundChannel", "id", "bp")) { + override fun Create() { + val tabledefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" + + "`index` INT AUTO_INCREMENT PRIMARY KEY," + + "description VARCHAR(512) NOT NULL," + // Description of the broadcast zone + "SoundChannel VARCHAR(45) NOT NULL," + // Sound channel of the broadcast zone + "id VARCHAR(45) NOT NULL," + // Box of the broadcast zone + "bp VARCHAR(45) NOT NULL" + // Relay of the broadcast zone + ")" + super.Create(tabledefinition) + } + + override fun Get(cbOK: Consumer?, cbFail: Consumer?) { + List.clear() + try { + val statement = connection.createStatement() + val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") + while (resultSet?.next() == true) { + val zone = BroadcastZones( + resultSet.getLong("index").toUInt(), + resultSet.getString("description"), + resultSet.getString("SoundChannel"), + resultSet.getString("id"), + resultSet.getString("bp") + ) + List.add(zone) + } + cbOK?.accept(Unit) + } catch (e: Exception) { + Logger.error("Error fetching ${super.dbName} : ${e.message}" as Any) + cbFail?.accept("Error fetching ${super.dbName} : ${e.message}" ) + } + } + + override fun Add(data: BroadcastZones): Boolean { + try { + val statement = + connection.prepareStatement("INSERT INTO ${super.dbName} (description, SoundChannel, id, bp) VALUES (?, ?, ?, ?)") + statement?.setString(1, data.description) + statement?.setString(2, data.SoundChannel) + statement?.setString(3, data.id) + statement?.setString(4, data.bp) + val rowsAffected = statement?.executeUpdate() + if (rowsAffected != null && rowsAffected > 0) { + Logger.info("Broadcast zone added: ${data.description}" as Any) + return true + } else { + Logger.warn("No broadcast zone entry added for: ${data.description}" as Any) + } + } catch (e: Exception) { + Logger.error("Error adding broadcast zone entry: ${e.message}" as Any) + } + return false + } + + override fun AddAll(data: ArrayList): Boolean { + try { + connection.autoCommit = false + val sql = + "INSERT INTO ${super.dbName} (description, SoundChannel, id, bp) VALUES (?, ?, ?, ?)" + val statement = connection.prepareStatement(sql) + for (bz in data) { + statement.setString(1, bz.description) + statement.setString(2, bz.SoundChannel) + statement.setString(3, bz.id) + statement.setString(4, bz.bp) + statement.addBatch() + } + statement.executeBatch() + connection.commit() + Logger.info("Bulk ${super.dbName} insert successful: ${data.size} entries" as Any) + connection.autoCommit = true + return true + } catch (e: Exception) { + Logger.error("Error adding ${super.dbName} entries: ${e.message}" as Any) + } + return false + } + + override fun UpdateByIndex(index: Int, data: BroadcastZones): Boolean { + try { + val statement = + connection.prepareStatement("UPDATE ${super.dbName} SET description = ?, SoundChannel = ?, id = ?, bp = ? WHERE `index` = ?") + statement?.setString(1, data.description) + statement?.setString(2, data.SoundChannel) + statement?.setString(3, data.id) + statement?.setString(4, data.bp) + statement?.setLong(5, index.toLong()) + val rowsAffected = statement?.executeUpdate() + if (rowsAffected != null && rowsAffected > 0) { + Logger.info("Broadcast zone updated at index $index: ${data.description}" as Any) + return true + } else { + Logger.warn("No broadcast zone entry updated at index $index for: ${data.description}" as Any) + } + } catch (e: Exception) { + Logger.error("Error updating broadcast zone entry at index $index: ${e.message}" as Any) + } + return false + } + + override fun Resort(): Boolean { + try { + val statement = connection.createStatement() + val tempdb_name = "temp_${super.dbName}" + // 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 (description, SoundChannel, id, bp) SELECT description, SoundChannel, id, bp FROM ${super.dbName} ORDER BY description ") + statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") + statement?.executeUpdate("INSERT INTO ${super.dbName} (description, SoundChannel, id, bp) SELECT description, SoundChannel, id, bp FROM $tempdb_name") + statement?.executeUpdate("DROP TABLE $tempdb_name") + Logger.info("${super.dbName} table resorted by description" as Any) + // reload the local list + Get() + return true + } catch (e: Exception) { + Logger.error("Error resorting ${super.dbName} table by description: ${e.message}" as Any) + } + return false + } + + override fun Import_XLSX(workbook: XSSFWorkbook): Boolean { + try { + val sheet = workbook.getSheet("BroadcastZones") + ?: throw Exception("No sheet named 'BroadcastZones' found") + val headerRow = sheet.getRow(0) ?: throw Exception("No header row found") + val headers = arrayOf("Index", "description", "SoundChannel", "id", "bp") + for ((colIndex, header) in headers.withIndex()) { + val cell = headerRow.getCell(colIndex) ?: throw Exception("Header '$header' not found") + if (cell.stringCellValue != header) throw Exception("Header '$header' not found") + } + // clear existing broadcast_zones + Clear() + // read each row and insert into database + val _broadcastZonesList = ArrayList() + for (rowIndex in 1..sheet.lastRowNum) { + val row = sheet.getRow(rowIndex) ?: continue + val description = row.getCell(1)?.stringCellValue ?: continue + val soundChannel = row.getCell(2)?.stringCellValue ?: continue + val id = row.getCell(3)?.stringCellValue ?: continue + val bp = row.getCell(4)?.stringCellValue ?: continue + val broadcastZone = BroadcastZones(0u, description, soundChannel, id, bp) + _broadcastZonesList.add(broadcastZone) + } + return AddAll(_broadcastZonesList) + } catch (e: Exception) { + Logger.error { "Error importing BroadcastZones, Msg: ${e.message}" } + } + return false + } + + override fun Export_XLSX(): XSSFWorkbook? { + try { + val statement = connection.createStatement() + val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") + val workbook = XSSFWorkbook() + val sheet = workbook.createSheet("BroadcastZones") + val headerRow = sheet.createRow(0) + val headers = arrayOf("Index", "description", "SoundChannel", "id", "bp") + for ((colIndex, header) in headers.withIndex()) { + val cell = headerRow.createCell(colIndex) + cell.setCellValue(header) + } + var rowIndex = 1 + while (resultSet?.next() == true) { + val row = sheet.createRow(rowIndex++) + row.createCell(0).setCellValue(resultSet.getString("index")) + row.createCell(1).setCellValue(resultSet.getString("description")) + row.createCell(2).setCellValue(resultSet.getString("SoundChannel")) + row.createCell(3).setCellValue(resultSet.getString("id")) + row.createCell(4).setCellValue(resultSet.getString("bp")) + } + for (i in headers.indices) { + sheet.autoSizeColumn(i) + } + return workbook + } catch (e: Exception) { + Logger.error { "Error exporting BroadcastZones, Msg: ${e.message}" } + } + return null + } + + /** + * Get all distinct broadcast zone descriptions from broadcastDB + * @return a list of distinct broadcast zone descriptions sorted alphabetically + */ + fun Get_BroadcastZone_List(): List { + return List + .distinctBy { it.description } + .map { it.description } + .sorted() + } +} \ No newline at end of file diff --git a/src/database/table/Table_LanguageLink.kt b/src/database/table/Table_LanguageLink.kt new file mode 100644 index 0000000..2ab437c --- /dev/null +++ b/src/database/table/Table_LanguageLink.kt @@ -0,0 +1,180 @@ +package database.table + +import database.data.LanguageLink +import database.dbFunctions +import org.apache.poi.xssf.usermodel.XSSFWorkbook +import org.tinylog.Logger +import java.sql.Connection +import java.util.function.Consumer + +class Table_LanguageLink(connection: Connection) : dbFunctions("languagelinking", connection, listOf("index", "TAG", "Language")) { + override fun Create() { + val tabledefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" + + "`index` INT AUTO_INCREMENT PRIMARY KEY," + + "TAG VARCHAR(45) NOT NULL," + // Language tag (e.g., EN, FR) + "Language VARCHAR(128) NOT NULL" + // Full language name (e.g., English, French) + ")" + + super.Create(tabledefinition) + } + + override fun Get(cbOK: Consumer?, cbFail: Consumer?) { + List.clear() + try { + val statement = connection.createStatement() + val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") + while (resultSet?.next() == true) { + val languageLink = LanguageLink( + resultSet.getLong("index").toUInt(), + resultSet.getString("TAG"), + resultSet.getString("Language") + ) + List.add(languageLink) + } + cbOK?.accept(Unit) + } catch (e: Exception) { + Logger.error("Error fetching ${super.dbName} : ${e.message}" as Any) + cbFail?.accept("Error fetching ${super.dbName} : ${e.message}" ) + } + } + + override fun Add(data: LanguageLink): Boolean { + try { + val statement = connection.prepareStatement("INSERT INTO ${super.dbName} (TAG, Language) VALUES (?, ?)") + statement.setString(1, data.TAG) + statement.setString(2, data.Language) + val rowsAffected = statement.executeUpdate() + if (rowsAffected > 0) { + Logger.info("Language link added: ${data.TAG} -> ${data.Language}" as Any) + return true + } else { + Logger.warn("No language link entry added for: ${data.TAG} -> ${data.Language}" as Any) + } + } catch (e: Exception) { + Logger.error("Error adding language link entry: ${e.message}" as Any) + } + return false + } + + override fun AddAll(data: ArrayList): Boolean { + try { + connection.autoCommit = false + val sql = "INSERT INTO ${super.dbName} (TAG, Language) VALUES (?, ?)" + val statement = connection.prepareStatement(sql) + + //for (ll in List) { + for (ll in data) { + statement.setString(1, ll.TAG) + statement.setString(2, ll.Language) + statement.addBatch() + } + statement.executeBatch() + connection.commit() + Logger.info("Bulk languagelinking insert successful: ${List.size} entries" as Any) + connection.autoCommit = true + return true + } catch (e: Exception) { + Logger.error("Error adding languagelinking entries: ${e.message}" as Any) + } + return false + } + + override fun UpdateByIndex(index: Int, data: LanguageLink): Boolean { + try { + val statement = + connection.prepareStatement("UPDATE ${super.dbName} SET TAG = ?, Language = ? WHERE `index` = ?") + statement?.setString(1, data.TAG) + statement?.setString(2, data.Language) + statement?.setLong(3, index.toLong()) + val rowsAffected = statement?.executeUpdate() + if (rowsAffected != null && rowsAffected > 0) { + Logger.info("Language link updated at index $index: ${data.TAG} -> ${data.Language}" as Any) + + return true + } else { + Logger.warn("No language link entry updated at index $index for: ${data.TAG} -> ${data.Language}" as Any) + } + } catch (e: Exception) { + Logger.error("Error updating language link entry at index $index: ${e.message}" as Any) + } + return false + } + + override fun Resort(): Boolean { + try { + val statement = connection.createStatement() + val tempdb_name = "temp_${super.dbName}" + // 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 (TAG, Language) SELECT TAG, Language FROM ${super.dbName} ORDER BY TAG") + statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") + statement?.executeUpdate("INSERT INTO ${super.dbName} (TAG, Language) SELECT TAG, Language FROM $tempdb_name") + statement?.executeUpdate("DROP TABLE $tempdb_name") + Logger.info("${super.dbName} table resorted by TAG" as Any) + // reload the local list + Get() + return true + } catch (e: Exception) { + Logger.error("Error resorting ${super.dbName} table by TAG: ${e.message}" as Any) + } + return false + } + + override fun Import_XLSX(workbook: XSSFWorkbook): Boolean { + try { + val sheet = + workbook.getSheet("LanguageLink") ?: throw Exception("No sheet named 'LanguageLink' found") + val headerRow = sheet.getRow(0) ?: throw Exception("No header row found") + val headers = arrayOf("Index", "TAG", "Language") + for ((colIndex, header) in headers.withIndex()) { + val cell = headerRow.getCell(colIndex) ?: throw Exception("Header '$header' not found") + if (cell.stringCellValue != header) throw Exception("Header '$header' not found") + } + // clear existing languagelink + Clear() + // read each row and insert into database + val _languageLinkList = ArrayList() + for (rowIndex in 1..sheet.lastRowNum) { + val row = sheet.getRow(rowIndex) ?: continue + val tag = row.getCell(1)?.stringCellValue ?: continue + val language = row.getCell(2)?.stringCellValue ?: continue + val languageLink = LanguageLink(0u, tag, language) + _languageLinkList.add(languageLink) + } + return AddAll(_languageLinkList) + } catch (e: Exception) { + Logger.error { "Error importing LanguageLink, Msg: ${e.message}" } + } + return false + } + + override fun Export_XLSX(): XSSFWorkbook? { + try { + val statement = connection.createStatement() + val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") + val workbook = XSSFWorkbook() + val sheet = workbook.createSheet("LanguageLink") + val headerRow = sheet.createRow(0) + val headers = arrayOf("Index", "TAG", "Language") + for ((colIndex, header) in headers.withIndex()) { + val cell = headerRow.createCell(colIndex) + cell.setCellValue(header) + } + var rowIndex = 1 + while (resultSet?.next() == true) { + val row = sheet.createRow(rowIndex++) + row.createCell(0).setCellValue(resultSet.getString("index")) + row.createCell(1).setCellValue(resultSet.getString("TAG")) + row.createCell(2).setCellValue(resultSet.getString("Language")) + } + for (i in headers.indices) { + sheet.autoSizeColumn(i) + } + return workbook + } catch (e: Exception) { + Logger.error { "Error exporting languagelinking, Msg: ${e.message}" } + } + return null + } +} \ No newline at end of file diff --git a/src/database/Table_LogSemiAuto.kt b/src/database/table/Table_LogSemiAuto.kt similarity index 94% rename from src/database/Table_LogSemiAuto.kt rename to src/database/table/Table_LogSemiAuto.kt index 83c0ac6..14dc921 100644 --- a/src/database/Table_LogSemiAuto.kt +++ b/src/database/table/Table_LogSemiAuto.kt @@ -1,8 +1,11 @@ -package database +package database.table +import database.data.LogSemiauto +import database.dbFunctions import org.apache.poi.xssf.usermodel.XSSFWorkbook import org.tinylog.Logger import java.sql.Connection +import java.sql.Date import java.time.LocalDate import java.time.format.DateTimeFormatter import java.util.function.Consumer @@ -92,18 +95,18 @@ class Table_LogSemiAuto(connection: Connection) : dbFunctions("logs fun GetLogSemiAutoForHtml(date: String, filter: String?, cbOK: Consumer>?, cbFail: Consumer?){ try{ - val valid_date : java.sql.Date? = when{ + val valid_date : Date? = when{ dateformat1.matches(date) -> { - java.sql.Date.valueOf(LocalDate.parse(date, DateTimeFormatter.ofPattern("dd/MM/yyyy"))) + Date.valueOf(LocalDate.parse(date, DateTimeFormatter.ofPattern("dd/MM/yyyy"))) } dateformat2.matches(date) -> { - java.sql.Date.valueOf(LocalDate.parse(date, DateTimeFormatter.ofPattern("dd-MM-yyyy"))) + Date.valueOf(LocalDate.parse(date, DateTimeFormatter.ofPattern("dd-MM-yyyy"))) } dateformat3.matches(date) -> { - java.sql.Date.valueOf(LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy/MM/dd"))) + Date.valueOf(LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy/MM/dd"))) } dateformat4.matches(date) -> { - java.sql.Date.valueOf(LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd"))) + Date.valueOf(LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd"))) } else -> null } diff --git a/src/database/Table_Logs.kt b/src/database/table/Table_Logs.kt similarity index 69% rename from src/database/Table_Logs.kt rename to src/database/table/Table_Logs.kt index 2267d7c..1c52c1a 100644 --- a/src/database/Table_Logs.kt +++ b/src/database/table/Table_Logs.kt @@ -1,13 +1,16 @@ -package database +package database.table +import database.data.Log +import database.dbFunctions import org.apache.poi.xssf.usermodel.XSSFWorkbook import org.tinylog.Logger import java.sql.Connection +import java.sql.Date import java.time.LocalDate import java.time.format.DateTimeFormatter import java.util.function.Consumer -class Table_Logs(connection: Connection) : dbFunctions ("logs", connection,listOf("index", "datenya", "timenya", "machine", "description")) { +class Table_Logs(connection: Connection) : dbFunctions("logs", connection,listOf("index", "datenya", "timenya", "machine", "description")) { /** * dateformat1 is regex for DD/MM/YYYY */ @@ -44,18 +47,18 @@ class Table_Logs(connection: Connection) : dbFunctions ("logs", connection, fun GetLogForHtml(date: String, filter: String?, cbOK: Consumer>?, cbFail: Consumer?){ try{ - val valid_date : java.sql.Date? = when{ + val valid_date : Date? = when{ dateformat1.matches(date) -> { - java.sql.Date.valueOf(LocalDate.parse(date, DateTimeFormatter.ofPattern("dd/MM/yyyy"))) + Date.valueOf(LocalDate.parse(date, DateTimeFormatter.ofPattern("dd/MM/yyyy"))) } dateformat2.matches(date) -> { - java.sql.Date.valueOf(LocalDate.parse(date, DateTimeFormatter.ofPattern("dd-MM-yyyy"))) + Date.valueOf(LocalDate.parse(date, DateTimeFormatter.ofPattern("dd-MM-yyyy"))) } dateformat3.matches(date) -> { - java.sql.Date.valueOf(LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy/MM/dd"))) + Date.valueOf(LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy/MM/dd"))) } dateformat4.matches(date) -> { - java.sql.Date.valueOf(LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd"))) + Date.valueOf(LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd"))) } else -> null } @@ -202,6 +205,74 @@ class Table_Logs(connection: Connection) : dbFunctions ("logs", connection, throw Exception("Importing Logs from XLSX is not supported") } + /** + * Exports the log table to an XLSX workbook for a specific date and optional filter. + * @param logDate The date string in format "dd/MM/yyyy". + * @param logFilter The filter string for the description or machine. If empty, exports all logs for the date. + * @return An XSSFWorkbook containing the filtered log data, or null if an error occurred. + */ + fun Export_Log_XLSX(logDate: String, logFilter: String): XSSFWorkbook? { + try { + val valid_date : Date? = when{ + dateformat1.matches(logDate) -> { + Date.valueOf(LocalDate.parse(logDate, DateTimeFormatter.ofPattern("dd/MM/yyyy"))) + } + dateformat2.matches(logDate) -> { + Date.valueOf(LocalDate.parse(logDate, DateTimeFormatter.ofPattern("dd-MM-yyyy"))) + } + dateformat3.matches(logDate) -> { + Date.valueOf(LocalDate.parse(logDate, DateTimeFormatter.ofPattern("yyyy/MM/dd"))) + } + dateformat4.matches(logDate) -> { + Date.valueOf(LocalDate.parse(logDate, DateTimeFormatter.ofPattern("yyyy-MM-dd"))) + } + else -> null + } + + if (valid_date!=null){ + // use coalescing for different datenya formats + val statement = if (logFilter.isEmpty()){ + connection.prepareStatement("SELECT * FROM ${super.dbName} WHERE COALESCE(STR_TO_DATE(datenya,'%d/%m/%Y'), STR_TO_DATE(datenya,'%d-%m-%Y'), STR_TO_DATE(datenya,'%Y/%m/%d'), STR_TO_DATE(datenya,'%Y-%m-%d')) = ?") + } else { + connection.prepareStatement("SELECT * FROM ${super.dbName} WHERE COALESCE(STR_TO_DATE(datenya,'%d/%m/%Y'), STR_TO_DATE(datenya,'%d-%m-%Y'), STR_TO_DATE(datenya,'%Y/%m/%d'), STR_TO_DATE(datenya,'%Y-%m-%d')) = ? AND description LIKE ?") + } + statement?.setDate(1, valid_date) + if (logFilter.isNotEmpty()){ + statement?.setString(2, "%$logFilter%") + } + val resultSet = statement?.executeQuery() + + val workbook = XSSFWorkbook() + val sheet = workbook.createSheet("Log") + val headerRow = sheet.createRow(0) + val headers = arrayOf("Index", "datenya", "timenya", "machine", "description") + for ((colIndex, header) in headers.withIndex()) { + val cell = headerRow.createCell(colIndex) + cell.setCellValue(header) + } + var rowIndex = 1 + + while (resultSet?.next() == true) { + val row = sheet.createRow(rowIndex++) + row.createCell(0).setCellValue(resultSet.getString("index")) + row.createCell(1).setCellValue(resultSet.getString("datenya")) + row.createCell(2).setCellValue(resultSet.getString("timenya")) + row.createCell(3).setCellValue(resultSet.getString("machine")) + row.createCell(4).setCellValue(resultSet.getString("description")) + } + + for (i in headers.indices) { + sheet.autoSizeColumn(i) + } + return workbook + } else throw Exception("Invalid date") + + } catch (e: Exception) { + Logger.error { "Error exporting Log, Msg: ${e.message}" } + } + return null + } + override fun Export_XLSX(): XSSFWorkbook? { try { val statement = connection.createStatement() diff --git a/src/database/table/Table_Messagebank.kt b/src/database/table/Table_Messagebank.kt new file mode 100644 index 0000000..8f6f3ba --- /dev/null +++ b/src/database/table/Table_Messagebank.kt @@ -0,0 +1,239 @@ +package database.table + +import database.data.Messagebank +import database.dbFunctions +import org.apache.poi.xssf.usermodel.XSSFWorkbook +import org.tinylog.Logger +import java.sql.Connection +import java.util.function.Consumer + +class Table_Messagebank(connection: Connection) : dbFunctions("messagebank", connection, listOf("index", "Description", "Language", "ANN_ID", "Voice_Type", "Message_Detail", "Message_TAGS")) { + override fun Create() { + val tabledefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" + + "`index` INT AUTO_INCREMENT PRIMARY KEY," + + "Description VARCHAR(512) NOT NULL," + // Description of the message + "Language VARCHAR(45) NOT NULL," + // Language of the message + "ANN_ID INT NOT NULL," + // ANN ID of the message + "Voice_Type VARCHAR(45) NOT NULL," + // Voice type of the message + "Message_Detail VARCHAR(1024) NOT NULL," + // Full message text + "Message_TAGS VARCHAR(1024)" + // Comma-separated tags for the message + ")" + super.Create(tabledefinition) + } + + override fun Get(cbOK: Consumer?, cbFail: Consumer?) { + List.clear() + try { + val statement = connection.createStatement() + val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") + while (resultSet?.next() == true) { + val messagebank = Messagebank( + resultSet.getLong("index").toUInt(), + resultSet.getString("Description"), + resultSet.getString("Language"), + resultSet.getInt("ANN_ID").toUInt(), + resultSet.getString("Voice_Type"), + resultSet.getString("Message_Detail"), + resultSet.getString("Message_TAGS") + ) + + List.add(messagebank) + } + cbOK?.accept(Unit) + } catch (e: Exception) { + Logger.error("Error fetching ${super.dbName} : ${e.message}" as Any) + cbFail?.accept("Error fetching ${super.dbName} : ${e.message}") + } + } + + override fun Add(data: Messagebank): Boolean { + try { + val statement = + connection.prepareStatement("INSERT INTO ${super.dbName} (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) VALUES (?, ?, ?, ?, ?, ?)") + statement?.setString(1, data.Description) + statement?.setString(2, data.Language) + statement?.setInt(3, data.ANN_ID.toInt()) + statement?.setString(4, data.Voice_Type) + statement?.setString(5, data.Message_Detail) + statement?.setString(6, data.Message_TAGS) + val rowsAffected = statement?.executeUpdate() + if (rowsAffected != null && rowsAffected > 0) { + Logger.info("Messagebank added: ${data.Description}" as Any) + return true + } else { + Logger.warn("No messagebank entry added for: ${data.Description}" as Any) + } + } catch (e: Exception) { + Logger.error("Error adding messagebank entry: ${e.message}" as Any) + } + return false + } + + override fun AddAll(data: ArrayList): Boolean { + try { + connection.autoCommit = false + val sql = + "INSERT INTO ${super.dbName} (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) VALUES (?, ?, ?, ?, ?, ?)" + val statement = connection.prepareStatement(sql) + for (mb in data) { + statement.setString(1, mb.Description) + statement.setString(2, mb.Language) + statement.setInt(3, mb.ANN_ID.toInt()) + statement.setString(4, mb.Voice_Type) + statement.setString(5, mb.Message_Detail) + statement.setString(6, mb.Message_TAGS) + statement.addBatch() + } + statement.executeBatch() + connection.commit() + Logger.info("Bulk messagebank insert successful: ${data.size} entries" as Any) + connection.autoCommit = true + return true + } catch (e: Exception) { + Logger.error("Error adding messagebank entries: ${e.message}" as Any) + } + return false + } + + override fun UpdateByIndex(index: Int, data: Messagebank): Boolean { + try { + val statement = + connection.prepareStatement("UPDATE ${super.dbName} SET Description = ?, Language = ?, ANN_ID = ?, Voice_Type = ?, Message_Detail = ?, Message_TAGS = ? WHERE `index` = ?") + statement?.setString(1, data.Description) + statement?.setString(2, data.Language) + statement?.setInt(3, data.ANN_ID.toInt()) + statement?.setString(4, data.Voice_Type) + statement?.setString(5, data.Message_Detail) + statement?.setString(6, data.Message_TAGS) + statement?.setLong(7, index.toLong()) + val rowsAffected = statement?.executeUpdate() + if (rowsAffected != null && rowsAffected > 0) { + Logger.info("Messagebank updated at index $index: ${data.Description}" as Any) + return true + } else { + Logger.warn("No messagebank entry updated at index $index for: ${data.Description}" as Any) + } + } catch (e: Exception) { + Logger.error("Error updating messagebank entry at index $index: ${e.message}" as Any) + } + return false + } + + override fun Resort(): Boolean { + try { + val statement = connection.createStatement() + val tempdb_name = "temp_${super.dbName}" + // 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 (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) SELECT Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS FROM ${super.dbName} ORDER BY ANN_ID ") + statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") + statement?.executeUpdate("INSERT INTO ${super.dbName} (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) SELECT Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS FROM $tempdb_name") + statement?.executeUpdate("DROP TABLE $tempdb_name") + Logger.info("${super.dbName} table resorted by Description" as Any) + // reload the local list + Get() + return true + } catch (e: Exception) { + Logger.error("Error resorting ${super.dbName} table by Description: ${e.message}" as Any) + } + return false + } + + override fun Import_XLSX(workbook: XSSFWorkbook): Boolean { + try { + // check if there is sheet named "Messagebank" + val sheet = + workbook.getSheet("Messagebank") ?: throw Exception("No sheet named 'Messagebank' found") + // check if the sheet contains header named "Index", "Description", "Language", "ANN_ID", "Voice_Type", "Message_Detail", "Message_TAGS" + val headerRow = sheet.getRow(0) ?: throw Exception("No header row found") + val headers = + arrayOf( + "Index", + "Description", + "Language", + "ANN_ID", + "Voice_Type", + "Message_Detail", + "Message_TAGS" + ) + for ((colIndex, header) in headers.withIndex()) { + val cell = headerRow.getCell(colIndex) ?: throw Exception("Header '$header' not found") + if (cell.stringCellValue != header) throw Exception("Header '$header' not found") + } + // clear existing messagebank + Clear() + // read each row and insert into database + val _messagebankList = ArrayList() + for (rowIndex in 1..sheet.lastRowNum) { + val row = sheet.getRow(rowIndex) ?: continue + val description = row.getCell(1)?.stringCellValue ?: continue + val language = row.getCell(2)?.stringCellValue ?: continue + val annId = row.getCell(3)?.stringCellValue?.toUIntOrNull() ?: continue + val voiceType = row.getCell(4)?.stringCellValue ?: continue + val messageDetail = row.getCell(5)?.stringCellValue ?: continue + val messageTags = row.getCell(6)?.stringCellValue ?: continue + val messagebank = + Messagebank(0u, description, language, annId, voiceType, messageDetail, messageTags) + _messagebankList.add(messagebank) + } + return AddAll(_messagebankList) + } catch (e: Exception) { + Logger.error { "Error importing Messagebank, Msg: ${e.message}" } + } + return false + } + + override fun Export_XLSX(): XSSFWorkbook? { + try { + val statement = connection.createStatement() + val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") + val workbook = XSSFWorkbook() + val sheet = workbook.createSheet("Messagebank") + val headerRow = sheet.createRow(0) + val headers = + arrayOf( + "Index", + "Description", + "Language", + "ANN_ID", + "Voice_Type", + "Message_Detail", + "Message_TAGS" + ) + for ((colIndex, header) in headers.withIndex()) { + val cell = headerRow.createCell(colIndex) + cell.setCellValue(header) + } + var rowIndex = 1 + while (resultSet?.next() == true) { + val row = sheet.createRow(rowIndex++) + row.createCell(0).setCellValue(resultSet.getString("index")) + row.createCell(1).setCellValue(resultSet.getString("Description")) + row.createCell(2).setCellValue(resultSet.getString("Language")) + row.createCell(3).setCellValue(resultSet.getString("ANN_ID")) + row.createCell(4).setCellValue(resultSet.getString("Voice_Type")) + row.createCell(5).setCellValue(resultSet.getString("Message_Detail")) + row.createCell(6).setCellValue(resultSet.getString("Message_TAGS")) + } + for (i in headers.indices) { + sheet.autoSizeColumn(i) + } + return workbook + } catch (e: Exception) { + Logger.error { "Error exporting Messagebank, Msg: ${e.message}" } + } + return null + } + + /** + * Get all distinct message ID from messagebank + * @return a list of distinct ANN_ID sorted numerically + */ + fun Get_MessageID_List(): List { + return List + .distinctBy { it.ANN_ID } + .map { it.ANN_ID } + .sorted() + } +} \ No newline at end of file diff --git a/src/database/table/Table_QueuePaging.kt b/src/database/table/Table_QueuePaging.kt new file mode 100644 index 0000000..6989732 --- /dev/null +++ b/src/database/table/Table_QueuePaging.kt @@ -0,0 +1,156 @@ +package database.table + +import database.data.QueuePaging +import database.dbFunctions +import org.apache.poi.xssf.usermodel.XSSFWorkbook +import org.tinylog.Logger +import java.sql.Connection +import java.util.function.Consumer + +class Table_QueuePaging(connection : Connection) : dbFunctions("queue_paging", connection, listOf("index", "Date_Time", "Source", "Type", "Message", "BroadcastZones")) { + override fun Create() { + val tabledefinition ="CREATE TABLE IF NOT EXISTS ${super.dbName} (" + + "`index` INT AUTO_INCREMENT PRIMARY KEY," + + "Date_Time VARCHAR(45) NOT NULL," + // Date and time of the entry + "Source VARCHAR(45) NOT NULL," + // Source of the entry + "Type VARCHAR(45) NOT NULL," + // Type of the entry + "Message VARCHAR(1024) NOT NULL," + // Message content + "BroadcastZones VARCHAR(1024)" + // Comma-separated soundbank tags + ")" + super.Create(tabledefinition) + } + + override fun Get(cbOK: Consumer?, cbFail: Consumer?) { + List.clear() + val queueList = ArrayList() + try { + val statement = connection.createStatement() + val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") + while (resultSet?.next() == true) { + val queuePaging = QueuePaging( + resultSet.getLong("index").toUInt(), + resultSet.getString("Date_Time"), + resultSet.getString("Source"), + resultSet.getString("Type"), + resultSet.getString("Message"), + resultSet.getString("BroadcastZones"), + ) + queueList.add(queuePaging) + List.add(queuePaging) + } + cbOK?.accept(Unit) + } catch (e: Exception) { + Logger.error("Error fetching ${super.dbName} : ${e.message}" as Any) + cbFail?.accept("Error fetching ${super.dbName} : ${e.message}" ) + } + } + + override fun Add(data: QueuePaging): Boolean { + try { + val statement = connection.prepareStatement( + "INSERT INTO ${super.dbName} (Date_Time, Source, Type, Message, BroadcastZones) VALUES (?, ?, ?, ?, ?)" + ) + statement?.setString(1, data.Date_Time) + statement?.setString(2, data.Source) + statement?.setString(3, data.Type) + statement?.setString(4, data.Message) + statement?.setString(5, data.BroadcastZones) + val rowsAffected = statement?.executeUpdate() + if (rowsAffected != null && rowsAffected > 0) { + Logger.info("QueuePaging added: ${data.Message}" as Any) + return true + } else { + Logger.warn("No QueuePaging entry added for: ${data.Message}" as Any) + } + } catch (e: Exception) { + Logger.error("Error adding QueuePaging entry: ${e.message}" as Any) + } + return false + } + + override fun AddAll(data: ArrayList): Boolean { + return try { + connection.autoCommit = false + val sql = + "INSERT INTO ${super.dbName} (Date_Time, Source, Type, Message, BroadcastZones) VALUES (?, ?, ?, ?, ?)" + val statement = connection.prepareStatement(sql) + for (qp in data) { + statement.setString(1, qp.Date_Time) + statement.setString(2, qp.Source) + statement.setString(3, qp.Type) + statement.setString(4, qp.Message) + statement.setString(5, qp.BroadcastZones) + statement.addBatch() + } + statement.executeBatch() + connection.commit() + Logger.info("Bulk QueuePaging insert successful: ${data.size} entries" as Any) + connection.autoCommit = true + true + } catch (e: Exception) { + Logger.error("Error adding QueuePaging entries: ${e.message}" as Any) + false + } + } + + override fun UpdateByIndex(index: Int, data: QueuePaging): Boolean { + throw Exception("Update not supported") + } + + override fun Resort(): Boolean { + try { + val statement = connection.createStatement() + val tempdb_name = "temp_${super.dbName}" + // 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, 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, 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 + Get() + return true + } catch (e: Exception) { + Logger.error("Error resorting ${super.dbName} table by index: ${e.message}" as Any) + } + return false + } + + override fun Import_XLSX(workbook: XSSFWorkbook): Boolean { + throw Exception("Importing QueuePaging from XLSX is not supported") + } + + override fun Export_XLSX(): XSSFWorkbook? { + try { + val statement = connection.createStatement() + val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") + val workbook = XSSFWorkbook() + val sheet = workbook.createSheet("QueuePaging") + val headerRow = sheet.createRow(0) + val headers = arrayOf("Index", "Date_Time", "Source", "Type", "Message", "BroadcastZones") + for ((colIndex, header) in headers.withIndex()) { + val cell = headerRow.createCell(colIndex) + cell.setCellValue(header) + } + var rowIndex = 1 + while (resultSet?.next() == true) { + val row = sheet.createRow(rowIndex++) + row.createCell(0).setCellValue(resultSet.getString("index")) + row.createCell(1).setCellValue(resultSet.getString("Date_Time")) + 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("BroadcastZones")) + } + for (i in headers.indices) { + sheet.autoSizeColumn(i) + } + return workbook + } catch (e: Exception) { + Logger.error { "Error exporting QueuePaging, Msg: ${e.message}" } + } + return null + } +} \ No newline at end of file diff --git a/src/database/table/Table_QueueSoundbank.kt b/src/database/table/Table_QueueSoundbank.kt new file mode 100644 index 0000000..e9d58bf --- /dev/null +++ b/src/database/table/Table_QueueSoundbank.kt @@ -0,0 +1,181 @@ +package database.table + +import database.data.QueueTable +import database.dbFunctions +import org.apache.poi.xssf.usermodel.XSSFWorkbook +import org.tinylog.Logger +import java.sql.Connection +import java.util.function.Consumer + +class Table_QueueSoundbank(connection: Connection) : dbFunctions("queue_table", connection, listOf("index", "Date_Time", "Source", "Type", "Message", "SB_TAGS", "BroadcastZones", "Repeat", "Language")) { + override fun Create() { + val tabledefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" + + "`index` INT AUTO_INCREMENT PRIMARY KEY," + + "Date_Time VARCHAR(45) NOT NULL," + // Date and time of the entry + "Source VARCHAR(45) NOT NULL," + // Source of the entry + "Type VARCHAR(45) NOT NULL," + // Type of the entry + "Message VARCHAR(1024) NOT NULL," + // Message content + "SB_TAGS VARCHAR(1024)," + // Comma-separated soundbank tags + "BroadcastZones VARCHAR(1024) NOT NULL," + // Comma-separated broadcast zones + "`Repeat` INT NOT NULL," + // Number of repeats + "Language VARCHAR(100) NOT NULL" + // Language of the message + ")" + super.Create(tabledefinition) + } + + override fun Get(cbOK: Consumer?, cbFail: Consumer?) { + List.clear() + val queueList = ArrayList() + try { + val statement = connection.createStatement() + val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") + while (resultSet?.next() == true) { + val queueTable = QueueTable( + resultSet.getLong("index").toUInt(), + resultSet.getString("Date_Time"), + resultSet.getString("Source"), + resultSet.getString("Type"), + resultSet.getString("Message"), + resultSet.getString("SB_TAGS"), + resultSet.getString("BroadcastZones"), + resultSet.getInt("Repeat").toUInt(), + resultSet.getString("Language") + ) + queueList.add(queueTable) + List.add(queueTable) + } + cbOK?.accept(Unit) + } catch (e: Exception) { + Logger.error("Error fetching ${super.dbName} : ${e.message}" as Any) + cbFail?.accept("Error fetching ${super.dbName} : ${e.message}" ) + } + } + + override fun Add(data: QueueTable): Boolean { + try { + val statement = connection.prepareStatement( + "INSERT INTO ${super.dbName} (`Date_Time`, `Source`, `Type`, `Message`, `SB_TAGS`, `BroadcastZones`, `Repeat`, `Language`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" + ) + statement?.setString(1, data.Date_Time) + statement?.setString(2, data.Source) + statement?.setString(3, data.Type) + statement?.setString(4, data.Message) + statement?.setString(5, data.SB_TAGS) + statement?.setString(6, data.BroadcastZones) + statement?.setInt(7, data.Repeat.toInt()) + statement?.setString(8, data.Language) + val rowsAffected = statement?.executeUpdate() + if (rowsAffected != null && rowsAffected > 0) { + Logger.info("QueueTable added Source=${data.Source} Type=${data.Type} Message=${data.Message}, Languages=${data.Language} Variables=${data.SB_TAGS}, BroadcastZones=${data.BroadcastZones}" as Any) + return true + } else { + Logger.warn("No QueueTable entry added for: ${data.Message}" as Any) + } + } catch (e: Exception) { + Logger.error("Error adding QueueTable entry: ${e.message}" as Any) + } + return false + } + + override fun AddAll(data: ArrayList): Boolean { + try { + connection.autoCommit = false + val sql = + "INSERT INTO ${super.dbName} (`Date_Time`, `Source`, `Type`, `Message`, `SB_TAGS`, `BroadcastZones`, `Repeat`, `Language`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" + val statement = connection.prepareStatement(sql) + for (qt in data) { + statement.setString(1, qt.Date_Time) + statement.setString(2, qt.Source) + statement.setString(3, qt.Type) + statement.setString(4, qt.Message) + statement.setString(5, qt.SB_TAGS) + statement.setString(6, qt.BroadcastZones) + statement.setInt(7, qt.Repeat.toInt()) + statement.setString(8, qt.Language) + statement.addBatch() + } + statement.executeBatch() + connection.commit() + Logger.info("Bulk QueueTable insert successful: ${data.size} entries" as Any) + connection.autoCommit = true + return true + } catch (e: Exception) { + Logger.error("Error adding QueueTable entries: ${e.message}" as Any) + } + return false + } + + override fun UpdateByIndex(index: Int, data: QueueTable): Boolean { + throw Exception("Update not supported") + } + + override fun Resort(): Boolean { + try { + val statement = connection.createStatement() + val tempdb_name = "temp_${super.dbName}" + // 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, BroadcastZones, `Repeat`, Language) SELECT Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, `Repeat`, Language FROM ${super.dbName} ORDER BY `index` ") + statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") + statement?.executeUpdate("INSERT INTO ${super.dbName} (Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, `Repeat`, Language) SELECT Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, `Repeat`, Language FROM $tempdb_name") + statement?.executeUpdate("DROP TABLE $tempdb_name") + Logger.info("${super.dbName} table resorted by index" as Any) + // reload the local list + Get() + return true + } catch (e: Exception) { + Logger.error("Error resorting ${super.dbName} table by index: ${e.message}" as Any) + } + return false + } + + override fun Import_XLSX(workbook: XSSFWorkbook): Boolean { + throw Exception("Import XLSX not supported for QueueTable") + } + + override fun Export_XLSX(): XSSFWorkbook? { + try { + val statement = connection.createStatement() + val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") + val workbook = XSSFWorkbook() + val sheet = workbook.createSheet("QueueTable") + val headerRow = sheet.createRow(0) + val headers = arrayOf( + "Index", + "Date_Time", + "Source", + "Type", + "Message", + "SB_TAGS", + "BroadcastZones", + "Repeat", + "Language" + ) + for ((colIndex, header) in headers.withIndex()) { + val cell = headerRow.createCell(colIndex) + cell.setCellValue(header) + } + var rowIndex = 1 + while (resultSet?.next() == true) { + val row = sheet.createRow(rowIndex++) + row.createCell(0).setCellValue(resultSet.getString("index")) + row.createCell(1).setCellValue(resultSet.getString("Date_Time")) + 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(6).setCellValue(resultSet.getString("BroadcastZones")) + row.createCell(7).setCellValue(resultSet.getString("Repeat")) + row.createCell(8).setCellValue(resultSet.getString("Language")) + } + for (i in headers.indices) { + sheet.autoSizeColumn(i) + } + return workbook + } catch (e: Exception) { + Logger.error { "Error exporting QueueTable, Msg: ${e.message}" } + } + return null + } +} \ No newline at end of file diff --git a/src/database/table/Table_Schedule.kt b/src/database/table/Table_Schedule.kt new file mode 100644 index 0000000..3ef7cd7 --- /dev/null +++ b/src/database/table/Table_Schedule.kt @@ -0,0 +1,287 @@ +package database.table + +import codes.Somecodes.Companion.ValidScheduleDay +import database.MariaDB.Companion.ValidTime +import database.data.ScheduleBank +import database.dbFunctions +import org.apache.poi.xssf.usermodel.XSSFWorkbook +import org.tinylog.Logger +import java.sql.Connection +import java.util.function.Consumer + +class Table_Schedule(connection: Connection) : dbFunctions("schedulebank", connection, listOf("index", "Description", "Day", "Time", "Soundpath", "Repeat", "Enable", "BroadcastZones", "Language")) { + override fun Create() { + val tabledefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" + + "`index` INT AUTO_INCREMENT PRIMARY KEY," + + "Description VARCHAR(128) NOT NULL," + // Description of the schedule + "Day VARCHAR(255) NOT NULL," + // Day in format DD/MM/YYYY + "Time VARCHAR(20) NOT NULL," + // Time in format HH:MM:SS + "Soundpath VARCHAR(512) NOT NULL," + // Path to the sound file + "`Repeat` TINYINT UNSIGNED NOT NULL," + // Repeat type (0=Once, 1=Daily, 2=Weekly, 3=Monthly, 4=Yearly) + "Enable BOOLEAN NOT NULL," + // Enable or disable the schedule + "BroadcastZones TEXT NOT NULL," + // Comma-separated list of broadcast zones + "Language VARCHAR(45) NOT NULL" + // Language code (e.g., EN, FR) + ")" + super.Create(tabledefinition) + } + + override fun Get(cbOK: Consumer?, cbFail: Consumer?) { + List.clear() + try { + val statement = connection.createStatement() + val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") + 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") + ) + List.add(schedulebank) + } + cbOK?.accept(Unit) + } catch (e: Exception) { + Logger.error("Error fetching ${super.dbName}: ${e.message}" as Any) + cbFail?.accept("Error fetching ${super.dbName}: ${e.message}" ) + } + } + + override fun Add(data: ScheduleBank): Boolean { + if (!ValidScheduleDay(data.Day)) { + Logger.error("Error adding schedulebank entry: Invalid date format ${data.Day}" as Any) + return false + } + if (!ValidTime(data.Time)) { + Logger.error("Error adding schedulebank entry: Invalid time format ${data.Time}" as Any) + return false + } + try { + val statement = + connection.prepareStatement("INSERT INTO ${super.dbName} (Description, Day, Time, Soundpath, `Repeat`, Enable, BroadcastZones, Language) VALUES (?, ?, ?, ?, ?, ?, ?, ?)") + statement?.setString(1, data.Description) + statement?.setString(2, data.Day) + statement?.setString(3, data.Time) + statement?.setString(4, data.Soundpath) + statement?.setInt(5, data.Repeat.toInt()) + statement?.setBoolean(6, data.Enable) + statement?.setString(7, data.BroadcastZones) + statement?.setString(8, data.Language) + val rowsAffected = statement?.executeUpdate() + if (rowsAffected != null && rowsAffected > 0) { + Logger.info("Schedulebank added: ${data.Description}" as Any) + return true + } else { + Logger.warn("No schedulebank entry added for: ${data.Description}" as Any) + } + } catch (e: Exception) { + Logger.error("Error adding schedulebank entry: ${e.message}" as Any) + } + return false + } + + override fun AddAll(data: ArrayList): Boolean { + try { + connection.autoCommit = false + val sql = + "INSERT INTO ${super.dbName} (Description, Day, Time, Soundpath, `Repeat`, Enable, BroadcastZones, Language) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" + val statement = connection.prepareStatement(sql) + for (sb in data) { + if (!ValidScheduleDay(sb.Day) || !ValidTime(sb.Time)) { + Logger.error("Invalid date or time format for schedulebank: ${sb.Description}" as Any) + continue + } + statement.setString(1, sb.Description) + statement.setString(2, sb.Day) + statement.setString(3, sb.Time) + statement.setString(4, sb.Soundpath) + statement.setInt(5, sb.Repeat.toInt()) + statement.setBoolean(6, sb.Enable) + statement.setString(7, sb.BroadcastZones) + statement.setString(8, sb.Language) + statement.addBatch() + } + statement.executeBatch() + connection.commit() + Logger.info("Bulk schedulebank insert successful: ${data.size} entries" as Any) + connection.autoCommit = true + return true + } catch (e: Exception) { + Logger.error("Error adding schedulebank entries: ${e.message}" as Any) + } + return false + } + + + override fun UpdateByIndex(index: Int, data: ScheduleBank): Boolean { + if (!ValidScheduleDay(data.Day)) { + Logger.error("Error updating schedulebank entry: Invalid date format ${data.Day}" as Any) + return false + } + if (!ValidTime(data.Time)) { + Logger.error("Error updating schedulebank entry: Invalid time format ${data.Time}" as Any) + return false + } + try { + val statement = + connection.prepareStatement("UPDATE ${super.dbName} SET Description = ?, Day = ?, Time = ?, Soundpath = ?, `Repeat` = ?, Enable = ?, BroadcastZones = ?, Language = ? WHERE `index` = ?") + statement?.setString(1, data.Description) + statement?.setString(2, data.Day) + statement?.setString(3, data.Time) + statement?.setString(4, data.Soundpath) + statement?.setInt(5, data.Repeat.toInt()) + statement?.setBoolean(6, data.Enable) + statement?.setString(7, data.BroadcastZones) + statement?.setString(8, data.Language) + statement?.setLong(9, index.toLong()) + val rowsAffected = statement?.executeUpdate() + if (rowsAffected != null && rowsAffected > 0) { + Logger.info("Schedulebank updated at index $index: ${data.Description}" as Any) + return true + } else { + Logger.warn("No schedulebank entry updated at index $index for: ${data.Description}" as Any) + } + } catch (e: Exception) { + Logger.error("Error updating schedulebank entry at index $index: ${e.message}" as Any) + } + return false + } + + override fun Resort(): Boolean { + try { + val statement = connection.createStatement() + val tempdb_name = "temp_${super.dbName}" + // 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 (Description, Day, Time, Soundpath, `Repeat`, Enable, BroadcastZones, Language) SELECT Description, Day, Time, Soundpath, `Repeat`, Enable, BroadcastZones, Language FROM ${super.dbName} ORDER BY Day , Time ") + statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") + statement?.executeUpdate("INSERT INTO ${super.dbName} (Description, Day, Time, Soundpath, `Repeat`, Enable, BroadcastZones, Language) SELECT Description, Day, Time, Soundpath, `Repeat`, Enable, BroadcastZones, Language FROM $tempdb_name") + statement?.executeUpdate("DROP TABLE $tempdb_name") + Logger.info("${super.dbName} table resorted by Day and Time" as Any) + // reload the local list + Get() + return true + } catch (e: Exception) { + Logger.error("Error resorting ${super.dbName} table by Day and Time: ${e.message}" as Any) + } + return false + } + + override fun Import_XLSX(workbook: XSSFWorkbook): Boolean { + try { + val sheet = + workbook.getSheet("Schedulebank") ?: throw Exception("No sheet named 'Schedulebank' found") + val headerRow = sheet.getRow(0) ?: throw Exception("No header row found") + val headers = arrayOf( + "Index", + "Description", + "Day", + "Time", + "Soundpath", + "Repeat", + "Enable", + "BroadcastZones", + "Language" + ) + for ((colIndex, header) in headers.withIndex()) { + val cell = headerRow.getCell(colIndex) ?: throw Exception("Header '$header' not found") + if (cell.stringCellValue != header) throw Exception("Header '$header' not found") + } + // clear existing schedulebank + Clear() + // read each row and insert into database + val _schedulebankList = ArrayList() + //Logger.info{"Sheet last row num: ${sheet.lastRowNum}"} + for (rowIndex in 1..sheet.lastRowNum) { + val row = sheet.getRow(rowIndex) ?: continue + //println(row) + val description = row.getCell(1)?.stringCellValue ?: continue + //println(description.toString()) + val day = row.getCell(2)?.stringCellValue ?: continue + //println(day.toString()) + val time = row.getCell(3)?.stringCellValue ?: continue + //println(time.toString()) + val soundpath = row.getCell(4)?.stringCellValue ?: continue + //println(soundpath.toString()) + val repeat = row.getCell(5)?.stringCellValue?.toUByteOrNull() ?: continue + // println(repeat.toString()) + //val enable = row.getCell(6)?.stringCellValue?.toBooleanStrictOrNull() ?: continue + val enable = row.getCell(6)?.stringCellValue?.toBoolean() ?: continue + //println(enable.toString()) + val broadcastZones = row.getCell(7)?.stringCellValue ?: continue + //println(broadcastZones.toString()) + val language = row.getCell(8)?.stringCellValue ?: continue + //println(language.toString()) + val schedulebank = + ScheduleBank( + 0u, + description, + day, + time, + soundpath, + repeat, + enable, + broadcastZones, + language + ) + Logger.info{"SchedulebankList added 1"} + + _schedulebankList.add(schedulebank) + } + return AddAll(_schedulebankList) + } catch (e: Exception) { + Logger.error { "Error importing Schedulebank, Msg: ${e.message}" } + } + return false + } + + override fun Export_XLSX(): XSSFWorkbook? { + try { + val statement = connection.createStatement() + val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") + val workbook = XSSFWorkbook() + val sheet = workbook.createSheet("Schedulebank") + val headerRow = sheet.createRow(0) + val headers = arrayOf( + "Index", + "Description", + "Day", + "Time", + "Soundpath", + "Repeat", + "Enable", + "BroadcastZones", + "Language" + ) + for ((colIndex, header) in headers.withIndex()) { + val cell = headerRow.createCell(colIndex) + cell.setCellValue(header) + } + var rowIndex = 1 + while (resultSet?.next() == true) { + val row = sheet.createRow(rowIndex++) + row.createCell(0).setCellValue(resultSet.getString("index")) + row.createCell(1).setCellValue(resultSet.getString("Description")) + row.createCell(2).setCellValue(resultSet.getString("Day")) + row.createCell(3).setCellValue(resultSet.getString("Time")) + row.createCell(4).setCellValue(resultSet.getString("Soundpath")) + row.createCell(5).setCellValue(resultSet.getString("Repeat")) + row.createCell(6).setCellValue(resultSet.getString("Enable")) + row.createCell(7).setCellValue(resultSet.getString("BroadcastZones")) + row.createCell(8).setCellValue(resultSet.getString("Language")) + } + for (i in headers.indices) { + sheet.autoSizeColumn(i) + } + return workbook + } catch (e: Exception) { + Logger.error { "Error exporting Schedulebank, Msg: ${e.message}" } + } + return null + } +} \ No newline at end of file diff --git a/src/database/table/Table_SoundChannel.kt b/src/database/table/Table_SoundChannel.kt new file mode 100644 index 0000000..6a2408b --- /dev/null +++ b/src/database/table/Table_SoundChannel.kt @@ -0,0 +1,256 @@ +package database.table + +import database.data.SoundChannel +import database.dbFunctions +import max_channel +import org.apache.poi.xssf.usermodel.XSSFWorkbook +import org.tinylog.Logger +import java.sql.Connection +import java.util.function.Consumer + +class Table_SoundChannel(connection: Connection) : dbFunctions("soundchannel", connection, listOf("index", "channel", "ip")) { + override fun Create() { + val tableDefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" + + "`index` INT AUTO_INCREMENT PRIMARY KEY," + + "channel VARCHAR(45) NOT NULL," + // Channel 01 to Channel 64 + "ip VARCHAR(45) NOT NULL" + // IP address or empty string + ")" + + super.Create(tableDefinition) + + // Check if table is empty, if so, populate with 64 channels + try { + val statement = connection.createStatement() + val countResult = statement?.executeQuery("SELECT COUNT(*) AS count FROM ${super.dbName}") + if (countResult?.next() == true) { + val count = countResult.getInt("count") + if (count < max_channel) { + Logger.info("SoundChannel table is empty, populating with default channels" as Any) + Clear() + } + } + } catch (e: Exception) { + Logger.error("Error creating SoundChannel table: ${e.message}" as Any) + } + } + + override fun Get(cbOK: Consumer?, cbFail: Consumer?) { + List.clear() + try { + val statement = connection.createStatement() + val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName} ORDER BY `index` ") + while (resultSet?.next() == true) { + val channel = SoundChannel( + resultSet.getLong("index").toUInt(), + resultSet.getString("channel"), + resultSet.getString("ip") + ) + List.add(channel) + } + cbOK?.accept(Unit) + } catch (e: Exception) { + Logger.error("Error fetching sound channels: ${e.message}" as Any) + cbFail?.accept("Error fetching sound channels: ${e.message}" ) + } + } + + override fun Add(data: SoundChannel): Boolean { + try { + val statement = connection.prepareStatement("UPDATE ${super.dbName} SET ip = ? WHERE channel = ?") + statement?.setString(1, data.ip) + statement?.setString(2, data.channel) + val rowsAffected = statement?.executeUpdate() + if (rowsAffected != null && rowsAffected > 0) { + Logger.info("SoundChannel updated: ${data.channel} -> ${data.ip}" as Any) + return true + } else { + Logger.warn("No SoundChannel entry updated for: ${data.channel} -> ${data.ip}" as Any) + } + } catch (e: Exception) { + Logger.error("Error updating SoundChannel entry: ${e.message}" as Any) + } + return false + } + + override fun AddAll(data: ArrayList): Boolean { + return try { + connection.autoCommit = false + val sql = "UPDATE ${super.dbName} SET ip = ? WHERE channel = ?" + val statement = connection.prepareStatement(sql) + for (sc in data) { + statement.setString(1, sc.ip) + statement.setString(2, sc.channel) + statement.addBatch() + } + statement.executeBatch() + connection.commit() + Logger.info("Bulk SoundChannel update successful: ${data.size} entries" as Any) + connection.autoCommit = true + true + } catch (e: Exception) { + Logger.error("Error updating SoundChannel entries: ${e.message}" as Any) + false + } + } + + override fun UpdateByIndex(index: Int, data: SoundChannel): Boolean { + try { + val statement = + connection.prepareStatement("UPDATE ${super.dbName} SET channel = ?, ip = ? WHERE `index` = ?") + statement?.setString(1, data.channel) + statement?.setString(2, data.ip) + statement?.setLong(3, index.toLong()) + val rowsAffected = statement?.executeUpdate() + if (rowsAffected != null && rowsAffected > 0) { + Logger.info("SoundChannel updated at index $index: ${data.channel} -> ${data.ip}" as Any) + return true + } else { + Logger.warn("No Sound Channel entry updated at index $index for: ${data.channel} -> ${data.ip}" as Any) + } + } catch (e: Exception) { + Logger.error("Error updating SoundChannel entry at index $index: ${e.message}" as Any) + } + return false + } + + override fun Resort(): Boolean { + try { + val statement = connection.createStatement() + val tempdb_name = "temp_${super.dbName}" + // 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 (channel, ip) SELECT channel, ip FROM ${super.dbName} ORDER BY `index` ") + statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") + statement?.executeUpdate("INSERT INTO ${super.dbName} (channel, ip) SELECT channel, ip FROM $tempdb_name") + statement?.executeUpdate("DROP TABLE $tempdb_name") + Logger.info("${super.dbName} table resorted by index" as Any) + // reload the local list + Get() + return true + } catch (e: Exception) { + Logger.error("Error resorting ${super.dbName} table by index: ${e.message}" as Any) + } + return false + } + + override fun Clear(): Boolean { + try { + val statement = connection.createStatement() + // use TRUNCATE to reset auto increment index + statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") + Logger.info("${super.dbName} table cleared" as Any) + List.clear() + // create new rows from 1 to 64 with description "Channel 1" to "Channel 64" and empty ip + for (i in 1..max_channel) { + val channel = String.format("Channel %d", i) + val insertStatement = + connection.prepareStatement("INSERT INTO ${super.dbName} (channel, ip) VALUES (?, ?)") + insertStatement?.setString(1, channel) + insertStatement?.setString(2, "") + insertStatement?.executeUpdate() + } + return true + } catch (e: Exception) { + Logger.error("Error clearing soundchannel table: ${e.message}" as Any) + } + return false + } + + override fun Import_XLSX(workbook: XSSFWorkbook): Boolean { + try { + val sheet = + workbook.getSheet("SoundChannel") ?: throw Exception("No sheet named 'SoundChannel' found") + val headerRow = sheet.getRow(0) ?: throw Exception("No header row found") + val headers = arrayOf("Index", "channel", "ip") + for ((colIndex, header) in headers.withIndex()) { + val cell = headerRow.getCell(colIndex) ?: throw Exception("Header '$header' not found") + if (cell.stringCellValue != header) throw Exception("Header '$header' not found") + } + // clear existing soundchannel + Clear() + // read each row and insert into database + val _soundChannelList = ArrayList() + for (rowIndex in 1..sheet.lastRowNum) { + val row = sheet.getRow(rowIndex) ?: continue + val channel = row.getCell(1)?.stringCellValue ?: continue + val ip = row.getCell(2)?.stringCellValue ?: continue + val soundChannel = SoundChannel(0u, channel, ip) + _soundChannelList.add(soundChannel) + } + // Bulk update IPs for channels + var success = true + for (sc in _soundChannelList) { + if (!Add(sc)) { + success = false + } + } + return success + } catch (e: Exception) { + Logger.error { "Error importing SoundChannel, Msg: ${e.message}" } + } + return false + } + + override fun Export_XLSX(): XSSFWorkbook? { + try { + val statement = connection.createStatement() + val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") + val workbook = XSSFWorkbook() + val sheet = workbook.createSheet("SoundChannel") + val headerRow = sheet.createRow(0) + val headers = arrayOf("Index", "channel", "ip") + for ((colIndex, header) in headers.withIndex()) { + val cell = headerRow.createCell(colIndex) + cell.setCellValue(header) + } + var rowIndex = 1 + while (resultSet?.next() == true) { + val row = sheet.createRow(rowIndex++) + row.createCell(0).setCellValue(resultSet.getString("index")) + row.createCell(1).setCellValue(resultSet.getString("channel")) + row.createCell(2).setCellValue(resultSet.getString("ip")) + } + for (i in headers.indices) { + sheet.autoSizeColumn(i) + } + return workbook + } catch (e: Exception) { + Logger.error { "Error exporting SoundChannel, Msg: ${e.message}" } + } + return null + } + + /** + * Delete entry by index, but only clear the IP field + * @param index The index of the entry to delete + * @return true if successful, false otherwise + */ + override fun DeleteByIndex(index: Int): Boolean { + try { + val statement = connection.prepareStatement("UPDATE ${super.dbName} SET ip = '' WHERE `index` = ?") + statement?.setLong(1, index.toLong()) + val rowsAffected = statement?.executeUpdate() + if (rowsAffected != null && rowsAffected > 0) { + Logger.info("${super.dbName} IP cleared for index $index" as Any) + return true + } else { + Logger.warn("No ${super.dbName} entry cleared for index $index" as Any) + } + } catch (e: Exception) { + Logger.error("Error clearing ${super.dbName} entry for index $index: ${e.message}" as Any) + } + return false + } + + /** + * Get all distinct sound channel from soundchannelDB + * @return a list of distinct sound channel sorted alphabetically + */ + fun Get_SoundChannel_List(): List { + return List + .distinctBy { it.channel } + .map { it.channel } + .sorted() + } +} \ No newline at end of file diff --git a/src/database/table/Table_Soundbank.kt b/src/database/table/Table_Soundbank.kt new file mode 100644 index 0000000..fe3d420 --- /dev/null +++ b/src/database/table/Table_Soundbank.kt @@ -0,0 +1,308 @@ +package database.table + +import content.Category +import database.data.Soundbank +import database.dbFunctions +import org.apache.poi.xssf.usermodel.XSSFWorkbook +import org.tinylog.Logger +import java.sql.Connection +import java.util.function.Consumer + +class Table_Soundbank(connection: Connection) : dbFunctions("soundbank", connection, listOf("index", "Description", "TAG", "Category", "Language", "VoiceType", "Path")) { + override fun Create() { + val tabledefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" + + "`index` INT AUTO_INCREMENT PRIMARY KEY," + + "Description VARCHAR(1024) NOT NULL," + // Description of the soundbank + "TAG VARCHAR(45) NOT NULL," + // TAG of the soundbank + "Category VARCHAR(45) NOT NULL," + // Category of the soundbank + "Language VARCHAR(45) NOT NULL," + // Language of the soundbank + "VoiceType VARCHAR(45) NOT NULL," + // VoiceType of the soundbank + "Path VARCHAR(1024) NOT NULL" + // Path to the sound file + ")" + super.Create(tabledefinition) + } + + override fun Get(cbOK: Consumer?, cbFail: Consumer?) { + List.clear() + try { + val statement = connection.createStatement() + val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName} ORDER BY Category, Language, VoiceType, TAG") + while (resultSet?.next() == true) { + val soundbank = Soundbank( + resultSet.getLong("index").toUInt(), + resultSet.getString("Description"), + resultSet.getString("TAG"), + resultSet.getString("Category"), + resultSet.getString("Language"), + resultSet.getString("VoiceType"), + resultSet.getString("Path") + ) + List.add(soundbank) + } + cbOK?.accept(Unit) + } catch (e: Exception) { + Logger.error("Error fetching soundbanks: ${e.message}" as Any) + cbFail?.accept("Error fetching ${super.dbName} : ${e.message}") + } + } + + override fun Add(data: Soundbank): Boolean { + try { + val statement = + connection.prepareStatement("INSERT INTO ${super.dbName} (Description, TAG, Category, Language, VoiceType, Path) VALUES (?, ?, ?, ?, ?, ?)") + statement?.setString(1, data.Description) + statement?.setString(2, data.TAG) + statement?.setString(3, data.Category) + statement?.setString(4, data.Language) + statement?.setString(5, data.VoiceType) + statement?.setString(6, data.Path) + val rowsAffected = statement?.executeUpdate() + if (rowsAffected != null && rowsAffected > 0) { + Logger.info("Soundbank added: ${data.Description}" as Any) + return true + } else { + Logger.warn("No soundbank entry added for: ${data.Description}" as Any) + } + } catch (e: Exception) { + Logger.error("Error adding soundbank entry: ${e.message}" as Any) + } + return false + } + + override fun AddAll(data: ArrayList): Boolean { + // use mysql bulk insert + try { + connection.autoCommit = false + val sql = + "INSERT INTO ${super.dbName} (Description, TAG, Category, Language, VoiceType, Path) VALUES (?, ?, ?, ?, ?, ?)" + val statement = connection.prepareStatement(sql) + for (sb in data) { + statement.setString(1, sb.Description) + statement.setString(2, sb.TAG) + statement.setString(3, sb.Category) + statement.setString(4, sb.Language) + statement.setString(5, sb.VoiceType) + statement.setString(6, sb.Path) + statement.addBatch() + } + statement.executeBatch() + connection.commit() + Logger.info("Bulk soundbank insert successful: ${data.size} entries" as Any) + connection.autoCommit = true + return true + } catch (e: Exception) { + Logger.error("Error adding soundbank entries: ${e.message}" as Any) + } + return false + } + + override fun UpdateByIndex(index: Int, data: Soundbank): Boolean { + try { + val statement = + connection.prepareStatement("UPDATE ${super.dbName} SET Description = ?, TAG = ?, Category = ?, Language = ?, VoiceType = ?, Path = ? WHERE `index` = ?") + statement?.setString(1, data.Description) + statement?.setString(2, data.TAG) + statement?.setString(3, data.Category) + statement?.setString(4, data.Language) + statement?.setString(5, data.VoiceType) + statement?.setString(6, data.Path) + statement?.setLong(7, index.toLong()) + val rowsAffected = statement?.executeUpdate() + if (rowsAffected != null && rowsAffected > 0) { + Logger.info("Soundbank updated at index $index: ${data.Description}" as Any) + return true + } else { + Logger.warn("No soundbank entry updated at index $index for: ${data.Description}" as Any) + } + } catch (e: Exception) { + Logger.error("Error updating soundbank entry at index $index: ${e.message}" as Any) + } + return false + } + + override fun Resort(): Boolean { + try { + val statement = connection.createStatement() + val tempdb_name = "temp_${super.dbName}" + + // 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 (Description, TAG, Category, Language, VoiceType, Path) SELECT Description, TAG, Category, Language, VoiceType, Path FROM ${super.dbName} ORDER BY Category, Language, VoiceType, TAG ") + statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") + statement?.executeUpdate("INSERT INTO ${super.dbName} (Description, TAG, Category, Language, VoiceType, Path) SELECT Description, TAG, Category, Language, VoiceType, Path FROM $tempdb_name") + statement?.executeUpdate("DROP TABLE $tempdb_name") + Logger.info("${super.dbName} table resorted by Description" as Any) + // reload the local list + Get() + return true + } catch (e: Exception) { + Logger.error("Error resorting ${super.dbName} table by Description: ${e.message}" as Any) + } + return false + } + + override fun Import_XLSX(workbook: XSSFWorkbook): Boolean { + try { + // check if there is sheet named "Soundbank" + val sheet = + workbook.getSheet("Soundbank") ?: throw Exception("No sheet named 'Soundbank' found") + // check if the sheet contains header named "index", "Description", "TAG", "Category", "Language", "VoiceType", "Path" + val headerRow = sheet.getRow(0) ?: throw Exception("No header row found") + val headers = + arrayOf("Index", "Description", "TAG", "Category", "Language", "VoiceType", "Path") + for ((colIndex, header) in headers.withIndex()) { + val cell = headerRow.getCell(colIndex) ?: throw Exception("Header '$header' not found") + if (cell.stringCellValue != header) throw Exception("Header '$header' not found") + } + // clear existing soundbank + Clear() + // read each row and insert into database + val _soundbankList = ArrayList() + for (rowIndex in 1..sheet.lastRowNum) { + val row = sheet.getRow(rowIndex) ?: continue + val description = row.getCell(1)?.stringCellValue ?: continue + val tag = row.getCell(2)?.stringCellValue ?: continue + val category = row.getCell(3)?.stringCellValue ?: continue + val language = row.getCell(4)?.stringCellValue ?: continue + val voiceType = row.getCell(5)?.stringCellValue ?: continue + val path = row.getCell(6)?.stringCellValue ?: continue + val soundbank = Soundbank(0u, description, tag, category, language, voiceType, path) + _soundbankList.add(soundbank) + } + return AddAll(_soundbankList) + } catch (e: Exception) { + Logger.error { "Error importing Soundbank, Msg: ${e.message}" } + } + return false + } + + override fun Export_XLSX(): XSSFWorkbook? { + try { + val statement = connection.createStatement() + val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") + val workbook = XSSFWorkbook() + val sheet = workbook.createSheet("Soundbank") + val headerRow = sheet.createRow(0) + val headers = + arrayOf("Index", "Description", "TAG", "Category", "Language", "VoiceType", "Path") + for ((colIndex, header) in headers.withIndex()) { + val cell = headerRow.createCell(colIndex) + cell.setCellValue(header) + } + var rowIndex = 1 + while (resultSet?.next() == true) { + val row = sheet.createRow(rowIndex++) + row.createCell(0).setCellValue(resultSet.getString("index")) + row.createCell(1).setCellValue(resultSet.getString("Description")) + row.createCell(2).setCellValue(resultSet.getString("TAG")) + row.createCell(3).setCellValue(resultSet.getString("Category")) + row.createCell(4).setCellValue(resultSet.getString("Language")) + row.createCell(5).setCellValue(resultSet.getString("VoiceType")) + row.createCell(6).setCellValue(resultSet.getString("Path")) + } + for (i in 0 until headers.size) { + sheet.autoSizeColumn(i) + } + return workbook + } catch (e: Exception) { + Logger.error { "Error exporting Soundbank, Msg: ${e.message}" } + } + return null + } + + /** + * Get all distinct airline code tags from soundbank + * @return a list of distinct airline code tags sorted alphabetically + */ + fun Get_AirlineCode_Tags(): List { + + return List + .filter { it.Category.equals(Category.Airline_Code.name,true) } + .distinctBy { it.TAG } + .map { it.TAG } + .sorted() + } + + /** + * Get all distinct city tags from soundbank + * @return a list of distinct city tags sorted alphabetically + */ + fun Get_City_Tags(): List { + return List + .filter { it.Category.equals(Category.City.name,true) } + .distinctBy { it.TAG } + .map { it.TAG } + .sorted() + } + + fun Find_City_By_TAG(tag: String) : List { + return List + .filter {it.Category.equals(Category.City.name,true) } + .filter { it.TAG.equals(tag, true) } + .distinctBy { it.TAG } + } + + fun Find_AirlineName_By_TAG(tag: String) : List { + return List + .filter {it.Category.equals(Category.Airplane_Name.name,true) } + .filter { it.TAG.equals(tag, true) } + .distinctBy { it.TAG } + } + + fun Get_Places(): List { + return List + .filter { it.Category.equals(Category.Places.name,true) } + .distinctBy { it.TAG } + .sortedBy { it.TAG } + } + + fun Get_Shalat() : List { + return List + .filter { it.Category.equals(Category.Shalat.name,true) } + .distinctBy { it.TAG } + .sortedBy { it.TAG } + } + + fun Get_Sequences() : List { + return List + .filter { it.Category.equals(Category.Sequence.name,true) } + .distinctBy { it.TAG } + .sortedBy { it.TAG } + } + + fun Get_Reasons() : List { + return List + .filter { it.Category.equals(Category.Reason.name,true) } + .distinctBy { it.TAG } + .sortedBy { it.TAG } + } + + fun Get_Gates(): List { + return List + .filter { it.Category.equals(Category.Gate.name,true) } + .distinctBy { it.TAG } + .sortedBy { it.TAG } + } + + fun Get_Compensation(): List { + return List + .filter { it.Category.equals(Category.Compensation.name,true) } + .distinctBy { it.TAG } + .sortedBy { it.TAG } + } + + fun Get_Greeting() : List { + return List + .filter { it.Category.equals(Category.Greeting.name,true) } + .distinctBy { it.TAG } + .sortedBy { it.TAG } + } + + fun Get_Procedures(): List { + return List + .filter { it.Category.equals(Category.Procedure.name,true) } + .distinctBy { it.TAG } + .sortedBy { it.TAG } + } +} \ No newline at end of file diff --git a/src/database/table/Table_Users.kt b/src/database/table/Table_Users.kt new file mode 100644 index 0000000..f051f4c --- /dev/null +++ b/src/database/table/Table_Users.kt @@ -0,0 +1,225 @@ +package database.table + +import database.data.UserDB +import database.dbFunctions +import org.apache.poi.xssf.usermodel.XSSFWorkbook +import org.tinylog.Logger +import java.sql.Connection +import java.util.function.Consumer + +class Table_Users(connection : Connection) : dbFunctions("newuser", connection, listOf("index", "username", "password", "location", "airline_tags", "city_tags", "messagebank_ann_id", "broadcastzones")) { + override fun Create() { + val tableDefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" + + "`index` INT AUTO_INCREMENT PRIMARY KEY," + + "username VARCHAR(100) NOT NULL," + + "password VARCHAR(100) NOT NULL," + + "location VARCHAR(100) NOT NULL," + + "airline_tags TEXT NOT NULL,"+ // Comma-separated soundbank tags + "city_tags TEXT NOT NULL,"+ // Comma-separated soundbank tags + "messagebank_ann_id TEXT NOT NULL,"+ // Comma-separated messagebank announcement index + "broadcastzones TEXT NOT NULL"+ // Comma-separated broadcast zones + ")" + super.Create(tableDefinition) + } + + override fun Get(cbOK: Consumer?, cbFail: Consumer?) { + List.clear() + try { + val statement = connection.createStatement() + val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") + while (resultSet?.next() == true) { + val user = UserDB( + resultSet.getLong("index").toUInt(), + resultSet.getString("username"), + resultSet.getString("password"), + resultSet.getString("location"), + resultSet.getString("airline_tags"), + resultSet.getString("city_tags"), + resultSet.getString("messagebank_ann_id"), + resultSet.getString("broadcastzones") + ) + List.add(user) + } + cbOK?.accept(Unit) + } catch (e: Exception) { + Logger.error("Error fetching users: ${e.message}" as Any) + cbFail?.accept("Error fetching users: ${e.message}" ) + } + } + + override fun Add(data: UserDB): Boolean { + try { + val statement = connection.prepareStatement("INSERT INTO ${super.dbName} (username, password, location, airline_tags, city_tags, messagebank_ann_id, broadcastzones) VALUES (?, ?, ?, ?,?, ?, ?)") + statement?.setString(1, data.username) + statement?.setString(2, data.password) + statement?.setString(3, data.location) + statement?.setString(4, data.airline_tags) + statement?.setString(5, data.city_tags) + statement?.setString(6, data.messagebank_ann_id) + statement?.setString(7, data.broadcastzones) + val rowsAffected = statement?.executeUpdate() + if (rowsAffected != null && rowsAffected > 0) { + Logger.info("User added: ${data.username}" as Any) + return true + } else { + Logger.warn("No user entry added for: ${data.username}" as Any) + } + } catch (e: Exception) { + Logger.error("Error adding user entry: ${e.message}" as Any) + } + return false + } + + override fun AddAll(data: ArrayList): Boolean { + return try { + connection.autoCommit = false + val sql = "INSERT INTO ${super.dbName} (username, password, location,airline_tags,city_tags, messagebank_ann_id, broadcastzones) VALUES (?, ?, ?,?, ?, ?, ?)" + val statement = connection.prepareStatement(sql) + for (user in data) { + statement.setString(1, user.username) + statement.setString(2, user.password) + statement.setString(3, user.location) + statement.setString(4, user.airline_tags) + statement.setString(5, user.city_tags) + statement.setString(6, user.messagebank_ann_id) + statement.setString(7, user.broadcastzones) + statement.addBatch() + } + statement.executeBatch() + connection.commit() + Logger.info("Bulk user insert successful: ${data.size} entries" as Any) + connection.autoCommit = true + true + } catch (e: Exception) { + Logger.error("Error adding user entries: ${e.message}" as Any) + false + } + } + + override fun UpdateByIndex(index: Int, data: UserDB): Boolean { + try { + val statement = connection.prepareStatement("UPDATE ${super.dbName} SET username = ?, password = ?, location = ?, airline_tags = ?,city_tags=?, messagebank_ann_id = ?, broadcastzones = ? WHERE `index` = ?") + statement?.setString(1, data.username) + statement?.setString(2, data.password) + statement?.setString(3, data.location) + statement?.setString(4, data.airline_tags) + statement?.setString(5, data.city_tags) + statement?.setString(6, data.messagebank_ann_id) + statement?.setString(7, data.broadcastzones) + statement?.setLong(8, index.toLong()) + val rowsAffected = statement?.executeUpdate() + if (rowsAffected != null && rowsAffected > 0) { + Logger.info("User updated at index $index: ${data.username}" as Any) + return true + } else { + Logger.warn("No user entry updated at index $index for: ${data.username}" as Any) + } + } catch (e: Exception) { + Logger.error("Error updating user entry at index $index: ${e.message}" as Any) + } + return false + } + + override fun Resort(): Boolean { + try { + val statement = connection.createStatement() + val tempdb_name = "temp_${super.dbName}" + // 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 (username, password, location, airline_tags, city_tags, messagebank_ann_id, broadcastzones) SELECT username, password, location, airline_tags, city_tags, messagebank_ann_id, broadcastzones FROM ${super.dbName} ORDER BY username ") + statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") + statement?.executeUpdate("INSERT INTO ${super.dbName} (username, password, location, airline_tags, city_tags, messagebank_ann_id, broadcastzones) SELECT username, password, location, airline_tags, city_tags, messagebank_ann_id, broadcastzones FROM $tempdb_name") + statement?.executeUpdate("DROP TABLE $tempdb_name") + Logger.info("${super.dbName} table resorted by index" as Any) + // reload the local list + Get() + return true + } catch (e: Exception) { + Logger.error("Error resorting ${super.dbName} table by index: ${e.message}" as Any) + } + return false + } + + override fun Import_XLSX(workbook: XSSFWorkbook): Boolean { + try { + val sheet = workbook.getSheet("User") ?: throw Exception("No sheet named 'User' found") + val headerRow = sheet.getRow(0) ?: throw Exception("No header row found") + val headers = arrayOf("Index", "username", "password", "location", "airline_tags", "city_tags", "messagebank_ann_id", "broadcastzones") + for ((colIndex, header) in headers.withIndex()) { + val cell = headerRow.getCell(colIndex) ?: throw Exception("Header '$header' not found") + if (cell.stringCellValue != header) throw Exception("Header '$header' not found") + } + // clear existing users + Clear() + // read each row and insert into database + val _userList = ArrayList() + for (rowIndex in 1..sheet.lastRowNum) { + val row = sheet.getRow(rowIndex) ?: continue + val username = row.getCell(1)?.stringCellValue ?: continue + val password = row.getCell(2)?.stringCellValue ?: continue + val location = row.getCell(3)?.stringCellValue ?: continue + val airline_tags = row.getCell(4)?.stringCellValue ?: continue + val city_tags = row.getCell(5)?.stringCellValue ?: continue + val messagebank_ann_id = row.getCell(6)?.stringCellValue ?: continue + val broadcastzones = row.getCell(7)?.stringCellValue ?: continue + val user = UserDB( + 0u, + username, + password, + location, + airline_tags, + city_tags, + messagebank_ann_id, + broadcastzones + ) + _userList.add(user) + } + return AddAll(_userList) + } catch (e: Exception) { + Logger.error { "Error importing User, Msg: ${e.message}" } + } + return false + } + + override fun Export_XLSX(): XSSFWorkbook? { + try { + val statement = connection.createStatement() + val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") + val workbook = XSSFWorkbook() + val sheet = workbook.createSheet("User") + val headerRow = sheet.createRow(0) + val headers = arrayOf("Index", "username", "password", "location", "airline_tags","city_tags", "messagebank_ann_id", "broadcastzones") + for ((colIndex, header) in headers.withIndex()) { + val cell = headerRow.createCell(colIndex) + cell.setCellValue(header) + } + var rowIndex = 1 + while (resultSet?.next() == true) { + val row = sheet.createRow(rowIndex++) + row.createCell(0).setCellValue(resultSet.getString("index")) + row.createCell(1).setCellValue(resultSet.getString("username")) + row.createCell(2).setCellValue(resultSet.getString("password")) + row.createCell(3).setCellValue(resultSet.getString("location")) + row.createCell(4).setCellValue(resultSet.getString("airline_tags")) + row.createCell(5).setCellValue(resultSet.getString("city_tags")) + row.createCell(6).setCellValue(resultSet.getString("messagebank_ann_id")) + row.createCell(7).setCellValue(resultSet.getString("broadcastzones")) + } + for (i in headers.indices) { + sheet.autoSizeColumn(i) + } + return workbook + } catch (e: Exception) { + Logger.error { "Error exporting User, Msg: ${e.message}" } + } + return null + } + + /** + * Check if a username already exists in the userDB (case-insensitive) + */ + fun Username_exists(username: String): Boolean { + return List.any { it.username.equals(username, ignoreCase = true) } + } +} \ No newline at end of file diff --git a/src/google/GoogleTTS.kt b/src/google/GoogleTTS.kt index 8a26d1f..1be30c7 100644 --- a/src/google/GoogleTTS.kt +++ b/src/google/GoogleTTS.kt @@ -9,7 +9,7 @@ import com.google.cloud.texttospeech.v1.VoiceSelectionParams import content.Category import content.Language import content.VoiceType -import database.Soundbank +import database.data.Soundbank import org.tinylog.Logger import java.nio.file.Files import java.util.function.BiConsumer diff --git a/src/web/WebApp.kt b/src/web/WebApp.kt index f481ae2..4299b22 100644 --- a/src/web/WebApp.kt +++ b/src/web/WebApp.kt @@ -24,14 +24,14 @@ import content.Category import content.Language import content.ScheduleDay import content.VoiceType -import database.BroadcastZones -import database.LanguageLink +import database.data.BroadcastZones +import database.data.LanguageLink import database.MariaDB -import database.Messagebank -import database.ScheduleBank -import database.SoundChannel -import database.Soundbank -import database.UserDB +import database.data.Messagebank +import database.data.ScheduleBank +import database.data.SoundChannel +import database.data.Soundbank +import database.data.UserDB import db import io.javalin.Javalin import io.javalin.apibuilder.ApiBuilder.* @@ -43,8 +43,8 @@ import java.nio.file.Files import java.time.LocalDateTime import codes.configKeys import config -import database.LogSemiauto -import database.QueueTable +import database.data.LogSemiauto +import database.data.QueueTable import google.GoogleTTS import google.autoadd import google.fileoperation @@ -128,7 +128,7 @@ class WebApp(val listenPort: Int, var userlist: List>, val //it.sessionAttribute("user", user.first) it.cookie("aas-user", user.first) // Redirect to home page - if (user.first== GetAdminUserFromConfig()){ + if (user.first == GetAdminUserFromConfig()) { it.redirect("homeadmin.html") } else { it.redirect("homeviewer.html") @@ -149,9 +149,10 @@ class WebApp(val listenPort: Int, var userlist: List>, val val cmd = objectmapper.readValue(wsMessageContext.message(), WebsocketCommand::class.java) when (cmd.command) { - "getAppVersion" ->{ + "getAppVersion" -> { SendReply(wsMessageContext, cmd.command, version) } + "getSystemTime" -> { val systemtime = LocalDateTime.now().format(datetimeformat1) @@ -184,11 +185,19 @@ class WebApp(val listenPort: Int, var userlist: List>, val } "getMemoryStatus" -> { - SendReply(wsMessageContext, cmd.command, objectmapper.writeValueAsString(Somecodes.getMemoryUsage())) + SendReply( + wsMessageContext, + cmd.command, + objectmapper.writeValueAsString(Somecodes.getMemoryUsage()) + ) } "getDiskStatus" -> { - SendReply(wsMessageContext, cmd.command, objectmapper.writeValueAsString(Somecodes.getDiskUsage())) + SendReply( + wsMessageContext, + cmd.command, + objectmapper.writeValueAsString(Somecodes.getDiskUsage()) + ) } "getNetworkStatus" -> { @@ -223,8 +232,8 @@ class WebApp(val listenPort: Int, var userlist: List>, val SendReply(wsMessageContext, cmd.command, objectmapper.writeValueAsString(reply)) } - "start_generate_tts" ->{ - val js : JsonNode = objectmapper.readTree(cmd.data) + "start_generate_tts" -> { + val js: JsonNode = objectmapper.readTree(cmd.data) SendReply(wsMessageContext, cmd.command, "ok") val voicetype = js.get("voicetype")?.asText("Wavenet-A") ?: "Wavenet-A" val languagecode = js.get("languagecode")?.asText("id-ID") ?: "id-ID" @@ -232,21 +241,27 @@ class WebApp(val listenPort: Int, var userlist: List>, val val targetas = js.get("targetas")?.asText("VOICE_2") ?: "VOICE_2" val fop = js.get("fileoperation")?.asText("overwrite") ?: "overwrite" val aa = js.get("autoadd")?.asText("add") ?: "add" - Logger.info {"Starting TTS Soundbank Generation, VoiceType=$voicetype, Language"} - ttsjob.GenerateSoundbank(voicetype, Language.from_GoogleTTSLanguage(languagecode), + Logger.info { "Starting TTS Soundbank Generation, VoiceType=$voicetype, Language" } + ttsjob.GenerateSoundbank( + voicetype, Language.from_GoogleTTSLanguage(languagecode), VoiceType.fromString(databasesource), VoiceType.fromString(targetas), fileoperation.fromString(fop), - autoadd.fromString(aa)){ progress, message -> - SendReply(wsMessageContext, "tts_generate_progress", objectmapper.writeValueAsString( - mapOf( - "progress" to progress, - "message" to message + autoadd.fromString(aa) + ) { progress, message -> + SendReply( + wsMessageContext, + "tts_generate_progress", + objectmapper.writeValueAsString( + mapOf( + "progress" to progress, + "message" to message + ) ) - )) + ) } } - "stop_generate_tts" ->{ + "stop_generate_tts" -> { SendReply(wsMessageContext, cmd.command, "ok") ttsjob.StopGenerate() @@ -311,11 +326,19 @@ class WebApp(val listenPort: Int, var userlist: List>, val } "getMemoryStatus" -> { - SendReply(wsMessageContext, cmd.command, objectmapper.writeValueAsString(Somecodes.getMemoryUsage())) + SendReply( + wsMessageContext, + cmd.command, + objectmapper.writeValueAsString(Somecodes.getMemoryUsage()) + ) } "getDiskStatus" -> { - SendReply(wsMessageContext, cmd.command, objectmapper.writeValueAsString(Somecodes.getDiskUsage())) + SendReply( + wsMessageContext, + cmd.command, + objectmapper.writeValueAsString(Somecodes.getDiskUsage()) + ) } "getNetworkStatus" -> { @@ -367,7 +390,7 @@ class WebApp(val listenPort: Int, var userlist: List>, val before { val user = CheckUsers(it) // only admin user can access soundbank page - if (GetAdminUserFromConfig() != user){ + if (GetAdminUserFromConfig() != user) { it.redirect("overview.html") } } @@ -376,7 +399,7 @@ class WebApp(val listenPort: Int, var userlist: List>, val before { val user = CheckUsers(it) // only admin user can access messagebank page - if (GetAdminUserFromConfig() != user){ + if (GetAdminUserFromConfig() != user) { it.redirect("overview.html") } } @@ -397,7 +420,7 @@ class WebApp(val listenPort: Int, var userlist: List>, val before { val user = CheckUsers(it) // only admin user can access settings page - if (GetAdminUserFromConfig() != user){ + if (GetAdminUserFromConfig() != user) { it.redirect("overview.html") } } @@ -406,7 +429,7 @@ class WebApp(val listenPort: Int, var userlist: List>, val before { val user = CheckUsers(it) // only admin user can access timer page - if (GetAdminUserFromConfig() != user){ + if (GetAdminUserFromConfig() != user) { it.redirect("overview.html") } } @@ -415,7 +438,7 @@ class WebApp(val listenPort: Int, var userlist: List>, val before { val user = CheckUsers(it) // only admin user can access file management page - if (GetAdminUserFromConfig() != user){ + if (GetAdminUserFromConfig() != user) { it.redirect("overview.html") } } @@ -424,7 +447,7 @@ class WebApp(val listenPort: Int, var userlist: List>, val before { val user = CheckUsers(it) // only admin user can access broadcast zones page - if (GetAdminUserFromConfig() != user){ + if (GetAdminUserFromConfig() != user) { it.redirect("overview.html") } } @@ -441,7 +464,7 @@ class WebApp(val listenPort: Int, var userlist: List>, val before { val user = CheckUsers(it) // only admin user can access user management page - if (GetAdminUserFromConfig() != user){ + if (GetAdminUserFromConfig() != user) { it.redirect("overview.html") } } @@ -449,23 +472,26 @@ class WebApp(val listenPort: Int, var userlist: List>, val path("api") { //TODO https://stackoverflow.com/questions/70002015/streaming-into-audio-element path("LiveAudio") { - post{ ctx-> - val json : JsonNode = objectmapper.readTree(ctx.body()) + post { ctx -> + val json: JsonNode = objectmapper.readTree(ctx.body()) val broadcastzone = json.get("broadcastzone")?.asText("") ?: "" val command = json.get("command")?.asText("") ?: "" - if (command == "Open" || command == "Close"){ - if (broadcastzone.isNotEmpty()){ + if (command == "Open" || command == "Close") { + if (broadcastzone.isNotEmpty()) { val bc = Get_Barix_Connection_by_ZoneName(broadcastzone) - if (bc!=null){ + if (bc != null) { val key = ctx.cookie("client-stream-id") - if (command == "Open"){ + if (command == "Open") { // open command - if (!key.isNullOrEmpty()){ + if (!key.isNullOrEmpty()) { // ada connection sebelumnya, kemungkinan reconnect val prev = WsContextMap[key] - if (prev!=null){ + if (prev != null) { prev.bc?.Remove_Mp3_Consumer(key) - prev.ws?.closeSession(WsCloseStatus.NORMAL_CLOSURE, "Reopen Live Audio Stream") + prev.ws?.closeSession( + WsCloseStatus.NORMAL_CLOSURE, + "Reopen Live Audio Stream" + ) } WsContextMap.remove(key) ctx.cookie("client-stream-id", "") @@ -478,12 +504,15 @@ class WebApp(val listenPort: Int, var userlist: List>, val } else { // close command - if (!key.isNullOrEmpty()){ + if (!key.isNullOrEmpty()) { // close connection val prev = WsContextMap[key] - if (prev!=null){ + if (prev != null) { prev.bc?.Remove_Mp3_Consumer(key) - prev.ws?.closeSession(WsCloseStatus.NORMAL_CLOSURE, "Close Live Audio Stream") + prev.ws?.closeSession( + WsCloseStatus.NORMAL_CLOSURE, + "Close Live Audio Stream" + ) } WsContextMap.remove(key) ctx.cookie("client-stream-id", "") @@ -495,42 +524,42 @@ class WebApp(val listenPort: Int, var userlist: List>, val } else ResultMessageString(ctx, 400, "Invalid command") } - ws("ws"){ wscontext -> + ws("ws") { wscontext -> wscontext.onConnect { val key = it.cookie("client-stream-id") - if (!key.isNullOrEmpty()){ - val lld = WsContextMap[key] - if (lld!=null){ + if (!key.isNullOrEmpty()) { + val lld = WsContextMap[key] + if (lld != null) { it.enableAutomaticPings() lld.ws = it - lld.bc?.Add_Mp3_Consumer(key){ mp3data-> + lld.bc?.Add_Mp3_Consumer(key) { mp3data -> try { - if (it.session.isOpen){ + if (it.session.isOpen) { it.send(ByteBuffer.wrap(mp3data)) } - } catch (e: Exception){ - Logger.error {"Error sending LiveAudio mp3 data for key $key, Message: ${e.message}"} + } catch (e: Exception) { + Logger.error { "Error sending LiveAudio mp3 data for key $key, Message: ${e.message}" } } } } else { it.closeSession(WsCloseStatus.POLICY_VIOLATION, "WsContextMap key not found") - Logger.info{"LiveAudio WebSocket connection rejected, WsContextMap key not found"} + Logger.info { "LiveAudio WebSocket connection rejected, WsContextMap key not found" } } } else { it.closeSession(WsCloseStatus.POLICY_VIOLATION, "Invalid client-stream-id") - Logger.info{"LiveAudio WebSocket connection rejected, invalid client-stream-id"} + Logger.info { "LiveAudio WebSocket connection rejected, invalid client-stream-id" } } } wscontext.onClose { val key = it.cookie("client-stream-id") - if (!key.isNullOrEmpty()){ + if (!key.isNullOrEmpty()) { val lld = WsContextMap[key] lld?.bc?.Remove_Mp3_Consumer(key) lld?.ws?.closeSession() lld?.bc = null - lld?.ws= null + lld?.ws = null WsContextMap.remove(key) - Logger.info{"LiveAudio WebSocket closed for key $key"} + Logger.info { "LiveAudio WebSocket closed for key $key" } } @@ -538,7 +567,7 @@ class WebApp(val listenPort: Int, var userlist: List>, val wscontext.onError { val key = it.cookie("client-stream-id") val msg = it.error()?.message ?: "" - Logger.info{"LiveAudio WebSocket error for key $key, Message: $msg"} + Logger.info { "LiveAudio WebSocket error for key $key, Message: $msg" } } } @@ -595,7 +624,7 @@ class WebApp(val listenPort: Int, var userlist: List>, val get("List") { ctx -> db.soundDB.Get({ ctx.result(MariaDB.ArrayListtoString(db.soundDB.List)) - },{msgFail -> + }, { msgFail -> ctx.status(500) .result(objectmapper.writeValueAsString(resultMessage(msgFail))) }) @@ -623,11 +652,11 @@ class WebApp(val listenPort: Int, var userlist: List>, val .result(objectmapper.writeValueAsString(resultMessage("Invalid Language"))) } get("AirlineTags") { ctx -> - val tags = db.Get_AirlineCode_Tags() + val tags = db.soundDB.Get_AirlineCode_Tags() val value = db.soundDB.List .asSequence() - .filter { f1 -> f1.Category.equals(Category.Airplane_Name.name,true) } - .filter { f2 -> tags.any{t -> t.equals(f2.TAG,true)}} + .filter { f1 -> f1.Category.equals(Category.Airplane_Name.name, true) } + .filter { f2 -> tags.any { t -> t.equals(f2.TAG, true) } } .distinctBy { it.TAG } .sortedBy { it.TAG } .map { KeyValueMessage(it.TAG, it.Description) } @@ -831,7 +860,7 @@ class WebApp(val listenPort: Int, var userlist: List>, val // get messagebank list db.messageDB.Get({ ctx.result(MariaDB.ArrayListtoString(db.messageDB.List)) - },{msgFail -> + }, { msgFail -> ctx.status(500).result(objectmapper.writeValueAsString(resultMessage(msgFail))) }) @@ -867,8 +896,9 @@ class WebApp(val listenPort: Int, var userlist: List>, val message_detail, message_tags ) - val existed = db.messageDB.List.any{ it.ANN_ID== mb.ANN_ID && it.Language == mb.Language && it.Voice_Type == mb.Voice_Type } - if (!existed){ + val existed = + db.messageDB.List.any { it.ANN_ID == mb.ANN_ID && it.Language == mb.Language && it.Voice_Type == mb.Voice_Type } + if (!existed) { if (db.messageDB.Add(mb)) { db.messageDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) @@ -1026,11 +1056,21 @@ class WebApp(val listenPort: Int, var userlist: List>, val db.languageDB.Get({ // get language link list ctx.result(MariaDB.ArrayListtoString(db.languageDB.List)) - },{ - msgFail -> + }, { msgFail -> ctx.status(500).result(objectmapper.writeValueAsString(resultMessage(msgFail))) }) + } + post("DefaultInit") { ctx -> + // clear languageDB and init with default values + // default value : every CITY in soundDB will have language linked to + db.languageDB.Clear() + db.soundDB.Get_City_Tags().forEach { city -> + val newvalue = LanguageLink(0u, city, Language.DefaultLanguageLink()) + db.languageDB.Add(newvalue) + } + db.languageDB.Resort() + } post("Add") { // Parse JSON from request body @@ -1179,8 +1219,7 @@ class WebApp(val listenPort: Int, var userlist: List>, val db.scheduleDB.Get({ // get timer list ctx.result(MariaDB.ArrayListtoString(db.scheduleDB.List)) - },{ - msgFail -> + }, { msgFail -> ctx.status(500).result(objectmapper.writeValueAsString(resultMessage(msgFail))) }) // get timer list @@ -1388,8 +1427,7 @@ class WebApp(val listenPort: Int, var userlist: List>, val get("List") { ctx -> db.userDB.Get({ ctx.result(objectmapper.writeValueAsString(db.userDB.List)) - },{ - msgFail -> + }, { msgFail -> ctx.status(500).result(objectmapper.writeValueAsString(resultMessage(msgFail))) }) @@ -1416,54 +1454,73 @@ class WebApp(val listenPort: Int, var userlist: List>, val // city, airline, messagebank_ann_id boleh kosong, untuk user yang paging saja // location juga optional // yang wajib ada adalah username, password, broadcastzones - if (ValidStrings(username, password, broadcastzones )) - { - if (!db.Username_exists(username)) { - if (ValidString(airline_tags)){ + if (ValidStrings(username, password, broadcastzones)) { + if (!db.userDB.Username_exists(username)) { + if (ValidString(airline_tags)) { // ada airline_tags, berarti harus di cek apakah ada di table soundbank // kalau tidak ada airline_tags, tidak perlu cek val atags = airline_tags.split(";").map { it.trim() } .filter { it.isNotEmpty() }.distinct() - val airlinetags = db.Get_AirlineCode_Tags() + val airlinetags = db.soundDB.Get_AirlineCode_Tags() val missing_airlinetags = ArrayList() atags.forEach { tag -> - if (!airlinetags.any { it.equals(tag,true) }) { + if (!airlinetags.any { it.equals(tag, true) }) { missing_airlinetags.add(tag) } } - if (missing_airlinetags.isNotEmpty()){ + if (missing_airlinetags.isNotEmpty()) { ctx.status(400) - .result(objectmapper.writeValueAsString(resultMessage("Airline tags not found in soundbank: ${missing_airlinetags.joinToString(", ")}"))) + .result( + objectmapper.writeValueAsString( + resultMessage( + "Airline tags not found in soundbank: ${ + missing_airlinetags.joinToString( + ", " + ) + }" + ) + ) + ) return@post } } - if (ValidString(city_tags)){ + if (ValidString(city_tags)) { // ada city_tags, berarti harus di cek apakah ada di table soundbank // kalau tidak ada city_tags, tidak perlu cek val ctags = city_tags.split(";").map { it.trim() } .filter { it.isNotEmpty() }.distinct() - val citytags = db.Get_City_Tags() + val citytags = db.soundDB.Get_City_Tags() val missing_citytags = ArrayList() ctags.forEach { tag -> - if (!citytags.any { it.equals(tag,true) }) { + if (!citytags.any { it.equals(tag, true) }) { missing_citytags.add(tag) } } - if (missing_citytags.isNotEmpty()){ + if (missing_citytags.isNotEmpty()) { ctx.status(400) - .result(objectmapper.writeValueAsString(resultMessage("City tags not found in soundbank: ${missing_citytags.joinToString(", ")}"))) + .result( + objectmapper.writeValueAsString( + resultMessage( + "City tags not found in soundbank: ${ + missing_citytags.joinToString( + ", " + ) + }" + ) + ) + ) return@post } } - if (ValidString(messagebank_ann_id)){ + if (ValidString(messagebank_ann_id)) { // ada messagebank_ann_id, berarti harus di cek apakah ada di table messagebank // kalau tidak ada messagebank_ann_id, tidak perlu cek val mbids = messagebank_ann_id.split(";") .map { it.trim() } .filter { it.isNotEmpty() }.distinct() .mapNotNull { it.toUIntOrNull() } - val mbankids = db.Get_MessageID_List() + val mbankids = db.messageDB.Get_MessageID_List() if (!mbids.all { id -> mbankids.any { it == id } }) { ctx.status(400) .result(objectmapper.writeValueAsString(resultMessage("Some ANN_ID not found in Messagebank"))) @@ -1475,16 +1532,26 @@ class WebApp(val listenPort: Int, var userlist: List>, val val bzdesc = broadcastzones.split(";").map { it.trim() } .filter { it.isNotEmpty() }.distinct() - val bzlist = db.Get_BroadcastZone_List() + val bzlist = db.broadcastDB.Get_BroadcastZone_List() val missing_broadcastzones = ArrayList() bzdesc.forEach { bz -> - if (!bzlist.any { it.equals(bz,true) }) { + if (!bzlist.any { it.equals(bz, true) }) { missing_broadcastzones.add(bz) } } - if (missing_broadcastzones.isNotEmpty()){ + if (missing_broadcastzones.isNotEmpty()) { ctx.status(400) - .result(objectmapper.writeValueAsString(resultMessage("Broadcast zone tags not found in soundbank: ${missing_broadcastzones.joinToString(", ")}"))) + .result( + objectmapper.writeValueAsString( + resultMessage( + "Broadcast zone tags not found in soundbank: ${ + missing_broadcastzones.joinToString( + ", " + ) + }" + ) + ) + ) return@post } @@ -1546,16 +1613,15 @@ class WebApp(val listenPort: Int, var userlist: List>, val // city, airline, messagebank_ann_id boleh kosong, untuk user yang paging saja // location juga optional // yang wajib ada adalah username, password, broadcastzones - if (ValidStrings(_username, _password, _broadcastzones)) - { + if (ValidStrings(_username, _password, _broadcastzones)) { val _otherusername = db.userDB.List.find { xx -> xx.username.equals( _username, true ) && xx.index != index } - if (_otherusername!=null){ - Logger.info{"Found other username with same name: ${_otherusername.username} at index ${_otherusername.index}"} + if (_otherusername != null) { + Logger.info { "Found other username with same name: ${_otherusername.username} at index ${_otherusername.index}" } ctx.status(400).result( objectmapper.writeValueAsString( resultMessage("Username already exists for another user") @@ -1564,60 +1630,91 @@ class WebApp(val listenPort: Int, var userlist: List>, val return@patch } - if (ValidString(_airline_tags)){ + if (ValidString(_airline_tags)) { // ada airline_tags, berarti harus di cek apakah ada di table soundbank // kalau tidak ada airline_tags, tidak perlu cek val atags = _airline_tags.split(";").map { it.trim() } .filter { it.isNotEmpty() }.distinct() - val airlinetags = db.Get_AirlineCode_Tags() + val airlinetags = db.soundDB.Get_AirlineCode_Tags() val missing_airlinetags = ArrayList() atags.forEach { tag -> - if (!airlinetags.any { it.equals(tag,true) }) { + if (!airlinetags.any { it.equals(tag, true) }) { missing_airlinetags.add(tag) } } - if (missing_airlinetags.isNotEmpty()){ + if (missing_airlinetags.isNotEmpty()) { ctx.status(400) - .result(objectmapper.writeValueAsString(resultMessage("Airline tags not found in soundbank: ${missing_airlinetags.joinToString(", ")}"))) + .result( + objectmapper.writeValueAsString( + resultMessage( + "Airline tags not found in soundbank: ${ + missing_airlinetags.joinToString( + ", " + ) + }" + ) + ) + ) return@patch } } - if (ValidString(_city_tags)){ + if (ValidString(_city_tags)) { // ada city_tags, berarti harus di cek apakah ada di table soundbank // kalau tidak ada city_tags, tidak perlu cek val ctags = _city_tags.split(";").map { it.trim() } .filter { it.isNotEmpty() }.distinct() - val citytags = db.Get_City_Tags() + val citytags = db.soundDB.Get_City_Tags() val missing_citytags = ArrayList() ctags.forEach { tag -> - if (!citytags.any { it.equals(tag,true) }) { + if (!citytags.any { it.equals(tag, true) }) { missing_citytags.add(tag) } } - if (missing_citytags.isNotEmpty()){ ctx.status(400) - .result(objectmapper.writeValueAsString(resultMessage("City tags not found in soundbank: ${missing_citytags.joinToString(", ")}"))) + if (missing_citytags.isNotEmpty()) { + ctx.status(400) + .result( + objectmapper.writeValueAsString( + resultMessage( + "City tags not found in soundbank: ${ + missing_citytags.joinToString( + ", " + ) + }" + ) + ) + ) return@patch } } - if (ValidString(_messagebank_ann_id)){ + if (ValidString(_messagebank_ann_id)) { // ada messagebank_ann_id, berarti harus di cek apakah ada di table messagebank // kalau tidak ada messagebank_ann_id, tidak perlu cek val mbids = _messagebank_ann_id.split(";") .map { it.trim() } .filter { it.isNotEmpty() }.distinct() .mapNotNull { it.toUIntOrNull() } - val mbankids = db.Get_MessageID_List() + val mbankids = db.messageDB.Get_MessageID_List() val missing_broadcastids = ArrayList() mbids.forEach { mbid -> if (!mbankids.any { it == mbid }) { missing_broadcastids.add(mbid) } } - if (missing_broadcastids.isNotEmpty()){ + if (missing_broadcastids.isNotEmpty()) { ctx.status(400) - .result(objectmapper.writeValueAsString(resultMessage("ANN_ID not found in Messagebank: ${missing_broadcastids.joinToString(", ")}"))) + .result( + objectmapper.writeValueAsString( + resultMessage( + "ANN_ID not found in Messagebank: ${ + missing_broadcastids.joinToString( + ", " + ) + }" + ) + ) + ) return@patch } @@ -1625,16 +1722,26 @@ class WebApp(val listenPort: Int, var userlist: List>, val val bzdesc = _broadcastzones.split(";").map { it.trim() } .filter { it.isNotEmpty() }.distinct() - val bzlist = db.Get_BroadcastZone_List() + val bzlist = db.broadcastDB.Get_BroadcastZone_List() val missing_broadcastzones = ArrayList() bzdesc.forEach { bz -> - if (!bzlist.any { it.equals(bz,true) }) { + if (!bzlist.any { it.equals(bz, true) }) { missing_broadcastzones.add(bz) } } - if (missing_broadcastzones.isNotEmpty()){ + if (missing_broadcastzones.isNotEmpty()) { ctx.status(400) - .result(objectmapper.writeValueAsString(resultMessage("Broadcast zone tags not found in soundbank: ${missing_broadcastzones.joinToString(", ")}"))) + .result( + objectmapper.writeValueAsString( + resultMessage( + "Broadcast zone tags not found in soundbank: ${ + missing_broadcastzones.joinToString( + ", " + ) + }" + ) + ) + ) return@patch } @@ -1741,9 +1848,9 @@ class WebApp(val listenPort: Int, var userlist: List>, val val logfilter = get1.queryParam("filter") ?: "" if (ValiDateForLogHtml(logdate)) { val xlsxdata = if (ValidString(logfilter)) { - db.Export_Log_XLSX(logdate.replace('-', '/'), logfilter) + db.logDB.Export_Log_XLSX(logdate.replace('-', '/'), logfilter) } else { - db.Export_Log_XLSX(logdate.replace('-', '/'), "") + db.logDB.Export_Log_XLSX(logdate.replace('-', '/'), "") } if (xlsxdata != null) { get1.header( @@ -1766,8 +1873,7 @@ class WebApp(val listenPort: Int, var userlist: List>, val get("List") { ctx -> db.broadcastDB.Get({ ctx.result(MariaDB.ArrayListtoString(db.broadcastDB.List)) - },{ - msgFail -> + }, { msgFail -> ctx.status(500).result(objectmapper.writeValueAsString(resultMessage(msgFail))) }) } @@ -1924,14 +2030,13 @@ class WebApp(val listenPort: Int, var userlist: List>, val get("List") { ctx -> db.soundchannelDB.Get({ ctx.result(MariaDB.ArrayListtoString(db.soundchannelDB.List)) - },{ - msgFail -> + }, { msgFail -> ctx.status(500).result(objectmapper.writeValueAsString(resultMessage(msgFail))) }) } get("SoundChannelDescriptions") { - it.result(objectmapper.writeValueAsString(db.Get_SoundChannel_List())) + it.result(objectmapper.writeValueAsString(db.soundchannelDB.Get_SoundChannel_List())) } delete("List") { // truncate sound channel table @@ -2060,8 +2165,7 @@ class WebApp(val listenPort: Int, var userlist: List>, val get("List") { ctx -> db.queuepagingDB.Get({ ctx.result(MariaDB.ArrayListtoString(db.queuepagingDB.List)) - },{ - msgFail -> + }, { msgFail -> ctx.status(500).result(objectmapper.writeValueAsString(resultMessage(msgFail))) }) @@ -2099,8 +2203,7 @@ class WebApp(val listenPort: Int, var userlist: List>, val get("List") { ctx -> db.queuetableDB.Get({ ctx.result(MariaDB.ArrayListtoString(db.queuetableDB.List)) - },{ - msgFail -> + }, { msgFail -> ctx.status(500).result(objectmapper.writeValueAsString(resultMessage(msgFail))) }) @@ -2424,7 +2527,7 @@ class WebApp(val listenPort: Int, var userlist: List>, val } } } - path("WebAccess"){ + path("WebAccess") { get { val value = object { val adminpass = _config.Get(configKeys.WEBAPP_ADMIN_PASSWORD.key) @@ -2432,19 +2535,25 @@ class WebApp(val listenPort: Int, var userlist: List>, val } it.result(objectmapper.writeValueAsString(value)) } - post{ - val json : JsonNode = objectmapper.readTree(it.body()) + post { + val json: JsonNode = objectmapper.readTree(it.body()) val adminpass = json.get("adminpass").asText("") val viewerpass = json.get("viewerpass").asText("") - if(ValidString(adminpass) && ValidString(viewerpass)){ + if (ValidString(adminpass) && ValidString(viewerpass)) { _config.Set(configKeys.WEBAPP_ADMIN_PASSWORD.key, adminpass) _config.Set(configKeys.WEBAPP_VIEWER_PASSWORD.key, viewerpass) _config.Save() Logger.info { "Changed Web Access Passwords" } // update userlist userlist = listOf( - 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)) + 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) + ) ) it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else { @@ -2469,7 +2578,7 @@ class WebApp(val listenPort: Int, var userlist: List>, val config.staticFiles.add("/semiauto") config.jsonMapper(JavalinJackson(jacksonObjectMapper())) config.router.apiBuilder { - before{ + before { //Logger.info{"${it.ip()} method ${it.method()} connect to ${it.path()}"} } path("/") { @@ -2494,7 +2603,12 @@ class WebApp(val listenPort: Int, var userlist: List>, val it.cookie("semiauto-user", user.username) it.redirect("index.html") - db.logSemiAuto.Add(LogSemiauto.NewLog("SEMIAUTOWEB", "User logged in: ${user.username} from ip ${it.ip()}")) + db.logSemiAuto.Add( + LogSemiauto.NewLog( + "SEMIAUTOWEB", + "User logged in: ${user.username} from ip ${it.ip()}" + ) + ) } else ResultMessageString(it, 400, "Invalid username or password") } else ResultMessageString(it, 400, "Username or password cannot be empty") } @@ -2551,8 +2665,8 @@ class WebApp(val listenPort: Int, var userlist: List>, val // sequences db.soundDB.List.filter { it.Category == Category.Sequence.name } .distinctBy { it.TAG }.forEach { xx -> - result.sequences.add("${xx.TAG};${xx.Description}") - } + result.sequences.add("${xx.TAG};${xx.Description}") + } // reasons db.soundDB.List.filter { it.Category == Category.Reason.name }.distinctBy { it.TAG } .forEach { xx -> @@ -2561,8 +2675,8 @@ class WebApp(val listenPort: Int, var userlist: List>, val // procedures db.soundDB.List.filter { it.Category == Category.Procedure.name } .distinctBy { it.TAG }.forEach { xx -> - result.procedures.add("${xx.TAG};${xx.Description}") - } + result.procedures.add("${xx.TAG};${xx.Description}") + } // gates db.soundDB.List.filter { it.Category == Category.Gate.name }.distinctBy { it.TAG } .forEach { xx -> @@ -2571,16 +2685,21 @@ class WebApp(val listenPort: Int, var userlist: List>, val // compensation db.soundDB.List.filter { it.Category == Category.Compensation.name } .distinctBy { it.TAG }.forEach { xx -> - result.compensation.add("${xx.TAG};${xx.Description}") - } + result.compensation.add("${xx.TAG};${xx.Description}") + } // greetings db.soundDB.List.filter { it.Category == Category.Greeting.name } .distinctBy { it.TAG }.forEach { xx -> - result.greetings.add("${xx.TAG};${xx.Description}") - } + result.greetings.add("${xx.TAG};${xx.Description}") + } ctx.result(objectmapper.writeValueAsString(result)) - db.logSemiAuto.Add(LogSemiauto.NewLog("SEMIAUTOWEB", "Initialized Web variables for user: ${user.username}")) + db.logSemiAuto.Add( + LogSemiauto.NewLog( + "SEMIAUTOWEB", + "Initialized Web variables for user: ${user.username}" + ) + ) } else ResultMessageString(ctx, 400, "User not found") } else ResultMessageString(ctx, 400, "Username is empty") } @@ -2612,7 +2731,12 @@ class WebApp(val listenPort: Int, var userlist: List>, val db.queuetableDB.Resort() Logger.info { "SemiAutoWeb added to queue table: $qt" } ctx.result(objectmapper.writeValueAsString(resultMessage("OK"))) - db.logSemiAuto.Add(LogSemiauto.NewLog("SEMIAUTOWEB", "Added to queue table: $qt")) + db.logSemiAuto.Add( + LogSemiauto.NewLog( + "SEMIAUTOWEB", + "Added to queue table: $qt" + ) + ) } else ResultMessageString(ctx, 500, "Failed to add to queue table") } else ResultMessageString(ctx, 400, "Broadcast zones cannot be empty") } else ResultMessageString(ctx, 400, "Tags cannot be empty") @@ -2626,9 +2750,9 @@ class WebApp(val listenPort: Int, var userlist: List>, val val logfilter = get1.queryParam("filter") ?: "" if (ValiDateForLogHtml(logdate)) { val xlsxdata = if (ValidString(logfilter)) { - db.Export_Log_XLSX(logdate.replace('-', '/'), logfilter) + db.logDB.Export_Log_XLSX(logdate.replace('-', '/'), logfilter) } else { - db.Export_Log_XLSX(logdate.replace('-', '/'), "") + db.logDB.Export_Log_XLSX(logdate.replace('-', '/'), "") } if (xlsxdata != null) { get1.header( @@ -2652,7 +2776,7 @@ class WebApp(val listenPort: Int, var userlist: List>, val println("Request log for date: $datelog") db.logSemiAuto.GetLogSemiAutoForHtml(datelog, null, { ctx.result(MariaDB.ArrayListtoString(it)) - },{ err -> + }, { err -> ctx.status(500).result(objectmapper.writeValueAsString(resultMessage(err))) }) @@ -2678,7 +2802,7 @@ class WebApp(val listenPort: Int, var userlist: List>, val } } - fun CheckUsers(ctx: Context) : String? { + fun CheckUsers(ctx: Context): String? { //val user = ctx.sessionAttribute("user") val user = ctx.cookie("aas-user") if (user == null) { @@ -2693,12 +2817,11 @@ class WebApp(val listenPort: Int, var userlist: List>, val return foundUser.first } - fun GetAdminUserFromConfig() : String{ + fun GetAdminUserFromConfig(): String { return config.Get(configKeys.WEBAPP_ADMIN_USERNAME.key) } - fun Get_Barix_Connection_by_ZoneName(zonename: String): BarixConnection? { if (ValidString(zonename)) { val bz = db.broadcastDB.List.find { it.description == zonename }