commit 05/02/2026

This commit is contained in:
2026-02-06 17:02:23 +07:00
parent afd896161e
commit c8f7f35c79
8 changed files with 957 additions and 410 deletions

View File

@@ -240,6 +240,7 @@ fun main(args: Array<String>) {
_bc.bufferRemain = cmd.buffremain _bc.bufferRemain = cmd.buffremain
_bc.statusData = cmd.statusdata _bc.statusData = cmd.statusdata
_bc.commandsocket = _tcp _bc.commandsocket = _tcp
_bc.BarixMode = cmd.isBarix
StreamerOutputs[cmd.ipaddress] = _bc StreamerOutputs[cmd.ipaddress] = _bc
Logger.info { "Created new Streamer Output for channel ${_sc.channel} with IP ${cmd.ipaddress}" } Logger.info { "Created new Streamer Output for channel ${_sc.channel} with IP ${cmd.ipaddress}" }

File diff suppressed because it is too large Load Diff

View File

@@ -14,6 +14,7 @@ import java.net.InetSocketAddress
import java.net.Socket import java.net.Socket
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.util.function.Consumer import java.util.function.Consumer
import kotlin.experimental.or
@Suppress("unused") @Suppress("unused")
class BarixConnection(val index: UInt, var channel: String, val ipaddress: String, val port: Int = 5002) : AutoCloseable { class BarixConnection(val index: UInt, var channel: String, val ipaddress: String, val port: Int = 5002) : AutoCloseable {
@@ -27,6 +28,16 @@ class BarixConnection(val index: UInt, var channel: String, val ipaddress: Strin
private val mp3encoder = Mp3Encoder() private val mp3encoder = Mp3Encoder()
private val mp3Consumer = mutableMapOf<String, Consumer<ByteArray>>() private val mp3Consumer = mutableMapOf<String, Consumer<ByteArray>>()
private val udp = DatagramSocket() private val udp = DatagramSocket()
private var _barixmode: Boolean = false
/**
* Barix mode flag
*/
var BarixMode: Boolean
get() = _barixmode
set(value) {
_barixmode = value
}
init { init {
mp3encoder.Start { data -> mp3encoder.Start { data ->
@@ -132,11 +143,12 @@ class BarixConnection(val index: UInt, var channel: String, val ipaddress: Strin
val chunk = ByteArray(if (bb.remaining() > maxUDPsize) maxUDPsize else bb.remaining()) val chunk = ByteArray(if (bb.remaining() > maxUDPsize) maxUDPsize else bb.remaining())
bb.get(chunk) bb.get(chunk)
while(bufferRemain<chunk.size){ while(bufferRemain<chunk.size){
delay(5) delay(10)
} }
udp.send(DatagramPacket(chunk, chunk.size, inet)) udp.send(DatagramPacket(chunk, chunk.size, inet))
mp3encoder.PushData(chunk) mp3encoder.PushData(chunk)
delay(1) if (_barixmode) delay(10) else delay(1)
} catch (e: Exception) { } catch (e: Exception) {
@@ -177,40 +189,62 @@ class BarixConnection(val index: UInt, var channel: String, val ipaddress: Strin
return Somecodes.toJsonString(toJsonNode()) return Somecodes.toJsonString(toJsonNode())
} }
fun ActivateRelay(relays: List<Int>){
if (relays.isNotEmpty()){
var value : Byte = 0
for (r in relays){
if (r in 1..8){
value = value or (1 shl (r - 1)).toByte()
}
}
SendSimpleCommand(byteArrayOf(0x1A, value, 0x61))
}
}
/** /**
* Activate relay on Barix device * Activate relay on Barix device
* @param relays The relay numbers to activate (1-8) * @param relays The relay numbers to activate (1-8)
* @return true if successful
*/ */
fun ActivateRelay(vararg relays: Int){ fun ActivateRelay(vararg relays: Int){
val command = StringBuilder("RELAY;") if (relays.isNotEmpty()){
var binary = 0 var value : Byte = 0
relays.forEach { for (r in relays){
if (it in 1..8) { if (r in 1..8){
binary = binary or (1 shl (it - 1)) value = value or (1 shl (r - 1)).toByte()
}
} }
SendSimpleCommand(byteArrayOf(0x1A, value, 0x61))
} }
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 * Deactivate relay on Barix device
*/ */
fun DeactivateRelay(){ fun DeactivateRelay(){
SendCommand("RELAY;0@") SendSimpleCommand(byteArrayOf(0x1A, 0, 0x61))
}
/**
* Send simple command to Barix device
* @param command The command to send
* @return true if successful
*/
fun SendSimpleCommand(command: ByteArray) : Boolean {
try {
if (_tcp !=null){
if (_tcp!!.isConnected){
val out = _tcp!!.getOutputStream()
out.write(command)
out.flush()
return true
} else throw Exception("Socket to $ipaddress is not connected")
} else throw Exception("Socket to $ipaddress is null")
} catch (e: Exception) {
if (e.message != null && e.message!!.isNotEmpty()) {
Logger.error { "Failed to send command, message : ${e.message}" }
}
return false
}
} }
/** /**

View File

@@ -1,6 +1,6 @@
package barix package barix
@Suppress("unused") @Suppress("unused")
data class BarixStatus(val ipaddress: String, val vu: Int, val buffremain: Int, val statusdata: Int){ data class BarixStatus(val ipaddress: String, val vu: Int, val buffremain: Int, val statusdata: Int, val isBarix: Boolean){
override fun toString(): String { override fun toString(): String {
return "BarixStatus(ipaddress='$ipaddress', vu=$vu, buffremain=$buffremain, statusdata=$statusdata)" return "BarixStatus(ipaddress='$ipaddress', vu=$vu, buffremain=$buffremain, statusdata=$statusdata)"
} }

View File

@@ -1,5 +1,6 @@
package barix package barix
import codes.Somecodes.Companion.LitteEndianToInt
import codes.Somecodes.Companion.ValidString import codes.Somecodes.Companion.ValidString
import kotlinx.coroutines.* import kotlinx.coroutines.*
import org.tinylog.Logger import org.tinylog.Logger
@@ -15,7 +16,7 @@ class TCP_Barix_Command_Server {
lateinit var job: Job lateinit var job: Job
private val socketMap = mutableMapOf<String, Socket>() private val socketMap = mutableMapOf<String, Socket>()
private val regex = """STATUSBARIX;(\d+);(\d+);?(\d)?""" private val regex = """STATUSBARIX;(\d+);(\d+)(;(\d+))?"""
private val pattern = Regex(regex) private val pattern = Regex(regex)
/** /**
@@ -43,15 +44,25 @@ class TCP_Barix_Command_Server {
val din = DataInputStream(socket.getInputStream()) val din = DataInputStream(socket.getInputStream())
while (isActive) { while (isActive) {
val length = ByteArray(4)
din.readFully(length) val bb = ByteArray(128)
val readlength = ByteBuffer.wrap(length).getInt() val readbytes = din.read(bb)
//println("Read Length : $readlength") if (readbytes == -1) {
val bb = ByteArray(readlength) Logger.info { "Connection closed by Streamer Output with IP $key" }
din.readFully(bb) break
// B4A format, 4 bytes di depan adalah size }
val str = String(bb) if (readbytes == 0) continue
//println("Received from $key : $str") var stringlength = 0
try{
stringlength = LitteEndianToInt(bb[0], bb[1], bb[2], bb[3])
if (stringlength<1 || stringlength>bb.size-4) throw Exception("Invalid string length $stringlength")
} catch (ex:Exception){
Logger.error { "Error reading length from Streamer Output with IP $key, Message : ${ex.message}" }
continue
}
val str = String(bb,4, stringlength).trim()
if (str.isBlank()) continue
if (!str.startsWith("STATUSBARIX")) continue
if (ValidString(str)) { if (ValidString(str)) {
// Valid command from Barix is in format $"STATUSBARIX;VU;BuffRemain;StatusData"$ // Valid command from Barix is in format $"STATUSBARIX;VU;BuffRemain;StatusData"$
pattern.find(str)?.let { matchResult -> pattern.find(str)?.let { matchResult ->
@@ -60,7 +71,8 @@ class TCP_Barix_Command_Server {
socket.inetAddress.hostAddress, socket.inetAddress.hostAddress,
vu.toInt(), vu.toInt(),
buffremain.toInt(), buffremain.toInt(),
statusdata.toIntOrNull() ?: 0 statusdata.toIntOrNull() ?: 0,
statusdata.isNullOrEmpty() // barix tidak ada statusdata , Q-AG1 ada
) )
//Logger.info { "Received valid command from $key : $status" } //Logger.info { "Received valid command from $key : $status" }
cb.accept(status) cb.accept(status)

View File

@@ -522,6 +522,22 @@ class Somecodes {
return value is String && value.isNotBlank() return value is String && value.isNotBlank()
} }
fun LitteEndianToInt(vararg bb: Byte): Int {
var result = 0
for (i in bb.indices) {
result = result or ((bb[i].toInt() and 0xFF) shl (8 * i))
}
return result
}
fun BigEndianToInt(vararg bb: Byte): Int {
var result = 0
for (i in bb.indices) {
result = result or ((bb[i].toInt() and 0xFF) shl (8 * (bb.size - 1 - i)))
}
return result
}
/** /**
* Check if all strings in a list are valid non-blank strings. * Check if all strings in a list are valid non-blank strings.
* @param values The list of strings to check. * @param values The list of strings to check.

View File

@@ -8,4 +8,22 @@ data class QueuePaging(var index: UInt, var Date_Time: String, var Source: Strin
override fun toString(): String { override fun toString(): String {
return "QueuePaging(index=$index, Date_Time='$Date_Time', Source='$Source', Type='$Type', Message='$Message', BroadcastZones='$BroadcastZones')" return "QueuePaging(index=$index, Date_Time='$Date_Time', Source='$Source', Type='$Type', Message='$Message', BroadcastZones='$BroadcastZones')"
} }
companion object{
/**
* Check if the QueuePaging entry is expired (older than 5 seconds)
* @param qt QueuePaging entry to check
* @return true if expired, false otherwise
*/
fun isExpired(qt: QueuePaging) : Boolean{
try{
val t1 = java.time.LocalDateTime.parse(qt.Date_Time, codes.Somecodes.datetimeformat1)
val delta = java.time.Duration.between(t1, java.time.LocalDateTime.now())
return delta.seconds > 5
} catch (_: Exception){
return false
}
}
}
} }

View File

@@ -1,5 +1,8 @@
package database package database
import codes.Somecodes.Companion.datetimeformat1
import java.time.LocalDateTime
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){
/** /**
@@ -11,4 +14,23 @@ data class QueueTable(var index: UInt, var Date_Time: String, var Source: String
override fun toString(): 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')" return "QueueTable(index=$index, Date_Time='$Date_Time', Source='$Source', Type='$Type', Message='$Message', SB_TAGS='$SB_TAGS', BroadcastZones='$BroadcastZones', Repeat=$Repeat, Language='$Language')"
} }
companion object{
/**
* Check if the QueueTable entry is expired (older than 5 seconds)
* @param qt QueueTable entry to check
* @return true if expired, false otherwise
*/
fun isExpired(qt: QueueTable) : Boolean{
try{
val t1 = LocalDateTime.parse(qt.Date_Time, datetimeformat1)
val delta = java.time.Duration.between(t1, LocalDateTime.now())
// expired if more than 5 seconds
return delta.seconds > 5
} catch (_: Exception){
return false
}
}
}
} }