|
|
@@ -0,0 +1,259 @@
|
|
|
+package com.zksy.base.service.impl;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.zksy.base.domain.EquipmentBase;
|
|
|
+import com.zksy.base.domain.EquipmentPointRel;
|
|
|
+import com.zksy.base.domain.MonitorPoint;
|
|
|
+import com.zksy.base.mapper.EquipmentBaseMapper;
|
|
|
+import com.zksy.base.mapper.EquipmentPointRelMapper;
|
|
|
+import com.zksy.base.mapper.MonitorPointMapper;
|
|
|
+import com.zksy.base.pressure.domain.FirefightingPressure;
|
|
|
+import com.zksy.base.pressure.mapper.FirefightingPressureMapper;
|
|
|
+import com.zksy.base.service.PressureMonitorService;
|
|
|
+import com.zksy.common.utils.StringUtils;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.math.RoundingMode;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
+import java.util.*;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+public class PressureMonitorServiceImpl implements PressureMonitorService {
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private MonitorPointMapper monitorPointMapper;
|
|
|
+ @Autowired
|
|
|
+ private EquipmentPointRelMapper equipmentPointRelMapper;
|
|
|
+ @Autowired
|
|
|
+ private EquipmentBaseMapper equipmentBaseMapper;
|
|
|
+ @Autowired
|
|
|
+ private FirefightingPressureMapper firefightingPressureMapper;
|
|
|
+
|
|
|
+ private static final DateTimeFormatter DT_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
|
|
+
|
|
|
+ /** 压力等级 → 阈值映射 */
|
|
|
+ private static final Map<String, double[]> PRESSURE_THRESHOLDS = new LinkedHashMap<>();
|
|
|
+ static {
|
|
|
+ PRESSURE_THRESHOLDS.put("high", new double[]{240, 420});
|
|
|
+ PRESSURE_THRESHOLDS.put("medium", new double[]{150, 250});
|
|
|
+ PRESSURE_THRESHOLDS.put("low", new double[]{80, 150});
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<Map<String, Object>> getPoints() {
|
|
|
+ List<MonitorPoint> points = monitorPointMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<MonitorPoint>()
|
|
|
+ .eq(MonitorPoint::getPointType, "燃气监测")
|
|
|
+ .eq(MonitorPoint::getDelFlag, "0")
|
|
|
+ );
|
|
|
+
|
|
|
+ List<Map<String, Object>> result = new ArrayList<>();
|
|
|
+ for (MonitorPoint p : points) {
|
|
|
+ // 查关联的压力设备
|
|
|
+ FirefightingPressure latestPressure = getLatestPressureByPoint(p.getPointId());
|
|
|
+ if (latestPressure == null) continue; // 只返回有压力数据的监测点
|
|
|
+
|
|
|
+ // 从设备信息推断压力等级
|
|
|
+ String pressureLevel = inferPressureLevel(latestPressure);
|
|
|
+
|
|
|
+ double[] thresholds = PRESSURE_THRESHOLDS.getOrDefault(pressureLevel, new double[]{150, 250});
|
|
|
+ double minP = thresholds[0];
|
|
|
+ double maxP = thresholds[1];
|
|
|
+ double latestVal = latestPressure.getPressureValue() != null ? latestPressure.getPressureValue() : 0;
|
|
|
+ String status = computeStatus(latestVal, minP, maxP);
|
|
|
+
|
|
|
+ Map<String, Object> item = new LinkedHashMap<>();
|
|
|
+ item.put("pointId", p.getPointId());
|
|
|
+ item.put("name", p.getPointName());
|
|
|
+ item.put("pipelineName", derivePipelineName(p.getPointName(), pressureLevel));
|
|
|
+ item.put("pressureLevel", pressureLevel);
|
|
|
+ item.put("address", p.getLocation());
|
|
|
+ item.put("lng", p.getLongitude());
|
|
|
+ item.put("lat", p.getLatitude());
|
|
|
+ item.put("latestPressure", Math.round(latestVal * 10.0) / 10.0);
|
|
|
+ item.put("minPressure", minP);
|
|
|
+ item.put("maxPressure", maxP);
|
|
|
+ item.put("status", status);
|
|
|
+ item.put("latestTime", latestPressure.getObservedTime() != null
|
|
|
+ ? latestPressure.getObservedTime().format(DT_FMT) : "");
|
|
|
+ result.add(item);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Map<String, Object> getTrend(String pointId, int days) {
|
|
|
+ Map<String, Object> result = new LinkedHashMap<>();
|
|
|
+ String telemCode = getTelemCodeByPoint(pointId);
|
|
|
+
|
|
|
+ // 先查所有数据确定时间范围
|
|
|
+ List<FirefightingPressure> allData = firefightingPressureMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<FirefightingPressure>()
|
|
|
+ .eq(StringUtils.isNotEmpty(telemCode), FirefightingPressure::getTelemeteringStation, telemCode)
|
|
|
+ .orderByAsc(FirefightingPressure::getObservedTime)
|
|
|
+ );
|
|
|
+
|
|
|
+ // 从数据中提取时间范围
|
|
|
+ LocalDateTime dataStart = allData.isEmpty() ? LocalDateTime.now().withHour(0).withMinute(0).withSecond(0).withNano(0)
|
|
|
+ : allData.get(0).getObservedTime();
|
|
|
+ LocalDateTime dataEnd = allData.isEmpty() ? dataStart.plusDays(9).withHour(23)
|
|
|
+ : allData.get(allData.size() - 1).getObservedTime();
|
|
|
+ if (dataEnd == null) dataEnd = dataStart.plusDays(9).withHour(23);
|
|
|
+
|
|
|
+ int actualDays = Math.max(1, (int) java.time.Duration.between(dataStart, dataEnd).toDays() + 1);
|
|
|
+ actualDays = Math.min(actualDays, days);
|
|
|
+
|
|
|
+ // 聚合为每天6时段 (0/4/8/12/16/20)
|
|
|
+ int[] hourSlots = {0, 4, 8, 12, 16, 20};
|
|
|
+ Map<String, List<Double>> grouped = new LinkedHashMap<>();
|
|
|
+ for (FirefightingPressure fp : allData) {
|
|
|
+ if (fp.getObservedTime() == null || fp.getPressureValue() == null) continue;
|
|
|
+ int slotHour = findNearestSlot(fp.getObservedTime().getHour(), hourSlots);
|
|
|
+ String key = fp.getObservedTime().toLocalDate().toString() + " " + String.format("%02d:00", slotHour);
|
|
|
+ grouped.computeIfAbsent(key, k -> new ArrayList<>()).add(fp.getPressureValue());
|
|
|
+ }
|
|
|
+
|
|
|
+ LocalDateTime rangeStart = dataStart.withHour(0).withMinute(0).withSecond(0).withNano(0);
|
|
|
+ LocalDateTime rangeEnd = dataEnd.withHour(23).withMinute(0).withSecond(0).withNano(0);
|
|
|
+
|
|
|
+ List<String> times = new ArrayList<>();
|
|
|
+ List<Double> pressures = new ArrayList<>();
|
|
|
+ double lastVal = 0;
|
|
|
+
|
|
|
+ for (int d = actualDays - 1; d >= 0; d--) {
|
|
|
+ LocalDateTime day = rangeEnd.toLocalDate().atStartOfDay().minusDays(d);
|
|
|
+ for (int h : hourSlots) {
|
|
|
+ String key = day.toLocalDate().toString() + " " + String.format("%02d:00", h);
|
|
|
+ List<Double> vals = grouped.getOrDefault(key, Collections.emptyList());
|
|
|
+ double avg = vals.isEmpty() ? lastVal : vals.stream().mapToDouble(v -> v).average().orElse(lastVal);
|
|
|
+ if (!vals.isEmpty()) lastVal = avg;
|
|
|
+ times.add(day.toLocalDate().toString().substring(5) + "\n" + String.format("%02d:00", h));
|
|
|
+ pressures.add(Math.round(avg * 10.0) / 10.0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 从数据推断阈值
|
|
|
+ double minP = 240, maxP = 420;
|
|
|
+ FirefightingPressure latest = allData.isEmpty() ? null : allData.get(allData.size() - 1);
|
|
|
+ if (latest != null) {
|
|
|
+ double[] thresholds = inferThresholds(latest);
|
|
|
+ minP = thresholds[0];
|
|
|
+ maxP = thresholds[1];
|
|
|
+ }
|
|
|
+
|
|
|
+ result.put("times", times);
|
|
|
+ result.put("pressures", pressures);
|
|
|
+ result.put("minPressure", minP);
|
|
|
+ result.put("maxPressure", maxP);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Map<String, Object> getHistory(String pointId, int pageNum, int pageSize) {
|
|
|
+ String telemCode = getTelemCodeByPoint(pointId);
|
|
|
+
|
|
|
+ // 总数
|
|
|
+ long total = firefightingPressureMapper.selectCount(
|
|
|
+ new LambdaQueryWrapper<FirefightingPressure>()
|
|
|
+ .eq(StringUtils.isNotEmpty(telemCode), FirefightingPressure::getTelemeteringStation, telemCode)
|
|
|
+ );
|
|
|
+
|
|
|
+ int offset = (pageNum - 1) * pageSize;
|
|
|
+ List<FirefightingPressure> list = firefightingPressureMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<FirefightingPressure>()
|
|
|
+ .eq(StringUtils.isNotEmpty(telemCode), FirefightingPressure::getTelemeteringStation, telemCode)
|
|
|
+ .orderByDesc(FirefightingPressure::getObservedTime)
|
|
|
+ .last("limit " + offset + ", " + pageSize)
|
|
|
+ );
|
|
|
+
|
|
|
+ List<Map<String, Object>> rows = new ArrayList<>();
|
|
|
+ for (FirefightingPressure fp : list) {
|
|
|
+ Map<String, Object> row = new LinkedHashMap<>();
|
|
|
+ row.put("time", fp.getObservedTime() != null ? fp.getObservedTime().format(DT_FMT) : "");
|
|
|
+ row.put("pressure", fp.getPressureValue());
|
|
|
+ row.put("collector", "YLCG-" + (telemCode != null ? telemCode.substring(telemCode.length() - 3) : "001"));
|
|
|
+ row.put("remark", "");
|
|
|
+ rows.add(row);
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, Object> result = new LinkedHashMap<>();
|
|
|
+ result.put("total", total);
|
|
|
+ result.put("rows", rows);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ========== 辅助方法 ==========
|
|
|
+
|
|
|
+ private String getTelemCodeByPoint(String pointId) {
|
|
|
+ List<EquipmentPointRel> rels = equipmentPointRelMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<EquipmentPointRel>()
|
|
|
+ .eq(EquipmentPointRel::getPointId, pointId)
|
|
|
+ .eq(EquipmentPointRel::getDelFlag, "0")
|
|
|
+ );
|
|
|
+ if (rels.isEmpty()) return null;
|
|
|
+ List<EquipmentBase> eqs = equipmentBaseMapper.selectBatchIds(
|
|
|
+ rels.stream().map(EquipmentPointRel::getEquipmentId).collect(Collectors.toList()));
|
|
|
+ for (EquipmentBase eq : eqs) {
|
|
|
+ // 压力设备的 equipment_code 即 telemetering_station
|
|
|
+ FirefightingPressure check = getLatestByTelemCode(eq.getEquipmentCode());
|
|
|
+ if (check != null) return eq.getEquipmentCode();
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ private FirefightingPressure getLatestPressureByPoint(String pointId) {
|
|
|
+ String telemCode = getTelemCodeByPoint(pointId);
|
|
|
+ if (telemCode == null) return null;
|
|
|
+ return getLatestByTelemCode(telemCode);
|
|
|
+ }
|
|
|
+
|
|
|
+ private FirefightingPressure getLatestByTelemCode(String telemCode) {
|
|
|
+ List<FirefightingPressure> list = firefightingPressureMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<FirefightingPressure>()
|
|
|
+ .eq(FirefightingPressure::getTelemeteringStation, telemCode)
|
|
|
+ .orderByDesc(FirefightingPressure::getObservedTime)
|
|
|
+ .last("limit 1")
|
|
|
+ );
|
|
|
+ return list.isEmpty() ? null : list.get(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String inferPressureLevel(FirefightingPressure fp) {
|
|
|
+ if (fp == null || fp.getPressureValue() == null) return "medium";
|
|
|
+ double v = fp.getPressureValue();
|
|
|
+ if (v >= 240) return "high";
|
|
|
+ if (v >= 150) return "medium";
|
|
|
+ return "low";
|
|
|
+ }
|
|
|
+
|
|
|
+ private double[] inferThresholds(FirefightingPressure fp) {
|
|
|
+ return PRESSURE_THRESHOLDS.getOrDefault(inferPressureLevel(fp), new double[]{150, 250});
|
|
|
+ }
|
|
|
+
|
|
|
+ private String computeStatus(double val, double min, double max) {
|
|
|
+ if (val <= 0) return "offline";
|
|
|
+ if (val > max) return "danger";
|
|
|
+ if (val < min) return "warning";
|
|
|
+ return "normal";
|
|
|
+ }
|
|
|
+
|
|
|
+ private String derivePipelineName(String pointName, String level) {
|
|
|
+ String levelStr = "高压".equals(level) ? "高压段" : "中压".equals(level) ? "中压段" : "低压段";
|
|
|
+ return pointName.replace("监测点", "").replace("LNG储配站", "LNG段").replace("计量站", "计量段") + levelStr;
|
|
|
+ }
|
|
|
+
|
|
|
+ private int findNearestSlot(int hour, int[] slots) {
|
|
|
+ int nearest = slots[0];
|
|
|
+ int minDiff = Math.abs(hour - slots[0]);
|
|
|
+ for (int s : slots) {
|
|
|
+ int diff = Math.abs(hour - s);
|
|
|
+ if (diff < minDiff) { minDiff = diff; nearest = s; }
|
|
|
+ }
|
|
|
+ return nearest;
|
|
|
+ }
|
|
|
+}
|