package database import codes.Somecodes.Companion.ValiDateForLogHtml import codes.Somecodes.Companion.toJsonString 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 kotlin.math.max /** * A class to manage a connection to a MariaDB database. * * @property address The address of the MariaDB server. * @property port The port number of the MariaDB server. * @property dbName The name of the database to connect to. * @property username The username for the database connection. * @property password The password for the database connection. */ @Suppress("unused", "SqlSourceToSinkFlow") class MariaDB( address: String = "localhost", port: Int = 3306, dbName: String = "aas", username: String = "admin", password: String = "admin" ) { 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 logDB: dbFunctions lateinit var userDB: dbFunctions companion object { fun ValidDate(date: String): Boolean { // Check if the date is in the format DD/MM/YYYY val regex = Regex("""^\d{2}/\d{2}/\d{4}$""") return regex.matches(date) } fun ValidTime(time: String): Boolean { // Check if the time is in the format HH:MM:SS val regex = Regex("""^\d{2}:\d{2}:\d{2}$""") return regex.matches(time) } /** * Convert SoundbankList, MessagebankList, LanguageLinkList, or SchedulebankList to a JSON String. * @param list The ArrayList to convert to a String. * @return A JSON String representation of the ArrayList. */ fun ArrayListtoString(list: ArrayList): String { return try { toJsonString(list) } catch (e: Exception) { Logger.error("Error converting list to JSON: ${e.message}" as Any) "[]" } } } init { try { connection = DriverManager.getConnection( "jdbc:mysql://$address:$port/$dbName?sslMode=REQUIRED", username, password ) as Connection Logger.info("Connected to MySQL" as Any) connected = true soundDB = object : dbFunctions("soundbank", connection) { 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() { List.clear() try { val statement = connection.createStatement() val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") 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) } } catch (e: Exception) { Logger.error("Error fetching soundbanks: ${e.message}" as Any) } } 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) { 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() // use a temporary table to reorder the index statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}") statement?.executeUpdate("INSERT INTO temp_${super.dbName} (Description, TAG, Category, Language, VoiceType, Path) SELECT Description, TAG, Category, Language, VoiceType, Path FROM ${super.dbName} ORDER BY Description ") 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 temp_${super.dbName}") statement?.executeUpdate("DROP TABLE temp_${super.dbName}") 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) { 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() { 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) } } catch (e: Exception) { Logger.error("Error fetching ${super.dbName} : ${e.message}" as Any) } } 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() // use a temporary table to reorder the index statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}") statement?.executeUpdate("INSERT INTO temp_${super.dbName} (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 temp_${super.dbName}") statement?.executeUpdate("DROP TABLE temp_${super.dbName}") 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) { 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() { 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) } } catch (e: Exception) { Logger.error("Error fetching ${super.dbName} : ${e.message}" as Any) } } 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) { 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() // use a temporary table to reorder the index statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}") statement?.executeUpdate("INSERT INTO temp_${super.dbName} (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 temp_${super.dbName}") statement?.executeUpdate("DROP TABLE temp_${super.dbName}") 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("languagelinking") 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) { 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() { 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) } } catch (e: Exception) { Logger.error("Error fetching ${super.dbName}: ${e.message}" as Any) } } override fun Add(data: ScheduleBank): Boolean { if (!ValidDate(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 (!ValidDate(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 (!ValidDate(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() // use a temporary table to reorder the index statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}") statement?.executeUpdate("INSERT INTO temp_${super.dbName} (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 temp_${super.dbName}") statement?.executeUpdate("DROP TABLE temp_${super.dbName}") 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() for (rowIndex in 1..sheet.lastRowNum) { val row = sheet.getRow(rowIndex) ?: continue val description = row.getCell(1)?.stringCellValue ?: continue val day = row.getCell(2)?.stringCellValue ?: continue val time = row.getCell(3)?.stringCellValue ?: continue val soundpath = row.getCell(4)?.stringCellValue ?: continue val repeat = row.getCell(5)?.stringCellValue?.toUByteOrNull() ?: continue val enable = row.getCell(6)?.stringCellValue?.toBooleanStrictOrNull() ?: continue val broadcastZones = row.getCell(7)?.stringCellValue ?: continue val language = row.getCell(8)?.stringCellValue ?: continue val schedulebank = ScheduleBank( 0u, description, day, time, soundpath, repeat, enable, broadcastZones, language ) _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) { 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() { 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) } } catch (e: Exception) { Logger.error("Error fetching ${super.dbName} : ${e.message}" as Any) } } override fun Add(data: BroadcastZones): Boolean { try { val statement = connection.prepareStatement("INSERT INTO ${super.dbName} (description, SoundChannel, Box, Relay) 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, Box, Relay) 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 = ?, Box = ?, Relay = ? 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() // use a temporary table to reorder the index statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}") statement?.executeUpdate("INSERT INTO temp_${super.dbName} (description, SoundChannel, Box, Relay) SELECT description, SoundChannel, Box, Relay FROM ${super.dbName} ORDER BY description ") statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") statement?.executeUpdate("INSERT INTO ${super.dbName} (description, SoundChannel, Box, Relay) SELECT description, SoundChannel, Box, Relay FROM temp_${super.dbName}") statement?.executeUpdate("DROP TABLE temp_${super.dbName}") 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", "Box", "Relay") 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 box = row.getCell(3)?.stringCellValue ?: continue val relay = row.getCell(4)?.stringCellValue ?: continue val broadcastZone = BroadcastZones(0u, description, soundChannel, box, relay) _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", "Box", "Relay") 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("Box")) row.createCell(4).setCellValue(resultSet.getString("Relay")) } 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) { 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(45) NOT NULL" + // Language of the message ")" super.Create(tabledefinition) } override fun Get() { 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) } } catch (e: Exception) { Logger.error("Error fetching ${super.dbName} : ${e.message}" as Any) } } 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: ${data.Message}" 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() // use a temporary table to reorder the index statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}") statement?.executeUpdate("INSERT INTO temp_${super.dbName} (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 temp_${super.dbName}") statement?.executeUpdate("DROP TABLE temp_${super.dbName}") 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) { 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(512) NOT NULL," + // Message content "BroadcastZones VARCHAR(1024)" + // Comma-separated soundbank tags ")" super.Create(tabledefinition) } override fun Get() { 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("SB_TAGS"), ) queueList.add(queuePaging) List.add(queuePaging) } } catch (e: Exception) { Logger.error("Error fetching ${super.dbName} : ${e.message}" as Any) } } 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() // use a temporary table to reorder the index statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}") statement?.executeUpdate("INSERT INTO temp_${super.dbName} (Date_Time, Source, Type, Message, SB_TAGS) SELECT Date_Time, Source, Type, Message, SB_TAGS 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) SELECT Date_Time, Source, Type, Message, SB_TAGS FROM temp_${super.dbName}") statement?.executeUpdate("DROP TABLE temp_${super.dbName}") 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", "SB_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("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")) } 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) { 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() { 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) } } catch (e: Exception) { Logger.error("Error fetching sound channels: ${e.message}" as Any) } } 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 SoundChannel 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() // use a temporary table to reorder the index statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}") statement?.executeUpdate("INSERT INTO temp_${super.dbName} (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 temp_${super.dbName}") statement?.executeUpdate("DROP TABLE temp_${super.dbName}") 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) 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 } } logDB = object : dbFunctions("logs", connection) { override fun Create() { val tabledefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" + "`index` INT AUTO_INCREMENT PRIMARY KEY," + "datenya VARCHAR(20) NOT NULL," + // format DD/MM/YYYY "timenya VARCHAR(20) NOT NULL," + // format HH:MM:SS "machine VARCHAR(45) NOT NULL," + "description TEXT NOT NULL" + ")" super.Create(tabledefinition) } override fun Get() { List.clear() try { val statement = connection.createStatement() val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") while (resultSet?.next() == true) { val log = Log( resultSet.getLong("index").toULong(), resultSet.getString("datenya"), resultSet.getString("timenya"), resultSet.getString("machine"), resultSet.getString("description") ) List.add(log) } } catch (e: Exception) { Logger.error("Error fetching ${super.dbName}: ${e.message}" as Any) } } override fun Add(data: Log): Boolean { try { val statement = connection.prepareStatement("INSERT INTO logs (datenya, timenya, machine, description) VALUES (?, ?, ?, ?)") statement?.setString(1, data.datenya) statement?.setString(2, data.timenya) statement?.setString(3, data.machine) statement?.setString(4, data.description) val rowsAffected = statement?.executeUpdate() if (rowsAffected != null && rowsAffected > 0) { Logger.info{"Log added : $data"} return true } else { Logger.warn{"Failed to add log entry : $data"} } } catch (e: Exception) { Logger.error{"Error adding log entry: ${e.message}"} } return false } override fun AddAll(data: ArrayList): Boolean { return try { connection.autoCommit = false val sql = "INSERT INTO logs (datenya, timenya, machine, description) VALUES (?, ?, ?, ?)" val statement = connection.prepareStatement(sql) for (log in data) { statement.setString(1, log.datenya) statement.setString(2, log.timenya) statement.setString(3, log.machine) statement.setString(4, log.description) statement.addBatch() } statement.executeBatch() connection.commit() Logger.info("Bulk log insert successful: ${data.size} entries" as Any) connection.autoCommit = true true } catch (e: Exception) { Logger.error("Error adding log entries: ${e.message}" as Any) false } } override fun UpdateByIndex(index: Int, data: Log): Boolean { throw Exception("Update not supported") } override fun Resort(): Boolean { try { val statement = connection.createStatement() // use a temporary table to reorder the index statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}") statement?.executeUpdate( "INSERT INTO temp_${super.dbName} (datenya, timenya, machine, description) " + "SELECT datenya, timenya, machine, description FROM ${super.dbName} " + "ORDER BY datenya , timenya , machine " ) statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") statement?.executeUpdate( "INSERT INTO ${super.dbName} (datenya, timenya, machine, description) " + "SELECT datenya, timenya, machine, description FROM temp_${super.dbName}" ) statement?.executeUpdate("DROP TABLE temp_${super.dbName}") Logger.info("${super.dbName} table resorted by datenya, timenya, machine" as Any) // reload the local list Get() return true } catch (e: Exception) { Logger.error("Error resorting ${super.dbName} table by datenya, timenya, machine: ${e.message}" as Any) } return false } override fun Import_XLSX(workbook: XSSFWorkbook): Boolean { throw Exception("Importing Logs 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("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 } } userDB = object : dbFunctions("newuser", connection){ 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," + "soundbank_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() { 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("soundbank_tags"), resultSet.getString("messagebank_ann_id"), resultSet.getString("broadcastzones") ) List.add(user) } } catch (e: Exception) { Logger.error("Error fetching users: ${e.message}" as Any) } } override fun Add(data: UserDB): Boolean { try { val statement = connection.prepareStatement("INSERT INTO ${super.dbName} (username, password, location, soundbank_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.soundbank_tags) statement?.setString(5, data.messagebank_ann_id) statement?.setString(6, 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,soundbank_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.soundbank_tags) statement.setString(5, user.messagebank_ann_id) statement.setString(6, 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 = ?, soundbank_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.soundbank_tags) statement?.setString(5, data.messagebank_ann_id) statement?.setString(6, data.broadcastzones) statement?.setLong(7, 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() // use a temporary table to reorder the index statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_${super.dbName} LIKE ${super.dbName}") statement?.executeUpdate("INSERT INTO temp_${super.dbName} (username, password, location, soundbank_tags, messagebank_ann_id, broadcastzones) SELECT username, password, location, soundbank_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, soundbank_tags, messagebank_ann_id, broadcastzones) SELECT username, password, location, soundbank_tags, messagebank_ann_id, broadcastzones FROM temp_${super.dbName}") statement?.executeUpdate("DROP TABLE temp_${super.dbName}") 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", "soundbank_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 soundbank_tags = row.getCell(4)?.stringCellValue ?: continue val messagebank_ann_id = row.getCell(5)?.stringCellValue ?: continue val broadcastzones = row.getCell(6)?.stringCellValue ?: continue val user = UserDB(0u, username, password, location, soundbank_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", "soundbank_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("soundbank_tags")) row.createCell(5).setCellValue(resultSet.getString("messagebank_ann_id")) row.createCell(6).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 } } runBlocking { withContext(Dispatchers.IO) { logDB.Create() soundchannelDB.Create() scheduleDB.Create() broadcastDB.Create() messageDB.Create() soundDB.Create() languageDB.Create() queuetableDB.Create() queuepagingDB.Create() userDB.Create() messageDB.Get() soundDB.Get() languageDB.Get() scheduleDB.Get() broadcastDB.Get() soundchannelDB.Get() userDB.Get() } } Logger.info { "Loading MariaDB completed" } Logger.info { "Soundbank count: ${soundDB.List.size}" } Logger.info { "Messagebank count: ${messageDB.List.size}" } Logger.info { "LanguageLink count: ${languageDB.List.size}" } Logger.info { "Schedulebank count: ${scheduleDB.List.size}" } Logger.info { "BroadcastZones count: ${broadcastDB.List.size}" } Logger.info { "SoundChannel count: ${soundchannelDB.List.size}" } Logger.info { "User count: ${userDB.List.size}" } } catch (e: Exception) { Logger.error("Failed to connect to MariaDB: ${e.message}" as Any) } } /** * Closes the MariaDB connection. */ fun close() { try { connection.close() Logger.info("Connection to MariaDB closed" as Any) } catch (e: Exception) { Logger.error("Error closing MariaDB connection: ${e.message}" as Any) } connected = false } /** * Adds a new log entry to the database with the current date and time. * @param machine The machine name or identifier. * @param description The log description. * @return true if the log was added successfully, false otherwise. */ fun Add_Log(machine: String, description: String): Boolean { val current = java.time.LocalDateTime.now() val date = current.toLocalDate().toString() // format YYYY-MM-DD val time = current.toLocalTime().withNano(0).toString() // format HH:MM:SS val datenya = date.split("-").reversed().joinToString("/") // convert to DD/MM/YYYY val log = Log(0u, datenya, time, machine, description) return logDB.Add(log) } /** * Get All Log from database * @param consumer A Consumer that will receive the list of logs */ fun GetLog(consumer: Consumer>) { val logList = ArrayList() try { val statement = connection.createStatement() val resultSet = statement?.executeQuery("SELECT * FROM logs") while (resultSet?.next() == true) { val log = Log( resultSet.getLong("index").toULong(), resultSet.getString("datenya"), resultSet.getString("timenya"), resultSet.getString("machine"), resultSet.getString("description") ) logList.add(log) } } catch (e: Exception) { Logger.error("Error fetching logs table: ${e.message}" as Any) } consumer.accept(logList) } /** * Get Log from database by date for HTML usage * @param date The date to filter logs by (format: DD-MM-YYYY) * @param consumer A Consumer that will receive the list of logs for the specified date */ fun GetLogForHtml(date: String, consumer: Consumer>) { val logList = ArrayList() //println("GetLogForHtml Date: $date" ) if (ValiDateForLogHtml(date)) { try { // must convert from DD-MM-YYYY to DD/MM/YYYY, because in database we use DD/MM/YYYY val adjusteddate = date.replace("-", "/") val statement = connection.prepareStatement("SELECT * FROM logs WHERE datenya = ?") statement?.setString(1, adjusteddate) //println("GetLogForHtml Date: $adjusteddate" ) // println("GetLogForHtml SQL: " +statement?.toString()) val resultSet = statement?.executeQuery() while (resultSet?.next() == true) { val log = Log( resultSet.getLong("index").toULong(), resultSet.getString("datenya"), resultSet.getString("timenya"), resultSet.getString("machine"), resultSet.getString("description") ) logList.add(log) } } catch (e: Exception) { Logger.error("Error fetching logs table for date $date: ${e.message}" as Any) } } consumer.accept(logList) } /** * Get Log from database by date and filter for HTML usage * @param date The date to filter logs by (format: DD-MM-YYYY) * @param filter The filter string to search in description or machine * @param consumer A Consumer that will receive the list of logs for the specified date and filter */ fun GetLogForHtml(date: String, filter: String, consumer: Consumer>) { val logList = ArrayList() //println("GetLogForHtml Date: $date Filter: $filter" ) if (ValiDateForLogHtml(date)) { try { // must convert from DD-MM-YYYY to DD/MM/YYYY, because in database we use DD/MM/YYYY val adjusteddate = date.replace("-", "/") val statement = connection.prepareStatement("SELECT * FROM logs WHERE datenya = ? AND description LIKE ?") statement?.setString(1, adjusteddate) statement?.setString(2, "%$filter%") //println("GetLogForHtml Date: $adjusteddate , Filter=$filter" ) //println("GetLogForHtml SQL: " +statement?.toString()) val resultSet = statement?.executeQuery() while (resultSet?.next() == true) { val log = Log( resultSet.getLong("index").toULong(), resultSet.getString("datenya"), resultSet.getString("timenya"), resultSet.getString("machine"), resultSet.getString("description") ) logList.add(log) } } catch (e: Exception) { Logger.error("Error fetching logs for date $date with filter $filter: ${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{ val lowerTag = tag.lowercase() return soundDB.List .filter{ it.Category== Category.City.name } .filter { it.TAG.lowercase()==lowerTag} } fun Find_Soundbank_AirplaneName(tag: String) : List{ val lowerTag = tag.lowercase() return soundDB.List .filter{ it.Category== Category.Airplane_Name.name } .filter { it.TAG.lowercase()==lowerTag} } fun Find_Soundbank_Places(tag: String) : List{ val lowerTag = tag.lowercase() return soundDB.List .filter{ it.Category== Category.Places.name } .filter { it.TAG.lowercase()==lowerTag} } 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{ val lowerTag = tag.lowercase() return soundDB.List .filter{ it.Category== Category.Sequence.name } .filter { it.TAG.lowercase()==lowerTag} } fun Find_Soundbank_Reason(tag: String) : List{ val lowerTag = tag.lowercase() return soundDB.List .filter{ it.Category== Category.Reason.name } .filter { it.TAG.lowercase()==lowerTag} } fun Find_Soundbank_Procedure(tag: String) : List { val lowerTag = tag.lowercase() return soundDB.List .filter { it.Category == Category.Procedure.name } .filter { it.TAG.lowercase() == lowerTag } } }