import audio.AudioFileInfo import codes.Somecodes.Companion.Get_ANN_ID import codes.Somecodes.Companion.IsAlphabethic import codes.Somecodes.Companion.IsNumber import codes.Somecodes.Companion.Make_WAV_FileName import codes.Somecodes.Companion.SoundbankResult_directory import codes.Somecodes.Companion.ValidFile import codes.Somecodes.Companion.ValidIPV4 import codes.Somecodes.Companion.ValidString import codes.Somecodes.Companion.dateformat1 import codes.Somecodes.Companion.datetimeformat1 import codes.Somecodes.Companion.timeformat2 import content.Category import content.Language import content.ScheduleDay import database.BroadcastZones import database.Messagebank import database.QueueTable import database.Soundbank import org.tinylog.Logger import java.time.DayOfWeek import java.time.LocalDate import java.time.LocalDateTime import java.time.LocalTime import java.util.function.Consumer /** * MainExtension01 contains additional functions for the main application. * Main responsibilities are : * 1. Reading and processing Queue_Paging * 2. Reading and processing Queue_Table * 3. Reading and processing Schedule * This class is separated to keep the main.kt file cleaner and more organized. */ class MainExtension01 { /** * Fungsi untuk cek apakah semua broadcast zone valid * Valid berarti nama broadcast zone ada di tabel BroadcastZones, dan SoundChannel-nya ada di tabel SoundChannel, dan IP-nya valid * @param bz List of broadcast zone (SoundChannel) * @return true jika semua valid, false jika ada yang tidak valid */ fun AllBroadcastZonesValid(bz: List): Boolean { if (bz.isNotEmpty()) { val validbz = mutableListOf() db.broadcastDB.List.forEach { xx -> if (ValidString(xx.description) && ValidString(xx.SoundChannel)){ if (xx.description in bz) { db.soundchannelDB.List.forEach { sc -> if (sc.channel == xx.SoundChannel){ if (ValidIPV4(sc.ip)){ validbz.add(xx) } } } } } } if (validbz.size == bz.size) { return true } else Logger.error { "Some requested broadcast zones are not registered in BroadcastZone table" } } else Logger.error { "No Broadcast Zones checked for validity" } return false } /** * Fungsi untuk cek apakah semua Streamer Output idle * @param bz List of ip address * @return true jika semua idle, false jika ada yang tidak idle */ fun AllStreamerOutputIdle(bz: List): Boolean { if (bz.isNotEmpty()) { if (StreamerOutputs.isNotEmpty()){ val idlebz = mutableListOf() bz.forEach { z1 -> val so = StreamerOutputs.values.find { it.ipaddress == z1 } if (so != null) { if (so.isIdle()) { idlebz.add(z1) } } } if (idlebz.size == bz.size) { return true } else Logger.warn { "Some requested Streamer Outputs are not idle" } } else Logger.error { "Streamer Outputs empty, Idle Broadcast Zones not available" } } else Logger.error { "No Broadcast Zones checked for idle" } return false } /** * find SoundChannel IP from BroadcastZones description * @param bz List of BroadcastZones description * @return List of SoundChannel IP */ fun BroadcastZones_to_SoundChannel_IP(bz: List) : List{ val result = mutableListOf() if (bz.isNotEmpty()) { val l1 = db.broadcastDB.List.filter { bz.contains(it.description) } if (l1.size==bz.size){ l1.forEach { xx -> val yy = db.soundchannelDB.List.find { it.channel == xx.SoundChannel } if (yy!=null) result.add(yy.ip) } } else Logger.error { "Some requested broadcast zones are not registered in BroadcastZone table" } } else Logger.error { "No Broadcast Zones to convert to SoundChannel IP" } return result } /** * Fungsi untuk ambil messagebank berdasarkan ANN_ID, diurutkan berdasarkan urutan bahasa di urutan_bahasa * @param id ANN_ID dari messagebank * @param languages List of language yang diinginkan, default urutan_bahasa * @return List of Messagebank */ fun Get_MessageBank_by_id(id: Int, languages: List = urutan_bahasa): ArrayList { val mb_list = ArrayList() languages.forEach { lang -> db.messageDB.List.find { mb -> mb.ANN_ID == id.toUInt() && mb.Language.equals(lang, true) && mb.Voice_Type.equals(selected_voice, true) } ?.let { mb_list.add(it) } } return mb_list } /** * Find Soundbank path for AlphabetNumeric category based on value * @param sb List of Soundbank to search * @param value String value to search, can be combination of letters and numbers, e.g. A1, B2, 3C, 12, etc. * @return Soundbank path if found and valid, null if not found or invalid */ fun Get_Soundbank_AlpabethNumeric(sb: List, value: String): List? { val result = mutableListOf() if (ValidString(value)) { if (sb.isNotEmpty()) { val regex = Regex("([A-Z])?(\\d+)([A-Z])?") val match = regex.find(value) match?.groupValues?.forEach { if (IsNumber(it)) { val num = sb.firstOrNull { s1 -> s1.Category == Category.AlphabetNumeric.name && s1.TAG == "N$it" } if (num != null) { if (ValidFile(num.Path)) { result.add(num.Path) } } } else if (IsAlphabethic(it)) { val alp = sb.firstOrNull { s1 -> s1.Category == Category.AlphabetNumeric.name && s1.TAG == it } if (alp != null) { if (ValidFile(alp.Path)) { result.add(alp.Path) } } } } if (result.isNotEmpty()) { return result } } } return null } val SoundbankKeywords = listOf("ANN_ID","AL","FLNUM","A_D","I_D","ORIGIN","CITY","SHALAT","PLACES","DESTINATION","ETAD","STANDCODE","GATECODE","REMARK","BCB","PLATNOMOR","REASON","PROCEDURE") /** * Parse soundbank data from string value in format "KEY:VALUE KEY:VALUE ..." * @param value String value to parse * @return Map of key-value pairs if valid, null if invalid */ fun Get_Soundbank_Data(value: String) : Map? { if (ValidString(value)){ val values = value.split(" ").map { it.trim() }.filter { ValidString(it) } if (values.isNotEmpty()){ val result = mutableMapOf() values.forEach { val kv = it.split(":") if (kv.size==2){ val key = kv[0].trim().uppercase() val val1 = kv[1].trim().uppercase() if (ValidString(key) && ValidString(val1)){ if (SoundbankKeywords.contains(key)) result[key] = val1 } } } if (result.isNotEmpty()) return result } } return null } /** * Find soundbank files from messagebank tags, filtered by VoiceType and Language * @param mb Messagebank object * @param variables Map of variables to replace in tags. * @param cbOK Callback function if success, returns List of soundbank file names * @param cbFail Callback function if failed, returns error message */ fun Get_Soundbank_Files( mb: Messagebank, variables: Map, cbOK: Consumer>, cbFail: Consumer ) { val tags = mb.Message_TAGS.split(" ") if (tags.isEmpty()) { cbFail.accept("No tags found in messagebank id ${mb.ANN_ID}") return } // dapatkan soundbank array berdasarkan VoiceType dan Language val sb = db.soundDB.List .filter { it.VoiceType.equals(mb.Voice_Type, true) } .filter { it.Language.equals(mb.Language, true) } if (sb.isEmpty()) { cbFail.accept("No soundbank found for voice type ${mb.Voice_Type} and language ${mb.Language}") return } val files = mutableListOf() tags.forEach { tag -> when (val _tag = tag.trim()) { "[AIRPLANE_NAME]" -> { val value = variables["AIRPLANE_NAME"].orEmpty() if (ValidString(value)) { val airplane = sb.firstOrNull { it.Category == Category.Airplane_Name.name && it.TAG == value } if (airplane != null) { if (ValidFile(airplane.Path)) { files.add(airplane.Path) } else { cbFail.accept("Invalid soundbank file ${airplane.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return } } else { cbFail.accept("No Airplane_Name found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return } } else { cbFail.accept("AIRPLANE_NAME variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") return } } "[FLIGHT_NUMBER]" -> { val alcode = variables["AIRPLANE_NAME"].orEmpty() val fncode = variables["FLIGHT_NUMBER"].orEmpty() if (ValidString(alcode) && ValidString(fncode)) { val val1 = sb.firstOrNull { it.Category == Category.Airline_Code.name && it.TAG == alcode } val val2 = Get_Soundbank_AlpabethNumeric(sb, fncode) if (val1 != null) { if (ValidFile(val1.Path)) { files.add(val1.Path) } else { cbFail.accept("Invalid soundbank file ${val1.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return } } else { cbFail.accept("No Airline_Code found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return } if (val2 != null && val2.isNotEmpty()) { files.addAll(val2) } else { cbFail.accept("No valid soundbank files found for FLIGHT_NUMBER value '$fncode' for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return } } else { cbFail.accept("AIRPLANE_NAME or FLIGHT_NUMBER variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") return } } "[PLATNOMOR]" -> { // plat nomor bisa huruf dan angka, atau huruf angka huruf, misalnya B123CD, AB1234EF, RI1 val value = variables["PLATNOMOR"].orEmpty() if (ValidString(value)) { val regex = Regex("([A-Z]+)(\\d+)([A-Z]*)") val match = regex.find(value) if (match != null) { val depan = match.groups[1]?.value // huruf depan val tengah = match.groups[2]?.value // angka val belakang = match.groups[3]?.value // huruf belakang, bisa kosong // ambilin per huruf depan?.forEach { val dep = Get_Soundbank_AlpabethNumeric(sb, it.toString()) if (dep != null) { files.addAll(dep) } } // ambilin per angka tengah?.forEach { val tgh = Get_Soundbank_AlpabethNumeric(sb, it.toString()) if (tgh != null) { files.addAll(tgh) } } // ambilin per huruf belakang?.forEach { val blk = Get_Soundbank_AlpabethNumeric(sb, it.toString()) if (blk != null) { files.addAll(blk) } } } else { cbFail.accept("PLATNOMOR variable has invalid format for value '$value' in messagebank id ${mb.ANN_ID}") return } } else { cbFail.accept("PLATNOMOR variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") return } } "[CITY]" -> { val values = variables["CITY"].orEmpty().split(";").map { it.trim() }.filter { ValidString(it) } if (values.isNotEmpty()) { values.forEach { vv -> val city = sb.firstOrNull { it.Category == Category.City.name && it.TAG == vv } if (city != null) { if (ValidFile(city.Path)) { files.add(city.Path) } else { cbFail.accept("Invalid soundbank file ${city.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return } } else { cbFail.accept("No City found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return } } } else { cbFail.accept("CITY variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") return } } "[PLACES]" -> { val value = variables["PLACES"].orEmpty() if (ValidString(value)) { val places = sb.firstOrNull { it.Category == Category.Places.name && it.TAG == value } if (places != null) { if (ValidFile(places.Path)) { files.add(places.Path) } else { cbFail.accept("Invalid soundbank file ${places.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return } } else { cbFail.accept("No Places found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return } } else { cbFail.accept("PLACES variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") return } } "[ETAD]" -> { val values = variables["ETAD"].orEmpty().split(":").map { it.trim() }.filter { IsNumber(it) } if (values.size == 2) { val hh = Get_Soundbank_AlpabethNumeric(sb, values[0]) val mm = Get_Soundbank_AlpabethNumeric(sb, values[1]) if (hh != null && mm != null && hh.isNotEmpty() && mm.isNotEmpty()) { if (ValidFile(hh[0]) && ValidFile(mm[0])) { files.add(hh[0]) files.add(mm[0]) } else { cbFail.accept("ETAD variable has invalid soundbank files for tag $_tag in messagebank id ${mb.ANN_ID}, unable to find valid soundbank files for HH='${values[0]}' or MM='${values[1]}'") return } } else { cbFail.accept("ETAD variable has invalid soundbank for tag $_tag in messagebank id ${mb.ANN_ID}, unable to find soundbank for HH='${values[0]}' or MM='${values[1]}'") return } } else { cbFail.accept("ETAD variable has invalid format for tag $_tag in messagebank id ${mb.ANN_ID}, expected format HH:MM") return } } "[SHALAT]" -> { val value = variables["SHALAT"].orEmpty() if (ValidString(value)) { val shalat = sb.firstOrNull { it.Category == Category.Shalat.name && it.TAG == value } if (shalat != null) { if (ValidFile(shalat.Path)) { files.add(shalat.Path) } else { cbFail.accept("Invalid soundbank file ${shalat.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return } } else { cbFail.accept("No Shalat found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return } } else { cbFail.accept("SHALAT variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") return } } "[BCB]" -> { // BCB bisa angka saja, misalnya 1,2,3 // atau huruf dan angka, misalnya A1, B2, C3, 1A, 2B, 3C val value = variables["BCB"].orEmpty() val path = Get_Soundbank_AlpabethNumeric(sb, value) if (path != null) { files.addAll(path) } else { cbFail.accept("BCB variable is missing, empty, or doesn't have valid soundbank for value '$value' in messagebank id ${mb.ANN_ID}") return } } "[GATENUMBER]" -> { // gate number bisa angka saja, misalnya 1,2,3 // atau huruf dan angka, misalnya A1, B2, C3, 1A, 2B, 3C val value = variables["GATENUMBER"].orEmpty() if (ValidString(value)) { val values = value.split(",").map { it.trim() }.filter { ValidString(it) } if (values.isNotEmpty()) { values.forEach { vv -> val path = Get_Soundbank_AlpabethNumeric(sb, vv) if (path != null) { files.addAll(path) } else { cbFail.accept("GATENUMBER variable doesn't have valid soundbank for value '$vv' in messagebank id ${mb.ANN_ID}") return } } } else { cbFail.accept("GATENUMBER variable is empty after split for tag $_tag in messagebank id ${mb.ANN_ID}") return } } else { cbFail.accept("GATENUMBER variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") return } } "[REASON]" -> { val value = variables["REASON"].orEmpty() if (ValidString(value)) { val reason = sb.firstOrNull { it.Category == Category.Reason.name && it.TAG == value } if (reason != null) { if (ValidFile(reason.Path)) { files.add(reason.Path) } else { cbFail.accept("Invalid soundbank file ${reason.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return } } else { cbFail.accept("No Reason found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return } } else { cbFail.accept("REASON variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") return } } "[PROCEDURE]" -> { val value = variables["PROCEDURE"].orEmpty() if (ValidString(value)) { val procedure = sb.firstOrNull { it.Category == Category.Procedure.name && it.TAG == value } if (procedure != null) { if (ValidFile(procedure.Path)) { files.add(procedure.Path) } else { cbFail.accept("Invalid soundbank file ${procedure.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return } } else { cbFail.accept("No Procedure found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return } } else { cbFail.accept("PROCEDURE variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}") return } } else -> { // Phrase val phrase = sb.firstOrNull { it.Category == Category.Phrase.name && it.TAG == _tag } if (phrase != null) { if (ValidFile(phrase.Path)) { files.add(phrase.Path) } else { cbFail.accept("Invalid soundbank file ${phrase.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return } } else { cbFail.accept("No Phrase found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return } } } } // all tags processed, return files cbOK.accept(files) } /** * Read and process Queue_Paging table. */ fun Read_Queue_Paging(){ db.queuepagingDB.Get() for (qp in db.queuepagingDB.List) { println("Processing $qp") if (qp.BroadcastZones.isNotBlank()) { val zz = qp.BroadcastZones.split(";") if (AllBroadcastZonesValid(zz)) { val ips = BroadcastZones_to_SoundChannel_IP(zz) println("Broadcast zones $zz converted to SoundChannel IPs: $ips") if (AllStreamerOutputIdle(ips)) { if (qp.Source == "PAGING") { // nama file ada di Message if (ValidFile(qp.Message)) { val afi = audioPlayer.LoadAudioFile(qp.Message) zz.forEach { z1 -> StreamerOutputs.values.find { it.channel == z1 } ?.SendData(afi.bytes, { db.Add_Log("AAS", it) }, { db.Add_Log("AAS", it) }) } val logmessage = "Broadcast started PAGING with Filename '${qp.Message}' to zones: ${qp.BroadcastZones}" Logger.info { logmessage } db.Add_Log("AAS", logmessage) db.queuepagingDB.DeleteByIndex(qp.index.toInt()) return } else { // file tidak valid, delete from queue paging db.queuepagingDB.DeleteByIndex(qp.index.toInt()) db.Add_Log( "AAS", "Cancelled paging message with index ${qp.index} due to invalid audio file" ) } } else if (qp.Source == "SHALAT") { val ann_id = Get_ANN_ID(qp.Message) if (ann_id > 0) { // shalat, ambil messagebank berdasarkan ann_id dengan bahasa Indonesia saja Get_MessageBank_by_id(ann_id, listOf(Language.INDONESIA.name)).let { mblist -> if (mblist.isNotEmpty()) { Get_Soundbank_Files( mblist[0], emptyMap(), { // dapat list dari files dan sudah dicek valid path listfile -> val listafi = mutableListOf() listfile.forEach { filenya -> val afi = audioPlayer.LoadAudioFile(filenya) if (afi.isValid()) { listafi.add(afi) } } val targetfile = SoundbankResult_directory.resolve( Make_WAV_FileName( "Shalat", "" ) ).toString() audioPlayer.WavWriter( listafi, targetfile, true, ) { success, message -> db.Add_Log("AAS", message) if (success) { // file siap broadcast val targetafi = audioPlayer.LoadAudioFile(targetfile) if (targetafi.isValid()) { zz.forEach { z1 -> StreamerOutputs.values.find { it.channel == z1 } ?.SendData( targetafi.bytes, { db.Add_Log("AAS", it) }, { db.Add_Log("AAS", it) }) } val logmsg = "Broadcast started SHALAT message with generated file '$targetfile' to zones: ${qp.BroadcastZones}" Logger.info { logmsg } db.Add_Log("AAS", logmsg) db.queuepagingDB.DeleteByIndex(qp.index.toInt()) } else { db.Add_Log( "AAS", "Failed to load generated Shalat WAV file $targetfile" ) } } } }, { err -> db.queuepagingDB.DeleteByIndex(qp.index.toInt()) db.Add_Log("AAS", err) } ) } else { // tidak ada messagebank dengan ann_id ini, delete from queue paging db.queuepagingDB.DeleteByIndex(qp.index.toInt()) db.Add_Log( "AAS", "Cancelled Shalat message with index ${qp.index} due to ANN_ID $ann_id not found in Messagebank" ) } } } else { // invalid ann_id, delete from queue paging db.queuepagingDB.DeleteByIndex(qp.index.toInt()) db.Add_Log( "AAS", "Cancelled Shalat message with index ${qp.index} due to invalid ANN_ID" ) } } } } else { // ada broadcast zone yang tidak valid, delete from queue paging db.queuepagingDB.DeleteByIndex(qp.index.toInt()) db.Add_Log( "AAS", "Cancelled paging message with index ${qp.index} due to invalid broadcast zone" ) } } else { // invalid broadcast zone, delete from queue paging db.queuepagingDB.DeleteByIndex(qp.index.toInt()) db.Add_Log("AAS", "Cancelled paging message with index ${qp.index} due to empty broadcast zone") } } } /** * Read and process Queue_Table table. */ fun Read_Queue_Table(){ db.queuetableDB.Get() db.queuetableDB.List.forEach { qa -> println("Processing $qa") if (qa.BroadcastZones.isNotEmpty()) { val zz = qa.BroadcastZones.split(";") if (AllBroadcastZonesValid(zz)) { val ips = BroadcastZones_to_SoundChannel_IP(zz) if (AllStreamerOutputIdle(ips)) { if (qa.Type == "SOUNDBANK") { val variables = Get_Soundbank_Data(qa.SB_TAGS) val languages = qa.Language.split(";") // cek apakah ANN_ID ada di SB_TAGS val ann_id = variables?.get("ANN_ID")?.toIntOrNull() ?: 0 if (ann_id==0){ // not available from variables, try to get from Message column // ada ini, karena protokol FIS dulu tidak ada ANN_ID tapi pake Remark val remark = variables?.get("REMARK").orEmpty() when(remark){ "GOP" -> { //TODO Combobox First_Call_Message_Chooser } "GBD" ->{ // TODO Combobox Second_Call_Message_Chooser } "GFC" ->{ // TODO Combobox Final_Call_Message_Chooser } "FLD" ->{ // TODO Combobox Landed_Message_Chooser } } } // recheck again if (ann_id == 0) { db.Add_Log( "AAS", "Cancelled SOUNDBANK message with index ${qa.index} due to missing or invalid ANN_ID in SB_TAGS" ) db.queuetableDB.DeleteByIndex(qa.index.toInt()) return@forEach } // sampe sini punya ann_id valid val mblist = Get_MessageBank_by_id(ann_id, languages) if (mblist.isNotEmpty()) { val listafi = mutableListOf() mblist.forEach { mb -> Get_Soundbank_Files(mb, variables ?: emptyMap(), { listfile -> listfile.forEach { filenya -> val afi = audioPlayer.LoadAudioFile(filenya) if (afi.isValid()) { listafi.add(afi) } } }, { err -> db.Add_Log("AAS", err) db.queuetableDB.DeleteByIndex(qa.index.toInt()) }) } if (listafi.isNotEmpty()){ db.queuetableDB.DeleteByIndex(qa.index.toInt()) val targetfile = SoundbankResult_directory.resolve(Make_WAV_FileName("Soundbank","")).toString() println("Writing to target WAV file: $targetfile") audioPlayer.WavWriter(listafi, targetfile, true, ) { success, message -> if (success) { // file siap broadcast println("Successfully wrote WAV file: $targetfile") val targetafi = audioPlayer.LoadAudioFile(targetfile) if (targetafi.isValid()) { ips.forEach { ip -> StreamerOutputs[ip].let{ sc -> sc?.SendData(targetafi.bytes, { db.Add_Log("AAS", it) }, { db.Add_Log("AAS", it) } ) } } val logmsg = "Broadcast started SOUNDBANK message with generated file '$targetfile' to zones: ${qa.BroadcastZones}" Logger.info { logmsg } db.Add_Log("AAS", logmsg) } } else { println("Failed to write WAV file: $message") db.queuetableDB.DeleteByIndex(qa.index.toInt()) db.Add_Log("AAS", message) } } } } else { // tidak ada messagebank dengan ann_id ini, delete from queue table db.queuetableDB.DeleteByIndex(qa.index.toInt()) db.Add_Log( "AAS", "Cancelled SOUNDBANK message with index ${qa.index} due to ANN_ID $ann_id not found in Messagebank" ) } } else if (qa.Type == "TIMER") { val ann_id = Get_ANN_ID(qa.SB_TAGS) if (ann_id > 0) { val mblist = Get_MessageBank_by_id(ann_id, qa.Language.split(";")) if (mblist.isNotEmpty()) { mblist.forEach { mb -> Get_Soundbank_Files(mb, emptyMap(), { listfile -> val listafi = mutableListOf() listfile.forEach { filenya -> val afi = audioPlayer.LoadAudioFile(filenya) if (afi.isValid()) { listafi.add(afi) } } val targetfile = SoundbankResult_directory.resolve(Make_WAV_FileName("Timer","")).toString() audioPlayer.WavWriter(listafi, targetfile, true, ) { success, message -> if (success) { // file siap broadcast val targetafi = audioPlayer.LoadAudioFile(targetfile) if (targetafi.isValid()) { zz.forEach { z1 -> StreamerOutputs.values.find { it.channel == z1 } ?.SendData( targetafi.bytes, { db.Add_Log("AAS", it) }, { db.Add_Log("AAS", it) }) } val logmsg = "Broadcast started TIMER message with generated file '$targetfile' to zones: ${qa.BroadcastZones}" Logger.info { logmsg } db.Add_Log("AAS", logmsg) db.queuetableDB.DeleteByIndex(qa.index.toInt()) } } db.Add_Log("AAS", message) } }, { err -> db.Add_Log("AAS", err) db.queuetableDB.DeleteByIndex(qa.index.toInt()) }) } } else { // tidak ada messagebank dengan ann_id ini, delete from queue table db.queuetableDB.DeleteByIndex(qa.index.toInt()) db.Add_Log( "AAS", "Cancelled TIMER with index ${qa.index} due to ANN_ID $ann_id not found in Messagebank" ) } } else { // invalid ann_id, delete from queue table db.queuetableDB.DeleteByIndex(qa.index.toInt()) db.Add_Log( "AAS", "Cancelled TIMER with index ${qa.index} due to invalid ANN_ID" ) } } } } else { // ada broadcast zone yang tidak valid, delete from queue table db.queuetableDB.DeleteByIndex(qa.index.toInt()) db.Add_Log( "AAS", "Cancelled table message with index ${qa.index} due to invalid broadcast zone" ) } } else { // invalid broadcast zone, delete from queue table db.queuetableDB.DeleteByIndex(qa.index.toInt()) db.Add_Log("AAS", "Cancelled table message with index ${qa.index} due to empty broadcast zone") } } } /** * Read and process Schedule_Table table. * This function is called every minute when second=00. * It checks for schedules that match the current time and day, * and adds them to the Queue_Table for processing. */ fun Read_Schedule_Table(){ val localtime = LocalTime.now() // detik harus 00 if (localtime.second != 0) return val timestring = timeformat2.format(localtime) val sch = db.scheduleDB.List.filter { it.Time == timestring && it.Enable } // tidak ada schedule dengan time sekarang dan enable=true if (sch.isEmpty()) return val localdate = LocalDate.now() val ddmmyyyy = dateformat1.format(localdate) // check special date dulu val specialdate = sch.find { it.Day == ddmmyyyy } if (specialdate != null) { val qt = QueueTable( 0u, LocalDateTime.now().format(datetimeformat1), "AAS", "TIMER", specialdate.Description, specialdate.Soundpath, specialdate.BroadcastZones, 1.toUInt(), specialdate.Language ) db.queuetableDB.Add(qt) return } // cek weekly schedule val weekly = sch.find { it.Day == when (localdate.dayOfWeek) { DayOfWeek.MONDAY -> ScheduleDay.Monday.name DayOfWeek.TUESDAY -> ScheduleDay.Tuesday.name DayOfWeek.WEDNESDAY -> ScheduleDay.Wednesday.name DayOfWeek.THURSDAY -> ScheduleDay.Thursday.name DayOfWeek.FRIDAY -> ScheduleDay.Friday.name DayOfWeek.SATURDAY -> ScheduleDay.Saturday.name DayOfWeek.SUNDAY -> ScheduleDay.Sunday.name } } if (weekly != null) { val qt = QueueTable( 0u, LocalDateTime.now().format(datetimeformat1), "AAS", "TIMER", weekly.Description, weekly.Soundpath, weekly.BroadcastZones, 1.toUInt(), weekly.Language ) db.queuetableDB.Add(qt) return } // check daily schedule val daily = sch.find { it.Day == ScheduleDay.Everyday.name } if (daily != null) { val qt = QueueTable( 0u, LocalDateTime.now().format(datetimeformat1), "AAS", "TIMER", daily.Description, daily.Soundpath, daily.BroadcastZones, 1.toUInt(), daily.Language ) db.queuetableDB.Add(qt) return } } }