package barix import codes.Somecodes.Companion.LitteEndianToInt import codes.Somecodes.Companion.ValidString import kotlinx.coroutines.* import org.tinylog.Logger import java.io.DataInputStream import java.net.ServerSocket import java.net.Socket import java.util.function.Consumer class TCP_Barix_Command_Server { lateinit var tcpserver: ServerSocket lateinit var job: Job private val socketMap = mutableMapOf() //private val regex = """STATUSBARIX;(\d+);(\d+)(;(\d+))?""" //private val pattern = Regex(regex) /** * Start TCP Command Server * @param port The port number to listen on (default is 5001) * @param cb A callback function that will be called when a valid command is received * @return true if successful */ fun StartTcpServer(port: Int = 5001, cb: Consumer): Boolean { try { val tcp = ServerSocket(port) tcpserver = tcp job = CoroutineScope(Dispatchers.IO).launch { Logger.info { "TCP StreamerOutput server started on port $port" } while (isActive) { if (tcpserver.isClosed) break try { 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" } try{ val din = DataInputStream(socket.getInputStream()) var VuZeroCounter = 0L while (isActive) { val bb = ByteArray(128) val readbytes = din.read(bb) if (readbytes == -1) { Logger.info { "Connection closed by Streamer Output with IP $key" } break } if (readbytes == 0) continue 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 } var str = String(bb,4, stringlength).trim() if (str.isBlank()) continue if (!str.startsWith("STATUSBARIX")) continue if (str.endsWith("@")) str = str.removeSuffix("@") if (ValidString(str)) { // Valid command from StreamerOutput is in format $"STATUSBARIX;VU;BuffRemain;StatusData"$ // Valid command from Barix is in format $"STATUSBARIX;VU;BuffRemain"$ val values = str.split(";") if (values.size<3) continue if ("STATUSBARIX" != values[0]) continue val vu = values[1].toIntOrNull() ?: continue val buffremain = values[2].toIntOrNull() ?: continue var status: BarixStatus when(values.size){ 3 ->{ // mode barix // kadang vu stuck tidak di 0 saat idle, // jadi kalau vu <512 selama 10 kali berturut2 // dan buffer lebih dari 16000, anggap idle if ((vu < 512) && (buffremain>=16000)){ VuZeroCounter++ } else { VuZeroCounter = 0 } // statusdata = isplaying = , if VuZeroCounter >=10 then idle (0) else playing (1) val statusdata = if (VuZeroCounter>=10) 0 else 1 status = BarixStatus( socket.inetAddress.hostAddress, vu, buffremain, statusdata, true ) } 4 ->{ // mode Q-AG1 val statusdata = values[3].toIntOrNull() ?: 0 status = BarixStatus( socket.inetAddress.hostAddress, vu, buffremain, statusdata, false ) } else -> continue } cb.accept(status) } } } catch (ex:Exception){ if (ex.message!=null) Logger.error { "Error in communication with Streamer Output with IP $key, Message : ${ex.message}" } } Logger.info { "Finished communicating with Streamer Output with IP $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}" } } 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] } }