From 2ad26c3ef6734dde816bb344c8cc417ae0952011 Mon Sep 17 00:00:00 2001 From: rdkartono Date: Wed, 8 Oct 2025 16:33:19 +0700 Subject: [PATCH] commit 08/10/2025 BarixConnection Activate Deactivate Relays --- src/Main.kt | 19 +++++ src/MainExtension01.kt | 106 ++++++++++++++++++++++++-- src/barix/BarixConnection.kt | 84 ++++++++++++++++++++ src/barix/TCP_Barix_Command_Server.kt | 11 +++ 4 files changed, 215 insertions(+), 5 deletions(-) diff --git a/src/Main.kt b/src/Main.kt index ce2fd70..9aa7c0f 100644 --- a/src/Main.kt +++ b/src/Main.kt @@ -160,16 +160,20 @@ fun main() { val barixserver = TCP_Barix_Command_Server() barixserver.StartTcpServer { cmd -> //Logger.info { 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) _bc.vu = cmd.vu _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 Logger.warn { "soundChannelDB doesn't have soundchannel with IP ${cmd.ipaddress}" } @@ -182,6 +186,21 @@ fun main() { _streamer.vu = cmd.vu _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 + } + } } } diff --git a/src/MainExtension01.kt b/src/MainExtension01.kt index d5edfbd..102d265 100644 --- a/src/MainExtension01.kt +++ b/src/MainExtension01.kt @@ -1,4 +1,5 @@ import audio.AudioFileInfo +import barix.BarixConnection import codes.Result_GetSoundbankFiles import codes.Somecodes.Companion.Get_ANN_ID import codes.Somecodes.Companion.IsAlphabethic @@ -89,6 +90,25 @@ class MainExtension01 { return false } + /** + * Find BarixConnection by BroadcastZones description + * @param zonename BroadcastZones description + * @return BarixConnection if found, null if not found or invalid + */ + fun Get_Barix_Connection_by_ZoneName(zonename: String) : BarixConnection? { + if (ValidString(zonename)){ + val bz = db.broadcastDB.List.find{ it.description == zonename } + val sc = if (bz!=null) db.soundchannelDB.List.find { it.channel == bz.SoundChannel } else null + val ip = sc?.ip ?: "" + if (ValidIPV4(ip)){ + // ketemu ip-nya + return StreamerOutputs[ip] + + } + } + return null + } + /** * find SoundChannel IP from BroadcastZones description * @param bz List of BroadcastZones description @@ -550,8 +570,18 @@ class MainExtension01 { if (AllStreamerOutputIdle(ips)){ val afi = audioPlayer.LoadAudioFile(qp.Message) if (afi.isValid()){ - // file bisa di load, kirim ke masing masing Streamer Output by IP address - ips.forEach { ip -> StreamerOutputs[ip]?.SendData(afi.bytes, { db.Add_Log("AAS", it) }, { db.Add_Log("AAS", it) }) } + // file bisa di load, kirim ke masing-masing Streamer Output by IP address + Activate_Relays(zz) + ips.forEach { ip -> + // send byte array to streamer output + StreamerOutputs[ip]?.SendData(afi.bytes, + { + Deactivate_Relays(zz) + db.Add_Log("AAS", it) }, + { + Deactivate_Relays(zz) + db.Add_Log("AAS", it) }) + } val logmessage = "Broadcast started PAGING with Filename '${qp.Message}' to zones: ${qp.BroadcastZones}" @@ -640,7 +670,18 @@ class MainExtension01 { // file siap broadcast val targetafi = audioPlayer.LoadAudioFile(targetfile) if (targetafi.isValid()) { - ips.forEach { ip -> StreamerOutputs[ip]?.SendData(targetafi.bytes, { db.Add_Log("AAS", it) }, {db.Add_Log("AAS", it) }) } + // activate relays from broadcast zone + Activate_Relays(zz) + ips.forEach { ip -> + // send byte array to streamer output + StreamerOutputs[ip]?.SendData(targetafi.bytes, + { + Deactivate_Relays(zz) + db.Add_Log("AAS", it) }, + { + Deactivate_Relays(zz) + db.Add_Log("AAS", it) }) + } val logmsg = "Broadcast started SHALAT message with generated file '$targetfile' to zones: ${qp.BroadcastZones}" @@ -730,7 +771,18 @@ class MainExtension01 { // file siap broadcast val targetafi = audioPlayer.LoadAudioFile(targetfile) if (targetafi.isValid()) { - ips.forEach { ip -> StreamerOutputs[ip]?.SendData(targetafi.bytes, { db.Add_Log("AAS", it) }, { db.Add_Log("AAS", it) }) } + // activate relays from broadcast zone + Activate_Relays(zz) + ips.forEach { ip -> + // send byte array to streamer output + StreamerOutputs[ip]?.SendData(targetafi.bytes, + { + Deactivate_Relays(zz) + db.Add_Log("AAS", it) }, + { + Deactivate_Relays(zz) + db.Add_Log("AAS", it) }) + } val logmsg = "Broadcast started TIMER message with generated file '$targetfile' to zones: ${qa.BroadcastZones}" @@ -779,6 +831,37 @@ class MainExtension01 { return false } + fun Activate_Relays(zz: List){ + zz.forEach { zonename -> + val bz = db.broadcastDB.List.find { it.description == zonename } + if (bz!=null){ + val bc = Get_Barix_Connection_by_ZoneName(zonename) + if (bc!=null){ + // ada barix connection + val relays = bz.bp.split(",") + .map { it.trim()} + .filter { it.isNotBlank() } + .filter { IsNumber(it) && it.toInt() in 1..8 } + .map{ it.toInt() } + if (relays.isNotEmpty()) bc.ActivateRelay(relays) + + } + } + + } + } + + fun Deactivate_Relays(zz: List){ + zz.forEach { zonename -> + val bz = db.broadcastDB.List.find { it.description == zonename } + if (bz!=null){ + val bc = Get_Barix_Connection_by_ZoneName(zonename) + bc?.DeactivateRelay() + } + + } + } + fun Read_Queue_Soundbank() : Boolean{ db.queuetableDB.Get() val list = db.queuetableDB.List.filter { it.Type=="SOUNDBANK" } @@ -864,7 +947,20 @@ class MainExtension01 { //println("Successfully wrote WAV file: $targetfile") val targetafi = audioPlayer.LoadAudioFile(targetfile) if (targetafi.isValid()) { - ips.forEach { ip -> StreamerOutputs[ip]?.SendData(targetafi.bytes, { db.Add_Log("AAS", it) }, { db.Add_Log("AAS", it) }) } + // activate relays from broadcast zone + Activate_Relays(zz) + ips.forEach { ip -> + // send byte array to streamer output + StreamerOutputs[ip]?.SendData(targetafi.bytes, + { + Deactivate_Relays(zz) + db.Add_Log("AAS", it) + + }, { + Deactivate_Relays(zz) + db.Add_Log("AAS", it) + }) + } val logmsg = "Broadcast started SOUNDBANK message with generated file '$targetfile' to zones: ${qa.BroadcastZones}" diff --git a/src/barix/BarixConnection.kt b/src/barix/BarixConnection.kt index 0a91ad1..ff170be 100644 --- a/src/barix/BarixConnection.kt +++ b/src/barix/BarixConnection.kt @@ -6,9 +6,11 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import org.tinylog.Logger import java.net.DatagramPacket import java.net.DatagramSocket import java.net.InetSocketAddress +import java.net.Socket import java.nio.ByteBuffer import java.util.function.Consumer @@ -21,6 +23,7 @@ class BarixConnection(val index: UInt, var channel: String, val ipaddress: Strin private val udp = DatagramSocket(0) private val inet = InetSocketAddress(ipaddress, port) private val maxUDPsize = 1000 + private var _tcp: Socket? = null /** @@ -53,6 +56,15 @@ class BarixConnection(val index: UInt, var channel: String, val ipaddress: Strin _onlinecounter = 5 } + /** + * TCP command socket for communication with this Barix device + */ + var commandsocket: Socket? + get() = _tcp + set(value){ + _tcp = value + } + /** * Decrement online counter, if counter reaches 0, the device is considered offline */ @@ -142,4 +154,76 @@ class BarixConnection(val index: UInt, var channel: String, val ipaddress: Strin fun toJsonString(): String { return Somecodes.toJsonString(toJsonNode()) } + + /** + * Activate relay on Barix device + * @param relays The relay numbers to activate (1-8) + * @return true if successful + */ + fun ActivateRelay(vararg relays: Int){ + val command = StringBuilder("RELAY;") + var binary = 0 + relays.forEach { + if (it in 1..8) { + binary = binary or (1 shl (it - 1)) + } + } + command.append(binary.toString()).append("@") + SendCommand(command.toString()) + } + + fun ActivateRelay(relays: List){ + val command = StringBuilder("RELAY;") + var binary = 0 + relays.forEach { + if (it in 1..8) { + binary = binary or (1 shl (it - 1)) + } + } + command.append(binary.toString()).append("@") + SendCommand(command.toString()) + } + + /** + * Deactivate relay on Barix device + */ + fun DeactivateRelay(){ + SendCommand("RELAY;0@") + } + + /** + * Send command to Barix device + * @param command The command to send + * @return true if successful + */ + fun SendCommand(command: String): Boolean { + try { + if (_tcp!=null){ + if (!_tcp!!.isClosed){ + val bb = command.toByteArray() + val size = bb.size + 4 + val b4 = byteArrayOf( + (size shr 24 and 0xFF).toByte(), + (size shr 16 and 0xFF).toByte(), + (size shr 8 and 0xFF).toByte(), + (size and 0xFF).toByte() + ) + val out = _tcp!!.getOutputStream() + out.write(b4) + out.write(bb) + out.flush() + Logger.info { "SendCommand to $ipaddress : $command" } + return true + }else { + Logger.error { "Socket to $ipaddress is not connected" } + } + } else { + Logger.error { "Socket to $ipaddress is null" } + } + + } catch (e: Exception) { + Logger.error { "Failed to SendCommand to $ipaddress, Message : ${e.message}" } + } + return false + } } \ No newline at end of file diff --git a/src/barix/TCP_Barix_Command_Server.kt b/src/barix/TCP_Barix_Command_Server.kt index 180afa6..7b10bc2 100644 --- a/src/barix/TCP_Barix_Command_Server.kt +++ b/src/barix/TCP_Barix_Command_Server.kt @@ -102,4 +102,15 @@ class TCP_Barix_Command_Server { } return false } + + /** + * Get Socket by IP address + * @param ip The IP address of the client + * @return Socket if found, null otherwise + */ + fun getSocket(ip: String): Socket? { + return socketMap[ip] + } + + } \ No newline at end of file