1120 lines
53 KiB
Kotlin
1120 lines
53 KiB
Kotlin
import audio.AudioFileInfo
|
|
import barix.BarixConnection
|
|
import codes.Result_GetSoundbankFiles
|
|
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
|
|
|
|
/**
|
|
* 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<String>): Boolean {
|
|
if (bz.isNotEmpty()) {
|
|
val validbz = mutableListOf<BroadcastZones>()
|
|
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<String>): Boolean {
|
|
if (bz.isNotEmpty()) {
|
|
if (StreamerOutputs.isNotEmpty()){
|
|
val idlebz = mutableListOf<String>()
|
|
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
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
/**
|
|
* Find BarixConnection by BroadcastZones description
|
|
* @param zonename BroadcastZones description
|
|
* @return BarixConnection if found, null if not found or invalid
|
|
*/
|
|
fun Get_Barix_Connection_by_ZoneName(zonename: String) : BarixConnection? {
|
|
if (ValidString(zonename)){
|
|
val bz = db.broadcastDB.List.find{ it.description == zonename }
|
|
val sc = if (bz!=null) db.soundchannelDB.List.find { it.channel == bz.SoundChannel } else null
|
|
val ip = sc?.ip ?: ""
|
|
if (ValidIPV4(ip)){
|
|
// ketemu ip-nya
|
|
return StreamerOutputs[ip]
|
|
|
|
}
|
|
}
|
|
return null
|
|
}
|
|
|
|
/**
|
|
* find SoundChannel IP from BroadcastZones description
|
|
* @param bz List of BroadcastZones description
|
|
* @return List of SoundChannel IP
|
|
*/
|
|
fun BroadcastZones_to_SoundChannel_IP(bz: List<String>) : List<String>{
|
|
val result = mutableListOf<String>()
|
|
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<String> = urutan_bahasa): ArrayList<Messagebank> {
|
|
val mb_list = ArrayList<Messagebank>()
|
|
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<Soundbank>, value: String): List<String>? {
|
|
val result = mutableListOf<String>()
|
|
if (ValidString(value)) {
|
|
if (sb.isNotEmpty()) {
|
|
val regex = Regex("[A-Z0-9]")
|
|
val match = regex.findAll(value)
|
|
match.forEach {
|
|
mm ->
|
|
if (IsNumber(mm.value)) {
|
|
val num =sb.firstOrNull { s1 -> s1.Category == Category.AlphabetNumeric.name && s1.TAG == "N${mm.value}" }
|
|
if (num!=null){
|
|
if (ValidFile(num.Path)){
|
|
result.add(num.Path)
|
|
}
|
|
}
|
|
} else if (IsAlphabethic(mm.value)) {
|
|
val alp =
|
|
sb.firstOrNull { s1 -> s1.Category == Category.AlphabetNumeric.name && s1.TAG == mm.value }
|
|
if (alp != null) {
|
|
if (ValidFile(alp.Path)) {
|
|
result.add(alp.Path)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result
|
|
|
|
}
|
|
}
|
|
return null
|
|
}
|
|
|
|
fun Get_Soundbank_Hour_Minute(sb: List<Soundbank>, value: Int) : String {
|
|
if (value in 0..59){
|
|
val tag = if (value<10) "N0${value}" else "N${value}"
|
|
val hm = sb.firstOrNull { it.Category == Category.AlphabetNumeric.name && it.TAG == tag }
|
|
if (hm!=null){
|
|
if (ValidFile(hm.Path)) return hm.Path
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
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<String, String>? {
|
|
if (ValidString(value)){
|
|
//println("Parsing soundbank data from value: $value")
|
|
val values = value.split(" ").map { it.trim() }.filter { ValidString(it) }
|
|
//println("Split into values: $values")
|
|
if (values.isNotEmpty()){
|
|
val result = mutableMapOf<String, String>()
|
|
values.forEach {
|
|
val separatorindex = it.indexOf(":")
|
|
if (separatorindex!=-1){
|
|
// ada separator
|
|
val key = it.substring(0, separatorindex).trim().uppercase()
|
|
val value = it.substring(separatorindex+1).trim().uppercase()
|
|
//println("Got $key : $value")
|
|
if (ValidString(key) && ValidString(value)){
|
|
if (SoundbankKeywords.contains(key)) result[key] = value
|
|
}
|
|
}
|
|
|
|
}
|
|
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.
|
|
* @return Result_GetSoundbankFiles object containing success status, message, and list of soundbank file paths.
|
|
*/
|
|
fun Get_Soundbank_Files(
|
|
mb: Messagebank,
|
|
variables: Map<String, String>
|
|
|
|
) : Result_GetSoundbankFiles {
|
|
val tags = mb.Message_TAGS.split(" ")
|
|
if (tags.isEmpty()) {
|
|
return Result_GetSoundbankFiles(false, "No tags found in messagebank id ${mb.ANN_ID}")
|
|
|
|
}
|
|
// 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()) {
|
|
return Result_GetSoundbankFiles(false, "No soundbank found for voice type ${mb.Voice_Type} and language ${mb.Language}")
|
|
|
|
}
|
|
|
|
val files = mutableListOf<String>()
|
|
|
|
tags.forEach { tag ->
|
|
when (val _tag = tag.trim()) {
|
|
"[AIRPLANE_NAME]" -> {
|
|
val value = variables["AL"].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 {
|
|
return Result_GetSoundbankFiles(false, "Invalid soundbank file ${airplane.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
|
|
|
|
}
|
|
} else {
|
|
return Result_GetSoundbankFiles(false, "No Airplane_Name found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
|
|
|
|
}
|
|
} else {
|
|
return Result_GetSoundbankFiles(false, "AIRPLANE_NAME variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"[FLIGHT_NUMBER]" -> {
|
|
val alcode = variables["AL"].orEmpty()
|
|
val fncode = variables["FLNUM"].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 {
|
|
return Result_GetSoundbankFiles(false, "Invalid soundbank file ${val1.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
|
|
|
|
}
|
|
} else {
|
|
|
|
return Result_GetSoundbankFiles(false, "No Airline_Code found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
|
|
}
|
|
if (val2 != null && val2.isNotEmpty()) {
|
|
files.addAll(val2)
|
|
} else {
|
|
return Result_GetSoundbankFiles(false, "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}")
|
|
|
|
}
|
|
|
|
} else {
|
|
return Result_GetSoundbankFiles(false, "FLIGHT_NUMBER or AIRLINE_CODE variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"[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 {
|
|
return Result_GetSoundbankFiles(false, "PLATNOMOR variable has invalid format for value '$value' in messagebank id ${mb.ANN_ID}")
|
|
|
|
}
|
|
} else {
|
|
return Result_GetSoundbankFiles(false, "PLATNOMOR variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}")
|
|
|
|
}
|
|
}
|
|
|
|
"[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 {
|
|
return Result_GetSoundbankFiles(false, "Invalid soundbank file ${city.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
|
|
}
|
|
} else {
|
|
return Result_GetSoundbankFiles(false, "No City found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
|
|
}
|
|
}
|
|
} else {
|
|
return Result_GetSoundbankFiles(false, "CITY variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}")
|
|
|
|
}
|
|
}
|
|
|
|
"[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 {
|
|
return Result_GetSoundbankFiles(false, "Invalid soundbank file ${places.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
|
|
|
|
}
|
|
} else {
|
|
return Result_GetSoundbankFiles(false, "No Places found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
|
|
|
|
}
|
|
} else {
|
|
return Result_GetSoundbankFiles(false, "PLACES variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"[ETAD]" -> {
|
|
val values = variables["ETAD"].orEmpty().split(":").map { it.trim() }.filter { IsNumber(it) }
|
|
println("ETAD values: $values")
|
|
if (values.size == 2) {
|
|
if (IsNumber(values[0]) && IsNumber(values[1])) {
|
|
val _h = values[0].toInt()
|
|
val _m = values[1].toInt()
|
|
if (_h in 0..23 && _m in 0..59) {
|
|
// valid
|
|
val hh = Get_Soundbank_Hour_Minute(sb, _h)
|
|
val mm = Get_Soundbank_Hour_Minute(sb, _m)
|
|
if (hh.isNotEmpty() && mm.isNotEmpty()){
|
|
files.add(hh)
|
|
files.add(mm)
|
|
} else {
|
|
return Result_GetSoundbankFiles(false, "ETAD variable has invalid soundbank for hour or minute for tag $_tag in messagebank id ${mb.ANN_ID}")
|
|
|
|
}
|
|
|
|
} else {
|
|
return Result_GetSoundbankFiles(false, "ETAD variable has invalid hour or minute for tag $_tag in messagebank id ${mb.ANN_ID}, hour must be 0-23 and minute must be 0-59")
|
|
|
|
}
|
|
}
|
|
|
|
} else {
|
|
return Result_GetSoundbankFiles(false, "ETAD variable has invalid format for tag $_tag in messagebank id ${mb.ANN_ID}, expected format HH:MM")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"[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 {
|
|
return Result_GetSoundbankFiles(false, "Invalid soundbank file ${shalat.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
|
|
|
|
}
|
|
} else {
|
|
return Result_GetSoundbankFiles(false, "No Shalat found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
|
|
|
|
}
|
|
} else {
|
|
return Result_GetSoundbankFiles(false, "SHALAT variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"[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 {
|
|
return Result_GetSoundbankFiles(false, "BCB variable doesn't have valid soundbank for value '$value' in messagebank id ${mb.ANN_ID}")
|
|
|
|
}
|
|
}
|
|
|
|
"[GATENUMBER]" -> {
|
|
// gate number bisa angka saja, misalnya 1,2,3
|
|
// atau huruf dan angka, misalnya A1, B2, C3, 1A, 2B, 3C
|
|
val value = variables["GATECODE"].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 {
|
|
return Result_GetSoundbankFiles(false, "GATENUMBER variable doesn't have valid soundbank for value '$vv' in messagebank id ${mb.ANN_ID}")
|
|
|
|
}
|
|
}
|
|
} else {
|
|
return Result_GetSoundbankFiles(false, "GATENUMBER variable is empty after split for tag $_tag in messagebank id ${mb.ANN_ID}")
|
|
|
|
}
|
|
} else {
|
|
return Result_GetSoundbankFiles(false, "GATENUMBER variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"[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 {
|
|
return Result_GetSoundbankFiles(false, "Invalid soundbank file ${reason.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
|
|
|
|
}
|
|
} else {
|
|
return Result_GetSoundbankFiles(false, "No Reason found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
|
|
|
|
}
|
|
} else {
|
|
return Result_GetSoundbankFiles(false, "REASON variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"[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 {
|
|
return Result_GetSoundbankFiles(false, "Invalid soundbank file ${procedure.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
|
|
|
|
}
|
|
} else {
|
|
return Result_GetSoundbankFiles(false, "No Procedure found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
|
|
|
|
}
|
|
} else {
|
|
return Result_GetSoundbankFiles(false,"PROCEDURE variable is missing or empty for tag $_tag in messagebank id ${mb.ANN_ID}")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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 {
|
|
return Result_GetSoundbankFiles(false, "Invalid soundbank file ${phrase.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
|
|
|
|
}
|
|
} else {
|
|
return Result_GetSoundbankFiles(false, "No Phrase found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
// all tags processed, return files
|
|
return Result_GetSoundbankFiles(true, "Success", files)
|
|
}
|
|
|
|
fun Read_Queue_Paging() : Boolean{
|
|
db.queuepagingDB.Get()
|
|
val list = db.queuepagingDB.List
|
|
.filter { it.Type=="PAGING" }
|
|
|
|
list.forEach { qp ->
|
|
println("Processing $qp")
|
|
if (qp.BroadcastZones.isNotBlank()){
|
|
if (ValidFile(qp.Message)){
|
|
val zz = qp.BroadcastZones.split(";")
|
|
if (AllBroadcastZonesValid(zz)){
|
|
val ips = BroadcastZones_to_SoundChannel_IP(zz)
|
|
if (AllStreamerOutputIdle(ips)){
|
|
val afi = audioPlayer.LoadAudioFile(qp.Message)
|
|
if (afi.isValid()){
|
|
// file bisa di load, kirim ke masing-masing Streamer Output by IP address
|
|
Activate_Relays(zz)
|
|
ips.forEach { ip ->
|
|
// send byte array to streamer output
|
|
StreamerOutputs[ip]?.SendData(afi.bytes,
|
|
{
|
|
Deactivate_Relays(zz)
|
|
db.Add_Log("AAS", it) },
|
|
{
|
|
Deactivate_Relays(zz)
|
|
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())
|
|
db.queuepagingDB.Resort()
|
|
return true
|
|
} else {
|
|
// file tidak valid, delete from queue paging
|
|
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
|
db.queuepagingDB.Resort()
|
|
db.Add_Log(
|
|
"AAS",
|
|
"Cancelled paging message with index ${qp.index} due to invalid audio file"
|
|
)
|
|
}
|
|
} // kalau enggak semua streamer idle, skip dulu
|
|
} else {
|
|
// ada broadcast zone yang tidak valid, delete from queue paging
|
|
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
|
db.queuepagingDB.Resort()
|
|
db.Add_Log("AAS", "Cancelled paging message with index ${qp.index} due to invalid broadcast zone")
|
|
}
|
|
} else {
|
|
// file tidak valid, delete from queue paging
|
|
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
|
db.queuepagingDB.Resort()
|
|
db.Add_Log(
|
|
"AAS",
|
|
"Cancelled paging message with index ${qp.index} due to invalid audio file"
|
|
)
|
|
}
|
|
} else {
|
|
// invalid broadcast zone, delete from queue paging
|
|
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
|
db.queuepagingDB.Resort()
|
|
db.Add_Log("AAS", "Cancelled paging message with index ${qp.index} due to empty broadcast zone")
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
fun Read_Queue_Shalat() : Boolean{
|
|
db.queuepagingDB.Get()
|
|
val list = db.queuepagingDB.List
|
|
.filter { it.Type=="SHALAT" }
|
|
list.forEach { qp ->
|
|
println("Processing $qp")
|
|
if (qp.BroadcastZones.isNotBlank()){
|
|
val zz = qp.BroadcastZones.split(";")
|
|
if (AllBroadcastZonesValid(zz)){
|
|
val ann_id = Get_ANN_ID(qp.Message)
|
|
if (ann_id>0){
|
|
val ips = BroadcastZones_to_SoundChannel_IP(zz)
|
|
if (AllStreamerOutputIdle(ips)){
|
|
val mblist = Get_MessageBank_by_id(ann_id, listOf(Language.INDONESIA.name))
|
|
if (mblist.isNotEmpty()) {
|
|
mblist.forEach { mb ->
|
|
val result = Get_Soundbank_Files(mb, emptyMap())
|
|
if (result.success){
|
|
val listafi = mutableListOf<AudioFileInfo>()
|
|
result.files.forEach { filenya ->
|
|
val afi = contentCache.getAudioFile(filenya)
|
|
if (afi!=null && afi.isValid()){
|
|
listafi.add(afi)
|
|
} else {
|
|
val afi = audioPlayer.LoadAudioFile(filenya)
|
|
if (afi.isValid()) {
|
|
listafi.add(afi)
|
|
contentCache.addAudioFile(filenya, afi)
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
val targetfile = SoundbankResult_directory.resolve(
|
|
Make_WAV_FileName(
|
|
"Shalat",
|
|
""
|
|
)
|
|
).toString()
|
|
|
|
val result =audioPlayer.WavWriter(
|
|
listafi,
|
|
targetfile, true,
|
|
)
|
|
db.Add_Log("AAS", result.message)
|
|
if (result.success) {
|
|
// file siap broadcast
|
|
val targetafi = audioPlayer.LoadAudioFile(targetfile)
|
|
if (targetafi.isValid()) {
|
|
// activate relays from broadcast zone
|
|
Activate_Relays(zz)
|
|
ips.forEach { ip ->
|
|
// send byte array to streamer output
|
|
StreamerOutputs[ip]?.SendData(targetafi.bytes,
|
|
{
|
|
Deactivate_Relays(zz)
|
|
db.Add_Log("AAS", it) },
|
|
{
|
|
Deactivate_Relays(zz)
|
|
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())
|
|
db.queuepagingDB.Resort()
|
|
return true
|
|
} else {
|
|
db.Add_Log(
|
|
"AAS",
|
|
"Failed to load generated Shalat WAV file $targetfile"
|
|
)
|
|
}
|
|
}
|
|
} else{
|
|
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
|
db.queuepagingDB.Resort()
|
|
db.Add_Log("AAS", result.message)
|
|
}
|
|
}
|
|
|
|
|
|
} else {
|
|
// tidak ada messagebank dengan ann_id ini, delete from queue paging
|
|
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
|
db.queuepagingDB.Resort()
|
|
db.Add_Log(
|
|
"AAS",
|
|
"Cancelled Shalat message with index ${qp.index} due to ANN_ID $ann_id not found in Messagebank"
|
|
)
|
|
}
|
|
} // kalau enggak semua streamer idle, skip dulu
|
|
|
|
} else {
|
|
// invalid ann_id, delete from queue paging
|
|
db.queuepagingDB.DeleteByIndex(qp.index.toInt())
|
|
db.queuepagingDB.Resort()
|
|
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.queuepagingDB.Resort()
|
|
db.Add_Log("AAS", "Cancelled shalat 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.queuepagingDB.Resort()
|
|
db.Add_Log("AAS", "Cancelled shalat message with index ${qp.index} due to empty broadcast zone")
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
fun Read_Queue_Timer() : Boolean{
|
|
db.queuetableDB.Get()
|
|
val list = db.queuetableDB.List.filter { it.Type=="TIMER" }
|
|
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)
|
|
val ann_id = Get_ANN_ID(qa.Message)
|
|
if (ann_id>0){
|
|
if (AllStreamerOutputIdle(ips)){
|
|
val mblist = Get_MessageBank_by_id(ann_id, qa.Language.split(";"))
|
|
if (mblist.isNotEmpty()) {
|
|
mblist.forEach {
|
|
mb ->
|
|
val result = Get_Soundbank_Files(mb, emptyMap())
|
|
if (result.success){
|
|
val listafi = mutableListOf<AudioFileInfo>()
|
|
result.files.forEach { filenya ->
|
|
val afi = contentCache.getAudioFile(filenya)
|
|
if (afi!=null && afi.isValid()){
|
|
listafi.add(afi)
|
|
} else {
|
|
val afi = audioPlayer.LoadAudioFile(filenya)
|
|
if (afi.isValid()) {
|
|
listafi.add(afi)
|
|
contentCache.addAudioFile(filenya, afi)
|
|
}
|
|
}
|
|
|
|
}
|
|
val targetfile = SoundbankResult_directory.resolve(Make_WAV_FileName("Timer","")).toString()
|
|
val result = audioPlayer.WavWriter(listafi, targetfile, true)
|
|
db.Add_Log("AAS", result.message)
|
|
if (result.success){
|
|
// file siap broadcast
|
|
val targetafi = audioPlayer.LoadAudioFile(targetfile)
|
|
if (targetafi.isValid()) {
|
|
// activate relays from broadcast zone
|
|
Activate_Relays(zz)
|
|
ips.forEach { ip ->
|
|
// send byte array to streamer output
|
|
StreamerOutputs[ip]?.SendData(targetafi.bytes,
|
|
{
|
|
Deactivate_Relays(zz)
|
|
db.Add_Log("AAS", it) },
|
|
{
|
|
Deactivate_Relays(zz)
|
|
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.queuetableDB.Resort()
|
|
return true
|
|
}
|
|
} else{
|
|
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
|
db.queuetableDB.Resort()
|
|
db.Add_Log("AAS", result.message)
|
|
}
|
|
} else {
|
|
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
|
db.queuetableDB.Resort()
|
|
db.Add_Log("AAS", result.message)
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
// tidak ada messagebank dengan ann_id ini, delete from queue table
|
|
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
|
db.queuetableDB.Resort()
|
|
db.Add_Log(
|
|
"AAS",
|
|
"Cancelled TIMER with index ${qa.index} due to ANN_ID $ann_id not found in Messagebank"
|
|
)
|
|
}
|
|
} // kalau enggak semua streamer idle, skip dulu
|
|
|
|
} else {
|
|
// invalid ann_id, delete from queue table
|
|
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
|
db.queuetableDB.Resort()
|
|
db.Add_Log("AAS", "Cancelled TIMER message 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.queuetableDB.Resort()
|
|
db.Add_Log("AAS", "Cancelled TIMER 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.queuetableDB.Resort()
|
|
db.Add_Log("AAS", "Cancelled TIMER message with index ${qa.index} due to empty broadcast zone")
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
fun Activate_Relays(zz: List<String>){
|
|
zz.forEach { zonename ->
|
|
val bz = db.broadcastDB.List.find { it.description == zonename }
|
|
if (bz!=null){
|
|
val bc = Get_Barix_Connection_by_ZoneName(zonename)
|
|
if (bc!=null){
|
|
// ada barix connection
|
|
val relays = bz.bp.split(",")
|
|
.map { it.trim()}
|
|
.filter { it.isNotBlank() }
|
|
.filter { IsNumber(it) && it.toInt() in 1..8 }
|
|
.map{ it.toInt() }
|
|
if (relays.isNotEmpty()) bc.ActivateRelay(relays)
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
fun Deactivate_Relays(zz: List<String>){
|
|
zz.forEach { zonename ->
|
|
val bz = db.broadcastDB.List.find { it.description == zonename }
|
|
if (bz!=null){
|
|
val bc = Get_Barix_Connection_by_ZoneName(zonename)
|
|
bc?.DeactivateRelay()
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
fun Read_Queue_Soundbank() : Boolean{
|
|
db.queuetableDB.Get()
|
|
val list = db.queuetableDB.List.filter { it.Type=="SOUNDBANK" }
|
|
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)) {
|
|
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())
|
|
db.queuetableDB.Resort()
|
|
return@forEach
|
|
}
|
|
// sampe sini punya ann_id valid
|
|
|
|
val mblist = Get_MessageBank_by_id(ann_id, languages)
|
|
if (mblist.isNotEmpty()) {
|
|
val listafi = mutableListOf<AudioFileInfo>()
|
|
|
|
mblist.forEach { mb ->
|
|
val result = Get_Soundbank_Files(mb, variables ?: emptyMap())
|
|
if (result.success){
|
|
result.files.forEach { filenya ->
|
|
val afi = contentCache.getAudioFile(filenya)
|
|
if (afi!=null && afi.isValid()){
|
|
listafi.add(afi)
|
|
} else {
|
|
val afi = audioPlayer.LoadAudioFile(filenya)
|
|
if (afi.isValid()) {
|
|
listafi.add(afi)
|
|
contentCache.addAudioFile(filenya, afi)
|
|
}
|
|
}
|
|
|
|
}
|
|
} else {
|
|
db.Add_Log("AAS", result.message)
|
|
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
|
db.queuetableDB.Resort()
|
|
}
|
|
|
|
}
|
|
|
|
if (listafi.isNotEmpty()){
|
|
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
|
db.queuetableDB.Resort()
|
|
// bikin nama file ada postifx nya dari SB_TAGS, tapi spasi diganti underscore
|
|
// dan titik dua diganti dash
|
|
val postfix = qa.SB_TAGS.replace(" ","_").replace(":","-")
|
|
val targetfile = SoundbankResult_directory.resolve(Make_WAV_FileName("${qa.Source}_${qa.Type}",postfix)).toString()
|
|
//println("Writing to target WAV file: $targetfile")
|
|
val result = audioPlayer.WavWriter(listafi, targetfile, true)
|
|
if (result.success){
|
|
// file siap broadcast
|
|
//println("Successfully wrote WAV file: $targetfile")
|
|
val targetafi = audioPlayer.LoadAudioFile(targetfile)
|
|
if (targetafi.isValid()) {
|
|
// activate relays from broadcast zone
|
|
Activate_Relays(zz)
|
|
ips.forEach { ip ->
|
|
// send byte array to streamer output
|
|
StreamerOutputs[ip]?.SendData(targetafi.bytes,
|
|
{
|
|
Deactivate_Relays(zz)
|
|
db.Add_Log("AAS", it)
|
|
|
|
}, {
|
|
Deactivate_Relays(zz)
|
|
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)
|
|
|
|
return true
|
|
}
|
|
} else {
|
|
println("Failed to write WAV file: ${result.message}")
|
|
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
|
db.queuetableDB.Resort()
|
|
db.Add_Log("AAS", result.message)
|
|
}
|
|
|
|
}
|
|
} else {
|
|
// tidak ada messagebank dengan ann_id ini, delete from queue table
|
|
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
|
db.queuetableDB.Resort()
|
|
db.Add_Log(
|
|
"AAS",
|
|
"Cancelled SOUNDBANK message with index ${qa.index} due to ANN_ID $ann_id not found in Messagebank"
|
|
)
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
// ada broadcast zone yang tidak valid, delete from queue table
|
|
db.queuetableDB.DeleteByIndex(qa.index.toInt())
|
|
db.queuetableDB.Resort()
|
|
db.Add_Log("AAS", "Cancelled SOUNDBANK 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.queuetableDB.Resort()
|
|
db.Add_Log("AAS", "Cancelled SOUNDBANK message with index ${qa.index} due to empty broadcast zone")
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
* 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
|
|
|
|
}
|
|
}
|
|
} |