Compare commits

..

3 Commits

Author SHA1 Message Date
c0e920d1d5 commit 08/10/2025 WebApp 2025-10-08 14:32:09 +07:00
0d6b4aa49e Merge remote-tracking branch 'origin/master' into feature-webapp 2025-10-08 14:25:10 +07:00
efe243e440 commit 07/10/2025 2025-10-08 14:10:50 +07:00
9 changed files with 533 additions and 167 deletions

View File

@@ -1,5 +1,6 @@
import audio.AudioPlayer import audio.AudioPlayer
import audio.ContentCache import audio.ContentCache
import audio.TCPReceiver
import audio.UDPReceiver import audio.UDPReceiver
import barix.BarixConnection import barix.BarixConnection
import barix.TCP_Barix_Command_Server import barix.TCP_Barix_Command_Server
@@ -27,6 +28,7 @@ lateinit var db: MariaDB
lateinit var audioPlayer: AudioPlayer lateinit var audioPlayer: AudioPlayer
val StreamerOutputs: MutableMap<String, BarixConnection> = HashMap() val StreamerOutputs: MutableMap<String, BarixConnection> = HashMap()
lateinit var udpreceiver: UDPReceiver lateinit var udpreceiver: UDPReceiver
lateinit var tcpreceiver: TCPReceiver
const val version = "0.0.2 (23/09/2025)" const val version = "0.0.2 (23/09/2025)"
// AAS 64 channels // AAS 64 channels
const val max_channel = 64 const val max_channel = 64
@@ -140,6 +142,15 @@ fun main() {
Logger.error { "Failed to start UDP Receiver on port 5002" } Logger.error { "Failed to start UDP Receiver on port 5002" }
} }
tcpreceiver = TCPReceiver()
if (tcpreceiver.Start()) {
Logger.info { "TCP Receiver started on port 5002" }
} else {
Logger.error { "Failed to start TCP Receiver on port 5002" }
}
val androidserver = TCP_Android_Command_Server() val androidserver = TCP_Android_Command_Server()
androidserver.StartTcpServer(5003){ androidserver.StartTcpServer(5003){
Logger.info { it } Logger.info { it }
@@ -182,14 +193,18 @@ fun main() {
} }
} }
db.Add_Log("AAS"," Application started")
// shutdown hook // shutdown hook
Runtime.getRuntime().addShutdownHook(Thread { Runtime.getRuntime().addShutdownHook(Thread {
db.Add_Log("AAS"," Application stopping")
Logger.info { "Shutdown hook called, stopping services..." } Logger.info { "Shutdown hook called, stopping services..." }
barixserver.StopTcpCommand() barixserver.StopTcpCommand()
androidserver.StopTcpCommand() androidserver.StopTcpCommand()
onlinechecker.cancel() onlinechecker.cancel()
web.Stop() web.Stop()
udpreceiver.Stop() udpreceiver.Stop()
tcpreceiver.Stop()
audioPlayer.Close() audioPlayer.Close()
db.close() db.close()
Logger.info { "All services stopped, exiting application." } Logger.info { "All services stopped, exiting application." }

View File

@@ -183,7 +183,9 @@ class MainExtension01 {
*/ */
fun Get_Soundbank_Data(value: String) : Map<String, String>? { fun Get_Soundbank_Data(value: String) : Map<String, String>? {
if (ValidString(value)){ if (ValidString(value)){
//println("Parsing soundbank data from value: $value")
val values = value.split(" ").map { it.trim() }.filter { ValidString(it) } val values = value.split(" ").map { it.trim() }.filter { ValidString(it) }
//println("Split into values: $values")
if (values.isNotEmpty()){ if (values.isNotEmpty()){
val result = mutableMapOf<String, String>() val result = mutableMapOf<String, String>()
values.forEach { values.forEach {
@@ -192,6 +194,7 @@ class MainExtension01 {
// ada separator // ada separator
val key = it.substring(0, separatorindex).trim().uppercase() val key = it.substring(0, separatorindex).trim().uppercase()
val value = it.substring(separatorindex+1).trim().uppercase() val value = it.substring(separatorindex+1).trim().uppercase()
//println("Got $key : $value")
if (ValidString(key) && ValidString(value)){ if (ValidString(key) && ValidString(value)){
if (SoundbankKeywords.contains(key)) result[key] = value if (SoundbankKeywords.contains(key)) result[key] = value
} }
@@ -338,11 +341,9 @@ class MainExtension01 {
files.add(city.Path) files.add(city.Path)
} else { } else {
return Result_GetSoundbankFiles(false, "Invalid soundbank file ${city.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "Invalid soundbank file ${city.Path} for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
} }
} else { } else {
return Result_GetSoundbankFiles(false, "No City found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}") return Result_GetSoundbankFiles(false, "No City found for tag=$_tag voicetype=${mb.Voice_Type} language=${mb.Language} ANN_ID=${mb.ANN_ID}")
} }
} }
} else { } else {
@@ -537,7 +538,7 @@ class MainExtension01 {
fun Read_Queue_Paging() : Boolean{ fun Read_Queue_Paging() : Boolean{
db.queuepagingDB.Get() db.queuepagingDB.Get()
val list = db.queuepagingDB.List val list = db.queuepagingDB.List
.filter { it.Source=="PAGING" } .filter { it.Type=="PAGING" }
list.forEach { qp -> list.forEach { qp ->
println("Processing $qp") println("Processing $qp")
@@ -593,7 +594,7 @@ class MainExtension01 {
fun Read_Queue_Shalat() : Boolean{ fun Read_Queue_Shalat() : Boolean{
db.queuepagingDB.Get() db.queuepagingDB.Get()
val list = db.queuepagingDB.List val list = db.queuepagingDB.List
.filter { it.Source=="SHALAT" } .filter { it.Type=="SHALAT" }
list.forEach { qp -> list.forEach { qp ->
println("Processing $qp") println("Processing $qp")
if (qp.BroadcastZones.isNotBlank()){ if (qp.BroadcastZones.isNotBlank()){
@@ -852,13 +853,15 @@ class MainExtension01 {
if (listafi.isNotEmpty()){ if (listafi.isNotEmpty()){
db.queuetableDB.DeleteByIndex(qa.index.toInt()) db.queuetableDB.DeleteByIndex(qa.index.toInt())
// bikin nama file ada postifx nya dari SB_TAGS, tapi spasi diganti underscore
val targetfile = SoundbankResult_directory.resolve(Make_WAV_FileName("Soundbank","")).toString() // dan titik dua diganti dash
println("Writing to target WAV file: $targetfile") val postfix = qa.SB_TAGS.replace(" ","_").replace(":","-")
val targetfile = SoundbankResult_directory.resolve(Make_WAV_FileName("${qa.Source}_${qa.Type}",postfix)).toString()
//println("Writing to target WAV file: $targetfile")
val result = audioPlayer.WavWriter(listafi, targetfile, true) val result = audioPlayer.WavWriter(listafi, targetfile, true)
if (result.success){ if (result.success){
// file siap broadcast // file siap broadcast
println("Successfully wrote WAV file: $targetfile") //println("Successfully wrote WAV file: $targetfile")
val targetafi = audioPlayer.LoadAudioFile(targetfile) val targetafi = audioPlayer.LoadAudioFile(targetfile)
if (targetafi.isValid()) { if (targetafi.isValid()) {
ips.forEach { ip -> StreamerOutputs[ip]?.SendData(targetafi.bytes, { db.Add_Log("AAS", it) }, { db.Add_Log("AAS", it) }) } ips.forEach { ip -> StreamerOutputs[ip]?.SendData(targetafi.bytes, { db.Add_Log("AAS", it) }, { db.Add_Log("AAS", it) }) }

View File

@@ -143,8 +143,7 @@ class AudioPlayer (var samplingrate: Int) {
mem.write(0, data, 0, data.size) mem.write(0, data, 0, data.size)
val pushresult = bass.BASS_StreamPutData(streamhandle, mem, data.size) val pushresult = bass.BASS_StreamPutData(streamhandle, mem, data.size)
if (pushresult==-1){ if (pushresult==-1){
val errcode = bass.BASS_ErrorGetCode() Logger.error { "BASS_StreamPutData failed: ${bass.BASS_ErrorGetCode()}" }
println("BASS_StreamPutData failed: $errcode")
} }
return pushresult != -1 return pushresult != -1
} }
@@ -154,32 +153,27 @@ class AudioPlayer (var samplingrate: Int) {
if (withChime){ if (withChime){
val chup = contentCache.getAudioFile("chimeup") val chup = contentCache.getAudioFile("chimeup")
if (chup!=null && chup.isValid()){ if (chup!=null && chup.isValid()){
if (pushData(chup.bytes)){ if (!pushData(chup.bytes)){
println("Chime up pushed")
} else {
all_success = false all_success = false
println("Chime up failed") Logger.error { "Failed to push Chime Up" }
} }
} else println("Chime Up not valid") } else Logger.error { "withChime=true, but Chime Up not available" }
} }
if (pushData(data)){ if (!pushData(data)){
println("Data pushed")
} else {
all_success = false all_success = false
println("Data push failed") Logger.error { "Failed to push Data ByteArray" }
} }
if (withChime){ if (withChime){
val chdn = contentCache.getAudioFile("chimedown") val chdn = contentCache.getAudioFile("chimedown")
if (chdn!=null && chdn.isValid()){ if (chdn!=null && chdn.isValid()){
if (pushData(chdn.bytes)){ if (!pushData(chdn.bytes)){
println("Chime down pushed")
} else {
all_success = false all_success = false
println("Chime down failed") Logger.error { "Failed to push Chime Down" }
} }
} else println("Chime Down not valid") } else Logger.error { "withChime=true, but Chime Down not available" }
} }
val readsize: Long = 1024 * 1024 // read 1 MB at a time val readsize: Long = 1024 * 1024 // read 1 MB at a time
@@ -191,7 +185,6 @@ class AudioPlayer (var samplingrate: Int) {
totalread += read totalread += read
} }
} while (read > 0) } while (read > 0)
println("Finished reading stream data, total $totalread bytes read")
bassenc.BASS_Encode_Stop(encodehandle) bassenc.BASS_Encode_Stop(encodehandle)
bass.BASS_StreamFree(streamhandle) bass.BASS_StreamFree(streamhandle)
@@ -246,8 +239,7 @@ class AudioPlayer (var samplingrate: Int) {
mem.write(0, data, 0, data.size) mem.write(0, data, 0, data.size)
val pushresult = bass.BASS_StreamPutData(streamhandle, mem, data.size) val pushresult = bass.BASS_StreamPutData(streamhandle, mem, data.size)
if (pushresult==-1){ if (pushresult==-1){
val errcode = bass.BASS_ErrorGetCode() Logger.error { "BASS_StreamPutData failed: ${bass.BASS_ErrorGetCode()}" }
println("BASS_StreamPutData failed: $errcode")
} }
return pushresult != -1 return pushresult != -1
} }
@@ -258,39 +250,34 @@ class AudioPlayer (var samplingrate: Int) {
if (withChime){ if (withChime){
val chup = contentCache.getAudioFile("chimeup") val chup = contentCache.getAudioFile("chimeup")
if (chup!=null && chup.isValid()){ if (chup!=null && chup.isValid()){
if (pushData(chup.bytes)){ if (!pushData(chup.bytes)){
println("Chime up pushed")
} else {
allsuccess = false allsuccess = false
println("Chime up failed") Logger.error { "Failed to push Chime Up" }
} }
} else println("Chime Up not valid") } else Logger.error { "withChime=true, but Chime Up not available" }
} }
sources.forEach { source -> sources.forEach { source ->
if (source.isValid()) { if (source.isValid()) {
// write the bytes to the stream // write the bytes to the stream
if (pushData(source.bytes)){ if (!pushData(source.bytes)){
println("Source ${source.fileName} pushed")
} else {
allsuccess = false allsuccess = false
println("Source ${source.fileName} push failed") Logger.error { "Source ${source.fileName} push failed" }
} }
} else { } else {
allsuccess = false allsuccess = false
println("Source ${source.fileName} is not valid") Logger.error { "Not pushing Source=${source.fileName} because invalid" }
} }
} }
if (withChime){ if (withChime){
val chdn = contentCache.getAudioFile("chimedown") val chdn = contentCache.getAudioFile("chimedown")
if (chdn!=null && chdn.isValid()){ if (chdn!=null && chdn.isValid()){
if (pushData(chdn.bytes)){ if (!pushData(chdn.bytes)){
println("Chime down pushed")
} else {
allsuccess = false allsuccess = false
println("Chime down failed") Logger.error { "Failed to push Chime Down" }
} }
} else println("Chime Down not valid") } else Logger.error { "withChime=true, but Chime Down not available"}
} }
val readsize: Long = 1024 * 1024 // read 1 MB at a time val readsize: Long = 1024 * 1024 // read 1 MB at a time
@@ -302,7 +289,6 @@ class AudioPlayer (var samplingrate: Int) {
totalread += read totalread += read
} }
} while (read > 0) } while (read > 0)
println("Finished reading stream data, total $totalread bytes read")
// close the encoding handle // close the encoding handle

104
src/audio/TCPReceiver.kt Normal file
View File

@@ -0,0 +1,104 @@
package audio
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.tinylog.Logger
import java.net.ServerSocket
import java.util.function.Consumer
/**
* TCPReceiver is a class that listens for TCP connections on a specified port.
* for receiving PCMFILE from Android SAMI
*/
class TCPReceiver(val portnumber: Int = 5002){
private lateinit var server: ServerSocket
private var isRunning = false
private val dataCallback = mutableMapOf<String, Consumer<ByteArray>>()
private val isfinishd = mutableMapOf<String, Boolean>()
/**
* Start listening for TCP connections on the specified port.
* @return true if successful, false otherwise
*/
fun Start() : Boolean{
try{
server = ServerSocket(portnumber)
isRunning = true
Logger.info { "Server started at port $portnumber" }
CoroutineScope(Dispatchers.IO).launch {
while(isRunning){
try {
val client = server.accept()
val clientAddress = client.inetAddress.hostAddress
CoroutineScope(Dispatchers.IO).launch {
isfinishd[clientAddress] = false
var totalbytes = 0L
try{
val din = client.getInputStream()
Logger.info{ "Start receiving PCMFILE from Android with IP=${clientAddress}" }
do {
val buffer = ByteArray(16384)
val bytesRead = din.read(buffer)
if (bytesRead>0){
val data = ByteArray(bytesRead)
System.arraycopy(buffer, 0, data, 0, bytesRead)
//println("Received $bytesRead bytes from $clientAddress")
totalbytes+=bytesRead
dataCallback[clientAddress].let {
it?.accept(data)
}
}
} while (bytesRead > 0)
} catch (e : Exception){
Logger.error { "Failed receiving data from $clientAddress, Message : ${e.message}" }
}
Logger.info { "Connection from $clientAddress ended, total bytesRead=$totalbytes" }
isfinishd[clientAddress] = true
}
} catch (e: Exception) {
Logger.error { "Failed to accept socket, Message : ${e.message}" }
}
}
}
return true
} catch (e : Exception){
Logger.error { "Failed to Start Server at port $portnumber, Message : ${e.message}" }
return false
}
}
fun RequestDataFrom(ipaddress: String, cb: Consumer<ByteArray>){
dataCallback[ipaddress] = cb
}
fun StopRequestDataFrom(ipaddress: String){
if (isfinishd[ipaddress] != null){
if (isfinishd[ipaddress]==false){
// belum selesai
//println("Waiting for receiving from $ipaddress to finish...")
runBlocking {
while (isfinishd[ipaddress] == false){
kotlinx.coroutines.delay(100)
}
}
}
//println("Removing callback for $ipaddress")
dataCallback.remove(ipaddress)
}
}
/**
* Stop listening for TCP connections and close the server socket.
*/
fun Stop(){
if (isRunning){
isRunning = false
server.close()
}
}
}

View File

@@ -2,7 +2,6 @@ package commandServer
import codes.Somecodes.Companion.PagingResult_directory import codes.Somecodes.Companion.PagingResult_directory
import codes.Somecodes.Companion.filenameformat import codes.Somecodes.Companion.filenameformat
import org.tinylog.Logger
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.nio.file.Path import java.nio.file.Path
import java.time.LocalDateTime import java.time.LocalDateTime
@@ -18,6 +17,11 @@ class PagingJob(val fromIP: String, val broadcastzones: String) {
var totalBytesReceived = 0; private set var totalBytesReceived = 0; private set
var isRunning = true; private set var isRunning = true; private set
/**
* Expected Size from PCMFILE android
*/
var expectedSize = 0
/** /**
* Adds incoming audio data to the job. * Adds incoming audio data to the job.

View File

@@ -3,6 +3,7 @@ package commandServer
import audioPlayer import audioPlayer
import codes.Somecodes.Companion.ValidString import codes.Somecodes.Companion.ValidString
import codes.Somecodes.Companion.datetimeformat1 import codes.Somecodes.Companion.datetimeformat1
import content.Category
import content.Language import content.Language
import database.Messagebank import database.Messagebank
import database.QueuePaging import database.QueuePaging
@@ -16,6 +17,7 @@ import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.tinylog.Logger import org.tinylog.Logger
import tcpreceiver
import udpreceiver import udpreceiver
import java.net.ServerSocket import java.net.ServerSocket
import java.net.Socket import java.net.Socket
@@ -167,102 +169,158 @@ class TCP_Android_Command_Server {
} }
} }
"PCMFILE_START","STARTPAGINGAND" -> { "PCMFILE_START" ->{
//TODO Paging IPM1 success, Paging IPMT gagal // start sending PCM data from Android for paging
val zones = parts.getOrElse(1) { "" }.replace(",",";") val size = parts.getOrElse(1) { "0" }.toInt()
val filename = parts.getOrElse(2) { "" }
val zones = parts.getOrElse(3) { "" }.replace(",",";")
if (size>0){
if (ValidString(filename)){
if (ValidString(zones)){ if (ValidString(zones)){
// create paging job // create paging job
val pj = PagingJob(key, zones) val pj = PagingJob(key, zones)
// ada expected size
pj.expectedSize = size
// masukin ke list // masukin ke list
listOnGoingPaging[key] = pj listOnGoingPaging[key] = pj
Logger.info{"PagingJob created for Android $key, zones: $zones, file: ${pj.filePath.absolutePathString()}"} Logger.info{"PagingJob created for Android $key, zones: $zones, file: ${pj.filePath.absolutePathString()}"}
// start minta data dari udpreceiver tcpreceiver.RequestDataFrom(key) {
udpreceiver.RequestDataFrom(key){
// push data ke paging job // push data ke paging job
pj.addData(it, it.size) pj.addData(it, it.size)
} }
logcb.accept("Paging started from Android $key") cb.accept("PCMFILE_START;OK@")
cb.accept(parts[0]+";OK@") Logger.info{"Android $key start sending PCM data, expecting $size bytes"}
return return
} else logcb.accept("Paging start from Android $key failed, empty zones")
cb.accept(parts[0]+";NG@")
}
"PCMFILE_STOP","STOPPAGINGAND" -> { } else logcb.accept("PCMFILE_START from Android $key failed, empty zones")
// TODO Paging IPM1 success, Paging IPMT gagal } else logcb.accept("PCMFILE_START from Android $key failed, empty filename")
} else logcb.accept("PCMFILE_START from Android $key failed, invalid size")
cb.accept("PCMFILE_START;NG@")
}
"PCMFILE_STOP" -> {
// stop sending PCM data from Android for paging
val pj = listOnGoingPaging[key] val pj = listOnGoingPaging[key]
if (pj!=null) { if (pj!=null) {
listOnGoingPaging.remove(key) listOnGoingPaging.remove(key)
udpreceiver.StopRequestDataFrom(key) tcpreceiver.StopRequestDataFrom(key)
logcb.accept("Paging stopped from Android $key")
cb.accept(parts[0]+";OK@")
// get remaining data // get remaining data
val data = pj.GetData() val data = pj.GetData()
pj.Close() pj.Close()
if (data.size==pj.expectedSize){
Logger.info { "Paging job closed from Android $key, total bytes received ${data.size}, writing to file ${pj.filePath.absolutePathString()}" } Logger.info { "Paging job closed from Android $key, total bytes received ${data.size}, writing to file ${pj.filePath.absolutePathString()}" }
val result = audioPlayer.WavWriter(data, pj.filePath.absolutePathString(), true) val result = audioPlayer.WavWriter(data, pj.filePath.absolutePathString(), true)
if (result.success) { if (result.success) {
val qp = QueuePaging( val qp = QueuePaging(
0u, 0u,
LocalDateTime.now().format(datetimeformat1), LocalDateTime.now().format(datetimeformat1),
"ANDROID",
"PAGING", "PAGING",
"NORMAL",
pj.filePath.absolutePathString(), pj.filePath.absolutePathString(),
pj.broadcastzones pj.broadcastzones
) )
if (db.queuepagingDB.Add(qp)) { if (db.queuepagingDB.Add(qp)) {
logcb.accept("Paging audio inserted to queue paging table from Android $key, file ${pj.filePath.absolutePathString()}") logcb.accept("Paging audio inserted to queue paging table from Android $key, file ${pj.filePath.absolutePathString()}")
cb.accept(parts[0]+";OK@") cb.accept("PCMFILE_STOP;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@")
}
} else {
logcb.accept("Failed to write paging audio to file ${pj.filePath.absolutePathString()}, Message : ${result.message}")
cb.accept(parts[0]+";NG@")
return return
} else logcb.accept("Failed to insert paging audio to queue paging table from Android $key, file ${pj.filePath.absolutePathString()}")
} else logcb.accept("Failed to write paging audio to file ${pj.filePath.absolutePathString()}, Message : ${result.message}")
} else logcb.accept("PCMFILE_STOP from Android $key received size ${data.size} does not match expected ${pj.expectedSize}")
} else logcb.accept("PCMFILE_STOP from Android $key failed, no ongoing PCM data receiving")
cb.accept("PCMFILE_STOP;NG@")
} }
"STARTPAGINGAND" -> {
// Start Paging request from IPM
val zones = parts.getOrElse(1) { "" }.replace(",",";")
if (ValidString(zones)){
// create pagingjob
val pj = PagingJob(key, zones)
// masukin ke list
listOnGoingPaging[key] = pj
Logger.info{"PagingJob created for IPM $key, zones: $zones, file: ${pj.filePath.absolutePathString()}"}
} else { // start minta data dari udpreceiver
logcb.accept("Paging stop from Android $key failed, no ongoing paging") udpreceiver.RequestDataFrom(key){
cb.accept(parts[0]+";NG@") // push data ke paging job
pj.addData(it, it.size)
} }
logcb.accept("Paging started from IPM $key")
cb.accept("STARTPAGINGAND;OK@")
return
} else logcb.accept("Paging start from IPM $key failed, empty zones")
cb.accept("STARTPAGINGAND;NG@")
}
"STOPPAGINGAND" -> {
// stop paging request from IPM
val pj = listOnGoingPaging[key]
if (pj!=null){
listOnGoingPaging.remove(key)
udpreceiver.StopRequestDataFrom(key)
logcb.accept("Paging stopped from IPM $key")
// get remaining data
val data = pj.GetData()
pj.Close()
Logger.info{"Paging job closed from IPM $key, total bytes received ${data.size}, writing to file ${pj.filePath.absolutePathString()}"}
val result = audioPlayer.WavWriter(data, pj.filePath.absolutePathString(), true)
if (result.success){
val qp = QueuePaging(
0u,
LocalDateTime.now().format(datetimeformat1),
"IPM",
"PAGING",
pj.filePath.absolutePathString(),
pj.broadcastzones
)
if (db.queuepagingDB.Add(qp)){
logcb.accept("Paging audio inserted to queue paging table from IPM $key, file ${pj.filePath.absolutePathString()}")
cb.accept("STOPPAGINGAND;OK@")
return
} else logcb.accept("Failed to insert paging audio to queue paging table from IPM $key, file ${pj.filePath.absolutePathString()}")
} else logcb.accept("Failed to write paging audio to file ${pj.filePath.absolutePathString()}, Message : ${result.message}")
} else logcb.accept("Paging stop from IPM $key failed, no ongoing paging")
cb.accept("STOPPAGINGAND;NG@")
} }
"CANCELPAGINGAND" -> { "CANCELPAGINGAND" -> {
// TODO Cancel Paging IPM1 success, IPMT gagal // cancel paging request from IPM
val pj = listOnGoingPaging[key] val pj = listOnGoingPaging[key]
if (pj!=null){ if (pj!=null){
pj.Close() pj.Close()
listOnGoingPaging.remove(key) listOnGoingPaging.remove(key)
udpreceiver.StopRequestDataFrom(key) udpreceiver.StopRequestDataFrom(key)
logcb.accept("Paging from Android $key cancelled") logcb.accept("Paging from IPM $key cancelled")
cb.accept("CANCELPAGINGAND;OK@") cb.accept("CANCELPAGINGAND;OK@")
return return
} else logcb.accept("Paging cancel from Android $key failed, no ongoing paging") } else logcb.accept("Paging cancel from IPM $key failed, no ongoing paging")
cb.accept("CANCELPAGINGAND;NG@") cb.accept("CANCELPAGINGAND;NG@")
} }
"STARTINITIALIZE" -> { "STARTINITIALIZE" -> {
// TODO STARTINITIALIZE IPM1 success, IPMT gagal
val username = parts.getOrElse(1) { "" } val username = parts.getOrElse(1) { "" }
if (ValidString(username)){ if (ValidString(username)){
val userlogin = listUserLogin.find { it.username == username } val userlogin = listUserLogin.find { it.username == username }
if (userlogin != null){ if (userlogin != null){
val userdb = db.userDB.List.find { it.username == username } val userdb = db.userDB.List.find { it.username == username }
if (userdb != null){ if (userdb != null){
println("Sending initialization data to $key with username $username")
val result = StringBuilder() val result = StringBuilder()
// kirim Zone
result.append("ZONE") result.append("ZONE")
userdb.broadcastzones.split(";").map { it.trim() }.filter { it.isNotBlank() }.forEach { userdb.broadcastzones.split(";").map { it.trim() }.filter { it.isNotBlank() }.forEach {
result.append(";") result.append(";")
result.append(it) result.append(it)
} }
result.append("@") result.append("@")
cb.accept(result.toString())
// kirim MSGTOTAL
result.clear()
val VARMESSAGES = mutableListOf<Messagebank>() val VARMESSAGES = mutableListOf<Messagebank>()
result.append("MSGTOTAL;")
userdb.messagebank_ann_id userdb.messagebank_ann_id
// messagebank_ann_id adalah rentengan ANN_ID (digit) yang dipisah dengan ; // messagebank_ann_id adalah rentengan ANN_ID (digit) yang dipisah dengan ;
.split(";") .split(";")
@@ -270,112 +328,272 @@ class TCP_Android_Command_Server {
.map { it.trim() } .map { it.trim() }
// bukan string kosong antar dua tanda ; // bukan string kosong antar dua tanda ;
.filter { it.isNotBlank() } .filter { it.isNotBlank() }
// beneran digit semua
.filter { xx -> xx.all{it.isDigit()} }
// iterasi setiap ANN_ID // iterasi setiap ANN_ID
.forEach { annid -> .forEach { annid ->
// masukin ke VARMESSAGES yang unik secara ANN_ID dan Language // masukin ke VARMESSAGES yang unik secara ANN_ID dan Language
val xx = db.messageDB.List val xx = db.messageDB.List
.filter{ it.ANN_ID == annid.toUInt() } .filter{ it.ANN_ID == annid.toUInt() }
.distinctBy { it.Language } .distinctBy { it.ANN_ID }
VARMESSAGES.addAll(xx) VARMESSAGES.addAll(xx)
} }
result.append("MSGTOTAL;").append(VARMESSAGES.size).append("@") result.append(VARMESSAGES.size).append("@")
// VAR AP TOTAL
val VARAPTOTAL = mutableListOf<Soundbank>()
val al_split = userdb.airline_tags.split(";").map { it.trim() }.filter { it.isNotBlank() }
al_split.forEach {
val sb = db.Find_Soundbank_AirplaneName(it).firstOrNull()
if (sb != null) VARAPTOTAL.add(sb)
}
result.append("VARAPTOTAL;").append(VARAPTOTAL.size).append("@")
// VAR CITY TOTAL
val VARCITYTOTAL = mutableListOf<Soundbank>()
val ct_split = userdb.city_tags.split(";").map { it.trim() }.filter { it.isNotBlank() }
ct_split.forEach {
val sb = db.Find_Soundbank_City(it).firstOrNull()
if (sb != null) VARCITYTOTAL.add(sb)
}
result.append("VARCITYTOTAL;").append(VARCITYTOTAL.size).append("@")
// VAR PLACES TOTAL
val VARPLACESTOTAL = mutableListOf<Soundbank>()
// sb_split.forEach {
// val sb = db.Find_Soundbank_Places(it).firstOrNull()
// if (sb != null) VARPLACESTOTAL.add(sb)
// }
result.append("VARPLACESTOTAL;").append(VARPLACESTOTAL.size).append("@")
// VAR SHALAT TOTAL
val VARSHALATTOTAL = mutableListOf<Soundbank>()
// sb_split.forEach {
// val sb = db.Find_Soundbank_Shalat(it).firstOrNull()
// if (sb != null) VARSHALATTOTAL.add(sb)
// }
result.append("VARSHALATTOTAL;").append(VARSHALATTOTAL.size).append("@")
// VAR SEQUENCE TOTAL
val VARSEQUENCETOTAL = mutableListOf<Soundbank>()
// sb_split.forEach {
// val sb = db.Find_Soundbank_Sequence(it).firstOrNull()
// if (sb != null) VARSEQUENCETOTAL.add(sb)
// }
// VAR REASON TOTAL
val VARREASONTOTAL = mutableListOf<Soundbank>()
// sb_split.forEach {
// val sb = db.Find_Soundbank_Reason(it).firstOrNull()
// if (sb != null) VARREASONTOTAL.add(sb)
// }
result.append("VARREASONTOTAL;").append(VARREASONTOTAL.size).append("@")
// VAR PROCEDURE TOTAL
val VARPROCEDURETOTAL = mutableListOf<Soundbank>()
// sb_split.forEach {
// val sb = db.Find_Soundbank_Procedure(it).firstOrNull()
// if (sb != null) VARPROCEDURETOTAL.add(sb)
// }
result.append("VARPROCEDURETOTAL;").append(VARPROCEDURETOTAL.size).append("@")
// send to sender
cb.accept(result.toString()) cb.accept(result.toString())
// kirim VARAPTOTAL
result.clear() result.clear()
result.append("VARAPTOTAL;")
val VARAPTOTAL = mutableListOf<Soundbank>()
userdb.airline_tags
.split(";")
.map { it.trim() }
.filter { it.isNotBlank() }
.forEach { al ->
val sb = db.soundDB.List
.filter { it.Category.equals(Category.Airplane_Name.name, true) }
.filter { it.TAG.equals(al, true)}
.distinctBy { it.TAG }
VARAPTOTAL.addAll(sb)
}
result.append(VARAPTOTAL.size).append("@")
cb.accept(result.toString())
// kirim VARCITYTOTAL
result.clear()
result.append("VARCITYTOTAL;")
val VARCITYTOTAL = mutableListOf<Soundbank>()
userdb.city_tags
.split(";")
.map { it.trim() }
.filter { it.isNotBlank() }
.forEach { ct ->
val sb = db.soundDB.List
.filter { it.Category.equals(Category.City.name, true) }
.filter { it.TAG.equals(ct, true)}
.distinctBy { it.TAG }
VARCITYTOTAL.addAll(sb)
}
result.append(VARCITYTOTAL.size).append("@")
cb.accept(result.toString())
// kirim VARPLACESTOTAL
result.clear()
result.append("VARPLACESTOTAL;")
val VARPLACESTOTAL = mutableListOf<Soundbank>()
db.soundDB.List
.filter { it.Category.equals(Category.Places.name, true) }
.distinctBy { it.TAG }
.forEach {
VARPLACESTOTAL.add(it)
}
result.append(VARPLACESTOTAL.size).append("@")
cb.accept(result.toString())
// kirim VARSHALATTOTAL
result.clear()
result.append("VARSHALATTOTAL;")
val VARSHALATTOTAL = mutableListOf<Soundbank>()
db.soundDB.List
.filter { it.Category.equals(Category.Shalat.name, true) }
.distinctBy { it.TAG }
.forEach {
VARSHALATTOTAL.add(it)
}
result.append(VARSHALATTOTAL.size).append("@")
cb.accept(result.toString())
// kirim VARSEQUENCETOTAL
result.clear()
result.append("VARSEQUENCETOTAL;")
val VARSEQUENCETOTAL = mutableListOf<Soundbank>()
db.soundDB.List
.filter { it.Category.equals(Category.Sequence.name, true) }
.distinctBy { it.TAG }
.forEach {
VARSEQUENCETOTAL.add(it)
}
result.append(VARSEQUENCETOTAL.size).append("@")
cb.accept(result.toString())
// kirim VARREASONTOTAL
result.clear()
result.append("VARREASONTOTAL;")
val VARREASONTOTAL = mutableListOf<Soundbank>()
db.soundDB.List
.filter { it.Category.equals(Category.Reason.name, true) }
.distinctBy { it.TAG }
.forEach {
VARREASONTOTAL.add(it)
}
result.append(VARREASONTOTAL.size).append("@")
cb.accept(result.toString())
// kirim VARPROCEDURETOTAL
val VARPROCEDURETOTAL = mutableListOf<Soundbank>()
result.clear()
result.append("VARPROCEDURETOTAL;")
db.soundDB.List
.filter { it.Category.equals(Category.Procedure.name, true) }
.distinctBy { it.TAG }
.forEach {
VARPROCEDURETOTAL.add(it)
}
result.append(VARPROCEDURETOTAL.size).append("@")
cb.accept(result.toString())
// kirim VARGATETOTAL
val VARGATETOTAL = mutableListOf<Soundbank>()
result.clear()
result.append("VARGATETOTAL;")
db.soundDB.List
.filter { it.Category.equals(Category.Gate.name, true) }
.distinctBy { it.TAG }
.forEach {
VARGATETOTAL.add(it)
}
result.append(VARGATETOTAL.size).append("@")
cb.accept(result.toString())
// kirim VARCOMPENSATIONTOTAL
result.clear()
result.append("VARCOMPENSATIONTOTAL;")
val VARCOMPENSATIONTOTAL = mutableListOf<Soundbank>()
db.soundDB.List
.filter { it.Category.equals(Category.Compensation.name, true) }
.distinctBy { it.TAG }
.forEach {
VARCOMPENSATIONTOTAL.add(it)
}
result.append(VARCOMPENSATIONTOTAL.size).append("@")
cb.accept(result.toString())
// kirim VARGREETINGTOTAL
result.clear()
result.append("VARGREETINGTOTAL;")
val VARGREETINGTOTAL = mutableListOf<Soundbank>()
db.soundDB.List
.filter { it.Category.equals(Category.Greeting.name, true) }
.distinctBy { it.TAG }
.forEach {
VARGREETINGTOTAL.add(it)
}
result.append(VARGREETINGTOTAL.size).append("@")
cb.accept(result.toString())
//Append MSG, for Android only Indonesia and English //Append MSG, for Android only Indonesia and English
VARMESSAGES.groupBy { it.ANN_ID }.forEach { (ann_id, value) -> if (VARMESSAGES.isNotEmpty()) {
result.append("MSG;").append(ann_id) result.clear()
result.append(";") VARMESSAGES.forEachIndexed { index, msg ->
value.find { it.Language.equals(Language.INDONESIA.name, true) }?.let {result.append(it.Message_Detail)} ?: result.append("NA")
result.append(";") val ann_id = msg.ANN_ID
value.find {it.Language.equals(Language.ENGLISH.name, true) }?.let {result.append(it.Message_Detail)} ?: result.append("NA") val msg_indo = db.messageDB.List.find {
result.append("@") it.ANN_ID == ann_id && it.Language.equals(
Language.INDONESIA.name,
true
)
}
val msg_eng = db.messageDB.List.find {
it.ANN_ID == ann_id && it.Language.equals(
Language.ENGLISH.name,
true
)
}
val description = msg_indo?.Description ?: msg_eng?.Description ?: "UNKNOWN"
result.append("MSG;$index;$ann_id;$description;")
result.append(msg_indo?.Message_Detail ?:"").append(";")
result.append(msg_eng?.Message_Detail ?:"").append("@")
}
cb.accept(result.toString())
} }
// append VARAP // append VARAP
VARAPTOTAL.distinctBy { it.Description }.forEachIndexed { index, soundbank -> if (VARAPTOTAL.isNotEmpty()) {
result.append("VARAP;").append(index).append(";").append(soundbank.TAG).append(";").append(soundbank.Description).append("@") result.clear()
VARAPTOTAL.forEachIndexed { index, sb ->
result.append("VARAP;$index;${sb.TAG};${sb.Description}@")
} }
cb.accept(result.toString())
}
// append VARCITY // append VARCITY
VARCITYTOTAL.distinctBy { it.Description }.forEachIndexed { index, soundbank -> if (VARCITYTOTAL.isNotEmpty()) {
result.append("VARCITY;").append(index).append(";").append(soundbank.TAG).append(";").append(soundbank.Description).append("@") result.clear()
VARCITYTOTAL.forEachIndexed { index, sb ->
result.append("VARCITY;$index;${sb.TAG};${sb.Description}@")
} }
cb.accept(result.toString())
}
// append VARPLACES // append VARPLACES
VARPLACESTOTAL.distinctBy { it.Description }.forEachIndexed { index, soundbank -> if (VARPLACESTOTAL.isNotEmpty()) {
result.append("VARPLACES;").append(index).append(";").append(soundbank.TAG).append(";").append(soundbank.Description).append("@") result.clear()
VARPLACESTOTAL.forEachIndexed { index, sb ->
result.append("VARPLACES;$index;${sb.TAG};${sb.Description}@")
}
cb.accept(result.toString())
} }
// append VARSHALAT // append VARSHALAT
VARSHALATTOTAL.distinctBy { it.Description }.forEachIndexed { index, soundbank -> if (VARSHALATTOTAL.isNotEmpty()) {
result.append("VARSHALAT;").append(index).append(";").append(soundbank.TAG).append(";").append(soundbank.Description).append("@") result.clear()
VARSHALATTOTAL.forEachIndexed { index, sb ->
result.append("VARSHALAT;$index;${sb.TAG};${sb.Description}@")
} }
// append VARSEQUENCE
VARSEQUENCETOTAL.distinctBy { it.Description }.forEachIndexed { index, soundbank ->
result.append("VARSEQUENCE;").append(index).append(";").append(soundbank.TAG).append(";").append(soundbank.Description).append("@")
}
// append VARREASON
VARREASONTOTAL.distinctBy { it.Description }.forEachIndexed { index, soundbank ->
result.append("VARREASON;").append(index).append(";").append(soundbank.TAG).append(";").append(soundbank.Description).append("@")
}
// append VARPROCEDURE
VARPROCEDURETOTAL.distinctBy { it.Description }.forEachIndexed { index, soundbank ->
result.append("VARPROCEDURE;").append(index).append(";").append(soundbank.TAG).append(";").append(soundbank.Description).append("@")
}
// send to sender
cb.accept(result.toString()) cb.accept(result.toString())
}
// append VARSEQUENCE
if (VARSEQUENCETOTAL.isNotEmpty()) {
result.clear()
VARSEQUENCETOTAL.forEachIndexed { index, sb ->
result.append("VARSEQUENCE;$index;${sb.TAG};${sb.Description}@")
}
cb.accept(result.toString())
}
// append VARREASON
if (VARREASONTOTAL.isNotEmpty()) {
result.clear()
VARREASONTOTAL.forEachIndexed { index, sb ->
result.append("VARREASON;$index;${sb.TAG};${sb.Description}@")
}
cb.accept(result.toString())
}
// append VARPROCEDURE
if (VARPROCEDURETOTAL.isNotEmpty()) {
result.clear()
VARPROCEDURETOTAL.forEachIndexed { index, sb ->
result.append("VARPROCEDURE;$index;${sb.TAG};${sb.Description}@")
}
cb.accept(result.toString())
}
// append VARGATE
if (VARGATETOTAL.isNotEmpty()) {
result.clear()
VARGATETOTAL.forEachIndexed { index, sb ->
result.append("VARGATE;$index;${sb.TAG};${sb.Description}@")
}
cb.accept(result.toString())
}
// append VARCOMPENSATION
if (VARCOMPENSATIONTOTAL.isNotEmpty()) {
result.clear()
VARCOMPENSATIONTOTAL.forEachIndexed { index, sb ->
result.append("VARCOMPENSATION;$index;${sb.TAG};${sb.Description}@")
}
cb.accept(result.toString())
}
// append VARGREETING
if (VARGREETINGTOTAL.isNotEmpty()) {
result.clear()
VARGREETINGTOTAL.forEachIndexed { index, sb ->
result.append("VARGREETING;$index;${sb.TAG};${sb.Description}@")
}
cb.accept(result.toString())
}
logcb.accept("All variables sent to $key with username $username") logcb.accept("All variables sent to $key with username $username")
return return
} else logcb.accept("STARTINITIALIZE failed from $key with username $username not found in userDB") } else logcb.accept("STARTINITIALIZE failed from $key with username $username not found in userDB")
@@ -387,8 +605,11 @@ class TCP_Android_Command_Server {
"BROADCASTAND" -> { "BROADCASTAND" -> {
// semi auto dari android, masukin ke queue table // semi auto dari android, masukin ke queue table
val desc = parts.getOrElse(1) { "" } val desc = parts.getOrElse(1) { "" }
// language bisa lebih dari satu, dipisah dengan koma
val lang = parts.getOrElse(2) { "" }.replace(",",";") val lang = parts.getOrElse(2) { "" }.replace(",",";")
// tags bisa lebih dari satu, dipisah dengan spasi
val tags = parts.getOrElse(3) { "" }.replace(",",";") val tags = parts.getOrElse(3) { "" }.replace(",",";")
// zone bisa lebih dari satu, dipisah dengan koma
val zone = parts.getOrElse(4) { "" }.replace(",",";") val zone = parts.getOrElse(4) { "" }.replace(",",";")
if (ValidString(desc)){ if (ValidString(desc)){
if (ValidString(lang)){ if (ValidString(lang)){
@@ -414,7 +635,7 @@ class TCP_Android_Command_Server {
} else logcb.accept("Broadcsast request from Android $key username=${listUserLogin.find { it.ip==key }?.username ?: "UNKNOWN"} failed, empty tags") } 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 language")
} else logcb.accept("Broadcast request from Android $key username=${listUserLogin.find { it.ip==key }?.username ?: "UNKNOWN"} failed, empty description") } else logcb.accept("Broadcast request from Android $key username=${listUserLogin.find { it.ip==key }?.username ?: "UNKNOWN"} failed, empty description")
cb.accept("NG@") cb.accept("BROADCASTAND;NG@")
} }

View File

@@ -14,5 +14,8 @@ enum class Category(name: String) {
Birthday("Birthday"), Birthday("Birthday"),
Reason("Reason"), Reason("Reason"),
Sequence("Sequence"), Sequence("Sequence"),
Procedure("Procedure"); Procedure("Procedure"),
Gate("Gate"),
Greeting("Greeting"),
Compensation("Compensation");
} }

View File

@@ -1179,7 +1179,7 @@ class MariaDB(
override fun Add(data: QueueTable): Boolean { override fun Add(data: QueueTable): Boolean {
try { try {
val statement = connection.prepareStatement( val statement = connection.prepareStatement(
"INSERT INTO ${super.dbName} (Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, Repeat, Language) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" "INSERT INTO ${super.dbName} (`Date_Time`, `Source`, `Type`, `Message`, `SB_TAGS`, `BroadcastZones`, `Repeat`, `Language`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
) )
statement?.setString(1, data.Date_Time) statement?.setString(1, data.Date_Time)
statement?.setString(2, data.Source) statement?.setString(2, data.Source)
@@ -1191,7 +1191,7 @@ class MariaDB(
statement?.setString(8, data.Language) statement?.setString(8, data.Language)
val rowsAffected = statement?.executeUpdate() val rowsAffected = statement?.executeUpdate()
if (rowsAffected != null && rowsAffected > 0) { if (rowsAffected != null && rowsAffected > 0) {
Logger.info("QueueTable added: ${data.Message}" as Any) Logger.info("QueueTable added Source=${data.Source} Type=${data.Type} Message=${data.Message}, Languages=${data.Language} Variables=${data.SB_TAGS}, BroadcastZones=${data.BroadcastZones}" as Any)
return true return true
} else { } else {
Logger.warn("No QueueTable entry added for: ${data.Message}" as Any) Logger.warn("No QueueTable entry added for: ${data.Message}" as Any)
@@ -1206,7 +1206,7 @@ class MariaDB(
try { try {
connection.autoCommit = false connection.autoCommit = false
val sql = val sql =
"INSERT INTO ${super.dbName} (Date_Time, Source, Type, Message, SB_TAGS, BroadcastZones, Repeat, Language) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" "INSERT INTO ${super.dbName} (`Date_Time`, `Source`, `Type`, `Message`, `SB_TAGS`, `BroadcastZones`, `Repeat`, `Language`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
val statement = connection.prepareStatement(sql) val statement = connection.prepareStatement(sql)
for (qt in data) { for (qt in data) {
statement.setString(1, qt.Date_Time) statement.setString(1, qt.Date_Time)

View File

@@ -15,12 +15,7 @@ import content.Category
import content.Language import content.Language
import content.ScheduleDay import content.ScheduleDay
import content.VoiceType import content.VoiceType
import database.BroadcastZones import database.*
import database.LanguageLink
import database.MariaDB
import database.Messagebank
import database.SoundChannel
import database.Soundbank
import db import db
import io.javalin.Javalin import io.javalin.Javalin
import io.javalin.apibuilder.ApiBuilder.before import io.javalin.apibuilder.ApiBuilder.before
@@ -654,7 +649,41 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
} }
} }
post("Add"){ post("Add"){
// TODO add new schedule // TODO add new schedule
// recheck lagi tambahan steph
val json: JsonNode = objectmapper.readTree(it.body())
val description = json.get("Description")?.asText("") ?: ""
val day = json.get("Day")?.asText("") ?: ""
val time = json.get("Time")?.asText("") ?: ""
val soundpath = json.get("Soundpath")?.asText("") ?: ""
val repeat = json.get("Repeat")?.asInt()?.toUByte() ?: 0u
val enable = json.get("Enable")?.asBoolean() ?: false
val broadcast_zones = json.get("BroadcastZones")?.asText("") ?: ""
val language = json.get("Language")?.asText("") ?: ""
if (ValidString(description)){
if (ValidString(day) && ValidScheduleDay(day)){
if (ValidString(time) && ValidScheduleTime(time)){
if (ValidString(soundpath) && ValidFile(soundpath)){
if (repeat in 0u..127u){
if (ValidString(broadcast_zones)){
val zones = broadcast_zones.split(";")
if (zones.all { zz -> db.broadcastDB.List.any { xx -> xx.description.equals(zz,true) } }){
if (ValidString(language) && Language.entries.any{ lang -> lang.name == language }){
val newvalue = ScheduleBank(0u, description, day, time, soundpath, repeat, enable, broadcast_zones, language)
if (db.scheduleDB.Add(newvalue)){
db.scheduleDB.Resort()
it.result(objectmapper.writeValueAsString(resultMessage("OK")))
} else it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to add schedule to database")))
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Language")))
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Contains unsupported BroadcastZones")))
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid BroadcastZones")))
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Repeat, must be between 0-127")))
} else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Soundpath")))
} 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("DeleteByIndex/{index}") {
// delete by index // delete by index
@@ -1145,6 +1174,7 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
} }
} }
} }
} }
} }
}.start(listenPort) }.start(listenPort)