commit 02/10/2025

This commit is contained in:
2025-10-02 13:17:52 +07:00
parent 83a6ee9fd0
commit 1e7adeba25
5 changed files with 278 additions and 19 deletions

View File

@@ -0,0 +1,45 @@
package commandServer
import codes.Somecodes.Companion.PagingResult_directory
import codes.Somecodes.Companion.filenameformat
import org.tinylog.Logger
import java.io.ByteArrayOutputStream
import java.nio.file.Path
import java.time.LocalDateTime
/**
* Class to handle a paging job, storing incoming audio data and metadata.
* @param fromIP The IP address from which the paging data is received.
* @param broadcastzones The zones to which the paging is broadcasted, is a semicolon-separated string.
*/
class PagingJob(val fromIP: String, val broadcastzones: String) {
val filePath : Path = PagingResult_directory.resolve(LocalDateTime.now().format(filenameformat)+"_RAW.wav")
private val bos : ByteArrayOutputStream = ByteArrayOutputStream()
var totalBytesReceived = 0; private set
var isRunning = true; private set
/**
* Adds incoming audio data to the job.
* @param data The byte array containing audio data.
* @param length The number of bytes to write from the data array.
*/
fun addData(data: ByteArray, length: Int) {
Logger.info{"PagingJob from $fromIP, zones: $broadcastzones, received $length bytes"}
bos.write(data, 0, length)
totalBytesReceived += length
}
/**
* Retrieves the accumulated audio data as a byte array.
* @return A byte array containing all received audio data.
*/
fun GetData(): ByteArray {
return bos.toByteArray()
}
fun Close(){
bos.close()
isRunning = false
}
}

View File

@@ -1,7 +1,12 @@
package commandServer
import audioPlayer
import codes.Somecodes.Companion.ValidString
import codes.Somecodes.Companion.datetimeformat1
import content.Language
import database.Messagebank
import database.QueuePaging
import database.QueueTable
import database.Soundbank
import db
import kotlinx.coroutines.CoroutineScope
@@ -11,11 +16,14 @@ import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.tinylog.Logger
import udpreceiver
import java.net.ServerSocket
import java.net.Socket
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.time.LocalDateTime
import java.util.function.Consumer
import kotlin.io.path.absolutePathString
@Suppress("unused")
class TCP_Android_Command_Server {
@@ -24,6 +32,7 @@ class TCP_Android_Command_Server {
private val socketMap = mutableMapOf<String, Socket>()
lateinit var logcb: Consumer<String>
private val listUserLogin = mutableListOf<userLogin>()
private val listOnGoingPaging = mutableMapOf<String, PagingJob>()
/**
* Start TCP Command Server
@@ -45,7 +54,8 @@ class TCP_Android_Command_Server {
{
CoroutineScope(Dispatchers.Main).launch {
if (socket != null) {
val key: String = socket.inetAddress.hostAddress + ":" + socket.port
// key is IP address only
val key: String = socket.inetAddress.hostAddress
socketMap[key] = socket
Logger.info { "Start communicating with $key" }
socket.getInputStream().let { din ->
@@ -110,23 +120,31 @@ class TCP_Android_Command_Server {
return ByteArray(0)
}
/**
* Process command from Android client
* @param key The client IP address
* @param cmd The command string
* @param cb Callback to send reply string
*/
private fun process_command(key: String, cmd: String, cb: Consumer<String>) {
Logger.info { "Command from $key : $cmd" }
val parts = cmd.split(";").map { it.trim() }.filter { it.isNotBlank() }.map { it.uppercase() }
when (parts[0]) {
"GETLOGIN" -> {
// Android login request
val username = parts.getOrElse(1) { "" }
val password = parts.getOrElse(2) { "" }
if (ValidString(username) && ValidString(password)) {
if (db.userDB.List.any{it.username==username && it.password==password}) {
cb.accept("LOGIN;TRUE@")
val existing = listUserLogin.find { it.ip == key}
if (existing!=null){
existing.username = username
} else{
listUserLogin.add(userLogin(key, username))
}
cb.accept("LOGIN;TRUE@")
logcb.accept("Android Login success from $key as $username")
return
} else {
logcb.accept("Android Login failed from $key as $username")
cb.accept("LOGIN;FALSE@")
@@ -137,27 +155,85 @@ class TCP_Android_Command_Server {
}
}
"PCMFILE_START" -> {
// TODO read coding here
"PCMFILE_START","STARTPAGINGAND" -> {
val zones = parts.getOrElse(3) { "" }.replace(",",";")
if (ValidString(zones)){
// create pagingjob
val pj = PagingJob(key, zones)
// masukin ke list
listOnGoingPaging[key] = pj
// start minta data dari udpreceiver
udpreceiver.RequestDataFrom(key){
// push data ke paging job
pj.addData(it, it.size)
}
logcb.accept("Paging started from Android $key")
cb.accept(parts[0]+";OK@")
return
} else logcb.accept("Paging start from Android $key failed, empty zones")
cb.accept(parts[0]+";NG@")
}
"PCMFILE_STOP" -> {
// TODO read coding here
}
"PCMFILE_STOP","STOPPAGINGAND" -> {
val pj = listOnGoingPaging[key]
if (pj!=null){
listOnGoingPaging.remove(key)
udpreceiver.StopRequestDataFrom(key)
logcb.accept("Paging stopped from Android $key")
cb.accept(parts[0]+";OK@")
// get remaining data
val data = pj.GetData()
pj.Close()
audioPlayer.WavWriter(data, pj.filePath.absolutePathString()){
success, message ->
if (success){
// insert to paging queue
val qp = QueuePaging(
0u,
LocalDateTime.now().format(datetimeformat1),
"PAGING",
"NORMAL",
pj.filePath.absolutePathString(),
pj.broadcastzones
)
if (db.queuepagingDB.Add(qp)){
logcb.accept("Paging audio inserted to queue paging table from Android $key, file ${pj.filePath.absolutePathString()}")
cb.accept(parts[0]+";OK@")
} else {
logcb.accept("Failed to insert paging audio to queue paging table from Android $key, file ${pj.filePath.absolutePathString()}")
cb.accept(parts[0]+";NG@")
}
"STARTPAGINGAND" -> {
// TODO read coding here
}
} else {
logcb.accept("Failed to write paging audio to file ${pj.filePath.absolutePathString()}, Message : $message")
cb.accept(parts[0]+";NG@")
}
}
} else {
logcb.accept("Paging stop from Android $key failed, no ongoing paging")
cb.accept(parts[0]+";NG@")
}
"STOPPAGINGAND" -> {
// TODO read coding here
}
"CANCELPAGINGAND" -> {
// TODO read coding here
val pj = listOnGoingPaging[key]
if (pj!=null){
pj.Close()
listOnGoingPaging.remove(key)
udpreceiver.StopRequestDataFrom(key)
logcb.accept("Paging from Android $key cancelled")
cb.accept("CANCELPAGINGAND;OK@")
return
} else logcb.accept("Paging cancel from Android $key failed, no ongoing paging")
cb.accept("CANCELPAGINGAND;NG@")
}
"STARTINITIALIZE" -> {
// pengiriman variabel ke Android
val username = parts.getOrElse(1) { "" }
if (ValidString(username)){
val userlogin = listUserLogin.find { it.username == username }
@@ -183,8 +259,11 @@ class TCP_Android_Command_Server {
.filter { xx -> xx.all{it.isDigit()} }
// iterasi setiap ANN_ID
.forEach { annid ->
// masukin ke VARMESSAGES yang unik secara ANN_ID dan Description
VARMESSAGES.addAll(db.messageDB.List.distinctBy { it.ANN_ID }.distinctBy { it.Description })
// masukin ke VARMESSAGES yang unik secara ANN_ID dan Language
val xx = db.messageDB.List
.filter{ it.ANN_ID == annid.toUInt() }
.distinctBy { it.Language }
VARMESSAGES.addAll(xx)
}
result.append("MSGTOTAL;").append(VARMESSAGES.size).append("@")
// VAR AP TOTAL
@@ -240,7 +319,16 @@ class TCP_Android_Command_Server {
cb.accept(result.toString())
result.clear()
//TODO append MSG
//Append MSG, for Android only Indonesia and English
VARMESSAGES.groupBy { it.ANN_ID }.forEach { (ann_id, value) ->
result.append("MSG;").append(ann_id)
result.append(";")
value.find { it.Language== Language.INDONESIA.name }?.let {result.append(it.Message_Detail)} ?: result.append("NA")
result.append(";")
value.find {it.Language== Language.ENGLISH.name }?.let {result.append(it.Message_Detail)} ?: result.append("NA")
result.append("@")
}
// append VARAP
VARAPTOTAL.distinctBy { it.Description }.forEachIndexed { index, soundbank ->
@@ -274,7 +362,7 @@ class TCP_Android_Command_Server {
cb.accept(result.toString())
logcb.accept("All variables sent to $key with username $username")
return
} else logcb.accept("STARTINITIALIZE failed from $key with username $username not found in userDB")
} else logcb.accept("STARTINITIALIZE failed from $key with unregistered username $username")
} else logcb.accept("STARTINITIALIZE failed from $key with empty username")
@@ -282,7 +370,37 @@ class TCP_Android_Command_Server {
}
"BROADCASTAND" -> {
// TODO read coding here
// semi auto dari android, masukin ke queue table
val desc = parts.getOrElse(1) { "" }
val lang = parts.getOrElse(2) { "" }.replace(",",";")
val tags = parts.getOrElse(3) { "" }.replace(",",";")
val zone = parts.getOrElse(4) { "" }.replace(",",";")
if (ValidString(desc)){
if (ValidString(lang)){
if (ValidString(tags)){
if (ValidString(zone)){
val qt = QueueTable(
0u,
LocalDateTime.now().format(datetimeformat1),
"ANDROID",
"SOUNDBANK",
desc,
tags,
zone,
1u,
lang
)
if (db.queuetableDB.Add(qt)){
logcb.accept("Broadcast request from Android $key username=${listUserLogin.find { it.ip==key }?.username ?: "UNKNOWN"} inserted. Message: $desc;$lang;$tags;$zone")
cb.accept("BROADCASTAND;OK@")
return
} else logcb.accept("Broadcast request from Android $key username=${listUserLogin.find { it.ip==key }?.username ?: "UNKNOWN"} failed, cannot add to queue table")
} else logcb.accept("Broadcast request from Android $key username=${listUserLogin.find { it.ip==key }?.username ?: "UNKNOWN"} failed, empty zone")
} else logcb.accept("Broadcsast request from Android $key username=${listUserLogin.find { it.ip==key }?.username ?: "UNKNOWN"} failed, empty tags")
} else logcb.accept("Broadcast request from Android $key username=${listUserLogin.find { it.ip==key }?.username ?: "UNKNOWN"} failed, empty language")
} else logcb.accept("Broadcast request from Android $key username=${listUserLogin.find { it.ip==key }?.username ?: "UNKNOWN"} failed, empty description")
cb.accept("NG@")
}
else -> {