import audio.AudioPlayer import audio.ContentCache import audio.UDPReceiver import barix.BarixConnection import barix.TCP_Barix_Command_Server import codes.Somecodes import com.sun.jna.Platform import commandServer.TCP_Android_Command_Server import content.Language import content.VoiceType import database.Log import database.MariaDB import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import org.tinylog.Logger import oshi.util.GlobalConfig import web.WebApp import java.nio.file.Files import kotlin.concurrent.fixedRateTimer import kotlin.io.path.absolutePathString lateinit var db: MariaDB lateinit var audioPlayer: AudioPlayer val StreamerOutputs: MutableMap = HashMap() lateinit var udpreceiver: UDPReceiver const val version = "0.0.2 (23/09/2025)" // AAS 64 channels const val max_channel = 64 // dipakai untuk pilih voice type, bisa diganti via web nanti var selected_voice = VoiceType.VOICE_1.name // dipakai untuk ambil messagebank berdasarkan id val urutan_bahasa = listOf( Language.INDONESIA.name, Language.LOCAL.name, Language.ENGLISH.name, Language.CHINESE.name, Language.JAPANESE.name, Language.ARABIC.name ) val contentCache = ContentCache() /** * Create necessary folders if not exist */ fun folder_preparation(){ Files.createDirectories(Somecodes.SoundbankResult_directory) Files.createDirectories(Somecodes.PagingResult_directory) Files.createDirectories(Somecodes.Soundbank_directory) Somecodes.Soundbank_Languages_directory.forEach { Files.createDirectories(it) } } /** * Extract necessary wav files from classpath to soundbank directory * and Load them */ fun files_preparation(){ val list = listOf("chimeup.wav", "chimedown.wav", "silence1s.wav", "silencehalf.wav") list.forEach { Somecodes.ExtractFilesFromClassPath("/$it", Somecodes.Soundbank_directory) val pp = Somecodes.Soundbank_directory.resolve(it) if (Files.isRegularFile(pp)){ val afi = audioPlayer.LoadAudioFile(pp.absolutePathString()) if (afi.isValid()){ Logger.info { "Common audio $it loaded from ${pp.toAbsolutePath()}" } val key = it.substring(0, it.length - 4) // buang .wav contentCache.addAudioFile(key, afi) } else { Logger.error { "Failed to load common audio $it from ${pp.toAbsolutePath()}" } } } else { Logger.error { "Common audio $it not found at ${pp.toAbsolutePath()}" } } } } // Application start here fun main() { if (Platform.isWindows()) { // supaya OSHI bisa mendapatkan CPU usage di Windows seperti di Task Manager GlobalConfig.set(GlobalConfig.OSHI_OS_WINDOWS_CPU_UTILITY, true) } Logger.info { "Starting AAS New Generation version $version" } folder_preparation() audioPlayer = AudioPlayer(44100) // 44100 Hz sampling rate audioPlayer.InitAudio(1) files_preparation() db = MariaDB() val subcode01 = MainExtension01() // Coroutine untuk cek Paging Queue dan AAS Queue setiap detik CoroutineScope(Dispatchers.Default).launch { while (isActive) { delay(1000) // prioritas 1 , habisin queue paging subcode01.Read_Queue_Paging() // prioritas 2, habisin queue table subcode01.Read_Queue_Table() } } // Coroutine untuk cek Schedulebank tiap menit saat detik 00 CoroutineScope(Dispatchers.Default).launch { while (isActive) { delay(1000) subcode01.Read_Schedule_Table() } } val web = WebApp( 3030, listOf( Pair("admin", "password"), Pair("user", "password") )) web.Start() udpreceiver = UDPReceiver() if (udpreceiver.Start()) { Logger.info { "UDP Receiver started on port 5002" } } else { Logger.error { "Failed to start UDP Receiver on port 5002" } } val androidserver = TCP_Android_Command_Server() androidserver.StartTcpServer(5003){ Logger.info { it } db.logDB.Add(Log.NewLog("ANDROID", it)) } val barixserver = TCP_Barix_Command_Server() barixserver.StartTcpServer { cmd -> //Logger.info { cmd } val _streamer = StreamerOutputs[cmd.ipaddress] val _sc = db.soundchannelDB.List.find { it.ip == cmd.ipaddress } if (_streamer == null) { // belum create BarixConnection untuk ipaddress ini Logger.info { "New Streamer Output connection from ${cmd.ipaddress}" } if (_sc != null) { val _bc = BarixConnection(_sc.index, _sc.channel, cmd.ipaddress) _bc.vu = cmd.vu _bc.bufferRemain = cmd.buffremain _bc.statusData = cmd.statusdata StreamerOutputs[cmd.ipaddress] = _bc Logger.info { "Created new Streamer Output for channel ${_sc.channel} with IP ${cmd.ipaddress}" } } else Logger.warn { "soundChannelDB doesn't have soundchannel with IP ${cmd.ipaddress}" } } else { // sudah ada, update data if (_sc != null && _sc.channel != _streamer.channel) { _streamer.channel = _sc.channel } _streamer.vu = cmd.vu _streamer.bufferRemain = cmd.buffremain _streamer.statusData = cmd.statusdata } } val onlinechecker = fixedRateTimer(name = "onlinecheck", initialDelay = 1000, period = 1000) { // cek setiap 1 detik, decrement online counter semua BarixConnection StreamerOutputs.values.forEach { it.decrementOnlineCounter() } } // shutdown hook Runtime.getRuntime().addShutdownHook(Thread { Logger.info { "Shutdown hook called, stopping services..." } barixserver.StopTcpCommand() androidserver.StopTcpCommand() onlinechecker.cancel() web.Stop() udpreceiver.Stop() audioPlayer.Close() db.close() Logger.info { "All services stopped, exiting application." } }) }