Bladeren bron

储能控制,日志性能

learshaw 1 week geleden
bovenliggende
commit
d146523dd6

+ 83 - 0
ems/AGENTS.md

@@ -0,0 +1,83 @@
+# AGENTS.md — ems (Energy Management System)
+
+## Project Overview
+
+高速公路服务区能源管理后台系统。接入服务区各类表计(电表、水表、充电桩等),通过物模型进行设备管理,实现服务区**源网荷储**(光伏、电网、负荷、储能)的调配管理。
+
+后端基于若依(RuoYi)框架开发,**当前生产环境使用 Spring Cloud 微服务版本**。
+
+## Workspace Context
+
+本仓库 (`ems/`) 是 `prod_group_bg/` 工作空间的一个模块,依赖以下兄弟项目(不在本 pom 中):
+- `common-application/` — 若依单体版框架模块
+- `common-cloud/` — 若依微服务版模块(ruoyi-common-*, ruoyi-gateway, ruoyi-auth, ruoyi-api-system)
+- `prod-common/` — 公共库 (`prod-commons`)
+
+这些依赖发布在私有 Nexus (`maven.xt.wenhq.top:8083`)。修改这些项目后需先 `mvn install` 才能被 ems 引用。
+
+## 模块结构
+
+```
+ems-core/                    # 核心业务模块(domain, mapper, service)
+├── 被 ems-cloud/ems-server 引用(微服务接口层)
+├── 被 ems-cloud/ems-dev-adapter 引用(设备采集层)
+└── 被 ems-application/ems-admin 引用(单体版)
+
+ems-cloud/                   # 微服务部署版(生产使用)
+├── ems-server/              # 能源管理接口服务 :9202
+└── ems-dev-adapter/         # 设备数据采集服务 :9203
+
+ems-application/             # 单体部署版
+└── ems-admin/               # 单体入口 :9100
+```
+
+**核心设计**:`ems-core` 复用,不同部署模式仅暴露外部接口实现不同。
+
+## 生产微服务实例
+
+| 实例 | 端口 | 说明 |
+|------|------|------|
+| ruoyi-gateway | 9100 | 网关 |
+| ruoyi-auth | 9200 | 认证中心 |
+| ruoyi-system | 9201 | 系统模块 |
+| ems-server | 9202 | 能源管理业务 |
+| ems-dev-adapter | 9203 | 设备数据采集 |
+
+## Build Commands
+
+```bash
+# 先构建 ems-core(必须)
+mvn clean install -pl ems-core
+
+# 构建微服务
+mvn clean package -pl ems-cloud/ems-server,ems-cloud/ems-dev-adapter -am
+
+# 构建单体版
+mvn clean package -pl ems-application/ems-admin -am
+```
+
+Java 1.8 required. Tests skipped (`<skipTests>true</skipTests>`).
+
+## Key Conventions
+
+- **Base package**: `com.ruoyi.ems` (业务), `com.ruoyi` (单体入口)
+- **MyBatis**: XML mappers 在 `ems-core/src/main/resources/mapper/ems/`
+- **DB**: MySQL + Druid; 数据源配置在 `application-druid.yml` (单体) 或 Nacos (微服务)
+- **Nacos**: `192.168.20.210:8848`, namespace `2c519718-1e09-4de4-9af5-d07f837372d6`
+- **Context paths**: ems-server `/ems`, ems-dev-adapter `/ems-dev-adapter`
+
+## SQL Files
+
+无迁移框架,手动执行:
+
+| 文件 | 用途 |
+|------|------|
+| `ems_sys_data.sql` | 若依系统表初始化数据 |
+| `ems_server.sql` | 能源管理业务表结构 (DDL) |
+| `ems_init_data_ctfwq.sql` | 服务区落地初始化数据(能源分类、设备、物模型) |
+| `ems_clear_data.sql` | 数据清理 |
+| `ca.sql` | 碳管理相关表 |
+
+## Docker
+
+`docker/cloud/docker-compose.yml` 部署完整微服务栈。镜像需单独构建。

+ 331 - 18
ems/ems-cloud/ems-dev-adapter/src/main/java/com/ruoyi/ems/gcc/handle/GccHandler.java

@@ -15,6 +15,7 @@ import com.alibaba.fastjson2.JSONObject;
 import com.huashe.common.utils.DateUtils;
 import com.ruoyi.ems.common.handle.BaseDevHandler;
 import com.ruoyi.ems.common.mqtt.MqttMessageHandler;
+import com.ruoyi.ems.common.mqtt.MqttTemplate;
 import com.ruoyi.ems.domain.ElecStoreH;
 import com.ruoyi.ems.domain.EmsDevice;
 import com.ruoyi.ems.domain.EmsObjAttrValue;
@@ -37,6 +38,8 @@ import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.nio.charset.StandardCharsets;
 import java.sql.Time;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Date;
@@ -44,6 +47,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -83,6 +87,54 @@ public class GccHandler extends BaseDevHandler implements MqttMessageHandler {
     private static final String READING_FWD = "GCC_STORE_READING_FWD";
     private static final String READING_REV = "GCC_STORE_READING_REV";
 
+    /**
+     * 储能一体机BMS累计充放电缓存字段(redis hash field)
+     */
+    private static final String READING_CUM_C = "GCC_YTJ_CUM_CHARGE";
+    private static final String READING_CUM_D = "GCC_YTJ_CUM_DISCHARGE";
+
+    /**
+     * 当日零点基线快照key前缀(按日)
+     */
+    private static final String DAY_BASE_PREFIX = "GCC_DAY_BASE_";
+    private static final DateTimeFormatter DAY_FMT = DateTimeFormatter.ofPattern("yyyyMMdd");
+
+    /**
+     * 策略下发能力key
+     */
+    public static final String ABILITY_APPLY_DEFAULT_STRATEGY = "applyDefaultStrategy";
+    public static final String ABILITY_APPLY_STRATEGY_IDLE = "applyStrategyIdle";
+
+    /**
+     * 下行/应答topic后缀
+     */
+    private static final String TOPIC_SUFFIX_COMMAND = "/command";
+    private static final String TOPIC_SUFFIX_COMMAND_RESPONSE = "/commandResponse";
+
+    /**
+     * 策略时段保守功率预置值(kW,待厂家确认PCS额定功率后调整)
+     */
+    private static final int SLOT_POWER_LIMIT_KW = 100;
+
+    /**
+     * 默认策略时段(厂家描述:02-06谷时充电、11-13谷时补电;光伏随动时段协议无字段暂禁止)
+     * 字段:{峰谷选择, 开始小时, 开始分钟, 结束小时, 结束分钟, 充电最大功率kW, 放电最大功率kW}
+     */
+    private static final int[][] DEFAULT_STRATEGY_SLOTS = {
+        { 4, 2, 0, 6, 0, SLOT_POWER_LIMIT_KW, 0 },
+        { 4, 11, 0, 13, 0, SLOT_POWER_LIMIT_KW, 0 }
+    };
+
+    /**
+     * 全静置策略:无有效时段(24时段全部禁止)
+     */
+    private static final int[][] IDLE_STRATEGY_SLOTS = {};
+
+    /**
+     * MQTT操作模板(由订阅器建连后绑定,用于策略下发)
+     */
+    private volatile MqttTemplate mqttTemplate;
+
     private static final Pattern SUFFIX_PATTERN = Pattern.compile("(\\d+)$");
 
     /**
@@ -272,8 +324,33 @@ public class GccHandler extends BaseDevHandler implements MqttMessageHandler {
         }
     }
 
+    /**
+     * 绑定MQTT操作模板(订阅器建连成功后调用)
+     *
+     * @param template MQTT模板
+     */
+    public void bindMqttTemplate(MqttTemplate template) {
+        this.mqttTemplate = template;
+    }
+
+    /**
+     * 获取全部站点(策略下发用)
+     *
+     * @return 站点列表
+     */
+    public List<GccSite> getSites() {
+        return sites;
+    }
+
     @Override
     public void handle(String topic, byte[] payload) {
+        // 策略下发应答:当前阶段仅记录日志,不走遥测解析链路
+        if (StringUtils.endsWith(topic, TOPIC_SUFFIX_COMMAND_RESPONSE)) {
+            log.info("储能策略下发应答, topic[{}], payload[{}]", topic,
+                new String(payload, StandardCharsets.UTF_8));
+            return;
+        }
+
         try {
             JSONObject root = JSON.parseObject(new String(payload, StandardCharsets.UTF_8));
             JSONObject device = root.getJSONObject("device");
@@ -373,6 +450,11 @@ public class GccHandler extends BaseDevHandler implements MqttMessageHandler {
         Map<String, String> attrs = new HashMap<>();
         Map<Integer, FieldDef> indexFields = StringUtils.equals(SERVICE_PCS, serviceCode) ? PCS_FIELDS : BMS_FIELDS;
 
+        // BMS报文:缓存累计充放电原始值,供当日基线计算
+        if (StringUtils.equals(SERVICE_BMS, serviceCode)) {
+            cacheYtjCum(siteDevice.getDeviceCode(), data);
+        }
+
         if (StringUtils.equals(SERVICE_FAULT, serviceCode)) {
             for (Map.Entry<String, FieldDef> entry : FAULT_FIELDS.entrySet()) {
                 String value = convert(data, entry.getKey(), entry.getValue(), 1, 1);
@@ -539,6 +621,30 @@ public class GccHandler extends BaseDevHandler implements MqttMessageHandler {
     }
 
     /**
+     * 缓存储能一体机BMS累计充放电值(info_7/info_8)
+     *
+     * @param deviceCode 设备代码
+     * @param data       数据体
+     */
+    private void cacheYtjCum(String deviceCode, JSONObject data) {
+        try {
+            BigDecimal cumC = data.getBigDecimal("info_7");
+            BigDecimal cumD = data.getBigDecimal("info_8");
+
+            if (null != cumC) {
+                updateCacheAfterSuccess(deviceCode, READING_CUM_C, cumC.toPlainString());
+            }
+
+            if (null != cumD) {
+                updateCacheAfterSuccess(deviceCode, READING_CUM_D, cumD.toPlainString());
+            }
+        }
+        catch (Exception e) {
+            log.error("缓存储能一体机累计充放电值失败 device[{}]", deviceCode, e);
+        }
+    }
+
+    /**
      * 储能计量小时产出:按电表累计抄表值计算上一小时充放电增量,按设施汇总写入 adm_ems_elec_store_h
      *
      * @return 产出条数
@@ -682,32 +788,146 @@ public class GccHandler extends BaseDevHandler implements MqttMessageHandler {
     }
 
     /**
-     * 周期落库属性值
+     * 周期落库属性值,并刷新当日充放电增量(当前累计值 - 当日零点基线)
      */
     @Scheduled(fixedDelayString = "${gcc.mqtt.save-interval-seconds:10}000")
     public void flushPending() {
-        if (pendingAttrs.isEmpty()) {
-            return;
+        if (!pendingAttrs.isEmpty()) {
+            Map<String, Map<String, String>> snapshot = new HashMap<>(pendingAttrs);
+            pendingAttrs.clear();
+
+            for (Map.Entry<String, Map<String, String>> entry : snapshot.entrySet()) {
+                String[] keys = entry.getKey().split(":");
+                String modelCode = keys[0];
+                String objCode = keys[1];
+
+                for (Map.Entry<String, String> attr : entry.getValue().entrySet()) {
+                    try {
+                        objAttrValueService.mergeObjAttrValue(
+                            new EmsObjAttrValue(objCode, modelCode, attr.getKey(), attr.getValue()));
+                    }
+                    catch (Exception e) {
+                        log.error("储能属性值落库失败 obj[{}] key[{}]", objCode, attr.getKey(), e);
+                    }
+                }
+            }
         }
 
-        Map<String, Map<String, String>> snapshot = new HashMap<>(pendingAttrs);
-        pendingAttrs.clear();
+        refreshDayQuantity();
+    }
+
+    /**
+     * 刷新当日充放电增量:电表当日正向/反向电能、一体机当日充电量/放电量,
+     * 计算口径 = 当前累计值 - 当日零点基线快照,结果写入物模型属性值(下一周期落库)
+     */
+    private void refreshDayQuantity() {
+        try {
+            List<EmsDevice> meters = getDeviceListByModel(MODEL_DB);
+            List<EmsDevice> ytjs = getDeviceListByModel(MODEL_YTJ);
+            if (CollectionUtils.isEmpty(meters) && CollectionUtils.isEmpty(ytjs)) {
+                return;
+            }
+
+            String baseKey = ensureDayBase(meters, ytjs);
 
-        for (Map.Entry<String, Map<String, String>> entry : snapshot.entrySet()) {
-            String[] keys = entry.getKey().split(":");
-            String modelCode = keys[0];
-            String objCode = keys[1];
+            for (EmsDevice meter : meters) {
+                putDayDelta(baseKey, meter.getDeviceCode(), MODEL_DB, READING_FWD, "dayFwdEnergy");
+                putDayDelta(baseKey, meter.getDeviceCode(), MODEL_DB, READING_REV, "dayRevEnergy");
+            }
 
-            for (Map.Entry<String, String> attr : entry.getValue().entrySet()) {
-                try {
-                    objAttrValueService.mergeObjAttrValue(
-                        new EmsObjAttrValue(objCode, modelCode, attr.getKey(), attr.getValue()));
-                }
-                catch (Exception e) {
-                    log.error("储能属性值落库失败 obj[{}] key[{}]", objCode, attr.getKey(), e);
-                }
+            for (EmsDevice ytj : ytjs) {
+                putDayDelta(baseKey, ytj.getDeviceCode(), MODEL_YTJ, READING_CUM_C, "dayChargeKwh");
+                putDayDelta(baseKey, ytj.getDeviceCode(), MODEL_YTJ, READING_CUM_D, "dayDischargeKwh");
             }
         }
+        catch (Exception e) {
+            log.error("刷新储能当日充放电增量失败", e);
+        }
+    }
+
+    /**
+     * 确保当日零点基线快照存在(缺失时以当前累计值拍照,当日增量从拍照时刻起算)
+     *
+     * @param meters 储能电表列表
+     * @param ytjs   储能一体机列表
+     * @return 基线快照redis key
+     */
+    private String ensureDayBase(List<EmsDevice> meters, List<EmsDevice> ytjs) {
+        String baseKey = DAY_BASE_PREFIX + LocalDate.now().format(DAY_FMT);
+
+        if (Boolean.TRUE.equals(redisService.hasKey(baseKey))) {
+            return baseKey;
+        }
+
+        synchronized (this) {
+            if (Boolean.TRUE.equals(redisService.hasKey(baseKey))) {
+                return baseKey;
+            }
+
+            for (EmsDevice meter : meters) {
+                snapshotReading(baseKey, meter.getDeviceCode(), READING_FWD);
+                snapshotReading(baseKey, meter.getDeviceCode(), READING_REV);
+            }
+
+            for (EmsDevice ytj : ytjs) {
+                snapshotReading(baseKey, ytj.getDeviceCode(), READING_CUM_C);
+                snapshotReading(baseKey, ytj.getDeviceCode(), READING_CUM_D);
+            }
+
+            redisService.expire(baseKey, 2, TimeUnit.DAYS);
+            log.info("储能当日基线快照已建立 baseKey[{}]", baseKey);
+        }
+
+        return baseKey;
+    }
+
+    /**
+     * 快照单设备累计读数到当日基线
+     *
+     * @param baseKey    基线key
+     * @param deviceCode 设备代码
+     * @param field      累计值缓存字段
+     */
+    private void snapshotReading(String baseKey, String deviceCode, String field) {
+        String value = redisService.getCacheMapValue(deviceCode, field);
+        if (StringUtils.isNotEmpty(value)) {
+            redisService.setCacheMapValue(baseKey, deviceCode + ":" + field, value);
+        }
+    }
+
+    /**
+     * 计算单设备当日增量并放入待落库队列(负值视为表计复位,重置基线并从0起算)
+     *
+     * @param baseKey      基线key
+     * @param deviceCode   设备代码
+     * @param modelCode    设备模型
+     * @param readingField 累计值缓存字段
+     * @param attrKey      当日增量属性key
+     */
+    private void putDayDelta(String baseKey, String deviceCode, String modelCode, String readingField,
+        String attrKey) {
+        String current = redisService.getCacheMapValue(deviceCode, readingField);
+        String base = redisService.getCacheMapValue(baseKey, deviceCode + ":" + readingField);
+        if (StringUtils.isEmpty(current) || StringUtils.isEmpty(base)) {
+            return;
+        }
+
+        try {
+            double delta = Double.parseDouble(current) - Double.parseDouble(base);
+
+            if (delta < 0) {
+                log.warn("储能当日增量为负(表计复位/基线异常),重置基线 device[{}] field[{}] base[{}] current[{}]",
+                    deviceCode, readingField, base, current);
+                redisService.setCacheMapValue(baseKey, deviceCode + ":" + readingField, current);
+                delta = 0;
+            }
+
+            pendingAttrs.computeIfAbsent(modelCode + ":" + deviceCode, k -> new ConcurrentHashMap<>()).put(attrKey,
+                BigDecimal.valueOf(delta).setScale(2, RoundingMode.HALF_UP).toPlainString());
+        }
+        catch (NumberFormatException e) {
+            log.warn("储能累计值解析失败 device[{}] field[{}]", deviceCode, readingField);
+        }
     }
 
     /**
@@ -815,9 +1035,102 @@ public class GccHandler extends BaseDevHandler implements MqttMessageHandler {
         return matcher.find() ? matcher.group(1) : dataCode;
     }
 
+    /**
+     * 能力调用:策略下发(当前支持默认策略/全静置策略)
+     * 链路:前端 -> ems-server能力转发 -> 本方法 -> 逐站点发布command报文 -> EMS应答commandResponse(记日志)
+     *
+     * @param abilityParam 能力参数
+     * @return 调用结果
+     */
     @Override
     public CallResponse<Void> call(AbilityPayload abilityParam) {
-        return new CallResponse<>(-1, "储能系统为订阅采集模式,暂不支持能力下发");
+        if (null == mqttTemplate) {
+            return new CallResponse<>(-1, "储能MQTT通道未就绪");
+        }
+
+        String abilityKey = abilityParam.getAbilityKey();
+        int[][] slots;
+        if (StringUtils.equals(ABILITY_APPLY_DEFAULT_STRATEGY, abilityKey)) {
+            slots = DEFAULT_STRATEGY_SLOTS;
+        }
+        else if (StringUtils.equals(ABILITY_APPLY_STRATEGY_IDLE, abilityKey)) {
+            slots = IDLE_STRATEGY_SLOTS;
+        }
+        else {
+            return new CallResponse<>(-1, "不支持的能力: " + abilityKey);
+        }
+
+        if (CollectionUtils.isEmpty(sites)) {
+            return new CallResponse<>(-1, "储能站点配置未加载");
+        }
+
+        String payloadJson = buildStrategyPayload(sites.get(0).getDeviceId(), slots);
+        int cnt = 0;
+
+        // 逐站点下发(报文仅deviceId不同,其余一致)
+        for (GccSite site : sites) {
+            if (StringUtils.isEmpty(site.getDeviceId())) {
+                continue;
+            }
+
+            String topic = "/v1/devices/" + site.getDeviceId() + TOPIC_SUFFIX_COMMAND;
+            String msg = buildStrategyPayload(site.getDeviceId(), slots);
+            log.info("储能策略下发, 能力[{}], 站点[{}], topic[{}], payload[{}]", abilityKey, site.getSiteName(),
+                topic, msg);
+            mqttTemplate.sendString(topic, msg, 0, false);
+            cnt++;
+        }
+
+        // 记录能力调用日志(应答异步到达,仅记录下发结果)
+        saveCallLog(abilityParam.getObjCode(), abilityParam.getModelCode(), abilityKey, 0, payloadJson,
+            "已下发" + cnt + "个站点,等待EMS应答");
+        return new CallResponse<>(0, "策略已下发" + cnt + "个站点,等待EMS应答");
+    }
+
+    /**
+     * 构建策略报文(平台下行sheet:serviceCode=strategy, code=strategy01, 24时段×5字段)
+     * 时间编码:小时×256+分钟;未定义时段峰谷选择=0(禁止)
+     *
+     * @param deviceId 站点客户端ID
+     * @param slots    有效时段定义(最多24组)
+     * @return 报文JSON字符串
+     */
+    private String buildStrategyPayload(String deviceId, int[][] slots) {
+        JSONObject data = new JSONObject();
+        data.put("code", "strategy01");
+        // info_1 保留
+        data.put("info_1", 0);
+
+        for (int n = 1; n <= 24; n++) {
+            int base = 2 + (n - 1) * 5;
+            int[] slot = (n <= slots.length) ? slots[n - 1] : null;
+
+            if (null == slot) {
+                // 时段禁止:全部填0
+                for (int i = 0; i < 5; i++) {
+                    data.put("info_" + (base + i), 0);
+                }
+            }
+            else {
+                data.put("info_" + base, slot[0]);
+                data.put("info_" + (base + 1), slot[1] * 256 + slot[2]);
+                data.put("info_" + (base + 2), slot[3] * 256 + slot[4]);
+                data.put("info_" + (base + 3), slot[5]);
+                data.put("info_" + (base + 4), slot[6]);
+            }
+        }
+
+        JSONObject service = new JSONObject();
+        service.put("serviceCode", "strategy");
+        service.put("data", data);
+
+        JSONObject device = new JSONObject();
+        device.put("deviceId", deviceId);
+        device.put("service", service);
+
+        JSONObject root = new JSONObject();
+        root.put("device", device);
+        return root.toJSONString();
     }
 
     @Override

+ 4 - 0
ems/ems-cloud/ems-dev-adapter/src/main/java/com/ruoyi/ems/gcc/handle/GccSubscriber.java

@@ -116,6 +116,8 @@ public class GccSubscriber implements ApplicationRunner {
         // 3. 连接并订阅
         MqttTemplate template = new MqttTemplate(property.getHost(), property.getClientId(), property.getUserName(),
             property.getPassword());
+        // 绑定到handler,供策略下发使用
+        gccHandler.bindMqttTemplate(template);
 
         for (GccSite site : sites) {
             if (StringUtils.isEmpty(site.getTopic())) {
@@ -123,6 +125,8 @@ public class GccSubscriber implements ApplicationRunner {
             }
 
             template.subscribe(site.getTopic(), 0, gccHandler);
+            // 策略下发应答主题(仅记日志)
+            template.subscribe("/v1/devices/" + site.getDeviceId() + "/commandResponse", 0, gccHandler);
             log.info("储能站点订阅成功: {} -> {}", site.getSiteName(), site.getTopic());
         }
 

+ 15 - 0
ems/ems-core/src/main/java/com/ruoyi/ems/service/impl/EmsObjAbilityCallLogServiceImpl.java

@@ -3,9 +3,13 @@ package com.ruoyi.ems.service.impl;
 import com.ruoyi.ems.domain.EmsObjAbilityCallLog;
 import com.ruoyi.ems.mapper.EmsObjAbilityCallLogMapper;
 import com.ruoyi.ems.service.IEmsObjAbilityCallLogService;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
 import java.util.List;
 
 /**
@@ -26,6 +30,17 @@ public class EmsObjAbilityCallLogServiceImpl implements IEmsObjAbilityCallLogSer
 
     @Override
     public List<EmsObjAbilityCallLog> selectLogList(EmsObjAbilityCallLog param) {
+        // 防御:日志表数据量大,强制时间窗口,未传时默认查最近24小时,避免全表扫描超时
+        if (StringUtils.isEmpty(param.getStartRecTime()) || StringUtils.isEmpty(param.getEndRecTime())) {
+            SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+            Date now = new Date();
+            Calendar cal = Calendar.getInstance();
+            cal.setTime(now);
+            cal.add(Calendar.DAY_OF_MONTH, -1);
+            param.setStartRecTime(fmt.format(cal.getTime()));
+            param.setEndRecTime(fmt.format(now));
+        }
+
         return objAbilityCallLogMapper.selectObjAbilityCallLog(param);
     }
 

+ 7 - 2
ems/sql/ems_init_data_ctfwq.sql

@@ -783,6 +783,8 @@ INSERT INTO `adm_ems_obj_attr` (`model_code`, `attr_group`, `attr_key`, `attr_na
 INSERT INTO `adm_ems_obj_attr` (`model_code`, `attr_group`, `attr_key`, `attr_name`, `attr_unit`, `attr_value_type`) VALUES ('M_W4_DEV_GCC_DB', 'Measure', 'emRevValleyEnergy', '反向有功谷电能', 'kWh', 'Value');
 INSERT INTO `adm_ems_obj_attr` (`model_code`, `attr_group`, `attr_key`, `attr_name`, `attr_unit`, `attr_value_type`) VALUES ('M_W4_DEV_GCC_DB', 'Measure', 'emPtRatio', '电压变比', NULL, 'Value');
 INSERT INTO `adm_ems_obj_attr` (`model_code`, `attr_group`, `attr_key`, `attr_name`, `attr_unit`, `attr_value_type`) VALUES ('M_W4_DEV_GCC_DB', 'Measure', 'emCtRatio', '电流变比', NULL, 'Value');
+INSERT INTO `adm_ems_obj_attr` (`model_code`, `attr_group`, `attr_key`, `attr_name`, `attr_unit`, `attr_value_type`) VALUES ('M_W4_DEV_GCC_DB', 'Measure', 'dayFwdEnergy', '当日充电电能', 'kWh', 'Value');
+INSERT INTO `adm_ems_obj_attr` (`model_code`, `attr_group`, `attr_key`, `attr_name`, `attr_unit`, `attr_value_type`) VALUES ('M_W4_DEV_GCC_DB', 'Measure', 'dayRevEnergy', '当日放电电能', 'kWh', 'Value');
 
 -- ---------------------------- 储能一体机模型属性-PCS(serviceCode=pcs) ----------------------------
 INSERT INTO `adm_ems_obj_attr` (`model_code`, `attr_group`, `attr_key`, `attr_name`, `attr_unit`, `attr_value_type`) VALUES ('M_W4_DEV_GCC_YTJ', 'Base', 'siteId', '所属接入点', NULL, 'String');
@@ -840,6 +842,8 @@ INSERT INTO `adm_ems_obj_attr` (`model_code`, `attr_group`, `attr_key`, `attr_na
 INSERT INTO `adm_ems_obj_attr` (`model_code`, `attr_group`, `attr_key`, `attr_name`, `attr_unit`, `attr_value_type`) VALUES ('M_W4_DEV_GCC_YTJ', 'BMS', 'bmsMinCellV', '最低单体电压', 'V', 'Value');
 INSERT INTO `adm_ems_obj_attr` (`model_code`, `attr_group`, `attr_key`, `attr_name`, `attr_unit`, `attr_value_type`) VALUES ('M_W4_DEV_GCC_YTJ', 'BMS', 'bmsMinCellVModule', '最低单体电压所在模块号', NULL, 'Value');
 INSERT INTO `adm_ems_obj_attr` (`model_code`, `attr_group`, `attr_key`, `attr_name`, `attr_unit`, `attr_value_type`) VALUES ('M_W4_DEV_GCC_YTJ', 'BMS', 'bmsMinCellVIndex', '最低单体电压节号', NULL, 'Value');
+INSERT INTO `adm_ems_obj_attr` (`model_code`, `attr_group`, `attr_key`, `attr_name`, `attr_unit`, `attr_value_type`) VALUES ('M_W4_DEV_GCC_YTJ', 'BMS', 'dayChargeKwh', '当日充电量', 'kWh', 'Value');
+INSERT INTO `adm_ems_obj_attr` (`model_code`, `attr_group`, `attr_key`, `attr_name`, `attr_unit`, `attr_value_type`) VALUES ('M_W4_DEV_GCC_YTJ', 'BMS', 'dayDischargeKwh', '当日放电量', 'kWh', 'Value');
 
 -- ---------------------------- 储能一体机模型属性-故障(serviceCode=fault) ----------------------------
 INSERT INTO `adm_ems_obj_attr` (`model_code`, `attr_group`, `attr_key`, `attr_name`, `attr_unit`, `attr_value_type`) VALUES ('M_W4_DEV_GCC_YTJ', 'Fault', 'faultPcs1', 'PCS故障字1', NULL, 'Value');
@@ -1924,6 +1928,8 @@ INSERT INTO `adm_ems_obj_ability` (`model_code`, `ability_key`, `ability_name`,
 INSERT INTO `adm_ems_obj_ability` (`model_code`, `ability_key`, `ability_name`, `ability_desc`, `param_definition`, `hidden_flag`) VALUES ('M_W2_DEV_PHOTOVOLTAIC_COL', 'SyncAttr', '同步属性', '从API同步当前采集器的属性数据', null, 1);
 INSERT INTO `adm_ems_obj_ability` (`model_code`, `ability_key`, `ability_name`, `ability_desc`, `param_definition`, `hidden_flag`) VALUES ('M_E5_DEV_PHOTOVOLTAIC_INVERTER', 'SyncAttr', '同步属性', '从API同步当前逆变器的实时数据', null, 1);
 
+INSERT INTO `adm_ems_obj_ability` (`model_code`, `ability_key`, `ability_name`, `ability_desc`, `param_definition`, `hidden_flag`) VALUES ('M_W4_SYS_GCC', 'applyDefaultStrategy', '执行默认策略', '下发厂家默认时段策略:02-06谷时充电、11-13谷时补电(功率限制100kW),其余时段禁止;仅下发strategy01,不触碰系统参数', NULL, 1);
+INSERT INTO `adm_ems_obj_ability` (`model_code`, `ability_key`, `ability_name`, `ability_desc`, `param_definition`, `hidden_flag`) VALUES ('M_W4_SYS_GCC', 'applyStrategyIdle', '执行全静置策略', '全部24时段设为禁止,下发后EMS自动模式不再触发充放电;慎用:会覆盖厂家侧现行策略', NULL, 1);
 
 -- 对象事件数据
 INSERT INTO `adm_ems_obj_event` (`model_code`, `event_key`, `event_type`, `event_name`, `event_desc`, `event_code`, `ext_event_code`) VALUES ('M_W4_SYS_BA', 'connect', 1, '连接恢复', '连接正常', 'M_W4_SYS_BA_00', NULL);
@@ -2004,7 +2010,6 @@ INSERT INTO adm_elecprice_strategy_hour (`strategy_code`, `start_time`, `end_tim
 INSERT INTO adm_elecprice_strategy_hour (`strategy_code`, `start_time`, `end_time`, `type`) VALUES ('1002', '22:00:00', '23:59:59', 0);
 
 
-
 -- 非电能源价格
 INSERT INTO `adm_fd_energyprice_config` (`cfg_code`, `area_code`, `meter_cls`, `unit_price`, `meter_unit`) VALUES ('0000', '-1', 70, 4.1, '吨');
 INSERT INTO `adm_fd_energyprice_config` (`cfg_code`, `area_code`, `meter_cls`, `unit_price`, `meter_unit`) VALUES ('0001', '321283124S3001', 70, 4.1, '吨');
@@ -2499,7 +2504,7 @@ INSERT INTO `adm_ems_subsystem` (`system_code`, `system_name`, `short_name`, `mo
 INSERT INTO `adm_ems_subsystem` (`system_code`, `system_name`, `short_name`, `model_code`, `man_facturer`, `contact_person`, `contact_number`, `maintainer_person`, `maintainer_number`, `descr`) VALUES ('SYS_DLJK', '电力监控系统', '电力监控', 'M_W4_SYS_ELEC_MONITOR', '安科瑞', 'gzl', '1212121121', 'gzl', '1212221111', NULL);
 INSERT INTO `adm_ems_subsystem` (`system_code`, `system_name`, `short_name`, `model_code`, `man_facturer`, `contact_person`, `contact_number`, `maintainer_person`, `maintainer_number`, `descr`) VALUES ('SYS_GF', '光伏系统', '光伏', 'M_E5_SYS_PHOTOVOLTAIC', '古瑞瓦特', '张', '1380000', '李工', '123123', NULL);
 INSERT INTO `adm_ems_subsystem` (`system_code`, `system_name`, `short_name`, `model_code`, `man_facturer`, `contact_person`, `contact_number`, `maintainer_person`, `maintainer_number`, `descr`) VALUES ('SYS_CD', '充电桩系统', '充电桩', 'M_W2_SYS_CHARGING', '科士达', NULL, NULL, NULL, NULL, NULL);
-INSERT INTO `adm_ems_subsystem` (`system_code`, `system_name`, `short_name`, `model_code`, `man_facturer`, `contact_person`, `contact_number`, `maintainer_person`, `maintainer_number`, `descr`) VALUES ('SYS_GCC', '光储充系统', '光储充', 'M_W4_SYS_GCC', '城市动力', '安磊', '-', '安磊', '-', NULL);
+INSERT INTO `adm_ems_subsystem` (`system_code`, `system_name`, `short_name`, `model_code`, `man_facturer`, `contact_person`, `contact_number`, `maintainer_person`, `maintainer_number`, `descr`) VALUES ('SYS_GCC', '储能系统', '储能', 'M_W4_SYS_GCC', '城市动力', '安磊', '-', '安磊', '-', NULL);
 
 -- 告警策略
 -- 通用设备离线规则

+ 1 - 1
ems/sql/ems_sys_data.sql

@@ -82,7 +82,7 @@ insert into sys_menu values ('1552',  '智慧照明',       '155',   '3',  'adap
 insert into sys_menu values ('1553',  '充电桩',         '155',   '4',  'adapter-cdz',        'adapter/cdz/index',     '', 1, 0, 'C', '0', '0',   'adapter:devc:list',     'powerstore',    'admin', sysdate(), '', null, '充电桩');
 insert into sys_menu values ('1554',  '光伏监测',       '155',   '5',  'adapter-pv',         'adapter/pv/index',      '', 1, 0, 'C', '0', '0',   'adapter:devc:list',     'photovoltaic',   'admin', sysdate(), '', null, '光伏');
 insert into sys_menu values ('1555',  '光储直柔',       '155',   '6',  'adapter-gczr',       'adapter/gczr/index',    '', 1, 0, 'C', '0', '0',   'adapter:devc:list',     'deviceaccess',  'admin', sysdate(), '', null, '光储直柔');
-insert into sys_menu values ('1556',  '光储充',         '155',   '7',  'adapter-gcc',        'adapter/gcc/index',     '', 1, 0, 'C', '0', '0',   'adapter:devc:list',     'energyconsume', 'admin', sysdate(), '', null, '光储充');
+insert into sys_menu values ('1556',  '储能监测',       '155',   '7',  'adapter-gcc',        'adapter/gcc/index',     '', 1, 0, 'C', '0', '0',   'adapter:devc:list',     'energyconsume', 'admin', sysdate(), '', null, '光储充');
 insert into sys_menu values ('1557',  '智慧海绵',       '155',   '8',  'adapter-hm',         'adapter/hm/index',      '', 1, 0, 'C', '0', '0',   'adapter:devc:list',      'system',        'admin', sysdate(), '', null, '智慧海绵');
 insert into sys_menu values ('1558',  '垃圾厨余',       '155',   '9',  'adapter-ljcy',       'adapter/ljcy/index',    '', 1, 0, 'C', '0', '0',   'adapter:devc:list',      'system',        'admin', sysdate(), '', null, '垃圾厨余');
 insert into sys_menu values ('1559',  '电力监控',       '155',   '10',  'adapter-dljk',      'adapter/dljk/index',    '', 1, 0, 'C', '0', '0',   'adapter:devc:list',     'system',        'admin', sysdate(), '', null, '电力监控');