Commit 29/09/2025

This commit is contained in:
rdkartono
2025-09-29 11:56:08 +07:00
parent f18a0ca9cd
commit cf24c06b35
6 changed files with 1004 additions and 845 deletions

View File

@@ -10,6 +10,8 @@ import kotlinx.coroutines.runBlocking
import org.tinylog.Logger
import java.net.ServerSocket
import java.net.Socket
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.util.function.Consumer
@Suppress("unused")
@@ -17,14 +19,16 @@ class TCP_Android_Command_Server {
private var tcpserver: ServerSocket? = null
private var job: Job? = null
private val socketMap = mutableMapOf<String, Socket>()
lateinit var logcb: Consumer<String>
/**
* Start TCP Command Server
* @param port The port to listen on, default is 5003
* @param cb The callback function to handle incoming messages
* @param logCB Callback to handle Log messages
* @return true if successful
*/
fun StartTcpServer(port: Int = 5003, cb: Consumer<String>): Boolean {
fun StartTcpServer(port: Int = 5003, logCB: Consumer<String>): Boolean {
logcb = logCB
try {
val tcp = ServerSocket(port)
tcpserver = tcp
@@ -37,27 +41,32 @@ class TCP_Android_Command_Server {
{
CoroutineScope(Dispatchers.Main).launch {
if (socket != null) {
val key : String = socket.inetAddress.hostAddress+":"+socket.port
val key: String = socket.inetAddress.hostAddress + ":" + socket.port
socketMap[key] = socket
Logger.info { "Start communicating with $key" }
socket.getInputStream().use { din ->
socket.getInputStream().let { din ->
{
while (isActive) {
if (din.available()>0){
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)
str.split("@").forEach {
if (ValidString(it)){
cb.accept(it)
val str = String(bb, 4, bb.size - 4)
str.split("@").map { it.trim() }.filter { ValidString(it) }
.map { it.uppercase() }.forEach {
process_command(it) { reply ->
try {
socket.getOutputStream().write(String_to_Byte_Android(reply))
} catch (e: Exception) {
logcb.accept("Failed to send reply to $key, Message : ${e.message}")
}
}
}
}
}
}
}
Logger.info { "Finished communicating with $key" }
logcb.accept("Finished communicatiing with $key")
socketMap.remove(key)
}
@@ -66,19 +75,84 @@ class TCP_Android_Command_Server {
}
} catch (ex: Exception) {
Logger.error { "Failed accepting TCP Socket, Message : ${ex.message}" }
logcb.accept("Failed accepting TCP Socket, Message : ${ex.message}")
}
}
Logger.info { "TCP server stopped" }
logcb.accept("TCP server stopped")
}
return true
} catch (e: Exception) {
Logger.error { "Failed to StartTcpServer, Message : ${e.message}" }
logcb.accept("Failed to StartTcpServer, Message : ${e.message}")
}
return false
}
/**
* Convert a String to ByteArray in prefix AsyncStream format in B4X
* @param str The input string
* @return ByteArray with 4 bytes prefix length + string bytes
*/
private fun String_to_Byte_Android(str: String): ByteArray {
if (ValidString(str)) {
val len = str.length
val bytes = str.toByteArray()
return ByteBuffer.allocate(len + 4)
.order(ByteOrder.LITTLE_ENDIAN)
.putInt(len)
.put(bytes)
.array()
}
return ByteArray(0)
}
private fun process_command(cmd: String, cb: Consumer<String>) {
Logger.info { "Command from Android: $cmd" }
val parts = cmd.split(";").map { it.trim() }.filter { it.isNotBlank() }.map { it.uppercase() }
when (parts[0]) {
"GETLOGIN" -> {
val username = parts.getOrElse(1) { "" }
val password = parts.getOrElse(2) { "" }
if (ValidString(username) && ValidString(password)) {
//TODO handle login here
} else cb.accept("LOGIN;FALSE@")
}
"PCMFILE_START" -> {
// TODO read coding here
}
"PCMFILE_STOP" -> {
// TODO read coding here
}
"STARTPAGINGAND" -> {
// TODO read coding here
}
"STOPPAGINGAND" -> {
// TODO read coding here
}
"CANCELPAGINGAND" -> {
// TODO read coding here
}
"STARTINITIALIZE" -> {
// TODO read coding here
}
"BROADCASTAND" -> {
// TODO read coding here
}
else -> {
logcb.accept("Unknown command from Android: $cmd")
}
}
}
/**
* Stop TCP Command Server
* @return true if succesful