|
|
@@ -2,10 +2,9 @@ package com.ruoyi.project.production.controller;
|
|
|
|
|
|
import java.text.ParseException;
|
|
|
import java.text.SimpleDateFormat;
|
|
|
-import java.util.ArrayList;
|
|
|
-import java.util.Calendar;
|
|
|
-import java.util.Date;
|
|
|
-import java.util.List;
|
|
|
+import java.time.LocalDate;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
+import java.util.*;
|
|
|
|
|
|
import com.github.stuxuhai.jpinyin.PinyinException;
|
|
|
import com.ruoyi.common.jpush.JiGuangPushService;
|
|
|
@@ -17,7 +16,7 @@ import com.ruoyi.project.plant.domain.TStaffmgr;
|
|
|
import com.ruoyi.project.plant.service.ITStaffmgrService;
|
|
|
import com.ruoyi.project.production.controller.vo.FurnancePressureFvpVO;
|
|
|
import com.ruoyi.project.production.controller.vo.FurnanceSummaryVO;
|
|
|
-import com.ruoyi.project.production.controller.vo.FurnanceTemperatureCoilVO;
|
|
|
+import com.ruoyi.project.production.controller.vo.FurnanceTemperatureVO;
|
|
|
import com.ruoyi.project.production.controller.vo.FurnanceTemperatureVO;
|
|
|
import com.ruoyi.project.production.domain.TFurnancePressure;
|
|
|
import com.ruoyi.project.production.domain.TFurnanceTemperature;
|
|
|
@@ -28,6 +27,10 @@ import com.ruoyi.project.system.service.ISysConfigService;
|
|
|
import com.ruoyi.project.system.service.ISysDictTypeService;
|
|
|
import com.ruoyi.project.system.service.ISysUserService;
|
|
|
import org.springframework.security.access.prepost.PreAuthorize;
|
|
|
+import com.ruoyi.common.utils.MathUtil;
|
|
|
+import com.ruoyi.project.production.controller.vo.FurnanceTemperatureCoilVO;
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
+
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.web.bind.annotation.GetMapping;
|
|
|
import org.springframework.web.bind.annotation.PostMapping;
|
|
|
@@ -990,43 +993,43 @@ public class TFurnanceTemperatureController extends BaseController
|
|
|
tFurnanceTemperature.setFurnanceName("H109");
|
|
|
List<TFurnanceTemperature> list109 = tFurnanceTemperatureService.selectTFurnanceTemperatureList(tFurnanceTemperature);
|
|
|
logger.debug("查询H109数据条数: {}", list109.size());
|
|
|
-
|
|
|
+
|
|
|
tFurnanceTemperature.setFurnanceName("H110");
|
|
|
List<TFurnanceTemperature> list110 = tFurnanceTemperatureService.selectTFurnanceTemperatureList(tFurnanceTemperature);
|
|
|
logger.debug("查询H110数据条数: {}", list110.size());
|
|
|
-
|
|
|
+
|
|
|
tFurnanceTemperature.setFurnanceName("H111");
|
|
|
List<TFurnanceTemperature> list111 = tFurnanceTemperatureService.selectTFurnanceTemperatureList(tFurnanceTemperature);
|
|
|
logger.debug("查询H111数据条数: {}", list111.size());
|
|
|
-
|
|
|
+
|
|
|
tFurnanceTemperature.setFurnanceName("H112");
|
|
|
List<TFurnanceTemperature> list112 = tFurnanceTemperatureService.selectTFurnanceTemperatureList(tFurnanceTemperature);
|
|
|
logger.debug("查询H112数据条数: {}", list112.size());
|
|
|
-
|
|
|
+
|
|
|
tFurnanceTemperature.setFurnanceName("H113");
|
|
|
List<TFurnanceTemperature> list113 = tFurnanceTemperatureService.selectTFurnanceTemperatureList(tFurnanceTemperature);
|
|
|
logger.debug("查询H113数据条数: {}", list113.size());
|
|
|
-
|
|
|
+
|
|
|
tFurnanceTemperature.setFurnanceName("H114");
|
|
|
List<TFurnanceTemperature> list114 = tFurnanceTemperatureService.selectTFurnanceTemperatureList(tFurnanceTemperature);
|
|
|
logger.debug("查询H114数据条数: {}", list114.size());
|
|
|
-
|
|
|
+
|
|
|
tFurnanceTemperature.setFurnanceName("H115");
|
|
|
List<TFurnanceTemperature> list115 = tFurnanceTemperatureService.selectTFurnanceTemperatureList(tFurnanceTemperature);
|
|
|
logger.debug("查询H115数据条数: {}", list115.size());
|
|
|
-
|
|
|
+
|
|
|
tFurnanceTemperature.setFurnanceName("H116");
|
|
|
List<TFurnanceTemperature> list116 = tFurnanceTemperatureService.selectTFurnanceTemperatureList(tFurnanceTemperature);
|
|
|
logger.debug("查询H116数据条数: {}", list116.size());
|
|
|
-
|
|
|
+
|
|
|
tFurnanceTemperature.setFurnanceName("H117");
|
|
|
List<TFurnanceTemperature> list117 = tFurnanceTemperatureService.selectTFurnanceTemperatureList(tFurnanceTemperature);
|
|
|
logger.debug("查询H117数据条数: {}", list117.size());
|
|
|
-
|
|
|
+
|
|
|
tFurnanceTemperature.setFurnanceName("H118");
|
|
|
List<TFurnanceTemperature> list118 = tFurnanceTemperatureService.selectTFurnanceTemperatureList(tFurnanceTemperature);
|
|
|
logger.debug("查询H118数据条数: {}", list118.size());
|
|
|
-
|
|
|
+
|
|
|
tFurnanceTemperature.setFurnanceName("H130");
|
|
|
List<TFurnanceTemperature> list130 = tFurnanceTemperatureService.selectTFurnanceTemperatureList(tFurnanceTemperature);
|
|
|
logger.debug("查询H130数据条数: {}", list130.size());
|
|
|
@@ -1301,13 +1304,207 @@ public class TFurnanceTemperatureController extends BaseController
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 预测每个PASS达到阈值(默认1080)的日期(最近60天数据),仅返回一条结果,含各PASS预测日期
|
|
|
+ */
|
|
|
+ @PreAuthorize("@ss.hasPermi('production:temperature:list')")
|
|
|
+ @GetMapping("/coilPredict1080")
|
|
|
+ public AjaxResult coilPredict1080(TFurnanceTemperature tFurnanceTemperature) {
|
|
|
+ try {
|
|
|
+ // 组装最近60天时间范围
|
|
|
+ Date today = new Date();
|
|
|
+ Calendar endCal = Calendar.getInstance();
|
|
|
+ endCal.setTime(today);
|
|
|
+ endCal.add(Calendar.DAY_OF_MONTH, 1);
|
|
|
+ endCal.set(Calendar.HOUR_OF_DAY, 0);
|
|
|
+ endCal.set(Calendar.MINUTE, 0);
|
|
|
+ endCal.set(Calendar.SECOND, 0);
|
|
|
+ endCal.set(Calendar.MILLISECOND, 0);
|
|
|
+
|
|
|
+ Calendar startCal = Calendar.getInstance();
|
|
|
+ startCal.setTime(today);
|
|
|
+ startCal.add(Calendar.DAY_OF_MONTH, -60);
|
|
|
+ startCal.set(Calendar.HOUR_OF_DAY, 0);
|
|
|
+ startCal.set(Calendar.MINUTE, 0);
|
|
|
+ startCal.set(Calendar.SECOND, 0);
|
|
|
+ startCal.set(Calendar.MILLISECOND, 0);
|
|
|
+
|
|
|
+ // 需要处理的炉号
|
|
|
+ String[] furnaces = {"H109","H110","H111","H112","H113","H114","H115","H116","H117","H118","H130"};
|
|
|
+ // 返回结构参考 /coil:一条记录包含所有炉的数组字符串(这里用日期数组字符串代替)
|
|
|
+ FurnanceTemperatureVO vo = new FurnanceTemperatureVO();
|
|
|
+ vo.setRecordTime(new Date());
|
|
|
+ SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
+
|
|
|
+ for (String furnace : furnaces) {
|
|
|
+ // 先查询该炉在窗口内的所有原始数据,仅查询一次,避免 N+1
|
|
|
+ TFurnanceTemperature q = new TFurnanceTemperature();
|
|
|
+ q.setFurnanceName(furnace);
|
|
|
+ q.setStartDate(startCal.getTime());
|
|
|
+ q.setEndDate(endCal.getTime());
|
|
|
+ List<TFurnanceTemperature> raw = tFurnanceTemperatureService.selectTFurnanceTemperatureList(q);
|
|
|
+ // 按时间升序
|
|
|
+ raw.sort(Comparator.comparing(TFurnanceTemperature::getRecordTime));
|
|
|
+ // 找到最近一次 status 为 1 或 2 的位置
|
|
|
+ int pivot = -1;
|
|
|
+ for (int i = 0; i < raw.size(); i++) {
|
|
|
+ String st = raw.get(i).getStatus();
|
|
|
+ if ("1".equals(st) || "2".equals(st)) {
|
|
|
+ pivot = i;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 仅保留“最近一次状态为1或2”之后的所有数据;忽略值为0在后续组装阶段处理
|
|
|
+ List<TFurnanceTemperature> filtered = new ArrayList<>();
|
|
|
+ int startIdx = (pivot >= 0) ? (pivot + 1) : 0;
|
|
|
+ for (int i = startIdx; i < raw.size(); i++) {
|
|
|
+ filtered.add(raw.get(i));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 针对每个PASS构建数据并做线性回归预测
|
|
|
+ int passCount = "H130".equals(furnace) ? 12 : 8;
|
|
|
+ Map<String, String> passToDate = new LinkedHashMap<>();
|
|
|
+
|
|
|
+ for (int passNo = 1; passNo <= passCount; passNo++) {
|
|
|
+ // 如果过滤后仍少于2条,则不进行计算,直接返回"-"
|
|
|
+ if (filtered.size() < 2) {
|
|
|
+ logger.info("coilPredict1080 skip | furnace={} pass={} reason=insufficient_points after pivot", furnace, passNo);
|
|
|
+ passToDate.put("PASS" + passNo, "-");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 组装日期->值,并打印调试
|
|
|
+ Map<String, Double> series = new LinkedHashMap<>();
|
|
|
+ List<String> debugPairs = new ArrayList<>();
|
|
|
+ String passKey = "pass" + passNo;
|
|
|
+ for (TFurnanceTemperature item : filtered) {
|
|
|
+ // 使用与 /coil 一致的规则:每条记录中该 PASS 对应的最大值(getMaxValue 用于拼接并取最大)
|
|
|
+ int value;
|
|
|
+ if ("H109".equals(furnace)) {
|
|
|
+ value = setPassMaxValueH109(item, passKey);
|
|
|
+ } else if ("H130".equals(furnace)) {
|
|
|
+ value = setPassMaxValueH130(item, passKey);
|
|
|
+ } else {
|
|
|
+ value = setPassMaxValueH11x(item, passKey);
|
|
|
+ }
|
|
|
+ String d = fmt.format(item.getRecordTime());
|
|
|
+ // 只纳入有效数值
|
|
|
+ if (value > 0) {
|
|
|
+ series.put(d, (double) value);
|
|
|
+ debugPairs.add(d + "=" + value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 只在调试模式下打印详细数据
|
|
|
+ if (logger.isDebugEnabled()) {
|
|
|
+ logger.debug("coilPredict1080 input | furnace={} pass={} points={} data={}", furnace, passNo, series.size(), String.join(" | ", debugPairs));
|
|
|
+ // 打印回归前的数据点(日期, 值)单行输出,例如 [2025-01-01, 121], [2025-01-02, 145], ...
|
|
|
+ if (!series.isEmpty()) {
|
|
|
+ List<String> datePairs = new ArrayList<>();
|
|
|
+ for (Map.Entry<String, Double> e : series.entrySet()) {
|
|
|
+ datePairs.add("[" + e.getKey() + ", " + e.getValue().intValue() + "]");
|
|
|
+ }
|
|
|
+ logger.debug("coilPredict1080 series (date,val) | furnace={} pass={} => {}", furnace, passNo, String.join(", ", datePairs));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 使用MathUtil线性回归预测达到1080的日期,并打印结果与斜率
|
|
|
+ String date = "-";
|
|
|
+ if (series.size() >= 2) {
|
|
|
+ Map<String, String> res = MathUtil.predictDateToReachLimit(series, 1080.0);
|
|
|
+ if (res != null) {
|
|
|
+ String equation = res.get("equation"); // 形如 y = 20.4833x + 80.1400
|
|
|
+ String slopeStr = null;
|
|
|
+ if (equation != null) {
|
|
|
+ try {
|
|
|
+ String right = equation.split("=")[1].trim();
|
|
|
+ slopeStr = right.split("x")[0].trim();
|
|
|
+ } catch (Exception ignore) {}
|
|
|
+ }
|
|
|
+ if (res.get("date") != null) {
|
|
|
+ date = res.get("date");
|
|
|
+ // 如果预测结果早于或等于最后一天数据日期,则不显示(置为“-”)
|
|
|
+ try {
|
|
|
+ DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
|
+ String lastKey = null;
|
|
|
+ for (String k : series.keySet()) { lastKey = k; }
|
|
|
+ if (lastKey != null) {
|
|
|
+ LocalDate predicted = LocalDate.parse(date, df);
|
|
|
+ LocalDate last = LocalDate.parse(lastKey, df);
|
|
|
+ if (!predicted.isAfter(last)) {
|
|
|
+ date = "-";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception ignore) {}
|
|
|
+ }
|
|
|
+ // 只在调试模式下打印详细结果
|
|
|
+ if (logger.isDebugEnabled()) {
|
|
|
+ logger.debug("coilPredict1080 result | furnace={} pass={} predictedDate={} slope={} equation={}",
|
|
|
+ furnace, passNo, date, slopeStr, equation);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ passToDate.put("PASS" + passNo, date);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 写入VO对应字段:与 /coil 保持相同字段名与字符串格式(逗号分隔)
|
|
|
+ String joined = String.join(",",
|
|
|
+ passToDate.getOrDefault("PASS1","-"),
|
|
|
+ passToDate.getOrDefault("PASS2","-"),
|
|
|
+ passToDate.getOrDefault("PASS3","-"),
|
|
|
+ passToDate.getOrDefault("PASS4","-"),
|
|
|
+ passToDate.getOrDefault("PASS5","-"),
|
|
|
+ passToDate.getOrDefault("PASS6","-"),
|
|
|
+ passToDate.getOrDefault("PASS7","-"),
|
|
|
+ passToDate.getOrDefault("PASS8","-")
|
|
|
+ );
|
|
|
+ if ("H109".equals(furnace)) vo.setH109Out(joined);
|
|
|
+ if ("H110".equals(furnace)) vo.setH110Out(joined);
|
|
|
+ if ("H111".equals(furnace)) vo.setH111Out(joined);
|
|
|
+ if ("H112".equals(furnace)) vo.setH112Out(joined);
|
|
|
+ if ("H113".equals(furnace)) vo.setH113Out(joined);
|
|
|
+ if ("H114".equals(furnace)) vo.setH114Out(joined);
|
|
|
+ if ("H115".equals(furnace)) vo.setH115Out(joined);
|
|
|
+ if ("H116".equals(furnace)) vo.setH116Out(joined);
|
|
|
+ if ("H117".equals(furnace)) vo.setH117Out(joined);
|
|
|
+ if ("H118".equals(furnace)) vo.setH118Out(joined);
|
|
|
+ if ("H130".equals(furnace)) {
|
|
|
+ String joined130 = String.join(",",
|
|
|
+ passToDate.getOrDefault("PASS1","-"),
|
|
|
+ passToDate.getOrDefault("PASS2","-"),
|
|
|
+ passToDate.getOrDefault("PASS3","-"),
|
|
|
+ passToDate.getOrDefault("PASS4","-"),
|
|
|
+ passToDate.getOrDefault("PASS5","-"),
|
|
|
+ passToDate.getOrDefault("PASS6","-"),
|
|
|
+ passToDate.getOrDefault("PASS7","-"),
|
|
|
+ passToDate.getOrDefault("PASS8","-"),
|
|
|
+ passToDate.getOrDefault("PASS9","-"),
|
|
|
+ passToDate.getOrDefault("PASS10","-"),
|
|
|
+ passToDate.getOrDefault("PASS11","-"),
|
|
|
+ passToDate.getOrDefault("PASS12","-")
|
|
|
+ );
|
|
|
+ vo.setH130Out(joined130);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 只返回一条记录,与 /coil 保持一致
|
|
|
+ List<FurnanceTemperatureVO> one = new ArrayList<>();
|
|
|
+ one.add(vo);
|
|
|
+
|
|
|
+ // 汇总日志:显示接口执行情况
|
|
|
+ logger.info("coilPredict1080 completed | furnaces={} timeWindow={}days", furnaces.length, 60);
|
|
|
+ return AjaxResult.success(one);
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("预测PASS达到1080接口执行异常", e);
|
|
|
+ throw e;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 查询裂解炉炉管测温COIL趋势分析
|
|
|
*/
|
|
|
@PreAuthorize("@ss.hasPermi('production:pressure:list')")
|
|
|
@GetMapping("/coilAnalysis")
|
|
|
public AjaxResult coilAnalysis(TFurnanceTemperature tFurnanceTemperature) {
|
|
|
- logger.info("开始执行查询裂解炉炉管测温COIL趋势分析接口,入参: furnanceName={}, pass={}, startDate={}, endDate={}",
|
|
|
+ logger.info("开始执行查询裂解炉炉管测温COIL趋势分析接口,入参: furnanceName={}, pass={}, startDate={}, endDate={}",
|
|
|
tFurnanceTemperature.getFurnanceName(), tFurnanceTemperature.getPassNo(),
|
|
|
tFurnanceTemperature.getStartDate(), tFurnanceTemperature.getEndDate());
|
|
|
try {
|
|
|
@@ -1343,7 +1540,7 @@ public class TFurnanceTemperatureController extends BaseController
|
|
|
}
|
|
|
List<TFurnanceTemperature> tFurnanceTemperatures = tFurnanceTemperatureService.selectCoilAnalysis(temperature);//原始数据列表
|
|
|
logger.info("查询到原始数据条数: {}", tFurnanceTemperatures.size());
|
|
|
-
|
|
|
+
|
|
|
List<FurnanceTemperatureCoilVO> coilVoList = new ArrayList<FurnanceTemperatureCoilVO>();//返回数据列表
|
|
|
//数据组装
|
|
|
for (TFurnanceTemperature obj : tFurnanceTemperatures) {
|
|
|
@@ -1486,7 +1683,7 @@ public class TFurnanceTemperatureController extends BaseController
|
|
|
@GetMapping("/list")
|
|
|
public TableDataInfo list(TFurnanceTemperature tFurnanceTemperature)
|
|
|
{
|
|
|
- logger.info("开始执行查询裂解炉炉管测温列表接口,入参: furnanceName={}, recordTime={}",
|
|
|
+ logger.info("开始执行查询裂解炉炉管测温列表接口,入参: furnanceName={}, recordTime={}",
|
|
|
tFurnanceTemperature.getFurnanceName(), tFurnanceTemperature.getRecordTime());
|
|
|
try {
|
|
|
startPage();
|
|
|
@@ -1507,7 +1704,7 @@ public class TFurnanceTemperatureController extends BaseController
|
|
|
@GetMapping("/export")
|
|
|
public AjaxResult export(TFurnanceTemperature tFurnanceTemperature)
|
|
|
{
|
|
|
- logger.info("开始执行导出裂解炉炉管测温列表接口,入参: furnanceName={}, recordTime={}",
|
|
|
+ logger.info("开始执行导出裂解炉炉管测温列表接口,入参: furnanceName={}, recordTime={}",
|
|
|
tFurnanceTemperature.getFurnanceName(), tFurnanceTemperature.getRecordTime());
|
|
|
try {
|
|
|
List<TFurnanceTemperature> list = tFurnanceTemperatureService.selectTFurnanceTemperatureList(tFurnanceTemperature);
|
|
|
@@ -1551,7 +1748,7 @@ public class TFurnanceTemperatureController extends BaseController
|
|
|
@PostMapping
|
|
|
public AjaxResult add(@RequestBody TFurnanceTemperature tFurnanceTemperature)
|
|
|
{
|
|
|
- logger.info("开始执行新增裂解炉炉管测温接口,入参: furnanceName={}, recordTime={}",
|
|
|
+ logger.info("开始执行新增裂解炉炉管测温接口,入参: furnanceName={}, recordTime={}",
|
|
|
tFurnanceTemperature.getFurnanceName(), tFurnanceTemperature.getRecordTime());
|
|
|
try {
|
|
|
int result = tFurnanceTemperatureService.insertTFurnanceTemperature(tFurnanceTemperature);
|
|
|
@@ -1574,7 +1771,7 @@ public class TFurnanceTemperatureController extends BaseController
|
|
|
@Log(title = "裂解炉炉管测温", businessType = BusinessType.UPDATE)
|
|
|
@PutMapping
|
|
|
public AjaxResult edit(@RequestBody TFurnanceTemperature tFurnanceTemperature) throws PinyinException {
|
|
|
- logger.info("开始执行修改裂解炉炉管测温接口(APP),入参: id={}, furnanceName={}, recordTime={}",
|
|
|
+ logger.info("开始执行修改裂解炉炉管测温接口(APP),入参: id={}, furnanceName={}, recordTime={}",
|
|
|
tFurnanceTemperature.getId(), tFurnanceTemperature.getFurnanceName(), tFurnanceTemperature.getRecordTime());
|
|
|
try {
|
|
|
String pass1 = tFurnanceTemperature.getPass1();
|
|
|
@@ -1935,6 +2132,146 @@ public class TFurnanceTemperatureController extends BaseController
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 获取预测图表的历史数据
|
|
|
+ */
|
|
|
+ @PreAuthorize("@ss.hasPermi('production:temperature:list')")
|
|
|
+ @GetMapping("/coilPredictChartData")
|
|
|
+ public AjaxResult coilPredictChartData(TFurnanceTemperature tFurnanceTemperature) {
|
|
|
+ logger.info("开始获取预测图表历史数据 | furnace={} pass={}", tFurnanceTemperature.getFurnanceName(), tFurnanceTemperature.getPassNo());
|
|
|
+ try {
|
|
|
+ // 计算最近60天的日期范围
|
|
|
+ Calendar endCal = Calendar.getInstance();
|
|
|
+ Calendar startCal = Calendar.getInstance();
|
|
|
+ startCal.add(Calendar.DAY_OF_MONTH, -60);
|
|
|
+
|
|
|
+ String[] furnaces = {"H109", "H110", "H111", "H112", "H113", "H114", "H115", "H116", "H117", "H118", "H130"};
|
|
|
+ String furnace = tFurnanceTemperature.getFurnanceName();
|
|
|
+ String passNo = tFurnanceTemperature.getPassNo();
|
|
|
+
|
|
|
+ if (furnace == null || passNo == null) {
|
|
|
+ return AjaxResult.error("炉子和PASS号不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询历史数据
|
|
|
+ TFurnanceTemperature query = new TFurnanceTemperature();
|
|
|
+ query.setFurnanceName(furnace);
|
|
|
+ query.setStartDate(startCal.getTime());
|
|
|
+ query.setEndDate(endCal.getTime());
|
|
|
+ List<TFurnanceTemperature> rawData = tFurnanceTemperatureService.selectTFurnanceTemperatureList(query);
|
|
|
+
|
|
|
+ // 按时间升序排序
|
|
|
+ rawData.sort(Comparator.comparing(TFurnanceTemperature::getRecordTime));
|
|
|
+
|
|
|
+ // 找到最近一次 status 为 1 或 2 的下标
|
|
|
+ int pivot = -1;
|
|
|
+ for (int i = 0; i < rawData.size(); i++) {
|
|
|
+ String status = rawData.get(i).getStatus();
|
|
|
+ if ("1".equals(status) || "2".equals(status)) {
|
|
|
+ pivot = i;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 只取 pivot 之后的所有数据
|
|
|
+ List<TFurnanceTemperature> filteredData = new ArrayList<>();
|
|
|
+ int startIndex = (pivot == -1) ? 0 : pivot + 1;
|
|
|
+ for (int i = startIndex; i < rawData.size(); i++) {
|
|
|
+ filteredData.add(rawData.get(i));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建散点图数据
|
|
|
+ List<Map<String, Object>> chartData = new ArrayList<>();
|
|
|
+ SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
+ int dayCount = 1;
|
|
|
+
|
|
|
+ for (TFurnanceTemperature item : filteredData) {
|
|
|
+ int value = 0;
|
|
|
+ String passKey = "pass" + passNo;
|
|
|
+
|
|
|
+ if ("H109".equals(furnace)) {
|
|
|
+ value = setPassMaxValueH109(item, passKey);
|
|
|
+ } else if ("H130".equals(furnace)) {
|
|
|
+ value = setPassMaxValueH130(item, passKey);
|
|
|
+ } else {
|
|
|
+ value = setPassMaxValueH11x(item, passKey);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (value > 0) { // 只包含有效数据
|
|
|
+ Map<String, Object> dataPoint = new HashMap<>();
|
|
|
+ dataPoint.put("day", dayCount);
|
|
|
+ dataPoint.put("value", value);
|
|
|
+ dataPoint.put("date", fmt.format(item.getRecordTime()));
|
|
|
+ chartData.add(dataPoint);
|
|
|
+ dayCount++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算线性回归斜率和方程
|
|
|
+ Map<String, String> regressionResult = null;
|
|
|
+ String slope = "0";
|
|
|
+ String equation = "y = 0x + 0";
|
|
|
+ String predictedDate = "-";
|
|
|
+
|
|
|
+ if (chartData.size() >= 2) {
|
|
|
+ // 构建日期->值的映射用于线性回归
|
|
|
+ Map<String, Double> dateValueMap = new LinkedHashMap<>();
|
|
|
+ for (Map<String, Object> point : chartData) {
|
|
|
+ // 安全地转换Integer到Double
|
|
|
+ Object valueObj = point.get("value");
|
|
|
+ Double value;
|
|
|
+ if (valueObj instanceof Integer) {
|
|
|
+ value = ((Integer) valueObj).doubleValue();
|
|
|
+ } else if (valueObj instanceof Double) {
|
|
|
+ value = (Double) valueObj;
|
|
|
+ } else {
|
|
|
+ value = Double.valueOf(valueObj.toString());
|
|
|
+ }
|
|
|
+ dateValueMap.put((String) point.get("date"), value);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 使用MathUtil进行线性回归
|
|
|
+ regressionResult = MathUtil.predictDateToReachLimit(dateValueMap, 1080.0);
|
|
|
+ if (regressionResult != null) {
|
|
|
+ equation = regressionResult.get("equation");
|
|
|
+ predictedDate = regressionResult.get("date");
|
|
|
+
|
|
|
+ // 提取斜率
|
|
|
+ try {
|
|
|
+ slope = equation.split("x")[0].split("=")[1].trim();
|
|
|
+ } catch (Exception e) {
|
|
|
+ slope = "0";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加预测点位
|
|
|
+ Map<String, Object> predictPoint = new HashMap<>();
|
|
|
+ if (!"-".equals(predictedDate)) {
|
|
|
+ predictPoint.put("date", predictedDate);
|
|
|
+ predictPoint.put("value", 1080);
|
|
|
+ predictPoint.put("day", chartData.size() + 1); // 预测点位的天数
|
|
|
+ chartData.add(predictPoint);
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ result.put("furnace", furnace);
|
|
|
+ result.put("passNo", passNo);
|
|
|
+ result.put("chartData", chartData);
|
|
|
+ result.put("dataCount", chartData.size());
|
|
|
+ result.put("slope", slope);
|
|
|
+ result.put("equation", equation);
|
|
|
+ result.put("predictedDate", predictedDate);
|
|
|
+ result.put("predictPoint", predictPoint);
|
|
|
+
|
|
|
+ logger.info("coilPredictChartData completed | furnace={} pass={} dataPoints={}", furnace, passNo, chartData.size());
|
|
|
+ return AjaxResult.success(result);
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("获取预测图表历史数据异常", e);
|
|
|
+ return AjaxResult.error("获取数据失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
public boolean isPassNegativeOrNull(String pass) {
|
|
|
boolean flag = false;
|
|
|
if (StringUtils.isNotNull(pass) && StringUtils.isNotEmpty(pass)) {
|