From c01c4e39fd28255fb3479d1e91460ebe13c73bb6 Mon Sep 17 00:00:00 2001 From: rdkartono Date: Wed, 3 Sep 2025 16:01:18 +0700 Subject: [PATCH] commit 03/09/2025 --- src/Main.kt | 73 ++++++++++-- .../TCP_Android_Command_Server.kt | 105 ++++++++++++++++++ src/commandServer/TCP_PC_Command_Server.kt | 101 +++++++++++++++++ src/database/MariaDB.kt | 4 +- src/web/WebApp.kt | 59 +++++----- 5 files changed, 298 insertions(+), 44 deletions(-) create mode 100644 src/commandServer/TCP_Android_Command_Server.kt create mode 100644 src/commandServer/TCP_PC_Command_Server.kt diff --git a/src/Main.kt b/src/Main.kt index 856a9f3..3725bb1 100644 --- a/src/Main.kt +++ b/src/Main.kt @@ -1,6 +1,9 @@ import audio.AudioPlayer +import codes.Somecodes.Companion.dateformat1 +import codes.Somecodes.Companion.timeformat2 import com.sun.jna.Platform import content.ContentCache +import content.ScheduleDay import database.MariaDB import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -10,6 +13,9 @@ import kotlinx.coroutines.launch import org.tinylog.Logger import oshi.util.GlobalConfig import web.WebApp +import java.time.DayOfWeek +import java.time.LocalDate +import java.time.LocalTime fun main() { @@ -22,6 +28,7 @@ fun main() { audioPlayer.InitAudio(1) val content = ContentCache() val db = MariaDB() + // Coroutine untuk cek Paging Queue dan AAS Queue setiap detik CoroutineScope(Dispatchers.Default).launch { while (isActive) { delay(1000) @@ -38,16 +45,12 @@ fun main() { } else { // ada broadcast zone yang tidak valid, delete from queue paging db.Delete_Queue_Paging_by_index(it.index) - //TODO check log message yang benar - 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 ${it.index} due to invalid broadcast zone" ) } } else { // invalid broadcast zone, delete from queue paging db.Delete_Queue_Paging_by_index(it.index) - // TODO check log message yang benar db.Add_Log("AAS", "Cancelled paging message with index ${it.index} due to empty broadcast zone") } } @@ -62,22 +65,72 @@ fun main() { } else { // ada broadcast zone yang tidak valid, delete from queue table db.Delete_Queue_Table_by_index(it.index) - //TODO check log message yang benar - 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 { // invalid broadcast zone, delete from queue table db.Delete_Queue_Table_by_index(it.index) - // TODO check log message yang benar db.Add_Log("AAS", "Cancelled table message with index ${it.index} due to empty broadcast zone") } } } } + // Coroutine untuk cek Schedulebank tiap menit saat detik 00 + CoroutineScope(Dispatchers.Default).launch { + while (isActive) { + delay(1000) + val localtime = LocalTime.now() + // detik harus 00 + if (localtime.second!=0) continue + val timestring = timeformat2.format(localtime) + val sch = db.SchedulebankList.filter{ + it.Time==timestring && it.Enable + } + // tidak ada schedule dengan time sekarang dan enable=true + if (sch.isEmpty()) continue + + val localdate = LocalDate.now() + val ddmmyyyy = dateformat1.format(localdate) + // check special date dulu + val specialdate = sch.find { + it.Day==ddmmyyyy + } + if (specialdate!=null) { + // TODO Masukin ke queue table sebagai schedule special date + + } + // 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) { + // TODO Masukin ke queue table sebagai schedule weekly + + } + // check daily schedule + val daily = sch.find { + it.Day == ScheduleDay.Everyday.name + } + if (daily!=null) { + // TODO Masukin ke queue table sebagai schedule daily + + } + + + + } + } + val web = WebApp( 3030, listOf( diff --git a/src/commandServer/TCP_Android_Command_Server.kt b/src/commandServer/TCP_Android_Command_Server.kt new file mode 100644 index 0000000..16989c2 --- /dev/null +++ b/src/commandServer/TCP_Android_Command_Server.kt @@ -0,0 +1,105 @@ +package commandServer + +import codes.Somecodes.Companion.ValidString +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import org.tinylog.Logger +import java.net.ServerSocket +import java.net.Socket +import java.util.function.Consumer + +@Suppress("unused") +class TCP_Android_Command_Server { + private var tcpserver: ServerSocket? = null + private var job: Job? = null + private val socketMap = mutableMapOf() + + /** + * Start TCP Command Server + * @param port The port to listen on, default is 5003 + * @param cb The callback function to handle incoming messages + * @return true if successful + */ + fun StartTcpServer(port: Int = 5003, cb: Consumer): Boolean { + try { + val tcp = ServerSocket(port) + tcpserver = tcp + job = CoroutineScope(Dispatchers.IO).launch { + Logger.info { "TCP server started" } + while (isActive) { + if (tcpserver?.isClosed == true) break + try { + tcpserver?.accept().use { socket -> + { + CoroutineScope(Dispatchers.Main).launch { + if (socket != null) { + val key : String = socket.inetAddress.hostAddress+":"+socket.port + socketMap[key] = socket + Logger.info { "Start communicating with $key" } + socket.getInputStream().use { din -> + { + while (isActive) { + if (din.available()>0){ + val bb = ByteArray(din.available()) + din.read(bb) + // B4A format, 4 bytes di depan adalah size + val str = String(bb,4,bb.size-4) + str.split("@").forEach { + if (ValidString(it)){ + cb.accept(it) + } + } + } + } + } + } + Logger.info { "Finished communicating with $key" } + socketMap.remove(key) + } + + } + } + } + + } catch (ex: Exception) { + Logger.error { "Failed accepting TCP Socket, Message : ${ex.message}" } + } + + } + Logger.info { "TCP server stopped" } + } + return true + } catch (e: Exception) { + Logger.error { "Failed to StartTcpServer, Message : ${e.message}" } + } + return false + } + + /** + * Stop TCP Command Server + * @return true if succesful + */ + fun StopTcpCommand(): Boolean { + try { + tcpserver?.close() + runBlocking { + socketMap.values.forEach { + it.close() + } + socketMap.clear() + job?.join() + } + Logger.info { "StopTcpCommand success" } + return true + } catch (e: Exception) { + Logger.error { "Failed to StopTcpServer, Message : ${e.message}" } + } finally { + tcpserver = null + } + return false + } +} \ No newline at end of file diff --git a/src/commandServer/TCP_PC_Command_Server.kt b/src/commandServer/TCP_PC_Command_Server.kt new file mode 100644 index 0000000..9e865ca --- /dev/null +++ b/src/commandServer/TCP_PC_Command_Server.kt @@ -0,0 +1,101 @@ +package commandServer + +import codes.Somecodes.Companion.ValidString +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import org.tinylog.Logger +import java.net.ServerSocket +import java.net.Socket +import java.util.function.Consumer + +@Suppress("unused") +class TCP_PC_Command_Server { + private var tcpserver: ServerSocket? = null + private var job: Job? = null + private val socketMap = mutableMapOf() + + /** + * Start TCP Command Server + * @param port The port number to listen on (default is 5000) + * @param cb A callback function that will be called when a valid command is received + * @return true if successful + */ + fun StartTcpServer(port: Int = 5000, cb: Consumer): Boolean { + try { + val tcp = ServerSocket(port) + tcpserver = tcp + job = CoroutineScope(Dispatchers.IO).launch { + Logger.info { "TCP server started" } + while (isActive) { + if (tcpserver?.isClosed == true) break + try { + tcpserver?.accept().use { socket -> + { + CoroutineScope(Dispatchers.Main).launch { + if (socket != null) { + val key : String = socket.inetAddress.hostAddress+":"+socket.port + socketMap[key] = socket + Logger.info { "Start communicating with $key" } + socket.getInputStream().use { din -> + { + while (isActive) { + if (din.available()>0){ + val bb = ByteArray(din.available()) + din.read(bb) + // B4A format, 4 bytes di depan adalah size + val str = String(bb) + if (ValidString(str)) cb.accept(str) + } + } + } + } + Logger.info { "Finished communicating with $key" } + socketMap.remove(key) + } + + } + } + } + + } catch (ex: Exception) { + Logger.error { "Failed accepting TCP Socket, Message : ${ex.message}" } + } + + } + Logger.info { "TCP server stopped" } + } + return true + } catch (e: Exception) { + Logger.error { "Failed to StartTcpServer, Message : ${e.message}" } + } + return false + } + + /** + * Stop TCP Command Server + * @return true if succesful + */ + fun StopTcpCommand(): Boolean { + try { + tcpserver?.close() + runBlocking { + socketMap.values.forEach { + it.close() + } + socketMap.clear() + job?.join() + } + Logger.info { "StopTcpCommand success" } + return true + } catch (e: Exception) { + Logger.error { "Failed to StopTcpServer, Message : ${e.message}" } + } finally { + tcpserver = null + } + return false + } +} \ No newline at end of file diff --git a/src/database/MariaDB.kt b/src/database/MariaDB.kt index cbf040f..217ce77 100644 --- a/src/database/MariaDB.kt +++ b/src/database/MariaDB.kt @@ -1247,7 +1247,7 @@ class MariaDB( * Reads all entries from the queue_table in the database. * @return A list of QueueTable entries. */ - fun Read_Queue_Table(): List { + fun Read_Queue_Table(): ArrayList { val queueList = ArrayList() try { val statement = connection?.createStatement() @@ -1314,7 +1314,7 @@ class MariaDB( * Reads all entries from the queue_paging in the database. * @return A list of QueuePaging entries. */ - fun Read_Queue_Paging(): List { + fun Read_Queue_Paging(): ArrayList { val queueList = ArrayList() try { val statement = connection?.createStatement() diff --git a/src/web/WebApp.kt b/src/web/WebApp.kt index be6193f..5cb349c 100644 --- a/src/web/WebApp.kt +++ b/src/web/WebApp.kt @@ -119,40 +119,12 @@ class WebApp(val listenPort: Int, val userlist: List>, val SendReply(wsMessageContext, cmd.command, "OK") } - "getSoundBankList" -> { - println("getSoundBankList command received") - SendReply( - wsMessageContext, - cmd.command, - MariaDB.ArrayListtoString(db.SoundbankList) - ) + "getPagingQueue" ->{ + SendReply(wsMessageContext, cmd.command, MariaDB.ArrayListtoString(db.Read_Queue_Paging())) } - "getMessageBankList" -> { - println("getMessageBankList command received") - SendReply( - wsMessageContext, - cmd.command, - MariaDB.ArrayListtoString(db.MessagebankList) - ) - } - - "getLanguageList" -> { - println("getLanguageList command received") - SendReply( - wsMessageContext, - cmd.command, - MariaDB.ArrayListtoString(db.LanguageLinkList) - ) - } - - "getTimerList" -> { - println("getTimerList command received") - SendReply( - wsMessageContext, - cmd.command, - MariaDB.ArrayListtoString(db.SchedulebankList) - ) + "getAASPagingQueue" ->{ + SendReply(wsMessageContext, cmd.command, MariaDB.ArrayListtoString(db.Read_Queue_Table())) } else -> { @@ -718,6 +690,29 @@ class WebApp(val listenPort: Int, val userlist: List>, val } } else get1.status(400).result("Invalid logdate") } + get("ExportXLSX//") { get1 -> + val logdate = get1.pathParam("logdate") + val logfilter = get1.pathParam("logfilter") + if (ValidDate(logdate)) { + val xlsxdata = if (ValidString(logfilter)) { + db.Export_Log_XLSX(logdate, logfilter) + } else { + db.Export_Log_XLSX(logdate, "") + } + if (xlsxdata != null) { + get1.header( + "Content-Type", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + ) + get1.header("Content-Disposition", "attachment; filename=\"log_$logdate.xlsx\"") + get1.outputStream().use { out -> + xlsxdata.write(out) + } + } else { + get1.status(500).result("Failed to export log to XLSX") + } + } else get1.status(400).result("Invalid logdate") + } } } }