瀏覽代碼

适配器取数及报告代码

王波 2 周之前
父節點
當前提交
ffbc591a4a
共有 24 個文件被更改,包括 1918 次插入97 次删除
  1. 110 83
      runeconomy-xk/src/main/java/com/gyee/runeconomy/config/GyeeConfig.java
  2. 31 2
      runeconomy-xk/src/main/java/com/gyee/runeconomy/controller/EarlyWarning/EarlyWarningController.java
  3. 27 0
      runeconomy-xk/src/main/java/com/gyee/runeconomy/controller/WindDirection/WindDirectionController.java
  4. 2 0
      runeconomy-xk/src/main/java/com/gyee/runeconomy/model/ReliabilityIssues.java
  5. 51 0
      runeconomy-xk/src/main/java/com/gyee/runeconomy/model/vo/IndicatorData.java
  6. 26 10
      runeconomy-xk/src/main/java/com/gyee/runeconomy/service/EarlyWarning/EarlyWarningReliableService.java
  7. 327 0
      runeconomy-xk/src/main/java/com/gyee/runeconomy/service/EarlyWarning/EarlyWarninggetService.java
  8. 333 0
      runeconomy-xk/src/main/java/com/gyee/runeconomy/service/EarlyWarning/ReportService.java
  9. 16 0
      runeconomy-xk/src/main/java/com/gyee/runeconomy/service/WindDirection/Point.java
  10. 504 0
      runeconomy-xk/src/main/java/com/gyee/runeconomy/service/WindDirection/WindALG.java
  11. 161 0
      runeconomy-xk/src/main/java/com/gyee/runeconomy/service/WindDirection/WindDirectionService.java
  12. 4 0
      runeconomy-xk/src/main/java/com/gyee/runeconomy/service/auto/IProBasicEquipmentPointService.java
  13. 1 0
      runeconomy-xk/src/main/java/com/gyee/runeconomy/service/auto/IProBasicPowerstationService.java
  14. 3 1
      runeconomy-xk/src/main/java/com/gyee/runeconomy/service/auto/ProEconAlarmRuleService.java
  15. 19 0
      runeconomy-xk/src/main/java/com/gyee/runeconomy/service/auto/impl/ProBasicEquipmentPointServiceImpl.java
  16. 8 0
      runeconomy-xk/src/main/java/com/gyee/runeconomy/service/auto/impl/ProBasicPowerstationServiceImpl.java
  17. 11 1
      runeconomy-xk/src/main/java/com/gyee/runeconomy/service/auto/impl/ProEconAlarmRuleServiceImpl.java
  18. 19 0
      runeconomy-xk/src/main/java/com/gyee/runeconomy/util/DateUtils.java
  19. 35 0
      runeconomy-xk/src/main/java/com/gyee/runeconomy/util/realtimesource/feign/FeignsBuilder.java
  20. 58 0
      runeconomy-xk/src/main/java/com/gyee/runeconomy/util/realtimesource/feign/IAdapterService.java
  21. 33 0
      runeconomy-xk/src/main/java/com/gyee/runeconomy/util/realtimesource/feign/IDataAdapter.java
  22. 52 0
      runeconomy-xk/src/main/java/com/gyee/runeconomy/util/realtimesource/feign/RemoteServiceBuilder.java
  23. 52 0
      runeconomy-xk/src/main/java/com/gyee/runeconomy/util/realtimesource/feign/TsDoubleData.java
  24. 35 0
      runeconomy-xk/src/main/java/com/gyee/runeconomy/util/realtimesource/feign/TsDoubleTsData.java

+ 110 - 83
runeconomy-xk/src/main/java/com/gyee/runeconomy/config/GyeeConfig.java

@@ -1,85 +1,112 @@
-//package com.gyee.runeconomy.config;
-//
-//
-//import lombok.Data;
-//import org.springframework.boot.context.properties.ConfigurationProperties;
-//import org.springframework.boot.system.ApplicationHome;
-//import org.springframework.stereotype.Component;
-//
-//import java.io.File;
-//import java.util.Arrays;
-//import java.util.List;
-//
-//@Data
-//@Component
-//@ConfigurationProperties(prefix = "gyee")
-//public class GyeeConfig {
-//
-//    public File jarF = null;
-//
-//    {
-//        ApplicationHome h = new ApplicationHome(getClass());
-//        jarF = h.getSource();
-//    }
-//
-//    /**
-//     * 数据适配器网址
-//     **/
-//    private String adapterUrl;
-//    /**
-//     * 数据准备保存路径(原始数据)
-//     **/
-//    private String filePathPrepare;
-//    /**
-//     * 数据处理保存路径(处理后的数据)
-//     **/
-//    private String filePathProcess;
-//    /**
-//     * 数据拟合保存路径(拟合后的数据)
-//     **/
-//    private String filePathFitting;
-//    /**
-//     * 数据压缩下载
-//     **/
-//    private String filePathDownload;
-//    /**
-//     * 功率曲线拟合测点
-//     **/
-//    private String points;
-//
+package com.gyee.runeconomy.config;
+
+
+import com.gyee.runeconomy.dto.FiveLoss.AnnotationTool;
+import com.gyee.runeconomy.dto.FiveLoss.FixedVo;
+import com.gyee.runeconomy.dto.result.PowerPointData;
+import lombok.Data;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.system.ApplicationHome;
+import org.springframework.stereotype.Component;
+
+import java.io.File;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Data
+@Component
+@ConfigurationProperties(prefix = "gyee")
+public class GyeeConfig {
+
+    public File jarF = null;
+
+    {
+        ApplicationHome h = new ApplicationHome(getClass());
+        jarF = h.getSource();
+    }
+
+    /**
+     * 数据适配器网址
+     **/
+    @Value("${db.url1}")
+    private String AdapterfdUrl;
+    @Value("${db.url1}")
+    private String AdaptergfUrl;
+    @Value("${db.url1}")
+    private String TaosUrl;
+    /**
+     * 数据准备保存路径(原始数据)
+     **/
+    private String filePathPrepare;
+    /**
+     * 数据处理保存路径(处理后的数据)
+     **/
+    private String filePathProcess;
+    /**
+     * 数据拟合保存路径(拟合后的数据)
+     **/
+    private String filePathFitting;
+    /**
+     * 数据压缩下载
+     **/
+    private String filePathDownload;
+    /**
+     * 功率曲线拟合测点
+     **/
+    private String points;
+
 //    public List<String> getPoints() {
-//        return Arrays.asList(this.points.split(","));
-//    }
-//
-//    public String getFilePathPrepare() {
-//        return jarF.getParentFile().getAbsolutePath() + "\\" + filePathPrepare;
-//    }
-//
-//    public void setFilePathPrepare(String filePathPrepare) {
-//        this.filePathPrepare = filePathPrepare;
-//    }
-//
-//    public String getFilePathProcess() {
-//        return jarF.getParentFile().getAbsolutePath() + "\\" + filePathProcess;
-//    }
-//
-//    public void setFilePathProcess(String filePathProcess) {
-//        this.filePathProcess = filePathProcess;
-//    }
-//
-//    public String getFilePathFitting() {
-//        return jarF.getParentFile().getAbsolutePath() + "\\" + filePathFitting;
-//    }
-//
-//    public void setFilePathFitting(String filePathFitting) {
-//        this.filePathFitting = filePathFitting;
-//    }
-//
-//    public String getFilePathDownload() {
-//        return jarF.getParentFile().getAbsolutePath() + "\\" + filePathDownload;
-//    }
-//
-//    public void setFilePathDownload(String filePathDownload) {
-//        this.filePathDownload = filePathDownload;
+//        if (null == points) {
+//            return Collections.emptyList();
+//        } else {
+//            return Arrays.asList(points.split(","));
+//        }
 //    }
-//}
+
+    public List<String> getPoints() {
+        return getUniformCodes();
+    }
+
+    private List<String> getUniformCodes(){
+        List<FixedVo> fxList = AnnotationTool.getFixedVoList(PowerPointData.class);
+        List<String> codes = fxList.stream().filter(f -> !StringUtils.isEmpty(f.getUniformCode()))
+                .map(d -> d.getUniformCode()).collect(Collectors.toList());
+
+        return codes;
+    }
+
+    public String getFilePathPrepare() {
+        return jarF.getParentFile().getAbsolutePath() + "\\" + filePathPrepare;
+    }
+
+    public void setFilePathPrepare(String filePathPrepare) {
+        this.filePathPrepare = filePathPrepare;
+    }
+
+    public String getFilePathProcess() {
+        return jarF.getParentFile().getAbsolutePath() + "\\" + filePathProcess;
+    }
+
+    public void setFilePathProcess(String filePathProcess) {
+        this.filePathProcess = filePathProcess;
+    }
+
+    public String getFilePathFitting() {
+        return jarF.getParentFile().getAbsolutePath() + "\\" + filePathFitting;
+    }
+
+    public void setFilePathFitting(String filePathFitting) {
+        this.filePathFitting = filePathFitting;
+    }
+
+    public String getFilePathDownload() {
+        return jarF.getParentFile().getAbsolutePath() + "\\" + filePathDownload;
+    }
+
+    public void setFilePathDownload(String filePathDownload) {
+        this.filePathDownload = filePathDownload;
+    }
+}

+ 31 - 2
runeconomy-xk/src/main/java/com/gyee/runeconomy/controller/EarlyWarning/EarlyWarningController.java

@@ -5,6 +5,8 @@ import com.alibaba.fastjson.JSONObject;
 import com.gyee.runeconomy.dto.result.JsonResult;
 import com.gyee.runeconomy.dto.result.ResultCode;
 import com.gyee.runeconomy.service.EarlyWarning.EarlyWarningReliableService;
+import com.gyee.runeconomy.service.EarlyWarning.EarlyWarninggetService;
+import com.gyee.runeconomy.service.EarlyWarning.ReportService;
 import com.gyee.runeconomy.service.Weather.WertherForecast;
 import io.swagger.annotations.ApiImplicitParam;
 import io.swagger.annotations.ApiImplicitParams;
@@ -27,6 +29,12 @@ public class EarlyWarningController {
     private EarlyWarningReliableService earlyWarningReliableService;
 
     @Autowired
+    private EarlyWarninggetService earlyWarninggetService;
+
+    @Autowired
+    private ReportService reportService;
+
+    @Autowired
     private WertherForecast wertherForecast;
 
     @GetMapping(value = "/reporkkxwt")
@@ -40,7 +48,7 @@ public class EarlyWarningController {
 
 
     @GetMapping(value = "/report")
-    @ApiOperation(value = "分析报告", notes = "分析报告")
+    @ApiOperation(value = "预警分析报告", notes = "预警分析报告")
     @ApiImplicitParams({
             @ApiImplicitParam(name = "time", value = "时间", required = true, dataType = "string", paramType = "query"),
             @ApiImplicitParam(name = "wpid", value = "场站", required = true, dataType = "string", paramType = "query")})
@@ -48,7 +56,28 @@ public class EarlyWarningController {
 
         Map<String, String> map = new HashMap<>();
         if (!time.isEmpty()) {
-//            map = earlyWarninggetService.Electricity(time, wpid);
+            map = earlyWarninggetService.Electricity(time, wpid);
+        }
+        if (map.size() > 0) {
+            return JsonResult.successData(ResultCode.SUCCESS, map);
+        } else {
+            return JsonResult.error(500, "该月份没有数据");
+        }
+
+    }
+
+    @GetMapping(value = "/econreport")
+    @ApiOperation(value = "经济运行分析报告", notes = "经济运行分析报告")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "wpid", value = "场站", required = true, dataType = "string", paramType = "query"),
+            @ApiImplicitParam(name = "wtid", value = "风机", required = true, dataType = "string", paramType = "query"),
+            @ApiImplicitParam(name = "time", value = "时间", required = true, dataType = "string", paramType = "query")
+    })
+    public JSONObject geteconreportinformation(String wpid,String wtid,String time) throws Exception {
+
+        Map<String, String> map = new HashMap<>();
+        if (!wpid.isEmpty()) {
+            map = reportService.Electricity(wpid,wtid,time);
         }
         if (map.size() > 0) {
             return JsonResult.successData(ResultCode.SUCCESS, map);

+ 27 - 0
runeconomy-xk/src/main/java/com/gyee/runeconomy/controller/WindDirection/WindDirectionController.java

@@ -0,0 +1,27 @@
+package com.gyee.runeconomy.controller.WindDirection;
+
+import com.alibaba.fastjson.JSONObject;
+import com.gyee.runeconomy.dto.result.JsonResult;
+import com.gyee.runeconomy.dto.result.ResultCode;
+import com.gyee.runeconomy.service.WindDirection.WindDirectionService;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.text.ParseException;
+
+@RestController
+@CrossOrigin
+@RequestMapping("/winddirecton")
+public class WindDirectionController {
+    @Resource
+    private WindDirectionService windDirectionService;
+    /***
+     * 风速风向频次玫瑰图
+     * @return
+     */
+    @GetMapping("/roses")
+    public JSONObject fsRoses(String wtid,String kssj,String jssj) throws Exception {
+        Object o = windDirectionService.fxRoses(wtid, kssj, jssj);
+        return JsonResult.successData(ResultCode.SUCCESS, o);
+    }
+}

+ 2 - 0
runeconomy-xk/src/main/java/com/gyee/runeconomy/model/ReliabilityIssues.java

@@ -167,4 +167,6 @@ public class ReliabilityIssues extends Model<ReliabilityIssues> {
      */
     private Integer reliabilityIs;
 
+    private double reliabilityIssueJs;
+
 }

+ 51 - 0
runeconomy-xk/src/main/java/com/gyee/runeconomy/model/vo/IndicatorData.java

@@ -0,0 +1,51 @@
+package com.gyee.runeconomy.model.vo;
+
+public class IndicatorData {
+    private String indicator;  // 指标名称
+    private double currentMonthData; // 本月数据
+    private double lastYearData; // 去年同期数据
+    private String growthRate;  // 增长率
+
+
+
+    public IndicatorData(String indicator, double currentMonthData, double lastYearData, String growthRate) {
+        this.indicator = indicator;
+        this.currentMonthData = currentMonthData;
+        this.lastYearData = lastYearData;
+        this.growthRate = growthRate;
+    }
+
+    public String getIndicator() {
+        return indicator;
+    }
+
+    public void setIndicator(String indicator) {
+        this.indicator = indicator;
+    }
+
+    public double getCurrentMonthData() {
+        return currentMonthData;
+    }
+
+    public void setCurrentMonthData(double currentMonthData) {
+        this.currentMonthData = currentMonthData;
+    }
+
+    public double getLastYearData() {
+        return lastYearData;
+    }
+
+    public void setLastYearData(double lastYearData) {
+        this.lastYearData = lastYearData;
+    }
+
+    public String getGrowthRate() {
+        return growthRate;
+    }
+
+    public void setGrowthRate(String growthRate) {
+        this.growthRate = growthRate;
+    }
+
+
+}

+ 26 - 10
runeconomy-xk/src/main/java/com/gyee/runeconomy/service/EarlyWarning/EarlyWarningReliableService.java

@@ -20,8 +20,7 @@ import javax.annotation.Resource;
 import java.time.LocalDate;
 import java.time.YearMonth;
 import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.*;
 import java.util.stream.Collectors;
 
 @Service
@@ -57,9 +56,9 @@ public class EarlyWarningReliableService {
         // 获取指定月份的起始和结束日期时间,并转换为字符串
         String startOfMonth = yearMonth.atDay(1).atTime(0, 0, 0).format(outputFormatter); // 上个月的第一天,精确到 00:00:00
         String endOfMonth = yearMonth.atEndOfMonth().atTime(23, 59, 59).format(outputFormatter); // 上个月的最后一天,精确到 23:59:59
-//
+
 //        String startby = "2024-11-01 00:00:00";
-//        String startbyjs = "2024-11-14 10:00:00";
+//        String startbyjs = "2024-11-30 10:00:00";
         /*********************************************时间处理*********************************/
 
         List<ProBasicPowerstation> powerstationList = CacheContext.wpls;
@@ -109,6 +108,23 @@ public class EarlyWarningReliableService {
                 issue.setDowntimeCount(tjcssum);
                 IssuesList.add(issue);
 
+
+                // 使用流按部件类型分组,并筛选出每组中count最大的记录
+                Map<String, Optional<AlarmDataParser.AlarmData>> groupedData = response.stream()
+                        .collect(Collectors.groupingBy(
+                                AlarmDataParser.AlarmData::getRelateParts, // 按部件类型分组
+                                Collectors.maxBy(Comparator.comparingInt(AlarmDataParser.AlarmData::getCount)) // 按count最大值筛选
+                        ));
+                // 将筛选出的最大count值的AlarmData重新赋给response
+                List<AlarmDataParser.AlarmData> filteredResponse = groupedData.values().stream()
+                        .map(Optional::get)
+                        .collect(Collectors.toList());
+
+                // 将筛选后的结果赋给原始response
+                response.clear(); // 清空原有数据
+                response.addAll(filteredResponse); // 将筛选后的数据添加回response
+
+                
                 // 处理每条预警数据
                 for (AlarmDataParser.AlarmData alarmData : response) {
                     // 查询 Alarmid 对应信息
@@ -153,10 +169,10 @@ public class EarlyWarningReliableService {
                         totalFrequency += parseFrequency(iss.getTemperatureOtherFrequencyHours());
 
                         // 将计算的总频次存储在临时变量中
-                        iss.setReliabilityIssue(String.valueOf(totalFrequency));  // 临时存储频次总和为字符串
+                        iss.setReliabilityIssueJs(totalFrequency);  // 存储为double类型的频次总和
                         return iss;
                     })
-                    .sorted((i1, i2) -> Double.compare(parseFrequency(i2.getReliabilityIssue()), parseFrequency(i1.getReliabilityIssue())))  // 按总频次排序
+                    .sorted((i1, i2) -> Double.compare(i2.getReliabilityIssueJs(), i1.getReliabilityIssueJs()))  // 按总频次排序
                     .limit(5)  // 取前五名
                     .collect(Collectors.toList());
 
@@ -173,15 +189,15 @@ public class EarlyWarningReliableService {
                         totalFrequency += parseFrequency(iss.getOtherFrequencyHours());
 
                         // 将计算的总频次存储在临时变量中
-                        iss.setReliabilityIssue(String.valueOf(totalFrequency));  // 临时存储频次总和为字符串
+                        iss.setReliabilityIssueJs(totalFrequency);  // 临时存储频次总和为字符串
                         return iss;
                     })
-                    .sorted((i1, i2) -> Double.compare(parseFrequency(i2.getReliabilityIssue()), parseFrequency(i1.getReliabilityIssue())))  // 按总频次排序
+                    .sorted((i1, i2) -> Double.compare(i2.getReliabilityIssueJs(), i1.getReliabilityIssueJs())) // 按总频次排序
                     .limit(5)  // 取前五名
                     .collect(Collectors.toList());
 
             // 合并两组数据
-            List<ReliabilityIssues> topTenIssues = new ArrayList<>();
+//            List<ReliabilityIssues> topTenIssues = new ArrayList<>();
             firstSortedIssues.forEach(iss -> {
                 iss.setReliabilityIssue("是");
                 iss.setReliabilityIs(1);});
@@ -189,7 +205,7 @@ public class EarlyWarningReliableService {
             secondSortedIssues.forEach(isc -> {
                 isc.setReliabilityIssue("是");
                 // 判断 ReliabilityIs 是否为 1
-                if (isc.getReliabilityIs() == 1) {
+                if (isc.getReliabilityIs() != null && isc.getReliabilityIs() == 1) {
                     isc.setReliabilityIs(3);  // 如果是 1,设置为 3
                 } else {
                     isc.setReliabilityIs(2);  // 否则,设置为 2

+ 327 - 0
runeconomy-xk/src/main/java/com/gyee/runeconomy/service/EarlyWarning/EarlyWarninggetService.java

@@ -0,0 +1,327 @@
+package com.gyee.runeconomy.service.EarlyWarning;
+
+import com.gyee.runeconomy.init.CacheContext;
+import com.gyee.runeconomy.model.ProEconAlarmPlan;
+import com.gyee.runeconomy.model.ProEconAlarmReal;
+import com.gyee.runeconomy.model.ProEconAlarmRule;
+import com.gyee.runeconomy.model.ReliabilityIssues;
+import com.gyee.runeconomy.model.auto.ProBasicEquipment;
+import com.gyee.runeconomy.model.auto.ProBasicPowerstation;
+import com.gyee.runeconomy.service.auto.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Service
+public class EarlyWarninggetService {
+
+    @Autowired
+    private IProBasicPowerstationService powerstationService;
+    @Resource
+    private ReliabilityIssuesService reliabilityIssuesService;
+    @Resource
+    private ProEconAlarmRuleService ruleService;
+    @Resource
+    private ProEconAlarmRealService realService;
+    @Resource
+    private ProEconAlarmPlanService planService;
+
+    public Map Electricity(String time, String wpid) throws Exception {
+
+        Map<String, Object> map = new HashMap<>();
+
+
+        List<ProBasicPowerstation> selectwpid = powerstationService.selectwpid(wpid);
+        ProBasicPowerstation proBasicPowerstation = null;
+        if (selectwpid.size() > 0) {
+            proBasicPowerstation = selectwpid.get(0);
+        }
+        List<ReliabilityIssues> iskkx = reliabilityIssuesService.getReliabilityIssues(wpid, time);
+
+        /*************************************************************筛选结果***********************************************/
+
+        // 筛选 reliabilityIssue 字段为 "是",并且 reliabilityIs 字段为 1 的记录
+        String wdl = iskkx.stream()
+                .filter(issue -> "是".equals(issue.getReliabilityIssue()) && issue.getReliabilityIs() == 1)
+                .sorted(Comparator.comparing(ReliabilityIssues::getTurbineId))  // 根据 turbineId 排序
+                .map(ReliabilityIssues::getTurbineId)
+                .collect(Collectors.joining(","));
+
+        // 筛选 reliabilityIssue 字段为 "是",并且 reliabilityIs 字段为 2 的记录
+        String ztl = iskkx.stream()
+                .filter(issue -> "是".equals(issue.getReliabilityIssue()) && issue.getReliabilityIs() == 2)
+                .sorted(Comparator.comparing(ReliabilityIssues::getTurbineId))  // 根据 turbineId 排序
+                .map(ReliabilityIssues::getTurbineId)
+                .collect(Collectors.joining(","));
+
+        // 筛选 reliabilityIssue 字段为 "是",并且 reliabilityIs 字段为 3 的记录
+        String ztwdl = iskkx.stream()
+                .filter(issue -> "是".equals(issue.getReliabilityIssue()) && issue.getReliabilityIs() == 3)
+                .sorted(Comparator.comparing(ReliabilityIssues::getTurbineId))  // 根据 turbineId 排序
+                .map(ReliabilityIssues::getTurbineId)
+                .collect(Collectors.joining(","));
+
+
+        // 拼接相关字段,检查每个字段是否为空,并加入结果中
+        StringBuilder sxjgBuilder = new StringBuilder();
+
+
+        if (wdl != null && !wdl.isEmpty()) {
+            sxjgBuilder.append("以下风机存在温度类隐患:").append(wdl).append("\n");
+        }
+        if (ztl != null && !ztl.isEmpty()) {
+            sxjgBuilder.append("以下风机存在状态类隐患:").append(ztl).append("\n");
+        }
+        if (ztwdl != null && !ztwdl.isEmpty()) {
+            sxjgBuilder.append("以下风机存在状态类及温度类隐患:").append(ztwdl).append("\n");
+        }
+
+        //不去掉换行符
+        String sxjg = sxjgBuilder.toString();
+        if (sxjg.endsWith("\n")) {
+            sxjg = sxjg.substring(0, sxjg.length() - 1);  // 去掉最后一个换行符
+        }
+        //去掉换行符
+//        String sxjg = sxjgBuilder.toString().trim();
+
+        /*************************************************************筛选结果***********************************************/
+
+        Map<String,List> jxjymap = new TreeMap<>();
+        Map<String, List> finalReport = new TreeMap<>();
+
+        for (ReliabilityIssues vo : iskkx) {
+            List<ReliabilityIssues> collect = iskkx.stream().filter(v -> vo.getTurbineId().equals(v.getTurbineId())).collect(Collectors.toList());
+            StringBuilder kkxBuilder = new StringBuilder();
+            List<String> qksm = new ArrayList<>();
+            List<String> jxjyls = new ArrayList<>();
+
+            List<ProBasicEquipment> equipments = CacheContext.wtls.stream()
+                    .filter(wt -> vo.getTurbineId().equals(wt.getAname())).collect(Collectors.toList());
+            String modelId = equipments.get(0).getModelId();
+
+            /*******************************************************添加检修建议*************************************************/
+            if (collect.size() > 0) {
+                if (collect.get(0).getMainShaftType() != null){
+                    kkxBuilder.append(collect.get(0).getMainShaftType()).append(",");
+                    String mainShaftType = collect.get(0).getMainShaftType();
+                    List<ProEconAlarmRule> ruleList = ruleService.getProEconAlarmRuleList(mainShaftType,modelId);
+                    if (ruleList.size() > 0) {
+                        String id = ruleList.get(0).getId();
+                        List<ProEconAlarmReal> realid = realService.getProEconAlarmReal(id);
+                        if (realid.size() > 0){
+                            String alarmPlan = realid.get(0).getAlarmPlan();
+                            List<ProEconAlarmPlan> plans = planService.selectAlarmPlanByProId(alarmPlan);
+                            if (plans.size() > 0){
+                                jxjyls.add(mainShaftType + ":" + plans.get(0).getProcessMethod());
+                            }
+                        }
+                    }
+
+                }
+                if (collect.get(0).getGeneratorType() != null){
+                    kkxBuilder.append(collect.get(0).getGeneratorType()).append(",");
+                    String generatorType = collect.get(0).getGeneratorType();
+                    List<ProEconAlarmRule> ruleList = ruleService.getProEconAlarmRuleList(generatorType,modelId);
+                    if (ruleList.size() > 0) {
+                        String id = ruleList.get(0).getId();
+                        List<ProEconAlarmReal> realid = realService.getProEconAlarmReal(id);
+                        if (realid.size() > 0){
+                            String alarmPlan = realid.get(0).getAlarmPlan();
+                            List<ProEconAlarmPlan> plans = planService.selectAlarmPlanByProId(alarmPlan);
+                            if (plans.size() > 0){
+                                jxjyls.add( generatorType + ":" + plans.get(0).getProcessMethod());
+                            }
+                        }
+                    }
+
+                }
+                if (collect.get(0).getGearboxType() !=null){
+                    kkxBuilder.append(collect.get(0).getGearboxType()).append(",");
+                    String gearboxType = collect.get(0).getGearboxType();
+                    List<ProEconAlarmRule> ruleList = ruleService.getProEconAlarmRuleList(gearboxType,modelId);
+                    if (ruleList.size() > 0) {
+                        String id = ruleList.get(0).getId();
+                        List<ProEconAlarmReal> realid = realService.getProEconAlarmReal(id);
+                        if (realid.size() > 0){
+                            String alarmPlan = realid.get(0).getAlarmPlan();
+                            List<ProEconAlarmPlan> plans = planService.selectAlarmPlanByProId(alarmPlan);
+                            if (plans.size() > 0){
+                                jxjyls.add(gearboxType + ":" + plans.get(0).getProcessMethod());
+                            }
+                        }
+                    }
+                }
+                if (collect.get(0).getPitchSystemType() !=null){
+                    kkxBuilder.append(collect.get(0).getPitchSystemType()).append(",");
+                    String pitchSystemType = collect.get(0).getPitchSystemType();
+                    List<ProEconAlarmRule> ruleList = ruleService.getProEconAlarmRuleList(pitchSystemType,modelId);
+                    if (ruleList.size() > 0) {
+                        String id = ruleList.get(0).getId();
+                        List<ProEconAlarmReal> realid = realService.getProEconAlarmReal(id);
+                        if (realid.size() > 0){
+                            String alarmPlan = realid.get(0).getAlarmPlan();
+                            List<ProEconAlarmPlan> plans = planService.selectAlarmPlanByProId(alarmPlan);
+                            if (plans.size() > 0){
+                                jxjyls.add(pitchSystemType + ":" + plans.get(0).getProcessMethod());
+                            }
+                        }
+                    }
+                }
+                if (collect.get(0).getTemperatureOtherIssues() !=null) {
+                    kkxBuilder.append(collect.get(0).getTemperatureOtherIssues()).append(",");
+
+                    String temperatureOtherIssues = collect.get(0).getTemperatureOtherIssues();
+                    List<ProEconAlarmRule> ruleList = ruleService.getProEconAlarmRuleList(temperatureOtherIssues,modelId);
+                    if (ruleList.size() > 0) {
+                        String id = ruleList.get(0).getId();
+                        List<ProEconAlarmReal> realid = realService.getProEconAlarmReal(id);
+                        if (realid.size() > 0){
+                            String alarmPlan = realid.get(0).getAlarmPlan();
+                            List<ProEconAlarmPlan> plans = planService.selectAlarmPlanByProId(alarmPlan);
+                            if (plans.size() > 0){
+                                jxjyls.add(temperatureOtherIssues + ":" + plans.get(0).getProcessMethod());
+                            }
+                        }
+                    }
+                }
+                if (collect.get(0).getVibrationType() !=null){
+                    kkxBuilder.append(collect.get(0).getVibrationType()).append(",");
+                    String vibrationType = collect.get(0).getVibrationType();
+
+                    List<ProEconAlarmRule> ruleList = ruleService.getProEconAlarmRuleList(vibrationType,modelId);
+                    if (ruleList.size() > 0) {
+                        String id = ruleList.get(0).getId();
+                        List<ProEconAlarmReal> realid = realService.getProEconAlarmReal(id);
+                        if (realid.size() > 0){
+                            String alarmPlan = realid.get(0).getAlarmPlan();
+                            List<ProEconAlarmPlan> plans = planService.selectAlarmPlanByProId(alarmPlan);
+                            if (plans.size() > 0){
+                                jxjyls.add(vibrationType + ":" + plans.get(0).getProcessMethod());
+                            }
+                        }
+                    }
+                }
+                if (collect.get(0).getMechanicalType() !=null){
+                    kkxBuilder.append(collect.get(0).getMechanicalType()).append(",");
+                    String mechanicalType = collect.get(0).getMechanicalType();
+                    List<ProEconAlarmRule> ruleList = ruleService.getProEconAlarmRuleList(mechanicalType,modelId);
+                    if (ruleList.size() > 0) {
+                        String id = ruleList.get(0).getId();
+                        List<ProEconAlarmReal> realid = realService.getProEconAlarmReal(id);
+                        if (realid.size() > 0){
+                            String alarmPlan = realid.get(0).getAlarmPlan();
+                            List<ProEconAlarmPlan> plans = planService.selectAlarmPlanByProId(alarmPlan);
+                            if (plans.size() > 0){
+                                jxjyls.add(mechanicalType + ":" + plans.get(0).getProcessMethod());
+                            }
+                        }
+                    }
+                }
+                if (collect.get(0).getElectricalType() !=null){
+                    kkxBuilder.append(collect.get(0).getElectricalType()).append(",");
+                    String electricalType = collect.get(0).getElectricalType();
+                    List<ProEconAlarmRule> ruleList = ruleService.getProEconAlarmRuleList(electricalType,modelId);
+                    if (ruleList.size() > 0) {
+                        String id = ruleList.get(0).getId();
+                        List<ProEconAlarmReal> realid = realService.getProEconAlarmReal(id);
+                        if (realid.size() > 0){
+                            String alarmPlan = realid.get(0).getAlarmPlan();
+                            List<ProEconAlarmPlan> plans = planService.selectAlarmPlanByProId(alarmPlan);
+                            if (plans.size() > 0){
+                                jxjyls.add(electricalType + ":" + plans.get(0).getProcessMethod());
+                            }
+                        }
+                    }
+                }
+                if (collect.get(0).getHydraulicType() !=null){
+                    kkxBuilder.append(collect.get(0).getHydraulicType()).append(",");
+                    String hydraulicType = collect.get(0).getHydraulicType();
+                    List<ProEconAlarmRule> ruleList = ruleService.getProEconAlarmRuleList(hydraulicType,modelId);
+                    if (ruleList.size() > 0) {
+                        String id = ruleList.get(0).getId();
+                        List<ProEconAlarmReal> realid = realService.getProEconAlarmReal(id);
+                        if (realid.size() > 0){
+                            String alarmPlan = realid.get(0).getAlarmPlan();
+                            List<ProEconAlarmPlan> plans = planService.selectAlarmPlanByProId(alarmPlan);
+                            if (plans.size() > 0){
+                                jxjyls.add(hydraulicType + ":" + plans.get(0).getProcessMethod());
+                            }
+                        }
+                    }
+                }
+                if (collect.get(0).getWindMeasurementType() !=null){
+                    kkxBuilder.append(collect.get(0).getWindMeasurementType()).append(",");
+                    String windMeasurementType = collect.get(0).getWindMeasurementType();
+                    List<ProEconAlarmRule> ruleList = ruleService.getProEconAlarmRuleList(windMeasurementType,modelId);
+                    if (ruleList.size() > 0) {
+                        String id = ruleList.get(0).getId();
+                        List<ProEconAlarmReal> realid = realService.getProEconAlarmReal(id);
+                        if (realid.size() > 0){
+                            String alarmPlan = realid.get(0).getAlarmPlan();
+                            List<ProEconAlarmPlan> plans = planService.selectAlarmPlanByProId(alarmPlan);
+                            if (plans.size() > 0){
+                                jxjyls.add(windMeasurementType + ":" + plans.get(0).getProcessMethod());
+                            }
+                        }
+                    }
+                }
+                if (collect.get(0).getOtherIssues() !=null){
+                    kkxBuilder.append(collect.get(0).getOtherIssues()).append(",");
+                    String otherIssues = collect.get(0).getOtherIssues();
+                    List<ProEconAlarmRule> ruleList = ruleService.getProEconAlarmRuleList(otherIssues,modelId);
+                    if (ruleList.size() > 0) {
+                        String id = ruleList.get(0).getId();
+                        List<ProEconAlarmReal> realid = realService.getProEconAlarmReal(id);
+                        if (realid.size() > 0){
+                            String alarmPlan = realid.get(0).getAlarmPlan();
+                            List<ProEconAlarmPlan> plans = planService.selectAlarmPlanByProId(alarmPlan);
+                            if (plans.size() > 0){
+                                jxjyls.add(otherIssues + ":"+ plans.get(0).getProcessMethod());
+                            }
+                        }
+                    }
+                }
+            }
+            /*******************************************************添加检修建议*************************************************/
+
+
+            String issue = "0";
+            Integer issis = null;
+            if (collect.size() > 0) {
+                issue = collect.get(0).getReliabilityIssue();
+                issis = collect.get(0).getReliabilityIs();
+                if (Objects.isNull(issis)) {
+                    issis = 3; // 如果为空则赋值为3
+                }
+            }
+
+            if (kkxBuilder.length() > 0 && issue.equals("是")) {
+                kkxBuilder.insert(0, "可靠性问题:");
+                kkxBuilder.deleteCharAt(kkxBuilder.length() - 1);
+                kkxBuilder.append("。"); // 添加句号,删除最后一个字符
+                String kkxresult = kkxBuilder.toString();
+                qksm.add(kkxresult);
+            }
+
+            if (qksm.size() > 0) {
+                finalReport.put(vo.getTurbineId(), qksm);
+            }
+
+            if (jxjyls.size() > 0) {
+                jxjymap.put(vo.getTurbineId(), jxjyls);
+            }
+        }
+
+        map.put("stationid", proBasicPowerstation);
+        map.put("reporttime", time);
+        iskkx.sort(Comparator.comparing(ReliabilityIssues::getTurbineId));
+        map.put("kkxwtls", iskkx);
+        map.put("FilterResults", sxjg);
+        map.put("qksm",finalReport);
+        map.put("jxjy",jxjymap);
+        return map;
+    }
+}

+ 333 - 0
runeconomy-xk/src/main/java/com/gyee/runeconomy/service/EarlyWarning/ReportService.java

@@ -0,0 +1,333 @@
+package com.gyee.runeconomy.service.EarlyWarning;
+
+import com.gyee.common.util.DoubleUtils;
+import com.gyee.runeconomy.init.CacheContext;
+import com.gyee.runeconomy.model.auto.ProBasicEquipment;
+import com.gyee.runeconomy.model.auto.ProBasicPowerstation;
+import com.gyee.runeconomy.model.auto.ProEconEquipmentmodel;
+import com.gyee.runeconomy.model.auto.TurbineInfoDay;
+import com.gyee.runeconomy.model.vo.IndicatorData;
+import com.gyee.runeconomy.service.auto.IProBasicPowerstationService;
+import com.gyee.runeconomy.service.auto.ITurbineInfoDayService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
+import java.time.LocalDate;
+import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Service
+public class ReportService {
+
+    @Autowired
+    private IProBasicPowerstationService powerstationService;
+
+    @Autowired
+    private ITurbineInfoDayService turbineInfoDayService;
+
+    public Map Electricity(String wpid, String wtid, String time) throws Exception {
+
+        //查询时间-开始结束
+        String[] timeRange = generateTimeRange(time);
+
+        //去年同期时间
+        String qntq = subtractOneYear(time);
+        String[] qntime = generateTimeRange(qntq);
+
+        //月天数
+        int daysInMonth = getDaysInMonth(timeRange);
+        int qnInMonth = getDaysInMonth(qntime);
+
+        System.out.println("开始时间: " + timeRange[0]);
+        System.out.println("结束时间: " + timeRange[1]);
+
+        Map<String, Object> map = new HashMap<>();
+
+        List<ProBasicPowerstation> selectwpid = powerstationService.selectwpid(wpid);
+
+        List<ProBasicEquipment> collect = CacheContext.wtls.stream()
+                .filter(wt -> wtid.equals(wt.getId())).collect(Collectors.toList());
+        //获取单机设备容量
+        List<ProEconEquipmentmodel> rl = CacheContext.equipmentmodels.stream()
+                .filter(mo -> collect.get(0).getModelId().equals(mo.getId())).collect(Collectors.toList());
+
+        double sbrl = 1;
+        if (rl.size() > 0) {
+            sbrl = rl.get(0).getPowerProduction();
+        }
+        List<TurbineInfoDay> turbineList = turbineInfoDayService.getTurbineList(wtid, timeRange[0], timeRange[1]);
+        List<TurbineInfoDay> tqturbineList = turbineInfoDayService.getTurbineList(wtid, qntime[0], qntime[1]);
+
+        List<IndicatorData> result = new ArrayList<>();
+
+        double currentPower = turbineList.stream()
+                .mapToDouble(t -> t.getRfdl() == null ? 0 : Math.round(t.getRfdl().doubleValue() * 100.0) / 100.0)
+                .sum(); // 本月实际发电量
+        double lastYearPower = tqturbineList.stream()
+                .mapToDouble(t -> t.getRfdl() == null ? 0 : Math.round(t.getRfdl().doubleValue() * 100.0) / 100.0)
+                .sum(); // 去年同期实际发电量
+
+
+        double currentTheoryPower = turbineList.stream()
+                .mapToDouble(t -> t.getLlfdl() == null ? 0 : Math.round(t.getLlfdl().doubleValue() * 100.0) / 100.0)
+                .sum(); // 本月理论发电量
+        double lastYearTheoryPower = tqturbineList.stream()
+                .mapToDouble(t -> t.getLlfdl() == null ? 0 : Math.round(t.getLlfdl().doubleValue() * 100.0) / 100.0)
+                .sum(); // 去年同期理论发电量
+
+
+        double currentWindSpeed =  new BigDecimal(
+                turbineList.stream()
+                        .mapToDouble(t -> t.getPjfs() == null ? 0 : t.getPjfs().doubleValue())
+                        .average()
+                        .orElse(0.0)
+        ).setScale(2, RoundingMode.HALF_UP)
+                .doubleValue();// 本月平均风速
+
+        double lastYearWindSpeed =  new BigDecimal(
+                tqturbineList.stream()
+                        .mapToDouble(t -> t.getPjfs() == null ? 0 : t.getPjfs().doubleValue())
+                        .average()
+                        .orElse(0.0)
+        ).setScale(2, RoundingMode.HALF_UP)
+                .doubleValue(); // 去年同期平均风速
+
+
+        double currentUtilization = DoubleUtils.getRoundingNum(currentPower / sbrl,0); // 本月利用小时
+        double lastYearUtilization = DoubleUtils.getRoundingNum(lastYearPower / sbrl,0);; // 去年同期利用小时
+
+        //计划运行时间
+        int jhyxsj = daysInMonth * 24;
+        int tqjhyxsj = qnInMonth * 24;
+
+        //运行时间
+        double yxsj = turbineList.stream()
+                .mapToDouble(t -> t.getYxMin() == null ? 0 : Math.round(t.getYxMin().doubleValue() * 100.0) / 100.0)
+                .sum();
+        double tqyxsj = tqturbineList.stream()
+                .mapToDouble(t -> t.getYxMin() == null ? 0 : Math.round(t.getYxMin().doubleValue() * 100.0) / 100.0)
+                .sum();
+
+        double currentEquipmentUtilization = yxsj / jhyxsj * 100; // 本月设备可利用率
+        double lastYearEquipmentUtilization = tqyxsj / tqjhyxsj * 100; // 去年同期设备可利用率
+
+        //检修时间
+        double jxsj = turbineList.stream()
+                .mapToDouble(t -> t.getYxMin() == null ? 0 : Math.round(t.getYxMin().doubleValue() * 100.0) / 100.0)
+                .sum();
+        double tqjxsj = tqturbineList.stream()
+                .mapToDouble(t -> t.getYxMin() == null ? 0 : Math.round(t.getYxMin().doubleValue() * 100.0) / 100.0)
+                .sum();
+
+        double currentEfficiencyCoefficient = (yxsj + jxsj) / jhyxsj * 100;; // 本月等效可用系数
+        double lastYearEfficiencyCoefficient = (tqyxsj + tqjxsj) / tqjhyxsj * 100; // 去年同期等效可用系数
+
+
+        double currentSmallWindSpeed = new BigDecimal(
+                turbineList.stream()
+                        .mapToDouble(t -> t.getXfqrfs() == null ? 0 : t.getXfqrfs().doubleValue())
+                        .average()
+                        .orElse(0.0)
+        ).setScale(2, RoundingMode.HALF_UP)
+                .doubleValue(); // 本月小风切入风速
+        double lastYearSmallWindSpeed = new BigDecimal(
+                tqturbineList.stream()
+                        .mapToDouble(t -> t.getXfqrfs() == null ? 0 : t.getXfqrfs().doubleValue())
+                        .average()
+                        .orElse(0.0)
+        ).setScale(2, RoundingMode.HALF_UP)
+                .doubleValue(); // 去年同期小风切入风速
+
+        double currentSmallWindRate = 0.95; // 本月小风切入合格率
+        double lastYearSmallWindRate = 0.93; // 去年同期小风切入合格率
+
+        double currentRunningWindHours = 0.1; // 本月有效风时数
+        double lastYearRunningWindHours = 0.1; // 去年同期有效风时数
+
+        double currentFaultLossPower = turbineList.stream()
+                .mapToDouble(t -> t.getGzss() == null ? 0 : Math.round(t.getGzss().doubleValue() * 100.0) / 100.0)
+                .sum(); // 本月故障损失电量
+        double lastYearFaultLossPower = tqturbineList.stream()
+                .mapToDouble(t -> t.getGzss() == null ? 0 : Math.round(t.getGzss().doubleValue() * 100.0) / 100.0)
+                .sum(); // 去年同期故障损失电量
+
+        double currentDispatchLossPower = turbineList.stream()
+                .mapToDouble(t -> t.getXdss() == null ? 0 : Math.round(t.getXdss().doubleValue() * 100.0) / 100.0)
+                .sum(); // 本月调度限电损失电量
+        double lastYearDispatchLossPower = tqturbineList.stream()
+                .mapToDouble(t -> t.getXdss() == null ? 0 : Math.round(t.getXdss().doubleValue() * 100.0) / 100.0)
+                .sum(); // 去年同期调度限电损失电量
+
+        double currentPlannedMaintenanceLossPower = turbineList.stream()
+                .mapToDouble(t -> t.getJhjxss() == null ? 0 : Math.round(t.getJhjxss().doubleValue() * 100.0) / 100.0)
+                .sum(); // 本月计划检修损失电量
+        double lastYearPlannedMaintenanceLossPower = tqturbineList.stream()
+                .mapToDouble(t -> t.getJhjxss() == null ? 0 : Math.round(t.getJhjxss().doubleValue() * 100.0) / 100.0)
+                .sum(); // 去年同期计划检修损失电量
+
+        double currentPerformanceLossPower = turbineList.stream()
+                .mapToDouble(t -> t.getXnss() == null ? 0 : Math.round(t.getXnss().doubleValue() * 100.0) / 100.0)
+                .sum(); // 本月性能未达标损失电量
+        double lastYearPerformanceLossPower = tqturbineList.stream()
+                .mapToDouble(t -> t.getXnss() == null ? 0 : Math.round(t.getXnss().doubleValue() * 100.0) / 100.0)
+                .sum(); // 去年同期性能未达标损失电量
+
+        double currentFaultDowntime = turbineList.stream()
+                .mapToDouble(t -> t.getGzMin() == null ? 0 : Math.round(t.getGzMin().doubleValue() * 100.0) / 100.0)
+                .sum(); // 本月故障停运时间
+        double lastYearFaultDowntime = tqturbineList.stream()
+                .mapToDouble(t -> t.getGzMin() == null ? 0 : Math.round(t.getGzMin().doubleValue() * 100.0) / 100.0)
+                .sum(); // 去年同期故障停运时间
+
+        double currentMaintenanceDowntime = jxsj; // 本月检修停运时间
+        double lastYearMaintenanceDowntime = tqjxsj; // 去年同期检修停运时间
+
+        double currentGridConnectionTime = turbineList.stream()
+                .mapToDouble(t -> t.getYxMin() == null ? 0 : Math.round(t.getYxMin().doubleValue() * 100.0) / 100.0)
+                .sum(); // 本月并网时间
+        double lastYearGridConnectionTime = tqturbineList.stream()
+                .mapToDouble(t -> t.getYxMin() == null ? 0 : Math.round(t.getYxMin().doubleValue() * 100.0) / 100.0)
+                .sum(); // 去年同期并网时间
+
+        double currentStandbyTime = turbineList.stream()
+                .mapToDouble(t -> t.getDjMin() == null ? 0 : Math.round(t.getDjMin().doubleValue() * 100.0) / 100.0)
+                .sum(); // 本月待机时间
+        double lastYearStandbyTime = tqturbineList.stream()
+                .mapToDouble(t -> t.getDjMin() == null ? 0 : Math.round(t.getDjMin().doubleValue() * 100.0) / 100.0)
+                .sum(); // 去年同期待机时间
+
+        double currentCommFailureTime = turbineList.stream()
+                .mapToDouble(t -> t.getTjMin() == null ? 0 : Math.round(t.getTjMin().doubleValue() * 100.0) / 100.0)
+                .sum(); // 本月通讯中断时间
+        double lastYearCommFailureTime = tqturbineList.stream()
+                .mapToDouble(t -> t.getTjMin() == null ? 0 : Math.round(t.getTjMin().doubleValue() * 100.0) / 100.0)
+                .sum(); // 去年同期通讯中断时间
+
+        double currentCalmWindFrequency = 0.05; // 本月静风频率
+        double lastYearCalmWindFrequency = 0.04; // 去年同期静风频率
+
+        // 使用方法来简化添加数据
+        result.add(createIndicatorData("实际发电量", currentPower, lastYearPower));
+        result.add(createIndicatorData("理论发电量", currentTheoryPower, lastYearTheoryPower));
+        result.add(createIndicatorData("平均风速", currentWindSpeed, lastYearWindSpeed));
+        result.add(createIndicatorData("利用小时", currentUtilization, lastYearUtilization));
+        result.add(createIndicatorData("设备可利用率", currentEquipmentUtilization, lastYearEquipmentUtilization));
+        result.add(createIndicatorData("等效可用系数", currentEfficiencyCoefficient, lastYearEfficiencyCoefficient));
+        result.add(createIndicatorData("小风切入风速", currentSmallWindSpeed, lastYearSmallWindSpeed));
+        result.add(createIndicatorData("小风切入合格率", currentSmallWindRate, lastYearSmallWindRate));
+        result.add(createIndicatorData("有效风时数", currentRunningWindHours, lastYearRunningWindHours));
+        result.add(createIndicatorData("故障损失电量", currentFaultLossPower, lastYearFaultLossPower));
+        result.add(createIndicatorData("调度限电损失电量", currentDispatchLossPower, lastYearDispatchLossPower));
+        result.add(createIndicatorData("计划检修损失电量", currentPlannedMaintenanceLossPower, lastYearPlannedMaintenanceLossPower));
+        result.add(createIndicatorData("性能未达标损失电量", currentPerformanceLossPower, lastYearPerformanceLossPower));
+        result.add(createIndicatorData("故障停运时间", currentFaultDowntime, lastYearFaultDowntime));
+        result.add(createIndicatorData("检修停运时间", currentMaintenanceDowntime, lastYearMaintenanceDowntime));
+        result.add(createIndicatorData("并网时间", currentGridConnectionTime, lastYearGridConnectionTime));
+        result.add(createIndicatorData("待机时间", currentStandbyTime, lastYearStandbyTime));
+        result.add(createIndicatorData("通讯中断时间", currentCommFailureTime, lastYearCommFailureTime));
+        result.add(createIndicatorData("静风频率", currentCalmWindFrequency, lastYearCalmWindFrequency));
+        map.put("lb",result);
+
+        double fdlzzl = calculateGrowthRate(currentPower, lastYearPower);//发电量增长率
+        double ssdllj = currentFaultLossPower + currentDispatchLossPower + currentPlannedMaintenanceLossPower + currentPerformanceLossPower; //损失电量合计
+        double tqssdllj = lastYearFaultLossPower + lastYearDispatchLossPower + lastYearPlannedMaintenanceLossPower + lastYearPerformanceLossPower;//同期损失电量合计
+        double ssdlzzl = calculateGrowthRate(ssdllj,tqssdllj);
+        double dlxc =DoubleUtils.getRoundingNum( currentPower - currentTheoryPower,2);
+        String wz1 = "本月"+ collect.get(0).getName() +"风机设备利用小时数"+ currentUtilization +"小时、同比"+ lastYearUtilization +"小时," +
+                "设备可利用率"+ currentEquipmentUtilization +"%、同比"+ lastYearEquipmentUtilization +"%,等效可用系数"+ currentEfficiencyCoefficient +"%、同比"+ lastYearEfficiencyCoefficient +"%," +
+                "静风频率达到"+ currentCalmWindFrequency +"%、同比"+ lastYearCalmWindFrequency +"%。";
+        String wz2 = collect.get(0).getName() + time +"月平均风速"+ currentWindSpeed +"m/s、同比"+ lastYearWindSpeed +"m/s,小风平均切入风速"+ currentSmallWindSpeed +"m/s、同比"+ lastYearSmallWindSpeed +"m/," +
+                "有效风时数"+ currentRunningWindHours +"小时、同比:"+ lastYearRunningWindHours +"小时,实际发电电里"+ currentPower +"万kmh、同比"+ lastYearPower +"万kwh,同比增长率"+ fdlzzl +"%," +
+                "各项损失电量累计"+ ssdllj +"万kmh、同比"+ tqssdllj +"万kmh,同比增长率"+ ssdlzzl +"%," +
+                "理论发电量"+ currentTheoryPower +"万kwh,实际发电量与理论发电里相差"+ dlxc +"万kwh。";
+        map.put("wz1",wz1);
+        map.put("wz2",wz2);
+        turbineList.sort(Comparator.comparing(TurbineInfoDay::getRecordDate));
+        map.put("tb",turbineList);
+
+        return map;
+    }
+
+    // 计算增长率
+    private double calculateGrowthRate(double current, double lastYear) {
+        if (lastYear == 0) {
+            return 0; // 避免除以0
+        }
+        return (current - lastYear) / lastYear;
+    }
+    // 定义一个方法来创建 IndicatorData
+    private IndicatorData createIndicatorData(String name, double currentValue, double lastYearValue) {
+        DecimalFormat df = new DecimalFormat("0.00%");
+        return new IndicatorData(
+                name,
+                currentValue,
+                lastYearValue,
+                df.format(calculateGrowthRate(currentValue, lastYearValue))
+        );
+    }
+    /**
+     * 获取时间月开始时间和月结束时间
+     *
+     * @param time 格式为 "yyyy-MM",例如 "2024-11"
+     * @return 包含开始时间和结束时间的字符串数组
+     */
+    public static String[] generateTimeRange(String time) {
+        // 时间格式化器,用于生成输出格式
+        DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("yyyy-MM");
+        DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+        // 解析输入的年月,并补充为该月的第一天
+        String timeWithDay = time + "-01";  // 补充为 "yyyy-MM-01"
+        LocalDate firstDayOfMonth = LocalDate.parse(timeWithDay, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+
+        // 开始时间:time月的第一天,00:00:00
+        String startTime = firstDayOfMonth.atStartOfDay().format(outputFormatter);
+
+        // 结束时间:time月的最后一天,23:59:59
+        LocalDate lastDayOfMonth = firstDayOfMonth.withDayOfMonth(firstDayOfMonth.lengthOfMonth());
+        String endTime = lastDayOfMonth.atTime(23, 59, 59).format(outputFormatter);
+
+        // 返回字符串数组,包含开始时间和结束时间
+        return new String[]{startTime, endTime};
+    }
+
+    /**
+     * 获取时间去年同期
+     *
+     * @param time
+     * @return
+     */
+    public static String subtractOneYear(String time) {
+        // 定义时间格式化器
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM");
+
+        // 使用 YearMonth 解析和操作年月
+        YearMonth yearMonth = YearMonth.parse(time, formatter);
+        YearMonth result = yearMonth.minusYears(1);
+
+        // 格式化为字符串
+        return result.format(formatter);
+    }
+
+    /**
+     * 获取指定时间段内的天数
+     *
+     * @param timeRange 包含开始时间和结束时间的字符串数组
+     * @return 天数
+     */
+    public static int getDaysInMonth(String[] timeRange) {
+        // 解析开始时间和结束时间
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+        LocalDate startDate = LocalDate.parse(timeRange[0], formatter);
+        LocalDate endDate = LocalDate.parse(timeRange[1], formatter);
+
+        // 计算开始时间到结束时间的天数差
+        return (int) (ChronoUnit.DAYS.between(startDate, endDate) + 1);
+    }
+}

+ 16 - 0
runeconomy-xk/src/main/java/com/gyee/runeconomy/service/WindDirection/Point.java

@@ -0,0 +1,16 @@
+package com.gyee.runeconomy.service.WindDirection;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class Point {
+
+    private double x;
+
+    private double y;
+
+}

+ 504 - 0
runeconomy-xk/src/main/java/com/gyee/runeconomy/service/WindDirection/WindALG.java

@@ -0,0 +1,504 @@
+package com.gyee.runeconomy.service.WindDirection;
+
+
+
+import com.gyee.runeconomy.dto.result.PowerPointData;
+import com.gyee.runeconomy.util.DateUtils;
+
+import java.text.DecimalFormat;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * 风向玫瑰图
+ * 风向频次玫瑰图
+ */
+public class WindALG {
+
+    /**
+     * 风速风向玫瑰图
+     * 风速:[0,2.5) [2.5,5) [5,7.5) [7.5,10) [10,12.5) [12.5,15) [15,17.5) [17.5,20) [20,22.5) [22.5,25) [25,inf)  11
+     * @param list
+     */
+    public static Object fxRoses(List<PowerPointData> list){
+        int[][] count = new int[11][16];
+
+        list.stream().forEach(item -> {
+            int fx = windFXAngle(item.getFx());
+            int speed = windSpeed(item.getSpeed());
+            if (speed < 11 && fx < 16)
+                count[speed][fx] = 1;
+        });
+
+        return count;
+    }
+    /**
+     * 风速风向玫瑰图-风速区域-映射
+     * 风速:[0,3) [3,5) [5,10)  [10,12) [12,25) [25,inf)  6
+     * @param list
+     */
+    public static Object fxysNewRoses(List<PowerPointData> list){
+        int[][] count = new int[8][16];
+
+        list.stream().forEach(item -> {
+            int fx = windFXAngle(item.getFx());
+            int speed = windNewSpeed(item.getSpeed());
+            if (speed <= 8 && fx < 16)
+                count[speed][fx] = 1;
+        });
+        return count;
+    }
+    /**
+     * 风速风向玫瑰图-风速区域
+     * 风速:[0,3) [3,5) [5,10)  [10,12) [12,25) [25,inf)  6
+     * @param list
+     */
+    public static Object fxNewRoses(List<PowerPointData> list){
+
+        double[][] count = new double[8][16]; // 存储风速值
+        int[][] counts = new int[8][16]; // 记录每个位置的添加次数
+
+        list.stream().forEach(item -> {
+            int fx = windFXAngle(item.getFx()); // 获取风向索引
+            int speed = windNewSpeed(item.getSpeed()); // 获取风速索引
+
+            // 确保 speed 的值在有效范围内
+            if (speed >= 0 && speed < 8 && fx >= 0 && fx < 16) {
+                // 如果当前位置已经有数据,计算新的平均值
+                if (counts[speed][fx] > 0) {
+                    // 累加新的风速值
+                    count[speed][fx] = Math.round((count[speed][fx] * counts[speed][fx] + item.getSpeed()) / (counts[speed][fx] + 1) * 100.0) / 100.0;
+                } else {
+                    // 第一次添加直接赋值并保留两位小数
+                    count[speed][fx] = Math.round(item.getSpeed() * 100.0) / 100.0;
+                }
+                // 更新次数
+                counts[speed][fx]++;
+            }
+        });
+
+
+
+        return count;
+    }
+
+    public static Object speedtimeNewRoses(List<PowerPointData> list){
+        double[] time1 = new double[7]; // 存储风时数(小时)
+
+        // 按时间排序记录
+        list.sort(Comparator.comparing(item -> item.getTime()));
+
+        // 初始化上条时间变量
+        String previousTime = ""; // 用来保存上一条记录的时间
+
+        // 初始化上条时间变量
+        String firstTime = "";  // 第一条数据的时间
+        String lastTime = "";   // 最后一条数据的时间
+        double totalInterval = 0.0; // 计算w文件中时间的间隔总和
+
+        // 遍历数据
+        for (int i = 0; i < list.size(); i++) {
+            PowerPointData item = list.get(i);
+            try {
+                // 获取当前时间
+                String currentTime = item.getTime();
+
+                // 如果是第一条数据,记录第一条时间
+                if (i == 0) {
+                    firstTime = currentTime;
+                }
+
+                // 更新最后一条数据的时间
+                if (i == list.size() - 1) {
+                    lastTime = currentTime;
+                }
+
+                double interval = 0.0;
+                if (!previousTime.isEmpty()) {
+                    // 计算两个时间之间的间隔(单位:分钟)
+                    interval = DateUtils.getTimeDiff(previousTime, currentTime);
+                }
+
+                // 更新上一条记录的时间
+                previousTime = currentTime;
+
+                // 计算总时间
+                totalInterval += interval;
+
+                if (item.getSpeed() < 3) {
+                    time1[0] += interval;
+                }
+                if (item.getSpeed() >= 3.0 && item.getSpeed() < 5.0) {
+                    time1[1] += interval;
+                }
+                if (item.getSpeed() >= 5.0 && item.getSpeed() < 7.0) {
+                    time1[2] += interval;
+                }
+                if (item.getSpeed() >= 7.0 && item.getSpeed() < 9.0) {
+                    time1[3] += interval;
+                }
+                if (item.getSpeed() >= 9.0 && item.getSpeed() < 11.0) {
+                    time1[4] += interval;
+                }
+                if (item.getSpeed() >= 11.0 && item.getSpeed() < 20.0) {
+                    time1[5] += interval;
+                }
+                if (item.getSpeed() >= 20.0 && item.getSpeed() <= 25.0) {
+                    time1[6] += interval;
+                }
+            } catch (Exception e) {
+                e.printStackTrace(); // 捕获并打印异常
+            }
+        }
+
+        // 计算第一条和最后一条数据的间隔
+        double firstLastInterval = 0.0;
+        if (!firstTime.isEmpty() && !lastTime.isEmpty()) {
+            firstLastInterval = DateUtils.getTimeDiff(firstTime, lastTime);
+            firstLastInterval = firstLastInterval +10;
+        }
+
+        // 如果 time1 计算出来的总间隔不等于第一条和最后一条数据的间隔差异,调整 time1[0]
+        if (Math.abs(totalInterval - firstLastInterval) > 0.0) {
+            double difference = firstLastInterval - totalInterval;
+            time1[0] += difference;  // 将差值添加到 time1[0]
+        }
+        int totalHours = 0;
+        double jssj = firstLastInterval / 60;
+        // 将 time1 的每个元素都除以 60,取整
+        for (int i = 0; i < time1.length; i++) {
+            time1[i] = Math.round(time1[i] / 60); // 四舍五入取整
+            totalHours += time1[i];
+        }
+        if (totalHours != jssj){
+            int diff = (int) (jssj - totalHours); // 计算差值
+            // 调整第一个元素
+            time1[0] += diff; // 根据差值调整第一个元素
+            totalHours = (int) jssj; // 确保 totalHours 变为 jssj
+        }
+        return time1;
+    }
+    /**
+     * 风向频次玫瑰图
+     * 风速:[0,2.5) [2.5,5) [5,7.5) [7.5,10) [10,12.5) [12.5,15) [15,17.5) [17.5,20) [20,22.5) [22.5,25) [25,inf)  11
+     * @param list
+     */
+    public static int[][] fxCountRoses(List<PowerPointData> list){
+        int[][] count = new int[11][16];
+        AtomicBoolean speedTag = new AtomicBoolean(false);
+        AtomicInteger orispeed = new AtomicInteger();
+        list.stream().sorted(Comparator.comparing(PowerPointData::getTime)).forEach(item -> {
+            int fx = windFXAngle(item.getFx());
+            int speed = windSpeed(item.getSpeed());
+
+            if (!speedTag.get()) {
+
+                speedTag.set(orispeed.equals(speed));
+                orispeed.set(speed);
+                if (speed < 11 && fx < 16)
+                    count[speed][fx] ++;
+            }
+
+        });
+
+        return count;
+    }
+
+
+
+    /**
+     * 风向频次玫瑰图
+     * 风速:[0,3) [3,5) [5,10)  [10,12) [12,25) [25,inf)  6
+     * @param list
+     */
+    public static int[][] fxNewCountRoses(List<PowerPointData> list){
+        int[][] count = new int[8][16];
+        AtomicBoolean speedTag = new AtomicBoolean(false);
+        AtomicInteger orispeed = new AtomicInteger();
+        list.stream().sorted(Comparator.comparing(PowerPointData::getTime)).forEach(item -> {
+            int fx = windFXAngle(item.getFx());
+            int speed = windNewSpeed(item.getSpeed());
+
+            if (!speedTag.get()) {
+
+                speedTag.set(orispeed.equals(speed));
+                orispeed.set(speed);
+                if (speed <= 8 && fx < 16)
+                    count[speed][fx] ++;
+            }
+
+        });
+
+        return count;
+    }
+
+
+    /**
+     * 风向频次雷达图
+     * @param list
+     * @return
+     */
+    public static int[] fxRadarRoses(List<PowerPointData> list){
+
+        try {
+            int[] count = new int[16];
+            list.stream().sorted(Comparator.comparing(PowerPointData::getSpeed)).forEach(item -> {
+                int df = windDFAngle(item.getDfwc());
+                if (count.length > df && count[df] < 1000) {
+                    count[df]++;
+                }
+            });
+            return count;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+
+    }
+
+
+    /**
+     * 静风频率计算 、平均风速计算
+     * @param list  风速数组
+     * @param cutInSpeed 切入风速
+     * @return
+     */
+    public static List<Double> frequency(List<Double> list, double cutInSpeed){
+        List<Double> result = new ArrayList<>();
+        DecimalFormat df = new DecimalFormat("0.00");
+        int count = 0;
+        double speed = 0.0;
+        for (Double fs : list){
+            speed += fs;
+            if (fs < cutInSpeed)
+                count ++;
+        }
+        double frequency = list.size() > 0 ? Double.valueOf(df.format(((double)count / list.size() * 100 ))) : 0;
+        double speedAVG = list.size() > 0 ? Double.valueOf(df.format(speed/list.size())) : 0;
+        result.add(frequency);
+        result.add(speedAVG);
+        return result;
+    }
+
+
+
+    /**
+     * 对风偏差散点过滤  统计-50 到 50的
+     * 频次
+     * @param list
+     *
+     *
+     * @return
+     */
+    public static List<Point> windDeviationScatter(List<PowerPointData> list){
+        //正负偏差 [-50,-49,....,0,1,2,.....50]
+        List<Point> ls = new ArrayList<>();
+        LinkedHashSet<Double> keys = new LinkedHashSet<>();  //散点太多去重
+        //次数统计
+        for (int i = 0; i < list.size(); i++){
+            PowerPointData item = list.get(i);
+            int ele = (int)Math.round((item.getFx() + Math.abs(item.getDfwc())));
+            int index = ele - 180;
+            if (index >= -50 && index <= 50) {
+                double key = Math.abs(index) + item.getSpeed();
+                if (!keys.contains(key))
+                    ls.add(new Point(index, item.getSpeed()));
+                keys.add(key);
+            }
+        }
+
+        return ls;
+    }
+
+
+    /**
+     * 静态偏航对风分析
+     * 对风偏差散点过滤  统计-15 到 15的  正负偏差 [-15,-14,....,0,1,2,.....15]
+     * 数据为二维数组 内层数组为3项. 第一项表示Y轴索引(风速),  第二项代表X轴的索引(正负偏差) 第三项代表value值.
+     * @param points
+     * @return
+     */
+    public static Map<String, Object> windDeviationPoint(List<PowerPointData> points){
+        try {
+            List<PowerPointData> list = new ArrayList<>();
+            List<Object> temp = new ArrayList<>();
+            Map<String, Object> result = new HashMap<>();
+            Map<Integer, double[]> map = new LinkedHashMap<>();
+
+            //过滤对风偏差
+            for (int i = 0; i < points.size(); i++){
+                PowerPointData item = points.get(i);
+                if ((item.getSpeed() < 4.5 || item.getSpeed() >= 10.5) && item.getMxzt() != 2)
+                    continue;
+
+                list.add(item);
+                Integer speed = Math.toIntExact(Math.round(item.getSpeed()));
+                if (!map.containsKey(speed))
+                    map.put(speed, new double[2]);  //[0]最小值 [1]最大值
+
+                double[] value = map.get(speed);
+                double power = item.getPower() < 0 ? 0 : item.getPower();
+                value[0] = power < value[0] ? power : value[0]; //最小值
+                value[1] = power > value[1] ? power : value[1]; //最大值
+            }
+
+            int avg = 0;
+            DecimalFormat sf = new DecimalFormat("0.00");
+            for (int i = 0; i < list.size(); i++){
+                PowerPointData item = list.get(i);
+                Integer speed = Math.toIntExact(Math.round(item.getSpeed()));
+                int ele = (int) (Math.abs(item.getFx()) + Math.abs(item.getAngle()));
+                int index = ele - 180;
+                if (index >= -30 && index <= 30 && item.getPower() > 0) {
+                    avg += index;
+                    double[] value = map.get(speed);
+                    double v = (item.getPower() - value[0]) / (value[1] - value[0]);
+                    double s[] = {speed, index, Double.valueOf(sf.format(v)).doubleValue()};
+                    temp.add(s);
+                }
+            }
+            result.put("data", temp);
+            result.put("avg", temp.size() > 0 ? avg/temp.size() : 0);
+
+            return result;
+        } catch (NumberFormatException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    /**
+     * 对风偏差频次统计  统计-50 到 50
+     * @param list
+     * @return
+     */
+    public static int[] windDeviationRatio(List<PowerPointData> list){
+        int[] pc = new int[101];  //正负偏差 [-50,-49,....,0,1,2,.....50]
+        //次数统计
+        for (int i = 0; i < list.size(); i++){
+            PowerPointData item = list.get(i);
+            int ele = (int) (Math.abs(item.getFx()) + Math.abs(item.getDfwc()));
+            int index = ele - 180;
+            if (index >= -50 && index <= 50)
+                pc[50-(index > 0 ? -index : Math.abs(index))]++;
+        }
+
+        return pc;
+    }
+
+
+    /**
+     * 风向划分
+     * 0:0-22.5
+     * 1:22.5-45
+     * 2:90-135
+     * .。。。。。。
+     * @param fx
+     * @return params: 0,1,2。。。。。15
+     */
+    private static int windFXAngle(double fx){
+        int split = 16;  //风向分为16个角度
+        double angle = (double)360 / split;
+        int index = Math.abs((int) (fx / angle));
+        return index;
+    }
+
+    /**
+     * 对风角度计算
+     * 0:0-22.5
+     * 1:22.5-45
+     * 2:90-135
+     * .。。。。。。
+     * @param
+     * @return
+     */
+    private static int windDFAngle(double angle){
+        int split = 16;  //风向分为8个角度
+        double interval = (double)360 / split;
+        angle = angle > 360 ? 720 - 360 : angle;
+        int index = (int) (Math.abs(angle) / interval);
+        return index;
+    }
+
+    /**
+     * 风速区域划分
+     * @param speed
+     * @return
+     */
+    private static int windSpeed(double speed){
+        if (speed < 2.5)
+            return 0;
+        else if (speed < 5)
+            return 1;
+        else if (speed < 7.5)
+            return 2;
+        else if (speed < 10)
+            return 3;
+        else if (speed < 12.5)
+            return 4;
+        else if (speed < 15)
+            return 5;
+        else if (speed < 17.5)
+            return 6;
+        else if (speed < 20)
+            return 7;
+        else if (speed < 22.5)
+            return 8;
+        else if (speed < 25)
+            return 9;
+        else
+            return 10;
+    }
+
+
+    /**
+     * 风速区域划分
+     * @param speed
+     * @return
+     */
+    private static int windNewSpeed(double speed){
+        if (speed < 3)
+            return 0;
+        else if (3<= speed  && speed < 5)
+            return 1;
+        else if (5<= speed && speed < 7)
+            return 2;
+        else if (7<= speed && speed < 9)
+            return 3;
+        else if (9<= speed && speed < 11)
+            return 4;
+        else if (11<= speed && speed < 20)
+            return 5;
+        else if (20<= speed && speed < 25)
+            return 6;
+        else
+            return 7;
+    }
+
+    /**
+     * 数据聚合  间隔10
+     * @param list
+     * @return
+     */
+    private static Object arrayDistinct(List<List<Double>> list){
+        for (List<Double> item : list){
+            List<Double> lt = new ArrayList<>();
+            LinkedHashSet<Integer> ls = new LinkedHashSet<>();
+            item.forEach(ele -> {
+                if (ele == null)
+                    return;
+                int v = (int) (ele / 10);
+                if (!ls.contains(v))
+                    lt.add(ele);
+
+                ls.add(v);
+            });
+            item.clear();
+            item.addAll(lt);
+        }
+
+        return list;
+    }
+}

+ 161 - 0
runeconomy-xk/src/main/java/com/gyee/runeconomy/service/WindDirection/WindDirectionService.java

@@ -0,0 +1,161 @@
+package com.gyee.runeconomy.service.WindDirection;
+
+import com.gyee.common.contant.ContantXk;
+import com.gyee.common.model.PointData;
+import com.gyee.runeconomy.dto.result.PowerPointData;
+import com.gyee.runeconomy.init.CacheContext;
+import com.gyee.runeconomy.model.auto.ProBasicEquipment;
+import com.gyee.runeconomy.model.auto.ProBasicEquipmentPoint;
+import com.gyee.runeconomy.service.auto.IProBasicEquipmentPointService;
+import com.gyee.runeconomy.util.realtimesource.IEdosUtil;
+import com.gyee.runeconomy.util.realtimesource.feign.RemoteServiceBuilder;
+import com.gyee.runeconomy.util.realtimesource.feign.TsDoubleData;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Service
+public class WindDirectionService {
+
+    @Resource
+    private IEdosUtil edosUtil;
+    @Resource
+    private IProBasicEquipmentPointService proBasicEquipmentPointService;
+    @Resource
+    private RemoteServiceBuilder remoteService;
+    /**
+     * 风向玫瑰图\风向频率玫瑰图
+     *
+     * @return
+     */
+    public Object fxRoses(String wtid, String kssj, String jssj) throws Exception {
+
+        Date[] sjcl = sjcl(kssj, jssj);
+
+        // 获取 startDate 和 endDate
+        Date startDate = sjcl[0];
+        Date endDate = sjcl[1];
+
+        List<ProBasicEquipment> list = CacheContext.wtls.stream().filter(wt -> wtid.equals(wt.getId())).collect(Collectors.toList());
+        if (list == null || list.size() == 0)
+            return null;
+
+        ProBasicEquipmentPoint pjfs = null;
+        ProBasicEquipmentPoint pjfx = null;
+        ProBasicEquipmentPoint sbzt = null;
+        ProBasicEquipmentPoint dfwc = null;
+        ProBasicEquipmentPoint phjd = null;
+        ProBasicEquipmentPoint gl = null;
+
+        pjfs = proBasicEquipmentPointService.getEquipmentPoint(wtid, ContantXk.CJ_SSFS);
+
+        pjfx = proBasicEquipmentPointService.getEquipmentPoint(wtid, ContantXk.CJ_FX);
+
+        sbzt = proBasicEquipmentPointService.getEquipmentPoint(wtid, ContantXk.MXZT);
+        List<TsDoubleData> pjfsls = remoteService.adapterfd().getHistorySnap(pjfs.getNemCode(), startDate.getTime(), endDate.getTime(), 900);
+        List<TsDoubleData> pjfxls = remoteService.adapterfd().getHistorySnap(pjfx.getNemCode(), startDate.getTime(), endDate.getTime(), 900);
+        List<TsDoubleData> sbztls = remoteService.adapterfd().getHistorySnap(sbzt.getNemCode(), startDate.getTime(), endDate.getTime(), 900);
+        PointData sbztlSs = edosUtil.getRealData(sbzt);
+
+        List<PowerPointData> lslist = new ArrayList<>();
+        for (int i = 0; i < pjfxls.size(); i++) {
+            TsDoubleData fxData = pjfxls.get(i);
+            TsDoubleData fsData = null;
+            TsDoubleData ztData = null;
+
+            for (TsDoubleData fs : pjfsls) {
+                if (fxData.getTs() == fs.getTs()) {
+                    fsData = fs;
+                    break; // 找到匹配的时间戳后退出循环
+                }
+            }
+
+            for (TsDoubleData zt : sbztls) {
+                if (fxData.getTs() == zt.getTs()) {
+                    ztData = zt;
+                    break; // 找到匹配的时间戳后退出循环
+                }
+            }
+
+            // 将时间戳转换为 LocalDateTime 对象
+            LocalDateTime dateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(fxData.getTs()), ZoneId.systemDefault());
+            // 格式化输出日期
+            DateTimeFormatter formatter11 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+            String formattedDate = dateTime.format(formatter11);
+
+            // 创建 PowerPointData 对象
+            PowerPointData data1 = new PowerPointData();
+            data1.setFx(fxData.getDoubleValue());
+//            data1.setMxzt(ztData.getPointValueInDouble());
+            data1.setTime(formattedDate);
+
+            // 如果没有找到对应的 fsData,默认设置 setFs 为 0
+            if (fsData != null) {
+                data1.setSpeed(fsData.getDoubleValue());
+            } else {
+                data1.setSpeed(0);  // 默认值
+            }
+
+            // 将 data1 添加到 lslist
+            lslist.add(data1);
+        }
+
+
+        List<Object> result = new ArrayList<>();
+
+        for (ProBasicEquipment obj : list) {
+            Map<String, Object> map = new HashMap<>();
+            map.put("wt", obj.getAname());
+            map.put("ys", WindALG.fxNewRoses(lslist));//风速、风向
+            map.put("speeedtime", WindALG.speedtimeNewRoses(lslist));//风速
+            map.put("roses", WindALG.fxysNewRoses(lslist));//风速、风向
+            map.put("count", WindALG.fxNewCountRoses(lslist));//风速、风向
+            int[] count = WindALG.windDeviationRatio(lslist);//风向、对风误差
+            List<Point> scatter = WindALG.windDeviationScatter(lslist);//风速、风向、对风误差
+            map.put("countpcl", count);
+            map.put("scatter", scatter);
+            map.put("radar", WindALG.fxRadarRoses(lslist));//对风误差
+            map.put("frequency", WindALG.windDeviationPoint(lslist)); //风速、风向、明细状态、功率、偏航角度
+            result.add(map);
+        }
+
+        return result;
+    }
+
+    private Date[] sjcl (String kssj,String jssj) throws ParseException {
+
+        // 时间格式,格式是 "yyyy-MM-dd HH:mm"
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+
+        // 将字符串转换为 Date 对象
+        Date kssjDate = sdf.parse(kssj);
+        Date jssjDate = sdf.parse(jssj);
+
+        // 创建 Calendar 对象
+        Calendar kssjCalendar = Calendar.getInstance();
+        Calendar jssjCalendar = Calendar.getInstance();
+
+        // 将 Date 设置到 Calendar 对象中
+        kssjCalendar.setTime(kssjDate);
+        jssjCalendar.setTime(jssjDate);
+
+        // 设置 endDate 为当天的 23:59:59
+        jssjCalendar.set(Calendar.HOUR_OF_DAY, 23);
+        jssjCalendar.set(Calendar.MINUTE, 59);
+        jssjCalendar.set(Calendar.SECOND, 59);
+        jssjCalendar.set(Calendar.MILLISECOND, 999);
+
+        // 获取 startDate 和 endDate
+        Date startDate = kssjCalendar.getTime();
+        Date endDate = jssjCalendar.getTime();
+        return new Date[] {startDate, endDate};
+    }
+}

+ 4 - 0
runeconomy-xk/src/main/java/com/gyee/runeconomy/service/auto/IProBasicEquipmentPointService.java

@@ -3,6 +3,8 @@ package com.gyee.runeconomy.service.auto;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.gyee.runeconomy.model.auto.ProBasicEquipmentPoint;
 
+import java.util.List;
+
 /**
  * <p>
  * 设备测点 服务类
@@ -15,4 +17,6 @@ public interface IProBasicEquipmentPointService extends IService<ProBasicEquipme
 
     public ProBasicEquipmentPoint getEquipmentPoint(String wpId, String uniformCode);
 
+    List<ProBasicEquipmentPoint> selectList(String station, List<String> points);
+
 }

+ 1 - 0
runeconomy-xk/src/main/java/com/gyee/runeconomy/service/auto/IProBasicPowerstationService.java

@@ -25,5 +25,6 @@ public interface IProBasicPowerstationService extends IService<ProBasicPowerstat
     List<ProBasicPowerstation> getProBasicPowerstationlist(String regionId, String companyId, String staType);
 
     List<ProBasicPowerstation> selectList1();
+    List<ProBasicPowerstation> selectwpid(String wpid);
 
 }

+ 3 - 1
runeconomy-xk/src/main/java/com/gyee/runeconomy/service/auto/ProEconAlarmRuleService.java

@@ -3,6 +3,8 @@ package com.gyee.runeconomy.service.auto;
 import com.gyee.runeconomy.model.ProEconAlarmRule;
 import com.baomidou.mybatisplus.extension.service.IService;
 
+import java.util.List;
+
 /**
  * <p>
  * 自定义报警配置 服务类
@@ -12,5 +14,5 @@ import com.baomidou.mybatisplus.extension.service.IService;
  * @since 2024-11-27
  */
 public interface ProEconAlarmRuleService extends IService<ProEconAlarmRule> {
-
+    List<ProEconAlarmRule> getProEconAlarmRuleList(String name, String modelId);
 }

+ 19 - 0
runeconomy-xk/src/main/java/com/gyee/runeconomy/service/auto/impl/ProBasicEquipmentPointServiceImpl.java

@@ -1,5 +1,6 @@
 package com.gyee.runeconomy.service.auto.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.gyee.runeconomy.init.CacheContext;
 import com.gyee.runeconomy.mapper.auto.ProBasicEquipmentPointMapper;
@@ -8,6 +9,8 @@ import com.gyee.runeconomy.service.auto.IProBasicEquipmentPointService;
 import com.gyee.runeconomy.util.StringUtils;
 import org.springframework.stereotype.Service;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -37,4 +40,20 @@ public class ProBasicEquipmentPointServiceImpl extends ServiceImpl<ProBasicEquip
         }
         return point;
     }
+
+
+    @Override
+    public List<ProBasicEquipmentPoint> selectList(String station, List<String> points) {
+        List<ProBasicEquipmentPoint> list = new ArrayList();
+        QueryWrapper<ProBasicEquipmentPoint> wrapper = new QueryWrapper<>();
+        wrapper.lambda().eq(ProBasicEquipmentPoint::getWindpowerstationId, station).in(ProBasicEquipmentPoint::getUniformCode,points);
+
+        try{
+            list = baseMapper.selectList(wrapper);
+        }catch (Exception e){
+            log.error("ProBasicEquipmentPoint--selectList", e);
+        }
+
+        return list;
+    }
 }

+ 8 - 0
runeconomy-xk/src/main/java/com/gyee/runeconomy/service/auto/impl/ProBasicPowerstationServiceImpl.java

@@ -54,4 +54,12 @@ public class ProBasicPowerstationServiceImpl extends ServiceImpl<ProBasicPowerst
         List<ProBasicPowerstation> windplants = baseMapper.selectList(qw);
         return windplants;
     }
+
+    @Override
+    public List<ProBasicPowerstation> selectwpid(String wpid) {
+        QueryWrapper<ProBasicPowerstation> wrapper = new QueryWrapper();
+        wrapper.lambda().eq(ProBasicPowerstation::getId, wpid);
+        List<ProBasicPowerstation> list = baseMapper.selectList(wrapper);
+        return list;
+    }
 }

+ 11 - 1
runeconomy-xk/src/main/java/com/gyee/runeconomy/service/auto/impl/ProEconAlarmRuleServiceImpl.java

@@ -1,11 +1,14 @@
 package com.gyee.runeconomy.service.auto.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.gyee.runeconomy.model.ProEconAlarmRule;
 import com.gyee.runeconomy.mapper.ProEconAlarmRuleMapper;
 import com.gyee.runeconomy.service.auto.ProEconAlarmRuleService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import org.springframework.stereotype.Service;
 
+import java.util.List;
+
 /**
  * <p>
  * 自定义报警配置 服务实现类
@@ -16,5 +19,12 @@ import org.springframework.stereotype.Service;
  */
 @Service
 public class ProEconAlarmRuleServiceImpl extends ServiceImpl<ProEconAlarmRuleMapper, ProEconAlarmRule> implements ProEconAlarmRuleService {
-
+    @Override
+    public List<ProEconAlarmRule> getProEconAlarmRuleList(String name, String modelId) {
+        QueryWrapper<ProEconAlarmRule> qw = new QueryWrapper<>();
+        qw.lambda().eq(ProEconAlarmRule::getName, name);
+        qw.lambda().eq(ProEconAlarmRule::getModelId, modelId);
+        List<ProEconAlarmRule> proEconAlarmRules = baseMapper.selectList(qw);
+        return proEconAlarmRules;
+    }
 }

+ 19 - 0
runeconomy-xk/src/main/java/com/gyee/runeconomy/util/DateUtils.java

@@ -609,6 +609,25 @@ public class DateUtils {
         return sb.toString();
     }
 
+
+    // 获取两个时间相差分钟数
+    public static int getTimeDiff(String oldTime, String newTime) {
+        int diff = 0;
+        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        long NTime = 0;
+        long OTime = 0;
+        try {
+            NTime = df.parse(newTime).getTime();
+            //从对象中拿到时间
+            OTime = df.parse(oldTime).getTime();
+            diff = (int) (Math.abs((NTime-OTime))/1000/60);
+        } catch (ParseException e) {
+        }
+
+        return diff;
+    }
+
+
     public static String convertEdnaTime(String pointTime) {
         return convertEdnaTime2(pointTime, false);
     }

+ 35 - 0
runeconomy-xk/src/main/java/com/gyee/runeconomy/util/realtimesource/feign/FeignsBuilder.java

@@ -0,0 +1,35 @@
+package com.gyee.runeconomy.util.realtimesource.feign;
+
+
+import com.gyee.runeconomy.config.GyeeConfig;
+import feign.Feign;
+import feign.Request;
+import feign.Retryer;
+import feign.jackson.JacksonDecoder;
+import feign.jackson.JacksonEncoder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.annotation.Resource;
+
+
+/**
+ * @author xysn
+ */
+
+@Configuration
+public class FeignsBuilder {
+
+    @Resource
+    private GyeeConfig config;
+
+    @Bean
+    public IDataAdapter dataAdapter() {
+        return Feign.builder()
+                .encoder(new JacksonEncoder())
+                .decoder(new JacksonDecoder())
+                .options(new Request.Options(1000, 100000))
+                .retryer(new Retryer.Default(5000, 5000, 3))
+                .target(IDataAdapter.class, config.getAdapterfdUrl());
+    }
+}

+ 58 - 0
runeconomy-xk/src/main/java/com/gyee/runeconomy/util/realtimesource/feign/IAdapterService.java

@@ -0,0 +1,58 @@
+package com.gyee.runeconomy.util.realtimesource.feign;
+
+
+import com.gyee.common.model.PointData;
+import feign.Headers;
+import feign.Param;
+import feign.RequestLine;
+
+import java.util.List;
+import java.util.Map;
+
+
+public interface IAdapterService {
+
+
+    @Headers({"Content-Type: application/json", "Accept: application/json"})
+    @RequestLine("GET /latest?keys={points}")
+    Map<String,TsDoubleData> getLatest(@Param(value = "points") String points);
+
+    @Headers({"Content-Type: application/json", "Accept: application/json"})
+    @RequestLine("GET /history/snap?tagName={tagName}&startTs={startTs}&endTs={endTs}&interval={interval}")
+    List<TsDoubleData> getHistorySnap(@Param(value = "tagName") String tagName, @Param(value = "startTs") long startTs,
+                                      @Param(value = "endTs") long endTs, @Param(value = "interval") Integer interval);
+    
+    @Headers({"Content-Type: application/json", "Accept: application/json"})
+    @RequestLine("GET /history/snap?tagName={tagName}&startTs={startTs}&endTs={endTs}&interval={interval}")
+    List<TsDoubleData> getValuesByKey(@Param(value = "tagName") String tagName, @Param(value = "startTs") long startTs,
+                                      @Param(value = "endTs") long endTs, @Param(value = "interval") int interval);
+    
+    @Headers({"Content-Type: application/json", "Accept: application/json"})
+    @RequestLine("GET /history/raw?tagName={tagName}&startTs={startTs}&endTs={endTs}")
+    List<TsDoubleData> getRawValuesByKey(@Param(value = "tagName") String tagName, @Param(value = "startTs") long startTs,
+                                         @Param(value = "endTs") long endTs);
+
+    @Headers({"Content-Type: application/json", "Accept: application/json"})
+    @RequestLine("GET /history/section?tagNames={tagNames}&ts={ts}")
+    Map<String, TsDoubleData> getHistorySection(@Param(value = "tagNames") String tagNames, @Param(value = "ts") long ts);
+
+
+    /**
+     * 获取等间隔历史数据
+     *
+     * @param tagName  测点名称
+     * @param startTs  开始时间
+     * @param endTs    结束时间
+     * @param interval 数据间隔
+     * @return 历史数据
+     */
+    @RequestLine("GET /history/snap?tagName={tagName}&startTs={startTs}&endTs={endTs}&interval={interval}")
+    List<PointData> getSnapValuesByKey(@Param(value = "tagName") String tagName, @Param(value = "startTs") long startTs,
+                                       @Param(value = "endTs") long endTs, @Param(value = "interval") int interval);
+
+
+    @Headers({"Content-Type: application/json", "Accept: application/json"})
+    @RequestLine("GET /history/snap?tagName={tagName}&startTs={startTs}&endTs={endTs}&interval={interval}")
+    List<TsDoubleTsData> getHistorytsSnap(@Param(value = "tagName") String tagName, @Param(value = "startTs") long startTs,
+                                        @Param(value = "endTs") long endTs, @Param(value = "interval") Integer interval);
+}

+ 33 - 0
runeconomy-xk/src/main/java/com/gyee/runeconomy/util/realtimesource/feign/IDataAdapter.java

@@ -0,0 +1,33 @@
+package com.gyee.runeconomy.util.realtimesource.feign;
+
+
+import com.gyee.common.model.PointData;
+import feign.Headers;
+import feign.Param;
+import feign.RequestLine;
+
+import java.util.List;
+
+/**
+ * DataAdapter数据请求
+ */
+public interface IDataAdapter {
+    /**
+     * 获取等间隔历史数据
+     *
+     * @param tagName  测点名称
+     * @param startTs  开始时间
+     * @param endTs    结束时间
+     * @param interval 数据间隔
+     * @return 历史数据
+     */
+    @RequestLine("GET /history/snap?tagName={tagName}&startTs={startTs}&endTs={endTs}&interval={interval}")
+    List<PointData> getSnapValuesByKey(@Param(value = "tagName") String tagName, @Param(value = "startTs") long startTs,
+                                       @Param(value = "endTs") long endTs, @Param(value = "interval") int interval);
+
+    @Headers({"Content-Type: application/json", "Accept: application/json"})
+    @RequestLine("GET /history/snap?tagName={tagName}&startTs={startTs}&endTs={endTs}&interval={interval}")
+    List<TsDoubleTsData> getHistorySnap(@Param(value = "tagName") String tagName, @Param(value = "startTs") long startTs,
+                                        @Param(value = "endTs") long endTs, @Param(value = "interval") Integer interval);
+}
+

+ 52 - 0
runeconomy-xk/src/main/java/com/gyee/runeconomy/util/realtimesource/feign/RemoteServiceBuilder.java

@@ -0,0 +1,52 @@
+package com.gyee.runeconomy.util.realtimesource.feign;
+
+
+import com.gyee.runeconomy.config.GyeeConfig;
+import feign.Feign;
+import feign.Request;
+import feign.Retryer;
+import feign.jackson.JacksonDecoder;
+import feign.jackson.JacksonEncoder;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Bean;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+
+
+@Slf4j
+@Component
+public class RemoteServiceBuilder {
+
+    @Resource
+    private GyeeConfig config;
+
+    @Bean
+    public IAdapterService adapterfd() {
+        return Feign.builder()
+                .encoder(new JacksonEncoder())
+                .decoder(new JacksonDecoder())
+                .options(new Request.Options(5000, 600000))
+                .retryer(new Retryer.Default(10000, 10000, 3))
+                .target(IAdapterService.class, config.getAdapterfdUrl());
+    }
+
+    public IAdapterService adaptergf() {
+        return Feign.builder()
+                .encoder(new JacksonEncoder())
+                .decoder(new JacksonDecoder())
+                .options(new Request.Options(5000, 600000))
+                .retryer(new Retryer.Default(10000, 10000, 3))
+                .target(IAdapterService.class, config.getAdaptergfUrl());
+    }
+
+
+    public IAdapterService taos() {
+        return Feign.builder()
+                .encoder(new JacksonEncoder())
+                .decoder(new JacksonDecoder())
+                .options(new Request.Options(5000, 600000))
+                .retryer(new Retryer.Default(10000, 10000, 3))
+                .target(IAdapterService.class, config.getTaosUrl());
+    }
+}

+ 52 - 0
runeconomy-xk/src/main/java/com/gyee/runeconomy/util/realtimesource/feign/TsDoubleData.java

@@ -0,0 +1,52 @@
+package com.gyee.runeconomy.util.realtimesource.feign;
+
+// 数据适配器取数对应的类
+public class TsDoubleData {
+
+    private long ts;
+    private int status;
+    private double doubleValue;
+    private long longValue;
+
+    public long getLongValue() {
+        return longValue;
+    }
+
+    public void setLongValue(long longValue) {
+        this.longValue = longValue;
+    }
+
+    public TsDoubleData(long ts, double doubleValue) {
+        this.ts = ts;
+        this.doubleValue = doubleValue;
+    }
+
+    public TsDoubleData() {
+    }
+
+    public long getTs() {
+        return ts;
+    }
+
+    public void setTs(long ts) {
+        this.ts = ts;
+    }
+
+    public int getStatus() {
+        return status;
+    }
+
+    public void setStatus(int status) {
+        this.status = status;
+    }
+
+    public double getDoubleValue() {
+//        BigDecimal bg = new BigDecimal(doubleValue);
+//        double v = bg.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
+        return doubleValue;
+    }
+
+    public void setDoubleValue(double doubleValue) {
+        this.doubleValue = doubleValue;
+    }
+}

+ 35 - 0
runeconomy-xk/src/main/java/com/gyee/runeconomy/util/realtimesource/feign/TsDoubleTsData.java

@@ -0,0 +1,35 @@
+package com.gyee.runeconomy.util.realtimesource.feign;
+
+/**
+ * 测点数据
+ */
+public class TsDoubleTsData {
+    /**
+     * 时间戳
+     */
+    private long ts;
+    /**
+     * 数据
+     */
+    private double doubleValue;
+
+    public long getTs() {
+        return ts;
+    }
+
+    public void setTs(long ts) {
+        this.ts = ts;
+    }
+
+    public double getValue() {
+        return doubleValue;
+    }
+
+    public double getDoubleValue() {
+        return doubleValue;
+    }
+
+    public void setDoubleValue(double doubleValue) {
+        this.doubleValue = doubleValue;
+    }
+}