Compare commits

..

3 Commits

Author SHA1 Message Date
6b00bc7eb0 commit 08/10/2025 WebApp 2025-10-08 17:03:17 +07:00
8409307631 Merge branch 'master' of https://gitea.rdkartono.my.id/rdkartono/AAS_NewGeneration into feature-webapp 2025-10-08 17:02:45 +07:00
2ad26c3ef6 commit 08/10/2025
BarixConnection Activate Deactivate Relays
2025-10-08 16:33:19 +07:00
5 changed files with 227 additions and 7 deletions

View File

@@ -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
}
}
}
}

View File

@@ -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<String>){
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<String>){
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}"

View File

@@ -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<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())
}
/**
* 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
}
}

View File

@@ -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]
}
}

View File

@@ -639,6 +639,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
// get timer list
it.result(MariaDB.ArrayListtoString(db.scheduleDB.List))
}
delete("List") {
// truncate timer table
if (db.scheduleDB.Clear()) {
@@ -649,7 +650,6 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
}
}
post("Add"){
// TODO add new schedule
// recheck lagi tambahan steph
val json: JsonNode = objectmapper.readTree(it.body())
@@ -683,7 +683,6 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Time format, must be HH:mm")))
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Day format")))
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Description")))
}
delete("DeleteByIndex/{index}") {
// delete by index
@@ -814,6 +813,17 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid XLSX file")))
}
}
//TODO kirim list message dan broadcast zones untuk ADD/Edit schedule
get("GetMessageAndBroadcastZones") {
val result = object {
//TODO filter message without input variable
val messages = db.messageDB.List
val broadcastzones = db.broadcastDB.List
}
it.result(objectmapper.writeValueAsString(result))
}
}
path("Log") {
get("List") { get1 ->