From d2d71a6bc8e445ee60b7be2667676138e277d676 Mon Sep 17 00:00:00 2001 From: feiyu02 <risaku@163.com> Date: 星期五, 28 六月 2024 17:41:08 +0800 Subject: [PATCH] 1. 修改走航报告自动输出模块 --- src/main/kotlin/com/flightfeather/uav/biz/FactorFilter.kt | 36 +++ src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/DataAnalysisConfig.kt | 8 src/main/kotlin/com/flightfeather/uav/common/chart/ChartUtil.kt | 130 +++++++++++- src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionValueMutation.kt | 2 src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuousSingle.kt | 7 src/main/resources/templates/report-underway.ftl | 99 +++++++++ src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt | 15 + src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/ExceptionAnalysisController.kt | 6 src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionSlideAverage.kt | 201 ++++++++++++++++++++ src/main/kotlin/com/flightfeather/uav/common/chart/DataToChartUtil.kt | 13 src/main/kotlin/com/flightfeather/uav/biz/report/MissionReport.kt | 28 ++ 11 files changed, 500 insertions(+), 45 deletions(-) diff --git a/src/main/kotlin/com/flightfeather/uav/biz/FactorFilter.kt b/src/main/kotlin/com/flightfeather/uav/biz/FactorFilter.kt index 3589b05..080d087 100644 --- a/src/main/kotlin/com/flightfeather/uav/biz/FactorFilter.kt +++ b/src/main/kotlin/com/flightfeather/uav/biz/FactorFilter.kt @@ -35,7 +35,41 @@ companion object{ fun builder() = FactorFilter().Builder() - fun default() = builder().create() + fun default() = builder() + .withMain(FactorType.VOC) + .withSubs(listOf( + FactorType.H2S, + FactorType.O3, + FactorType.PM25, + )) + .withMain(FactorType.H2S) + .withSubs(listOf( + FactorType.VOC, + FactorType.O3, + FactorType.PM25, + )) + .withMain(FactorType.O3) + .withSubs(listOf( + FactorType.VOC, + FactorType.H2S, + FactorType.PM25, + )) + .withMain(FactorType.PM25) + .withSubs(listOf( + FactorType.VOC, + FactorType.H2S, + FactorType.O3, + )) +// .withSubs(listOf( +// FactorType.NO2, +// FactorType.CO, +// FactorType.SO2, +// FactorType.O3, +// FactorType.PM25, +// FactorType.PM10, +// FactorType.VOC +// )) + .create() } // 鎵�閫夊洜瀛愰泦鍚� diff --git a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt index 2ea3314..c5ab169 100644 --- a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt +++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt @@ -70,6 +70,8 @@ /** * 鍒ゆ柇寮傚父鍑虹幇鐨勮繛缁椂闀挎槸鍚︽弧瓒虫潯浠� + * @param sIndex + * @param eIndex */ abstract fun judgeDuration(sIndex: Int, eIndex: Int): Boolean @@ -83,6 +85,7 @@ // existException.add(false) // exceptionData.add(mutableListOf()) // } + tagMap.clear() config.factorFilter.mainList().forEach {f-> tagMap[f] = Tag() } @@ -91,16 +94,18 @@ override fun onNextData(data: BaseRealTimeData) { val isContinue = isContinuous(lastData, data) val hasException = judgeException(lastData, data) - config.factorFilter.mainList().forEach {f-> + config.factorFilter.selectedList.forEach {s-> + val f = s.main tagMap[f]?.let { it.eIndex++ // 璧峰鏁版嵁 + it.endData = lastData if (it.endData == null) { it.refreshAfterCheckResult(data) } // 鍒ゆ柇鐩搁偦鏁版嵁鏄惁杩炵画骞朵笖鏄惁婊¤冻寮傚父鍒ゆ柇 if (!isContinue) { - checkResult() + checkResult(s) // 鏁版嵁涓嶈繛缁椂锛岃褰曞紓甯告儏鍐� if (it.eIndex - it.sIndex >= durationCount) { it.refreshAfterCheckResult(data) @@ -111,7 +116,7 @@ it.exceptionData.add(data) } else { // 寮傚父涓嶅啀閲嶅鍑虹幇鏃讹紝璁板綍寮傚父鎯呭喌 - checkResult() + checkResult(s) if (it.eIndex - it.sIndex >= durationCount) { it.refreshAfterCheckResult(data) } @@ -165,7 +170,7 @@ open fun checkResult(factor: FactorFilter.SelectedFactor? = null) { val tag = tagMap[factor?.main] if (factor != null && tag != null) { - if (tag.existException && judgeDuration(tag.sIndex, tag.eIndex)) { + if (tag.existException && judgeDuration(tag.sIndex, tag.eIndex - 1)) { tag.startData?.let { resultList.add(newResult(it, lastData, factor, tag.exceptionData)) } @@ -174,7 +179,7 @@ } else { config.factorFilter.selectedList.forEach { f -> val tag1 = tagMap[f.main] ?: return@forEach - if (tag1.existException && judgeDuration(tag1.sIndex, tag1.eIndex)) { + if (tag1.existException && judgeDuration(tag1.sIndex, tag1.eIndex - 1)) { tag1.startData?.let { resultList.add(newResult(it, lastData, f, tag1.exceptionData)) } diff --git a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuousSingle.kt b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuousSingle.kt index e65d775..f712600 100644 --- a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuousSingle.kt +++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuousSingle.kt @@ -11,7 +11,8 @@ override fun onNextData(data: BaseRealTimeData) { val isContinue = isContinuous(lastData, data) val hasException = judgeException(lastData, data) - config.factorFilter.mainList().forEach {f-> + config.factorFilter.selectedList.forEach {s-> + val f = s.main tagMap[f]?.let { it.eIndex++ if (lastData == null) { @@ -19,7 +20,7 @@ } // 鍒ゆ柇鐩搁偦鏁版嵁鏄惁杩炵画骞朵笖鏄惁婊¤冻寮傚父鍒ゆ柇 if (!isContinue) { - checkResult() + checkResult(s) it.sIndex = it.eIndex it.startData = data } else { @@ -31,7 +32,7 @@ } it.existException = true } else { - checkResult() + checkResult(s) } } } diff --git a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/ExceptionAnalysisController.kt b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/ExceptionAnalysisController.kt index 45008af..d73f3a7 100644 --- a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/ExceptionAnalysisController.kt +++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/ExceptionAnalysisController.kt @@ -2,6 +2,7 @@ import com.flightfeather.uav.biz.FactorFilter import com.flightfeather.uav.biz.dataanalysis.exceptiontype.ExceptionDataExceed +import com.flightfeather.uav.biz.dataanalysis.exceptiontype.ExceptionSlideAverage import com.flightfeather.uav.biz.dataanalysis.exceptiontype.ExceptionValueMutation import com.flightfeather.uav.biz.dataanalysis.model.DataAnalysisConfig import com.flightfeather.uav.biz.dataanalysis.model.ExceptionResult @@ -25,8 +26,9 @@ private fun initTask(config: DataAnalysisConfig) { taskList.clear() taskList.apply { - add(ExceptionDataExceed(config)) - add(ExceptionValueMutation(config)) +// add(ExceptionDataExceed(config)) +// add(ExceptionValueMutation(config)) + add(ExceptionSlideAverage(config)) } } diff --git a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionSlideAverage.kt b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionSlideAverage.kt new file mode 100644 index 0000000..391e20d --- /dev/null +++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionSlideAverage.kt @@ -0,0 +1,201 @@ +package com.flightfeather.uav.biz.dataanalysis.exceptiontype + +import com.flightfeather.uav.biz.FactorFilter +import com.flightfeather.uav.biz.dataanalysis.BaseExceptionAnalysis +import com.flightfeather.uav.biz.dataanalysis.model.DataAnalysisConfig +import com.flightfeather.uav.biz.dataanalysis.model.ExceptionType +import com.flightfeather.uav.domain.entity.BaseRealTimeData +import com.flightfeather.uav.socket.eunm.FactorType +import kotlin.math.abs + +/** + * 婊戝姩骞冲潎鍊肩獊鍙樺紓甯� + */ +class ExceptionSlideAverage(config: DataAnalysisConfig) : BaseExceptionAnalysis(config) { + + private val historyDataList = mutableListOf<BaseRealTimeData>() + private val tempDataList = mutableListOf<BaseRealTimeData>() + private var lastData: BaseRealTimeData? = null +// private val avgListReverse = mutableListOf<Pair<Double, Boolean>>() +// private var startData: BaseRealTimeData? = null +// private var sIndex = 0 +// private var eIndex = -1 +// private var existException = false + + inner class Tag { + // 璧峰鏁版嵁涓嬫爣 + var sIndex = 0 + + // 璧峰鏁版嵁瀵硅薄 + var startData :BaseRealTimeData? = null + + // 鏈熬鏁版嵁涓嬫爣 + var eIndex = -1 + + // 鏈熬鏁版嵁瀵硅薄 + var endData: BaseRealTimeData? = null + + // 鏁版嵁缁勫潎鍊肩殑闆嗗悎 + val avgListReverse = mutableListOf<Pair<Double, Boolean>>() + + // 寮傚父鏁版嵁娈� + var exceptionData = mutableListOf<BaseRealTimeData>() + + // 鏄惁瀛樺湪寮傚父 + var existException = false + + fun refreshAfterCheckResult(data: BaseRealTimeData) { + // 鍒ゆ柇骞舵洿鏂拌捣濮嬬偣浣嶇疆 +// val len = config.changeTrendGroup - 1 + config.changeTrendTimes + config.changeTrendInterval + val len = config.changeTrendGroup + if ((eIndex - sIndex + 1) > len) { + sIndex = eIndex + 1 - len + startData = historyDataList[sIndex] + exceptionData.clear() + exceptionData.addAll(historyDataList.subList(sIndex, eIndex + 1)) + } + } + } + + protected val tagMap = mutableMapOf<FactorType, Tag>() + + override fun init() { + super.init() + historyDataList.clear() + tempDataList.clear() + lastData = null + + tagMap.clear() + config.factorFilter.mainList().forEach {f-> + tagMap[f] = Tag() + } +// avgListReverse.clear() +// startData = null +// sIndex = 0 +// eIndex = -1 +// existException = false + } + + override fun getExceptionType(): ExceptionType = ExceptionType.TYPE7 + + override fun onNextData(data: BaseRealTimeData) { + historyDataList.add(data) + // 鏁版嵁鍔犲叆涓存椂鏁扮粍 + tempDataList.add(data) + // 鏁版嵁閲忚秴鍑鸿缃暟閲忔椂锛屽幓闄ゅ綋鍓嶆暟鎹粍棣栦釜鏁版嵁 + if (tempDataList.size > config.changeTrendGroup) { + tempDataList.removeAt(0) + } + config.factorFilter.selectedList.forEach {s-> + val f = s.main + tagMap[f]?.let { + it.eIndex++ + it.endData = lastData + if (it.startData == null) { + it.startData = data + } + // 鏁版嵁閲忕瓑浜庤缃暟閲忔椂锛岃绠楀綋鍓嶆暟鎹粍鍧囧�� + if (tempDataList.size == config.changeTrendGroup) { + calAvg(f, tempDataList) + if (checkSlideAvg(f)) { + it.existException = true + it.exceptionData.add(data) + } else { + checkResult(s) + it.refreshAfterCheckResult(data) + } + } + } + } + lastData = data + } + + override fun onDone() { + checkResult() + } + + /** + * 璁$畻涓�缁勬暟鎹殑鍧囧�� + */ + private fun calAvg(type: FactorType, list: List<BaseRealTimeData>) { + var total = .0 + var valid = true + val count = list.size + if (count == 0) return + list.forEach { + val v = it.getByFactorType(type) + if (v == null) { + valid = false + } else { + total += v + } + } + val avg = total / count + tagMap[type]?.avgListReverse?.add(0, Pair(avg, valid)) + } + + /** + * 璁$畻鏁版嵁缁勪箣闂寸殑鍧囧�煎樊寮傛槸鍚﹁繛缁秴杩囬檺瀹氭瘮鐜� + */ + private fun checkSlideAvg(type: FactorType): Boolean { + val tag = tagMap[type] ?: return false + // 璁$畻婊戝姩鍧囧�兼渶浣庤姹備釜鏁� + val minSize = config.changeTrendTimes + config.changeTrendInterval + if (tag.avgListReverse.size < minSize) { + return false + } else { + // 婊戝姩鍧囧�兼弧瓒虫暟閲忔椂锛岃绠楀潎鍊间箣闂存槸鍚﹁繛缁秴杩囬檺瀹氭瘮鐜� + val rateList = mutableListOf<Pair<Double, Boolean>>() + for (i in tag.avgListReverse.indices) { + if (i >= config.changeTrendTimes) break + val r = calAvgChangeRate(tag.avgListReverse[i], tag.avgListReverse[i + config.changeTrendInterval]) + rateList.add(r) + } + for (y in rateList) { + if (!y.second || y.first < config.changeTrendRate) { + return false + } + } + return true + } + } + + /** + * 璁$畻婊戝姩鍧囧�煎彉鍖栫巼 + * 姹俛1鐩稿浜巃2鐨勫彉鍖栫巼 + */ + private fun calAvgChangeRate(a1: Pair<Double, Boolean>, a2: Pair<Double, Boolean>): Pair<Double, Boolean> { + val valid = a1.second && a2.second + return if (a2.first == .0) { + Pair(1.0, valid) + } else { + Pair(abs(a1.first - a2.first) / a2.first, valid) + } + } + + /** + * 褰撳墠鏁版嵁鏈嚭鐜板紓甯告椂锛屾垨鏁版嵁寰幆缁撴潫鏃讹紝鍒ゆ柇鍚庣画姝ラ + */ + private fun checkResult(factor: FactorFilter.SelectedFactor? = null) { + val tag = tagMap[factor?.main] + if (factor != null && tag != null) { + if (tag.existException) { + tag.startData?.let { + resultList.add(newResult(it, lastData, factor, tag.exceptionData)) + } + tag.existException = false + } + } else { + config.factorFilter.selectedList.forEach { f -> + val tag1 = tagMap[f.main] ?: return@forEach + if (tag1.existException) { + tag1.startData?.let { + resultList.add(newResult(it, lastData, f, tag1.exceptionData)) + } + tag1.existException = false + } + } + } + + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionValueMutation.kt b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionValueMutation.kt index 0a9e73c..fdaa83c 100644 --- a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionValueMutation.kt +++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/exceptiontype/ExceptionValueMutation.kt @@ -34,6 +34,7 @@ val b2 = r >= config.mutationRate if (b1) special = true res[f] = (b1 || b2) + if (res[f] == true) println("p: $pValue --- n: $nValue --- r: $r") } // repeat(config.factorCount) { i-> // if (p?.getByFactorIndex(i) == null || n.getByFactorIndex(i) == null) { @@ -56,6 +57,7 @@ val b1 = special && (eIndex - sIndex) >= (config.mutationNum / 2) val b2 = (eIndex - sIndex) >= config.mutationNum special = false + println("sIndex: $sIndex --- eIndex: $eIndex --- special: $special") return b1 || b2 } } \ No newline at end of file diff --git a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/DataAnalysisConfig.kt b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/DataAnalysisConfig.kt index 06ffce6..877df87 100644 --- a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/DataAnalysisConfig.kt +++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/model/DataAnalysisConfig.kt @@ -20,4 +20,12 @@ var mutationNum = 6 // 绐佸彉鐜� var mutationRate = .5 + + // 姹傛粦鍔ㄥ钩鍧囧�肩殑鏁版嵁缁勪釜鏁� + var changeTrendGroup = 12 + // 婊戝姩骞冲潎鍊艰繛缁� + var changeTrendInterval = 12 + var changeTrendRate = 1 + // 婊戝姩骞冲潎鍊煎彉鍖栫巼寮傚父杩炵画娆℃暟 + var changeTrendTimes = 3 } \ No newline at end of file diff --git a/src/main/kotlin/com/flightfeather/uav/biz/report/MissionReport.kt b/src/main/kotlin/com/flightfeather/uav/biz/report/MissionReport.kt index ccc8b72..62bbcf6 100644 --- a/src/main/kotlin/com/flightfeather/uav/biz/report/MissionReport.kt +++ b/src/main/kotlin/com/flightfeather/uav/biz/report/MissionReport.kt @@ -4,7 +4,6 @@ import com.flightfeather.uav.biz.dataanalysis.ExceptionAnalysisController import com.flightfeather.uav.biz.dataanalysis.model.ExceptionResult import com.flightfeather.uav.biz.dataprocess.PreData -import com.flightfeather.uav.common.chart.ChartUtil import com.flightfeather.uav.common.chart.DataToChartUtil import com.flightfeather.uav.common.exception.BizException import com.flightfeather.uav.common.pdf.GeneratePdfUtil @@ -13,7 +12,6 @@ import com.flightfeather.uav.domain.entity.Mission import com.flightfeather.uav.domain.repository.MissionRep import com.flightfeather.uav.domain.repository.RealTimeDataRep -import com.flightfeather.uav.socket.eunm.FactorType import org.springframework.beans.BeanUtils import org.springframework.beans.factory.annotation.Value import org.springframework.stereotype.Component @@ -55,11 +53,22 @@ */ fun addExceptions(exceptions: List<ExceptionResult>) { this.exceptions = exceptions.map { - val byteArray = DataToChartUtil.lineToByteArray(it.selectedFactor, it.dataList) - val base64Str = ImageUtil.compressImage2(byteArray, 400, needPrefix = false) val c = ExceptionChart() BeanUtils.copyProperties(it, c) - c.pict = base64Str + // 鍒涘缓涓绘薄鏌撳洜瀛愮殑鏁版嵁鎶樼嚎鍥� + val byteArray = DataToChartUtil.lineToByteArray(it.selectedFactor?.main, it.dataList) + val base64Str = ImageUtil.compressImage2(byteArray, 800, needPrefix = false) + c.mainPict = base64Str + // 鍒涘缓鍏宠仈鍥犲瓙鐨勬暟鎹姌绾垮浘 + if (it.selectedFactor?.subs?.isNotEmpty() == true) { + val subList = mutableListOf<String>() + it.selectedFactor!!.subs.forEach { type -> + val byteArray1 = DataToChartUtil.lineToByteArray(type, it.dataList) + val base64Str1 = ImageUtil.compressImage2(byteArray1, 800, needPrefix = false) + subList.add(base64Str1) + } + c.subPictList = subList + } return@map c } } @@ -83,8 +92,11 @@ * 鏁版嵁寮傚父鎵�鍦ㄦ椂娈电殑鎶樼嚎鍥� */ class ExceptionChart : ExceptionResult() { - // 鏁版嵁鎶樼嚎鍥綛ase64缂栫爜 - var pict: String? = null + // 姹℃煋鍥犲瓙鐨勫紓甯告暟鎹姌绾垮浘Base64缂栫爜 + var mainPict: String? = null + + // 鍏宠仈鍥犲瓙鐨勫紓甯告暟鎹姌绾垮浘Base64缂栫爜 + var subPictList: List<String>? = null } private val templateName = "report-underway.ftl" @@ -118,7 +130,7 @@ max = u.max val byteArray = DataToChartUtil.lineToByteArray(t, realTimeData) - val base64Str = ImageUtil.compressImage2(byteArray, 400, needPrefix = false) + val base64Str = ImageUtil.compressImage2(byteArray, 800, needPrefix = false) pict = base64Str }) } diff --git a/src/main/kotlin/com/flightfeather/uav/common/chart/ChartUtil.kt b/src/main/kotlin/com/flightfeather/uav/common/chart/ChartUtil.kt index 4ce6f08..d448cb2 100644 --- a/src/main/kotlin/com/flightfeather/uav/common/chart/ChartUtil.kt +++ b/src/main/kotlin/com/flightfeather/uav/common/chart/ChartUtil.kt @@ -3,17 +3,21 @@ import org.jfree.chart.ChartFactory import org.jfree.chart.ChartUtils import org.jfree.chart.JFreeChart -import org.jfree.chart.labels.StandardCategoryItemLabelGenerator +import org.jfree.chart.axis.NumberAxis +import org.jfree.chart.axis.NumberTickUnit import org.jfree.chart.labels.StandardCategorySeriesLabelGenerator import org.jfree.chart.plot.CategoryPlot +import org.jfree.chart.plot.PlotOrientation import org.jfree.chart.renderer.category.LineAndShapeRenderer -import org.jfree.chart.title.TextTitle import org.jfree.data.category.DefaultCategoryDataset import java.awt.BasicStroke import java.awt.Color import java.awt.Font import java.awt.Paint import java.io.ByteArrayOutputStream +import java.text.DecimalFormat +import kotlin.math.max +import kotlin.math.min /** * 鍥捐〃鐢熸垚 @@ -22,40 +26,87 @@ */ object ChartUtil { - data class ChartValue(val x: String?, val y: Number) + private const val FONT_NAME = "SimHei" + private val defaultColors = listOf( + Color(191, 0, 0), + Color(181, 125, 69), + Color(236, 204, 6), + Color(84, 151, 57), + Color(13, 221, 151), + Color(57, 75, 151), + Color(115, 51, 206), + ) - fun line(title: String, dataset: DefaultCategoryDataset): JFreeChart { - val line = ChartFactory.createLineChart(title, "鏃堕棿", "娴撳害", dataset) -// line.categoryPlot.domainAxis.labelFont = Font("瀹嬩綋", Font.PLAIN, 12) -// line.categoryPlot.rangeAxis.labelFont = Font("瀹嬩綋", Font.PLAIN, 12) + // 鍥捐〃鏁版嵁鍊煎潗鏍� + data class ChartValue(val x: String?, val y: Double) + // 鍥捐〃鏁版嵁闆� + data class ChartDataset(val dataset: DefaultCategoryDataset, val minValue: Double, val maxValue: Double) + + /** + * 鍒涘缓鎶樼嚎鍥� + */ + fun line(title: String, chartDatasetList: List<ChartDataset>): JFreeChart? { + if (chartDatasetList.isEmpty()) return null + + val line = ChartFactory.createLineChart( + title, "鏃堕棿", "娴撳害(渭g/m3)", + null, PlotOrientation.VERTICAL, true, false, false + ) +// val line = ChartFactory.createLineChart( +// title, "", "娴撳害(渭g/m鲁)", +// null, PlotOrientation.VERTICAL, true, false, false +// ) + var minValue = chartDatasetList[0].minValue + var maxValue = chartDatasetList[0].maxValue + val datasetList = mutableListOf<DefaultCategoryDataset>() + chartDatasetList.forEach { + minValue = min(minValue, it.minValue) + maxValue = max(maxValue, it.maxValue) + datasetList.add(it.dataset) + } + val plot = line.categoryPlot setLine(line) + setRenderer(plot, datasetList) + setX(plot) + setY(plot, "娴撳害(渭g/m3)", minValue, maxValue) return line } - fun lineToByteArray(title: String, dataset: DefaultCategoryDataset):ByteArray { + /** + * 鏂板缓鎶樼嚎鍥惧苟杞崲涓篵yte鏁扮粍 + */ + fun lineToByteArray(title: String, dataset: List<ChartDataset>): ByteArray { val chart = line(title, dataset) val output = ByteArrayOutputStream() - ChartUtils.writeChartAsPNG(output, chart, 600, 400) + ChartUtils.writeChartAsJPEG(output, chart, 1080, 607) val byteArray = output.toByteArray() output.flush() output.close() return byteArray } - fun newDataset(dataList:List<ChartValue>, seriesName:String): DefaultCategoryDataset { + /** + * 鏂板缓鍥捐〃鏁版嵁闆� + */ + fun newDataset(dataList: List<ChartValue>, seriesName: String): ChartDataset? { + if (dataList.isEmpty()) return null val dataset = DefaultCategoryDataset() + var minValue = dataList[0].y + var maxValue = minValue dataList.forEach { dataset.setValue(it.y, seriesName, it.x) + minValue = min(minValue, it.y) + maxValue = max(maxValue, it.y) } - return dataset + return ChartDataset(dataset, minValue, maxValue) } /** * 璁剧疆鎶樼嚎鍥炬牱寮� */ private fun setLine(chart: JFreeChart) { - chart.legend.itemFont = Font("SimHei", Font.PLAIN, 16) - chart.title.font = Font("SimHei", Font.BOLD, 20) + chart.legend.itemFont = Font(FONT_NAME, Font.PLAIN, 16) + chart.title.font = Font(FONT_NAME, Font.BOLD, 20) chart.categoryPlot.apply { backgroundPaint = Color(255, 255, 255) rangeGridlinePaint = Color(200, 200, 200) @@ -64,11 +115,58 @@ } } - private fun setRenderer(plot:CategoryPlot, datasetList:List<DefaultCategoryDataset>) { - val renderer = newBasicRenderer(paint = Color(191, 0, 0)) + private fun setRenderer(plot: CategoryPlot, datasetList: List<DefaultCategoryDataset>) { + datasetList.forEachIndexed { index, v -> + val renderer = newBasicRenderer(paint = defaultColors[index % defaultColors.size]) + plot.setDataset(index, v) + plot.setRenderer(index, renderer) + } + } - private fun newBasicRenderer(series:Int = 0, paint:Paint): LineAndShapeRenderer { + private fun setX(plot: CategoryPlot) { + plot.domainAxis.apply { + // 璁剧疆x杞存瘡涓埢搴︾殑瀛椾綋 + tickLabelFont = Font(FONT_NAME, Font.BOLD, 16) + // 璁剧疆x杞存爣绛剧殑瀛椾綋 + labelFont = Font(FONT_NAME, Font.BOLD, 20) + // 璁剧疆x杞磋酱绾挎槸鍚︽樉绀� + isAxisLineVisible = false + // 璁剧疆x杞村埢搴︽槸鍚︽樉绀� + isTickMarksVisible = false + + upperMargin = .0 + lowerMargin = .0 + } + } + + private fun setY(plot: CategoryPlot, label:String, minValue: Double, maxValue: Double) { + val tickUnit = (maxValue - minValue) / 10 + val axis1 = NumberAxis().apply { + this.label = label + // 鍒诲害灞曠ず鏍煎紡鍖� + numberFormatOverride = DecimalFormat("0.0") + if (tickUnit != .0) { + // 鍙栨秷鑷姩鍒嗛厤闂磋窛 + isAutoTickUnitSelection = false + // 璁剧疆闂撮殧璺濈 + setTickUnit(NumberTickUnit(tickUnit)) + // 璁剧疆鏄剧ず鑼冨洿 + setRange(minValue, maxValue) + } + // 璁剧疆y杞存瘡涓埢搴︾殑瀛椾綋 + tickLabelFont = Font(FONT_NAME, Font.BOLD, 16) + // 璁剧疆y杞存爣绛剧殑瀛椾綋 + labelFont = Font(FONT_NAME, Font.BOLD, 20) + // 璁剧疆y杞磋酱绾夸笉鏄剧ず + isAxisLineVisible = false + // 璁剧疆y杞村埢搴︿笉鏄剧ず + isTickMarksVisible = false + } + plot.rangeAxis = axis1 + } + + private fun newBasicRenderer(series: Int = 0, paint: Paint): LineAndShapeRenderer { return LineAndShapeRenderer().apply { legendItemLabelGenerator = StandardCategorySeriesLabelGenerator() setSeriesStroke(series, BasicStroke(3f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 2000f)) diff --git a/src/main/kotlin/com/flightfeather/uav/common/chart/DataToChartUtil.kt b/src/main/kotlin/com/flightfeather/uav/common/chart/DataToChartUtil.kt index 1530fc2..6222166 100644 --- a/src/main/kotlin/com/flightfeather/uav/common/chart/DataToChartUtil.kt +++ b/src/main/kotlin/com/flightfeather/uav/common/chart/DataToChartUtil.kt @@ -1,6 +1,7 @@ package com.flightfeather.uav.common.chart import com.flightfeather.uav.biz.FactorFilter +import com.flightfeather.uav.common.exception.BizException import com.flightfeather.uav.common.utils.DateUtil import com.flightfeather.uav.domain.entity.BaseRealTimeData import com.flightfeather.uav.socket.eunm.FactorType @@ -12,16 +13,16 @@ */ object DataToChartUtil { - fun lineToByteArray(type: FactorFilter.SelectedFactor?, data: List<BaseRealTimeData>): ByteArray { - val title = type?.main?.des ?: "鏈煡鐩戞祴鍥犲瓙" - val seriesName = type?.main?.des ?: "鏈煡绯诲垪" + fun lineToByteArray(type: FactorType?, data: List<BaseRealTimeData>): ByteArray { + val title = type?.des ?: "鏈煡鐩戞祴鍥犲瓙" + val seriesName = type?.des ?: "鏈煡绯诲垪" val dataList = data.map { d -> ChartUtil.ChartValue( DateUtil.instance.dateToString(d.dataTime, DateUtil.DateStyle.HH_MM_SS), - d.getByFactorType(type?.main) ?: 0f + d.getByFactorType(type)?.toDouble() ?: .0 ) } - val dataset = ChartUtil.newDataset(dataList, seriesName) - return ChartUtil.lineToByteArray(title, dataset) + val dataset = ChartUtil.newDataset(dataList, seriesName) ?: throw BizException("鎶樼嚎鍥炬暟鎹泦涓虹┖") + return ChartUtil.lineToByteArray(title, listOf(dataset)) } } \ No newline at end of file diff --git a/src/main/resources/templates/report-underway.ftl b/src/main/resources/templates/report-underway.ftl index 3b39b2d..73f9bad 100644 --- a/src/main/resources/templates/report-underway.ftl +++ b/src/main/resources/templates/report-underway.ftl @@ -28,6 +28,11 @@ <Relationship Id="imageId_${item_index}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image_${item_index}.png" /> + <#list item.subPictList as subItem> + <Relationship Id="imageId_sub_${item_index}-${subItem_index}" + Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" + Target="media/image_sub_${item_index}-${subItem_index}.png" /> + </#list> </#list> <#list summary as item> <Relationship Id="imageId2_${item_index}" @@ -1247,7 +1252,7 @@ <w:szCs w:val="28" /> <w:lang w:val="en-US" w:eastAsia="zh-CN" /> </w:rPr> - <w:t>浠�${item.startTime}鑷�${item.endTime}锛�${item.factorName}鍑虹幇${item.exception}<#if item.relatedSceneName??>锛岀粨鍚堢洃娴嬫暟鎹彉鍖栧拰鍙楃偣鍙嶅悜婧簮锛�<#list item.relatedSceneName as scene>${scene}銆�</#list>绛夊彲鑳藉瓨鍦ㄦ薄鏌撻闄┿��<#else>銆�</#if></w:t> + <w:t>${item_index + 1}. 浠�${item.startTime}鑷�${item.endTime}锛�${item.factorName}鍑虹幇${item.exception}<#if item.relatedSceneName??>锛岀粨鍚堢洃娴嬫暟鎹彉鍖栧拰鍙楃偣鍙嶅悜婧簮锛�<#list item.relatedSceneName as scene>${scene}銆�</#list>绛夊彲鑳藉瓨鍦ㄦ薄鏌撻闄┿��<#else>銆�</#if></w:t> </w:r> </w:p> <w:p> @@ -1274,7 +1279,7 @@ <w:pict> <v:shape id="_x0000_i1025_${item_index}" o:spt="75" alt="f1725d6e3fb17a0e748045906f55ced" type="#_x0000_t75" - style="height:222.8pt;width:414.65pt;" filled="f" + style="height:225pt;width:400pt;" filled="f" o:preferrelative="t" stroked="f" coordsize="21600,21600"> <v:path /> <v:fill on="f" focussize="0,0" /> @@ -1288,6 +1293,87 @@ </w:pict> </w:r> </w:p> + <#if item.subPictList??> + <w:p> + <w:pPr> + <w:keepNext w:val="0" /> + <w:keepLines w:val="0" /> + <w:pageBreakBefore w:val="0" /> + <w:widowControl w:val="0" /> + <w:kinsoku /> + <w:wordWrap /> + <w:overflowPunct /> + <w:topLinePunct w:val="0" /> + <w:autoSpaceDE /> + <w:autoSpaceDN /> + <w:bidi w:val="0" /> + <w:adjustRightInd /> + <w:snapToGrid /> + <w:ind w:firstLine="560" w:firstLineChars="200" /> + <w:textAlignment w:val="auto" /> + <w:rPr> + <w:rFonts w:hint="default" w:ascii="浠垮畫_GB2312" w:hAnsi="浠垮畫" + w:eastAsia="浠垮畫_GB2312" + w:cs="Times New Roman" /> + <w:sz w:val="28" /> + <w:szCs w:val="28" /> + <w:lang w:val="en-US" w:eastAsia="zh-CN" /> + </w:rPr> + </w:pPr> + <w:r> + <w:rPr> + <w:rFonts w:hint="eastAsia" w:ascii="浠垮畫_GB2312" w:hAnsi="浠垮畫" + w:eastAsia="浠垮畫_GB2312" + w:cs="Times New Roman" /> + <w:color w:val="000000" /> + <w:sz w:val="28" /> + <w:szCs w:val="28" /> + <w:lang w:val="en-US" w:eastAsia="zh-CN" /> + </w:rPr> + <w:t>鍏宠仈鍥犲瓙<#list item.subFactorName as subItem><#if (subItem_index > 0)>銆�</#if>${subItem}</#list>鏁版嵁閲忕骇濡備笅锛�</w:t> + </w:r> + </w:p> + <#list item.subPictList as subItem> + <w:p> + <w:pPr> + <w:jc w:val="center" /> + <w:rPr> + <w:rFonts w:hint="default" w:ascii="浠垮畫_GB2312" w:hAnsi="浠垮畫" + w:eastAsia="浠垮畫_GB2312" /> + <w:sz w:val="28" /> + <w:szCs w:val="28" /> + <w:highlight w:val="none" /> + <w:lang w:val="en-US" w:eastAsia="zh-CN" /> + </w:rPr> + </w:pPr> + <w:r> + <w:rPr> + <w:rFonts w:hint="default" w:ascii="浠垮畫_GB2312" w:hAnsi="浠垮畫" + w:eastAsia="浠垮畫_GB2312" /> + <w:sz w:val="28" /> + <w:szCs w:val="28" /> + <w:highlight w:val="none" /> + <w:lang w:val="en-US" w:eastAsia="zh-CN" /> + </w:rPr> + <w:pict> + <v:shape id="_x0000_i1025_sub_${item_index}-${subItem_index}" o:spt="75" + alt="f1725d6e3fb17a0e748045906f55ced" type="#_x0000_t75" + style="height:225pt;width:400pt;" filled="f" + o:preferrelative="t" stroked="f" coordsize="21600,21600"> + <v:path /> + <v:fill on="f" focussize="0,0" /> + <v:stroke on="f" /> + <v:imagedata r:id="imageId_sub_${item_index}-${subItem_index}" + o:title="f1725d6e3fb17a0e748045906f55ced" /> + <o:lock v:ext="edit" aspectratio="t" /> + <w10:wrap type="none" /> + <w10:anchorlock /> + </v:shape> + </w:pict> + </w:r> + </w:p> + </#list> + </#if> </#list> <w:p> <w:pPr> @@ -1417,7 +1503,7 @@ <w:pict> <v:shape id="_x0000_i1026_${item_index}" o:spt="75" alt="f1725d6e3fb17a0e748045906f55ced" type="#_x0000_t75" - style="height:222.8pt;width:414.65pt;" filled="f" + style="height:225pt;width:400pt;" filled="f" o:preferrelative="t" stroked="f" coordsize="21600,21600"> <v:path /> <v:fill on="f" focussize="0,0" /> @@ -1756,8 +1842,13 @@ </pkg:part> <#list exceptions as item> <pkg:part pkg:name="/word/media/image_${item_index}.png" pkg:contentType="image/png"> - <pkg:binaryData>${item.pict}</pkg:binaryData> + <pkg:binaryData>${item.mainPict}</pkg:binaryData> </pkg:part> + <#list item.subPictList as subItem> + <pkg:part pkg:name="/word/media/image_sub_${item_index}-${subItem_index}.png" pkg:contentType="image/png"> + <pkg:binaryData>${subItem}</pkg:binaryData> + </pkg:part> + </#list> </#list> <#list summary as item> <pkg:part pkg:name="/word/media/image2_${item_index}.png" pkg:contentType="image/png"> -- Gitblit v1.9.3