dev #4
@ -15,6 +15,7 @@ import com.xzzn.ems.domain.vo.PointDataRequest;
|
|||||||
import com.xzzn.ems.domain.vo.PointQueryResponse;
|
import com.xzzn.ems.domain.vo.PointQueryResponse;
|
||||||
import com.xzzn.ems.domain.vo.SiteMonitorProjectPointMappingSaveRequest;
|
import com.xzzn.ems.domain.vo.SiteMonitorProjectPointMappingSaveRequest;
|
||||||
import com.xzzn.ems.domain.vo.SiteDeviceListVo;
|
import com.xzzn.ems.domain.vo.SiteDeviceListVo;
|
||||||
|
import com.xzzn.ems.domain.vo.WorkStatusEnumMappingSaveRequest;
|
||||||
import com.xzzn.ems.service.IEmsDeviceSettingService;
|
import com.xzzn.ems.service.IEmsDeviceSettingService;
|
||||||
import com.xzzn.ems.service.IEmsSiteService;
|
import com.xzzn.ems.service.IEmsSiteService;
|
||||||
|
|
||||||
@ -232,6 +233,25 @@ public class EmsSiteConfigController extends BaseController{
|
|||||||
return AjaxResult.success(rows);
|
return AjaxResult.success(rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取单站监控工作状态枚举映射(PCS)
|
||||||
|
*/
|
||||||
|
@GetMapping("/getSingleMonitorWorkStatusEnumMappings")
|
||||||
|
public AjaxResult getSingleMonitorWorkStatusEnumMappings(@RequestParam String siteId)
|
||||||
|
{
|
||||||
|
return success(iEmsDeviceSettingService.getSiteWorkStatusEnumMappings(siteId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存单站监控工作状态枚举映射(PCS)
|
||||||
|
*/
|
||||||
|
@PostMapping("/saveSingleMonitorWorkStatusEnumMappings")
|
||||||
|
public AjaxResult saveSingleMonitorWorkStatusEnumMappings(@RequestBody WorkStatusEnumMappingSaveRequest request)
|
||||||
|
{
|
||||||
|
int rows = iEmsDeviceSettingService.saveSiteWorkStatusEnumMappings(request.getSiteId(), request.getMappings(), getUsername());
|
||||||
|
return AjaxResult.success(rows);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PCS设备开关机
|
* PCS设备开关机
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -10,10 +10,13 @@ import com.xzzn.ems.domain.vo.DateSearchRequest;
|
|||||||
import com.xzzn.ems.domain.vo.RunningGraphRequest;
|
import com.xzzn.ems.domain.vo.RunningGraphRequest;
|
||||||
import com.xzzn.ems.domain.vo.SiteBatteryDataList;
|
import com.xzzn.ems.domain.vo.SiteBatteryDataList;
|
||||||
import com.xzzn.ems.domain.vo.SiteMonitorDataSaveRequest;
|
import com.xzzn.ems.domain.vo.SiteMonitorDataSaveRequest;
|
||||||
|
import com.xzzn.ems.domain.vo.SiteMonitorRuningInfoVo;
|
||||||
import com.xzzn.ems.service.IEmsDeviceSettingService;
|
import com.xzzn.ems.service.IEmsDeviceSettingService;
|
||||||
import com.xzzn.ems.service.IEmsSiteService;
|
import com.xzzn.ems.service.IEmsSiteService;
|
||||||
import com.xzzn.ems.service.IEmsStatsReportService;
|
import com.xzzn.ems.service.IEmsStatsReportService;
|
||||||
import com.xzzn.ems.service.ISingleSiteService;
|
import com.xzzn.ems.service.ISingleSiteService;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
@ -28,6 +31,8 @@ import java.util.List;
|
|||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/ems/siteMonitor")
|
@RequestMapping("/ems/siteMonitor")
|
||||||
public class EmsSiteMonitorController extends BaseController{
|
public class EmsSiteMonitorController extends BaseController{
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(EmsSiteMonitorController.class);
|
||||||
|
private static final String RUNNING_GRAPH_CTRL_DEBUG = "RunningGraphCtrlDebug";
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISingleSiteService iSingleSiteService;
|
private ISingleSiteService iSingleSiteService;
|
||||||
@ -60,27 +65,75 @@ public class EmsSiteMonitorController extends BaseController{
|
|||||||
* 单站监控-设备监控-实时运行曲线图数据
|
* 单站监控-设备监控-实时运行曲线图数据
|
||||||
*/
|
*/
|
||||||
@GetMapping("/runningGraph/storagePower")
|
@GetMapping("/runningGraph/storagePower")
|
||||||
public AjaxResult getRunningGraphStorage(RunningGraphRequest request)
|
public AjaxResult getRunningGraphStorage(RunningGraphRequest request,
|
||||||
|
@RequestParam(required = false) String startDate,
|
||||||
|
@RequestParam(required = false) String endDate)
|
||||||
{
|
{
|
||||||
return success(iSingleSiteService.getRunningGraphStorage(request));
|
SiteMonitorRuningInfoVo data = iSingleSiteService.getRunningGraphStorage(request);
|
||||||
|
int deviceCount = data == null || data.getPcsPowerList() == null ? 0 : data.getPcsPowerList().size();
|
||||||
|
log.info("{} storage, siteId={}, rawStartDate={}, rawEndDate={}, bindStartDate={}, bindEndDate={}, deviceCount={}",
|
||||||
|
RUNNING_GRAPH_CTRL_DEBUG,
|
||||||
|
request == null ? null : request.getSiteId(),
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
request == null ? null : request.getStartDate(),
|
||||||
|
request == null ? null : request.getEndDate(),
|
||||||
|
deviceCount);
|
||||||
|
return success(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/runningGraph/pcsMaxTemp")
|
@GetMapping("/runningGraph/pcsMaxTemp")
|
||||||
public AjaxResult getRunningGraphPcsMaxTemp(RunningGraphRequest request)
|
public AjaxResult getRunningGraphPcsMaxTemp(RunningGraphRequest request,
|
||||||
|
@RequestParam(required = false) String startDate,
|
||||||
|
@RequestParam(required = false) String endDate)
|
||||||
{
|
{
|
||||||
return success(iSingleSiteService.getRunningGraphPcsMaxTemp(request));
|
SiteMonitorRuningInfoVo data = iSingleSiteService.getRunningGraphPcsMaxTemp(request);
|
||||||
|
int deviceCount = data == null || data.getPcsMaxTempList() == null ? 0 : data.getPcsMaxTempList().size();
|
||||||
|
log.info("{} pcsMaxTemp, siteId={}, rawStartDate={}, rawEndDate={}, bindStartDate={}, bindEndDate={}, deviceCount={}",
|
||||||
|
RUNNING_GRAPH_CTRL_DEBUG,
|
||||||
|
request == null ? null : request.getSiteId(),
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
request == null ? null : request.getStartDate(),
|
||||||
|
request == null ? null : request.getEndDate(),
|
||||||
|
deviceCount);
|
||||||
|
return success(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/runningGraph/batteryAveSoc")
|
@GetMapping("/runningGraph/batteryAveSoc")
|
||||||
public AjaxResult getRunningGraphBatterySoc(RunningGraphRequest request)
|
public AjaxResult getRunningGraphBatterySoc(RunningGraphRequest request,
|
||||||
|
@RequestParam(required = false) String startDate,
|
||||||
|
@RequestParam(required = false) String endDate)
|
||||||
{
|
{
|
||||||
return success(iSingleSiteService.getRunningGraphBatterySoc(request));
|
SiteMonitorRuningInfoVo data = iSingleSiteService.getRunningGraphBatterySoc(request);
|
||||||
|
int pointCount = data == null || data.getBatteryAveSOCList() == null ? 0 : data.getBatteryAveSOCList().size();
|
||||||
|
log.info("{} batteryAveSoc, siteId={}, rawStartDate={}, rawEndDate={}, bindStartDate={}, bindEndDate={}, pointCount={}",
|
||||||
|
RUNNING_GRAPH_CTRL_DEBUG,
|
||||||
|
request == null ? null : request.getSiteId(),
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
request == null ? null : request.getStartDate(),
|
||||||
|
request == null ? null : request.getEndDate(),
|
||||||
|
pointCount);
|
||||||
|
return success(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/runningGraph/batteryAveTemp")
|
@GetMapping("/runningGraph/batteryAveTemp")
|
||||||
public AjaxResult getRunningGraphBatteryTemp(RunningGraphRequest request)
|
public AjaxResult getRunningGraphBatteryTemp(RunningGraphRequest request,
|
||||||
|
@RequestParam(required = false) String startDate,
|
||||||
|
@RequestParam(required = false) String endDate)
|
||||||
{
|
{
|
||||||
return success(iSingleSiteService.getRunningGraphBatteryTemp(request));
|
SiteMonitorRuningInfoVo data = iSingleSiteService.getRunningGraphBatteryTemp(request);
|
||||||
|
int pointCount = data == null || data.getBatteryAveTempList() == null ? 0 : data.getBatteryAveTempList().size();
|
||||||
|
log.info("{} batteryAveTemp, siteId={}, rawStartDate={}, rawEndDate={}, bindStartDate={}, bindEndDate={}, pointCount={}",
|
||||||
|
RUNNING_GRAPH_CTRL_DEBUG,
|
||||||
|
request == null ? null : request.getSiteId(),
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
request == null ? null : request.getStartDate(),
|
||||||
|
request == null ? null : request.getEndDate(),
|
||||||
|
pointCount);
|
||||||
|
return success(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -128,4 +128,10 @@ public class RedisKeyConstants
|
|||||||
|
|
||||||
/** 单站监控最新数据(按站点+模块) */
|
/** 单站监控最新数据(按站点+模块) */
|
||||||
public static final String SITE_MONITOR_LATEST = "SITE_MONITOR_LATEST_";
|
public static final String SITE_MONITOR_LATEST = "SITE_MONITOR_LATEST_";
|
||||||
|
|
||||||
|
/** 单站监控点位映射(按站点) */
|
||||||
|
public static final String SITE_MONITOR_POINT_MATCH = "SITE_MONITOR_POINT_MATCH_";
|
||||||
|
|
||||||
|
/** 站点保护约束(按站点) */
|
||||||
|
public static final String PROTECTION_CONSTRAINT = "PROTECTION_CONSTRAINT_";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,40 +10,14 @@ import com.xzzn.common.core.redis.RedisCache;
|
|||||||
import com.xzzn.common.enums.AlarmLevelStatus;
|
import com.xzzn.common.enums.AlarmLevelStatus;
|
||||||
import com.xzzn.common.enums.AlarmStatus;
|
import com.xzzn.common.enums.AlarmStatus;
|
||||||
import com.xzzn.common.enums.ProtPlanStatus;
|
import com.xzzn.common.enums.ProtPlanStatus;
|
||||||
import com.xzzn.common.enums.StrategyStatus;
|
|
||||||
import com.xzzn.common.utils.StringUtils;
|
import com.xzzn.common.utils.StringUtils;
|
||||||
import com.xzzn.ems.domain.EmsAlarmRecords;
|
import com.xzzn.ems.domain.EmsAlarmRecords;
|
||||||
import com.xzzn.ems.domain.EmsDevicesSetting;
|
|
||||||
import com.xzzn.ems.domain.EmsFaultIssueLog;
|
|
||||||
import com.xzzn.ems.domain.EmsFaultProtectionPlan;
|
import com.xzzn.ems.domain.EmsFaultProtectionPlan;
|
||||||
import com.xzzn.ems.domain.EmsStrategyRunning;
|
import com.xzzn.ems.domain.vo.ProtectionConstraintVo;
|
||||||
import com.xzzn.ems.domain.vo.ProtectionPlanVo;
|
|
||||||
import com.xzzn.ems.domain.vo.ProtectionSettingVo;
|
import com.xzzn.ems.domain.vo.ProtectionSettingVo;
|
||||||
import com.xzzn.ems.mapper.EmsAlarmRecordsMapper;
|
import com.xzzn.ems.mapper.EmsAlarmRecordsMapper;
|
||||||
import com.xzzn.ems.mapper.EmsDevicesSettingMapper;
|
|
||||||
import com.xzzn.ems.mapper.EmsFaultIssueLogMapper;
|
|
||||||
import com.xzzn.ems.mapper.EmsFaultProtectionPlanMapper;
|
import com.xzzn.ems.mapper.EmsFaultProtectionPlanMapper;
|
||||||
import com.xzzn.ems.mapper.EmsStrategyRunningMapper;
|
|
||||||
import com.xzzn.ems.service.IEmsFaultProtectionPlanService;
|
import com.xzzn.ems.service.IEmsFaultProtectionPlanService;
|
||||||
import com.xzzn.common.core.modbus.ModbusProcessor;
|
|
||||||
import com.xzzn.common.core.modbus.domain.DeviceConfig;
|
|
||||||
import com.xzzn.common.core.modbus.domain.WriteTagConfig;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
import java.util.concurrent.ScheduledFuture;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
|
|
||||||
import org.apache.commons.collections4.CollectionUtils;
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -51,14 +25,29 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 告警保护方案轮询
|
* 告警保护方案轮询
|
||||||
*
|
|
||||||
* @author xzzn
|
|
||||||
*/
|
*/
|
||||||
@Component("protectionPlanTask")
|
@Component("protectionPlanTask")
|
||||||
public class ProtectionPlanTask {
|
public class ProtectionPlanTask {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(ProtectionPlanTask.class);
|
private static final Logger logger = LoggerFactory.getLogger(ProtectionPlanTask.class);
|
||||||
|
private static final BigDecimal DEFAULT_L1_POWER_RATIO = new BigDecimal("0.5");
|
||||||
|
private static final int CONSTRAINT_TTL_SECONDS = 120;
|
||||||
|
|
||||||
@Resource(name = "scheduledExecutorService")
|
@Resource(name = "scheduledExecutorService")
|
||||||
private ScheduledExecutorService scheduledExecutorService;
|
private ScheduledExecutorService scheduledExecutorService;
|
||||||
@Autowired
|
@Autowired
|
||||||
@ -66,18 +55,11 @@ public class ProtectionPlanTask {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private EmsAlarmRecordsMapper emsAlarmRecordsMapper;
|
private EmsAlarmRecordsMapper emsAlarmRecordsMapper;
|
||||||
@Autowired
|
@Autowired
|
||||||
private EmsStrategyRunningMapper emsStrategyRunningMapper;
|
|
||||||
@Autowired
|
|
||||||
private EmsFaultProtectionPlanMapper emsFaultProtectionPlanMapper;
|
private EmsFaultProtectionPlanMapper emsFaultProtectionPlanMapper;
|
||||||
@Autowired
|
@Autowired
|
||||||
private RedisCache redisCache;
|
private RedisCache redisCache;
|
||||||
|
|
||||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
@Autowired
|
|
||||||
private EmsDevicesSettingMapper emsDevicesSettingMapper;
|
|
||||||
@Autowired
|
|
||||||
private ModbusProcessor modbusProcessor;
|
|
||||||
@Autowired
|
|
||||||
private EmsFaultIssueLogMapper emsFaultIssueLogMapper;
|
|
||||||
|
|
||||||
public ProtectionPlanTask(IEmsFaultProtectionPlanService iEmsFaultProtectionPlanService) {
|
public ProtectionPlanTask(IEmsFaultProtectionPlanService iEmsFaultProtectionPlanService) {
|
||||||
this.iEmsFaultProtectionPlanService = iEmsFaultProtectionPlanService;
|
this.iEmsFaultProtectionPlanService = iEmsFaultProtectionPlanService;
|
||||||
@ -86,303 +68,320 @@ public class ProtectionPlanTask {
|
|||||||
public void pollPlanList() {
|
public void pollPlanList() {
|
||||||
Long planId = 0L;
|
Long planId = 0L;
|
||||||
try {
|
try {
|
||||||
// 获取所有方案,轮询
|
|
||||||
List<EmsFaultProtectionPlan> planList = iEmsFaultProtectionPlanService.selectEmsFaultProtectionPlanList(null);
|
List<EmsFaultProtectionPlan> planList = iEmsFaultProtectionPlanService.selectEmsFaultProtectionPlanList(null);
|
||||||
|
|
||||||
for (EmsFaultProtectionPlan plan : planList) {
|
for (EmsFaultProtectionPlan plan : planList) {
|
||||||
planId = plan.getId();
|
planId = plan.getId();
|
||||||
String siteId = plan.getSiteId();
|
String siteId = plan.getSiteId();
|
||||||
if (StringUtils.isEmpty(siteId)) {
|
if (StringUtils.isEmpty(siteId)) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
// 保护前提
|
List<ProtectionSettingVo> protSettings = parseProtectionSettings(plan.getProtectionSettings());
|
||||||
String protectionSettings = plan.getProtectionSettings();
|
if (CollectionUtils.isEmpty(protSettings)) {
|
||||||
final List<ProtectionSettingVo> protSettings = objectMapper.readValue(
|
continue;
|
||||||
protectionSettings,
|
|
||||||
new TypeReference<List<ProtectionSettingVo>>() {}
|
|
||||||
);
|
|
||||||
if (protSettings == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理告警保护方案
|
|
||||||
boolean isHighLevel = dealWithProtectionPlan(plan, protSettings);
|
|
||||||
if (isHighLevel) {
|
|
||||||
// 触发最高故障等级-结束循环
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
dealWithProtectionPlan(plan, protSettings);
|
||||||
}
|
}
|
||||||
|
refreshProtectionConstraintCache(planList);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("轮询失败,方案id为:{}", planId, e);
|
logger.error("轮询失败,方案id为:{}", planId, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理告警保护方案-返回触发下发方案时是否最高等级
|
|
||||||
// 需要同步云端
|
|
||||||
@SyncAfterInsert
|
@SyncAfterInsert
|
||||||
private boolean dealWithProtectionPlan(EmsFaultProtectionPlan plan, List<ProtectionSettingVo> protSettings) {
|
private void dealWithProtectionPlan(EmsFaultProtectionPlan plan, List<ProtectionSettingVo> protSettings) {
|
||||||
logger.info("<轮询保护方案> 站点:{},方案ID:{}", plan.getSiteId(), plan.getId());
|
logger.info("<轮询保护方案> 站点:{},方案ID:{}", plan.getSiteId(), plan.getId());
|
||||||
boolean isHighLevel = false;
|
|
||||||
|
|
||||||
String siteId = plan.getSiteId();
|
String siteId = plan.getSiteId();
|
||||||
final Integer isAlertAlarm = plan.getIsAlert();
|
Integer isAlertAlarm = plan.getIsAlert();
|
||||||
final Long status = plan.getStatus();
|
Long status = plan.getStatus();
|
||||||
// 看方案是否启用,走不同判断
|
if (status == null) {
|
||||||
|
status = ProtPlanStatus.STOP.getCode();
|
||||||
|
}
|
||||||
|
|
||||||
if (Objects.equals(status, ProtPlanStatus.STOP.getCode())) {
|
if (Objects.equals(status, ProtPlanStatus.STOP.getCode())) {
|
||||||
logger.info("<方案未启用> 站点:{},方案ID:{}", siteId, plan.getId());
|
if (checkIsNeedIssuedPlan(protSettings, siteId)) {
|
||||||
// 未启用,获取方案的故障值与最新数据判断是否需要下发方案
|
int faultDelay = safeDelaySeconds(plan.getFaultDelaySeconds(), 0);
|
||||||
if(checkIsNeedIssuedPlan(protSettings, siteId)){
|
scheduledExecutorService.schedule(() -> {
|
||||||
if("3".equals(plan.getFaultLevel())){
|
if (!checkIsNeedIssuedPlan(protSettings, siteId)) {
|
||||||
isHighLevel = true;//最高故障等级
|
return;
|
||||||
}
|
|
||||||
// 延时
|
|
||||||
final int faultDelay = plan.getFaultDelaySeconds().intValue();
|
|
||||||
ScheduledFuture<?> delayTask = scheduledExecutorService.schedule(() -> {
|
|
||||||
// 延时后再次确认是否仍满足触发条件(防止期间状态变化)
|
|
||||||
if (checkIsNeedIssuedPlan(protSettings, siteId)) {
|
|
||||||
// 判断是否需要生成告警
|
|
||||||
if (isAlertAlarm == 1) {
|
|
||||||
logger.info("<生成告警> 方案ID:{},站点:{}", plan.getId(), siteId);
|
|
||||||
EmsAlarmRecords alarmRecords = addAlarmRecord(siteId,plan.getFaultName(),
|
|
||||||
getAlarmLevel(plan.getFaultLevel()));
|
|
||||||
emsAlarmRecordsMapper.insertEmsAlarmRecords(alarmRecords);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 是否有保护方案,有则通过modbus连接设备下发方案
|
|
||||||
String protPlanJson = plan.getProtectionPlan();
|
|
||||||
if (protPlanJson != null && !protPlanJson.isEmpty() && !"[]".equals(protPlanJson)) {
|
|
||||||
logger.info("<下发保护方案> 方案内容:{}", protPlanJson);
|
|
||||||
executeProtectionActions(protPlanJson,siteId,plan.getId(),plan.getFaultLevel()); // 执行Modbus指令
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新方案状态为“已启用”
|
|
||||||
logger.info("<方案已启用> 方案ID:{}", plan.getId());
|
|
||||||
plan.setStatus(ProtPlanStatus.RUNNING.getCode());
|
|
||||||
emsFaultProtectionPlanMapper.updateEmsFaultProtectionPlan(plan);
|
|
||||||
|
|
||||||
// 更新该站点策略为暂停状态
|
|
||||||
updateStrategyRunningStatus(siteId, StrategyStatus.SUSPENDED.getCode());
|
|
||||||
}
|
}
|
||||||
}, faultDelay, TimeUnit.SECONDS);
|
if (Integer.valueOf(1).equals(isAlertAlarm)) {
|
||||||
}
|
EmsAlarmRecords alarmRecords = addAlarmRecord(siteId, plan.getFaultName(), getAlarmLevel(plan.getFaultLevel()));
|
||||||
} else {
|
emsAlarmRecordsMapper.insertEmsAlarmRecords(alarmRecords);
|
||||||
logger.info("<方案已启用> 站点:{},方案ID:{}", siteId, plan.getId());
|
|
||||||
// 已启用,则获取方案的释放值与最新数据判断是否需要取消方案
|
|
||||||
if(checkIsNeedCancelPlan(protSettings, siteId)){
|
|
||||||
// 延时,
|
|
||||||
int releaseDelay = plan.getReleaseDelaySeconds().intValue();
|
|
||||||
ScheduledFuture<?> delayTask = scheduledExecutorService.schedule(() -> {
|
|
||||||
// 判断是否已存在未处理告警,有着取消
|
|
||||||
if(isAlertAlarm == 1){
|
|
||||||
logger.info("<取消告警>");
|
|
||||||
EmsAlarmRecords emsAlarmRecords = emsAlarmRecordsMapper.getFailedRecord(siteId,
|
|
||||||
plan.getFaultName(),getAlarmLevel(plan.getFaultLevel()));
|
|
||||||
if(emsAlarmRecords != null){
|
|
||||||
emsAlarmRecords.setStatus(AlarmStatus.DONE.getCode());
|
|
||||||
emsAlarmRecordsMapper.updateEmsAlarmRecords(emsAlarmRecords);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// 更新方案状态为“未启用”
|
plan.setStatus(ProtPlanStatus.RUNNING.getCode());
|
||||||
logger.info("<方案变更为未启用> 方案ID:{}", plan.getId());
|
|
||||||
plan.setStatus(ProtPlanStatus.STOP.getCode());
|
|
||||||
plan.setUpdateBy("system");
|
plan.setUpdateBy("system");
|
||||||
emsFaultProtectionPlanMapper.updateEmsFaultProtectionPlan(plan);
|
emsFaultProtectionPlanMapper.updateEmsFaultProtectionPlan(plan);
|
||||||
// 更新该站点策略为启用状态
|
refreshSiteProtectionConstraint(siteId);
|
||||||
updateStrategyRunningStatus(siteId, StrategyStatus.RUNNING.getCode());
|
}, faultDelay, TimeUnit.SECONDS);
|
||||||
}, releaseDelay, TimeUnit.SECONDS);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return isHighLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 下发保护方案
|
|
||||||
private void executeProtectionActions(String protPlanJson, String siteId, Long planId, Integer faultLevel){
|
|
||||||
final List<ProtectionPlanVo> protPlanList;
|
|
||||||
try {
|
|
||||||
protPlanList = objectMapper.readValue(
|
|
||||||
protPlanJson,
|
|
||||||
new TypeReference<List<ProtectionPlanVo>>() {}
|
|
||||||
);
|
|
||||||
if (protPlanList == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 遍历保护方案
|
|
||||||
for (ProtectionPlanVo plan : protPlanList) {
|
|
||||||
if (StringUtils.isEmpty(plan.getDeviceId()) || StringUtils.isEmpty(plan.getPoint())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 给设备发送指令记录日志,并同步云端
|
|
||||||
EmsFaultIssueLog faultIssueLog = createLogEntity(plan,siteId);
|
|
||||||
faultIssueLog.setLogLevel(faultLevel);
|
|
||||||
emsFaultIssueLogMapper.insertEmsFaultIssueLog(faultIssueLog);
|
|
||||||
|
|
||||||
// 通过modbus连接设备,发送数据
|
|
||||||
executeSinglePlan(plan,siteId);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("下发保护方案失败,方案id为:", planId, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private EmsFaultIssueLog createLogEntity(ProtectionPlanVo plan,String siteId) {
|
|
||||||
EmsFaultIssueLog faultIssueLog = new EmsFaultIssueLog();
|
|
||||||
faultIssueLog.setLogId(UUID.randomUUID().toString());
|
|
||||||
faultIssueLog.setLogTime(new Date());
|
|
||||||
faultIssueLog.setSiteId(siteId);
|
|
||||||
faultIssueLog.setDeviceId(plan.getDeviceId());
|
|
||||||
faultIssueLog.setPoint(plan.getPoint());
|
|
||||||
faultIssueLog.setValue(plan.getValue());
|
|
||||||
faultIssueLog.setCreateBy("sys");
|
|
||||||
faultIssueLog.setCreateTime(new Date());
|
|
||||||
return faultIssueLog;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void executeSinglePlan(ProtectionPlanVo plan, String siteId) throws Exception {
|
|
||||||
String deviceId = plan.getDeviceId();
|
|
||||||
// 获取设备地址信息
|
|
||||||
EmsDevicesSetting device = emsDevicesSettingMapper.getDeviceBySiteAndDeviceId(deviceId, siteId);
|
|
||||||
if (device == null || StringUtils.isEmpty(device.getIpAddress()) || device.getIpPort()==null) {
|
|
||||||
logger.warn("设备信息不完整,deviceId:{}", deviceId);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建设备配置
|
if (checkIsNeedCancelPlan(protSettings, siteId)) {
|
||||||
DeviceConfig config = new DeviceConfig();
|
int releaseDelay = safeDelaySeconds(plan.getReleaseDelaySeconds(), 0);
|
||||||
config.setHost(device.getIpAddress());
|
scheduledExecutorService.schedule(() -> {
|
||||||
config.setPort(device.getIpPort().intValue());
|
if (Integer.valueOf(1).equals(isAlertAlarm)) {
|
||||||
config.setSlaveId(device.getSlaveId().intValue());
|
EmsAlarmRecords emsAlarmRecords = emsAlarmRecordsMapper.getFailedRecord(
|
||||||
config.setDeviceName(device.getDeviceName());
|
siteId,
|
||||||
config.setDeviceNumber(device.getDeviceId());
|
plan.getFaultName(),
|
||||||
|
getAlarmLevel(plan.getFaultLevel())
|
||||||
// 构建写入标签配置
|
);
|
||||||
WriteTagConfig writeTag = new WriteTagConfig();
|
if (emsAlarmRecords != null) {
|
||||||
writeTag.setAddress(plan.getPoint());
|
emsAlarmRecords.setStatus(AlarmStatus.DONE.getCode());
|
||||||
writeTag.setValue(plan.getValue());
|
emsAlarmRecordsMapper.updateEmsAlarmRecords(emsAlarmRecords);
|
||||||
|
}
|
||||||
List<WriteTagConfig> writeTags = new ArrayList<>();
|
}
|
||||||
writeTags.add(writeTag);
|
plan.setStatus(ProtPlanStatus.STOP.getCode());
|
||||||
config.setWriteTags(writeTags);
|
plan.setUpdateBy("system");
|
||||||
|
emsFaultProtectionPlanMapper.updateEmsFaultProtectionPlan(plan);
|
||||||
// 写入数据到设备
|
refreshSiteProtectionConstraint(siteId);
|
||||||
boolean success = modbusProcessor.writeDataToDeviceWithRetry(config);
|
}, releaseDelay, TimeUnit.SECONDS);
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
logger.error("写入失败,设备地址:{}", device.getIpAddress());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校验释放值是否取消方案
|
private int safeDelaySeconds(Long delay, int defaultSeconds) {
|
||||||
private boolean checkIsNeedCancelPlan(List<ProtectionSettingVo> protSettings, String siteId) {
|
if (delay == null || delay < 0) {
|
||||||
BigDecimal releaseValue = BigDecimal.ZERO;
|
return defaultSeconds;
|
||||||
|
}
|
||||||
|
return delay.intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ProtectionSettingVo> parseProtectionSettings(String settingsJson) {
|
||||||
|
if (StringUtils.isEmpty(settingsJson)) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return objectMapper.readValue(settingsJson, new TypeReference<List<ProtectionSettingVo>>() {});
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("解析保护前提失败,json:{}", settingsJson, e);
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshProtectionConstraintCache(List<EmsFaultProtectionPlan> allPlans) {
|
||||||
|
Map<String, List<EmsFaultProtectionPlan>> planBySite = new HashMap<>();
|
||||||
|
for (EmsFaultProtectionPlan plan : allPlans) {
|
||||||
|
if (StringUtils.isEmpty(plan.getSiteId())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
planBySite.computeIfAbsent(plan.getSiteId(), k -> new ArrayList<>()).add(plan);
|
||||||
|
}
|
||||||
|
for (Map.Entry<String, List<EmsFaultProtectionPlan>> entry : planBySite.entrySet()) {
|
||||||
|
writeSiteProtectionConstraint(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshSiteProtectionConstraint(String siteId) {
|
||||||
|
if (StringUtils.isEmpty(siteId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
EmsFaultProtectionPlan query = new EmsFaultProtectionPlan();
|
||||||
|
query.setSiteId(siteId);
|
||||||
|
List<EmsFaultProtectionPlan> sitePlans = iEmsFaultProtectionPlanService.selectEmsFaultProtectionPlanList(query);
|
||||||
|
writeSiteProtectionConstraint(siteId, sitePlans);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeSiteProtectionConstraint(String siteId, List<EmsFaultProtectionPlan> sitePlans) {
|
||||||
|
List<EmsFaultProtectionPlan> runningPlans = new ArrayList<>();
|
||||||
|
for (EmsFaultProtectionPlan plan : sitePlans) {
|
||||||
|
if (Objects.equals(plan.getStatus(), ProtPlanStatus.RUNNING.getCode())) {
|
||||||
|
runningPlans.add(plan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String key = RedisKeyConstants.PROTECTION_CONSTRAINT + siteId;
|
||||||
|
if (runningPlans.isEmpty()) {
|
||||||
|
redisCache.deleteObject(key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProtectionConstraintVo merged = ProtectionConstraintVo.empty();
|
||||||
|
for (EmsFaultProtectionPlan runningPlan : runningPlans) {
|
||||||
|
ProtectionConstraintVo single = buildConstraintFromPlan(runningPlan);
|
||||||
|
mergeConstraint(merged, single);
|
||||||
|
}
|
||||||
|
merged.setUpdateAt(System.currentTimeMillis());
|
||||||
|
redisCache.setCacheObject(key, merged, CONSTRAINT_TTL_SECONDS, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProtectionConstraintVo buildConstraintFromPlan(EmsFaultProtectionPlan plan) {
|
||||||
|
ProtectionConstraintVo vo = ProtectionConstraintVo.empty();
|
||||||
|
int level = plan.getFaultLevel() == null ? 0 : plan.getFaultLevel();
|
||||||
|
vo.setLevel(level);
|
||||||
|
vo.setSourcePlanIds(new ArrayList<>());
|
||||||
|
vo.getSourcePlanIds().add(plan.getId());
|
||||||
|
|
||||||
|
if (level == 1) {
|
||||||
|
vo.setPowerLimitRatio(DEFAULT_L1_POWER_RATIO);
|
||||||
|
} else if (level >= 3) {
|
||||||
|
vo.setForceStop(true);
|
||||||
|
vo.setForceStandby(true);
|
||||||
|
vo.setAllowCharge(false);
|
||||||
|
vo.setAllowDischarge(false);
|
||||||
|
vo.setPowerLimitRatio(BigDecimal.ZERO);
|
||||||
|
}
|
||||||
|
|
||||||
|
String description = StringUtils.isEmpty(plan.getDescription()) ? "" : plan.getDescription();
|
||||||
|
BigDecimal ratioByDesc = parseDerateRatio(description);
|
||||||
|
if (ratioByDesc != null) {
|
||||||
|
vo.setPowerLimitRatio(minRatio(vo.getPowerLimitRatio(), ratioByDesc));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (description.contains("禁止充放电")) {
|
||||||
|
vo.setAllowCharge(false);
|
||||||
|
vo.setAllowDischarge(false);
|
||||||
|
} else {
|
||||||
|
if (description.contains("禁止充电")) {
|
||||||
|
vo.setAllowCharge(false);
|
||||||
|
}
|
||||||
|
if (description.contains("禁止放电")) {
|
||||||
|
vo.setAllowDischarge(false);
|
||||||
|
}
|
||||||
|
if (description.contains("允许充电")) {
|
||||||
|
vo.setAllowCharge(true);
|
||||||
|
}
|
||||||
|
if (description.contains("允许放电")) {
|
||||||
|
vo.setAllowDischarge(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (description.contains("待机")) {
|
||||||
|
vo.setForceStandby(true);
|
||||||
|
}
|
||||||
|
if (description.contains("停机") || description.contains("切断") || level >= 3) {
|
||||||
|
vo.setForceStop(true);
|
||||||
|
vo.setForceStandby(true);
|
||||||
|
vo.setAllowCharge(false);
|
||||||
|
vo.setAllowDischarge(false);
|
||||||
|
vo.setPowerLimitRatio(BigDecimal.ZERO);
|
||||||
|
}
|
||||||
|
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal parseDerateRatio(String text) {
|
||||||
|
if (StringUtils.isEmpty(text)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Matcher m = Pattern.compile("降功率\\s*(\\d+(?:\\.\\d+)?)%")
|
||||||
|
.matcher(text);
|
||||||
|
if (!m.find()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
BigDecimal percent = new BigDecimal(m.group(1));
|
||||||
|
if (percent.compareTo(BigDecimal.ZERO) < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return percent.divide(new BigDecimal("100"), 4, RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mergeConstraint(ProtectionConstraintVo merged, ProtectionConstraintVo incoming) {
|
||||||
|
if (incoming == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
merged.setLevel(Math.max(nullSafeInt(merged.getLevel()), nullSafeInt(incoming.getLevel())));
|
||||||
|
merged.setAllowCharge(boolAnd(merged.getAllowCharge(), incoming.getAllowCharge()));
|
||||||
|
merged.setAllowDischarge(boolAnd(merged.getAllowDischarge(), incoming.getAllowDischarge()));
|
||||||
|
merged.setForceStandby(boolOr(merged.getForceStandby(), incoming.getForceStandby()));
|
||||||
|
merged.setForceStop(boolOr(merged.getForceStop(), incoming.getForceStop()));
|
||||||
|
merged.setPowerLimitRatio(minRatio(merged.getPowerLimitRatio(), incoming.getPowerLimitRatio()));
|
||||||
|
|
||||||
|
if (incoming.getSourcePlanIds() != null && !incoming.getSourcePlanIds().isEmpty()) {
|
||||||
|
if (merged.getSourcePlanIds() == null) {
|
||||||
|
merged.setSourcePlanIds(new ArrayList<>());
|
||||||
|
}
|
||||||
|
merged.getSourcePlanIds().addAll(incoming.getSourcePlanIds());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal minRatio(BigDecimal a, BigDecimal b) {
|
||||||
|
BigDecimal left = a == null ? BigDecimal.ONE : a;
|
||||||
|
BigDecimal right = b == null ? BigDecimal.ONE : b;
|
||||||
|
return left.min(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int nullSafeInt(Integer value) {
|
||||||
|
return value == null ? 0 : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Boolean boolAnd(Boolean a, Boolean b) {
|
||||||
|
boolean left = a == null || a;
|
||||||
|
boolean right = b == null || b;
|
||||||
|
return left && right;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Boolean boolOr(Boolean a, Boolean b) {
|
||||||
|
boolean left = a != null && a;
|
||||||
|
boolean right = b != null && b;
|
||||||
|
return left || right;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkIsNeedCancelPlan(List<ProtectionSettingVo> protSettings, String siteId) {
|
||||||
StringBuilder conditionSb = new StringBuilder();
|
StringBuilder conditionSb = new StringBuilder();
|
||||||
for (int i = 0; i < protSettings.size(); i++) {
|
for (int i = 0; i < protSettings.size(); i++) {
|
||||||
ProtectionSettingVo vo = protSettings.get(i);
|
ProtectionSettingVo vo = protSettings.get(i);
|
||||||
String deviceId = vo.getDeviceId();
|
String deviceId = vo.getDeviceId();
|
||||||
String point = vo.getPoint();
|
String point = vo.getPoint();
|
||||||
releaseValue = vo.getFaultValue();
|
BigDecimal releaseValue = vo.getReleaseValue();
|
||||||
if(StringUtils.isEmpty(deviceId) || StringUtils.isEmpty(point) || releaseValue == null
|
if (StringUtils.isEmpty(deviceId)
|
||||||
|| StringUtils.isEmpty(vo.getReleaseOperator())){
|
|| StringUtils.isEmpty(point)
|
||||||
|
|| releaseValue == null
|
||||||
|
|| StringUtils.isEmpty(vo.getReleaseOperator())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// 获取点位最新值
|
|
||||||
BigDecimal lastPointValue = getPointLastValue(deviceId, point, siteId);
|
BigDecimal lastPointValue = getPointLastValue(deviceId, point, siteId);
|
||||||
logger.info("checkIsNeedCancelPlan 点位:{},最新值:{},比较方式:{},释放值:{}", point, lastPointValue, vo.getReleaseOperator(), releaseValue);
|
if (lastPointValue == null) {
|
||||||
if(lastPointValue == null){
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 拼接校验语句-最新值+比较方式+故障值+与下一点位关系(最后一个条件后不加关系)
|
|
||||||
conditionSb.append(lastPointValue).append(vo.getReleaseOperator()).append(releaseValue);
|
conditionSb.append(lastPointValue).append(vo.getReleaseOperator()).append(releaseValue);
|
||||||
if (i < protSettings.size() - 1) {
|
if (i < protSettings.size() - 1) {
|
||||||
String relation = vo.getRelationNext();
|
conditionSb.append(" ").append(vo.getRelationNext()).append(" ");
|
||||||
conditionSb.append(" ").append(relation).append(" ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
// 执行比较语句
|
|
||||||
return executeWithParser(conditionSb.toString());
|
return executeWithParser(conditionSb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校验故障值是否需要下发方案
|
|
||||||
private boolean checkIsNeedIssuedPlan(List<ProtectionSettingVo> protSettings, String siteId) {
|
private boolean checkIsNeedIssuedPlan(List<ProtectionSettingVo> protSettings, String siteId) {
|
||||||
BigDecimal faultValue = BigDecimal.ZERO;
|
|
||||||
|
|
||||||
StringBuilder conditionSb = new StringBuilder();
|
StringBuilder conditionSb = new StringBuilder();
|
||||||
for (int i = 0; i < protSettings.size(); i++) {
|
for (int i = 0; i < protSettings.size(); i++) {
|
||||||
ProtectionSettingVo vo = protSettings.get(i);
|
ProtectionSettingVo vo = protSettings.get(i);
|
||||||
String deviceId = vo.getDeviceId();
|
String deviceId = vo.getDeviceId();
|
||||||
String point = vo.getPoint();
|
String point = vo.getPoint();
|
||||||
faultValue = vo.getFaultValue();
|
BigDecimal faultValue = vo.getFaultValue();
|
||||||
if(StringUtils.isEmpty(deviceId) || StringUtils.isEmpty(point) || faultValue == null
|
if (StringUtils.isEmpty(deviceId)
|
||||||
|| StringUtils.isEmpty(vo.getFaultOperator())){
|
|| StringUtils.isEmpty(point)
|
||||||
|
|| faultValue == null
|
||||||
|
|| StringUtils.isEmpty(vo.getFaultOperator())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// 获取点位最新值
|
|
||||||
BigDecimal lastPointValue = getPointLastValue(deviceId, point, siteId);
|
BigDecimal lastPointValue = getPointLastValue(deviceId, point, siteId);
|
||||||
logger.info("checkIsNeedIssuedPlan 点位:{},最新值:{},比较方式:{},故障值:{}", point, lastPointValue, vo.getFaultOperator(), faultValue);
|
if (lastPointValue == null) {
|
||||||
if(lastPointValue == null){
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 拼接校验语句-最新值+比较方式+故障值+与下一点位关系(最后一个条件后不加关系)
|
|
||||||
conditionSb.append(lastPointValue).append(vo.getFaultOperator()).append(faultValue);
|
conditionSb.append(lastPointValue).append(vo.getFaultOperator()).append(faultValue);
|
||||||
if (i < protSettings.size() - 1) {
|
if (i < protSettings.size() - 1) {
|
||||||
String relation = vo.getRelationNext();
|
conditionSb.append(" ").append(vo.getRelationNext()).append(" ");
|
||||||
conditionSb.append(" ").append(relation).append(" ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
// 执行比较语句
|
|
||||||
return executeWithParser(conditionSb.toString());
|
return executeWithParser(conditionSb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private BigDecimal getPointLastValue(String deviceId, String point, String siteId) {
|
private BigDecimal getPointLastValue(String deviceId, String point, String siteId) {
|
||||||
JSONObject mqttJson = redisCache.getCacheObject(RedisKeyConstants.SYNC_DATA + siteId + "_" + deviceId);
|
JSONObject mqttJson = redisCache.getCacheObject(RedisKeyConstants.SYNC_DATA + siteId + "_" + deviceId);
|
||||||
if(mqttJson == null){
|
if (mqttJson == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
String jsonData = mqttJson.get("Data").toString();
|
String jsonData = mqttJson.get("Data").toString();
|
||||||
if(StringUtils.isEmpty(jsonData)){
|
if (StringUtils.isEmpty(jsonData)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Map<String, Object> obj = JSON.parseObject(jsonData, new com.alibaba.fastjson2.TypeReference<Map<String, Object>>() {});
|
Map<String, Object> obj = JSON.parseObject(jsonData, new com.alibaba.fastjson2.TypeReference<Map<String, Object>>() {});
|
||||||
return StringUtils.getBigDecimal(obj.get(point));
|
return StringUtils.getBigDecimal(obj.get(point));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新站点策略为启用
|
private EmsAlarmRecords addAlarmRecord(String siteId, String content, String level) {
|
||||||
private void updateStrategyRunningStatus(String siteId, String status) {
|
|
||||||
// 获取是否有正在运行的策略,如果有则不更改
|
|
||||||
EmsStrategyRunning query = new EmsStrategyRunning();
|
|
||||||
query.setSiteId(siteId);
|
|
||||||
query.setStatus(StrategyStatus.RUNNING.getCode().equals(status) ? StrategyStatus.SUSPENDED.getCode() : StrategyStatus.RUNNING.getCode());
|
|
||||||
List<EmsStrategyRunning> strategyRunningList = emsStrategyRunningMapper.selectEmsStrategyRunningList(query);
|
|
||||||
if (CollectionUtils.isNotEmpty(strategyRunningList)) {
|
|
||||||
// 获取已存在并且状态为:未启用和已暂停的最晚一条策略,更新为已启用
|
|
||||||
strategyRunningList.forEach(emsStrategyRunning -> {
|
|
||||||
emsStrategyRunning.setStatus(status);
|
|
||||||
emsStrategyRunningMapper.updateEmsStrategyRunning(emsStrategyRunning);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新站点策略为启用
|
|
||||||
private void updateStrategyRunning(String siteId) {
|
|
||||||
// 获取是否有正在运行的策略,如果有则不更改
|
|
||||||
EmsStrategyRunning emsStrategyRunning = emsStrategyRunningMapper.getRunningStrategy(siteId);
|
|
||||||
if (emsStrategyRunning == null) {
|
|
||||||
// 获取已存在并且状态为:未启用和已暂停的最晚一条策略,更新为已启用
|
|
||||||
emsStrategyRunning = emsStrategyRunningMapper.getPendingStrategy(siteId);
|
|
||||||
emsStrategyRunning.setStatus(StrategyStatus.RUNNING.getCode());
|
|
||||||
emsStrategyRunningMapper.updateEmsStrategyRunning(emsStrategyRunning);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private EmsAlarmRecords addAlarmRecord(String siteId, String content,String level) {
|
|
||||||
EmsAlarmRecords emsAlarmRecords = new EmsAlarmRecords();
|
EmsAlarmRecords emsAlarmRecords = new EmsAlarmRecords();
|
||||||
emsAlarmRecords.setSiteId(siteId);
|
emsAlarmRecords.setSiteId(siteId);
|
||||||
emsAlarmRecords.setAlarmContent(content);
|
emsAlarmRecords.setAlarmContent(content);
|
||||||
@ -395,29 +394,31 @@ public class ProtectionPlanTask {
|
|||||||
return emsAlarmRecords;
|
return emsAlarmRecords;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 故障等级-告警等级匹配
|
|
||||||
private String getAlarmLevel(Integer faultLevel) {
|
private String getAlarmLevel(Integer faultLevel) {
|
||||||
if (ObjectUtils.isEmpty(faultLevel) || faultLevel < 1 || faultLevel > 3) {
|
if (ObjectUtils.isEmpty(faultLevel) || faultLevel < 1 || faultLevel > 3) {
|
||||||
logger.warn("非法故障等级:{},默认返回普通告警", faultLevel);
|
logger.warn("非法故障等级:{},默认返回紧急告警", faultLevel);
|
||||||
return AlarmLevelStatus.EMERGENCY.getCode();
|
return AlarmLevelStatus.EMERGENCY.getCode();
|
||||||
}
|
}
|
||||||
switch (faultLevel) {
|
switch (faultLevel) {
|
||||||
case 1: return AlarmLevelStatus.GENERAL.getCode();
|
case 1:
|
||||||
case 2: return AlarmLevelStatus.SERIOUS.getCode();
|
return AlarmLevelStatus.GENERAL.getCode();
|
||||||
case 3: return AlarmLevelStatus.EMERGENCY.getCode();
|
case 2:
|
||||||
|
return AlarmLevelStatus.SERIOUS.getCode();
|
||||||
|
case 3:
|
||||||
|
return AlarmLevelStatus.EMERGENCY.getCode();
|
||||||
default:
|
default:
|
||||||
logger.error("未匹配的故障等级:{}", faultLevel);
|
|
||||||
return AlarmLevelStatus.EMERGENCY.getCode();
|
return AlarmLevelStatus.EMERGENCY.getCode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 自定义表达式解析器(仅支持简单运算符和逻辑关系)
|
/**
|
||||||
|
* 自定义表达式解析器(仅支持简单运算符和逻辑关系)
|
||||||
|
*/
|
||||||
public boolean executeWithParser(String conditionStr) {
|
public boolean executeWithParser(String conditionStr) {
|
||||||
if (conditionStr == null || conditionStr.isEmpty()) {
|
if (conditionStr == null || conditionStr.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. 拆分逻辑关系(提取 && 或 ||)
|
|
||||||
List<String> logicRelations = new ArrayList<>();
|
List<String> logicRelations = new ArrayList<>();
|
||||||
Pattern logicPattern = Pattern.compile("(&&|\\|\\|)");
|
Pattern logicPattern = Pattern.compile("(&&|\\|\\|)");
|
||||||
Matcher logicMatcher = logicPattern.matcher(conditionStr);
|
Matcher logicMatcher = logicPattern.matcher(conditionStr);
|
||||||
@ -425,10 +426,7 @@ public class ProtectionPlanTask {
|
|||||||
logicRelations.add(logicMatcher.group());
|
logicRelations.add(logicMatcher.group());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 拆分原子条件(如 "3.55>3.52")
|
|
||||||
String[] atomicConditions = logicPattern.split(conditionStr);
|
String[] atomicConditions = logicPattern.split(conditionStr);
|
||||||
|
|
||||||
// 3. 解析每个原子条件并计算结果
|
|
||||||
List<Boolean> atomicResults = new ArrayList<>();
|
List<Boolean> atomicResults = new ArrayList<>();
|
||||||
Pattern conditionPattern = Pattern.compile("(\\d+\\.?\\d*)\\s*([><]=?|==)\\s*(\\d+\\.?\\d*)");
|
Pattern conditionPattern = Pattern.compile("(\\d+\\.?\\d*)\\s*([><]=?|==)\\s*(\\d+\\.?\\d*)");
|
||||||
for (String atomic : atomicConditions) {
|
for (String atomic : atomicConditions) {
|
||||||
@ -437,11 +435,10 @@ public class ProtectionPlanTask {
|
|||||||
logger.error("无效的原子条件:{}", atomic);
|
logger.error("无效的原子条件:{}", atomic);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
double left = Double.parseDouble(matcher.group(1)); // 左值(最新值)
|
double left = Double.parseDouble(matcher.group(1));
|
||||||
String operator = matcher.group(2); // 运算符
|
String operator = matcher.group(2);
|
||||||
double right = Double.parseDouble(matcher.group(3)); // 右值(故障值)
|
double right = Double.parseDouble(matcher.group(3));
|
||||||
|
|
||||||
// 执行比较
|
|
||||||
boolean result;
|
boolean result;
|
||||||
switch (operator) {
|
switch (operator) {
|
||||||
case ">":
|
case ">":
|
||||||
@ -466,11 +463,10 @@ public class ProtectionPlanTask {
|
|||||||
atomicResults.add(result);
|
atomicResults.add(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 组合原子结果(根据逻辑关系)
|
|
||||||
boolean finalResult = atomicResults.get(0);
|
boolean finalResult = atomicResults.get(0);
|
||||||
for (int i = 0; i < logicRelations.size(); i++) {
|
for (int i = 0; i < logicRelations.size(); i++) {
|
||||||
String relation = logicRelations.get(i);
|
String relation = logicRelations.get(i);
|
||||||
boolean nextResult = atomicResults.get(i+1);
|
boolean nextResult = atomicResults.get(i + 1);
|
||||||
if ("&&".equals(relation)) {
|
if ("&&".equals(relation)) {
|
||||||
finalResult = finalResult && nextResult;
|
finalResult = finalResult && nextResult;
|
||||||
} else if ("||".equals(relation)) {
|
} else if ("||".equals(relation)) {
|
||||||
|
|||||||
@ -2,9 +2,11 @@ package com.xzzn.quartz.task;
|
|||||||
|
|
||||||
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSON;
|
||||||
import com.alibaba.fastjson2.JSONArray;
|
import com.alibaba.fastjson2.JSONArray;
|
||||||
|
import com.xzzn.common.constant.RedisKeyConstants;
|
||||||
import com.xzzn.common.core.modbus.ModbusProcessor;
|
import com.xzzn.common.core.modbus.ModbusProcessor;
|
||||||
import com.xzzn.common.core.modbus.domain.DeviceConfig;
|
import com.xzzn.common.core.modbus.domain.DeviceConfig;
|
||||||
import com.xzzn.common.core.modbus.domain.WriteTagConfig;
|
import com.xzzn.common.core.modbus.domain.WriteTagConfig;
|
||||||
|
import com.xzzn.common.core.redis.RedisCache;
|
||||||
import com.xzzn.common.enums.ChargeStatus;
|
import com.xzzn.common.enums.ChargeStatus;
|
||||||
import com.xzzn.common.enums.DeviceCategory;
|
import com.xzzn.common.enums.DeviceCategory;
|
||||||
import com.xzzn.common.enums.SiteDevice;
|
import com.xzzn.common.enums.SiteDevice;
|
||||||
@ -20,6 +22,7 @@ import com.xzzn.ems.domain.EmsStrategyRuntimeConfig;
|
|||||||
import com.xzzn.ems.domain.EmsStrategyLog;
|
import com.xzzn.ems.domain.EmsStrategyLog;
|
||||||
import com.xzzn.ems.domain.EmsStrategyTemp;
|
import com.xzzn.ems.domain.EmsStrategyTemp;
|
||||||
import com.xzzn.ems.domain.EmsStrategyTimeConfig;
|
import com.xzzn.ems.domain.EmsStrategyTimeConfig;
|
||||||
|
import com.xzzn.ems.domain.vo.ProtectionConstraintVo;
|
||||||
import com.xzzn.ems.domain.vo.StrategyRunningVo;
|
import com.xzzn.ems.domain.vo.StrategyRunningVo;
|
||||||
import com.xzzn.ems.mapper.EmsAmmeterDataMapper;
|
import com.xzzn.ems.mapper.EmsAmmeterDataMapper;
|
||||||
import com.xzzn.ems.mapper.EmsBatteryStackMapper;
|
import com.xzzn.ems.mapper.EmsBatteryStackMapper;
|
||||||
@ -73,6 +76,20 @@ public class StrategyPoller {
|
|||||||
private static final BigDecimal DEFAULT_ANTI_REVERSE_POWER_DOWN_PERCENT = new BigDecimal(10);
|
private static final BigDecimal DEFAULT_ANTI_REVERSE_POWER_DOWN_PERCENT = new BigDecimal(10);
|
||||||
// 电网有功功率低于20kW时,强制待机
|
// 电网有功功率低于20kW时,强制待机
|
||||||
private static final BigDecimal DEFAULT_ANTI_REVERSE_HARD_STOP_THRESHOLD = new BigDecimal(20);
|
private static final BigDecimal DEFAULT_ANTI_REVERSE_HARD_STOP_THRESHOLD = new BigDecimal(20);
|
||||||
|
// 设定功率倍率,默认10
|
||||||
|
private static final BigDecimal DEFAULT_POWER_SET_MULTIPLIER = new BigDecimal(10);
|
||||||
|
// 保护介入默认开启
|
||||||
|
private static final Integer DEFAULT_PROTECT_INTERVENE_ENABLE = 1;
|
||||||
|
// 一级保护默认降额50%
|
||||||
|
private static final BigDecimal DEFAULT_PROTECT_L1_DERATE_PERCENT = new BigDecimal("50");
|
||||||
|
// 保护约束失效保护时长(秒)
|
||||||
|
private static final Integer DEFAULT_PROTECT_RECOVERY_STABLE_SECONDS = 5;
|
||||||
|
// 三级保护默认锁存开启
|
||||||
|
private static final Integer DEFAULT_PROTECT_L3_LATCH_ENABLE = 1;
|
||||||
|
// 保护冲突策略默认值
|
||||||
|
private static final String DEFAULT_PROTECT_CONFLICT_POLICY = "MAX_LEVEL_WIN";
|
||||||
|
// 保护约束默认功率比例
|
||||||
|
private static final BigDecimal DEFAULT_PROTECTION_RATIO = BigDecimal.ONE;
|
||||||
// 除法精度,避免BigDecimal除不尽异常
|
// 除法精度,避免BigDecimal除不尽异常
|
||||||
private static final int POWER_SCALE = 4;
|
private static final int POWER_SCALE = 4;
|
||||||
|
|
||||||
@ -95,6 +112,8 @@ public class StrategyPoller {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private EmsStrategyRuntimeConfigMapper runtimeConfigMapper;
|
private EmsStrategyRuntimeConfigMapper runtimeConfigMapper;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
private RedisCache redisCache;
|
||||||
|
@Autowired
|
||||||
private ModbusProcessor modbusProcessor;
|
private ModbusProcessor modbusProcessor;
|
||||||
|
|
||||||
@Resource(name = "modbusExecutor")
|
@Resource(name = "modbusExecutor")
|
||||||
@ -186,6 +205,7 @@ public class StrategyPoller {
|
|||||||
}
|
}
|
||||||
// 判断SOC上下限
|
// 判断SOC上下限
|
||||||
if (isSocInRange(emsStrategyTemp, runtimeConfig)) {
|
if (isSocInRange(emsStrategyTemp, runtimeConfig)) {
|
||||||
|
ProtectionConstraintVo protectionConstraint = getProtectionConstraint(siteId);
|
||||||
Map<Long, EmsPcsSetting> pcsSettingCache = new HashMap<>();
|
Map<Long, EmsPcsSetting> pcsSettingCache = new HashMap<>();
|
||||||
BigDecimal avgChargeDischargePower = emsStrategyTemp.getChargeDischargePower()
|
BigDecimal avgChargeDischargePower = emsStrategyTemp.getChargeDischargePower()
|
||||||
.divide(new BigDecimal(pcsDeviceList.size()), POWER_SCALE, RoundingMode.HALF_UP);
|
.divide(new BigDecimal(pcsDeviceList.size()), POWER_SCALE, RoundingMode.HALF_UP);
|
||||||
@ -206,13 +226,27 @@ public class StrategyPoller {
|
|||||||
logger.info("当前站点: {}, PCS设备: {} 未获取电池簇数量", siteId, pcsDevice.getDeviceId());
|
logger.info("当前站点: {}, PCS设备: {} 未获取电池簇数量", siteId, pcsDevice.getDeviceId());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// 功率默认放大10倍,平均功率值,根据电池簇数量进行平均分配
|
// 平均功率值根据倍率放大后,再按电池簇数量平均分配
|
||||||
BigDecimal strategyPower = avgChargeDischargePower.multiply(new BigDecimal(10))
|
BigDecimal strategyPower = avgChargeDischargePower.multiply(runtimeConfig.getPowerSetMultiplier())
|
||||||
.divide(new BigDecimal(pcsSetting.getClusterNum()), POWER_SCALE, RoundingMode.HALF_UP);
|
.divide(new BigDecimal(pcsSetting.getClusterNum()), POWER_SCALE, RoundingMode.HALF_UP);
|
||||||
// 根据充电状态,处理数据
|
// 根据充电状态,处理数据
|
||||||
if (ChargeStatus.CHARGING.getCode().equals(emsStrategyTemp.getChargeStatus())) {
|
if (ChargeStatus.CHARGING.getCode().equals(emsStrategyTemp.getChargeStatus())) {
|
||||||
|
StrategyCommandDecision decision = applyProtectionConstraint(
|
||||||
|
strategyPower,
|
||||||
|
ChargeStatus.CHARGING,
|
||||||
|
runtimeConfig,
|
||||||
|
protectionConstraint
|
||||||
|
);
|
||||||
// 发送Modbus命令控制设备-充电
|
// 发送Modbus命令控制设备-充电
|
||||||
sendModbusCommand(Collections.singletonList(pcsDevice), pcsSetting, ChargeStatus.CHARGING, strategyPower, emsStrategyTemp, false, null);
|
sendModbusCommand(
|
||||||
|
Collections.singletonList(pcsDevice),
|
||||||
|
pcsSetting,
|
||||||
|
decision.getChargeStatus(),
|
||||||
|
decision.getPower(),
|
||||||
|
emsStrategyTemp,
|
||||||
|
false,
|
||||||
|
null
|
||||||
|
);
|
||||||
} else if (ChargeStatus.DISCHARGING.getCode().equals(emsStrategyTemp.getChargeStatus())) {
|
} else if (ChargeStatus.DISCHARGING.getCode().equals(emsStrategyTemp.getChargeStatus())) {
|
||||||
boolean needAntiReverseFlow = false;
|
boolean needAntiReverseFlow = false;
|
||||||
Integer powerDownType = null;
|
Integer powerDownType = null;
|
||||||
@ -277,13 +311,21 @@ public class StrategyPoller {
|
|||||||
if (chargeDischargePower.compareTo(BigDecimal.ZERO) < 0) {
|
if (chargeDischargePower.compareTo(BigDecimal.ZERO) < 0) {
|
||||||
chargeDischargePower = BigDecimal.ZERO;
|
chargeDischargePower = BigDecimal.ZERO;
|
||||||
}
|
}
|
||||||
if (BigDecimal.ZERO.compareTo(chargeDischargePower) == 0) {
|
StrategyCommandDecision decision = applyProtectionConstraint(
|
||||||
|
chargeDischargePower,
|
||||||
|
ChargeStatus.DISCHARGING,
|
||||||
|
runtimeConfig,
|
||||||
|
protectionConstraint
|
||||||
|
);
|
||||||
|
ChargeStatus finalStatus = decision.getChargeStatus();
|
||||||
|
BigDecimal finalPower = decision.getPower();
|
||||||
|
if (ChargeStatus.STANDBY.equals(finalStatus) || BigDecimal.ZERO.compareTo(finalPower) == 0) {
|
||||||
// 如果已经降功率到0,则设备直接待机
|
// 如果已经降功率到0,则设备直接待机
|
||||||
// 发送Modbus命令控制设备-待机
|
// 发送Modbus命令控制设备-待机
|
||||||
sendModbusCommand(Collections.singletonList(pcsDevice), pcsSetting, ChargeStatus.STANDBY, BigDecimal.ZERO, emsStrategyTemp, needAntiReverseFlow, powerDownType);
|
sendModbusCommand(Collections.singletonList(pcsDevice), pcsSetting, ChargeStatus.STANDBY, BigDecimal.ZERO, emsStrategyTemp, needAntiReverseFlow, powerDownType);
|
||||||
} else {
|
} else {
|
||||||
// 发送Modbus命令控制设备-放电
|
// 发送Modbus命令控制设备-放电
|
||||||
sendModbusCommand(Collections.singletonList(pcsDevice), pcsSetting, ChargeStatus.DISCHARGING, chargeDischargePower, emsStrategyTemp, needAntiReverseFlow, powerDownType);
|
sendModbusCommand(Collections.singletonList(pcsDevice), pcsSetting, finalStatus, finalPower, emsStrategyTemp, needAntiReverseFlow, powerDownType);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 发送Modbus命令控制设备-待机
|
// 发送Modbus命令控制设备-待机
|
||||||
@ -503,6 +545,103 @@ public class StrategyPoller {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ProtectionConstraintVo getProtectionConstraint(String siteId) {
|
||||||
|
ProtectionConstraintVo constraint = redisCache.getCacheObject(RedisKeyConstants.PROTECTION_CONSTRAINT + siteId);
|
||||||
|
if (constraint == null) {
|
||||||
|
return ProtectionConstraintVo.empty();
|
||||||
|
}
|
||||||
|
if (constraint.getPowerLimitRatio() == null) {
|
||||||
|
constraint.setPowerLimitRatio(DEFAULT_PROTECTION_RATIO);
|
||||||
|
}
|
||||||
|
if (constraint.getAllowCharge() == null) {
|
||||||
|
constraint.setAllowCharge(true);
|
||||||
|
}
|
||||||
|
if (constraint.getAllowDischarge() == null) {
|
||||||
|
constraint.setAllowDischarge(true);
|
||||||
|
}
|
||||||
|
if (constraint.getForceStandby() == null) {
|
||||||
|
constraint.setForceStandby(false);
|
||||||
|
}
|
||||||
|
if (constraint.getForceStop() == null) {
|
||||||
|
constraint.setForceStop(false);
|
||||||
|
}
|
||||||
|
if (constraint.getLevel() == null) {
|
||||||
|
constraint.setLevel(0);
|
||||||
|
}
|
||||||
|
return constraint;
|
||||||
|
}
|
||||||
|
|
||||||
|
private StrategyCommandDecision applyProtectionConstraint(BigDecimal targetPower,
|
||||||
|
ChargeStatus targetStatus,
|
||||||
|
EmsStrategyRuntimeConfig runtimeConfig,
|
||||||
|
ProtectionConstraintVo constraint) {
|
||||||
|
if (!Integer.valueOf(1).equals(runtimeConfig.getProtectInterveneEnable())) {
|
||||||
|
return new StrategyCommandDecision(targetStatus, safePower(targetPower));
|
||||||
|
}
|
||||||
|
if (constraint == null || nullSafeInt(constraint.getLevel()) <= 0) {
|
||||||
|
return new StrategyCommandDecision(targetStatus, safePower(targetPower));
|
||||||
|
}
|
||||||
|
if (Boolean.TRUE.equals(constraint.getForceStop()) || Boolean.TRUE.equals(constraint.getForceStandby())) {
|
||||||
|
return new StrategyCommandDecision(ChargeStatus.STANDBY, BigDecimal.ZERO);
|
||||||
|
}
|
||||||
|
if (ChargeStatus.CHARGING.equals(targetStatus) && Boolean.FALSE.equals(constraint.getAllowCharge())) {
|
||||||
|
return new StrategyCommandDecision(ChargeStatus.STANDBY, BigDecimal.ZERO);
|
||||||
|
}
|
||||||
|
if (ChargeStatus.DISCHARGING.equals(targetStatus) && Boolean.FALSE.equals(constraint.getAllowDischarge())) {
|
||||||
|
return new StrategyCommandDecision(ChargeStatus.STANDBY, BigDecimal.ZERO);
|
||||||
|
}
|
||||||
|
BigDecimal ratio = getPowerLimitRatio(constraint, runtimeConfig);
|
||||||
|
BigDecimal finalPower = safePower(targetPower).multiply(ratio).setScale(POWER_SCALE, RoundingMode.HALF_UP);
|
||||||
|
if (finalPower.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
return new StrategyCommandDecision(ChargeStatus.STANDBY, BigDecimal.ZERO);
|
||||||
|
}
|
||||||
|
return new StrategyCommandDecision(targetStatus, finalPower);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal getPowerLimitRatio(ProtectionConstraintVo constraint, EmsStrategyRuntimeConfig runtimeConfig) {
|
||||||
|
BigDecimal ratio = constraint.getPowerLimitRatio();
|
||||||
|
if (ratio == null || ratio.compareTo(BigDecimal.ZERO) < 0 || ratio.compareTo(BigDecimal.ONE) > 0) {
|
||||||
|
ratio = DEFAULT_PROTECTION_RATIO;
|
||||||
|
}
|
||||||
|
if (nullSafeInt(constraint.getLevel()) == 1 && DEFAULT_PROTECTION_RATIO.compareTo(ratio) == 0) {
|
||||||
|
BigDecimal deratePercent = runtimeConfig.getProtectL1DeratePercent();
|
||||||
|
if (deratePercent == null || deratePercent.compareTo(BigDecimal.ZERO) < 0 || deratePercent.compareTo(new BigDecimal("100")) > 0) {
|
||||||
|
deratePercent = DEFAULT_PROTECT_L1_DERATE_PERCENT;
|
||||||
|
}
|
||||||
|
ratio = deratePercent.divide(new BigDecimal("100"), POWER_SCALE, RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
return ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal safePower(BigDecimal power) {
|
||||||
|
if (power == null || power.compareTo(BigDecimal.ZERO) < 0) {
|
||||||
|
return BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
return power;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int nullSafeInt(Integer value) {
|
||||||
|
return value == null ? 0 : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class StrategyCommandDecision {
|
||||||
|
private final ChargeStatus chargeStatus;
|
||||||
|
private final BigDecimal power;
|
||||||
|
|
||||||
|
private StrategyCommandDecision(ChargeStatus chargeStatus, BigDecimal power) {
|
||||||
|
this.chargeStatus = chargeStatus;
|
||||||
|
this.power = power;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChargeStatus getChargeStatus() {
|
||||||
|
return chargeStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getPower() {
|
||||||
|
return power;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 判断当前时间是否在时间范围内
|
// 判断当前时间是否在时间范围内
|
||||||
private static boolean isTimeInRange(LocalTime now, Date startTime, Date endTime) {
|
private static boolean isTimeInRange(LocalTime now, Date startTime, Date endTime) {
|
||||||
ZoneId zoneId = ZoneId.of("Asia/Shanghai");
|
ZoneId zoneId = ZoneId.of("Asia/Shanghai");
|
||||||
@ -573,6 +712,26 @@ public class StrategyPoller {
|
|||||||
if (config.getAntiReverseHardStopThreshold() == null) {
|
if (config.getAntiReverseHardStopThreshold() == null) {
|
||||||
config.setAntiReverseHardStopThreshold(DEFAULT_ANTI_REVERSE_HARD_STOP_THRESHOLD);
|
config.setAntiReverseHardStopThreshold(DEFAULT_ANTI_REVERSE_HARD_STOP_THRESHOLD);
|
||||||
}
|
}
|
||||||
|
if (config.getPowerSetMultiplier() == null || config.getPowerSetMultiplier().compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
config.setPowerSetMultiplier(DEFAULT_POWER_SET_MULTIPLIER);
|
||||||
|
}
|
||||||
|
if (config.getProtectInterveneEnable() == null) {
|
||||||
|
config.setProtectInterveneEnable(DEFAULT_PROTECT_INTERVENE_ENABLE);
|
||||||
|
}
|
||||||
|
if (config.getProtectL1DeratePercent() == null
|
||||||
|
|| config.getProtectL1DeratePercent().compareTo(BigDecimal.ZERO) < 0
|
||||||
|
|| config.getProtectL1DeratePercent().compareTo(new BigDecimal("100")) > 0) {
|
||||||
|
config.setProtectL1DeratePercent(DEFAULT_PROTECT_L1_DERATE_PERCENT);
|
||||||
|
}
|
||||||
|
if (config.getProtectRecoveryStableSeconds() == null || config.getProtectRecoveryStableSeconds() < 0) {
|
||||||
|
config.setProtectRecoveryStableSeconds(DEFAULT_PROTECT_RECOVERY_STABLE_SECONDS);
|
||||||
|
}
|
||||||
|
if (config.getProtectL3LatchEnable() == null) {
|
||||||
|
config.setProtectL3LatchEnable(DEFAULT_PROTECT_L3_LATCH_ENABLE);
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(config.getProtectConflictPolicy())) {
|
||||||
|
config.setProtectConflictPolicy(DEFAULT_PROTECT_CONFLICT_POLICY);
|
||||||
|
}
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,7 @@ public class EmsSiteMonitorPointMatch extends BaseEntity {
|
|||||||
private Long id;
|
private Long id;
|
||||||
private String siteId;
|
private String siteId;
|
||||||
private String fieldCode;
|
private String fieldCode;
|
||||||
|
private String deviceId;
|
||||||
private String dataPoint;
|
private String dataPoint;
|
||||||
private String fixedDataPoint;
|
private String fixedDataPoint;
|
||||||
private Integer useFixedDisplay;
|
private Integer useFixedDisplay;
|
||||||
@ -47,6 +48,14 @@ public class EmsSiteMonitorPointMatch extends BaseEntity {
|
|||||||
this.dataPoint = dataPoint;
|
this.dataPoint = dataPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getDeviceId() {
|
||||||
|
return deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeviceId(String deviceId) {
|
||||||
|
this.deviceId = deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
public String getFixedDataPoint() {
|
public String getFixedDataPoint() {
|
||||||
return fixedDataPoint;
|
return fixedDataPoint;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,6 +49,29 @@ public class EmsStrategyRuntimeConfig extends BaseEntity {
|
|||||||
/** 防逆流硬停阈值(kW) */
|
/** 防逆流硬停阈值(kW) */
|
||||||
@Excel(name = "防逆流硬停阈值(kW)")
|
@Excel(name = "防逆流硬停阈值(kW)")
|
||||||
private BigDecimal antiReverseHardStopThreshold;
|
private BigDecimal antiReverseHardStopThreshold;
|
||||||
|
/** 设定功率倍率 */
|
||||||
|
@Excel(name = "设定功率倍率")
|
||||||
|
private BigDecimal powerSetMultiplier;
|
||||||
|
|
||||||
|
/** 保护介入开关(1-启用,0-禁用) */
|
||||||
|
@Excel(name = "保护介入开关")
|
||||||
|
private Integer protectInterveneEnable;
|
||||||
|
|
||||||
|
/** 一级保护降额比例(%) */
|
||||||
|
@Excel(name = "一级保护降额比例(%)")
|
||||||
|
private BigDecimal protectL1DeratePercent;
|
||||||
|
|
||||||
|
/** 保护释放稳定时长(秒) */
|
||||||
|
@Excel(name = "保护释放稳定时长(秒)")
|
||||||
|
private Integer protectRecoveryStableSeconds;
|
||||||
|
|
||||||
|
/** 三级保护锁存开关(1-启用,0-禁用) */
|
||||||
|
@Excel(name = "三级保护锁存开关")
|
||||||
|
private Integer protectL3LatchEnable;
|
||||||
|
|
||||||
|
/** 保护冲突策略 */
|
||||||
|
@Excel(name = "保护冲突策略")
|
||||||
|
private String protectConflictPolicy;
|
||||||
|
|
||||||
public Long getId() {
|
public Long getId() {
|
||||||
return id;
|
return id;
|
||||||
@ -122,6 +145,54 @@ public class EmsStrategyRuntimeConfig extends BaseEntity {
|
|||||||
this.antiReverseHardStopThreshold = antiReverseHardStopThreshold;
|
this.antiReverseHardStopThreshold = antiReverseHardStopThreshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BigDecimal getPowerSetMultiplier() {
|
||||||
|
return powerSetMultiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPowerSetMultiplier(BigDecimal powerSetMultiplier) {
|
||||||
|
this.powerSetMultiplier = powerSetMultiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getProtectInterveneEnable() {
|
||||||
|
return protectInterveneEnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProtectInterveneEnable(Integer protectInterveneEnable) {
|
||||||
|
this.protectInterveneEnable = protectInterveneEnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getProtectL1DeratePercent() {
|
||||||
|
return protectL1DeratePercent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProtectL1DeratePercent(BigDecimal protectL1DeratePercent) {
|
||||||
|
this.protectL1DeratePercent = protectL1DeratePercent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getProtectRecoveryStableSeconds() {
|
||||||
|
return protectRecoveryStableSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProtectRecoveryStableSeconds(Integer protectRecoveryStableSeconds) {
|
||||||
|
this.protectRecoveryStableSeconds = protectRecoveryStableSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getProtectL3LatchEnable() {
|
||||||
|
return protectL3LatchEnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProtectL3LatchEnable(Integer protectL3LatchEnable) {
|
||||||
|
this.protectL3LatchEnable = protectL3LatchEnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProtectConflictPolicy() {
|
||||||
|
return protectConflictPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProtectConflictPolicy(String protectConflictPolicy) {
|
||||||
|
this.protectConflictPolicy = protectConflictPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
|
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
|
||||||
@ -134,6 +205,12 @@ public class EmsStrategyRuntimeConfig extends BaseEntity {
|
|||||||
.append("antiReverseUp", getAntiReverseUp())
|
.append("antiReverseUp", getAntiReverseUp())
|
||||||
.append("antiReversePowerDownPercent", getAntiReversePowerDownPercent())
|
.append("antiReversePowerDownPercent", getAntiReversePowerDownPercent())
|
||||||
.append("antiReverseHardStopThreshold", getAntiReverseHardStopThreshold())
|
.append("antiReverseHardStopThreshold", getAntiReverseHardStopThreshold())
|
||||||
|
.append("powerSetMultiplier", getPowerSetMultiplier())
|
||||||
|
.append("protectInterveneEnable", getProtectInterveneEnable())
|
||||||
|
.append("protectL1DeratePercent", getProtectL1DeratePercent())
|
||||||
|
.append("protectRecoveryStableSeconds", getProtectRecoveryStableSeconds())
|
||||||
|
.append("protectL3LatchEnable", getProtectL3LatchEnable())
|
||||||
|
.append("protectConflictPolicy", getProtectConflictPolicy())
|
||||||
.append("createBy", getCreateBy())
|
.append("createBy", getCreateBy())
|
||||||
.append("createTime", getCreateTime())
|
.append("createTime", getCreateTime())
|
||||||
.append("updateBy", getUpdateBy())
|
.append("updateBy", getUpdateBy())
|
||||||
|
|||||||
@ -1,10 +1,19 @@
|
|||||||
package com.xzzn.ems.domain.vo;
|
package com.xzzn.ems.domain.vo;
|
||||||
|
|
||||||
public class GeneralQueryPointOptionVo {
|
public class GeneralQueryPointOptionVo {
|
||||||
|
private String pointId;
|
||||||
private String pointName;
|
private String pointName;
|
||||||
private String dataKey;
|
private String dataKey;
|
||||||
private String pointDesc;
|
private String pointDesc;
|
||||||
|
|
||||||
|
public String getPointId() {
|
||||||
|
return pointId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPointId(String pointId) {
|
||||||
|
this.pointId = pointId;
|
||||||
|
}
|
||||||
|
|
||||||
public String getPointName() {
|
public String getPointName() {
|
||||||
return pointName;
|
return pointName;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,8 @@ public class PointNameRequest {
|
|||||||
|
|
||||||
private String pointName;
|
private String pointName;
|
||||||
private List<String> pointNames;
|
private List<String> pointNames;
|
||||||
|
private String pointId;
|
||||||
|
private List<String> pointIds;
|
||||||
|
|
||||||
/** 数据分组 1-分钟 2-小时 3-天 */
|
/** 数据分组 1-分钟 2-小时 3-天 */
|
||||||
private int dataUnit;
|
private int dataUnit;
|
||||||
@ -59,6 +61,22 @@ public class PointNameRequest {
|
|||||||
this.pointNames = pointNames;
|
this.pointNames = pointNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getPointId() {
|
||||||
|
return pointId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPointId(String pointId) {
|
||||||
|
this.pointId = pointId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getPointIds() {
|
||||||
|
return pointIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPointIds(List<String> pointIds) {
|
||||||
|
this.pointIds = pointIds;
|
||||||
|
}
|
||||||
|
|
||||||
public int getDataUnit() {
|
public int getDataUnit() {
|
||||||
return dataUnit;
|
return dataUnit;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package com.xzzn.ems.domain.vo;
|
package com.xzzn.ems.domain.vo;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
@ -10,10 +11,12 @@ import java.util.Date;
|
|||||||
public class RunningGraphRequest {
|
public class RunningGraphRequest {
|
||||||
|
|
||||||
/** 开始时间 */
|
/** 开始时间 */
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||||
private Date startDate;
|
private Date startDate;
|
||||||
|
|
||||||
/** 结束时间 */
|
/** 结束时间 */
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||||
private Date endDate;
|
private Date endDate;
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,10 @@ public class SiteMonitorProjectPointMappingVo {
|
|||||||
|
|
||||||
private String fieldName;
|
private String fieldName;
|
||||||
|
|
||||||
|
private String deviceId;
|
||||||
|
|
||||||
|
private String deviceName;
|
||||||
|
|
||||||
private String dataPoint;
|
private String dataPoint;
|
||||||
|
|
||||||
private String fixedDataPoint;
|
private String fixedDataPoint;
|
||||||
@ -86,6 +90,22 @@ public class SiteMonitorProjectPointMappingVo {
|
|||||||
this.dataPoint = dataPoint;
|
this.dataPoint = dataPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getDeviceId() {
|
||||||
|
return deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeviceId(String deviceId) {
|
||||||
|
this.deviceId = deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDeviceName() {
|
||||||
|
return deviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeviceName(String deviceName) {
|
||||||
|
this.deviceName = deviceName;
|
||||||
|
}
|
||||||
|
|
||||||
public String getFixedDataPoint() {
|
public String getFixedDataPoint() {
|
||||||
return fixedDataPoint;
|
return fixedDataPoint;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,26 @@
|
|||||||
|
package com.xzzn.ems.domain.vo;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class WorkStatusEnumMappingSaveRequest {
|
||||||
|
|
||||||
|
private String siteId;
|
||||||
|
|
||||||
|
private List<WorkStatusEnumMappingVo> mappings;
|
||||||
|
|
||||||
|
public String getSiteId() {
|
||||||
|
return siteId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSiteId(String siteId) {
|
||||||
|
this.siteId = siteId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<WorkStatusEnumMappingVo> getMappings() {
|
||||||
|
return mappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMappings(List<WorkStatusEnumMappingVo> mappings) {
|
||||||
|
this.mappings = mappings;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,74 @@
|
|||||||
|
package com.xzzn.ems.domain.vo;
|
||||||
|
|
||||||
|
public class WorkStatusEnumMappingVo {
|
||||||
|
|
||||||
|
private String deviceCategory;
|
||||||
|
|
||||||
|
private String matchField;
|
||||||
|
|
||||||
|
private String matchFieldName;
|
||||||
|
|
||||||
|
private String enumCode;
|
||||||
|
|
||||||
|
private String enumName;
|
||||||
|
|
||||||
|
private String enumDesc;
|
||||||
|
|
||||||
|
private String dataEnumCode;
|
||||||
|
|
||||||
|
public String getEnumCode() {
|
||||||
|
return enumCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnumCode(String enumCode) {
|
||||||
|
this.enumCode = enumCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEnumName() {
|
||||||
|
return enumName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnumName(String enumName) {
|
||||||
|
this.enumName = enumName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEnumDesc() {
|
||||||
|
return enumDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnumDesc(String enumDesc) {
|
||||||
|
this.enumDesc = enumDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDataEnumCode() {
|
||||||
|
return dataEnumCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDataEnumCode(String dataEnumCode) {
|
||||||
|
this.dataEnumCode = dataEnumCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDeviceCategory() {
|
||||||
|
return deviceCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeviceCategory(String deviceCategory) {
|
||||||
|
this.deviceCategory = deviceCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMatchField() {
|
||||||
|
return matchField;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMatchField(String matchField) {
|
||||||
|
this.matchField = matchField;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMatchFieldName() {
|
||||||
|
return matchFieldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMatchFieldName(String matchFieldName) {
|
||||||
|
this.matchFieldName = matchFieldName;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -41,6 +41,7 @@ public interface EmsPointConfigMapper {
|
|||||||
|
|
||||||
List<EmsPointConfig> getConfigListForGeneralQuery(@Param("siteIds") List<String> siteIds,
|
List<EmsPointConfig> getConfigListForGeneralQuery(@Param("siteIds") List<String> siteIds,
|
||||||
@Param("deviceCategory") String deviceCategory,
|
@Param("deviceCategory") String deviceCategory,
|
||||||
|
@Param("pointIds") List<String> pointIds,
|
||||||
@Param("pointNames") List<String> pointNames,
|
@Param("pointNames") List<String> pointNames,
|
||||||
@Param("deviceIds") List<String> deviceIds);
|
@Param("deviceIds") List<String> deviceIds);
|
||||||
|
|
||||||
|
|||||||
@ -75,4 +75,8 @@ public interface EmsPointEnumMatchMapper
|
|||||||
int copyTemplateToSite(@Param("templateSiteId") String templateSiteId,
|
int copyTemplateToSite(@Param("templateSiteId") String templateSiteId,
|
||||||
@Param("targetSiteId") String targetSiteId,
|
@Param("targetSiteId") String targetSiteId,
|
||||||
@Param("operName") String operName);
|
@Param("operName") String operName);
|
||||||
|
|
||||||
|
int deleteByScope(@Param("siteId") String siteId,
|
||||||
|
@Param("deviceCategory") String deviceCategory,
|
||||||
|
@Param("matchField") String matchField);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,15 +14,10 @@ public interface EmsSiteMonitorDataMapper {
|
|||||||
@Param("siteId") String siteId,
|
@Param("siteId") String siteId,
|
||||||
@Param("statisMinute") java.util.Date statisMinute,
|
@Param("statisMinute") java.util.Date statisMinute,
|
||||||
@Param("dataJson") String dataJson,
|
@Param("dataJson") String dataJson,
|
||||||
|
@Param("hotSoc") String hotSoc,
|
||||||
|
@Param("hotTotalActivePower") String hotTotalActivePower,
|
||||||
|
@Param("hotTotalReactivePower") String hotTotalReactivePower,
|
||||||
|
@Param("hotDayChargedCap") String hotDayChargedCap,
|
||||||
|
@Param("hotDayDisChargedCap") String hotDayDisChargedCap,
|
||||||
@Param("operName") String operName);
|
@Param("operName") String operName);
|
||||||
|
|
||||||
int updateHistoryHotColumns(@Param("tableName") String tableName,
|
|
||||||
@Param("siteId") String siteId,
|
|
||||||
@Param("statisMinute") java.util.Date statisMinute,
|
|
||||||
@Param("hotSoc") String hotSoc,
|
|
||||||
@Param("hotTotalActivePower") String hotTotalActivePower,
|
|
||||||
@Param("hotTotalReactivePower") String hotTotalReactivePower,
|
|
||||||
@Param("hotDayChargedCap") String hotDayChargedCap,
|
|
||||||
@Param("hotDayDisChargedCap") String hotDayDisChargedCap,
|
|
||||||
@Param("operName") String operName);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import com.xzzn.ems.domain.vo.SiteMonitorDataSaveRequest;
|
|||||||
import com.xzzn.ems.domain.vo.SiteMonitorProjectDisplayVo;
|
import com.xzzn.ems.domain.vo.SiteMonitorProjectDisplayVo;
|
||||||
import com.xzzn.ems.domain.vo.SiteMonitorProjectPointMappingSaveRequest;
|
import com.xzzn.ems.domain.vo.SiteMonitorProjectPointMappingSaveRequest;
|
||||||
import com.xzzn.ems.domain.vo.SiteMonitorProjectPointMappingVo;
|
import com.xzzn.ems.domain.vo.SiteMonitorProjectPointMappingVo;
|
||||||
|
import com.xzzn.ems.domain.vo.WorkStatusEnumMappingVo;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -46,6 +47,10 @@ public interface IEmsDeviceSettingService
|
|||||||
|
|
||||||
public int saveSiteMonitorProjectPointMapping(SiteMonitorProjectPointMappingSaveRequest request, String operName);
|
public int saveSiteMonitorProjectPointMapping(SiteMonitorProjectPointMappingSaveRequest request, String operName);
|
||||||
|
|
||||||
|
public List<WorkStatusEnumMappingVo> getSiteWorkStatusEnumMappings(String siteId);
|
||||||
|
|
||||||
|
public int saveSiteWorkStatusEnumMappings(String siteId, List<WorkStatusEnumMappingVo> mappings, String operName);
|
||||||
|
|
||||||
public List<SiteMonitorProjectDisplayVo> getSiteMonitorProjectDisplay(String siteId);
|
public List<SiteMonitorProjectDisplayVo> getSiteMonitorProjectDisplay(String siteId);
|
||||||
|
|
||||||
public int saveSiteMonitorProjectData(SiteMonitorDataSaveRequest request, String operName);
|
public int saveSiteMonitorProjectData(SiteMonitorDataSaveRequest request, String operName);
|
||||||
|
|||||||
@ -181,6 +181,102 @@ public class InfluxPointDataWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<PointValue> queryCurveDataByPointKey(String siteId, String pointKey, Date startTime, Date endTime) {
|
||||||
|
if (!enabled) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
if (isBlank(siteId) || isBlank(pointKey) || startTime == null || endTime == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
String normalizedSiteId = siteId.trim();
|
||||||
|
String normalizedPointKey = pointKey.trim();
|
||||||
|
|
||||||
|
String influxQl = String.format(
|
||||||
|
"SELECT \"value\" FROM \"%s\" WHERE \"site_id\" = '%s' AND \"point_key\" = '%s' " +
|
||||||
|
"AND time >= %dms AND time <= %dms ORDER BY time ASC",
|
||||||
|
measurement,
|
||||||
|
escapeTagValue(normalizedSiteId),
|
||||||
|
escapeTagValue(normalizedPointKey),
|
||||||
|
startTime.getTime(),
|
||||||
|
endTime.getTime()
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
String queryUrl = buildQueryUrl(influxQl);
|
||||||
|
List<PointValue> values = parseInfluxQlResponse(executeRequestWithResponse(methodOrDefault(readMethod, "GET"), queryUrl));
|
||||||
|
if (!values.isEmpty()) {
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 兼容 pointId 大小写差异
|
||||||
|
String regexQuery = String.format(
|
||||||
|
"SELECT \"value\" FROM \"%s\" WHERE \"site_id\" = '%s' AND \"point_key\" =~ /(?i)^%s$/ " +
|
||||||
|
"AND time >= %dms AND time <= %dms ORDER BY time ASC",
|
||||||
|
measurement,
|
||||||
|
escapeTagValue(normalizedSiteId),
|
||||||
|
escapeRegex(normalizedPointKey),
|
||||||
|
startTime.getTime(),
|
||||||
|
endTime.getTime()
|
||||||
|
);
|
||||||
|
return parseInfluxQlResponse(
|
||||||
|
executeRequestWithResponse(methodOrDefault(readMethod, "GET"), buildQueryUrl(regexQuery))
|
||||||
|
);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("按 pointKey 查询 InfluxDB 曲线失败: {}", e.getMessage());
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public PointValue queryLatestPointValueByPointKey(String siteId, String pointKey, Date startTime, Date endTime) {
|
||||||
|
if (!enabled) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (isBlank(siteId) || isBlank(pointKey) || startTime == null || endTime == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String normalizedSiteId = siteId.trim();
|
||||||
|
String normalizedPointKey = pointKey.trim();
|
||||||
|
|
||||||
|
String influxQl = String.format(
|
||||||
|
"SELECT \"value\" FROM \"%s\" WHERE \"site_id\" = '%s' AND \"point_key\" = '%s' " +
|
||||||
|
"AND time >= %dms AND time <= %dms ORDER BY time DESC LIMIT 1",
|
||||||
|
measurement,
|
||||||
|
escapeTagValue(normalizedSiteId),
|
||||||
|
escapeTagValue(normalizedPointKey),
|
||||||
|
startTime.getTime(),
|
||||||
|
endTime.getTime()
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
List<PointValue> values = parseInfluxQlResponse(
|
||||||
|
executeRequestWithResponse(methodOrDefault(readMethod, "GET"), buildQueryUrl(influxQl))
|
||||||
|
);
|
||||||
|
if (!values.isEmpty()) {
|
||||||
|
return values.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
String regexQuery = String.format(
|
||||||
|
"SELECT \"value\" FROM \"%s\" WHERE \"site_id\" = '%s' AND \"point_key\" =~ /(?i)^%s$/ " +
|
||||||
|
"AND time >= %dms AND time <= %dms ORDER BY time DESC LIMIT 1",
|
||||||
|
measurement,
|
||||||
|
escapeTagValue(normalizedSiteId),
|
||||||
|
escapeRegex(normalizedPointKey),
|
||||||
|
startTime.getTime(),
|
||||||
|
endTime.getTime()
|
||||||
|
);
|
||||||
|
values = parseInfluxQlResponse(
|
||||||
|
executeRequestWithResponse(methodOrDefault(readMethod, "GET"), buildQueryUrl(regexQuery))
|
||||||
|
);
|
||||||
|
return values.isEmpty() ? null : values.get(0);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("按 pointKey 查询 InfluxDB 最新值失败: {}", e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private String buildWriteUrl() {
|
private String buildWriteUrl() {
|
||||||
if (isV2WritePath()) {
|
if (isV2WritePath()) {
|
||||||
return buildV2WriteUrl();
|
return buildV2WriteUrl();
|
||||||
@ -488,6 +584,7 @@ public class InfluxPointDataWriter {
|
|||||||
return value == null ? "" : value.replace("\\", "\\\\").replace("'", "\\'");
|
return value == null ? "" : value.replace("\\", "\\\\").replace("'", "\\'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private String escapeRegex(String value) {
|
private String escapeRegex(String value) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return "";
|
return "";
|
||||||
@ -546,6 +643,7 @@ public class InfluxPointDataWriter {
|
|||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private boolean isBlank(String value) {
|
private boolean isBlank(String value) {
|
||||||
return value == null || value.trim().isEmpty();
|
return value == null || value.trim().isEmpty();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -110,6 +110,7 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
|
|||||||
private static final Pattern DTDC_PATTERN = Pattern.compile("DTDC(\\d+)([A-Za-z]*)");
|
private static final Pattern DTDC_PATTERN = Pattern.compile("DTDC(\\d+)([A-Za-z]*)");
|
||||||
private static final Pattern DB_NAME_PATTERN = Pattern.compile("^[A-Za-z0-9_]+$");
|
private static final Pattern DB_NAME_PATTERN = Pattern.compile("^[A-Za-z0-9_]+$");
|
||||||
private static final Pattern VARIABLE_SAFE_PATTERN = Pattern.compile("[^A-Za-z0-9_]");
|
private static final Pattern VARIABLE_SAFE_PATTERN = Pattern.compile("[^A-Za-z0-9_]");
|
||||||
|
private static final Pattern SIMPLE_CALC_EXPRESSION_PATTERN = Pattern.compile("^[0-9A-Za-z_+\\-*/().\\s]+$");
|
||||||
private static final int POINT_QUEUE_CAPACITY = 100000;
|
private static final int POINT_QUEUE_CAPACITY = 100000;
|
||||||
private static final int POINT_FLUSH_BATCH_SIZE = 2000;
|
private static final int POINT_FLUSH_BATCH_SIZE = 2000;
|
||||||
private static final int POINT_FLUSH_MAX_DRAIN_PER_RUN = 20000;
|
private static final int POINT_FLUSH_MAX_DRAIN_PER_RUN = 20000;
|
||||||
@ -176,7 +177,7 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
|
|||||||
@Autowired
|
@Autowired
|
||||||
private InfluxPointDataWriter influxPointDataWriter;
|
private InfluxPointDataWriter influxPointDataWriter;
|
||||||
private final BlockingQueue<PointDataRecord> pointDataQueue = new LinkedBlockingQueue<>(POINT_QUEUE_CAPACITY);
|
private final BlockingQueue<PointDataRecord> pointDataQueue = new LinkedBlockingQueue<>(POINT_QUEUE_CAPACITY);
|
||||||
private final Map<String, List<ExpressionToken>> calcExpressionCache = new ConcurrentHashMap<>();
|
private final Map<String, CompiledExpression> calcExpressionCache = new ConcurrentHashMap<>();
|
||||||
private final ScheduledExecutorService pointDataWriter = Executors.newSingleThreadScheduledExecutor(r -> {
|
private final ScheduledExecutorService pointDataWriter = Executors.newSingleThreadScheduledExecutor(r -> {
|
||||||
Thread thread = new Thread(r);
|
Thread thread = new Thread(r);
|
||||||
thread.setName("point-data-writer");
|
thread.setName("point-data-writer");
|
||||||
@ -287,7 +288,7 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
|
|||||||
if (!uniquePointKeys.add(pointContextKey)) {
|
if (!uniquePointKeys.add(pointContextKey)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (isCalcPoint(pointConfig)) {
|
if (isComputedPoint(pointConfig)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
dataPointConfigs.add(pointConfig);
|
dataPointConfigs.add(pointConfig);
|
||||||
@ -383,7 +384,7 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
|
|||||||
List<EmsPointConfig> pointConfigs = getPointConfigsWithCache(siteId, deviceId, deviceCategory);
|
List<EmsPointConfig> pointConfigs = getPointConfigsWithCache(siteId, deviceId, deviceCategory);
|
||||||
if (!CollectionUtils.isEmpty(pointConfigs)) {
|
if (!CollectionUtils.isEmpty(pointConfigs)) {
|
||||||
for (EmsPointConfig pointConfig : pointConfigs) {
|
for (EmsPointConfig pointConfig : pointConfigs) {
|
||||||
if (isCalcPoint(pointConfig)) {
|
if (isComputedPoint(pointConfig)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String dataKey = StringUtils.defaultString(pointConfig.getDataKey()).trim().toUpperCase();
|
String dataKey = StringUtils.defaultString(pointConfig.getDataKey()).trim().toUpperCase();
|
||||||
@ -438,8 +439,12 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
|
|||||||
return StringUtils.isBlank(normalized) ? null : normalized;
|
return StringUtils.isBlank(normalized) ? null : normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isCalcPoint(EmsPointConfig pointConfig) {
|
private boolean isComputedPoint(EmsPointConfig pointConfig) {
|
||||||
return pointConfig != null && "calc".equalsIgnoreCase(pointConfig.getPointType());
|
if (pointConfig == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String pointType = StringUtils.defaultString(pointConfig.getPointType()).trim().toLowerCase();
|
||||||
|
return "calc".equals(pointType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enqueuePointData(String siteId, String deviceId, String pointKey, BigDecimal pointValue, Date dataTime) {
|
private void enqueuePointData(String siteId, String deviceId, String pointKey, BigDecimal pointValue, Date dataTime) {
|
||||||
@ -521,14 +526,17 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
|
|||||||
String calcDataKey = entry.getKey();
|
String calcDataKey = entry.getKey();
|
||||||
EmsPointConfig calcPointConfig = entry.getValue();
|
EmsPointConfig calcPointConfig = entry.getValue();
|
||||||
try {
|
try {
|
||||||
BigDecimal calcValue = evaluateCalcExpression(calcPointConfig.getCalcExpression(), contextValues);
|
ExpressionValue calcValue = evaluateCalcExpression(calcPointConfig.getCalcExpression(), contextValues);
|
||||||
contextValues.put(calcDataKey, calcValue);
|
if (calcValue.isNumber()) {
|
||||||
putPointValueToContext(calcPointConfig, calcValue, contextValues);
|
contextValues.put(calcDataKey, calcValue.asNumber());
|
||||||
|
putPointValueToContext(calcPointConfig, calcValue.asNumber(), contextValues);
|
||||||
|
}
|
||||||
// 计算点按站点维度统一落库,不再按配置中的 device_id 分流
|
// 计算点按站点维度统一落库,不再按配置中的 device_id 分流
|
||||||
String pointId = resolveInfluxPointKey(calcPointConfig);
|
String pointId = resolveInfluxPointKey(calcPointConfig);
|
||||||
if (StringUtils.isNotBlank(pointId)) {
|
if (StringUtils.isNotBlank(pointId)) {
|
||||||
enqueuePointData(siteId, deviceId, pointId, calcValue, dataUpdateTime);
|
BigDecimal storedValue = calcValue.asNumber();
|
||||||
calcPointIdValueMap.put(pointId, calcValue);
|
enqueuePointData(siteId, deviceId, pointId, storedValue, dataUpdateTime);
|
||||||
|
calcPointIdValueMap.put(pointId, storedValue);
|
||||||
}
|
}
|
||||||
finishedKeys.add(calcDataKey);
|
finishedKeys.add(calcDataKey);
|
||||||
progressed = true;
|
progressed = true;
|
||||||
@ -584,213 +592,19 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
|
|||||||
return StringUtils.isNotBlank(pointId) ? pointId : null;
|
return StringUtils.isNotBlank(pointId) ? pointId : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BigDecimal evaluateCalcExpression(String expression, Map<String, BigDecimal> contextValues) {
|
private ExpressionValue evaluateCalcExpression(String expression, Map<String, BigDecimal> contextValues) {
|
||||||
if (StringUtils.isBlank(expression)) {
|
if (StringUtils.isBlank(expression)) {
|
||||||
throw new IllegalArgumentException("计算表达式为空");
|
throw new IllegalArgumentException("计算表达式为空");
|
||||||
}
|
}
|
||||||
List<ExpressionToken> rpnTokens = calcExpressionCache.computeIfAbsent(expression, this::compileExpressionToRpn);
|
if (!SIMPLE_CALC_EXPRESSION_PATTERN.matcher(expression).matches()) {
|
||||||
return evaluateRpnTokens(rpnTokens, contextValues == null ? Collections.emptyMap() : contextValues);
|
throw new IllegalArgumentException("计算表达式仅支持四则运算");
|
||||||
|
}
|
||||||
|
CompiledExpression compiledExpression = calcExpressionCache.computeIfAbsent(expression, this::compileExpression);
|
||||||
|
return compiledExpression.evaluate(contextValues == null ? Collections.emptyMap() : contextValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ExpressionToken> compileExpressionToRpn(String expression) {
|
private CompiledExpression compileExpression(String expression) {
|
||||||
if (StringUtils.isBlank(expression)) {
|
return new CompiledExpression(expression);
|
||||||
throw new IllegalArgumentException("计算表达式为空");
|
|
||||||
}
|
|
||||||
List<ExpressionToken> output = new ArrayList<>();
|
|
||||||
Deque<ExpressionToken> operators = new ArrayDeque<>();
|
|
||||||
int index = 0;
|
|
||||||
ExpressionToken previous = null;
|
|
||||||
|
|
||||||
while (index < expression.length()) {
|
|
||||||
char ch = expression.charAt(index);
|
|
||||||
if (Character.isWhitespace(ch)) {
|
|
||||||
index++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (Character.isDigit(ch) || ch == '.') {
|
|
||||||
int start = index;
|
|
||||||
boolean hasDot = ch == '.';
|
|
||||||
index++;
|
|
||||||
while (index < expression.length()) {
|
|
||||||
char next = expression.charAt(index);
|
|
||||||
if (Character.isDigit(next)) {
|
|
||||||
index++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (next == '.' && !hasDot) {
|
|
||||||
hasDot = true;
|
|
||||||
index++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
String numberText = expression.substring(start, index);
|
|
||||||
try {
|
|
||||||
output.add(ExpressionToken.number(new BigDecimal(numberText)));
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
throw new IllegalArgumentException("数值格式错误: " + numberText);
|
|
||||||
}
|
|
||||||
previous = output.get(output.size() - 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (Character.isLetter(ch) || ch == '_') {
|
|
||||||
int start = index;
|
|
||||||
index++;
|
|
||||||
while (index < expression.length()) {
|
|
||||||
char next = expression.charAt(index);
|
|
||||||
if (Character.isLetterOrDigit(next) || next == '_') {
|
|
||||||
index++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
String variable = expression.substring(start, index).trim().toUpperCase();
|
|
||||||
output.add(ExpressionToken.variable(variable));
|
|
||||||
previous = output.get(output.size() - 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (ch == '(') {
|
|
||||||
operators.push(ExpressionToken.leftParen());
|
|
||||||
previous = ExpressionToken.leftParen();
|
|
||||||
index++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (ch == ')') {
|
|
||||||
boolean matched = false;
|
|
||||||
while (!operators.isEmpty()) {
|
|
||||||
ExpressionToken token = operators.pop();
|
|
||||||
if (token.getType() == ExpressionTokenType.LEFT_PAREN) {
|
|
||||||
matched = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
output.add(token);
|
|
||||||
}
|
|
||||||
if (!matched) {
|
|
||||||
throw new IllegalArgumentException("括号不匹配");
|
|
||||||
}
|
|
||||||
previous = ExpressionToken.rightParen();
|
|
||||||
index++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (isOperator(ch)) {
|
|
||||||
boolean unaryMinus = ch == '-' && (previous == null
|
|
||||||
|| previous.getType() == ExpressionTokenType.OPERATOR
|
|
||||||
|| previous.getType() == ExpressionTokenType.LEFT_PAREN);
|
|
||||||
String operatorText = unaryMinus ? "~" : String.valueOf(ch);
|
|
||||||
ExpressionToken currentOperator = ExpressionToken.operator(operatorText);
|
|
||||||
while (!operators.isEmpty() && operators.peek().getType() == ExpressionTokenType.OPERATOR) {
|
|
||||||
ExpressionToken top = operators.peek();
|
|
||||||
if (shouldPopOperator(currentOperator, top)) {
|
|
||||||
output.add(operators.pop());
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
operators.push(currentOperator);
|
|
||||||
previous = currentOperator;
|
|
||||||
index++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException("表达式包含非法字符: " + ch);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!operators.isEmpty()) {
|
|
||||||
ExpressionToken token = operators.pop();
|
|
||||||
if (token.getType() == ExpressionTokenType.LEFT_PAREN) {
|
|
||||||
throw new IllegalArgumentException("括号不匹配");
|
|
||||||
}
|
|
||||||
output.add(token);
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
private BigDecimal evaluateRpnTokens(List<ExpressionToken> rpnTokens, Map<String, BigDecimal> contextValues) {
|
|
||||||
Deque<BigDecimal> values = new ArrayDeque<>();
|
|
||||||
for (ExpressionToken token : rpnTokens) {
|
|
||||||
if (token.getType() == ExpressionTokenType.NUMBER) {
|
|
||||||
values.push(token.getNumber());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (token.getType() == ExpressionTokenType.VARIABLE) {
|
|
||||||
BigDecimal variableValue = contextValues.get(token.getText());
|
|
||||||
if (variableValue == null) {
|
|
||||||
throw new MissingVariableException(token.getText());
|
|
||||||
}
|
|
||||||
values.push(variableValue);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (token.getType() != ExpressionTokenType.OPERATOR) {
|
|
||||||
throw new IllegalArgumentException("表达式令牌类型不合法: " + token.getType());
|
|
||||||
}
|
|
||||||
if ("~".equals(token.getText())) {
|
|
||||||
if (values.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException("表达式不合法,缺少操作数");
|
|
||||||
}
|
|
||||||
values.push(values.pop().negate());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (values.size() < 2) {
|
|
||||||
throw new IllegalArgumentException("表达式不合法,缺少操作数");
|
|
||||||
}
|
|
||||||
BigDecimal right = values.pop();
|
|
||||||
BigDecimal left = values.pop();
|
|
||||||
values.push(applyOperator(left, right, token.getText()));
|
|
||||||
}
|
|
||||||
if (values.size() != 1) {
|
|
||||||
throw new IllegalArgumentException("表达式不合法,无法归约到单值");
|
|
||||||
}
|
|
||||||
return values.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
private BigDecimal applyOperator(BigDecimal left, BigDecimal right, String operator) {
|
|
||||||
switch (operator) {
|
|
||||||
case "+":
|
|
||||||
return left.add(right);
|
|
||||||
case "-":
|
|
||||||
return left.subtract(right);
|
|
||||||
case "*":
|
|
||||||
return left.multiply(right);
|
|
||||||
case "/":
|
|
||||||
if (BigDecimal.ZERO.compareTo(right) == 0) {
|
|
||||||
throw new IllegalArgumentException("除数不能为0");
|
|
||||||
}
|
|
||||||
return left.divide(right, 10, RoundingMode.HALF_UP);
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("不支持的操作符: " + operator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean shouldPopOperator(ExpressionToken currentOperator, ExpressionToken stackOperator) {
|
|
||||||
if (currentOperator == null || stackOperator == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int currentPriority = getOperatorPriority(currentOperator.getText());
|
|
||||||
int stackPriority = getOperatorPriority(stackOperator.getText());
|
|
||||||
if (isRightAssociative(currentOperator.getText())) {
|
|
||||||
return stackPriority > currentPriority;
|
|
||||||
}
|
|
||||||
return stackPriority >= currentPriority;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getOperatorPriority(String operator) {
|
|
||||||
if ("~".equals(operator)) {
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
if ("*".equals(operator) || "/".equals(operator)) {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
if ("+".equals(operator) || "-".equals(operator)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isRightAssociative(String operator) {
|
|
||||||
return "~".equals(operator);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isOperator(char ch) {
|
|
||||||
return ch == '+' || ch == '-' || ch == '*' || ch == '/';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void flushPointDataSafely() {
|
private void flushPointDataSafely() {
|
||||||
@ -864,7 +678,7 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
|
|||||||
}
|
}
|
||||||
Map<String, EmsPointConfig> uniqueByPointId = new LinkedHashMap<>();
|
Map<String, EmsPointConfig> uniqueByPointId = new LinkedHashMap<>();
|
||||||
for (EmsPointConfig calcPoint : calcPoints) {
|
for (EmsPointConfig calcPoint : calcPoints) {
|
||||||
if (!isCalcPoint(calcPoint)) {
|
if (!isComputedPoint(calcPoint)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String calcKey = resolvePointContextKey(calcPoint);
|
String calcKey = resolvePointContextKey(calcPoint);
|
||||||
@ -925,56 +739,622 @@ public class DeviceDataProcessServiceImpl extends AbstractBatteryDataProcessor i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum ExpressionTokenType {
|
private enum ExprTokenType {
|
||||||
NUMBER,
|
NUMBER,
|
||||||
VARIABLE,
|
STRING,
|
||||||
|
IDENTIFIER,
|
||||||
OPERATOR,
|
OPERATOR,
|
||||||
LEFT_PAREN,
|
LEFT_PAREN,
|
||||||
RIGHT_PAREN
|
RIGHT_PAREN,
|
||||||
|
COMMA,
|
||||||
|
QUESTION,
|
||||||
|
COLON,
|
||||||
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ExpressionToken {
|
private static class ExprToken {
|
||||||
private final ExpressionTokenType type;
|
private final ExprTokenType type;
|
||||||
private final String text;
|
private final String text;
|
||||||
private final BigDecimal number;
|
private final BigDecimal number;
|
||||||
|
private final String stringValue;
|
||||||
|
|
||||||
private ExpressionToken(ExpressionTokenType type, String text, BigDecimal number) {
|
private ExprToken(ExprTokenType type, String text, BigDecimal number, String stringValue) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.text = text;
|
this.text = text;
|
||||||
this.number = number;
|
this.number = number;
|
||||||
|
this.stringValue = stringValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ExpressionToken number(BigDecimal value) {
|
private static ExprToken number(BigDecimal value) {
|
||||||
return new ExpressionToken(ExpressionTokenType.NUMBER, null, value);
|
return new ExprToken(ExprTokenType.NUMBER, null, value, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ExpressionToken variable(String variable) {
|
private static ExprToken string(String value) {
|
||||||
return new ExpressionToken(ExpressionTokenType.VARIABLE, variable, null);
|
return new ExprToken(ExprTokenType.STRING, null, null, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ExpressionToken operator(String operator) {
|
private static ExprToken identifier(String value) {
|
||||||
return new ExpressionToken(ExpressionTokenType.OPERATOR, operator, null);
|
return new ExprToken(ExprTokenType.IDENTIFIER, value, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ExpressionToken leftParen() {
|
private static ExprToken operator(String value) {
|
||||||
return new ExpressionToken(ExpressionTokenType.LEFT_PAREN, "(", null);
|
return new ExprToken(ExprTokenType.OPERATOR, value, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ExpressionToken rightParen() {
|
private static ExprToken symbol(ExprTokenType type, String text) {
|
||||||
return new ExpressionToken(ExpressionTokenType.RIGHT_PAREN, ")", null);
|
return new ExprToken(type, text, null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ExpressionValue {
|
||||||
|
private final BigDecimal numberValue;
|
||||||
|
private final String textValue;
|
||||||
|
|
||||||
|
private ExpressionValue(BigDecimal numberValue, String textValue) {
|
||||||
|
this.numberValue = numberValue;
|
||||||
|
this.textValue = textValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExpressionTokenType getType() {
|
private static ExpressionValue ofNumber(BigDecimal numberValue) {
|
||||||
return type;
|
return new ExpressionValue(numberValue, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getText() {
|
private static ExpressionValue ofText(String textValue) {
|
||||||
return text;
|
return new ExpressionValue(null, textValue == null ? "" : textValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private BigDecimal getNumber() {
|
private boolean isNumber() {
|
||||||
return number;
|
return numberValue != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BigDecimal asNumber() {
|
||||||
|
if (numberValue == null) {
|
||||||
|
throw new IllegalArgumentException("表达式值不是数值类型: " + textValue);
|
||||||
|
}
|
||||||
|
return numberValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String asText() {
|
||||||
|
return numberValue != null ? numberValue.stripTrailingZeros().toPlainString() : textValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean asBoolean() {
|
||||||
|
return isNumber() ? BigDecimal.ZERO.compareTo(numberValue) != 0 : StringUtils.isNotBlank(textValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface ExpressionNode {
|
||||||
|
ExpressionValue evaluate(Map<String, BigDecimal> contextValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class NumberNode implements ExpressionNode {
|
||||||
|
private final BigDecimal value;
|
||||||
|
|
||||||
|
private NumberNode(BigDecimal value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExpressionValue evaluate(Map<String, BigDecimal> contextValues) {
|
||||||
|
return ExpressionValue.ofNumber(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class StringNode implements ExpressionNode {
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
private StringNode(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExpressionValue evaluate(Map<String, BigDecimal> contextValues) {
|
||||||
|
return ExpressionValue.ofText(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class VariableNode implements ExpressionNode {
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private VariableNode(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExpressionValue evaluate(Map<String, BigDecimal> contextValues) {
|
||||||
|
BigDecimal value = contextValues.get(name);
|
||||||
|
if (value == null) {
|
||||||
|
throw new MissingVariableException(name);
|
||||||
|
}
|
||||||
|
return ExpressionValue.ofNumber(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class UnaryNode implements ExpressionNode {
|
||||||
|
private final String operator;
|
||||||
|
private final ExpressionNode node;
|
||||||
|
|
||||||
|
private UnaryNode(String operator, ExpressionNode node) {
|
||||||
|
this.operator = operator;
|
||||||
|
this.node = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExpressionValue evaluate(Map<String, BigDecimal> contextValues) {
|
||||||
|
ExpressionValue value = node.evaluate(contextValues);
|
||||||
|
switch (operator) {
|
||||||
|
case "+":
|
||||||
|
return value;
|
||||||
|
case "-":
|
||||||
|
return ExpressionValue.ofNumber(value.asNumber().negate());
|
||||||
|
case "!":
|
||||||
|
return value.asBoolean() ? ExpressionValue.ofNumber(BigDecimal.ZERO) : ExpressionValue.ofNumber(BigDecimal.ONE);
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("不支持的一元操作符: " + operator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BinaryNode implements ExpressionNode {
|
||||||
|
private final String operator;
|
||||||
|
private final ExpressionNode left;
|
||||||
|
private final ExpressionNode right;
|
||||||
|
|
||||||
|
private BinaryNode(String operator, ExpressionNode left, ExpressionNode right) {
|
||||||
|
this.operator = operator;
|
||||||
|
this.left = left;
|
||||||
|
this.right = right;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExpressionValue evaluate(Map<String, BigDecimal> contextValues) {
|
||||||
|
if ("&&".equals(operator)) {
|
||||||
|
ExpressionValue leftValue = left.evaluate(contextValues);
|
||||||
|
if (!leftValue.asBoolean()) {
|
||||||
|
return ExpressionValue.ofNumber(BigDecimal.ZERO);
|
||||||
|
}
|
||||||
|
return right.evaluate(contextValues).asBoolean()
|
||||||
|
? ExpressionValue.ofNumber(BigDecimal.ONE)
|
||||||
|
: ExpressionValue.ofNumber(BigDecimal.ZERO);
|
||||||
|
}
|
||||||
|
if ("||".equals(operator)) {
|
||||||
|
ExpressionValue leftValue = left.evaluate(contextValues);
|
||||||
|
if (leftValue.asBoolean()) {
|
||||||
|
return ExpressionValue.ofNumber(BigDecimal.ONE);
|
||||||
|
}
|
||||||
|
return right.evaluate(contextValues).asBoolean()
|
||||||
|
? ExpressionValue.ofNumber(BigDecimal.ONE)
|
||||||
|
: ExpressionValue.ofNumber(BigDecimal.ZERO);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpressionValue leftValue = left.evaluate(contextValues);
|
||||||
|
ExpressionValue rightValue = right.evaluate(contextValues);
|
||||||
|
switch (operator) {
|
||||||
|
case "+":
|
||||||
|
if (!leftValue.isNumber() || !rightValue.isNumber()) {
|
||||||
|
return ExpressionValue.ofText(leftValue.asText() + rightValue.asText());
|
||||||
|
}
|
||||||
|
return ExpressionValue.ofNumber(leftValue.asNumber().add(rightValue.asNumber()));
|
||||||
|
case "-":
|
||||||
|
return ExpressionValue.ofNumber(leftValue.asNumber().subtract(rightValue.asNumber()));
|
||||||
|
case "*":
|
||||||
|
return ExpressionValue.ofNumber(leftValue.asNumber().multiply(rightValue.asNumber()));
|
||||||
|
case "/":
|
||||||
|
if (BigDecimal.ZERO.compareTo(rightValue.asNumber()) == 0) {
|
||||||
|
throw new IllegalArgumentException("除数不能为0");
|
||||||
|
}
|
||||||
|
return ExpressionValue.ofNumber(leftValue.asNumber().divide(rightValue.asNumber(), 10, RoundingMode.HALF_UP));
|
||||||
|
case ">":
|
||||||
|
return leftValue.asNumber().compareTo(rightValue.asNumber()) > 0
|
||||||
|
? ExpressionValue.ofNumber(BigDecimal.ONE) : ExpressionValue.ofNumber(BigDecimal.ZERO);
|
||||||
|
case ">=":
|
||||||
|
return leftValue.asNumber().compareTo(rightValue.asNumber()) >= 0
|
||||||
|
? ExpressionValue.ofNumber(BigDecimal.ONE) : ExpressionValue.ofNumber(BigDecimal.ZERO);
|
||||||
|
case "<":
|
||||||
|
return leftValue.asNumber().compareTo(rightValue.asNumber()) < 0
|
||||||
|
? ExpressionValue.ofNumber(BigDecimal.ONE) : ExpressionValue.ofNumber(BigDecimal.ZERO);
|
||||||
|
case "<=":
|
||||||
|
return leftValue.asNumber().compareTo(rightValue.asNumber()) <= 0
|
||||||
|
? ExpressionValue.ofNumber(BigDecimal.ONE) : ExpressionValue.ofNumber(BigDecimal.ZERO);
|
||||||
|
case "==":
|
||||||
|
if (leftValue.isNumber() && rightValue.isNumber()) {
|
||||||
|
return leftValue.asNumber().compareTo(rightValue.asNumber()) == 0
|
||||||
|
? ExpressionValue.ofNumber(BigDecimal.ONE) : ExpressionValue.ofNumber(BigDecimal.ZERO);
|
||||||
|
}
|
||||||
|
return Objects.equals(leftValue.asText(), rightValue.asText())
|
||||||
|
? ExpressionValue.ofNumber(BigDecimal.ONE) : ExpressionValue.ofNumber(BigDecimal.ZERO);
|
||||||
|
case "!=":
|
||||||
|
if (leftValue.isNumber() && rightValue.isNumber()) {
|
||||||
|
return leftValue.asNumber().compareTo(rightValue.asNumber()) != 0
|
||||||
|
? ExpressionValue.ofNumber(BigDecimal.ONE) : ExpressionValue.ofNumber(BigDecimal.ZERO);
|
||||||
|
}
|
||||||
|
return !Objects.equals(leftValue.asText(), rightValue.asText())
|
||||||
|
? ExpressionValue.ofNumber(BigDecimal.ONE) : ExpressionValue.ofNumber(BigDecimal.ZERO);
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("不支持的操作符: " + operator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TernaryNode implements ExpressionNode {
|
||||||
|
private final ExpressionNode condition;
|
||||||
|
private final ExpressionNode trueNode;
|
||||||
|
private final ExpressionNode falseNode;
|
||||||
|
|
||||||
|
private TernaryNode(ExpressionNode condition, ExpressionNode trueNode, ExpressionNode falseNode) {
|
||||||
|
this.condition = condition;
|
||||||
|
this.trueNode = trueNode;
|
||||||
|
this.falseNode = falseNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExpressionValue evaluate(Map<String, BigDecimal> contextValues) {
|
||||||
|
return condition.evaluate(contextValues).asBoolean()
|
||||||
|
? trueNode.evaluate(contextValues)
|
||||||
|
: falseNode.evaluate(contextValues);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CompiledExpression {
|
||||||
|
private final ExpressionNode root;
|
||||||
|
|
||||||
|
private CompiledExpression(String expression) {
|
||||||
|
List<ExprToken> tokens = tokenizeExpression(expression);
|
||||||
|
ExpressionParser parser = new ExpressionParser(tokens);
|
||||||
|
this.root = parser.parseExpression();
|
||||||
|
if (!parser.isEnd()) {
|
||||||
|
ExprToken token = parser.peek();
|
||||||
|
throw new IllegalArgumentException("表达式尾部有多余内容: " + token.text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExpressionValue evaluate(Map<String, BigDecimal> contextValues) {
|
||||||
|
return root.evaluate(contextValues);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ExpressionParser {
|
||||||
|
private final List<ExprToken> tokens;
|
||||||
|
private int index;
|
||||||
|
|
||||||
|
private ExpressionParser(List<ExprToken> tokens) {
|
||||||
|
this.tokens = tokens;
|
||||||
|
this.index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExpressionNode parseExpression() {
|
||||||
|
return parseTernary();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExpressionNode parseTernary() {
|
||||||
|
ExpressionNode condition = parseOr();
|
||||||
|
if (match(ExprTokenType.QUESTION)) {
|
||||||
|
ExpressionNode trueNode = parseTernary();
|
||||||
|
expect(ExprTokenType.COLON, "三元表达式缺少 ':'");
|
||||||
|
ExpressionNode falseNode = parseTernary();
|
||||||
|
return new TernaryNode(condition, trueNode, falseNode);
|
||||||
|
}
|
||||||
|
return condition;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExpressionNode parseOr() {
|
||||||
|
ExpressionNode left = parseAnd();
|
||||||
|
while (matchOperator("||")) {
|
||||||
|
left = new BinaryNode("||", left, parseAnd());
|
||||||
|
}
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExpressionNode parseAnd() {
|
||||||
|
ExpressionNode left = parseEquality();
|
||||||
|
while (matchOperator("&&")) {
|
||||||
|
left = new BinaryNode("&&", left, parseEquality());
|
||||||
|
}
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExpressionNode parseEquality() {
|
||||||
|
ExpressionNode left = parseComparison();
|
||||||
|
while (true) {
|
||||||
|
if (matchOperator("==")) {
|
||||||
|
left = new BinaryNode("==", left, parseComparison());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (matchOperator("!=")) {
|
||||||
|
left = new BinaryNode("!=", left, parseComparison());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExpressionNode parseComparison() {
|
||||||
|
ExpressionNode left = parseAddSub();
|
||||||
|
while (true) {
|
||||||
|
if (matchOperator(">=")) {
|
||||||
|
left = new BinaryNode(">=", left, parseAddSub());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (matchOperator("<=")) {
|
||||||
|
left = new BinaryNode("<=", left, parseAddSub());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (matchOperator(">")) {
|
||||||
|
left = new BinaryNode(">", left, parseAddSub());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (matchOperator("<")) {
|
||||||
|
left = new BinaryNode("<", left, parseAddSub());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExpressionNode parseAddSub() {
|
||||||
|
ExpressionNode left = parseMulDiv();
|
||||||
|
while (true) {
|
||||||
|
if (matchOperator("+")) {
|
||||||
|
left = new BinaryNode("+", left, parseMulDiv());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (matchOperator("-")) {
|
||||||
|
left = new BinaryNode("-", left, parseMulDiv());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExpressionNode parseMulDiv() {
|
||||||
|
ExpressionNode left = parseUnary();
|
||||||
|
while (true) {
|
||||||
|
if (matchOperator("*")) {
|
||||||
|
left = new BinaryNode("*", left, parseUnary());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (matchOperator("/")) {
|
||||||
|
left = new BinaryNode("/", left, parseUnary());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExpressionNode parseUnary() {
|
||||||
|
if (matchOperator("+")) {
|
||||||
|
return new UnaryNode("+", parseUnary());
|
||||||
|
}
|
||||||
|
if (matchOperator("-")) {
|
||||||
|
return new UnaryNode("-", parseUnary());
|
||||||
|
}
|
||||||
|
if (matchOperator("!")) {
|
||||||
|
return new UnaryNode("!", parseUnary());
|
||||||
|
}
|
||||||
|
return parsePrimary();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExpressionNode parsePrimary() {
|
||||||
|
ExprToken token = peek();
|
||||||
|
if (match(ExprTokenType.NUMBER)) {
|
||||||
|
return new NumberNode(token.number);
|
||||||
|
}
|
||||||
|
if (match(ExprTokenType.STRING)) {
|
||||||
|
return new StringNode(token.stringValue);
|
||||||
|
}
|
||||||
|
if (match(ExprTokenType.IDENTIFIER)) {
|
||||||
|
String identifier = token.text;
|
||||||
|
if (match(ExprTokenType.LEFT_PAREN)) {
|
||||||
|
if (!"IF".equalsIgnoreCase(identifier)) {
|
||||||
|
throw new IllegalArgumentException("不支持的函数: " + identifier);
|
||||||
|
}
|
||||||
|
ExpressionNode condition = parseExpression();
|
||||||
|
expect(ExprTokenType.COMMA, "IF函数参数格式错误,缺少第1个逗号");
|
||||||
|
ExpressionNode trueNode = parseExpression();
|
||||||
|
expect(ExprTokenType.COMMA, "IF函数参数格式错误,缺少第2个逗号");
|
||||||
|
ExpressionNode falseNode = parseExpression();
|
||||||
|
expect(ExprTokenType.RIGHT_PAREN, "IF函数缺少右括号");
|
||||||
|
return new TernaryNode(condition, trueNode, falseNode);
|
||||||
|
}
|
||||||
|
return new VariableNode(identifier.toUpperCase());
|
||||||
|
}
|
||||||
|
if (match(ExprTokenType.LEFT_PAREN)) {
|
||||||
|
ExpressionNode nested = parseExpression();
|
||||||
|
expect(ExprTokenType.RIGHT_PAREN, "括号不匹配,缺少右括号");
|
||||||
|
return nested;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("表达式语法错误,当前位置: " + token.text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExprToken peek() {
|
||||||
|
if (index >= tokens.size()) {
|
||||||
|
return ExprToken.symbol(ExprTokenType.EOF, "<eof>");
|
||||||
|
}
|
||||||
|
return tokens.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isEnd() {
|
||||||
|
return peek().type == ExprTokenType.EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean match(ExprTokenType type) {
|
||||||
|
if (peek().type != type) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean matchOperator(String operator) {
|
||||||
|
ExprToken token = peek();
|
||||||
|
if (token.type != ExprTokenType.OPERATOR || !operator.equals(token.text)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expect(ExprTokenType type, String message) {
|
||||||
|
if (!match(type)) {
|
||||||
|
throw new IllegalArgumentException(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<ExprToken> tokenizeExpression(String expression) {
|
||||||
|
if (StringUtils.isBlank(expression)) {
|
||||||
|
throw new IllegalArgumentException("计算表达式为空");
|
||||||
|
}
|
||||||
|
List<ExprToken> tokens = new ArrayList<>();
|
||||||
|
int index = 0;
|
||||||
|
while (index < expression.length()) {
|
||||||
|
char ch = expression.charAt(index);
|
||||||
|
if (Character.isWhitespace(ch)) {
|
||||||
|
index++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (Character.isDigit(ch) || ch == '.') {
|
||||||
|
int start = index;
|
||||||
|
boolean hasDot = ch == '.';
|
||||||
|
index++;
|
||||||
|
while (index < expression.length()) {
|
||||||
|
char next = expression.charAt(index);
|
||||||
|
if (Character.isDigit(next)) {
|
||||||
|
index++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (next == '.' && !hasDot) {
|
||||||
|
hasDot = true;
|
||||||
|
index++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
String text = expression.substring(start, index);
|
||||||
|
try {
|
||||||
|
tokens.add(ExprToken.number(new BigDecimal(text)));
|
||||||
|
} catch (NumberFormatException ex) {
|
||||||
|
throw new IllegalArgumentException("数值格式错误: " + text);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (Character.isLetter(ch) || ch == '_') {
|
||||||
|
int start = index;
|
||||||
|
index++;
|
||||||
|
while (index < expression.length()) {
|
||||||
|
char next = expression.charAt(index);
|
||||||
|
if (Character.isLetterOrDigit(next) || next == '_') {
|
||||||
|
index++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
String text = expression.substring(start, index).trim().toUpperCase();
|
||||||
|
tokens.add(ExprToken.identifier(text));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ch == '\'' || ch == '"') {
|
||||||
|
char quote = ch;
|
||||||
|
index++;
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
boolean escaped = false;
|
||||||
|
while (index < expression.length()) {
|
||||||
|
char current = expression.charAt(index++);
|
||||||
|
if (escaped) {
|
||||||
|
switch (current) {
|
||||||
|
case 'n':
|
||||||
|
sb.append('\n');
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
sb.append('\t');
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
sb.append('\r');
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
sb.append('\\');
|
||||||
|
break;
|
||||||
|
case '\'':
|
||||||
|
sb.append('\'');
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
sb.append('"');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sb.append(current);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
escaped = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (current == '\\') {
|
||||||
|
escaped = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (current == quote) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sb.append(current);
|
||||||
|
}
|
||||||
|
if (escaped || index > expression.length() || expression.charAt(index - 1) != quote) {
|
||||||
|
throw new IllegalArgumentException("字符串字面量未闭合");
|
||||||
|
}
|
||||||
|
tokens.add(ExprToken.string(sb.toString()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ch == '(') {
|
||||||
|
tokens.add(ExprToken.symbol(ExprTokenType.LEFT_PAREN, "("));
|
||||||
|
index++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ch == ')') {
|
||||||
|
tokens.add(ExprToken.symbol(ExprTokenType.RIGHT_PAREN, ")"));
|
||||||
|
index++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ch == ',') {
|
||||||
|
tokens.add(ExprToken.symbol(ExprTokenType.COMMA, ","));
|
||||||
|
index++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ch == '?') {
|
||||||
|
tokens.add(ExprToken.symbol(ExprTokenType.QUESTION, "?"));
|
||||||
|
index++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ch == ':') {
|
||||||
|
tokens.add(ExprToken.symbol(ExprTokenType.COLON, ":"));
|
||||||
|
index++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (index + 1 < expression.length()) {
|
||||||
|
String twoChars = expression.substring(index, index + 2);
|
||||||
|
if ("&&".equals(twoChars)
|
||||||
|
|| "||".equals(twoChars)
|
||||||
|
|| ">=".equals(twoChars)
|
||||||
|
|| "<=".equals(twoChars)
|
||||||
|
|| "==".equals(twoChars)
|
||||||
|
|| "!=".equals(twoChars)) {
|
||||||
|
tokens.add(ExprToken.operator(twoChars));
|
||||||
|
index += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ch == '+' || ch == '-' || ch == '*' || ch == '/'
|
||||||
|
|| ch == '>' || ch == '<' || ch == '!') {
|
||||||
|
tokens.add(ExprToken.operator(String.valueOf(ch)));
|
||||||
|
index++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("表达式包含非法字符: " + ch);
|
||||||
|
}
|
||||||
|
tokens.add(ExprToken.symbol(ExprTokenType.EOF, "<eof>"));
|
||||||
|
return tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
private JSONArray parseJsonData(String message) {
|
private JSONArray parseJsonData(String message) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -50,7 +50,6 @@ import java.util.stream.Collectors;
|
|||||||
public class EmsPointConfigServiceImpl implements IEmsPointConfigService {
|
public class EmsPointConfigServiceImpl implements IEmsPointConfigService {
|
||||||
private static final Logger log = LoggerFactory.getLogger(EmsPointConfigServiceImpl.class);
|
private static final Logger log = LoggerFactory.getLogger(EmsPointConfigServiceImpl.class);
|
||||||
private static final String TEMPLATE_SITE_ID = "DEFAULT";
|
private static final String TEMPLATE_SITE_ID = "DEFAULT";
|
||||||
private static final String SITE_LEVEL_CALC_DEVICE_ID = "SITE_CALC";
|
|
||||||
private static final int CSV_IMPORT_BATCH_SIZE = 200;
|
private static final int CSV_IMPORT_BATCH_SIZE = 200;
|
||||||
private static final Pattern DB_NAME_PATTERN = Pattern.compile("^[A-Za-z0-9_]+$");
|
private static final Pattern DB_NAME_PATTERN = Pattern.compile("^[A-Za-z0-9_]+$");
|
||||||
private static final Pattern INTEGER_PATTERN = Pattern.compile("^-?\\d+$");
|
private static final Pattern INTEGER_PATTERN = Pattern.compile("^-?\\d+$");
|
||||||
@ -249,19 +248,13 @@ public class EmsPointConfigServiceImpl implements IEmsPointConfigService {
|
|||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
String siteId = StringUtils.trim(request.getSiteId());
|
String siteId = StringUtils.trim(request.getSiteId());
|
||||||
String deviceId = StringUtils.trim(request.getDeviceId());
|
|
||||||
String pointId = StringUtils.trim(request.getPointId());
|
String pointId = StringUtils.trim(request.getPointId());
|
||||||
if (StringUtils.isAnyBlank(siteId, pointId)) {
|
if (StringUtils.isAnyBlank(siteId, pointId)) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
EmsPointConfig pointConfig = resolvePointConfigForCurve(siteId, pointId);
|
EmsPointConfig pointConfig = resolvePointConfigForCurve(siteId, pointId);
|
||||||
String pointType = resolvePointTypeForCurve(request, pointConfig);
|
|
||||||
String queryDeviceId = resolveCurveDeviceId(pointType, deviceId, pointConfig);
|
|
||||||
if (StringUtils.isBlank(queryDeviceId)) {
|
|
||||||
return new ArrayList<>();
|
|
||||||
}
|
|
||||||
Date[] range = resolveTimeRange(request);
|
Date[] range = resolveTimeRange(request);
|
||||||
return queryCurveDataFromInflux(siteId, queryDeviceId, pointId, pointConfig, range[0], range[1]);
|
return queryCurveDataFromInflux(siteId, pointId, pointConfig, range[0], range[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PointConfigLatestValueVo queryLatestValueFromRedis(PointConfigLatestValueItemVo item,
|
private PointConfigLatestValueVo queryLatestValueFromRedis(PointConfigLatestValueItemVo item,
|
||||||
@ -296,13 +289,13 @@ public class EmsPointConfigServiceImpl implements IEmsPointConfigService {
|
|||||||
return vo;
|
return vo;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<PointConfigCurveValueVo> queryCurveDataFromInflux(String siteId, String deviceId, String pointId,
|
private List<PointConfigCurveValueVo> queryCurveDataFromInflux(String siteId, String pointId,
|
||||||
EmsPointConfig pointConfig, Date startTime, Date endTime) {
|
EmsPointConfig pointConfig, Date startTime, Date endTime) {
|
||||||
String influxPointKey = resolveInfluxPointKey(pointConfig, pointId);
|
String influxPointKey = resolveInfluxPointKey(pointConfig, pointId);
|
||||||
if (StringUtils.isBlank(influxPointKey)) {
|
if (StringUtils.isBlank(influxPointKey)) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
List<InfluxPointDataWriter.PointValue> values = influxPointDataWriter.queryCurveData(siteId, deviceId, influxPointKey, startTime, endTime);
|
List<InfluxPointDataWriter.PointValue> values = influxPointDataWriter.queryCurveDataByPointKey(siteId, influxPointKey, startTime, endTime);
|
||||||
if (values == null || values.isEmpty()) {
|
if (values == null || values.isEmpty()) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
@ -518,29 +511,6 @@ public class EmsPointConfigServiceImpl implements IEmsPointConfigService {
|
|||||||
return pointConfigs.get(0);
|
return pointConfigs.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String resolvePointTypeForCurve(PointConfigCurveRequest request, EmsPointConfig pointConfig) {
|
|
||||||
if (pointConfig != null && StringUtils.isNotBlank(pointConfig.getPointType())) {
|
|
||||||
return StringUtils.trim(pointConfig.getPointType()).toLowerCase(Locale.ROOT);
|
|
||||||
}
|
|
||||||
if (request == null || StringUtils.isBlank(request.getPointType())) {
|
|
||||||
return "data";
|
|
||||||
}
|
|
||||||
return StringUtils.trim(request.getPointType()).toLowerCase(Locale.ROOT);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String resolveCurveDeviceId(String pointType, String requestDeviceId, EmsPointConfig pointConfig) {
|
|
||||||
if ("calc".equalsIgnoreCase(StringUtils.defaultString(pointType))) {
|
|
||||||
return SITE_LEVEL_CALC_DEVICE_ID;
|
|
||||||
}
|
|
||||||
if (StringUtils.isNotBlank(requestDeviceId)) {
|
|
||||||
return StringUtils.trim(requestDeviceId);
|
|
||||||
}
|
|
||||||
if (pointConfig != null && StringUtils.isNotBlank(pointConfig.getDeviceId())) {
|
|
||||||
return StringUtils.trim(pointConfig.getDeviceId());
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private BigDecimal convertPointValue(BigDecimal sourceValue, EmsPointConfig pointConfig) {
|
private BigDecimal convertPointValue(BigDecimal sourceValue, EmsPointConfig pointConfig) {
|
||||||
if (sourceValue == null || pointConfig == null) {
|
if (sourceValue == null || pointConfig == null) {
|
||||||
return sourceValue;
|
return sourceValue;
|
||||||
|
|||||||
@ -26,6 +26,12 @@ public class EmsStrategyRuntimeConfigServiceImpl implements IEmsStrategyRuntimeC
|
|||||||
private static final BigDecimal DEFAULT_ANTI_REVERSE_UP = new BigDecimal("100");
|
private static final BigDecimal DEFAULT_ANTI_REVERSE_UP = new BigDecimal("100");
|
||||||
private static final BigDecimal DEFAULT_ANTI_REVERSE_POWER_DOWN_PERCENT = new BigDecimal("10");
|
private static final BigDecimal DEFAULT_ANTI_REVERSE_POWER_DOWN_PERCENT = new BigDecimal("10");
|
||||||
private static final BigDecimal DEFAULT_ANTI_REVERSE_HARD_STOP_THRESHOLD = new BigDecimal("20");
|
private static final BigDecimal DEFAULT_ANTI_REVERSE_HARD_STOP_THRESHOLD = new BigDecimal("20");
|
||||||
|
private static final BigDecimal DEFAULT_POWER_SET_MULTIPLIER = new BigDecimal("10");
|
||||||
|
private static final Integer DEFAULT_PROTECT_INTERVENE_ENABLE = 1;
|
||||||
|
private static final BigDecimal DEFAULT_PROTECT_L1_DERATE_PERCENT = new BigDecimal("50");
|
||||||
|
private static final Integer DEFAULT_PROTECT_RECOVERY_STABLE_SECONDS = 5;
|
||||||
|
private static final Integer DEFAULT_PROTECT_L3_LATCH_ENABLE = 1;
|
||||||
|
private static final String DEFAULT_PROTECT_CONFLICT_POLICY = "MAX_LEVEL_WIN";
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private EmsStrategyRuntimeConfigMapper runtimeConfigMapper;
|
private EmsStrategyRuntimeConfigMapper runtimeConfigMapper;
|
||||||
@ -83,5 +89,23 @@ public class EmsStrategyRuntimeConfigServiceImpl implements IEmsStrategyRuntimeC
|
|||||||
if (config.getAntiReverseHardStopThreshold() == null) {
|
if (config.getAntiReverseHardStopThreshold() == null) {
|
||||||
config.setAntiReverseHardStopThreshold(DEFAULT_ANTI_REVERSE_HARD_STOP_THRESHOLD);
|
config.setAntiReverseHardStopThreshold(DEFAULT_ANTI_REVERSE_HARD_STOP_THRESHOLD);
|
||||||
}
|
}
|
||||||
|
if (config.getPowerSetMultiplier() == null || config.getPowerSetMultiplier().compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
config.setPowerSetMultiplier(DEFAULT_POWER_SET_MULTIPLIER);
|
||||||
|
}
|
||||||
|
if (config.getProtectInterveneEnable() == null) {
|
||||||
|
config.setProtectInterveneEnable(DEFAULT_PROTECT_INTERVENE_ENABLE);
|
||||||
|
}
|
||||||
|
if (config.getProtectL1DeratePercent() == null || config.getProtectL1DeratePercent().compareTo(BigDecimal.ZERO) < 0) {
|
||||||
|
config.setProtectL1DeratePercent(DEFAULT_PROTECT_L1_DERATE_PERCENT);
|
||||||
|
}
|
||||||
|
if (config.getProtectRecoveryStableSeconds() == null || config.getProtectRecoveryStableSeconds() < 0) {
|
||||||
|
config.setProtectRecoveryStableSeconds(DEFAULT_PROTECT_RECOVERY_STABLE_SECONDS);
|
||||||
|
}
|
||||||
|
if (config.getProtectL3LatchEnable() == null) {
|
||||||
|
config.setProtectL3LatchEnable(DEFAULT_PROTECT_L3_LATCH_ENABLE);
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(config.getProtectConflictPolicy())) {
|
||||||
|
config.setProtectConflictPolicy(DEFAULT_PROTECT_CONFLICT_POLICY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,15 +49,12 @@ public class GeneralQueryServiceImpl implements IGeneralQueryService
|
|||||||
if (siteIds == null || siteIds.isEmpty()) {
|
if (siteIds == null || siteIds.isEmpty()) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
return emsPointConfigMapper.getPointNameList(
|
||||||
String deviceCategory = request.getDeviceCategory();
|
siteIds,
|
||||||
String deviceId = request.getDeviceId();
|
request.getDeviceCategory(),
|
||||||
if ((deviceCategory == null || "".equals(deviceCategory.trim()))
|
request.getDeviceId(),
|
||||||
&& (deviceId == null || "".equals(deviceId.trim()))) {
|
request.getPointName()
|
||||||
return Collections.emptyList();
|
);
|
||||||
}
|
|
||||||
|
|
||||||
return emsPointConfigMapper.getPointNameList(siteIds, deviceCategory, deviceId, request.getPointName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -94,15 +91,10 @@ public class GeneralQueryServiceImpl implements IGeneralQueryService
|
|||||||
}
|
}
|
||||||
|
|
||||||
String deviceCategory = request.getDeviceCategory();
|
String deviceCategory = request.getDeviceCategory();
|
||||||
String requestDeviceId = request.getDeviceId();
|
List<String> pointIds = resolvePointIds(request);
|
||||||
if ((deviceCategory == null || "".equals(deviceCategory.trim()))
|
|
||||||
&& (requestDeviceId == null || "".equals(requestDeviceId.trim()))
|
|
||||||
) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> pointNames = resolvePointNames(request);
|
List<String> pointNames = resolvePointNames(request);
|
||||||
if (pointNames.isEmpty()) {
|
if (pointIds.isEmpty() && pointNames.isEmpty()) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,17 +106,19 @@ public class GeneralQueryServiceImpl implements IGeneralQueryService
|
|||||||
endDate = DateUtils.adjustToEndOfDay(request.getEndDate());
|
endDate = DateUtils.adjustToEndOfDay(request.getEndDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> selectedDeviceIds = resolveSelectedDeviceIds(request);
|
List<String> selectedDeviceIds = pointIds.isEmpty() ? resolveSelectedDeviceIds(request) : Collections.emptyList();
|
||||||
List<EmsPointConfig> pointConfigs = emsPointConfigMapper.getConfigListForGeneralQuery(
|
List<EmsPointConfig> pointConfigs = emsPointConfigMapper.getConfigListForGeneralQuery(
|
||||||
siteIds, deviceCategory, pointNames, selectedDeviceIds
|
siteIds, deviceCategory, pointIds, pointNames, selectedDeviceIds
|
||||||
);
|
);
|
||||||
if (pointConfigs == null || pointConfigs.isEmpty()) {
|
if (pointConfigs == null || pointConfigs.isEmpty()) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
Map<String, String> selectedPointNameById = buildSelectedPointNameById(request);
|
||||||
|
|
||||||
List<GeneralQueryDataVo> dataVoList = new ArrayList<>();
|
List<GeneralQueryDataVo> dataVoList = new ArrayList<>();
|
||||||
for (EmsPointConfig pointConfig : pointConfigs) {
|
for (EmsPointConfig pointConfig : pointConfigs) {
|
||||||
dataVoList.addAll(queryPointCurve(pointConfig, request.getDataUnit(), startDate, endDate));
|
String selectedPointName = selectedPointNameById.get(resolveInfluxPointKey(pointConfig));
|
||||||
|
dataVoList.addAll(queryPointCurve(pointConfig, request.getDataUnit(), startDate, endDate, selectedPointName));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dataVoList.isEmpty()) {
|
if (dataVoList.isEmpty()) {
|
||||||
@ -154,6 +148,19 @@ public class GeneralQueryServiceImpl implements IGeneralQueryService
|
|||||||
return names.stream().distinct().collect(Collectors.toList());
|
return names.stream().distinct().collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<String> resolvePointIds(PointNameRequest request) {
|
||||||
|
List<String> ids = new ArrayList<>();
|
||||||
|
if (request.getPointIds() != null && !request.getPointIds().isEmpty()) {
|
||||||
|
ids.addAll(request.getPointIds());
|
||||||
|
} else if (request.getPointId() != null && !"".equals(request.getPointId().trim())) {
|
||||||
|
ids.addAll(Arrays.stream(request.getPointId().split(","))
|
||||||
|
.map(String::trim)
|
||||||
|
.filter(s -> !s.isEmpty())
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
return ids.stream().distinct().collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
private List<String> resolveSelectedDeviceIds(PointNameRequest request) {
|
private List<String> resolveSelectedDeviceIds(PointNameRequest request) {
|
||||||
List<String> selected = new ArrayList<>();
|
List<String> selected = new ArrayList<>();
|
||||||
if (request.getDeviceId() != null && !"".equals(request.getDeviceId().trim())) {
|
if (request.getDeviceId() != null && !"".equals(request.getDeviceId().trim())) {
|
||||||
@ -175,16 +182,18 @@ public class GeneralQueryServiceImpl implements IGeneralQueryService
|
|||||||
return selected.stream().distinct().collect(Collectors.toList());
|
return selected.stream().distinct().collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<GeneralQueryDataVo> queryPointCurve(EmsPointConfig config, int dataUnit, Date startDate, Date endDate) {
|
private List<GeneralQueryDataVo> queryPointCurve(EmsPointConfig config, int dataUnit, Date startDate, Date endDate,
|
||||||
if (config == null || config.getSiteId() == null || config.getDeviceId() == null) {
|
String selectedPointName) {
|
||||||
|
if (config == null || config.getSiteId() == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
String influxPointKey = resolveInfluxPointKey(config);
|
String influxPointKey = resolveInfluxPointKey(config);
|
||||||
if (influxPointKey == null) {
|
if (influxPointKey == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
List<InfluxPointDataWriter.PointValue> values = influxPointDataWriter.queryCurveData(
|
// 与点位列表曲线保持一致:按 siteId + pointKey 查询,避免 deviceId 维度导致综合查询漏数
|
||||||
config.getSiteId(), config.getDeviceId(), influxPointKey, startDate, endDate
|
List<InfluxPointDataWriter.PointValue> values = influxPointDataWriter.queryCurveDataByPointKey(
|
||||||
|
config.getSiteId(), influxPointKey, startDate, endDate
|
||||||
);
|
);
|
||||||
if (values == null || values.isEmpty()) {
|
if (values == null || values.isEmpty()) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
@ -197,7 +206,7 @@ public class GeneralQueryServiceImpl implements IGeneralQueryService
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<GeneralQueryDataVo> result = new ArrayList<>();
|
List<GeneralQueryDataVo> result = new ArrayList<>();
|
||||||
String displayDeviceId = buildDisplayDeviceId(config);
|
String displayDeviceId = buildDisplayDeviceId(config, selectedPointName);
|
||||||
for (Map.Entry<String, Object> entry : latestByBucket.entrySet()) {
|
for (Map.Entry<String, Object> entry : latestByBucket.entrySet()) {
|
||||||
GeneralQueryDataVo vo = new GeneralQueryDataVo();
|
GeneralQueryDataVo vo = new GeneralQueryDataVo();
|
||||||
vo.setSiteId(config.getSiteId());
|
vo.setSiteId(config.getSiteId());
|
||||||
@ -219,10 +228,32 @@ public class GeneralQueryServiceImpl implements IGeneralQueryService
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String buildDisplayDeviceId(EmsPointConfig config) {
|
private String buildDisplayDeviceId(EmsPointConfig config, String selectedPointName) {
|
||||||
|
if (selectedPointName != null && !"".equals(selectedPointName.trim())) {
|
||||||
|
return selectedPointName.trim();
|
||||||
|
}
|
||||||
String pointName = config.getPointName() == null || "".equals(config.getPointName().trim())
|
String pointName = config.getPointName() == null || "".equals(config.getPointName().trim())
|
||||||
? config.getDataKey() : config.getPointName().trim();
|
? config.getDataKey() : config.getPointName().trim();
|
||||||
return config.getDeviceId() + "-" + pointName;
|
return pointName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> buildSelectedPointNameById(PointNameRequest request) {
|
||||||
|
Map<String, String> selectedNameById = new HashMap<>();
|
||||||
|
if (request == null || request.getPointIds() == null || request.getPointNames() == null) {
|
||||||
|
return selectedNameById;
|
||||||
|
}
|
||||||
|
List<String> pointIds = request.getPointIds();
|
||||||
|
List<String> pointNames = request.getPointNames();
|
||||||
|
int size = Math.min(pointIds.size(), pointNames.size());
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
String pointId = pointIds.get(i);
|
||||||
|
String pointName = pointNames.get(i);
|
||||||
|
if (pointId == null || "".equals(pointId.trim()) || pointName == null || "".equals(pointName.trim())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
selectedNameById.put(pointId.trim(), pointName.trim());
|
||||||
|
}
|
||||||
|
return selectedNameById;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String formatByDataUnit(Date dataTime, int dataUnit) {
|
private String formatByDataUnit(Date dataTime, int dataUnit) {
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import com.xzzn.ems.domain.EmsCoolingData;
|
|||||||
import com.xzzn.ems.domain.EmsDhData;
|
import com.xzzn.ems.domain.EmsDhData;
|
||||||
import com.xzzn.ems.domain.EmsEmsData;
|
import com.xzzn.ems.domain.EmsEmsData;
|
||||||
import com.xzzn.ems.domain.EmsPcsBranchData;
|
import com.xzzn.ems.domain.EmsPcsBranchData;
|
||||||
import com.xzzn.ems.domain.EmsPcsData;
|
import com.xzzn.ems.domain.EmsSiteMonitorPointMatch;
|
||||||
import com.xzzn.ems.domain.EmsStrategyTemp;
|
import com.xzzn.ems.domain.EmsStrategyTemp;
|
||||||
import com.xzzn.ems.domain.EmsXfData;
|
import com.xzzn.ems.domain.EmsXfData;
|
||||||
import com.xzzn.ems.domain.vo.*;
|
import com.xzzn.ems.domain.vo.*;
|
||||||
@ -28,12 +28,15 @@ import com.xzzn.ems.mapper.EmsDevicesSettingMapper;
|
|||||||
import com.xzzn.ems.mapper.EmsEnergyPriceConfigMapper;
|
import com.xzzn.ems.mapper.EmsEnergyPriceConfigMapper;
|
||||||
import com.xzzn.ems.mapper.EmsPcsDataMapper;
|
import com.xzzn.ems.mapper.EmsPcsDataMapper;
|
||||||
import com.xzzn.ems.mapper.EmsPointMatchMapper;
|
import com.xzzn.ems.mapper.EmsPointMatchMapper;
|
||||||
|
import com.xzzn.ems.mapper.EmsSiteMonitorPointMatchMapper;
|
||||||
import com.xzzn.ems.mapper.EmsStrategyRunningMapper;
|
import com.xzzn.ems.mapper.EmsStrategyRunningMapper;
|
||||||
import com.xzzn.ems.mapper.EmsStrategyTempMapper;
|
import com.xzzn.ems.mapper.EmsStrategyTempMapper;
|
||||||
import com.xzzn.ems.service.IEmsEnergyPriceConfigService;
|
import com.xzzn.ems.service.IEmsEnergyPriceConfigService;
|
||||||
import com.xzzn.ems.service.ISingleSiteService;
|
import com.xzzn.ems.service.ISingleSiteService;
|
||||||
|
import com.xzzn.ems.service.InfluxPointDataWriter;
|
||||||
import com.xzzn.ems.utils.DevicePointMatchDataProcessor;
|
import com.xzzn.ems.utils.DevicePointMatchDataProcessor;
|
||||||
|
|
||||||
|
import java.beans.PropertyDescriptor;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
@ -42,6 +45,7 @@ import java.time.LocalTime;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@ -53,7 +57,9 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.BeanWrapper;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.beans.BeanWrapperImpl;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
@ -65,6 +71,25 @@ import org.springframework.util.CollectionUtils;
|
|||||||
public class SingleSiteServiceImpl implements ISingleSiteService {
|
public class SingleSiteServiceImpl implements ISingleSiteService {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(SingleSiteServiceImpl.class);
|
private static final Logger log = LoggerFactory.getLogger(SingleSiteServiceImpl.class);
|
||||||
|
private static final String RUNNING_GRAPH_DEBUG = "RunningGraphDebug";
|
||||||
|
private static final String FIELD_CURVE_PCS_ACTIVE_POWER = "SBJK_SSYX__curvePcsActivePower";
|
||||||
|
private static final String FIELD_CURVE_PCS_REACTIVE_POWER = "SBJK_SSYX__curvePcsReactivePower";
|
||||||
|
private static final String FIELD_CURVE_PCS_MAX_TEMP = "SBJK_SSYX__curvePcsMaxTemp";
|
||||||
|
private static final String FIELD_CURVE_BATTERY_AVE_SOC = "SBJK_SSYX__curveBatteryAveSoc";
|
||||||
|
private static final String FIELD_CURVE_BATTERY_AVE_TEMP = "SBJK_SSYX__curveBatteryAveTemp";
|
||||||
|
private static final String FIELD_TOTAL_ACTIVE_POWER = "SBJK_SSYX__totalActivePower";
|
||||||
|
private static final String FIELD_TOTAL_REACTIVE_POWER = "SBJK_SSYX__totalReactivePower";
|
||||||
|
private static final String FIELD_SOC = "SBJK_SSYX__soc";
|
||||||
|
private static final String FIELD_HOME_AVG_TEMP = "HOME__avgTemp";
|
||||||
|
private static final String RUNNING_GRAPH_DEFAULT_DEVICE = "SITE";
|
||||||
|
private static final int USE_FIXED_DISPLAY_YES = 1;
|
||||||
|
private static final String DEVICE_INFO_ID = "id";
|
||||||
|
private static final String DEVICE_INFO_NAME = "deviceName";
|
||||||
|
private static final String DEVICE_INFO_COMM_STATUS = "communicationStatus";
|
||||||
|
private static final String DEVICE_INFO_DEVICE_STATUS = "deviceStatus";
|
||||||
|
private static final Set<String> PCS_DETAIL_META_FIELDS = new HashSet<>(Arrays.asList(
|
||||||
|
"siteId", "deviceId", "deviceName", "alarmNum", "pcsBranchInfoList", "dataUpdateTime"
|
||||||
|
));
|
||||||
|
|
||||||
private static final String CLUSTER_DATA_TEP = "温度";
|
private static final String CLUSTER_DATA_TEP = "温度";
|
||||||
|
|
||||||
@ -90,6 +115,10 @@ public class SingleSiteServiceImpl implements ISingleSiteService {
|
|||||||
private EmsDevicesSettingMapper emsDevicesSettingMapper;
|
private EmsDevicesSettingMapper emsDevicesSettingMapper;
|
||||||
@Autowired
|
@Autowired
|
||||||
private EmsPointMatchMapper emsPointMatchMapper;
|
private EmsPointMatchMapper emsPointMatchMapper;
|
||||||
|
@Autowired
|
||||||
|
private EmsSiteMonitorPointMatchMapper emsSiteMonitorPointMatchMapper;
|
||||||
|
@Autowired
|
||||||
|
private InfluxPointDataWriter influxPointDataWriter;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private RedisCache redisCache;
|
private RedisCache redisCache;
|
||||||
@ -217,46 +246,61 @@ public class SingleSiteServiceImpl implements ISingleSiteService {
|
|||||||
public SiteMonitorRuningInfoVo getRunningGraphStorage(RunningGraphRequest request) {
|
public SiteMonitorRuningInfoVo getRunningGraphStorage(RunningGraphRequest request) {
|
||||||
SiteMonitorRuningInfoVo siteMonitorRuningInfoVo = new SiteMonitorRuningInfoVo();
|
SiteMonitorRuningInfoVo siteMonitorRuningInfoVo = new SiteMonitorRuningInfoVo();
|
||||||
if (Objects.isNull(request) || StringUtils.isEmpty(request.getSiteId())) {
|
if (Objects.isNull(request) || StringUtils.isEmpty(request.getSiteId())) {
|
||||||
|
log.info("{} storage skip, request invalid, request={}", RUNNING_GRAPH_DEBUG, request);
|
||||||
return siteMonitorRuningInfoVo;
|
return siteMonitorRuningInfoVo;
|
||||||
}
|
}
|
||||||
// // 时间暂定今日+昨日
|
String siteId = request.getSiteId();
|
||||||
// Date today = DateUtils.getNowDate();
|
Date[] dateRange = normalizeRunningGraphDateRange(request.getStartDate(), request.getEndDate());
|
||||||
// Date yesterday = DateUtils.addDays(today, -1);
|
Date startDate = dateRange[0];
|
||||||
Date startDate = request.getStartDate();
|
Date endDate = dateRange[1];
|
||||||
Date endDate = request.getEndDate();
|
List<EmsSiteMonitorPointMatch> mappingList = emsSiteMonitorPointMatchMapper.selectBySiteId(siteId);
|
||||||
|
Map<String, EmsSiteMonitorPointMatch> mappingByFieldAndDevice = buildMonitorPointMappingByFieldAndDevice(mappingList);
|
||||||
|
List<DeviceMeta> pcsDeviceList = getDeviceMetaListByCategory(siteId, DeviceCategory.PCS.getCode());
|
||||||
|
if (CollectionUtils.isEmpty(pcsDeviceList)) {
|
||||||
|
pcsDeviceList = Collections.singletonList(new DeviceMeta(RUNNING_GRAPH_DEFAULT_DEVICE, RUNNING_GRAPH_DEFAULT_DEVICE));
|
||||||
|
}
|
||||||
|
|
||||||
//pcs有功无功
|
|
||||||
List<PcsPowerList> pcsPowerList = new ArrayList<>();
|
List<PcsPowerList> pcsPowerList = new ArrayList<>();
|
||||||
List<EnergyStoragePowVo> energyStoragePowList = emsPcsDataMapper.getStoragePowerList(request.getSiteId(), startDate, endDate);
|
for (DeviceMeta pcsDevice : pcsDeviceList) {
|
||||||
|
String deviceId = StringUtils.defaultString(pcsDevice.getDeviceId());
|
||||||
|
String activePointId = firstNonBlankPointByDevice(mappingByFieldAndDevice, deviceId, FIELD_CURVE_PCS_ACTIVE_POWER);
|
||||||
|
String reactivePointId = firstNonBlankPointByDevice(mappingByFieldAndDevice, deviceId, FIELD_CURVE_PCS_REACTIVE_POWER);
|
||||||
|
|
||||||
// List<EnergyStoragePowVo> -> 按pcs的deviceId分组转成List<PcsPowerList>
|
List<InfluxPointDataWriter.PointValue> activeValues = queryInfluxPointValues(siteId, activePointId, startDate, endDate);
|
||||||
if (!CollectionUtils.isEmpty(energyStoragePowList)) {
|
List<InfluxPointDataWriter.PointValue> reactiveValues = queryInfluxPointValues(siteId, reactivePointId, startDate, endDate);
|
||||||
Map<String, List<EnergyStoragePowVo>> dataMap = energyStoragePowList.stream()
|
Map<Long, BigDecimal> reactiveByTs = reactiveValues.stream()
|
||||||
.collect(Collectors.groupingBy(
|
.filter(v -> v != null && v.getDataTime() != null && v.getPointValue() != null)
|
||||||
EnergyStoragePowVo::getDeviceId,
|
.collect(Collectors.toMap(v -> v.getDataTime().getTime(), InfluxPointDataWriter.PointValue::getPointValue, (a, b) -> b));
|
||||||
Collectors.toList()));
|
|
||||||
|
|
||||||
pcsPowerList = dataMap.entrySet().stream()
|
List<EnergyStoragePowVo> energyStoragePowList = new ArrayList<>();
|
||||||
.map(entry -> {
|
for (InfluxPointDataWriter.PointValue activeValue : activeValues) {
|
||||||
PcsPowerList pcdData = new PcsPowerList();
|
if (activeValue == null || activeValue.getDataTime() == null || activeValue.getPointValue() == null) {
|
||||||
pcdData.setDeviceId(entry.getKey());
|
continue;
|
||||||
pcdData.setEnergyStoragePowList(entry.getValue());
|
}
|
||||||
return pcdData;
|
Date dataTime = activeValue.getDataTime();
|
||||||
}).collect(Collectors.toList());
|
EnergyStoragePowVo vo = new EnergyStoragePowVo();
|
||||||
|
vo.setDeviceId(deviceId);
|
||||||
|
vo.setDateDay(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, dataTime));
|
||||||
|
vo.setCreateDate(DateUtils.parseDateToStr("HH:mm:00", dataTime));
|
||||||
|
vo.setGroupTime(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, dataTime));
|
||||||
|
vo.setPcsTotalActPower(activeValue.getPointValue());
|
||||||
|
vo.setPcsTotalReactivePower(reactiveByTs.get(dataTime.getTime()));
|
||||||
|
energyStoragePowList.add(vo);
|
||||||
|
}
|
||||||
|
|
||||||
// // 生成时间列表(每5分钟一个)
|
PcsPowerList pcdData = new PcsPowerList();
|
||||||
// List<LocalDateTime> targetMinutes = new ArrayList<>(12);
|
pcdData.setDeviceId(deviceId);
|
||||||
// LocalDateTime startLocalDate = DateUtils.toLocalDateTime(startDate).truncatedTo(ChronoUnit.DAYS);
|
pcdData.setEnergyStoragePowList(energyStoragePowList);
|
||||||
// LocalDateTime endLocalDate = DateUtils.toLocalDateTime(endDate).with(LocalDateTime.now().toLocalTime());
|
pcsPowerList.add(pcdData);
|
||||||
// while (startLocalDate.isBefore(endLocalDate)) {
|
|
||||||
// targetMinutes.add(startLocalDate);
|
|
||||||
// startLocalDate = startLocalDate.plusMinutes(5); // 递增5分钟
|
|
||||||
// }
|
|
||||||
// // 根据时间列表填充数据
|
|
||||||
// pcsPowerList = fullFillData(pcsPowerList, targetMinutes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
siteMonitorRuningInfoVo.setPcsPowerList(pcsPowerList);
|
siteMonitorRuningInfoVo.setPcsPowerList(pcsPowerList);
|
||||||
|
int pointCount = pcsPowerList.stream()
|
||||||
|
.filter(item -> item != null && item.getEnergyStoragePowList() != null)
|
||||||
|
.mapToInt(item -> item.getEnergyStoragePowList().size())
|
||||||
|
.sum();
|
||||||
|
log.info("{} storage, siteId={}, startDate={}, endDate={}, deviceCount={}, pointCount={}",
|
||||||
|
RUNNING_GRAPH_DEBUG, siteId, startDate, endDate, pcsPowerList.size(), pointCount);
|
||||||
|
|
||||||
return siteMonitorRuningInfoVo;
|
return siteMonitorRuningInfoVo;
|
||||||
}
|
}
|
||||||
@ -316,49 +360,52 @@ public class SingleSiteServiceImpl implements ISingleSiteService {
|
|||||||
@Override
|
@Override
|
||||||
public SiteMonitorRuningInfoVo getRunningGraphPcsMaxTemp(RunningGraphRequest request) {
|
public SiteMonitorRuningInfoVo getRunningGraphPcsMaxTemp(RunningGraphRequest request) {
|
||||||
SiteMonitorRuningInfoVo siteMonitorRuningInfoVo = new SiteMonitorRuningInfoVo();
|
SiteMonitorRuningInfoVo siteMonitorRuningInfoVo = new SiteMonitorRuningInfoVo();
|
||||||
List<PcsMaxTempList> pcsMaxTempList = new ArrayList<>();
|
if (Objects.isNull(request) || StringUtils.isEmpty(request.getSiteId())) {
|
||||||
// // 时间暂定今日+昨日
|
log.info("{} pcsMaxTemp skip, request invalid, request={}", RUNNING_GRAPH_DEBUG, request);
|
||||||
// Date today = new Date();
|
return siteMonitorRuningInfoVo;
|
||||||
// Date yesterday = DateUtils.addDays(today, -1);
|
}
|
||||||
String siteId = request.getSiteId();
|
String siteId = request.getSiteId();
|
||||||
Date startDate = request.getStartDate();
|
Date[] dateRange = normalizeRunningGraphDateRange(request.getStartDate(), request.getEndDate());
|
||||||
Date endDate = request.getEndDate();
|
Date startDate = dateRange[0];
|
||||||
//PCS最高温度list
|
Date endDate = dateRange[1];
|
||||||
List<PcsMaxTempVo> pcsMaxTempVos = emsPcsDataMapper.getPcsMaxTemp(siteId, startDate, endDate);
|
List<EmsSiteMonitorPointMatch> mappingList = emsSiteMonitorPointMatchMapper.selectBySiteId(siteId);
|
||||||
// if (SiteEnum.FX.getCode().equals(siteId)) {
|
Map<String, EmsSiteMonitorPointMatch> mappingByFieldAndDevice = buildMonitorPointMappingByFieldAndDevice(mappingList);
|
||||||
// pcsMaxTempVos = emsPcsDataMapper.getFXMaxTemp(siteId, startDate, endDate);
|
List<DeviceMeta> pcsDeviceList = getDeviceMetaListByCategory(siteId, DeviceCategory.PCS.getCode());
|
||||||
// } else if (SiteEnum.DDS.getCode().equals(siteId)) {
|
if (CollectionUtils.isEmpty(pcsDeviceList)) {
|
||||||
// pcsMaxTempVos = emsPcsDataMapper.getDDSMaxTemp(siteId, startDate, endDate);
|
pcsDeviceList = Collections.singletonList(new DeviceMeta(RUNNING_GRAPH_DEFAULT_DEVICE, RUNNING_GRAPH_DEFAULT_DEVICE));
|
||||||
// }
|
}
|
||||||
|
|
||||||
// List<PcsMaxTempVo> -> 按pcs的deviceId分组转成List<PcsMaxTempList>
|
List<PcsMaxTempList> pcsMaxTempList = new ArrayList<>();
|
||||||
if (!CollectionUtils.isEmpty(pcsMaxTempVos)) {
|
for (DeviceMeta pcsDevice : pcsDeviceList) {
|
||||||
Map<String, List<PcsMaxTempVo>> dataMap = pcsMaxTempVos.stream()
|
String deviceId = StringUtils.defaultString(pcsDevice.getDeviceId());
|
||||||
.collect(Collectors.groupingBy(
|
String pointId = firstNonBlankPointByDevice(mappingByFieldAndDevice, deviceId, FIELD_CURVE_PCS_MAX_TEMP);
|
||||||
PcsMaxTempVo::getDeviceId,
|
List<InfluxPointDataWriter.PointValue> values = queryInfluxPointValues(siteId, pointId, startDate, endDate);
|
||||||
Collectors.toList()));
|
List<PcsMaxTempVo> pcsMaxTempVos = new ArrayList<>();
|
||||||
|
for (InfluxPointDataWriter.PointValue value : values) {
|
||||||
pcsMaxTempList = dataMap.entrySet().stream()
|
if (value == null || value.getDataTime() == null || value.getPointValue() == null) {
|
||||||
.map(entry -> {
|
continue;
|
||||||
PcsMaxTempList pcdData = new PcsMaxTempList();
|
}
|
||||||
pcdData.setDeviceId(entry.getKey());
|
Date dataTime = value.getDataTime();
|
||||||
pcdData.setMaxTempVoList(entry.getValue());
|
PcsMaxTempVo vo = new PcsMaxTempVo();
|
||||||
return pcdData;
|
vo.setDeviceId(deviceId);
|
||||||
}).collect(Collectors.toList());
|
vo.setDateDay(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, dataTime));
|
||||||
|
vo.setCreateDate(DateUtils.parseDateToStr("HH:mm:00", dataTime));
|
||||||
// // 生成时间列表(每小时一个)
|
vo.setTemp(value.getPointValue());
|
||||||
// List<LocalDateTime> targetHours = new ArrayList<>(60);
|
pcsMaxTempVos.add(vo);
|
||||||
// LocalDateTime startDate = DateUtils.toLocalDateTime(yesterday).truncatedTo(ChronoUnit.DAYS);
|
}
|
||||||
// LocalDateTime endDate = DateUtils.toLocalDateTime(today);
|
PcsMaxTempList pcdData = new PcsMaxTempList();
|
||||||
// while (startDate.isBefore(endDate)) {
|
pcdData.setDeviceId(deviceId);
|
||||||
// targetHours.add(startDate);
|
pcdData.setMaxTempVoList(pcsMaxTempVos);
|
||||||
// startDate = startDate.plusHours(1); // 递增1小时
|
pcsMaxTempList.add(pcdData);
|
||||||
// }
|
|
||||||
// // 根据时间列表填充数据
|
|
||||||
// pcsMaxTempList = fullFillMaxTempData(pcsMaxTempList,targetHours);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
siteMonitorRuningInfoVo.setPcsMaxTempList(pcsMaxTempList);
|
siteMonitorRuningInfoVo.setPcsMaxTempList(pcsMaxTempList);
|
||||||
|
int pointCount = pcsMaxTempList.stream()
|
||||||
|
.filter(item -> item != null && item.getMaxTempVoList() != null)
|
||||||
|
.mapToInt(item -> item.getMaxTempVoList().size())
|
||||||
|
.sum();
|
||||||
|
log.info("{} pcsMaxTemp, siteId={}, startDate={}, endDate={}, deviceCount={}, pointCount={}",
|
||||||
|
RUNNING_GRAPH_DEBUG, siteId, startDate, endDate, pcsMaxTempList.size(), pointCount);
|
||||||
return siteMonitorRuningInfoVo;
|
return siteMonitorRuningInfoVo;
|
||||||
}
|
}
|
||||||
private List<PcsMaxTempList> fullFillMaxTempData(List<PcsMaxTempList> pcsMaxTempList, List<LocalDateTime> targetHours) {
|
private List<PcsMaxTempList> fullFillMaxTempData(List<PcsMaxTempList> pcsMaxTempList, List<LocalDateTime> targetHours) {
|
||||||
@ -415,14 +462,32 @@ public class SingleSiteServiceImpl implements ISingleSiteService {
|
|||||||
@Override
|
@Override
|
||||||
public SiteMonitorRuningInfoVo getRunningGraphBatterySoc(RunningGraphRequest request) {
|
public SiteMonitorRuningInfoVo getRunningGraphBatterySoc(RunningGraphRequest request) {
|
||||||
SiteMonitorRuningInfoVo siteMonitorRuningInfoVo = new SiteMonitorRuningInfoVo();
|
SiteMonitorRuningInfoVo siteMonitorRuningInfoVo = new SiteMonitorRuningInfoVo();
|
||||||
if (!StringUtils.isEmpty(request.getSiteId())) {
|
if (Objects.isNull(request) || StringUtils.isEmpty(request.getSiteId())) {
|
||||||
// // 时间暂定今日+昨日
|
log.info("{} batteryAveSoc skip, request invalid, request={}", RUNNING_GRAPH_DEBUG, request);
|
||||||
// Date today = new Date();
|
return siteMonitorRuningInfoVo;
|
||||||
// Date yesterday = DateUtils.addDays(today, -1);
|
|
||||||
//电池平均soclist
|
|
||||||
List<BatteryAveSOCVo> batteryAveSOCList = emsBatteryStackMapper.getAveSocList(request.getSiteId(), request.getStartDate(), request.getEndDate());
|
|
||||||
siteMonitorRuningInfoVo.setBatteryAveSOCList(batteryAveSOCList);
|
|
||||||
}
|
}
|
||||||
|
Date[] dateRange = normalizeRunningGraphDateRange(request.getStartDate(), request.getEndDate());
|
||||||
|
Date startDate = dateRange[0];
|
||||||
|
Date endDate = dateRange[1];
|
||||||
|
Map<String, EmsSiteMonitorPointMatch> mappingByField = getMonitorPointMappingByField(request.getSiteId());
|
||||||
|
String pointId = firstNonBlankPoint(mappingByField, FIELD_CURVE_BATTERY_AVE_SOC, FIELD_SOC);
|
||||||
|
List<InfluxPointDataWriter.PointValue> values = queryInfluxPointValues(request.getSiteId(), pointId, startDate, endDate);
|
||||||
|
List<BatteryAveSOCVo> batteryAveSOCList = new ArrayList<>();
|
||||||
|
for (InfluxPointDataWriter.PointValue value : values) {
|
||||||
|
if (value == null || value.getDataTime() == null || value.getPointValue() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Date dataTime = value.getDataTime();
|
||||||
|
BatteryAveSOCVo vo = new BatteryAveSOCVo();
|
||||||
|
vo.setDateDay(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, dataTime));
|
||||||
|
vo.setCreateDate(DateUtils.parseDateToStr("HH:mm:00", dataTime));
|
||||||
|
vo.setBatterySOC(value.getPointValue());
|
||||||
|
batteryAveSOCList.add(vo);
|
||||||
|
}
|
||||||
|
siteMonitorRuningInfoVo.setBatteryAveSOCList(batteryAveSOCList);
|
||||||
|
int pointCount = CollectionUtils.isEmpty(batteryAveSOCList) ? 0 : batteryAveSOCList.size();
|
||||||
|
log.info("{} batteryAveSoc, siteId={}, startDate={}, endDate={}, pointId={}, pointCount={}",
|
||||||
|
RUNNING_GRAPH_DEBUG, request.getSiteId(), startDate, endDate, pointId, pointCount);
|
||||||
return siteMonitorRuningInfoVo;
|
return siteMonitorRuningInfoVo;
|
||||||
}
|
}
|
||||||
// 获取单站监控实时运行-电池平均温度
|
// 获取单站监控实时运行-电池平均温度
|
||||||
@ -430,40 +495,178 @@ public class SingleSiteServiceImpl implements ISingleSiteService {
|
|||||||
public SiteMonitorRuningInfoVo getRunningGraphBatteryTemp(RunningGraphRequest request) {
|
public SiteMonitorRuningInfoVo getRunningGraphBatteryTemp(RunningGraphRequest request) {
|
||||||
SiteMonitorRuningInfoVo siteMonitorRuningInfoVo = new SiteMonitorRuningInfoVo();
|
SiteMonitorRuningInfoVo siteMonitorRuningInfoVo = new SiteMonitorRuningInfoVo();
|
||||||
if (Objects.isNull(request) || StringUtils.isEmpty(request.getSiteId())) {
|
if (Objects.isNull(request) || StringUtils.isEmpty(request.getSiteId())) {
|
||||||
|
log.info("{} batteryAveTemp skip, request invalid, request={}", RUNNING_GRAPH_DEBUG, request);
|
||||||
return siteMonitorRuningInfoVo;
|
return siteMonitorRuningInfoVo;
|
||||||
}
|
}
|
||||||
String siteId = request.getSiteId();
|
String siteId = request.getSiteId();
|
||||||
Date startDate = request.getStartDate();
|
Date[] dateRange = normalizeRunningGraphDateRange(request.getStartDate(), request.getEndDate());
|
||||||
Date endDate = request.getEndDate();
|
Date startDate = dateRange[0];
|
||||||
//电池平均温度list,优先从电池堆取,电池堆没有的话再从电池簇取
|
Date endDate = dateRange[1];
|
||||||
|
Map<String, EmsSiteMonitorPointMatch> mappingByField = getMonitorPointMappingByField(siteId);
|
||||||
|
String pointId = firstNonBlankPoint(mappingByField, FIELD_CURVE_BATTERY_AVE_TEMP, FIELD_HOME_AVG_TEMP);
|
||||||
|
List<InfluxPointDataWriter.PointValue> values = queryInfluxPointValues(siteId, pointId, startDate, endDate);
|
||||||
List<BatteryAveTempVo> batteryAveTempList = new ArrayList<>();
|
List<BatteryAveTempVo> batteryAveTempList = new ArrayList<>();
|
||||||
batteryAveTempList = emsBatteryStackMapper.getBatteryAveTempList(siteId, startDate, endDate);
|
for (InfluxPointDataWriter.PointValue value : values) {
|
||||||
// 电池堆暂无数据,从电池簇取
|
if (value == null || value.getDataTime() == null || value.getPointValue() == null) {
|
||||||
if (CollectionUtils.isEmpty(batteryAveTempList)) {
|
continue;
|
||||||
batteryAveTempList = emsBatteryClusterMapper.getBatteryAveTempList(siteId, startDate, endDate);
|
}
|
||||||
|
Date dataTime = value.getDataTime();
|
||||||
|
BatteryAveTempVo vo = new BatteryAveTempVo();
|
||||||
|
vo.setDateDay(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, dataTime));
|
||||||
|
vo.setCreateDate(DateUtils.parseDateToStr("HH:mm:00", dataTime));
|
||||||
|
vo.setBatteryTemp(value.getPointValue());
|
||||||
|
batteryAveTempList.add(vo);
|
||||||
}
|
}
|
||||||
// if (SiteEnum.FX.getCode().equals(siteId)) {
|
|
||||||
// batteryAveTempList = emsBatteryClusterMapper.getBatteryAveTempList(siteId, startDate, endDate);
|
|
||||||
// } else if (SiteEnum.DDS.getCode().equals(siteId)) {
|
|
||||||
// batteryAveTempList = emsBatteryStackMapper.getBatteryAveTempList(siteId, startDate, endDate);
|
|
||||||
// }
|
|
||||||
siteMonitorRuningInfoVo.setBatteryAveTempList(batteryAveTempList);
|
siteMonitorRuningInfoVo.setBatteryAveTempList(batteryAveTempList);
|
||||||
// if (!StringUtils.isEmpty(siteId)) {
|
int pointCount = CollectionUtils.isEmpty(batteryAveTempList) ? 0 : batteryAveTempList.size();
|
||||||
// // 时间暂定今日+昨日
|
log.info("{} batteryAveTemp, siteId={}, startDate={}, endDate={}, pointId={}, pointCount={}",
|
||||||
// Date today = new Date();
|
RUNNING_GRAPH_DEBUG, siteId, startDate, endDate, pointId, pointCount);
|
||||||
// Date yesterday = DateUtils.addDays(today, -1);
|
|
||||||
// //电池平均温度list
|
|
||||||
// List<BatteryAveTempVo> batteryAveTempList = new ArrayList<>();
|
|
||||||
// if (SiteEnum.FX.getCode().equals(siteId)) {
|
|
||||||
// batteryAveTempList = emsBatteryClusterMapper.getBatteryAveTempList(siteId, yesterday, today);
|
|
||||||
// } else if (SiteEnum.DDS.getCode().equals(siteId)) {
|
|
||||||
// batteryAveTempList = emsBatteryStackMapper.getBatteryAveTempList(siteId, yesterday, today);
|
|
||||||
// }
|
|
||||||
// siteMonitorRuningInfoVo.setBatteryAveTempList(batteryAveTempList);
|
|
||||||
// }
|
|
||||||
return siteMonitorRuningInfoVo;
|
return siteMonitorRuningInfoVo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Date[] normalizeRunningGraphDateRange(Date startDate, Date endDate) {
|
||||||
|
Date normalizedStart = startDate;
|
||||||
|
Date normalizedEnd = endDate;
|
||||||
|
if (normalizedStart == null || normalizedEnd == null) {
|
||||||
|
Date today = DateUtils.getNowDate();
|
||||||
|
Date yesterday = DateUtils.addDays(today, -1);
|
||||||
|
normalizedStart = normalizedStart == null ? yesterday : normalizedStart;
|
||||||
|
normalizedEnd = normalizedEnd == null ? today : normalizedEnd;
|
||||||
|
}
|
||||||
|
LocalDate startDay = DateUtils.toLocalDateTime(normalizedStart).toLocalDate();
|
||||||
|
LocalDate endDay = DateUtils.toLocalDateTime(normalizedEnd).toLocalDate();
|
||||||
|
Date dayStart = DateUtils.toDate(startDay.atStartOfDay());
|
||||||
|
Date dayEnd = DateUtils.toDate(endDay.plusDays(1).atStartOfDay().minusNanos(1_000_000));
|
||||||
|
return new Date[]{dayStart, dayEnd};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, EmsSiteMonitorPointMatch> getMonitorPointMappingByField(String siteId) {
|
||||||
|
if (StringUtils.isBlank(siteId)) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
List<EmsSiteMonitorPointMatch> mappingList = emsSiteMonitorPointMatchMapper.selectBySiteId(siteId.trim());
|
||||||
|
if (CollectionUtils.isEmpty(mappingList)) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
return mappingList.stream()
|
||||||
|
.filter(item -> item != null && StringUtils.isNotBlank(item.getFieldCode()))
|
||||||
|
.collect(Collectors.toMap(
|
||||||
|
item -> item.getFieldCode().trim(),
|
||||||
|
item -> item,
|
||||||
|
(a, b) -> b
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, EmsSiteMonitorPointMatch> buildMonitorPointMappingByFieldAndDevice(List<EmsSiteMonitorPointMatch> mappingList) {
|
||||||
|
if (CollectionUtils.isEmpty(mappingList)) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
return mappingList.stream()
|
||||||
|
.filter(item -> item != null && StringUtils.isNotBlank(item.getFieldCode()))
|
||||||
|
.collect(Collectors.toMap(
|
||||||
|
item -> buildFieldDeviceKey(item.getFieldCode(), item.getDeviceId()),
|
||||||
|
item -> item,
|
||||||
|
(a, b) -> b
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildFieldDeviceKey(String fieldCode, String deviceId) {
|
||||||
|
return StringUtils.defaultString(fieldCode).trim() + "|" + StringUtils.defaultString(deviceId).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String firstNonBlankPointByDevice(Map<String, EmsSiteMonitorPointMatch> mappingByFieldAndDevice,
|
||||||
|
String deviceId, String... fieldCodes) {
|
||||||
|
if (mappingByFieldAndDevice == null || fieldCodes == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String normalizedDeviceId = StringUtils.defaultString(deviceId).trim();
|
||||||
|
for (String fieldCode : fieldCodes) {
|
||||||
|
if (StringUtils.isBlank(fieldCode)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
EmsSiteMonitorPointMatch exact = mappingByFieldAndDevice.get(buildFieldDeviceKey(fieldCode, normalizedDeviceId));
|
||||||
|
if (exact != null && StringUtils.isNotBlank(exact.getDataPoint())) {
|
||||||
|
return exact.getDataPoint().trim();
|
||||||
|
}
|
||||||
|
EmsSiteMonitorPointMatch fallback = mappingByFieldAndDevice.get(buildFieldDeviceKey(fieldCode, ""));
|
||||||
|
if (fallback != null && StringUtils.isNotBlank(fallback.getDataPoint())) {
|
||||||
|
return fallback.getDataPoint().trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<DeviceMeta> getDeviceMetaListByCategory(String siteId, String deviceCategory) {
|
||||||
|
if (StringUtils.isBlank(siteId) || StringUtils.isBlank(deviceCategory)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
List<Map<String, Object>> deviceList = emsDevicesSettingMapper.getDeviceInfosBySiteIdAndCategory(siteId, deviceCategory);
|
||||||
|
if (CollectionUtils.isEmpty(deviceList)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
List<DeviceMeta> result = new ArrayList<>();
|
||||||
|
for (Map<String, Object> item : deviceList) {
|
||||||
|
if (item == null || item.get("id") == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String deviceId = String.valueOf(item.get("id")).trim();
|
||||||
|
if (StringUtils.isBlank(deviceId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String deviceName = item.get("deviceName") == null ? deviceId : String.valueOf(item.get("deviceName")).trim();
|
||||||
|
result.add(new DeviceMeta(deviceId, deviceName));
|
||||||
|
}
|
||||||
|
result.sort(Comparator.comparing(DeviceMeta::getDeviceName, Comparator.nullsLast(String::compareTo))
|
||||||
|
.thenComparing(DeviceMeta::getDeviceId, Comparator.nullsLast(String::compareTo)));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class DeviceMeta {
|
||||||
|
private final String deviceId;
|
||||||
|
private final String deviceName;
|
||||||
|
|
||||||
|
private DeviceMeta(String deviceId, String deviceName) {
|
||||||
|
this.deviceId = deviceId;
|
||||||
|
this.deviceName = deviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDeviceId() {
|
||||||
|
return deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDeviceName() {
|
||||||
|
return deviceName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String firstNonBlankPoint(Map<String, EmsSiteMonitorPointMatch> mappingByField, String... fieldCodes) {
|
||||||
|
if (mappingByField == null || fieldCodes == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (String fieldCode : fieldCodes) {
|
||||||
|
if (StringUtils.isBlank(fieldCode)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
EmsSiteMonitorPointMatch match = mappingByField.get(fieldCode);
|
||||||
|
if (match != null && StringUtils.isNotBlank(match.getDataPoint())) {
|
||||||
|
return match.getDataPoint().trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<InfluxPointDataWriter.PointValue> queryInfluxPointValues(String siteId, String pointId, Date startDate, Date endDate) {
|
||||||
|
if (StringUtils.isBlank(siteId) || StringUtils.isBlank(pointId) || startDate == null || endDate == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
List<InfluxPointDataWriter.PointValue> values = influxPointDataWriter.queryCurveDataByPointKey(siteId, pointId, startDate, endDate);
|
||||||
|
if (CollectionUtils.isEmpty(values)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
values.sort(Comparator.comparing(InfluxPointDataWriter.PointValue::getDataTime, Comparator.nullsLast(Date::compareTo)));
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
// 根据site_id获取pcs详细数据+支路数据
|
// 根据site_id获取pcs详细数据+支路数据
|
||||||
@Override
|
@Override
|
||||||
public List<PcsDetailInfoVo> getPcsDetailInfo(String siteId) {
|
public List<PcsDetailInfoVo> getPcsDetailInfo(String siteId) {
|
||||||
@ -472,17 +675,27 @@ public class SingleSiteServiceImpl implements ISingleSiteService {
|
|||||||
if (!StringUtils.isEmpty(siteId)) {
|
if (!StringUtils.isEmpty(siteId)) {
|
||||||
// 获取该设备下所有pcs的id
|
// 获取该设备下所有pcs的id
|
||||||
List<Map<String, Object>> pcsIds = emsDevicesSettingMapper.getDeviceInfosBySiteIdAndCategory(siteId, DeviceCategory.PCS.getCode());
|
List<Map<String, Object>> pcsIds = emsDevicesSettingMapper.getDeviceInfosBySiteIdAndCategory(siteId, DeviceCategory.PCS.getCode());
|
||||||
|
List<EmsSiteMonitorPointMatch> mappingList = emsSiteMonitorPointMatchMapper.selectBySiteId(siteId);
|
||||||
|
Map<String, EmsSiteMonitorPointMatch> mappingByFieldAndDevice = buildMonitorPointMappingByFieldAndDevice(mappingList);
|
||||||
|
Map<String, InfluxPointDataWriter.PointValue> latestPointCache = new HashMap<>();
|
||||||
|
|
||||||
for (Map<String, Object> pcsDevice : pcsIds) {
|
for (Map<String, Object> pcsDevice : pcsIds) {
|
||||||
PcsDetailInfoVo pcsDetailInfoVo = new PcsDetailInfoVo();
|
PcsDetailInfoVo pcsDetailInfoVo = new PcsDetailInfoVo();
|
||||||
pcsDetailInfoVo.setDeviceName(pcsDevice.get("deviceName").toString());
|
String pcsId = String.valueOf(pcsDevice.get(DEVICE_INFO_ID));
|
||||||
pcsDetailInfoVo.setCommunicationStatus(pcsDevice.get("communicationStatus") == null ?
|
pcsDetailInfoVo.setSiteId(siteId);
|
||||||
"" :pcsDevice.get("communicationStatus").toString());
|
pcsDetailInfoVo.setDeviceId(pcsId);
|
||||||
// 从redis取pcs单个详细数据
|
pcsDetailInfoVo.setDeviceName(String.valueOf(pcsDevice.get(DEVICE_INFO_NAME)));
|
||||||
String pcsId = pcsDevice.get("id").toString();
|
|
||||||
EmsPcsData pcsData = redisCache.getCacheObject(RedisKeyConstants.PCS +siteId+"_"+pcsId);
|
fillPcsDetailByLatestPointMapping(siteId, pcsId, pcsDetailInfoVo, mappingByFieldAndDevice, latestPointCache);
|
||||||
if (pcsData != null) {
|
if (StringUtils.isBlank(pcsDetailInfoVo.getCommunicationStatus())) {
|
||||||
BeanUtils.copyProperties(pcsData, pcsDetailInfoVo);
|
pcsDetailInfoVo.setCommunicationStatus(pcsDevice.get(DEVICE_INFO_COMM_STATUS) == null
|
||||||
|
? ""
|
||||||
|
: pcsDevice.get(DEVICE_INFO_COMM_STATUS).toString());
|
||||||
|
}
|
||||||
|
if (StringUtils.isBlank(pcsDetailInfoVo.getDeviceStatus())) {
|
||||||
|
pcsDetailInfoVo.setDeviceStatus(pcsDevice.get(DEVICE_INFO_DEVICE_STATUS) == null
|
||||||
|
? ""
|
||||||
|
: pcsDevice.get(DEVICE_INFO_DEVICE_STATUS).toString());
|
||||||
}
|
}
|
||||||
// 支路信息数据
|
// 支路信息数据
|
||||||
List<PcsBranchInfo> pcsBranchInfoList = new ArrayList<>();
|
List<PcsBranchInfo> pcsBranchInfoList = new ArrayList<>();
|
||||||
@ -493,7 +706,6 @@ public class SingleSiteServiceImpl implements ISingleSiteService {
|
|||||||
// // 告警设备点位个数
|
// // 告警设备点位个数
|
||||||
// int alarmNum = emsPointMatchMapper.getDevicePointAlarmNum(siteId, pcsId, DeviceCategory.PCS.getCode());
|
// int alarmNum = emsPointMatchMapper.getDevicePointAlarmNum(siteId, pcsId, DeviceCategory.PCS.getCode());
|
||||||
pcsDetailInfoVo.setAlarmNum(alarmNum);
|
pcsDetailInfoVo.setAlarmNum(alarmNum);
|
||||||
pcsDetailInfoVo.setDeviceStatus(pcsDevice.get("deviceStatus") == null ? "" : pcsDevice.get("deviceStatus").toString());
|
|
||||||
|
|
||||||
// 处理枚举匹配字段
|
// 处理枚举匹配字段
|
||||||
devicePointMatchDataProcessor.convertFieldValueToEnumMatch(siteId, DeviceCategory.PCS.getCode(), pcsDetailInfoVo);
|
devicePointMatchDataProcessor.convertFieldValueToEnumMatch(siteId, DeviceCategory.PCS.getCode(), pcsDetailInfoVo);
|
||||||
@ -504,6 +716,114 @@ public class SingleSiteServiceImpl implements ISingleSiteService {
|
|||||||
return pcsDetailInfoVoList;
|
return pcsDetailInfoVoList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void fillPcsDetailByLatestPointMapping(String siteId,
|
||||||
|
String deviceId,
|
||||||
|
PcsDetailInfoVo target,
|
||||||
|
Map<String, EmsSiteMonitorPointMatch> mappingByFieldAndDevice,
|
||||||
|
Map<String, InfluxPointDataWriter.PointValue> latestPointCache) {
|
||||||
|
if (StringUtils.isAnyBlank(siteId, deviceId) || target == null || mappingByFieldAndDevice == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BeanWrapper beanWrapper = new BeanWrapperImpl(target);
|
||||||
|
Date latestDataTime = null;
|
||||||
|
for (PropertyDescriptor pd : beanWrapper.getPropertyDescriptors()) {
|
||||||
|
if (pd == null || StringUtils.isBlank(pd.getName()) || pd.getWriteMethod() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String fieldCode = pd.getName();
|
||||||
|
if (PCS_DETAIL_META_FIELDS.contains(fieldCode)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
EmsSiteMonitorPointMatch pointMatch = resolvePointMatchByFieldAndDevice(mappingByFieldAndDevice, fieldCode, deviceId);
|
||||||
|
if (pointMatch == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Object fieldValue;
|
||||||
|
Date valueTime = null;
|
||||||
|
if (USE_FIXED_DISPLAY_YES == (pointMatch.getUseFixedDisplay() == null ? 0 : pointMatch.getUseFixedDisplay())
|
||||||
|
&& StringUtils.isNotBlank(pointMatch.getFixedDataPoint())) {
|
||||||
|
fieldValue = pointMatch.getFixedDataPoint().trim();
|
||||||
|
} else {
|
||||||
|
InfluxPointDataWriter.PointValue latestValue = getLatestPointValueByPointId(siteId, pointMatch.getDataPoint(), latestPointCache);
|
||||||
|
if (latestValue == null || latestValue.getPointValue() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fieldValue = latestValue.getPointValue();
|
||||||
|
valueTime = latestValue.getDataTime();
|
||||||
|
}
|
||||||
|
Object convertedValue = convertFieldValueByType(fieldValue, pd.getPropertyType());
|
||||||
|
if (convertedValue == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
beanWrapper.setPropertyValue(fieldCode, convertedValue);
|
||||||
|
if (valueTime != null && (latestDataTime == null || valueTime.after(latestDataTime))) {
|
||||||
|
latestDataTime = valueTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (latestDataTime != null) {
|
||||||
|
target.setDataUpdateTime(latestDataTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private EmsSiteMonitorPointMatch resolvePointMatchByFieldAndDevice(Map<String, EmsSiteMonitorPointMatch> mappingByFieldAndDevice,
|
||||||
|
String fieldCode,
|
||||||
|
String deviceId) {
|
||||||
|
if (mappingByFieldAndDevice == null || StringUtils.isBlank(fieldCode)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String normalizedField = fieldCode.trim();
|
||||||
|
String normalizedDeviceId = StringUtils.defaultString(deviceId).trim();
|
||||||
|
EmsSiteMonitorPointMatch exact = mappingByFieldAndDevice.get(buildFieldDeviceKey(normalizedField, normalizedDeviceId));
|
||||||
|
if (exact != null) {
|
||||||
|
return exact;
|
||||||
|
}
|
||||||
|
return mappingByFieldAndDevice.get(buildFieldDeviceKey(normalizedField, ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
private InfluxPointDataWriter.PointValue getLatestPointValueByPointId(String siteId,
|
||||||
|
String pointId,
|
||||||
|
Map<String, InfluxPointDataWriter.PointValue> latestPointCache) {
|
||||||
|
if (StringUtils.isAnyBlank(siteId, pointId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String pointCacheKey = pointId.trim().toUpperCase();
|
||||||
|
if (latestPointCache.containsKey(pointCacheKey)) {
|
||||||
|
return latestPointCache.get(pointCacheKey);
|
||||||
|
}
|
||||||
|
Date endTime = DateUtils.getNowDate();
|
||||||
|
Date fastStartTime = DateUtils.addDays(endTime, -1);
|
||||||
|
InfluxPointDataWriter.PointValue latest = influxPointDataWriter.queryLatestPointValueByPointKey(
|
||||||
|
siteId.trim(), pointId.trim(), fastStartTime, endTime
|
||||||
|
);
|
||||||
|
if (latest == null) {
|
||||||
|
Date fallbackStartTime = DateUtils.addDays(endTime, -7);
|
||||||
|
latest = influxPointDataWriter.queryLatestPointValueByPointKey(
|
||||||
|
siteId.trim(), pointId.trim(), fallbackStartTime, endTime
|
||||||
|
);
|
||||||
|
}
|
||||||
|
latestPointCache.put(pointCacheKey, latest);
|
||||||
|
return latest;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object convertFieldValueByType(Object rawValue, Class<?> targetType) {
|
||||||
|
if (rawValue == null || targetType == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (BigDecimal.class.equals(targetType)) {
|
||||||
|
return StringUtils.getBigDecimal(rawValue);
|
||||||
|
}
|
||||||
|
if (String.class.equals(targetType)) {
|
||||||
|
if (rawValue instanceof BigDecimal) {
|
||||||
|
return ((BigDecimal) rawValue).stripTrailingZeros().toPlainString();
|
||||||
|
}
|
||||||
|
return String.valueOf(rawValue);
|
||||||
|
}
|
||||||
|
if (Date.class.equals(targetType) && rawValue instanceof Date) {
|
||||||
|
return rawValue;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private void processBranchDataInfo(String siteId, String pcsId, List<PcsBranchInfo> pcsBranchInfoList) {
|
private void processBranchDataInfo(String siteId, String pcsId, List<PcsBranchInfo> pcsBranchInfoList) {
|
||||||
if (!StringUtils.isEmpty(pcsId)) {
|
if (!StringUtils.isEmpty(pcsId)) {
|
||||||
List<EmsPcsBranchData> pcsBranchData = redisCache.getCacheObject(RedisKeyConstants.BRANCH +siteId+"_"+pcsId);
|
List<EmsPcsBranchData> pcsBranchData = redisCache.getCacheObject(RedisKeyConstants.BRANCH +siteId+"_"+pcsId);
|
||||||
|
|||||||
@ -213,7 +213,8 @@
|
|||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="getPointNameList" resultType="com.xzzn.ems.domain.vo.GeneralQueryPointOptionVo">
|
<select id="getPointNameList" resultType="com.xzzn.ems.domain.vo.GeneralQueryPointOptionVo">
|
||||||
select point_name as pointName,
|
select point_id as pointId,
|
||||||
|
point_name as pointName,
|
||||||
data_key as dataKey,
|
data_key as dataKey,
|
||||||
point_desc as pointDesc
|
point_desc as pointDesc
|
||||||
from ems_point_config
|
from ems_point_config
|
||||||
@ -237,8 +238,8 @@
|
|||||||
or point_desc like concat('%', #{pointName}, '%')
|
or point_desc like concat('%', #{pointName}, '%')
|
||||||
)
|
)
|
||||||
</if>
|
</if>
|
||||||
group by point_name, data_key, point_desc
|
group by point_id, point_name, data_key, point_desc
|
||||||
order by point_name asc, data_key asc
|
order by point_id asc, point_name asc, data_key asc
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="getConfigListForGeneralQuery" resultMap="EmsPointConfigResult">
|
<select id="getConfigListForGeneralQuery" resultMap="EmsPointConfigResult">
|
||||||
@ -250,21 +251,31 @@
|
|||||||
#{siteId}
|
#{siteId}
|
||||||
</foreach>
|
</foreach>
|
||||||
</if>
|
</if>
|
||||||
<if test="deviceCategory != null and deviceCategory != ''">
|
<choose>
|
||||||
and device_category = #{deviceCategory}
|
<when test="pointIds != null and pointIds.size() > 0">
|
||||||
</if>
|
and point_id in
|
||||||
<if test="pointNames != null and pointNames.size() > 0">
|
<foreach collection="pointIds" item="pointId" open="(" separator="," close=")">
|
||||||
and point_name in
|
#{pointId}
|
||||||
<foreach collection="pointNames" item="pointName" open="(" separator="," close=")">
|
</foreach>
|
||||||
#{pointName}
|
</when>
|
||||||
</foreach>
|
<otherwise>
|
||||||
</if>
|
<if test="deviceCategory != null and deviceCategory != ''">
|
||||||
<if test="deviceIds != null and deviceIds.size() > 0">
|
and device_category = #{deviceCategory}
|
||||||
and device_id in
|
</if>
|
||||||
<foreach collection="deviceIds" item="deviceId" open="(" separator="," close=")">
|
<if test="pointNames != null and pointNames.size() > 0">
|
||||||
#{deviceId}
|
and point_name in
|
||||||
</foreach>
|
<foreach collection="pointNames" item="pointName" open="(" separator="," close=")">
|
||||||
</if>
|
#{pointName}
|
||||||
|
</foreach>
|
||||||
|
</if>
|
||||||
|
<if test="deviceIds != null and deviceIds.size() > 0">
|
||||||
|
and device_id in
|
||||||
|
<foreach collection="deviceIds" item="deviceId" open="(" separator="," close=")">
|
||||||
|
#{deviceId}
|
||||||
|
</foreach>
|
||||||
|
</if>
|
||||||
|
</otherwise>
|
||||||
|
</choose>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectBySiteIdAndPointIds" resultMap="EmsPointConfigResult">
|
<select id="selectBySiteIdAndPointIds" resultMap="EmsPointConfigResult">
|
||||||
|
|||||||
@ -162,4 +162,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
where site_id = #{templateSiteId}
|
where site_id = #{templateSiteId}
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
|
<delete id="deleteByScope">
|
||||||
|
delete from ems_point_enum_match
|
||||||
|
where site_id = #{siteId}
|
||||||
|
and device_category = #{deviceCategory}
|
||||||
|
and match_field = #{matchField}
|
||||||
|
</delete>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@ -14,27 +14,23 @@
|
|||||||
|
|
||||||
<insert id="upsertHistoryJsonByMinute">
|
<insert id="upsertHistoryJsonByMinute">
|
||||||
insert into ${tableName} (
|
insert into ${tableName} (
|
||||||
site_id, statis_minute, data_json, create_by, create_time, update_by, update_time
|
site_id, statis_minute, data_json,
|
||||||
|
hot_soc, hot_total_active_power, hot_total_reactive_power, hot_day_charged_cap, hot_day_dis_charged_cap,
|
||||||
|
create_by, create_time, update_by, update_time
|
||||||
) values (
|
) values (
|
||||||
#{siteId}, #{statisMinute}, #{dataJson}, #{operName}, now(), #{operName}, now()
|
#{siteId}, #{statisMinute}, #{dataJson},
|
||||||
|
#{hotSoc}, #{hotTotalActivePower}, #{hotTotalReactivePower}, #{hotDayChargedCap}, #{hotDayDisChargedCap},
|
||||||
|
#{operName}, now(), #{operName}, now()
|
||||||
)
|
)
|
||||||
on duplicate key update
|
on duplicate key update
|
||||||
data_json = values(data_json),
|
data_json = values(data_json),
|
||||||
|
hot_soc = values(hot_soc),
|
||||||
|
hot_total_active_power = values(hot_total_active_power),
|
||||||
|
hot_total_reactive_power = values(hot_total_reactive_power),
|
||||||
|
hot_day_charged_cap = values(hot_day_charged_cap),
|
||||||
|
hot_day_dis_charged_cap = values(hot_day_dis_charged_cap),
|
||||||
update_by = values(update_by),
|
update_by = values(update_by),
|
||||||
update_time = now()
|
update_time = now()
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
<update id="updateHistoryHotColumns">
|
|
||||||
update ${tableName}
|
|
||||||
set hot_soc = #{hotSoc},
|
|
||||||
hot_total_active_power = #{hotTotalActivePower},
|
|
||||||
hot_total_reactive_power = #{hotTotalReactivePower},
|
|
||||||
hot_day_charged_cap = #{hotDayChargedCap},
|
|
||||||
hot_day_dis_charged_cap = #{hotDayDisChargedCap},
|
|
||||||
update_by = #{operName},
|
|
||||||
update_time = now()
|
|
||||||
where site_id = #{siteId}
|
|
||||||
and statis_minute = #{statisMinute}
|
|
||||||
</update>
|
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
<result property="id" column="id"/>
|
<result property="id" column="id"/>
|
||||||
<result property="siteId" column="site_id"/>
|
<result property="siteId" column="site_id"/>
|
||||||
<result property="fieldCode" column="field_code"/>
|
<result property="fieldCode" column="field_code"/>
|
||||||
|
<result property="deviceId" column="device_id"/>
|
||||||
<result property="dataPoint" column="data_point"/>
|
<result property="dataPoint" column="data_point"/>
|
||||||
<result property="fixedDataPoint" column="fixed_data_point"/>
|
<result property="fixedDataPoint" column="fixed_data_point"/>
|
||||||
<result property="useFixedDisplay" column="use_fixed_display"/>
|
<result property="useFixedDisplay" column="use_fixed_display"/>
|
||||||
@ -19,7 +20,7 @@
|
|||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<select id="selectBySiteId" resultMap="EmsSiteMonitorPointMatchResult">
|
<select id="selectBySiteId" resultMap="EmsSiteMonitorPointMatchResult">
|
||||||
select id, site_id, field_code, data_point, fixed_data_point, use_fixed_display, create_by, create_time, update_by, update_time, remark
|
select id, site_id, field_code, device_id, data_point, fixed_data_point, use_fixed_display, create_by, create_time, update_by, update_time, remark
|
||||||
from ems_site_monitor_point_match
|
from ems_site_monitor_point_match
|
||||||
where site_id = #{siteId}
|
where site_id = #{siteId}
|
||||||
order by id asc
|
order by id asc
|
||||||
@ -32,10 +33,10 @@
|
|||||||
|
|
||||||
<insert id="insertBatch">
|
<insert id="insertBatch">
|
||||||
insert into ems_site_monitor_point_match
|
insert into ems_site_monitor_point_match
|
||||||
(site_id, field_code, data_point, fixed_data_point, use_fixed_display, create_by, create_time, update_by, update_time)
|
(site_id, field_code, device_id, data_point, fixed_data_point, use_fixed_display, create_by, create_time, update_by, update_time)
|
||||||
values
|
values
|
||||||
<foreach collection="list" item="item" separator=",">
|
<foreach collection="list" item="item" separator=",">
|
||||||
(#{item.siteId}, #{item.fieldCode}, #{item.dataPoint}, #{item.fixedDataPoint}, #{item.useFixedDisplay}, #{item.createBy}, now(), #{item.updateBy}, now())
|
(#{item.siteId}, #{item.fieldCode}, #{item.deviceId}, #{item.dataPoint}, #{item.fixedDataPoint}, #{item.useFixedDisplay}, #{item.createBy}, now(), #{item.updateBy}, now())
|
||||||
</foreach>
|
</foreach>
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
|
|||||||
@ -134,17 +134,17 @@
|
|||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="getAllSiteDeviceList" parameterType="String" resultType="com.xzzn.ems.domain.vo.SiteDeviceListVo">
|
<select id="getAllSiteDeviceList" parameterType="String" resultType="com.xzzn.ems.domain.vo.SiteDeviceListVo">
|
||||||
select es.site_id as siteId,es.site_name as siteName,
|
select ed.site_id as siteId,
|
||||||
ed.device_id as deviceId,ed.device_name as deviceName,
|
ed.device_id as deviceId,ed.device_name as deviceName,
|
||||||
ed.device_type as deviceType,ed.device_status as deviceStatus,ed.work_status as workStatus,
|
ed.device_type as deviceType,ed.device_status as deviceStatus,ed.work_status as workStatus,
|
||||||
ed.device_category as deviceCategory,
|
ed.device_category as deviceCategory,
|
||||||
ed.picture_url as pictureUrl,
|
ed.picture_url as pictureUrl,
|
||||||
ed.id,
|
ed.id,
|
||||||
ed.parent_id as parentId
|
ed.parent_id as parentId
|
||||||
from ems_site_setting es INNER JOIN ems_devices_setting ed on es.site_id = ed.site_id
|
from ems_devices_setting ed
|
||||||
where 1=1
|
where 1=1
|
||||||
<if test="siteId != null and siteId != ''">
|
<if test="siteId != null and siteId != ''">
|
||||||
and es.site_id = #{siteId}
|
and ed.site_id = #{siteId}
|
||||||
</if>
|
</if>
|
||||||
<if test="deviceCategory != null and deviceCategory != ''">
|
<if test="deviceCategory != null and deviceCategory != ''">
|
||||||
and ed.device_category = #{deviceCategory}
|
and ed.device_category = #{deviceCategory}
|
||||||
@ -153,18 +153,19 @@
|
|||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="getAllSiteDeviceListNoDisp" parameterType="String" resultType="com.xzzn.ems.domain.vo.SiteDeviceListVo">
|
<select id="getAllSiteDeviceListNoDisp" parameterType="String" resultType="com.xzzn.ems.domain.vo.SiteDeviceListVo">
|
||||||
select es.site_id as siteId,es.site_name as siteName,
|
select ed.site_id as siteId,
|
||||||
ed.device_id as deviceId,ed.device_name as deviceName,
|
ed.device_id as deviceId,ed.device_name as deviceName,
|
||||||
ed.device_type as deviceType,ed.device_status as deviceStatus,ed.work_status as workStatus,
|
ed.device_type as deviceType,ed.device_status as deviceStatus,ed.work_status as workStatus,
|
||||||
ed.device_category as deviceCategory,
|
ed.device_category as deviceCategory,
|
||||||
ed.picture_url as pictureUrl,
|
ed.picture_url as pictureUrl,
|
||||||
ed.id,
|
ed.id,
|
||||||
ed.parent_id as parentId
|
ed.parent_id as parentId
|
||||||
from ems_site_setting es INNER JOIN ems_devices_setting ed on es.site_id = ed.site_id and (ed.display_flg is null or ed.display_flg != '1')
|
from ems_devices_setting ed
|
||||||
where 1 = 1
|
where 1 = 1
|
||||||
<if test="siteId != null and siteId != ''">
|
<if test="siteId != null and siteId != ''">
|
||||||
and es.site_id = #{siteId}
|
and ed.site_id = #{siteId}
|
||||||
</if>
|
</if>
|
||||||
|
and (ed.display_flg is null or ed.display_flg != '1')
|
||||||
<if test="deviceCategory != null and deviceCategory != ''">
|
<if test="deviceCategory != null and deviceCategory != ''">
|
||||||
and ed.device_category = #{deviceCategory}
|
and ed.device_category = #{deviceCategory}
|
||||||
</if>
|
</if>
|
||||||
@ -173,4 +174,4 @@
|
|||||||
<select id="getAllSiteId" resultType="String">
|
<select id="getAllSiteId" resultType="String">
|
||||||
select distinct site_id from ems_site_setting
|
select distinct site_id from ems_site_setting
|
||||||
</select>
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@ -14,6 +14,12 @@
|
|||||||
<result property="antiReverseUp" column="anti_reverse_up"/>
|
<result property="antiReverseUp" column="anti_reverse_up"/>
|
||||||
<result property="antiReversePowerDownPercent" column="anti_reverse_power_down_percent"/>
|
<result property="antiReversePowerDownPercent" column="anti_reverse_power_down_percent"/>
|
||||||
<result property="antiReverseHardStopThreshold" column="anti_reverse_hard_stop_threshold"/>
|
<result property="antiReverseHardStopThreshold" column="anti_reverse_hard_stop_threshold"/>
|
||||||
|
<result property="powerSetMultiplier" column="power_set_multiplier"/>
|
||||||
|
<result property="protectInterveneEnable" column="protect_intervene_enable"/>
|
||||||
|
<result property="protectL1DeratePercent" column="protect_l1_derate_percent"/>
|
||||||
|
<result property="protectRecoveryStableSeconds" column="protect_recovery_stable_seconds"/>
|
||||||
|
<result property="protectL3LatchEnable" column="protect_l3_latch_enable"/>
|
||||||
|
<result property="protectConflictPolicy" column="protect_conflict_policy"/>
|
||||||
<result property="createBy" column="create_by"/>
|
<result property="createBy" column="create_by"/>
|
||||||
<result property="createTime" column="create_time"/>
|
<result property="createTime" column="create_time"/>
|
||||||
<result property="updateBy" column="update_by"/>
|
<result property="updateBy" column="update_by"/>
|
||||||
@ -31,6 +37,12 @@
|
|||||||
anti_reverse_up,
|
anti_reverse_up,
|
||||||
anti_reverse_power_down_percent,
|
anti_reverse_power_down_percent,
|
||||||
anti_reverse_hard_stop_threshold,
|
anti_reverse_hard_stop_threshold,
|
||||||
|
power_set_multiplier,
|
||||||
|
protect_intervene_enable,
|
||||||
|
protect_l1_derate_percent,
|
||||||
|
protect_recovery_stable_seconds,
|
||||||
|
protect_l3_latch_enable,
|
||||||
|
protect_conflict_policy,
|
||||||
create_by,
|
create_by,
|
||||||
create_time,
|
create_time,
|
||||||
update_by,
|
update_by,
|
||||||
@ -56,6 +68,12 @@
|
|||||||
<if test="antiReverseUp != null">anti_reverse_up,</if>
|
<if test="antiReverseUp != null">anti_reverse_up,</if>
|
||||||
<if test="antiReversePowerDownPercent != null">anti_reverse_power_down_percent,</if>
|
<if test="antiReversePowerDownPercent != null">anti_reverse_power_down_percent,</if>
|
||||||
<if test="antiReverseHardStopThreshold != null">anti_reverse_hard_stop_threshold,</if>
|
<if test="antiReverseHardStopThreshold != null">anti_reverse_hard_stop_threshold,</if>
|
||||||
|
<if test="powerSetMultiplier != null">power_set_multiplier,</if>
|
||||||
|
<if test="protectInterveneEnable != null">protect_intervene_enable,</if>
|
||||||
|
<if test="protectL1DeratePercent != null">protect_l1_derate_percent,</if>
|
||||||
|
<if test="protectRecoveryStableSeconds != null">protect_recovery_stable_seconds,</if>
|
||||||
|
<if test="protectL3LatchEnable != null">protect_l3_latch_enable,</if>
|
||||||
|
<if test="protectConflictPolicy != null and protectConflictPolicy != ''">protect_conflict_policy,</if>
|
||||||
<if test="createBy != null">create_by,</if>
|
<if test="createBy != null">create_by,</if>
|
||||||
<if test="createTime != null">create_time,</if>
|
<if test="createTime != null">create_time,</if>
|
||||||
<if test="updateBy != null">update_by,</if>
|
<if test="updateBy != null">update_by,</if>
|
||||||
@ -71,6 +89,12 @@
|
|||||||
<if test="antiReverseUp != null">#{antiReverseUp},</if>
|
<if test="antiReverseUp != null">#{antiReverseUp},</if>
|
||||||
<if test="antiReversePowerDownPercent != null">#{antiReversePowerDownPercent},</if>
|
<if test="antiReversePowerDownPercent != null">#{antiReversePowerDownPercent},</if>
|
||||||
<if test="antiReverseHardStopThreshold != null">#{antiReverseHardStopThreshold},</if>
|
<if test="antiReverseHardStopThreshold != null">#{antiReverseHardStopThreshold},</if>
|
||||||
|
<if test="powerSetMultiplier != null">#{powerSetMultiplier},</if>
|
||||||
|
<if test="protectInterveneEnable != null">#{protectInterveneEnable},</if>
|
||||||
|
<if test="protectL1DeratePercent != null">#{protectL1DeratePercent},</if>
|
||||||
|
<if test="protectRecoveryStableSeconds != null">#{protectRecoveryStableSeconds},</if>
|
||||||
|
<if test="protectL3LatchEnable != null">#{protectL3LatchEnable},</if>
|
||||||
|
<if test="protectConflictPolicy != null and protectConflictPolicy != ''">#{protectConflictPolicy},</if>
|
||||||
<if test="createBy != null">#{createBy},</if>
|
<if test="createBy != null">#{createBy},</if>
|
||||||
<if test="createTime != null">#{createTime},</if>
|
<if test="createTime != null">#{createTime},</if>
|
||||||
<if test="updateBy != null">#{updateBy},</if>
|
<if test="updateBy != null">#{updateBy},</if>
|
||||||
@ -89,6 +113,12 @@
|
|||||||
<if test="antiReverseUp != null">anti_reverse_up = #{antiReverseUp},</if>
|
<if test="antiReverseUp != null">anti_reverse_up = #{antiReverseUp},</if>
|
||||||
<if test="antiReversePowerDownPercent != null">anti_reverse_power_down_percent = #{antiReversePowerDownPercent},</if>
|
<if test="antiReversePowerDownPercent != null">anti_reverse_power_down_percent = #{antiReversePowerDownPercent},</if>
|
||||||
<if test="antiReverseHardStopThreshold != null">anti_reverse_hard_stop_threshold = #{antiReverseHardStopThreshold},</if>
|
<if test="antiReverseHardStopThreshold != null">anti_reverse_hard_stop_threshold = #{antiReverseHardStopThreshold},</if>
|
||||||
|
<if test="powerSetMultiplier != null">power_set_multiplier = #{powerSetMultiplier},</if>
|
||||||
|
<if test="protectInterveneEnable != null">protect_intervene_enable = #{protectInterveneEnable},</if>
|
||||||
|
<if test="protectL1DeratePercent != null">protect_l1_derate_percent = #{protectL1DeratePercent},</if>
|
||||||
|
<if test="protectRecoveryStableSeconds != null">protect_recovery_stable_seconds = #{protectRecoveryStableSeconds},</if>
|
||||||
|
<if test="protectL3LatchEnable != null">protect_l3_latch_enable = #{protectL3LatchEnable},</if>
|
||||||
|
<if test="protectConflictPolicy != null and protectConflictPolicy != ''">protect_conflict_policy = #{protectConflictPolicy},</if>
|
||||||
<if test="updateBy != null">update_by = #{updateBy},</if>
|
<if test="updateBy != null">update_by = #{updateBy},</if>
|
||||||
<if test="updateTime != null">update_time = #{updateTime},</if>
|
<if test="updateTime != null">update_time = #{updateTime},</if>
|
||||||
<if test="remark != null">remark = #{remark},</if>
|
<if test="remark != null">remark = #{remark},</if>
|
||||||
|
|||||||
Reference in New Issue
Block a user