From cf69c72f3cb2b3c90fe49d7e2a42a932b5348e67 Mon Sep 17 00:00:00 2001 From: rdkartono Date: Mon, 6 Oct 2025 08:30:09 +0700 Subject: [PATCH] commit 06/10/2025 --- src/Main.kt | 7 +- src/MainExtension01.kt | 17 +++- src/barix/TCP_Barix_Command_Server.kt | 85 +++++++++---------- .../TCP_Android_Command_Server.kt | 18 ++-- src/commandServer/TCP_PC_Command_Server.kt | 16 ++-- src/database/Log.kt | 4 + src/database/MariaDB.kt | 16 ++-- src/database/QueuePaging.kt | 7 +- src/database/QueueTable.kt | 8 +- 9 files changed, 97 insertions(+), 81 deletions(-) diff --git a/src/Main.kt b/src/Main.kt index 9659b97..1bfab73 100644 --- a/src/Main.kt +++ b/src/Main.kt @@ -23,6 +23,8 @@ 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 @@ -91,7 +93,7 @@ fun main() { val barixserver = TCP_Barix_Command_Server() barixserver.StartTcpServer { cmd -> - Logger.info { cmd } + //Logger.info { cmd } val _streamer = StreamerOutputs[cmd.ipaddress] val _sc = db.soundchannelDB.List.find { it.ip == cmd.ipaddress } if (_streamer == null) { @@ -103,7 +105,8 @@ fun main() { _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 diff --git a/src/MainExtension01.kt b/src/MainExtension01.kt index 0ac237f..dabaaa9 100644 --- a/src/MainExtension01.kt +++ b/src/MainExtension01.kt @@ -39,16 +39,24 @@ class MainExtension01 { */ fun AllBroadcastZonesValid(bz: List): Boolean { if (bz.isNotEmpty()) { + println("StreamerOutputs: $StreamerOutputs") val validchannels = bz // check apakah tiap zone ada di database broadcast zones .filter { z1 -> - db.broadcastDB.List.find { z2 -> z2.SoundChannel == z1 } != null + println("Checking broadcast zone $z1") + val xx = db.broadcastDB.List.find { z2 -> z2.description == z1 } + println("Found in DB: $xx") + xx != null } // check apakah tiap zone ada di SoundChannelList dan Online .filter { z3 -> - StreamerOutputs.any { sc -> sc.value.channel == z3 && sc.value.isOnline() } + println("Checking if zone $z3 is in StreamerOutputs and online") + val xx = StreamerOutputs.values.find { it.channel == z3 } + println("Is online: $xx") + xx!= null } + println("Valid channels: $validchannels") // kalau jumlah valid channel sama dengan jumlah broadcast zone, berarti semua valid return validchannels.size == bz.size } @@ -484,6 +492,7 @@ class MainExtension01 { fun Read_Queue_Paging(){ db.queuepagingDB.Get() for (qp in db.queuepagingDB.List) { + println("Processing QueuePaging $qp") if (qp.BroadcastZones.isNotBlank()) { val zz = qp.BroadcastZones.split(";") if (AllBroadcastZonesValid(zz)) { @@ -615,10 +624,14 @@ class MainExtension01 { fun Read_Queue_Table(){ db.queuetableDB.Get() db.queuetableDB.List.forEach { qa -> + println("Processing QueueTable $qa") if (qa.BroadcastZones.isNotEmpty()) { val zz = qa.BroadcastZones.split(";") + println("Broadcast zones: $zz") if (AllBroadcastZonesValid(zz)) { + println("All broadcast zones valid") if (AllBroadcastZoneIdle(zz)) { + println("All broadcast zones idle") if (qa.Type == "SOUNDBANK") { val variables = Get_Soundbank_Data(qa.SB_TAGS) val languages = qa.Language.split(";") diff --git a/src/barix/TCP_Barix_Command_Server.kt b/src/barix/TCP_Barix_Command_Server.kt index 1a692c6..180afa6 100644 --- a/src/barix/TCP_Barix_Command_Server.kt +++ b/src/barix/TCP_Barix_Command_Server.kt @@ -9,11 +9,11 @@ import java.util.function.Consumer @Suppress("unused") class TCP_Barix_Command_Server { - private var tcpserver: ServerSocket? = null - private var job: Job? = null + lateinit var tcpserver: ServerSocket + lateinit var job: Job private val socketMap = mutableMapOf() - private val regex = """\$\\"STATUSBARIX;(\d+);(\d+);?(\d)?\\"$""" + private val regex = """STATUSBARIX;(\d+);(\d+);?(\d)?""" private val pattern = Regex(regex) /** @@ -27,51 +27,44 @@ class TCP_Barix_Command_Server { val tcp = ServerSocket(port) tcpserver = tcp job = CoroutineScope(Dispatchers.IO).launch { - Logger.info { "TCP server started" } + Logger.info { "TCP StreamerOutput server started on port $port" } while (isActive) { - if (tcpserver?.isClosed == true) break + if (tcpserver.isClosed) 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)) { - // Valid command from Barix is in format $"STATUSBARIX;VU;BuffRemain;StatusData"$ - pattern.find(str)?.let { matchResult -> - val (vu, buffremain, statusdata) = matchResult.destructured - val status = BarixStatus( - socket.inetAddress.hostAddress, - vu.toInt(), - buffremain.toInt(), - statusdata.toIntOrNull() ?: 0 - ) - Logger.info { "Received valid command from $key : $status" } - cb.accept(status) - } ?: run { - Logger.warn { "Invalid command format from $key : $str" } - } - } - } - } - } - } - Logger.info { "Finished communicating with $key" } - socketMap.remove(key) - } + val socket = tcpserver.accept() + CoroutineScope(Dispatchers.IO).launch { + val key : String = socket.inetAddress.hostAddress + socketMap[key] = socket + Logger.info { "Start communicating with Streamer Output with IP : $key" } + val din = socket.getInputStream() + 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) + if (ValidString(str)) { + // Valid command from Barix is in format $"STATUSBARIX;VU;BuffRemain;StatusData"$ + pattern.find(str)?.let { matchResult -> + val (vu, buffremain, statusdata) = matchResult.destructured + val status = BarixStatus( + socket.inetAddress.hostAddress, + vu.toInt(), + buffremain.toInt(), + statusdata.toIntOrNull() ?: 0 + ) + //Logger.info { "Received valid command from $key : $status" } + cb.accept(status) + } ?: run { + Logger.warn { "Invalid command format from $key : $str" } + } + } } } + Logger.info { "Finished communicating with Streamer Output with IP $key" } + socketMap.remove(key) + } } catch (ex: Exception) { @@ -94,20 +87,18 @@ class TCP_Barix_Command_Server { */ fun StopTcpCommand(): Boolean { try { - tcpserver?.close() + tcpserver.close() runBlocking { socketMap.values.forEach { it.close() } socketMap.clear() - job?.join() + job.join() } Logger.info { "StopTcpCommand success" } return true } catch (e: Exception) { Logger.error { "Failed to StopTcpServer, Message : ${e.message}" } - } finally { - tcpserver = null } return false } diff --git a/src/commandServer/TCP_Android_Command_Server.kt b/src/commandServer/TCP_Android_Command_Server.kt index 6597cfa..cdd5959 100644 --- a/src/commandServer/TCP_Android_Command_Server.kt +++ b/src/commandServer/TCP_Android_Command_Server.kt @@ -27,8 +27,8 @@ import kotlin.io.path.absolutePathString @Suppress("unused") class TCP_Android_Command_Server { - private var tcpserver: ServerSocket? = null - private var job: Job? = null + lateinit var tcpserver: ServerSocket + lateinit var job: Job private val socketMap = mutableMapOf() lateinit var logcb: Consumer private val listUserLogin = mutableListOf() @@ -46,13 +46,13 @@ class TCP_Android_Command_Server { val tcp = ServerSocket(port) tcpserver = tcp job = CoroutineScope(Dispatchers.IO).launch { - Logger.info { "TCP server started" } + Logger.info { "TCP Android server started on port $port" } while (isActive) { - if (tcpserver?.isClosed == true) break + if (tcpserver.isClosed) break try { - tcpserver?.accept().use { socket -> + tcpserver.accept().use { socket -> { - CoroutineScope(Dispatchers.Main).launch { + CoroutineScope(Dispatchers.IO).launch { if (socket != null) { // key is IP address only val key: String = socket.inetAddress.hostAddress @@ -416,20 +416,18 @@ class TCP_Android_Command_Server { */ fun StopTcpCommand(): Boolean { try { - tcpserver?.close() + tcpserver.close() runBlocking { socketMap.values.forEach { it.close() } socketMap.clear() - job?.join() + job.join() } Logger.info { "StopTcpCommand success" } return true } catch (e: Exception) { Logger.error { "Failed to StopTcpServer, Message : ${e.message}" } - } finally { - tcpserver = null } return false } diff --git a/src/commandServer/TCP_PC_Command_Server.kt b/src/commandServer/TCP_PC_Command_Server.kt index 9e865ca..ee4a280 100644 --- a/src/commandServer/TCP_PC_Command_Server.kt +++ b/src/commandServer/TCP_PC_Command_Server.kt @@ -14,8 +14,8 @@ import java.util.function.Consumer @Suppress("unused") class TCP_PC_Command_Server { - private var tcpserver: ServerSocket? = null - private var job: Job? = null + lateinit var tcpserver: ServerSocket + lateinit var job: Job private val socketMap = mutableMapOf() /** @@ -31,11 +31,11 @@ class TCP_PC_Command_Server { job = CoroutineScope(Dispatchers.IO).launch { Logger.info { "TCP server started" } while (isActive) { - if (tcpserver?.isClosed == true) break + if (tcpserver.isClosed) break try { - tcpserver?.accept().use { socket -> + tcpserver.accept().use { socket -> { - CoroutineScope(Dispatchers.Main).launch { + CoroutineScope(Dispatchers.IO).launch { if (socket != null) { val key : String = socket.inetAddress.hostAddress+":"+socket.port socketMap[key] = socket @@ -81,20 +81,18 @@ class TCP_PC_Command_Server { */ fun StopTcpCommand(): Boolean { try { - tcpserver?.close() + tcpserver.close() runBlocking { socketMap.values.forEach { it.close() } socketMap.clear() - job?.join() + job.join() } Logger.info { "StopTcpCommand success" } return true } catch (e: Exception) { Logger.error { "Failed to StopTcpServer, Message : ${e.message}" } - } finally { - tcpserver = null } return false } diff --git a/src/database/Log.kt b/src/database/Log.kt index 932e471..fb91ba9 100644 --- a/src/database/Log.kt +++ b/src/database/Log.kt @@ -20,4 +20,8 @@ data class Log( return Log(0u, date, time, machine, description) } } + + override fun toString() : String { + return "$datenya $timenya [$machine] $description" + } } diff --git a/src/database/MariaDB.kt b/src/database/MariaDB.kt index 662169b..0bb4dee 100644 --- a/src/database/MariaDB.kt +++ b/src/database/MariaDB.kt @@ -6,11 +6,13 @@ import content.Category import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext +import max_channel import org.apache.poi.xssf.usermodel.XSSFWorkbook import org.tinylog.Logger import java.sql.Connection import java.sql.DriverManager import java.util.function.Consumer +import kotlin.math.max /** * A class to manage a connection to a MariaDB database. @@ -1464,7 +1466,7 @@ class MariaDB( val countResult = statement?.executeQuery("SELECT COUNT(*) AS count FROM ${super.dbName}") if (countResult?.next() == true) { val count = countResult.getInt("count") - if (count == 0) { + if (count < max_channel) { Logger.info("SoundChannel table is empty, populating with default channels" as Any) Clear() } @@ -1578,9 +1580,9 @@ class MariaDB( statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") Logger.info("${super.dbName} table cleared" as Any) List.clear() - // create new rows from 1 to 64 with description "Channel 01" to "Channel 64" and empty ip - for (i in 1..64) { - val channel = String.format("Channel %02d", i) + // create new rows from 1 to 64 with description "Channel 1" to "Channel 64" and empty ip + for (i in 1..max_channel) { + val channel = String.format("Channel %d", i) val insertStatement = connection.prepareStatement("INSERT INTO ${super.dbName} (channel, ip) VALUES (?, ?)") insertStatement?.setString(1, channel) @@ -1728,13 +1730,13 @@ class MariaDB( statement?.setString(4, data.description) val rowsAffected = statement?.executeUpdate() if (rowsAffected != null && rowsAffected > 0) { - Logger.info("Log added: [$data.datenya $data.timenya] [$data.machine] $data.description" as Any) + Logger.info{"Log added : $data"} return true } else { - Logger.warn("No log entry added for: [$data.datenya $data.timenya] [$data.machine] $data.description" as Any) + Logger.warn{"Failed to add log entry : $data"} } } catch (e: Exception) { - Logger.error("Error adding log entry: ${e.message}" as Any) + Logger.error{"Error adding log entry: ${e.message}"} } return false } diff --git a/src/database/QueuePaging.kt b/src/database/QueuePaging.kt index ae6bb26..38afb29 100644 --- a/src/database/QueuePaging.kt +++ b/src/database/QueuePaging.kt @@ -1,4 +1,7 @@ package database -@Suppress("unused") -data class QueuePaging(var index: UInt, var Date_Time: String, var Source: String, var Type: String, var Message: String, var BroadcastZones: String) +data class QueuePaging(var index: UInt, var Date_Time: String, var Source: String, var Type: String, var Message: String, var BroadcastZones: String){ + override fun toString(): String { + return "QueuePaging(index=$index, Date_Time='$Date_Time', Source='$Source', Type='$Type', Message='$Message', BroadcastZones='$BroadcastZones')" + } +} diff --git a/src/database/QueueTable.kt b/src/database/QueueTable.kt index 3c22588..e5be756 100644 --- a/src/database/QueueTable.kt +++ b/src/database/QueueTable.kt @@ -1,4 +1,8 @@ package database -@Suppress("unused") -data class QueueTable(var index: UInt, var Date_Time: String, var Source: String, var Type: String, var Message: String, var SB_TAGS: String, var BroadcastZones: String, var Repeat: UInt, var Language: String) +data class QueueTable(var index: UInt, var Date_Time: String, var Source: String, var Type: String, var Message: String, var SB_TAGS: String, var BroadcastZones: String, var Repeat: UInt, var Language: String){ + + override fun toString(): String { + return "QueueTable(index=$index, Date_Time='$Date_Time', Source='$Source', Type='$Type', Message='$Message', SB_TAGS='$SB_TAGS', BroadcastZones='$BroadcastZones', Repeat=$Repeat, Language='$Language')" + } +}