Commit 25/09/2025
This commit is contained in:
241
src/Main.kt
241
src/Main.kt
@@ -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() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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]").
|
||||||
|
|||||||
@@ -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");
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user