import audio.AudioPlayer import audio.ContentCache import audio.TCPReceiver import audio.UDPReceiver import barix.BarixConnection import barix.TCP_Barix_Command_Server import codes.Somecodes import codes.configFile import codes.configKeys import com.sun.jna.Native import com.sun.jna.Platform import commandServer.TCP_Android_Command_Server import content.Category 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 org.tinylog.provider.ProviderRegistry import oshi.util.GlobalConfig import securedonglex.DongleChecker import web.WebApp import java.io.File import java.nio.file.Files import java.nio.file.Paths import kotlin.concurrent.fixedRateTimer import kotlin.io.path.absolutePathString import kotlin.io.path.exists import kotlin.system.exitProcess lateinit var db: MariaDB lateinit var audioPlayer: AudioPlayer val StreamerOutputs: MutableMap = HashMap() lateinit var udpreceiver: UDPReceiver lateinit var tcpreceiver: TCPReceiver const val version = "0.0.23 (29/01/2026)" // AAS 64 channels const val max_channel = 64 // 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(){ // sementara diset begini, nanti pake config file //Somecodes.Soundbank_directory = Paths.get("c:\\soundbank") Somecodes.Soundbank_directory = Paths.get(config.Get(configKeys.SOUNDBANK_DIRECTORY.key)) Somecodes.SoundbankResult_directory = Somecodes.Soundbank_directory.resolve("SoundbankResult") Somecodes.PagingResult_directory = Somecodes.Soundbank_directory.resolve("PagingResult") Files.createDirectories(Somecodes.SoundbankResult_directory) Files.createDirectories(Somecodes.PagingResult_directory) Files.createDirectories(Somecodes.Soundbank_directory) Language.entries.forEach { language -> VoiceType.entries.forEach { voice -> Category.entries.forEach { category -> Files.createDirectories(Somecodes.SoundbankDirectory(language, voice, category) ) } } } } private fun Extract_Libraries() { // extract from source root folder to current user dir val libs = listOf("bass", "bassenc", "bassenc_mp3","bassenc_ogg","bassenc_opus","bassmix","bassopus","sdx") try{ val targetfolder = File(Somecodes.current_directory) Logger.info {"target to extract libraries : $targetfolder"} libs.forEach { ff -> val x = Native.extractFromResourcePath(ff) val y = System.mapLibraryName(ff) val z = x.copyTo(targetfolder.resolve(y),overwrite = true) Logger.info {"Extracted libraries : $z"} } } catch (e : Exception){ Logger.error { "Error extracting libraries, msg : ${e.message}" } } } /** * 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 { ff -> Somecodes.extractWav(ff) val fd = Somecodes.Soundbank_directory.resolve(ff) if (fd.exists()){ Logger.info{"File ${fd.absolutePathString()} found, processing to content cache" } val afi = audioPlayer.LoadAudioFile(fd.absolutePathString()) if (afi.isValid()){ val key = Somecodes.FilenameWithoutExtension(fd.toFile()) contentCache.addAudioFile(Somecodes.FilenameWithoutExtension(fd.toFile()), afi) Logger.info{"Loaded file ${fd.absolutePathString()} to content cache as $key" } } else { Logger.error{"Failed to load file ${fd.absolutePathString()} to content cache" } } } else { Logger.error{"File ${fd.absolutePathString()} not found after extraction" } } } } lateinit var config : configFile //val sdx = DongleChecker() // 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" } config = configFile() folder_preparation() Extract_Libraries() // if (!sdx.CheckDongle()){ // Logger.error { "Dongle check failed. Application will exit." } // exitProcess(1) // } else { // sdx.startChecking { // Logger.error { "Dongle removed. Application will exit." } // exitProcess(1) // } // } 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 shalat subcode01.Read_Queue_Shalat() // prioritas 3, habisin queue timer subcode01.Read_Queue_Timer() // prioritas 4, habisin queue soundbank subcode01.Read_Queue_Soundbank() } } // Coroutine untuk cek Schedulebank tiap menit saat detik 00 CoroutineScope(Dispatchers.Default).launch { while (isActive) { delay(1000) subcode01.Read_Schedule_Table() } } val web = WebApp( config.Get(configKeys.WEBAPP_PORT.key).toInt(), listOf( Pair(config.Get(configKeys.WEBAPP_ADMIN_USERNAME.key), config.Get(configKeys.WEBAPP_ADMIN_PASSWORD.key)), Pair(config.Get(configKeys.WEBAPP_VIEWER_USERNAME.key), config.Get(configKeys.WEBAPP_VIEWER_PASSWORD.key)) ), config) 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" } } tcpreceiver = TCPReceiver() if (tcpreceiver.Start()) { Logger.info { "TCP Receiver started on port 5002" } } else { Logger.error { "Failed to start TCP 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 -> val _tcp = barixserver.getSocket(cmd.ipaddress) 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) // cmd.vu 0 - 32767, kita convert ke 0 - 100 _bc.vu = ((1.0 * cmd.vu / 32767.0)* 100.0).toInt() _bc.bufferRemain = cmd.buffremain _bc.statusData = cmd.statusdata _bc.commandsocket = _tcp StreamerOutputs[cmd.ipaddress] = _bc Logger.info { "Created new Streamer Output for channel ${_sc.channel} with IP ${cmd.ipaddress}" } } } else { // sudah ada, update data if (_sc != null && _sc.channel != _streamer.channel) { _streamer.channel = _sc.channel } // cmd.vu 0 - 32767, kita convert ke 0 - 100 _streamer.vu = ((1.0 * cmd.vu / 32767.0)* 100.0).toInt() _streamer.bufferRemain = cmd.buffremain _streamer.statusData = cmd.statusdata // cek apakah koneksi TCP nya ganti if (_streamer.commandsocket == null) { _streamer.commandsocket = _tcp } else { if (_streamer.commandsocket != _tcp) { // ganti koneksi try { _streamer.commandsocket?.close() } catch (ex: Exception) { Logger.error(ex) { "Error closing previous TCP command socket for ${cmd.ipaddress}" } } _streamer.commandsocket = _tcp } } } } val onlinechecker = fixedRateTimer(name = "onlinecheck", initialDelay = 1000, period = 1000) { // cek setiap 1 detik, decrement online counter semua BarixConnection StreamerOutputs.values.forEach { it.decrementOnlineCounter() } } db.Add_Log("AAS"," Application started") // shutdown hook Runtime.getRuntime().addShutdownHook(Thread ({ db.Add_Log("AAS"," Application stopping") Logger.info { "Shutdown hook called, stopping services..." } barixserver.StopTcpCommand() androidserver.StopTcpCommand() onlinechecker.cancel() web.Stop() udpreceiver.Stop() tcpreceiver.Stop() StreamerOutputs.values.forEach { it.close() } audioPlayer.Close() db.close() //sdx.stopChecking() Logger.info { "All services stopped, exiting application." } ProviderRegistry.getLoggingProvider().shutdown() },"ShutdownHook") ) }