commit 18/09/2025

This commit is contained in:
2025-09-18 15:57:11 +07:00
parent 09d074aa03
commit 120c8e5276
19 changed files with 2696 additions and 1591 deletions

View File

@@ -35,6 +35,7 @@ class MariaDB(
var LanguageLinkList: ArrayList<LanguageLink> = ArrayList()
var SchedulebankList: ArrayList<ScheduleBank> = ArrayList()
var BroadcastZoneList: ArrayList<BroadcastZones> = ArrayList()
var SoundChannelList: ArrayList<SoundChannel> = ArrayList()
companion object {
fun ValidDate(date: String): Boolean {
@@ -70,7 +71,11 @@ class MariaDB(
init {
try {
connection =
DriverManager.getConnection("jdbc:mysql://$address:$port/$dbName?sslMode=REQUIRED", username, password) as Connection
DriverManager.getConnection(
"jdbc:mysql://$address:$port/$dbName?sslMode=REQUIRED",
username,
password
) as Connection
Logger.info("Connected to MariaDB" as Any)
connected = true
@@ -81,6 +86,7 @@ class MariaDB(
Reload_LanguageLink()
Reload_Schedulebank()
GetBroadcastZones()
GetSoundChannel()
}
}
@@ -92,6 +98,7 @@ class MariaDB(
Logger.info { "LanguageLink count: ${LanguageLinkList.size}" }
Logger.info { "Schedulebank count: ${SchedulebankList.size}" }
Logger.info { "BroadcastZones count: ${BroadcastZoneList.size}" }
Logger.info { "SoundChannel count: ${SoundChannelList.size}" }
} catch (e: Exception) {
@@ -190,6 +197,218 @@ class MariaDB(
return null
}
/**
* Clears all entries from the soundchannel table in the database and the local list.
*/
fun Clear_SoundChannel(): Boolean {
try {
val statement = connection?.createStatement()
// use TRUNCATE to reset auto increment index
statement?.executeUpdate("TRUNCATE TABLE soundchannel")
Logger.info("SoundChannel table cleared" as Any)
SoundChannelList.clear()
// create new rows from 1 to 64 with description "Channel 01" to "Channel 64" and empty ip
for (i in 1..64) {
val channel = String.format("Channel %02d", i)
val insertStatement =
connection?.prepareStatement("INSERT INTO soundchannel (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
}
fun GetSoundChannel() {
SoundChannelList.clear()
try {
val statement = connection?.createStatement()
val resultSet = statement?.executeQuery("SELECT * FROM soundchannel ORDER BY `index` ASC")
while (resultSet?.next() == true) {
val channel = SoundChannel(
resultSet.getLong("index").toUInt(),
resultSet.getString("channel"),
resultSet.getString("ip")
)
SoundChannelList.add(channel)
}
} catch (e: Exception) {
Logger.error("Error fetching sound channels: ${e.message}" as Any)
}
}
/**
* Update SoundChannel IP in database
* @param channel The SoundChannel object containing the channel name and new IP address.
* @return True if the update was successful, false otherwise.
*/
fun Add_SoundChannel(channel: SoundChannel): Boolean {
try {
val statement = connection?.prepareStatement("UPDATE soundchannel SET ip = ? WHERE channel = ?")
statement?.setString(1, channel.ip)
statement?.setString(2, channel.channel)
val rowsAffected = statement?.executeUpdate()
if (rowsAffected != null && rowsAffected > 0) {
Logger.info("SoundChannel updated: ${channel.channel} -> ${channel.ip}" as Any)
return true
} else {
Logger.warn("No SoundChannel entry updated for: ${channel.channel} -> ${channel.ip}" as Any)
}
} catch (e: Exception) {
Logger.error("Error updating SoundChannel entry: ${e.message}" as Any)
}
return false
}
/**
* Delete SoundChannel IP by index in database (set to empty string)
* @param index The index of the SoundChannel entry to delete.
* @return True if the deletion was successful, false otherwise.
*/
fun Delete_SoundChannel_by_index(index: UInt): Boolean {
try {
val statement = connection?.prepareStatement("UPDATE soundchannel SET ip = '' WHERE `index` = ?")
statement?.setLong(1, index.toLong())
val rowsAffected = statement?.executeUpdate()
if (rowsAffected != null && rowsAffected > 0) {
Logger.info("SoundChannel IP cleared for index $index" as Any)
return true
} else {
Logger.warn("No SoundChannel entry cleared for index $index" as Any)
}
} catch (e: Exception) {
Logger.error("Error clearing SoundChannel entry for index $index: ${e.message}" as Any)
}
return false
}
/**
* Resort the soundchannel table, in order to reorder the index after deletions.
* @return True if the resorting was successful, false otherwise.
*/
fun Resort_SoundChannel_by_index(): Boolean {
try {
val statement = connection?.createStatement()
// use a temporary table to reorder the index
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_soundchannel LIKE soundchannel")
statement?.executeUpdate("INSERT INTO temp_soundchannel (channel, ip) SELECT channel, ip FROM soundchannel ORDER BY `index` ASC")
statement?.executeUpdate("TRUNCATE TABLE soundchannel")
statement?.executeUpdate("INSERT INTO soundchannel (channel, ip) SELECT channel, ip FROM temp_soundchannel")
statement?.executeUpdate("DROP TABLE temp_soundchannel")
Logger.info("soundchannel table resorted by index" as Any)
// reload the local list
GetSoundChannel()
return true
} catch (e: Exception) {
Logger.error("Error resorting soundchannel table by index: ${e.message}" as Any)
}
return false
}
/**
* Updates an existing SoundChannel entry in the database by its index.
* @param index The index of the SoundChannel entry to update.
* @param sc The SoundChannel object with updated values.
* @return True if the update was successful, false otherwise.
*/
fun Update_SoundChannel_by_index(index: UInt, sc: SoundChannel): Boolean {
try {
val statement =
connection?.prepareStatement("UPDATE soundchannel SET channel = ?, ip = ? WHERE `index` = ?")
statement?.setString(1, sc.channel)
statement?.setString(2, sc.ip)
statement?.setLong(3, index.toLong())
val rowsAffected = statement?.executeUpdate()
if (rowsAffected != null && rowsAffected > 0) {
Logger.info("SoundChannel updated at index $index: ${sc.channel} -> ${sc.ip}" as Any)
return true
} else {
Logger.warn("No SoundChannel entry updated at index $index for: ${sc.channel} -> ${sc.ip}" as Any)
}
} catch (e: Exception) {
Logger.error("Error updating SoundChannel entry at index $index: ${e.message}" as Any)
}
return false
}
/**
* Exports the soundchannel table to an XLSX workbook.
* @return An XSSFWorkbook containing the soundchannel data, or null if an error occurred.
*/
fun Export_SoundChannel_XLSX(): XSSFWorkbook? {
try {
val statement = connection?.createStatement()
val resultSet = statement?.executeQuery("SELECT * FROM soundchannel")
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
}
/**
* Imports soundchannel entries from an XLSX workbook.
* @param workbook The XSSFWorkbook containing the soundchannel data.
* @return True if the import was successful, false otherwise.
*/
fun Import_SoundChannel_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_SoundChannel()
// read each row and insert into database
val _soundChannelList = ArrayList<SoundChannel>()
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_SoundChannel(sc)) {
success = false
}
}
return success
} catch (e: Exception) {
Logger.error { "Error importing SoundChannel, Msg: ${e.message}" }
}
return false
}
/**
* Get All Log from database
* @param consumer A Consumer that will receive the list of logs
@@ -230,7 +449,7 @@ class MariaDB(
val statement = connection?.prepareStatement("SELECT * FROM logs WHERE datenya = ?")
statement?.setString(1, adjusteddate)
//println("GetLogForHtml Date: $adjusteddate" )
// println("GetLogForHtml SQL: " +statement?.toString())
// println("GetLogForHtml SQL: " +statement?.toString())
val resultSet = statement?.executeQuery()
while (resultSet?.next() == true) {
val log = Log(
@@ -458,27 +677,27 @@ class MariaDB(
}
/**
* Resort the schedulebank table, in order to reorder the index after deletions, and sort ascending by Day and Time.
* @return True if the resorting was successful, false otherwise.
*/
fun Resort_Schedulebank_by_Day_Time(): Boolean {
try {
val statement = connection?.createStatement()
// use a temporary table to reorder the index
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_schedulebank LIKE schedulebank")
statement?.executeUpdate("INSERT INTO temp_schedulebank (Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language) SELECT Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language FROM schedulebank ORDER BY Day ASC, Time ASC")
statement?.executeUpdate("TRUNCATE TABLE schedulebank")
statement?.executeUpdate("INSERT INTO schedulebank (Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language) SELECT Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language FROM temp_schedulebank")
statement?.executeUpdate("DROP TABLE temp_schedulebank")
Logger.info("schedulebank table resorted by Day and Time" as Any)
// reload the local list
Reload_Schedulebank()
return true
} catch (e: Exception) {
Logger.error("Error resorting schedulebank table by Day and Time: ${e.message}" as Any)
}
return false
* Resort the schedulebank table, in order to reorder the index after deletions, and sort ascending by Day and Time.
* @return True if the resorting was successful, false otherwise.
*/
fun Resort_Schedulebank_by_Day_Time(): Boolean {
try {
val statement = connection?.createStatement()
// use a temporary table to reorder the index
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_schedulebank LIKE schedulebank")
statement?.executeUpdate("INSERT INTO temp_schedulebank (Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language) SELECT Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language FROM schedulebank ORDER BY Day ASC, Time ASC")
statement?.executeUpdate("TRUNCATE TABLE schedulebank")
statement?.executeUpdate("INSERT INTO schedulebank (Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language) SELECT Description, Day, Time, Soundpath, Repeat, Enable, BroadcastZones, Language FROM temp_schedulebank")
statement?.executeUpdate("DROP TABLE temp_schedulebank")
Logger.info("schedulebank table resorted by Day and Time" as Any)
// reload the local list
Reload_Schedulebank()
return true
} catch (e: Exception) {
Logger.error("Error resorting schedulebank table by Day and Time: ${e.message}" as Any)
}
return false
}
/**
* Clears all entries from the schedulebank table in the database and the local list.
@@ -722,7 +941,7 @@ class MariaDB(
* Resort the language link table, in order to reorder the index after deletions, and sort ascending by TAG.
* @return True if the resorting was successful, false otherwise.
*/
fun Resort_LanguageLink_by_TAG() : Boolean {
fun Resort_LanguageLink_by_TAG(): Boolean {
try {
val statement = connection?.createStatement()
// use a temporary table to reorder the index
@@ -1243,27 +1462,27 @@ class MariaDB(
}
/**
* Resort the messagebank table, in order to reorder the index after deletions, and sort ascending by ANN_ID.
* @return True if the resorting was successful, false otherwise.
*/
fun Resort_Messagebank_by_ANN_ID(): Boolean {
try {
val statement = connection?.createStatement()
// use a temporary table to reorder the index
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_messagebank LIKE messagebank")
statement?.executeUpdate("INSERT INTO temp_messagebank (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) SELECT Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS FROM messagebank ORDER BY ANN_ID ASC")
statement?.executeUpdate("TRUNCATE TABLE messagebank")
statement?.executeUpdate("INSERT INTO messagebank (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) SELECT Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS FROM temp_messagebank")
statement?.executeUpdate("DROP TABLE temp_messagebank")
Logger.info("messagebank table resorted by Description" as Any)
// reload the local list
Reload_Messagebank()
return true
} catch (e: Exception) {
Logger.error("Error resorting messagebank table by Description: ${e.message}" as Any)
}
return false
* Resort the messagebank table, in order to reorder the index after deletions, and sort ascending by ANN_ID.
* @return True if the resorting was successful, false otherwise.
*/
fun Resort_Messagebank_by_ANN_ID(): Boolean {
try {
val statement = connection?.createStatement()
// use a temporary table to reorder the index
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_messagebank LIKE messagebank")
statement?.executeUpdate("INSERT INTO temp_messagebank (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) SELECT Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS FROM messagebank ORDER BY ANN_ID ASC")
statement?.executeUpdate("TRUNCATE TABLE messagebank")
statement?.executeUpdate("INSERT INTO messagebank (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) SELECT Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS FROM temp_messagebank")
statement?.executeUpdate("DROP TABLE temp_messagebank")
Logger.info("messagebank table resorted by Description" as Any)
// reload the local list
Reload_Messagebank()
return true
} catch (e: Exception) {
Logger.error("Error resorting messagebank table by Description: ${e.message}" as Any)
}
return false
}
/**
* Exports the messagebank table to an XLSX workbook.
@@ -1490,7 +1709,7 @@ class MariaDB(
* Get All Broadcast Zones from database
* @return A list of BroadcastZones entries.
*/
fun GetBroadcastZones(){
fun GetBroadcastZones() {
BroadcastZoneList.clear()
try {
val statement = connection?.createStatement()
@@ -1620,27 +1839,27 @@ class MariaDB(
}
/**
* Resort the broadcast_zones table, in order to reorder the index after deletions, and sort ascending by description.
* @return True if the resorting was successful, false otherwise.
*/
fun Resort_BroadcastZones_by_description(): Boolean {
try {
val statement = connection?.createStatement()
// use a temporary table to reorder the index
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_broadcast_zones LIKE broadcast_zones")
statement?.executeUpdate("INSERT INTO temp_broadcast_zones (description, SoundChannel, Box, Relay) SELECT description, SoundChannel, Box, Relay FROM broadcast_zones ORDER BY description ASC")
statement?.executeUpdate("TRUNCATE TABLE broadcast_zones")
statement?.executeUpdate("INSERT INTO broadcast_zones (description, SoundChannel, Box, Relay) SELECT description, SoundChannel, Box, Relay FROM temp_broadcast_zones")
statement?.executeUpdate("DROP TABLE temp_broadcast_zones")
Logger.info("broadcast_zones table resorted by description" as Any)
// reload the local list
GetBroadcastZones()
return true
} catch (e: Exception) {
Logger.error("Error resorting broadcast_zones table by description: ${e.message}" as Any)
}
return false
* Resort the broadcast_zones table, in order to reorder the index after deletions, and sort ascending by description.
* @return True if the resorting was successful, false otherwise.
*/
fun Resort_BroadcastZones_by_description(): Boolean {
try {
val statement = connection?.createStatement()
// use a temporary table to reorder the index
statement?.executeUpdate("CREATE TABLE IF NOT EXISTS temp_broadcast_zones LIKE broadcast_zones")
statement?.executeUpdate("INSERT INTO temp_broadcast_zones (description, SoundChannel, Box, Relay) SELECT description, SoundChannel, Box, Relay FROM broadcast_zones ORDER BY description ASC")
statement?.executeUpdate("TRUNCATE TABLE broadcast_zones")
statement?.executeUpdate("INSERT INTO broadcast_zones (description, SoundChannel, Box, Relay) SELECT description, SoundChannel, Box, Relay FROM temp_broadcast_zones")
statement?.executeUpdate("DROP TABLE temp_broadcast_zones")
Logger.info("broadcast_zones table resorted by description" as Any)
// reload the local list
GetBroadcastZones()
return true
} catch (e: Exception) {
Logger.error("Error resorting broadcast_zones table by description: ${e.message}" as Any)
}
return false
}
/**
* Clears all entries from the broadcast_zones in the database.

View File

@@ -0,0 +1,4 @@
package database
@Suppress("unused")
data class SoundChannel(val index: UInt, val channel: String, val ip: String)