Browse Source

1分钟平均指标计算

xushili 2 years ago
parent
commit
939c68d406
26 changed files with 1443 additions and 0 deletions
  1. 11 0
      electricity/wind/README.md
  2. 50 0
      electricity/wind/build.gradle
  3. 21 0
      electricity/wind/src/main/java/com/gyee/gaia/WindPowergenApp.java
  4. 42 0
      electricity/wind/src/main/java/com/gyee/gaia/electricity/wind/adapter/IAdapterApi.java
  5. 21 0
      electricity/wind/src/main/java/com/gyee/gaia/electricity/wind/adapter/IShardingApi.java
  6. 27 0
      electricity/wind/src/main/java/com/gyee/gaia/electricity/wind/config/AppConfig.java
  7. 75 0
      electricity/wind/src/main/java/com/gyee/gaia/electricity/wind/config/ThreadPoolConfig.java
  8. 78 0
      electricity/wind/src/main/java/com/gyee/gaia/electricity/wind/config/XxlJobConfig.java
  9. 6 0
      electricity/wind/src/main/resources/banner.txt
  10. 105 0
      electricity/wind/src/main/resources/bootstrap.yaml
  11. 49 0
      electricity/wind/src/main/resources/log4j2.xml
  12. 40 0
      electricity/wind/src/test/java/com/gyee/gaia/MyAppTests.java
  13. 119 0
      electricity/wind/src/test/resources/bootstrap.yaml
  14. 11 0
      realtime/wind/README.md
  15. 53 0
      realtime/wind/build.gradle
  16. 21 0
      realtime/wind/src/main/java/com/gyee/gaia/WindRealtimeApp.java
  17. 27 0
      realtime/wind/src/main/java/com/gyee/gaia/realtime/wind/adapter/IAdapterApi.java
  18. 21 0
      realtime/wind/src/main/java/com/gyee/gaia/realtime/wind/adapter/IShardingApi.java
  19. 27 0
      realtime/wind/src/main/java/com/gyee/gaia/realtime/wind/config/AppConfig.java
  20. 75 0
      realtime/wind/src/main/java/com/gyee/gaia/realtime/wind/config/ThreadPoolConfig.java
  21. 78 0
      realtime/wind/src/main/java/com/gyee/gaia/realtime/wind/config/XxlJobConfig.java
  22. 56 0
      realtime/wind/src/main/java/com/gyee/gaia/realtime/wind/init/CacheContext.java
  23. 220 0
      realtime/wind/src/main/java/com/gyee/gaia/realtime/wind/job/CauseJobHandler.java
  24. 116 0
      realtime/wind/src/main/resources/bootstrap.yaml
  25. 49 0
      realtime/wind/src/main/resources/log4j2.xml
  26. 45 0
      realtime/wind/src/test/java/com/gyee/gaia/MyAppTests.java

+ 11 - 0
electricity/wind/README.md

@@ -0,0 +1,11 @@
+### cause 分析原因,故障损失,计划检修损失,受累损失,限电损失,性能损失等
+
+状态为: 并网--是否限电--风速小于11并且桨叶角度大于2;风速大于11并且有功功率小于满发功率减100 待机-- 是否限停-- 有故障--故障 无故障--有功设定小于出线功率;有功设定小于应发的85% 并且
+出线功率大于有功设定94%;有功设定小于应发的70% 并且 出线功率大于有功设定80% 并且 当前场站限电台数大于等于3--限停 待机 维护-- 有故障--故障 无故障--计划检修 故障--小于?(3)分钟故障忽略
+是否受累--全站故障或者离线 故障 离线-- 是否受累--全站故障或者离线 故障
+
+青山华创风机在停机的状态下应该算故障损失
+
+
+
+

+ 50 - 0
electricity/wind/build.gradle

@@ -0,0 +1,50 @@
+buildscript {
+    repositories {
+        mavenLocal()
+        maven {
+            allowInsecureProtocol = true
+            url "http://maven.aliyun.com/nexus/content/groups/public"
+        }
+        mavenCentral()
+    }
+    dependencies {
+        classpath("$bootGroup:spring-boot-gradle-plugin:$springBootVersion")
+    }
+}
+
+apply plugin: "$bootGroup"
+apply plugin: 'io.spring.dependency-management'
+
+dependencyManagement {
+    imports {
+        mavenBom "org.springframework.cloud:spring-cloud-dependencies:$springCloudVersion"
+        mavenBom "org.springframework.boot:spring-boot-dependencies:$springBootVersion"
+        mavenBom "com.alibaba.cloud:spring-cloud-alibaba-dependencies:$springCloudAlibabaVersion"
+    }
+}
+
+dependencies {
+    implementation project(":timeseries:dao-sql")
+
+    implementation("$bootGroup:spring-boot-starter-web")
+    implementation("$bootGroup:spring-boot-starter-undertow")
+
+
+    implementation("org.postgresql:postgresql:$postgresqlDriverVersion")
+    implementation "com.baomidou:mybatis-plus-boot-starter:$mybatisPlusVersion"
+    implementation("com.alibaba:druid:$alibabaDruidVersion")
+    implementation("com.alibaba:fastjson:$fastjsonVersion")
+
+    implementation("$bootGroup:spring-boot-starter-test")
+    implementation("com.xuxueli:xxl-job-core:$xxlJobVersion")
+    implementation("org.springframework.cloud:spring-cloud-starter-bootstrap")
+    implementation("com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery")
+    implementation("com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-config")
+
+    implementation("$cloudGroup:spring-cloud-starter-openfeign")
+    implementation("cn.hutool:hutool-all:5.8.18")
+}
+
+test {
+    useJUnitPlatform()
+}

+ 21 - 0
electricity/wind/src/main/java/com/gyee/gaia/WindPowergenApp.java

@@ -0,0 +1,21 @@
+package com.gyee.gaia;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+
+
+@SpringBootApplication
+@EnableDiscoveryClient
+@EnableFeignClients
+public class WindPowergenApp {
+
+    public static void main(String[] args) {
+
+        SpringApplication.run(WindPowergenApp.class, args);
+
+    }
+}
+
+

+ 42 - 0
electricity/wind/src/main/java/com/gyee/gaia/electricity/wind/adapter/IAdapterApi.java

@@ -0,0 +1,42 @@
+package com.gyee.gaia.electricity.wind.adapter;
+
+import com.gyee.gaia.common.data.point.PointData;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import java.util.List;
+import java.util.Map;
+
+@FeignClient(name = "adapter", url = "${meter.adapter-url}")
+public interface IAdapterApi {
+
+    @GetMapping("/ts/latest")
+    Map<String, PointData> getHistoryLatest(
+            @RequestParam(value = "keys", required = false) String keyStr
+    );
+
+    //获取一个或多个测点,指定时间的数据(没有则返回前一个时间点的数据)
+    @GetMapping("/ts/history/section")
+    Map<String, PointData> getHistorySection(
+            @RequestParam(value = "tagNames", required = false) String tagNames,
+            @RequestParam(value = "ts", required = false) Long ts
+    );
+
+    //获取一个测点指定时间段内的数据(可以设置间隔时间单位S)
+    @GetMapping("/ts/history/snap")
+    List<PointData> getHistorySnap(
+            @RequestParam(value = "tagName", required = false) String tagName,
+            @RequestParam(value = "startTs", required = false) Long startTs,
+            @RequestParam(value = "endTs", required = false) Long endTs,
+            @RequestParam(value = "interval", required = false) Integer interval
+    );
+
+    //获取一个测点指定时间段内的数据
+    @GetMapping("/ts/history/raw")
+    List<PointData> getHistoryRaw(
+            @RequestParam(value = "tagName", required = false) String tagName,
+            @RequestParam(value = "startTs", required = false) Long startTs,
+            @RequestParam(value = "endTs", required = false) Long endTs
+    );
+}

+ 21 - 0
electricity/wind/src/main/java/com/gyee/gaia/electricity/wind/adapter/IShardingApi.java

@@ -0,0 +1,21 @@
+package com.gyee.gaia.electricity.wind.adapter;
+
+import com.gyee.gaia.common.data.alarm.FaultInfo;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+
+import java.util.List;
+
+@FeignClient(name = "sharding", url = "${meter.sharding-url}")
+public interface IShardingApi {
+
+    @GetMapping("/fault/history/list?category1={category1}&pagenum=1&pagesize=1000&stationid={stationid}&starttime={starttime}&endtime={endtime}&messagetype={messagetype}&keyword={keyword}")
+    List<FaultInfo> getFaultInfoList(
+            @PathVariable(value = "category1") String category1,
+            @PathVariable(value = "starttime") String starttime,
+            @PathVariable(value = "endtime") String endtime,
+            @PathVariable(value = "stationid") String stationid,
+            @PathVariable(value = "keyword") String keyWord,
+            @PathVariable(value = "messagetype") Integer messagetype);
+}

+ 27 - 0
electricity/wind/src/main/java/com/gyee/gaia/electricity/wind/config/AppConfig.java

@@ -0,0 +1,27 @@
+package com.gyee.gaia.electricity.wind.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+
+@Data
+@Component
+@ConfigurationProperties("meter")
+public class AppConfig {
+
+    //场站范围
+    private String stations;
+    /**
+     * 统一编码名,统一编码
+     */
+    private Map<String, String> uniformcode;
+
+    @Override
+    public String toString() {
+        return "stations = " + stations;
+    }
+
+}

+ 75 - 0
electricity/wind/src/main/java/com/gyee/gaia/electricity/wind/config/ThreadPoolConfig.java

@@ -0,0 +1,75 @@
+package com.gyee.gaia.electricity.wind.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * 线程池配置
+ *
+ * @author xysn
+ */
+@Configuration
+public class ThreadPoolConfig {
+
+    /**
+     *   默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,
+     *	当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
+     *  当队列满了,就继续创建线程,当线程数量大于等于maxPoolSize后,开始使用拒绝策略拒绝
+     */
+
+    /**
+     * 核心线程数(默认线程数)
+     */
+    private static final int corePoolSize = 40;
+    /**
+     * 最大线程数
+     */
+    private static final int maxPoolSize = 100;
+    /**
+     * 允许线程空闲时间(单位:默认为秒)
+     */
+    private static final int keepAliveTime = 60;
+    /**
+     * 缓冲队列大小
+     */
+    private static final int queueCapacity = 300;
+    /**
+     * 允许等待最长时间
+     */
+    private static final int awaitTime = 15;
+    /**
+     * 线程池名前缀
+     */
+    private static final String threadNamePrefix = "Alarm-Thread-";
+
+    private ThreadPoolTaskExecutor executor;
+
+    public ThreadPoolTaskExecutor getExecutor() {
+        if (executor == null) {
+            executor = taskExecutor();
+        }
+
+        return executor;
+    }
+
+    @Bean
+    public ThreadPoolTaskExecutor taskExecutor() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setCorePoolSize(corePoolSize);
+        executor.setMaxPoolSize(maxPoolSize);
+        executor.setQueueCapacity(queueCapacity);
+        executor.setKeepAliveSeconds(keepAliveTime);
+        executor.setThreadNamePrefix(threadNamePrefix);
+        executor.setAwaitTerminationSeconds(awaitTime);
+
+        // 线程池对拒绝任务的处理策略
+        // CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
+        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+        // 初始化
+        executor.initialize();
+        return executor;
+    }
+}

+ 78 - 0
electricity/wind/src/main/java/com/gyee/gaia/electricity/wind/config/XxlJobConfig.java

@@ -0,0 +1,78 @@
+package com.gyee.gaia.electricity.wind.config;
+
+import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * xxl-job config
+ *
+ * @author xuxueli 2017-04-28
+ */
+@Configuration
+public class XxlJobConfig {
+    private Logger logger = LoggerFactory.getLogger(com.gyee.gaia.electricity.wind.config.XxlJobConfig.class);
+
+    @Value("${xxl.job.admin.addresses}")
+    private String adminAddresses;
+
+    @Value("${xxl.job.accessToken}")
+    private String accessToken;
+
+    @Value("${xxl.job.executor.appname}")
+    private String appname;
+
+    @Value("${xxl.job.executor.address}")
+    private String address;
+
+    @Value("${xxl.job.executor.ip}")
+    private String ip;
+
+    @Value("${xxl.job.executor.port}")
+    private int port;
+
+    @Value("${xxl.job.executor.logpath}")
+    private String logPath;
+
+    @Value("${xxl.job.executor.logretentiondays}")
+    private int logRetentionDays;
+
+
+    @Bean
+    public XxlJobSpringExecutor xxlJobExecutor() {
+        logger.info(">>>>>>>>>>> xxl-job config init.");
+        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
+        xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
+        xxlJobSpringExecutor.setAppname(appname);
+        //xxlJobSpringExecutor.se(address);
+        xxlJobSpringExecutor.setIp(ip);
+        xxlJobSpringExecutor.setPort(port);
+        xxlJobSpringExecutor.setAccessToken(accessToken);
+        xxlJobSpringExecutor.setLogPath(logPath);
+        xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
+
+        return xxlJobSpringExecutor;
+    }
+
+    /**
+     * 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP;
+     *
+     *      1、引入依赖:
+     *          <dependency>
+     *             <groupId>org.springframework.cloud</groupId>
+     *             <artifactId>spring-cloud-commons</artifactId>
+     *             <version>${version}</version>
+     *         </dependency>
+     *
+     *      2、配置文件,或者容器启动变量
+     *          spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.'
+     *
+     *      3、获取IP
+     *          String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
+     */
+
+
+}

+ 6 - 0
electricity/wind/src/main/resources/banner.txt

@@ -0,0 +1,6 @@
+ ####  #####   ##   ##### ######        ####    ##   #    #  ####  ###### 
+#        #    #  #    #   #            #    #  #  #  #    # #      #      
+ ####    #   #    #   #   #####  ##### #      #    # #    #  ####  #####  
+     #   #   ######   #   #            #      ###### #    #      # #      
+#    #   #   #    #   #   #            #    # #    # #    # #    # #      
+ ####    #   #    #   #   ######        ####  #    #  ####   ####  ###### 

+ 105 - 0
electricity/wind/src/main/resources/bootstrap.yaml

@@ -0,0 +1,105 @@
+server:
+  port: 8022
+
+spring:
+  application:
+    name: state-cause
+  profiles:
+    # 环境配置
+    active: nx
+  cloud:
+    nacos:
+      discovery:
+        # 服务注册地址
+        server-addr: 192.168.10.18:8848
+      config:
+        # 配置中心地址
+        server-addr: 192.168.10.18:8848
+        # 配置文件格式
+        file-extension: yml
+        # 共享配置
+        shared-configs:
+          - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
+  cache:
+    type: SIMPLE
+  datasource:
+    driver-class-name: org.postgresql.Driver
+    url: jdbc:postgresql://192.168.1.67:5432/gyee
+    username: gyee
+    password: Gyee@2023!@#
+    type: com.alibaba.druid.pool.DruidDataSource
+    druid:
+      max-active: 20
+      initial-size: 1
+      min-idle: 3
+      max-wait: 60000
+      time-between-eviction-runs-millis: 60000
+      min-evictable-idle-time-millis: 300000
+      test-while-idle: true
+      test-on-borrow: false
+      test-on-return: false
+  jackson:
+    date-format: yyyy-MM-dd HH:mm:ss
+    time-zone: GMT+8
+    default-property-inclusion: always
+
+meter:
+  stations: MHS_FDC,NSS_FDC
+  adapter-url: http://192.168.10.18:8011
+  sharding-url: http://192.168.10.18:8075
+  uniformcode:
+    #8种状态
+    state8: FJZT8
+    #桨叶角度
+    blade-angle: AI082
+    #功率-有功功率
+    active-power: AI130
+    #风速
+    speed: AI022
+
+
+mybatis-plus:
+  typeAliasesPackage: com.gyee.gaia.meter.entity
+  mapper-locations: classpath:mappers-postgresql/*.xml
+  global-config:
+    #主键类型  0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
+    id-type: 3
+    #字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
+    field-strategy: 2
+    #驼峰下划线转换
+    db-column-underline: true
+    #mp2.3+ 全局表前缀 mp_
+    #table-prefix: mp_
+    #刷新mapper 调试神器
+    #refresh-mapper: true
+    #数据库大写下划线转换
+    #capital-mode: true
+    # Sequence序列接口实现类配置
+    key-generator: com.baomidou.mybatisplus.incrementer.OracleKeyGenerator
+    #逻辑删除配置(下面3个配置)
+    logic-delete-value: 1
+    logic-not-delete-value: 0
+    #sql-injector: com.baomidou.mybatisplus.mapper.LogicSqlInjector
+    #自定义填充策略接口实现
+    #meta-object-handler: com.baomidou.springboot.MyMetaObjectHandler
+  configuration:
+    #配置返回数据库(column下划线命名&&返回java实体是驼峰命名),自动匹配无需as(没开启这个,SQL需要写as: select user_id as userId)
+    map-underscore-to-camel-case: true
+    cache-enabled: false
+    #配置JdbcTypeForNull, oracle数据库必须配置
+    jdbc-type-for-null: 'null'
+    callSettersOnNulls: true
+    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+
+xxl:
+  job:
+    admin:
+      addresses: http://192.168.10.18:8080/xxl-job-admin
+    accessToken:
+    executor:
+      appname: meter
+      address:
+      ip:
+      port: 9021
+      logpath: d:/xxl-job/meter/logs
+      logretentiondays: 30

+ 49 - 0
electricity/wind/src/main/resources/log4j2.xml

@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Configuration status="WARN">
+    <Properties>
+        <Property name="Pattern">%d{yyyy-MM-dd HH:mm:ss.SSS} %5p %t %M(%F:%L) %m%n</Property>
+    </Properties>
+    <Filter type="ThresholdFilter" level="INFO"/>
+
+    <Appenders>
+        <Console name="Console" target="SYSTEM_OUT">
+            <PatternLayout pattern="${Pattern}"/>
+        </Console>
+        <RollingFile name="RollingFileInfo" fileName="logs/info.log"
+                     filePattern="logs/%d{yyyy-MM}/info-%d{yyyy-MM-dd}.%i.log">
+            <PatternLayout pattern="${Pattern}"/>
+            <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
+            <Policies>
+                <TimeBasedTriggeringPolicy/>
+                <SizeBasedTriggeringPolicy size="100 MB"/>
+            </Policies>
+        </RollingFile>
+        <RollingFile name="RollingFileWarn" fileName="logs/warn.log"
+                     filePattern="logs/%d{yyyy-MM}/warn-%d{yyyy-MM-dd}.%i.log">
+            <PatternLayout pattern="${Pattern}"/>
+            <ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY"/>
+            <Policies>
+                <TimeBasedTriggeringPolicy/>
+                <SizeBasedTriggeringPolicy size="100 MB"/>
+            </Policies>
+        </RollingFile>
+        <RollingFile name="RollingFileError" fileName="logs/error.log"
+                     filePattern="logs/%d{yyyy-MM}/error-%d{yyyy-MM-dd}.%i.log">
+            <PatternLayout pattern="${Pattern}"/>
+            <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
+            <Policies>
+                <TimeBasedTriggeringPolicy/>
+                <SizeBasedTriggeringPolicy size="100 MB"/>
+            </Policies>
+        </RollingFile>
+    </Appenders>
+
+    <Loggers>
+        <Root level="INFO">
+            <AppenderRef ref="Console"/>
+            <!--<appender-ref ref="RollingFileInfo"/>-->
+            <appender-ref ref="RollingFileWarn"/>
+            <appender-ref ref="RollingFileError"/>
+        </Root>
+    </Loggers>
+</Configuration>

+ 40 - 0
electricity/wind/src/test/java/com/gyee/gaia/MyAppTests.java

@@ -0,0 +1,40 @@
+package com.gyee.gaia;
+
+import com.gyee.gaia.electricity.wind.adapter.IShardingApi;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import javax.annotation.Resource;
+
+@SpringBootTest(classes = WindPowergenApp.class)
+class MyAppTests {
+
+    @Resource
+    private IShardingApi shardingApi;
+
+    @Test
+    void test() {
+
+        /*QueryWrapper<StateCause> scWrapper = new QueryWrapper<>();
+        scWrapper.select("max(end_time)");
+        Map<String, Object> map = stateCauseService.getMap(scWrapper);*/
+        /*List<FaultInfo> infos = shardingApi.getFaultInfoList("SYZ", "2023-05-10", "2023-05-13", CacheContext.stationMap.get("NX_GDDL_XS_FDC_STA").getCode(), null, null);
+        if(infos.size()>0){
+            infos = infos.stream().filter(fi -> fi.getAlertText().contains("位状态")).collect(Collectors.toList());
+            if(infos.isEmpty()) return;
+
+            Map<String, List<FaultInfo>> map = infos.stream().map(fi -> {
+                fi.setConfirmPerson(fi.getAlertText().substring(fi.getAlertText().indexOf("--")));
+                fi.setAlertText(fi.getAlertText().substring(0, fi.getAlertText().indexOf("--")));
+                return fi;
+            }).collect(Collectors.groupingBy(FaultInfo::getAlertText));
+
+            for (List<FaultInfo> value : map.values()) {
+                //判断当前故障是分位状态
+                if(value.get(0).getConfirmPerson().equals("分位状态")) return;
+            }
+        }*/
+
+
+    }
+}

+ 119 - 0
electricity/wind/src/test/resources/bootstrap.yaml

@@ -0,0 +1,119 @@
+server:
+  port: 8321
+
+spring:
+  application:
+    name: state-cause
+  profiles:
+    # 环境配置
+    active: nx
+  cloud:
+    nacos:
+      discovery:
+        # 服务注册地址
+        server-addr: 192.168.10.18:8848
+      config:
+        # 配置中心地址
+        server-addr: 192.168.10.18:8848
+        # 配置文件格式
+        file-extension: yml
+        # 共享配置
+        shared-configs:
+          - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
+  cache:
+    type: SIMPLE
+  datasource:
+    driver-class-name: org.postgresql.Driver
+    url: jdbc:postgresql://192.168.1.67:5432/gyee
+    username: gyee
+    password: Gyee@2023!@#
+    type: com.alibaba.druid.pool.DruidDataSource
+    druid:
+      max-active: 20
+      initial-size: 1
+      min-idle: 3
+      max-wait: 60000
+      time-between-eviction-runs-millis: 60000
+      min-evictable-idle-time-millis: 300000
+      test-while-idle: true
+      test-on-borrow: false
+      test-on-return: false
+  jackson:
+    date-format: yyyy-MM-dd HH:mm:ss
+    time-zone: GMT+8
+    default-property-inclusion: always
+
+meter:
+  stations: MHS_FDC,NSS_FDC
+  adapter-url: http://192.168.10.18:8011
+  sharding-url: http://192.168.10.18:8075
+  time:
+    #小于多长时间的故障不计算在内,单位秒
+    fault-min: 180
+    #限电跳变,2次限电间隔多长时间认为是同一次限电,单位秒
+    ration: 180
+    #小于多长时间的限电不计算在内,单位秒
+    ration-min: 180
+  uniformcode:
+    #8种状态
+    state8: FJZT8
+    #桨叶角度
+    blade-angle: AI082
+    #功率-有功功率
+    active-power: AI130
+    #风速
+    speed: AI022
+  uniformcode-boost:
+    #有功设定限值
+    active-power-set: BTYGSDXZ
+    #理论功率
+    apparent-power: BTLLGL
+    #实发有功
+    output-power: BTSFYG
+
+
+mybatis-plus:
+  typeAliasesPackage: com.gyee.gaia.meter.entity
+  mapper-locations: classpath:mappers-postgresql/*.xml
+  global-config:
+    #主键类型  0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
+    id-type: 3
+    #字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
+    field-strategy: 2
+    #驼峰下划线转换
+    db-column-underline: true
+    #mp2.3+ 全局表前缀 mp_
+    #table-prefix: mp_
+    #刷新mapper 调试神器
+    #refresh-mapper: true
+    #数据库大写下划线转换
+    #capital-mode: true
+    # Sequence序列接口实现类配置
+    key-generator: com.baomidou.mybatisplus.incrementer.OracleKeyGenerator
+    #逻辑删除配置(下面3个配置)
+    logic-delete-value: 1
+    logic-not-delete-value: 0
+    #sql-injector: com.baomidou.mybatisplus.mapper.LogicSqlInjector
+    #自定义填充策略接口实现
+    #meta-object-handler: com.baomidou.springboot.MyMetaObjectHandler
+  configuration:
+    #配置返回数据库(column下划线命名&&返回java实体是驼峰命名),自动匹配无需as(没开启这个,SQL需要写as: select user_id as userId)
+    map-underscore-to-camel-case: true
+    cache-enabled: false
+    #配置JdbcTypeForNull, oracle数据库必须配置
+    jdbc-type-for-null: 'null'
+    callSettersOnNulls: true
+    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+
+xxl:
+  job:
+    admin:
+      addresses: http://192.168.10.18:8080/xxl-job-admin
+    accessToken:
+    executor:
+      appname: meter
+      address:
+      ip:
+      port: 9021
+      logpath: d:/xxl-job/meter/logs
+      logretentiondays: 30

+ 11 - 0
realtime/wind/README.md

@@ -0,0 +1,11 @@
+### cause 分析原因,故障损失,计划检修损失,受累损失,限电损失,性能损失等
+
+状态为: 并网--是否限电--风速小于11并且桨叶角度大于2;风速大于11并且有功功率小于满发功率减100 待机-- 是否限停-- 有故障--故障 无故障--有功设定小于出线功率;有功设定小于应发的85% 并且
+出线功率大于有功设定94%;有功设定小于应发的70% 并且 出线功率大于有功设定80% 并且 当前场站限电台数大于等于3--限停 待机 维护-- 有故障--故障 无故障--计划检修 故障--小于?(3)分钟故障忽略
+是否受累--全站故障或者离线 故障 离线-- 是否受累--全站故障或者离线 故障
+
+青山华创风机在停机的状态下应该算故障损失
+
+
+
+

+ 53 - 0
realtime/wind/build.gradle

@@ -0,0 +1,53 @@
+buildscript {
+    repositories {
+        mavenLocal()
+        maven {
+            allowInsecureProtocol = true
+            url "http://maven.aliyun.com/nexus/content/groups/public"
+        }
+        mavenCentral()
+    }
+    dependencies {
+        classpath("$bootGroup:spring-boot-gradle-plugin:$springBootVersion")
+    }
+}
+
+apply plugin: "$bootGroup"
+apply plugin: 'io.spring.dependency-management'
+
+dependencyManagement {
+    imports {
+        mavenBom "org.springframework.cloud:spring-cloud-dependencies:$springCloudVersion"
+        mavenBom "org.springframework.boot:spring-boot-dependencies:$springBootVersion"
+        mavenBom "com.alibaba.cloud:spring-cloud-alibaba-dependencies:$springCloudAlibabaVersion"
+    }
+}
+
+dependencies {
+    implementation project(":timeseries:dao-sql")
+
+    implementation("$bootGroup:spring-boot-starter-web")
+    implementation("$bootGroup:spring-boot-starter-undertow")
+
+
+    implementation("org.postgresql:postgresql:$postgresqlDriverVersion")
+    implementation "com.baomidou:mybatis-plus-boot-starter:$mybatisPlusVersion"
+    implementation("com.alibaba:druid:$alibabaDruidVersion")
+    implementation("com.alibaba:fastjson:$fastjsonVersion")
+
+    implementation("$bootGroup:spring-boot-starter-test")
+    implementation("com.xuxueli:xxl-job-core:$xxlJobVersion")
+    implementation("org.springframework.cloud:spring-cloud-starter-bootstrap")
+    implementation("com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery")
+    implementation("com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-config")
+
+    implementation("com.taosdata.jdbc:taos-jdbcdriver:$taosVersion2")
+
+    implementation("$cloudGroup:spring-cloud-starter-openfeign")
+    implementation("cn.hutool:hutool-all:5.8.18")
+    implementation("com.baomidou:dynamic-datasource-spring-boot-starter:$mybatisPlusVersion")
+}
+
+test {
+    useJUnitPlatform()
+}

+ 21 - 0
realtime/wind/src/main/java/com/gyee/gaia/WindRealtimeApp.java

@@ -0,0 +1,21 @@
+package com.gyee.gaia;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+
+
+@SpringBootApplication
+@EnableDiscoveryClient
+@EnableFeignClients
+public class WindRealtimeApp {
+
+    public static void main(String[] args) {
+
+        SpringApplication.run(WindRealtimeApp.class, args);
+
+    }
+}
+
+

+ 27 - 0
realtime/wind/src/main/java/com/gyee/gaia/realtime/wind/adapter/IAdapterApi.java

@@ -0,0 +1,27 @@
+package com.gyee.gaia.realtime.wind.adapter;
+
+import com.gyee.gaia.common.data.point.PointData;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+
+import java.util.List;
+import java.util.Map;
+
+@FeignClient(name = "adapter", url = "${meter.adapter-url}")
+public interface IAdapterApi {
+
+    @GetMapping("/ts/history/snap?tagName={tagName}&startTs={startTs}&endTs={endTs}&interval={interval}")
+    List<PointData> getValuesByKey(@PathVariable(value = "tagName") String tagName, @PathVariable(value = "startTs") long startTs,
+                                   @PathVariable(value = "endTs") long endTs, @PathVariable(value = "interval") int interval);
+
+    @GetMapping("/ts/history/raw?tagName={tagName}&startTs={startTs}&endTs={endTs}")
+    List<PointData> getRawByKey(@PathVariable(value = "tagName") String tagName, @PathVariable(value = "startTs") long startTs,
+                                @PathVariable(value = "endTs") long endTs);
+
+    @GetMapping("/ts/history/section?tagNames={tagNames}&ts={ts}")
+    Map<String, PointData> getHistorySection(@PathVariable(value = "tagNames") String tagNames, @PathVariable(value = "ts") long ts);
+
+    @GetMapping("/ts/latest?keys={keys}")
+    public Map<String, PointData> getLatest(@PathVariable(value = "keys") String keys);
+}

+ 21 - 0
realtime/wind/src/main/java/com/gyee/gaia/realtime/wind/adapter/IShardingApi.java

@@ -0,0 +1,21 @@
+package com.gyee.gaia.realtime.wind.adapter;
+
+import com.gyee.gaia.common.data.alarm.FaultInfo;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+
+import java.util.List;
+
+@FeignClient(name = "sharding", url = "${meter.sharding-url}")
+public interface IShardingApi {
+
+    @GetMapping("/fault/history/list?category1={category1}&pagenum=1&pagesize=1000&stationid={stationid}&starttime={starttime}&endtime={endtime}&messagetype={messagetype}&keyword={keyword}")
+    List<FaultInfo> getFaultInfoList(
+            @PathVariable(value = "category1") String category1,
+            @PathVariable(value = "starttime") String starttime,
+            @PathVariable(value = "endtime") String endtime,
+            @PathVariable(value = "stationid") String stationid,
+            @PathVariable(value = "keyword") String keyWord,
+            @PathVariable(value = "messagetype") Integer messagetype);
+}

+ 27 - 0
realtime/wind/src/main/java/com/gyee/gaia/realtime/wind/config/AppConfig.java

@@ -0,0 +1,27 @@
+package com.gyee.gaia.realtime.wind.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+
+@Data
+@Component
+@ConfigurationProperties("meter")
+public class AppConfig {
+
+    //场站范围
+    private String stations;
+    /**
+     * 统一编码名,统一编码
+     */
+    private Map<String, String> uniformcodeOne;
+
+    @Override
+    public String toString() {
+        return "stations = " + stations;
+    }
+
+}

+ 75 - 0
realtime/wind/src/main/java/com/gyee/gaia/realtime/wind/config/ThreadPoolConfig.java

@@ -0,0 +1,75 @@
+package com.gyee.gaia.realtime.wind.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * 线程池配置
+ *
+ * @author xysn
+ */
+@Configuration
+public class ThreadPoolConfig {
+
+    /**
+     *   默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,
+     *	当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
+     *  当队列满了,就继续创建线程,当线程数量大于等于maxPoolSize后,开始使用拒绝策略拒绝
+     */
+
+    /**
+     * 核心线程数(默认线程数)
+     */
+    private static final int corePoolSize = 40;
+    /**
+     * 最大线程数
+     */
+    private static final int maxPoolSize = 100;
+    /**
+     * 允许线程空闲时间(单位:默认为秒)
+     */
+    private static final int keepAliveTime = 60;
+    /**
+     * 缓冲队列大小
+     */
+    private static final int queueCapacity = 300;
+    /**
+     * 允许等待最长时间
+     */
+    private static final int awaitTime = 15;
+    /**
+     * 线程池名前缀
+     */
+    private static final String threadNamePrefix = "Alarm-Thread-";
+
+    private ThreadPoolTaskExecutor executor;
+
+    public ThreadPoolTaskExecutor getExecutor() {
+        if (executor == null) {
+            executor = taskExecutor();
+        }
+
+        return executor;
+    }
+
+    @Bean
+    public ThreadPoolTaskExecutor taskExecutor() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setCorePoolSize(corePoolSize);
+        executor.setMaxPoolSize(maxPoolSize);
+        executor.setQueueCapacity(queueCapacity);
+        executor.setKeepAliveSeconds(keepAliveTime);
+        executor.setThreadNamePrefix(threadNamePrefix);
+        executor.setAwaitTerminationSeconds(awaitTime);
+
+        // 线程池对拒绝任务的处理策略
+        // CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
+        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+        // 初始化
+        executor.initialize();
+        return executor;
+    }
+}

+ 78 - 0
realtime/wind/src/main/java/com/gyee/gaia/realtime/wind/config/XxlJobConfig.java

@@ -0,0 +1,78 @@
+package com.gyee.gaia.realtime.wind.config;
+
+import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * xxl-job config
+ *
+ * @author xuxueli 2017-04-28
+ */
+@Configuration
+public class XxlJobConfig {
+    private Logger logger = LoggerFactory.getLogger(com.gyee.gaia.realtime.wind.config.XxlJobConfig.class);
+
+    @Value("${xxl.job.admin.addresses}")
+    private String adminAddresses;
+
+    @Value("${xxl.job.accessToken}")
+    private String accessToken;
+
+    @Value("${xxl.job.executor.appname}")
+    private String appname;
+
+    @Value("${xxl.job.executor.address}")
+    private String address;
+
+    @Value("${xxl.job.executor.ip}")
+    private String ip;
+
+    @Value("${xxl.job.executor.port}")
+    private int port;
+
+    @Value("${xxl.job.executor.logpath}")
+    private String logPath;
+
+    @Value("${xxl.job.executor.logretentiondays}")
+    private int logRetentionDays;
+
+
+    @Bean
+    public XxlJobSpringExecutor xxlJobExecutor() {
+        logger.info(">>>>>>>>>>> xxl-job config init.");
+        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
+        xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
+        xxlJobSpringExecutor.setAppname(appname);
+        //xxlJobSpringExecutor.se(address);
+        xxlJobSpringExecutor.setIp(ip);
+        xxlJobSpringExecutor.setPort(port);
+        xxlJobSpringExecutor.setAccessToken(accessToken);
+        xxlJobSpringExecutor.setLogPath(logPath);
+        xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
+
+        return xxlJobSpringExecutor;
+    }
+
+    /**
+     * 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP;
+     *
+     *      1、引入依赖:
+     *          <dependency>
+     *             <groupId>org.springframework.cloud</groupId>
+     *             <artifactId>spring-cloud-commons</artifactId>
+     *             <version>${version}</version>
+     *         </dependency>
+     *
+     *      2、配置文件,或者容器启动变量
+     *          spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.'
+     *
+     *      3、获取IP
+     *          String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
+     */
+
+
+}

+ 56 - 0
realtime/wind/src/main/java/com/gyee/gaia/realtime/wind/init/CacheContext.java

@@ -0,0 +1,56 @@
+package com.gyee.gaia.realtime.wind.init;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.gyee.gaia.common.data.windturbine.Equipment;
+import com.gyee.gaia.common.data.windturbine.Powerstation;
+import com.gyee.gaia.dao.sql.Windturbine.IEquipmentService;
+import com.gyee.gaia.dao.sql.Windturbine.IPowerstationService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+@Order(1)
+@Slf4j
+@Component
+public class CacheContext implements ApplicationRunner {
+
+    @Resource
+    private IEquipmentService equipmentService;
+    @Resource
+    private IPowerstationService powerstationService;
+    /**
+     * 风机号,Equipment
+     */
+    public static Map<String, Equipment> equipMap;
+    /**
+     * 场站id,Powerstation
+     */
+    public static Map<String, Powerstation> stationMap;
+    /**
+     * 风场,风机
+     */
+    public static Map<String, List<Equipment>> stationEquipMap;
+
+    @Override
+    public void run(ApplicationArguments args) throws Exception {
+
+        log.info("加载风机!");
+        QueryWrapper<Equipment> emWrapper = new QueryWrapper<>();
+        emWrapper.eq("spare1", "WT");
+        List<Equipment> emList = equipmentService.list(emWrapper);
+        equipMap = emList.stream().collect(Collectors.toMap(Equipment::getId, Function.identity()));
+        stationEquipMap = emList.stream().collect(Collectors.groupingBy(Equipment::getWindpowerstationId));
+
+        log.info("加载风场信息!");
+        List<Powerstation> stationList = powerstationService.list();
+        stationMap = stationList.stream().collect(Collectors.toMap(Powerstation::getId, Function.identity()));
+    }
+}

+ 220 - 0
realtime/wind/src/main/java/com/gyee/gaia/realtime/wind/job/CauseJobHandler.java

@@ -0,0 +1,220 @@
+package com.gyee.gaia.realtime.wind.job;
+
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.NumberUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.gyee.gaia.common.data.point.PointData;
+import com.gyee.gaia.common.data.point.TestingPoint;
+import com.gyee.gaia.common.data.taos.RealtimeAverageTarget;
+import com.gyee.gaia.dao.sql.point.ITestingPointService;
+import com.gyee.gaia.dao.sql.taos.IRealtimeAverageTargetService;
+import com.gyee.gaia.realtime.wind.adapter.IAdapterApi;
+import com.gyee.gaia.realtime.wind.config.AppConfig;
+import com.gyee.gaia.realtime.wind.init.CacheContext;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.core.annotation.Order;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+@Order(2)
+@Component
+public class CauseJobHandler implements ApplicationRunner {
+
+    @Resource
+    private ITestingPointService testingPointService;
+    @Resource
+    private AppConfig appConfig;
+    @Resource
+    private IRealtimeAverageTargetService realtimeAverageTargetService;
+    @Resource
+    private IAdapterApi adapterApi;
+    @Resource
+    private ThreadPoolTaskExecutor taskExecutor;
+    private String pointCodes;
+    private Map<String, ArrayDeque<PointData>> pdaqMap = new ConcurrentHashMap<>();
+    /**
+     * 风机id,uniformcode,点名
+     */
+    private Map<String, Map<String, String>> equipUcMap = new HashMap<>();
+    private Map<String, String> pjzb1fzMap;
+
+    @Override
+    public void run(ApplicationArguments args) throws Exception {
+
+        CacheContext.equipMap.keySet().forEach(em -> equipUcMap.put(em, new HashMap<>()));
+
+        Collection<String> uniformCodeList = appConfig.getUniformcodeOne().values();
+
+        //根据uniformcode获取测点
+        QueryWrapper<TestingPoint> tpWrapper = new QueryWrapper<>();
+        tpWrapper.eq("thing_type", "windturbine");
+        StringBuilder sb = new StringBuilder();
+
+        //获取最新更新时间
+        /*QueryWrapper<RealtimeAverageTarget> ratWrapper = new QueryWrapper<>();
+        ratWrapper.select("max(time)");
+        Map<String, Object> map = realtimeAverageTargetService.getMap(ratWrapper);
+        Date max;
+        if (map != null && map.size() > 0) {
+            max = (Date) map.get("max");
+        } else {
+            max = DateUtil.beginOfDay(DateUtil.date());
+        }*/
+
+        //风机号,时间,RealtimeAverageTarget
+        Map<String, Map<Long, RealtimeAverageTarget>> ratssmm = new HashMap<>();
+        List<RealtimeAverageTarget> rats = new ArrayList<>();
+        //5个指标
+        for (String value : uniformCodeList) {
+            //每个指标407个风机
+            List<TestingPoint> list = testingPointService.list(tpWrapper.clone().eq("uniform_code", value));
+
+            List<PointData> rawByKey;
+            RealtimeAverageTarget target = null;
+            //每个指标每个风机
+            for (TestingPoint point : list) {
+                String thingId = point.getThingId();
+                sb.append(",").append(point.getCode());
+                equipUcMap.get(thingId).put(value, point.getCode());
+                /*rawByKey = adapterApi.getRawByKey(point.getCode(), max.getTime(), System.currentTimeMillis());
+                Map<Long, Double> collect = rawByKey.stream().collect(Collectors.groupingBy(data -> data.getTs() / 60000, Collectors.averagingDouble(PointData::getDoubleValue)));
+                //TODO 补缺的时间
+                for (Map.Entry<Long, Double> entry : collect.entrySet()) {
+                    if(target==null){
+                        target = new RealtimeAverageTarget();
+                        target.setTime(new Timestamp(entry.getKey()*60000));
+                        target.setEquipmentId(thingId);
+                        setRats(target, value,entry.getValue());
+                        rats.add(target);
+
+                        if(!ratssmm.containsKey(thingId)) ratssmm.put(thingId, new HashMap<>());
+                        ratssmm.get(thingId).put(entry.getKey(), target);
+                    }else {
+                        RealtimeAverageTarget averageTarget = ratssmm.get(thingId).get(entry.getKey());
+                        if(averageTarget!=null) setRats(averageTarget, value, entry.getValue());
+                    }
+                }*/
+            }
+        }
+        //realtimeAverageTargetService.saveBatch(rats, 3000);
+        QueryWrapper<RealtimeAverageTarget> ratWrapper = new QueryWrapper<>();
+        ratWrapper.select("tbname", "equipment_id").eq("uniform_code", "1FZPJZB");
+        List<RealtimeAverageTarget> ratList = realtimeAverageTargetService.list(ratWrapper);
+        pjzb1fzMap = ratList.stream().collect(Collectors.toMap(RealtimeAverageTarget::getEquipmentId, RealtimeAverageTarget::getTbname));
+
+        pointCodes = sb.delete(0, 1).toString();
+        init();
+        taskExecutor.submit(this::refreshQueue);
+        taskExecutor.submit(this::calcRealtimeAverageTarget);
+    }
+
+    private void setRats(RealtimeAverageTarget target, String uc, double value) {
+        if (uc.equals(appConfig.getUniformcodeOne().get("wind-speed"))) {
+            target.setWindSpeed(value);
+        } else if (uc.equals(appConfig.getUniformcodeOne().get("active-power"))) {
+            target.setPower(value);
+        } else if (uc.equals(appConfig.getUniformcodeOne().get("generator_speed"))) {
+            target.setGeneratorSpeed(value);
+        } else if (uc.equals(appConfig.getUniformcodeOne().get("impeller_speed"))) {
+            target.setImpellerSpeed(value);
+        } else if (uc.equals(appConfig.getUniformcodeOne().get("wind_direction"))) {
+            target.setWindDirection(value);
+        }
+    }
+
+    private void init() throws InterruptedException {
+
+        //缓存1分钟的值填满pdaqMap
+        for (int i = 0; i < 60; i++) {
+            Map<String, PointData> latest = adapterApi.getLatest(pointCodes);
+            if (i == 0) {
+                latest.forEach((k, v) -> {
+                    ArrayDeque<PointData> pdaq = new ArrayDeque<>(60);
+                    pdaq.offer(v);
+                    pdaqMap.put(k, pdaq);
+                });
+            } else {
+                latest.forEach((k, v) -> {
+                    pdaqMap.get(k).offer(v);
+                });
+            }
+            Thread.sleep(1000);
+        }
+    }
+
+    private void refreshQueue() {
+
+        try {
+            while (true) {
+                Map<String, PointData> latest = adapterApi.getLatest(pointCodes);
+                latest.forEach((k, v) -> {
+                    pdaqMap.get(k).poll();
+                    pdaqMap.get(k).offer(v);
+                });
+                Thread.sleep(1000);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void calcRealtimeAverageTarget() {
+
+        String pointCode = null;
+        try {
+            while (true) {
+                if (LocalDateTime.now().getSecond() == 59) {
+
+                    RealtimeAverageTarget target;
+                    double v;
+                    List<RealtimeAverageTarget> targets = new ArrayList<>();
+                    for (Map.Entry<String, Map<String, String>> entry : equipUcMap.entrySet()) {
+                        target = new RealtimeAverageTarget();
+                        target.setTime(DateUtil.beginOfMinute(DateUtil.date()).toTimestamp());
+                        //target.setEquipmentId(entry.getKey());
+                        target.setTbname(pjzb1fzMap.get(entry.getKey()));
+
+                        pointCode = entry.getValue().get(appConfig.getUniformcodeOne().get("wind-speed"));
+                        if (pointCode != null) {
+                            v = pdaqMap.get(pointCode).stream().mapToDouble(data -> data.getDoubleValue()).average().orElse(0);
+                            target.setWindSpeed(NumberUtil.round(v, 2).doubleValue());
+                        }
+                        pointCode = entry.getValue().get(appConfig.getUniformcodeOne().get("active-power"));
+                        if (pointCode != null) {
+                            v = pdaqMap.get(pointCode).stream().mapToDouble(data -> data.getDoubleValue()).average().orElse(0);
+                            target.setPower(NumberUtil.round(v, 2).doubleValue());
+                        }
+                        pointCode = entry.getValue().get(appConfig.getUniformcodeOne().get("generator_speed"));
+                        if (pointCode != null) {
+                            v = pdaqMap.get(pointCode).stream().mapToDouble(data -> data.getDoubleValue()).average().orElse(0);
+                            target.setGeneratorSpeed(NumberUtil.round(v, 2).doubleValue());
+                        }
+                        pointCode = entry.getValue().get(appConfig.getUniformcodeOne().get("impeller_speed"));
+                        if (pointCode != null) {
+                            v = pdaqMap.get(pointCode).stream().mapToDouble(data -> data.getDoubleValue()).average().orElse(0);
+                            target.setImpellerSpeed(NumberUtil.round(v, 2).doubleValue());
+                        }
+                        pointCode = entry.getValue().get(appConfig.getUniformcodeOne().get("wind_direction"));
+                        if (pointCode != null) {
+                            v = pdaqMap.get(pointCode).stream().mapToDouble(data -> data.getDoubleValue()).average().orElse(0);
+                            target.setWindDirection(NumberUtil.round(v, 2).doubleValue());
+                        }
+                        targets.add(target);
+                    }
+                    realtimeAverageTargetService.saveBatch(targets);
+                }
+                Thread.sleep(1000);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+    }
+}

+ 116 - 0
realtime/wind/src/main/resources/bootstrap.yaml

@@ -0,0 +1,116 @@
+server:
+  port: 8022
+
+spring:
+  application:
+    name: state-cause
+  profiles:
+    # 环境配置
+    active: nx
+  cloud:
+    nacos:
+      discovery:
+        # 服务注册地址
+        server-addr: 192.168.10.18:8848
+      config:
+        # 配置中心地址
+        server-addr: 192.168.10.18:8848
+        # 配置文件格式
+        file-extension: yml
+        # 共享配置
+        shared-configs:
+          - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
+  cache:
+    type: SIMPLE
+  datasource:
+    dynamic:
+      primary: master #设置默认的数据源或者数据源组,默认值即为master
+      strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
+      datasource:
+        master:
+          url: jdbc:postgresql://192.168.1.67:5432/gyee
+          username: gyee
+          password: Gyee@2023!@#
+          driver-class-name: org.postgresql.Driver
+          type: com.alibaba.druid.pool.DruidDataSource
+        fjjsfw:
+          url: jdbc:TAOS-RS://192.168.1.67:6041/jsfw?charset=UTF-8&locale=en_US.UTF-8&timezone=UTC-8
+          username: root
+          password: taosdata
+          driver-class-name: com.taosdata.jdbc.rs.RestfulDriver
+    druid:
+      max-active: 20
+      initial-size: 1
+      min-idle: 3
+      max-wait: 60000
+      time-between-eviction-runs-millis: 60000
+      min-evictable-idle-time-millis: 300000
+      test-while-idle: false
+      test-on-borrow: false
+      test-on-return: false
+  jackson:
+    date-format: yyyy-MM-dd HH:mm:ss
+    time-zone: GMT+8
+    default-property-inclusion: always
+
+meter:
+  stations: MHS_FDC,NSS_FDC
+  adapter-url: http://192.168.10.18:8011
+  sharding-url: http://192.168.10.18:8075
+  uniformcode-one:
+    wind-speed: AI022  #风速
+    active-power: AI130  #功率-有功功率
+    generator_speed: AI128  #发电机转速
+    impeller_speed: AI012  #叶轮转速
+    wind_direction: AI010  #风向
+
+
+mybatis-plus:
+  typeAliasesPackage: com.gyee.gaia.meter.entity
+  mapper-locations: classpath:mappers-postgresql/*.xml
+  global-config:
+    #主键类型  0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
+    id-type: 3
+    #字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
+    field-strategy: 2
+    #驼峰下划线转换
+    db-column-underline: true
+    #mp2.3+ 全局表前缀 mp_
+    #table-prefix: mp_
+    #刷新mapper 调试神器
+    #refresh-mapper: true
+    #数据库大写下划线转换
+    #capital-mode: true
+    # Sequence序列接口实现类配置
+    key-generator: com.baomidou.mybatisplus.incrementer.OracleKeyGenerator
+    #逻辑删除配置(下面3个配置)
+    logic-delete-value: 1
+    logic-not-delete-value: 0
+    #sql-injector: com.baomidou.mybatisplus.mapper.LogicSqlInjector
+    #自定义填充策略接口实现
+    #meta-object-handler: com.baomidou.springboot.MyMetaObjectHandler
+  configuration:
+    #配置返回数据库(column下划线命名&&返回java实体是驼峰命名),自动匹配无需as(没开启这个,SQL需要写as: select user_id as userId)
+    map-underscore-to-camel-case: true
+    cache-enabled: false
+    #配置JdbcTypeForNull, oracle数据库必须配置
+    jdbc-type-for-null: 'null'
+    callSettersOnNulls: true
+    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+
+xxl:
+  job:
+    admin:
+      addresses: http://192.168.10.18:8080/xxl-job-admin
+    accessToken:
+    executor:
+      appname: meter
+      address:
+      ip:
+      port: 9021
+      logpath: d:/xxl-job/meter/logs
+      logretentiondays: 30
+
+#logging:
+#  level:
+#    org.springframework: debug

+ 49 - 0
realtime/wind/src/main/resources/log4j2.xml

@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Configuration status="WARN">
+    <Properties>
+        <Property name="Pattern">%d{yyyy-MM-dd HH:mm:ss.SSS} %5p %t %M(%F:%L) %m%n</Property>
+    </Properties>
+    <Filter type="ThresholdFilter" level="INFO"/>
+
+    <Appenders>
+        <Console name="Console" target="SYSTEM_OUT">
+            <PatternLayout pattern="${Pattern}"/>
+        </Console>
+        <RollingFile name="RollingFileInfo" fileName="logs/info.log"
+                     filePattern="logs/%d{yyyy-MM}/info-%d{yyyy-MM-dd}.%i.log">
+            <PatternLayout pattern="${Pattern}"/>
+            <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
+            <Policies>
+                <TimeBasedTriggeringPolicy/>
+                <SizeBasedTriggeringPolicy size="100 MB"/>
+            </Policies>
+        </RollingFile>
+        <RollingFile name="RollingFileWarn" fileName="logs/warn.log"
+                     filePattern="logs/%d{yyyy-MM}/warn-%d{yyyy-MM-dd}.%i.log">
+            <PatternLayout pattern="${Pattern}"/>
+            <ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY"/>
+            <Policies>
+                <TimeBasedTriggeringPolicy/>
+                <SizeBasedTriggeringPolicy size="100 MB"/>
+            </Policies>
+        </RollingFile>
+        <RollingFile name="RollingFileError" fileName="logs/error.log"
+                     filePattern="logs/%d{yyyy-MM}/error-%d{yyyy-MM-dd}.%i.log">
+            <PatternLayout pattern="${Pattern}"/>
+            <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
+            <Policies>
+                <TimeBasedTriggeringPolicy/>
+                <SizeBasedTriggeringPolicy size="100 MB"/>
+            </Policies>
+        </RollingFile>
+    </Appenders>
+
+    <Loggers>
+        <Root level="INFO">
+            <AppenderRef ref="Console"/>
+            <!--<appender-ref ref="RollingFileInfo"/>-->
+            <appender-ref ref="RollingFileWarn"/>
+            <appender-ref ref="RollingFileError"/>
+        </Root>
+    </Loggers>
+</Configuration>

+ 45 - 0
realtime/wind/src/test/java/com/gyee/gaia/MyAppTests.java

@@ -0,0 +1,45 @@
+package com.gyee.gaia;
+
+import com.gyee.gaia.common.data.alarm.FaultInfo;
+import com.gyee.gaia.realtime.wind.adapter.IShardingApi;
+import com.gyee.gaia.realtime.wind.init.CacheContext;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@SpringBootTest
+class MyAppTests {
+
+    @Resource
+    private IShardingApi shardingApi;
+
+    @Test
+    void test() {
+
+        /*QueryWrapper<StateCause> scWrapper = new QueryWrapper<>();
+        scWrapper.select("max(end_time)");
+        Map<String, Object> map = stateCauseService.getMap(scWrapper);*/
+        List<FaultInfo> infos = shardingApi.getFaultInfoList("SYZ", "2023-05-10", "2023-05-13", CacheContext.stationMap.get("NX_GDDL_XS_FDC_STA").getCode(), null, null);
+        if (infos.size() > 0) {
+            infos = infos.stream().filter(fi -> fi.getAlertText().contains("位状态")).collect(Collectors.toList());
+            if (infos.isEmpty()) return;
+
+            Map<String, List<FaultInfo>> map = infos.stream().map(fi -> {
+                fi.setConfirmPerson(fi.getAlertText().substring(fi.getAlertText().indexOf("--")));
+                fi.setAlertText(fi.getAlertText().substring(0, fi.getAlertText().indexOf("--")));
+                return fi;
+            }).collect(Collectors.groupingBy(FaultInfo::getAlertText));
+
+            for (List<FaultInfo> value : map.values()) {
+                //判断当前故障是分位状态
+                if (value.get(0).getConfirmPerson().equals("分位状态")) return;
+            }
+        }
+
+
+    }
+}