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