Commit 25/09/2025

This commit is contained in:
rdkartono
2025-09-25 11:28:52 +07:00
parent 7db10bd45a
commit e18a08ab6a
3 changed files with 261 additions and 72 deletions

View File

@@ -2,17 +2,20 @@ import audio.AudioPlayer
import barix.BarixConnection import barix.BarixConnection
import barix.TCP_Barix_Command_Server import barix.TCP_Barix_Command_Server
import codes.Somecodes.Companion.Get_ANN_ID import codes.Somecodes.Companion.Get_ANN_ID
import codes.Somecodes.Companion.IsAlphabethic
import codes.Somecodes.Companion.IsNumber
import codes.Somecodes.Companion.ValidFile import codes.Somecodes.Companion.ValidFile
import codes.Somecodes.Companion.ValidString
import codes.Somecodes.Companion.dateformat1 import codes.Somecodes.Companion.dateformat1
import codes.Somecodes.Companion.timeformat2 import codes.Somecodes.Companion.timeformat2
import com.sun.jna.Platform import com.sun.jna.Platform
import content.Category
import content.ContentCache import content.ContentCache
import content.Language import content.Language
import content.ScheduleDay import content.ScheduleDay
import content.VoiceType import content.VoiceType
import database.MariaDB import database.MariaDB
import database.Messagebank import database.Messagebank
import database.QueuePaging
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@@ -24,6 +27,7 @@ import web.WebApp
import java.time.DayOfWeek import java.time.DayOfWeek
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalTime import java.time.LocalTime
import java.util.function.Consumer
import kotlin.concurrent.fixedRateTimer import kotlin.concurrent.fixedRateTimer
@@ -52,12 +56,12 @@ fun main() {
if (bz.isNotEmpty()) { if (bz.isNotEmpty()) {
val validchannels = bz val validchannels = bz
// check apakah tiap zone ada di database broadcast zones // check apakah tiap zone ada di database broadcast zones
.filter { .filter { z1 ->
z1 -> db.BroadcastZoneList.find { z2 -> z2.SoundChannel==z1 } != null db.BroadcastZoneList.find { z2 -> z2.SoundChannel == z1 } != null
} }
// check apakah tiap zone ada di SoundChannelList dan Online // check apakah tiap zone ada di SoundChannelList dan Online
.filter { .filter { z3 ->
z3 -> StreamerOutputs.any { sc -> sc.value.channel==z3 && sc.value.isOnline() } StreamerOutputs.any { sc -> sc.value.channel == z3 && sc.value.isOnline() }
} }
// kalau jumlah valid channel sama dengan jumlah broadcast zone, berarti semua valid // kalau jumlah valid channel sama dengan jumlah broadcast zone, berarti semua valid
@@ -73,8 +77,8 @@ fun main() {
*/ */
fun AllBroadcastZoneIdle(bz: List<String>): Boolean { fun AllBroadcastZoneIdle(bz: List<String>): Boolean {
if (bz.isNotEmpty()) { if (bz.isNotEmpty()) {
return bz.all { return bz.all { z1 ->
z1 -> StreamerOutputs.any { sc -> sc.value.channel==z1 && sc.value.isIdle() } StreamerOutputs.any { sc -> sc.value.channel == z1 && sc.value.isIdle() }
} }
} }
return false return false
@@ -96,67 +100,234 @@ fun main() {
/** /**
* Fungsi untuk ambil messagebank berdasarkan ANN_ID, diurutkan berdasarkan urutan bahasa di urutan_bahasa * Fungsi untuk ambil messagebank berdasarkan ANN_ID, diurutkan berdasarkan urutan bahasa di urutan_bahasa
* @param id ANN_ID dari messagebank * @param id ANN_ID dari messagebank
* @param languages List of language yang diinginkan, default urutan_bahasa
* @return List of Messagebank * @return List of Messagebank
*/ */
fun Get_MessageBank_by_id(id: Int) : ArrayList<Messagebank>{ fun Get_MessageBank_by_id(id: Int, languages: List<String> = urutan_bahasa): ArrayList<Messagebank> {
val mb_list = ArrayList<Messagebank>() val mb_list = ArrayList<Messagebank>()
urutan_bahasa.forEach { languages.forEach { lang ->
lang -> db.MessagebankList.find { mb -> mb.ANN_ID==id.toUInt() && mb.Language==lang && mb.Voice_Type==selected_voice }?.let { db.MessagebankList.find { mb -> mb.ANN_ID == id.toUInt() && mb.Language == lang && mb.Voice_Type == selected_voice }
?.let {
mb_list.add(it) mb_list.add(it)
} }
} }
return mb_list return mb_list
} }
/**
* 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 fail, returns error message
*/
fun Get_Soundbank_Files(
mb: Messagebank,
variables: Map<String, String>,
cbOK: Consumer<List<String>>,
cbFail: Consumer<String>
) {
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.SoundbankList
.filter { it.VoiceType == mb.Voice_Type }
.filter { it.Language == mb.Language }
if (sb.isEmpty()) {
cbFail.accept("No soundbank found for voice type ${mb.Voice_Type} and language ${mb.Language}")
return
}
val files = mutableListOf<String>()
tags.forEach { tag ->
val _tag = tag.trim()
when (_tag) {
"[AIRPLANE_NAME]" -> {
}
"[FLIGHT_NUMBER]" -> {
}
"[PLATNOMOR]" -> {
}
"[CITY]" -> {
}
"[PLACES]" -> {
}
"[ETAD]" -> {
}
"[SHALAT]" -> {
}
"[BCB]" -> {
}
"[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 {
if (IsNumber(it)){
// gate number hanya angka
} else {
// gate number gabungan huruf dan angka
val regex = Regex("([A-Z])?(\\d+)([A-Z])?")
}
}
} 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 reason = sb.firstOrNull { it.Category == Category.Reason.name && it.TAG == _tag }
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
}
}
"[PROCEDURE]" -> {
val procedure = sb.firstOrNull { it.Category == Category.Procedure.name && it.TAG == _tag }
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 -> {
// 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
}
}
}
}
}
jobloop@ while (isActive) { jobloop@ while (isActive) {
delay(1000) delay(1000)
// prioritas 1 , habisin queue paging // prioritas 1 , habisin queue paging
for(it in db.Read_Queue_Paging()){ for (qp in db.Read_Queue_Paging()) {
if (it.BroadcastZones.isNotBlank()){ if (qp.BroadcastZones.isNotBlank()) {
val zz = it.BroadcastZones.split(";") val zz = qp.BroadcastZones.split(";")
if (AllBroadcastZonesValid(zz)) { if (AllBroadcastZonesValid(zz)) {
if (AllBroadcastZoneIdle(zz)) { if (AllBroadcastZoneIdle(zz)) {
if (it.Source=="PAGING"){ if (qp.Source == "PAGING") {
// nama file ada di Message // nama file ada di Message
if (ValidFile(it.Message)){ if (ValidFile(qp.Message)) {
val afi = audioPlayer.LoadAudioFile(it.Message) val afi = audioPlayer.LoadAudioFile(qp.Message)
zz.forEach { zz.forEach { z1 ->
z1 -> StreamerOutputs.values.find { it.channel==z1 }?.SendData(afi.bytes, {db.Add_Log("AAS", it) }, {db.Add_Log("AAS", it)} ) 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 '${it.Message}' to zones: ${it.BroadcastZones}" val logmessage =
"Broadcast started PAGING with Filename '${qp.Message}' to zones: ${qp.BroadcastZones}"
Logger.info { logmessage } Logger.info { logmessage }
db.Add_Log("AAS", logmessage) db.Add_Log("AAS", logmessage)
db.Delete_Queue_Paging_by_index(it.index) db.Delete_Queue_Paging_by_index(qp.index)
continue@jobloop continue@jobloop
} else { } else {
// file tidak valid, delete from queue paging // file tidak valid, delete from queue paging
db.Delete_Queue_Paging_by_index(it.index) db.Delete_Queue_Paging_by_index(qp.index)
db.Add_Log("AAS", "Cancelled paging message with index ${it.index} due to invalid audio file" ) db.Add_Log(
"AAS",
"Cancelled paging message with index ${qp.index} due to invalid audio file"
)
} }
} else if (it.Source=="SHALAT"){ } else if (qp.Source == "SHALAT") {
val ann_id = Get_ANN_ID(it.Message) val ann_id = Get_ANN_ID(qp.Message)
if (ann_id > 0) { if (ann_id > 0) {
Get_MessageBank_by_id(ann_id).forEach { // shalat, ambil messagebank berdasarkan ann_id dengan bahasa Indonesia saja
// cari tags nya, create content nya, broadcast ke semua zone Get_MessageBank_by_id(ann_id, listOf(Language.INDONESIA.name)).let { mblist ->
if (mblist.isNotEmpty()) {
//TODO find soundbank
} else {
// tidak ada messagebank dengan ann_id ini, delete from queue paging
db.Delete_Queue_Paging_by_index(qp.index)
db.Add_Log(
"AAS",
"Cancelled Shalat message with index ${qp.index} due to ANN_ID $ann_id not found in Messagebank"
)
}
} }
} else { } else {
// invalid ann_id, delete from queue paging // invalid ann_id, delete from queue paging
db.Delete_Queue_Paging_by_index(it.index) db.Delete_Queue_Paging_by_index(qp.index)
db.Add_Log("AAS", "Cancelled Shalat message with index ${it.index} due to invalid ANN_ID" ) db.Add_Log(
"AAS",
"Cancelled Shalat message with index ${qp.index} due to invalid ANN_ID"
)
} }
} }
} }
} else { } else {
// ada broadcast zone yang tidak valid, delete from queue paging // ada broadcast zone yang tidak valid, delete from queue paging
db.Delete_Queue_Paging_by_index(it.index) db.Delete_Queue_Paging_by_index(qp.index)
db.Add_Log("AAS", "Cancelled paging message with index ${it.index} due to invalid broadcast zone" ) db.Add_Log(
"AAS",
"Cancelled paging message with index ${qp.index} due to invalid broadcast zone"
)
} }
} else { } else {
// invalid broadcast zone, delete from queue paging // invalid broadcast zone, delete from queue paging
db.Delete_Queue_Paging_by_index(it.index) db.Delete_Queue_Paging_by_index(qp.index)
db.Add_Log("AAS", "Cancelled paging message with index ${it.index} due to empty broadcast zone") db.Add_Log("AAS", "Cancelled paging message with index ${qp.index} due to empty broadcast zone")
} }
} }
@@ -177,7 +348,10 @@ fun main() {
} else { } else {
// ada broadcast zone yang tidak valid, delete from queue table // ada broadcast zone yang tidak valid, delete from queue table
db.Delete_Queue_Table_by_index(it.index) db.Delete_Queue_Table_by_index(it.index)
db.Add_Log("AAS", "Cancelled table message with index ${it.index} due to invalid broadcast zone") db.Add_Log(
"AAS",
"Cancelled table message with index ${it.index} due to invalid broadcast zone"
)
} }
} else { } else {
// invalid broadcast zone, delete from queue table // invalid broadcast zone, delete from queue table
@@ -239,7 +413,6 @@ fun main() {
} }
} }
} }

View File

@@ -38,6 +38,20 @@ class Somecodes {
// regex for getting ann_id from Message, which is the number inside [] // regex for getting ann_id from Message, which is the number inside []
private val ann_id_regex = Regex("\\[(\\d+)]") private val ann_id_regex = Regex("\\[(\\d+)]")
/**
* Check if a string is a valid number.
*/
fun IsNumber(value: String) : Boolean {
return value.toIntOrNull() != null
}
/**
* Check if a string is alphabetic (contains only letters).
*/
fun IsAlphabethic(value: String) : Boolean {
return value.all { it.isLetter() }
}
/** /**
* Extract ANN ID from a message string. * Extract ANN ID from a message string.
* The ANN ID is expected to be a number enclosed in square brackets (e.g., "[123]"). * The ANN ID is expected to be a number enclosed in square brackets (e.g., "[123]").

View File

@@ -11,5 +11,7 @@ enum class Category(name: String) {
PlatNomor("PlatNomor"), PlatNomor("PlatNomor"),
Shalat("Shalat"), Shalat("Shalat"),
Year("Year"), Year("Year"),
Birthday("Birthday"); Birthday("Birthday"),
Reason("Reason"),
Procedure("Procedure");
} }