package codes import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import content.ScheduleDay import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch import oshi.SystemInfo import oshi.hardware.CentralProcessor import oshi.hardware.GlobalMemory import java.nio.file.Files import java.nio.file.Path import java.time.LocalDateTime import java.time.format.DateTimeFormatter import java.util.function.Consumer import kotlin.io.path.name @Suppress("unused") class Somecodes { companion object { val current_directory : String = System.getProperty("user.dir") val SoundbankResult_directory : Path = Path.of(current_directory,"SoundbankResult") val PagingResult_directory : Path = Path.of(current_directory,"PagingResult") val si = SystemInfo() val processor: CentralProcessor = si.hardware.processor val memory : GlobalMemory = si.hardware.memory val datetimeformat1: DateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss") val dateformat1: DateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy") val dateformat2: DateTimeFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy") val timeformat1: DateTimeFormatter = DateTimeFormatter.ofPattern("hh:mm:ss") val timeformat2: DateTimeFormatter = DateTimeFormatter.ofPattern("hh:mm") val filenameformat: DateTimeFormatter = DateTimeFormatter.ofPattern("ddMMyyyy_HHmmss") const val KB_threshold = 1024.0 const val MB_threshold = KB_threshold * 1024.0 const val GB_threshold = MB_threshold * 1024.0 const val TB_threshold = GB_threshold * 1024.0 val objectmapper = jacksonObjectMapper() // regex for getting ann_id from Message, which is the number inside [] private val ann_id_regex = Regex("\\[(\\d+)]") /** * Check if a string is a valid number. */ fun IsNumber(value: String) : Boolean { return value.toIntOrNull() != null } /** * Check if a string is alphabetic (contains only letters). */ fun IsAlphabethic(value: String) : Boolean { return value.all { it.isLetter() } } /** * Extract ANN ID from a message string. * The ANN ID is expected to be a number enclosed in square brackets (e.g., "[123]"). * @param message The message string to extract the ANN ID from. * @return The extracted ANN ID as an integer, or -1 if not found or invalid. */ fun Get_ANN_ID(message: String) : Int { val matchResult = ann_id_regex.find(message) return matchResult?.groups?.get(1)?.value?.toInt() ?: -1 } /** * Convert an object to a JSON string. * @param data The object to convert. * @return A JSON string representation of the object, or "{}" if conversion fails. */ fun toJsonString(data: Any) : String { return try { objectmapper.writeValueAsString(data) } catch (e: Exception){ "{}" } } /** * Convert a JSON string to an object of the specified class. * @param json The JSON string to convert. * @param clazz The class of the object to convert to. * @return An object of the specified class, or null if conversion fails. */ fun fromJsonString(json: String, clazz: Class) : T? { return try { objectmapper.readValue(json, clazz) } catch (e: Exception){ null } } /** * Convert a JSON string to a JsonNode. * @param data The JSON string to convert. * @return A JsonNode representation of the JSON string, or empty JsonNode if conversion fails. */ fun toJsonNode(data: String) : JsonNode { return try { objectmapper.readTree(data) } catch (e: Exception){ objectmapper.createObjectNode() } } /** * List all audio files (.mp3 and .wav) in the specified directory and its subdirectories. * Only files larger than 1KB are included. * @param dir The directory to search in (default is the current working directory). * @return A list of absolute paths to the audio files found. */ fun ListAudioFiles(dir: String = current_directory) : List{ return try{ // find all files that ends with .mp3 or .wav // and find them recursively val p = Path.of(dir) if (Files.exists(p) && Files.isDirectory(p)){ Files.walk(p) // cari file regular saja .filter { Files.isRegularFile(it)} // size lebih dari 1KB .filter { Files.size(it) > 1024} // extension .mp3 atau .wav .filter { it.name.endsWith(".mp3",true) || it.name.endsWith(".wav",true) } .map { it.toAbsolutePath().toString() } .toList() } else throw Exception() } catch (_ : Exception){ emptyList() } } /** * Converts a size in bytes to a human-readable format. * @param size Size in bytes. * @return A string representing the size in a human-readable format. */ fun SizetoHuman(size: Long): String { return when { size < KB_threshold -> "${size}B" size < MB_threshold -> String.format("%.2f KB", size / KB_threshold) size < GB_threshold -> String.format("%.2f MB", size / MB_threshold) size < TB_threshold -> String.format("%.2f GB", size / GB_threshold) else -> String.format("%.2f TB", size / TB_threshold) } } /** * Get Disk usage using OSHI library. * @param path The path to check disk usage, defaults to the current working directory. * @return A string representing the disk usage of the file system in a human-readable format. */ fun getDiskUsage(path: String = current_directory) : String { return try{ val p = Path.of(path).toFile() if (p.exists() && p.isDirectory){ val total = p.totalSpace val free = p.freeSpace val used = total - free String.format("Total: %s, Used: %s, Free: %s, Usage: %.2f%%", SizetoHuman(total), SizetoHuman(used), SizetoHuman(free), (used.toDouble() / total * 100) ) } else throw Exception() } catch (_ : Exception){ "N/A" } } /** * Get CPU usage using OSHI library. * @param cb A callback function that receives the CPU usage as a string. */ fun getCPUUsage(cb : Consumer){ CoroutineScope(Dispatchers.Default).launch { val prev = processor.systemCpuLoadTicks delay(1000) val current = processor.systemCpuLoadTicks fun delta(t: CentralProcessor.TickType) = current[t.index] - prev[t.index] val idle = delta(CentralProcessor.TickType.IDLE) + delta(CentralProcessor.TickType.IOWAIT) val busy = delta(CentralProcessor.TickType.USER) + delta(CentralProcessor.TickType.SYSTEM) + delta(CentralProcessor.TickType.NICE) + delta(CentralProcessor.TickType.IRQ) + delta(CentralProcessor.TickType.SOFTIRQ)+ delta(CentralProcessor.TickType.STEAL) val total = idle + busy val usage = if (total > 0) { (busy.toDouble() / total) * 100 } else { 0.0 } cb.accept(String.format("%.2f%%", usage)) } } /** * Get RAM usage using OSHI library. * @return A string representing the total, used, and available memory in a human-readable format. */ fun getMemoryUsage() : String{ val totalMemory = memory.total val availableMemory = memory.available val usedMemory = totalMemory - availableMemory return String.format("Total: %s, Used: %s, Available: %s, Usage: %.2f%%", SizetoHuman(totalMemory), SizetoHuman(usedMemory), SizetoHuman(availableMemory) , (usedMemory.toDouble() / totalMemory * 100)) } /** * Check if a value is a valid non-blank string. * @param value The value to check. * @return True if the value is a non-blank string, false otherwise. */ fun ValidString(value: Any) : Boolean { return value is String && value.isNotBlank() } /** * Check if a string is a valid file path and the file exists. * @param value The string to check. * @return True if the string is a valid file path, false otherwise. */ fun ValidFile(value : String) : Boolean { if (value.isNotBlank()){ return Files.exists(Path.of(value)) } return false } /** * Check if a string is a valid date in the format "dd/MM/yyyy". * @param value The string to check. * @return True if the string is a valid date, false otherwise. */ fun ValidDate(value: String): Boolean{ return try{ if (ValidString(value)){ dateformat1.parse(value) true } else throw Exception() } catch (_: Exception){ false } } /** * Check if a string is a valid IPv4 address. * @param value The string to check. * @return True if the string is a valid IPv4 address, false otherwise. */ fun ValidIPV4(value: String): Boolean{ return try{ if (ValidString(value)){ val parts = value.split(".") if (parts.size != 4) return false for (part in parts){ val num = part.toInt() if (num !in 0..255) return false } true } else throw Exception() } catch (_: Exception){ false } } /** * Check if a string is a valid date in the format "dd-MM-yyyy". * This format is used for log HTML files. * @param value The string to check. * @return True if the string is a valid date, false otherwise. */ fun ValiDateForLogHtml(value: String): Boolean{ return try{ if (ValidString(value)){ dateformat2.parse(value) true } else throw Exception() } catch (_: Exception){ false } } /** * Check if a string is a valid time in the format "hh:mm:ss". * @param value The string to check. * @return True if the string is a valid time, false otherwise. */ fun ValidTime(value: String): Boolean{ return try{ if (ValidString(value)){ timeformat1.parse(value) true } else throw Exception() } catch (_: Exception){ false } } /** * Check if a string is a valid schedule time in the format "HH:mm". * @param value The string to check. * @return True if the string is a valid schedule time, false otherwise. */ fun ValidScheduleTime(value: String): Boolean{ // format HH:mm try { if (ValidString(value)){ timeformat2.parse(value) return true } } catch (_ : Exception){ } return false } /** * Find a schedule day by its name. * @param value The name of the schedule day to find. * @return The name of the schedule day if found, null otherwise. */ fun FindScheduleDay(value: String) : String? { val sd = ScheduleDay.entries.find { sd -> sd.name == value } return sd?.name } /** * Check if a string is a valid schedule day or a valid date. * A valid schedule day is either one of the ScheduleDay enum names or a date in the format "dd/MM/yyyy". * @param value The string to check. * @return True if the string is a valid schedule day or date, false otherwise. */ fun ValidScheduleDay(value: String) : Boolean { if (ValidString(value)){ // check if value is one of ScheduleDay enum name if (FindScheduleDay(value) != null){ return true } // check if value is in format dd/MM/yyyy return ValidDate(value) } return false } /** * Generate a WAV file name with the current date and time. * The file name format is: [prefix]_ddMMyyyy_HHmmss_[postfix].wav * @param prefix An optional prefix to add before the date and time. * @param postfix An optional postfix to add after the date and time. * @return A string representing the generated WAV file name. */ fun Make_WAV_FileName(prefix: String, postfix: String) : String{ val sb = StringBuilder() if (prefix.isNotEmpty()){sb.append(prefix).append("_")} sb.append(filenameformat.format(LocalDateTime.now())) if (postfix.isNotEmpty()){sb.append("_").append(postfix)} sb.append(".wav") return sb.toString() } } }