diff --git a/src/main/java/com/ffii/fpsms/modules/monitoring/service/LabelPrinterMonitorService.kt b/src/main/java/com/ffii/fpsms/modules/monitoring/service/LabelPrinterMonitorService.kt index 82a36ae..1d0d98e 100644 --- a/src/main/java/com/ffii/fpsms/modules/monitoring/service/LabelPrinterMonitorService.kt +++ b/src/main/java/com/ffii/fpsms/modules/monitoring/service/LabelPrinterMonitorService.kt @@ -169,6 +169,75 @@ open class LabelPrinterMonitorService( ) } + @Transactional(readOnly = true) + open fun odometerRangeStats(from: LocalDateTime, to: LocalDateTime): Map { + require(!from.isAfter(to)) { "fromDateTime must be before toDateTime" } + require(!to.isAfter(from.plusDays(historyMaxRangeDays))) { + "Date range must not exceed $historyMaxRangeDays days" + } + + val zebraPrinters = labelPrinters().filter { ZebraLinkOsClient.isZebraBrand(it.brand) } + val results = zebraPrinters.mapNotNull { p -> + val printerId = p.id ?: return@mapNotNull null + val startRow = jdbcDao.queryForList( + """ + SELECT odometer_total AS odometerTotal, recorded_at AS recordedAt + FROM printer_label_odometer_log + WHERE printer_id = :printerId + AND odometer_total IS NOT NULL + AND recorded_at >= :from + AND recorded_at <= :to + ORDER BY recorded_at ASC, id ASC + LIMIT 1 + """.trimIndent(), + mapOf( + "printerId" to printerId, + "from" to from, + "to" to to, + ), + ).firstOrNull() + val endRow = jdbcDao.queryForList( + """ + SELECT odometer_total AS odometerTotal, recorded_at AS recordedAt + FROM printer_label_odometer_log + WHERE printer_id = :printerId + AND odometer_total IS NOT NULL + AND recorded_at >= :from + AND recorded_at <= :to + ORDER BY recorded_at DESC, id DESC + LIMIT 1 + """.trimIndent(), + mapOf( + "printerId" to printerId, + "from" to from, + "to" to to, + ), + ).firstOrNull() + + val startOdo = numberValue(startRow?.get("odometerTotal")) + val endOdo = numberValue(endRow?.get("odometerTotal")) + val delta = if (startOdo != null && endOdo != null && endOdo >= startOdo) endOdo - startOdo else null + + mapOf( + "printerId" to printerId, + "code" to p.code, + "name" to p.name, + "brand" to p.brand, + "startAt" to startRow?.get("recordedAt")?.toString(), + "startOdometer" to startOdo, + "endAt" to endRow?.get("recordedAt")?.toString(), + "endOdometer" to endOdo, + "delta" to delta, + ) + } + + return mapOf( + "from" to from.toString(), + "to" to to.toString(), + "printers" to results, + ) + } + private fun labelPrinters(): List = printerRepository.findAllByDeletedIsFalse().filter { it.type == "Label" } diff --git a/src/main/java/com/ffii/fpsms/modules/monitoring/web/LabelPrinterMonitorController.kt b/src/main/java/com/ffii/fpsms/modules/monitoring/web/LabelPrinterMonitorController.kt index be76a5d..75537dc 100644 --- a/src/main/java/com/ffii/fpsms/modules/monitoring/web/LabelPrinterMonitorController.kt +++ b/src/main/java/com/ffii/fpsms/modules/monitoring/web/LabelPrinterMonitorController.kt @@ -44,6 +44,21 @@ class LabelPrinterMonitorController( return ResponseEntity.ok(labelPrinterMonitorService.labelPrintStats(from, to)) } + @GetMapping("/odometer-stats") + fun odometerStats( + @RequestParam fromDateTime: String, + @RequestParam toDateTime: String, + ): ResponseEntity { + val from = parseDateTimeParam(fromDateTime, endOfDay = false) + ?: return ResponseEntity.badRequest().body(mapOf("error" to "Invalid fromDateTime")) + val to = parseDateTimeParam(toDateTime, endOfDay = true) + ?: return ResponseEntity.badRequest().body(mapOf("error" to "Invalid toDateTime")) + if (from.isAfter(to)) { + return ResponseEntity.badRequest().body(mapOf("error" to "fromDateTime must be before toDateTime")) + } + return ResponseEntity.ok(labelPrinterMonitorService.odometerRangeStats(from, to)) + } + private fun parseDateTimeParam(raw: String, endOfDay: Boolean): LocalDateTime? { val text = raw.trim() if (text.isEmpty()) return null