refactor: 重构设备参数查询逻辑使用Facade层

master
zangch@mesnac.com 3 weeks ago
parent 6ada262bf2
commit cbbe49407d

@ -0,0 +1,27 @@
package com.aucma.base.mapper;
import com.aucma.base.domain.BaseDeviceParamVal;
import java.util.List;
import java.util.Map;
/**
* Mapper
*/
public interface DeviceParamReadFacadeMapper {
/**
*
*/
BaseDeviceParamVal selectLatestParamValue(Map<String, Object> params);
/**
*
*/
List<BaseDeviceParamVal> selectParamsByTimePoint(Map<String, Object> params);
/**
*
*/
List<BaseDeviceParamVal> selectHistoryParamValues(Map<String, Object> params);
}

@ -1,11 +1,7 @@
package com.aucma.base.mapper;
import java.util.Date;
import java.util.List;
import java.util.Map;
import com.aucma.base.domain.ProcessSnapshot;
import com.aucma.base.domain.BaseDeviceParamVal;
import org.apache.ibatis.annotations.Param;
/**
* Mapper
@ -61,26 +57,4 @@ public interface ProcessSnapshotMapper {
* @return
*/
public int deleteProcessSnapshotBySnapshotIds(Long[] snapshotIds);
/**
*
*
* @param deviceCode
* @param snapshotTime
* @return
*/
public List<BaseDeviceParamVal> selectParamsBySnapshotTime(@Param("deviceCode") String deviceCode,
@Param("snapshotTime") Date snapshotTime);
/**
*
*
* @param deviceCode
* @param snapshotTime1 1
* @param snapshotTime2 2
* @return
*/
public List<Map<String, Object>> compareSnapshots(@Param("deviceCode") String deviceCode,
@Param("snapshotTime1") Date snapshotTime1,
@Param("snapshotTime2") Date snapshotTime2);
}

@ -0,0 +1,45 @@
package com.aucma.base.service;
import com.aucma.base.domain.BaseDeviceParamVal;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
*
*/
public interface IDeviceParamReadFacade {
/**
*
*/
BaseDeviceParamVal getLatestParamValue(String deviceCode, String paramCode, String paramName);
/**
*
*/
List<BaseDeviceParamVal> listParamsByTimePoint(String deviceCode, Date snapshotTime, int windowSeconds);
/**
*
*/
List<Map<String, Object>> compareParamsByTimePoint(String deviceCode, Date timePoint1, Date timePoint2, int windowSeconds);
/**
*
*/
List<BaseDeviceParamVal> listHistoryParamValues(String deviceCode, String paramCode, String paramName,
Date beginTime, Date endTime, Integer limit);
/**
*
*/
List<BaseDeviceParamVal> listHistoryParamValuesByNames(String deviceCode, List<String> paramNames,
Date beginTime, Date endTime, Integer limit);
/**
*
*/
List<Double> listNumericHistoryValues(String deviceCode, String paramCode, Date beginTime, Date endTime);
}

@ -0,0 +1,209 @@
package com.aucma.base.service.impl;
import com.aucma.base.domain.BaseDeviceParamVal;
import com.aucma.base.mapper.DeviceParamReadFacadeMapper;
import com.aucma.base.service.IDeviceParamReadFacade;
import com.aucma.base.support.DeviceParamTableRouter;
import com.aucma.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
* Service
*/
@Service
public class DeviceParamReadFacadeImpl implements IDeviceParamReadFacade {
private static final int DEFAULT_TIME_POINT_WINDOW_SECONDS = 60;
private static final int LATEST_LOOKBACK_MONTHS = 12;
@Autowired
private DeviceParamReadFacadeMapper deviceParamReadFacadeMapper;
@Autowired
private DeviceParamTableRouter deviceParamTableRouter;
@Override
public BaseDeviceParamVal getLatestParamValue(String deviceCode, String paramCode, String paramName) {
Map<String, Object> params = buildQueryParams(deviceCode, paramCode, paramName, buildLatestBeginTime(), new Date());
// 为什么这样做:旁路消费方不应该继续感知 OLD 单表、NEW 月表和跨月范围,
// latest 口径在这里统一收口后,预警/安灯/报表才能保持一致。
return deviceParamReadFacadeMapper.selectLatestParamValue(params);
}
@Override
public List<BaseDeviceParamVal> listParamsByTimePoint(String deviceCode, Date snapshotTime, int windowSeconds) {
if (StringUtils.isEmpty(deviceCode) || snapshotTime == null) {
return Collections.emptyList();
}
int effectiveWindowSeconds = windowSeconds > 0 ? windowSeconds : DEFAULT_TIME_POINT_WINDOW_SECONDS;
Date beginTime = addSeconds(snapshotTime, -effectiveWindowSeconds);
Date endTime = addSeconds(snapshotTime, effectiveWindowSeconds);
Map<String, Object> params = buildQueryParams(deviceCode, null, null, beginTime, endTime);
params.put("snapshotTime", snapshotTime);
// 为什么这样做:工艺快照的本质是“某时间点附近最近一次参数状态”,
// 所以这里统一用时间窗口 + 双源合并,而不是让快照模块直接写死旧表。
List<BaseDeviceParamVal> paramList = deviceParamReadFacadeMapper.selectParamsByTimePoint(params);
return paramList != null ? paramList : Collections.<BaseDeviceParamVal>emptyList();
}
@Override
public List<Map<String, Object>> compareParamsByTimePoint(String deviceCode, Date timePoint1, Date timePoint2, int windowSeconds) {
List<BaseDeviceParamVal> beforeList = listParamsByTimePoint(deviceCode, timePoint1, windowSeconds);
List<BaseDeviceParamVal> afterList = listParamsByTimePoint(deviceCode, timePoint2, windowSeconds);
if (beforeList.isEmpty() && afterList.isEmpty()) {
return Collections.emptyList();
}
Map<String, BaseDeviceParamVal> beforeMap = toParamIndex(beforeList);
Map<String, BaseDeviceParamVal> afterMap = toParamIndex(afterList);
TreeMap<String, Map<String, Object>> compareMap = new TreeMap<String, Map<String, Object>>();
fillCompareMap(compareMap, beforeMap, true);
fillCompareMap(compareMap, afterMap, false);
return new ArrayList<Map<String, Object>>(compareMap.values());
}
@Override
public List<BaseDeviceParamVal> listHistoryParamValues(String deviceCode, String paramCode, String paramName,
Date beginTime, Date endTime, Integer limit) {
Map<String, Object> params = buildQueryParams(deviceCode, paramCode, paramName, beginTime, endTime);
params.put("limit", limit);
List<BaseDeviceParamVal> paramList = deviceParamReadFacadeMapper.selectHistoryParamValues(params);
return paramList != null ? paramList : Collections.<BaseDeviceParamVal>emptyList();
}
@Override
public List<BaseDeviceParamVal> listHistoryParamValuesByNames(String deviceCode, List<String> paramNames,
Date beginTime, Date endTime, Integer limit) {
if (paramNames == null || paramNames.isEmpty()) {
return Collections.emptyList();
}
Map<String, Object> params = buildQueryParams(deviceCode, null, null, beginTime, endTime);
params.put("paramNames", paramNames);
params.put("limit", limit);
List<BaseDeviceParamVal> paramList = deviceParamReadFacadeMapper.selectHistoryParamValues(params);
return paramList != null ? paramList : Collections.<BaseDeviceParamVal>emptyList();
}
@Override
public List<Double> listNumericHistoryValues(String deviceCode, String paramCode, Date beginTime, Date endTime) {
List<BaseDeviceParamVal> historyList = listHistoryParamValues(deviceCode, paramCode, null, beginTime, endTime, 1000);
if (historyList.isEmpty()) {
return Collections.emptyList();
}
List<Double> values = new ArrayList<Double>(historyList.size());
for (BaseDeviceParamVal history : historyList) {
Double numericValue = parseNumeric(history.getParamValue());
if (numericValue != null) {
values.add(numericValue);
}
}
return values;
}
private Map<String, Object> buildQueryParams(String deviceCode, String paramCode, String paramName, Date beginTime, Date endTime) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("deviceCode", trimToNull(deviceCode));
params.put("paramCode", trimToNull(paramCode));
params.put("paramName", trimToNull(paramName));
params.put("beginTime", beginTime);
params.put("endTime", endTime);
params.put("tableSuffixes", resolveReadTableSuffixes(beginTime, endTime));
return params;
}
private List<String> resolveReadTableSuffixes(Date beginTime, Date endTime) {
if (beginTime == null || endTime == null) {
return Collections.emptyList();
}
return deviceParamTableRouter.resolveReadTableSuffixes(beginTime, endTime);
}
private Date buildLatestBeginTime() {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MONTH, -LATEST_LOOKBACK_MONTHS);
return calendar.getTime();
}
private Date addSeconds(Date baseTime, int seconds) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(baseTime);
calendar.add(Calendar.SECOND, seconds);
return calendar.getTime();
}
private Map<String, BaseDeviceParamVal> toParamIndex(List<BaseDeviceParamVal> paramList) {
Map<String, BaseDeviceParamVal> paramIndex = new LinkedHashMap<String, BaseDeviceParamVal>();
for (BaseDeviceParamVal paramVal : paramList) {
paramIndex.put(resolveParamKey(paramVal), paramVal);
}
return paramIndex;
}
private void fillCompareMap(TreeMap<String, Map<String, Object>> compareMap, Map<String, BaseDeviceParamVal> paramMap, boolean beforeFlag) {
for (Map.Entry<String, BaseDeviceParamVal> entry : paramMap.entrySet()) {
BaseDeviceParamVal paramVal = entry.getValue();
Map<String, Object> row = compareMap.get(entry.getKey());
if (row == null) {
row = new LinkedHashMap<String, Object>();
row.put("paramCode", trimToNull(paramVal.getParamCode()));
row.put("paramName", trimToNull(paramVal.getParamName()));
compareMap.put(entry.getKey(), row);
}
if (beforeFlag) {
row.put("beforeValue", paramVal.getParamValue());
} else {
row.put("afterValue", paramVal.getParamValue());
}
row.put("isChanged", isChanged(row.get("beforeValue"), row.get("afterValue")) ? "Y" : "N");
}
}
private boolean isChanged(Object beforeValue, Object afterValue) {
String beforeText = beforeValue == null ? null : beforeValue.toString();
String afterText = afterValue == null ? null : afterValue.toString();
if (beforeText == null) {
return afterText != null;
}
return !beforeText.equals(afterText);
}
private String resolveParamKey(BaseDeviceParamVal paramVal) {
if (paramVal == null) {
return "";
}
if (StringUtils.isNotEmpty(paramVal.getParamCode())) {
return "CODE:" + paramVal.getParamCode();
}
return "NAME:" + trimToNull(paramVal.getParamName());
}
private String trimToNull(String text) {
if (text == null) {
return null;
}
String trimmed = text.trim();
return trimmed.isEmpty() ? null : trimmed;
}
private Double parseNumeric(String paramValue) {
if (StringUtils.isEmpty(paramValue)) {
return null;
}
try {
return new BigDecimal(paramValue.trim()).doubleValue();
} catch (NumberFormatException ex) {
return null;
}
}
}

@ -4,11 +4,10 @@ import com.aucma.base.domain.BaseDeviceParam;
import com.aucma.base.domain.BaseDeviceParamVal;
import com.aucma.base.domain.ProcessAlert;
import com.aucma.base.mapper.BaseDeviceParamMapper;
import com.aucma.base.mapper.BaseDeviceParamValMapper;
import com.aucma.base.mapper.ProcessAlertMapper;
import com.aucma.base.service.IDeviceParamReadFacade;
import com.aucma.base.service.IProcessAlertService;
import com.aucma.common.utils.DateUtils;
import com.aucma.common.utils.SecurityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@ -33,9 +32,9 @@ public class ProcessAlertServiceImpl implements IProcessAlertService {
@Autowired
private BaseDeviceParamMapper baseDeviceParamMapper;
@Autowired
private BaseDeviceParamValMapper baseDeviceParamValMapper;
private IDeviceParamReadFacade deviceParamReadFacade;
@Override
public ProcessAlert selectProcessAlertByAlertId(Long alertId) {
@ -81,124 +80,103 @@ public class ProcessAlertServiceImpl implements IProcessAlertService {
@Override
public int checkThresholdAlerts() {
int alertCount = 0;
try {
// 查询所有启用预警的参数
BaseDeviceParam queryParam = new BaseDeviceParam();
queryParam.setAlertEnabled("1");
queryParam.setIsFlag(1L);
List<BaseDeviceParam> paramList = baseDeviceParamMapper.selectBaseDeviceParamList(queryParam);
if (paramList == null || paramList.isEmpty()) {
log.info("没有启用预警的参数配置");
return 0;
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
Date now = DateUtils.getNowDate();
for (BaseDeviceParam param : paramList) {
if (param.getUpperLimit() == null && param.getLowerLimit() == null) {
continue;
}
// 查询该参数最新的采集值
BaseDeviceParamVal valQuery = new BaseDeviceParamVal();
valQuery.setParamCode(param.getParamCode());
valQuery.setDeviceCode(param.getDeviceCode());
List<BaseDeviceParamVal> valList = baseDeviceParamValMapper.selectBaseDeviceParamValList(valQuery);
if (valList == null || valList.isEmpty()) {
continue;
}
// 取最新一条记录
BaseDeviceParamVal latestVal = valList.get(0);
String paramValueStr = latestVal.getParamValue();
if (paramValueStr == null || paramValueStr.isEmpty()) {
continue;
}
try {
double paramValue = Double.parseDouble(paramValueStr);
String alertType = null;
String thresholdValue = null;
// 检查是否超上限
if (param.getUpperLimit() != null && paramValue > param.getUpperLimit()) {
alertType = "超上限";
thresholdValue = String.valueOf(param.getUpperLimit());
}
// 检查是否低于下限
else if (param.getLowerLimit() != null && paramValue < param.getLowerLimit()) {
alertType = "低于下限";
thresholdValue = String.valueOf(param.getLowerLimit());
}
if (alertType != null) {
// 检查是否已存在未处理的预警(去重)
ProcessAlert existingAlert = processAlertMapper.selectUnhandledAlert(
param.getDeviceCode(),
param.getParamCode()
);
if (existingAlert != null) {
// 已存在未处理的预警,更新预警值和时间
existingAlert.setAlertValue(paramValueStr);
existingAlert.setAlertTime(latestVal.getCollectTime() != null ? latestVal.getCollectTime() : now);
existingAlert.setAlertContent(param.getParamName() + alertType + ",当前值:" + paramValueStr + ",阈值:" + thresholdValue);
updateProcessAlert(existingAlert);
log.info("更新未处理预警: {}", existingAlert.getAlertContent());
} else {
// 查询最新预警(包含已处理)
ProcessAlert latestAlert = processAlertMapper.selectLatestAlert(
param.getDeviceCode(),
param.getParamCode()
);
// 如果存在已处理的预警检查预警冷却时间5分钟
if (latestAlert != null && "2".equals(latestAlert.getAlertStatus())) {
long timeDiff = now.getTime() - latestAlert.getAlertTime().getTime();
long cooldownMinutes = 5; // 预警冷却时间5分钟
if (timeDiff < cooldownMinutes * 60 * 1000) {
// 还在冷却期内,不生成新预警
log.debug("预警冷却期内,跳过: deviceCode={}, paramCode={}",
param.getDeviceCode(), param.getParamCode());
continue;
}
}
// 生成新的预警记录
ProcessAlert alert = new ProcessAlert();
alert.setAlertCode("ALERT-" + sdf.format(now) + "-" + (alertCount + 1));
alert.setAlertType("参数超限");
alert.setAlertLevel(param.getAlertLevel() != null ? param.getAlertLevel() : "1");
alert.setDeviceCode(param.getDeviceCode());
alert.setDeviceName(param.getDeviceName());
alert.setParamCode(param.getParamCode());
alert.setParamName(param.getParamName());
alert.setAlertContent(param.getParamName() + alertType + ",当前值:" + paramValueStr + ",阈值:" + thresholdValue);
alert.setAlertValue(paramValueStr);
alert.setThresholdValue(thresholdValue);
alert.setAlertTime(latestVal.getCollectTime() != null ? latestVal.getCollectTime() : now);
alert.setAlertStatus("0");
alert.setIsFlag("1");
alert.setCreateTime(now);
insertProcessAlert(alert);
alertCount++;
log.info("生成新预警: {}", alert.getAlertContent());
}
}
} catch (NumberFormatException e) {
log.warn("参数值无法转换为数字: paramCode={}, value={}", param.getParamCode(), paramValueStr);
}
}
} catch (Exception e) {
log.error("检查阈值预警时发生错误", e);
BaseDeviceParam queryParam = new BaseDeviceParam();
queryParam.setAlertEnabled("1");
queryParam.setIsFlag(1L);
List<BaseDeviceParam> paramList = baseDeviceParamMapper.selectBaseDeviceParamList(queryParam);
if (paramList == null || paramList.isEmpty()) {
log.info("没有启用预警的参数配置");
return 0;
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
Date now = DateUtils.getNowDate();
for (BaseDeviceParam param : paramList) {
if (param.getUpperLimit() == null && param.getLowerLimit() == null) {
continue;
}
// 为什么这样做:预警必须基于统一 latest 口径判断,
// 否则 NEW 设备月表数据和跨月数据都会被旧逻辑直接漏掉。
BaseDeviceParamVal latestVal = deviceParamReadFacade.getLatestParamValue(
param.getDeviceCode(), param.getParamCode(), param.getParamName());
if (latestVal == null || latestVal.getParamValue() == null || latestVal.getParamValue().isEmpty()) {
continue;
}
String paramValueStr = latestVal.getParamValue();
try {
double paramValue = Double.parseDouble(paramValueStr);
String alertType = null;
String thresholdValue = null;
if (param.getUpperLimit() != null && paramValue > param.getUpperLimit()) {
alertType = "超上限";
thresholdValue = String.valueOf(param.getUpperLimit());
} else if (param.getLowerLimit() != null && paramValue < param.getLowerLimit()) {
alertType = "低于下限";
thresholdValue = String.valueOf(param.getLowerLimit());
}
if (alertType == null) {
continue;
}
ProcessAlert existingAlert = processAlertMapper.selectUnhandledAlert(
param.getDeviceCode(),
param.getParamCode()
);
if (existingAlert != null) {
existingAlert.setAlertValue(paramValueStr);
existingAlert.setAlertTime(latestVal.getCollectTime() != null ? latestVal.getCollectTime() : now);
existingAlert.setAlertContent(param.getParamName() + alertType + ",当前值:" + paramValueStr + ",阈值:" + thresholdValue);
updateProcessAlert(existingAlert);
log.info("更新未处理预警: {}", existingAlert.getAlertContent());
continue;
}
ProcessAlert latestAlert = processAlertMapper.selectLatestAlert(
param.getDeviceCode(),
param.getParamCode()
);
if (latestAlert != null && "2".equals(latestAlert.getAlertStatus())) {
long timeDiff = now.getTime() - latestAlert.getAlertTime().getTime();
long cooldownMinutes = 5;
if (timeDiff < cooldownMinutes * 60 * 1000) {
log.debug("预警冷却期内,跳过: deviceCode={}, paramCode={}",
param.getDeviceCode(), param.getParamCode());
continue;
}
}
ProcessAlert alert = new ProcessAlert();
alert.setAlertCode("ALERT-" + sdf.format(now) + "-" + (alertCount + 1));
alert.setAlertType("参数超限");
alert.setAlertLevel(param.getAlertLevel() != null ? param.getAlertLevel() : "1");
alert.setDeviceCode(param.getDeviceCode());
alert.setDeviceName(param.getDeviceName());
alert.setParamCode(param.getParamCode());
alert.setParamName(param.getParamName());
alert.setAlertContent(param.getParamName() + alertType + ",当前值:" + paramValueStr + ",阈值:" + thresholdValue);
alert.setAlertValue(paramValueStr);
alert.setThresholdValue(thresholdValue);
alert.setAlertTime(latestVal.getCollectTime() != null ? latestVal.getCollectTime() : now);
alert.setAlertStatus("0");
alert.setIsFlag("1");
alert.setCreateTime(now);
insertProcessAlert(alert);
alertCount++;
log.info("生成新预警: {}", alert.getAlertContent());
} catch (NumberFormatException e) {
log.warn("参数值无法转换为数字: paramCode={}, value={}", param.getParamCode(), paramValueStr);
}
}
return alertCount;
}

@ -5,9 +5,10 @@ import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.aucma.base.mapper.ProcessSnapshotMapper;
import com.aucma.base.domain.ProcessSnapshot;
import com.aucma.base.domain.BaseDeviceParamVal;
import com.aucma.base.domain.ProcessSnapshot;
import com.aucma.base.mapper.ProcessSnapshotMapper;
import com.aucma.base.service.IDeviceParamReadFacade;
import com.aucma.base.service.IProcessSnapshotService;
import com.aucma.common.utils.DateUtils;
import com.aucma.common.utils.SecurityUtils;
@ -23,6 +24,9 @@ public class ProcessSnapshotServiceImpl implements IProcessSnapshotService {
@Autowired
private ProcessSnapshotMapper processSnapshotMapper;
@Autowired
private IDeviceParamReadFacade deviceParamReadFacade;
@Override
public ProcessSnapshot selectProcessSnapshotBySnapshotId(Long snapshotId) {
return processSnapshotMapper.selectProcessSnapshotBySnapshotId(snapshotId);
@ -67,8 +71,10 @@ public class ProcessSnapshotServiceImpl implements IProcessSnapshotService {
public ProcessSnapshot selectSnapshotDetail(Long snapshotId) {
ProcessSnapshot snapshot = processSnapshotMapper.selectProcessSnapshotBySnapshotId(snapshotId);
if (snapshot != null && snapshot.getDeviceCode() != null && snapshot.getSnapshotTime() != null) {
List<BaseDeviceParamVal> paramList = processSnapshotMapper.selectParamsBySnapshotTime(
snapshot.getDeviceCode(), snapshot.getSnapshotTime());
// 为什么这样做:快照详情已经不是“查单表最近一分钟”这么简单,
// 统一交给 Facade 后才能同时兼容 OLD 单表、NEW 月分表和跨月查询。
List<BaseDeviceParamVal> paramList = deviceParamReadFacade.listParamsByTimePoint(
snapshot.getDeviceCode(), snapshot.getSnapshotTime(), 60);
snapshot.setParamList(paramList);
}
return snapshot;
@ -87,10 +93,13 @@ public class ProcessSnapshotServiceImpl implements IProcessSnapshotService {
return null;
}
return processSnapshotMapper.compareSnapshots(
// 为什么这样做:快照对比本质是两个时间点的参数差异对比,
// 由统一参数能力负责底层路由后,快照服务才不用继续感知物理表结构。
return deviceParamReadFacade.compareParamsByTimePoint(
snapshot1.getDeviceCode(),
snapshot1.getSnapshotTime(),
snapshot2.getSnapshotTime());
snapshot2.getSnapshotTime(),
60);
}
/**

@ -0,0 +1,125 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.aucma.base.mapper.DeviceParamReadFacadeMapper">
<resultMap type="BaseDeviceParamVal" id="BaseDeviceParamValResult">
<result property="recordId" column="record_id"/>
<result property="paramCode" column="param_code"/>
<result property="deviceCode" column="device_code"/>
<result property="deviceId" column="device_id"/>
<result property="paramName" column="param_name"/>
<result property="paramValue" column="param_value"/>
<result property="collectTime" column="collect_time"/>
<result property="recordTime" column="record_time"/>
</resultMap>
<sql id="baseSelectColumns">
record_id || '' AS record_id,
param_code,
device_code,
device_id,
param_name,
param_value,
collect_time,
record_time
</sql>
<sql id="commonFilters">
<if test="deviceCode != null and deviceCode != ''">
AND device_code = #{deviceCode}
</if>
<if test="paramCode != null and paramCode != ''">
AND param_code = #{paramCode}
</if>
<if test="paramName != null and paramName != ''">
AND param_name = #{paramName}
</if>
<if test="paramNames != null and paramNames.size() > 0">
AND param_name IN
<foreach item="paramNameItem" collection="paramNames" open="(" separator="," close=")">
#{paramNameItem}
</foreach>
</if>
<if test="beginTime != null">
AND NVL(record_time, collect_time) &gt;= #{beginTime}
</if>
<if test="endTime != null">
AND NVL(record_time, collect_time) &lt;= #{endTime}
</if>
</sql>
<sql id="mergedParamSource">
<trim suffixOverrides="UNION ALL">
SELECT <include refid="baseSelectColumns"/>
FROM BASE_DEVICE_PARAM_VAL
WHERE 1 = 1
<include refid="commonFilters"/>
UNION ALL
<if test="tableSuffixes != null and tableSuffixes.size() > 0">
<foreach item="suffix" collection="tableSuffixes" separator=" UNION ALL ">
SELECT <include refid="baseSelectColumns"/>
FROM BASE_DEVICE_PARAM_VAL_${suffix}
WHERE 1 = 1
<include refid="commonFilters"/>
</foreach>
</if>
</trim>
</sql>
<select id="selectLatestParamValue" parameterType="java.util.Map" resultMap="BaseDeviceParamValResult">
SELECT *
FROM (
SELECT merged_param.*,
ROW_NUMBER() OVER (
PARTITION BY merged_param.device_code,
NVL(merged_param.param_code, merged_param.param_name)
ORDER BY NVL(merged_param.record_time, merged_param.collect_time) DESC,
merged_param.record_id DESC
) AS rn
FROM (
<include refid="mergedParamSource"/>
) merged_param
)
WHERE rn = 1
AND ROWNUM = 1
</select>
<select id="selectParamsByTimePoint" parameterType="java.util.Map" resultMap="BaseDeviceParamValResult">
SELECT *
FROM (
SELECT merged_param.*,
ROW_NUMBER() OVER (
PARTITION BY NVL(merged_param.param_code, merged_param.param_name)
ORDER BY ABS((NVL(merged_param.record_time, merged_param.collect_time) - #{snapshotTime}) * 86400),
NVL(merged_param.record_time, merged_param.collect_time) DESC,
merged_param.record_id DESC
) AS rn
FROM (
<include refid="mergedParamSource"/>
) merged_param
)
WHERE rn = 1
ORDER BY NVL(param_code, param_name), NVL(record_time, collect_time) DESC
</select>
<select id="selectHistoryParamValues" parameterType="java.util.Map" resultMap="BaseDeviceParamValResult">
SELECT *
FROM (
SELECT merged_param.*,
ROW_NUMBER() OVER (
ORDER BY NVL(merged_param.record_time, merged_param.collect_time) ASC,
merged_param.record_id ASC
) AS row_num
FROM (
<include refid="mergedParamSource"/>
) merged_param
)
<if test="limit != null">
WHERE row_num &lt;= #{limit}
</if>
ORDER BY row_num
</select>
</mapper>

@ -119,61 +119,4 @@
</foreach>
</delete>
<!-- 根据快照时间点查询参数值时间窗口60秒内最接近的记录 -->
<select id="selectParamsBySnapshotTime" resultType="BaseDeviceParamVal">
SELECT * FROM (
SELECT
v.record_id AS recordId,
v.param_code AS paramCode,
v.device_code AS deviceCode,
v.device_id AS deviceId,
v.param_name AS paramName,
v.param_value AS paramValue,
v.collect_time AS collectTime,
v.record_time AS recordTime,
ROW_NUMBER() OVER (PARTITION BY v.param_code ORDER BY ABS(v.collect_time - #{snapshotTime})) AS rn
FROM base_device_param_val v
WHERE v.device_code = #{deviceCode}
AND v.collect_time BETWEEN #{snapshotTime} - INTERVAL '1' MINUTE AND #{snapshotTime} + INTERVAL '1' MINUTE
) WHERE rn = 1
ORDER BY paramCode
</select>
<!-- 对比两个快照的参数差异 -->
<select id="compareSnapshots" resultType="java.util.Map">
WITH snapshot1_params AS (
SELECT * FROM (
SELECT
v.param_code,
v.param_name,
v.param_value,
ROW_NUMBER() OVER (PARTITION BY v.param_code ORDER BY ABS(v.collect_time - #{snapshotTime1})) AS rn
FROM base_device_param_val v
WHERE v.device_code = #{deviceCode}
AND v.collect_time BETWEEN #{snapshotTime1} - INTERVAL '1' MINUTE AND #{snapshotTime1} + INTERVAL '1' MINUTE
) WHERE rn = 1
),
snapshot2_params AS (
SELECT * FROM (
SELECT
v.param_code,
v.param_name,
v.param_value,
ROW_NUMBER() OVER (PARTITION BY v.param_code ORDER BY ABS(v.collect_time - #{snapshotTime2})) AS rn
FROM base_device_param_val v
WHERE v.device_code = #{deviceCode}
AND v.collect_time BETWEEN #{snapshotTime2} - INTERVAL '1' MINUTE AND #{snapshotTime2} + INTERVAL '1' MINUTE
) WHERE rn = 1
)
SELECT
NVL(a.param_code, b.param_code) AS paramCode,
NVL(a.param_name, b.param_name) AS paramName,
a.param_value AS beforeValue,
b.param_value AS afterValue,
CASE WHEN NVL(a.param_value, 'NULL') != NVL(b.param_value, 'NULL') THEN 'Y' ELSE 'N' END AS isChanged
FROM snapshot1_params a
FULL OUTER JOIN snapshot2_params b ON a.param_code = b.param_code
ORDER BY NVL(a.param_code, b.param_code)
</select>
</mapper>

@ -3,7 +3,7 @@ package com.aucma.production.service.impl;
import com.aucma.base.domain.BaseDeviceLedger;
import com.aucma.base.domain.BaseDeviceParamVal;
import com.aucma.base.mapper.BaseDeviceLedgerMapper;
import com.aucma.base.mapper.BaseDeviceParamValMapper;
import com.aucma.base.service.IDeviceParamReadFacade;
import com.aucma.base.service.IBaseDeviceParamValService;
import com.aucma.common.constant.AnDonConstants;
import com.aucma.common.utils.DateUtils;
@ -37,7 +37,7 @@ public class AndonDashboardServiceImpl implements IAndonDashboardService {
private BaseDeviceLedgerMapper baseDeviceLedgerMapper;
@Autowired
private BaseDeviceParamValMapper baseDeviceParamValMapper;
private IDeviceParamReadFacade deviceParamReadFacade;
@Autowired
private IBaseDeviceParamValService baseDeviceParamValService;
@ -243,45 +243,11 @@ public class AndonDashboardServiceImpl implements IAndonDashboardService {
long totalPlannedTime = 0, totalDowntime = 0;
for (BaseDeviceLedger device : devices) {
// 查询该设备的总计划生产时间
BaseDeviceParamVal plannedQuery = new BaseDeviceParamVal();
plannedQuery.setDeviceCode(device.getDeviceCode());
plannedQuery.setParamName("机台状态-总计划生产时间");
List<BaseDeviceParamVal> plannedList = baseDeviceParamValMapper.selectLatestBaseDeviceParamValList(plannedQuery);
double plannedTime = 0;
if (plannedList != null && !plannedList.isEmpty()) {
plannedTime = parseDouble(plannedList.get(0).getParamValue());
}
// 查询该设备的实际生产时间
BaseDeviceParamVal actualQuery = new BaseDeviceParamVal();
actualQuery.setDeviceCode(device.getDeviceCode());
actualQuery.setParamName("机台状态-实际生产时间");
List<BaseDeviceParamVal> actualList = baseDeviceParamValMapper.selectLatestBaseDeviceParamValList(actualQuery);
double actualTime = 0;
if (actualList != null && !actualList.isEmpty()) {
actualTime = parseDouble(actualList.get(0).getParamValue());
}
// 查询该设备的设定产出数量
BaseDeviceParamVal targetQuery = new BaseDeviceParamVal();
targetQuery.setDeviceCode(device.getDeviceCode());
targetQuery.setParamName("机台状态-设定产出数量");
List<BaseDeviceParamVal> targetList = baseDeviceParamValMapper.selectLatestBaseDeviceParamValList(targetQuery);
long targetOutput = 0;
if (targetList != null && !targetList.isEmpty()) {
targetOutput = parseLong(targetList.get(0).getParamValue());
}
// 查询该设备的实际产出数量
BaseDeviceParamVal actualOutputQuery = new BaseDeviceParamVal();
actualOutputQuery.setDeviceCode(device.getDeviceCode());
actualOutputQuery.setParamName("机台状态-实际产出数量");
List<BaseDeviceParamVal> actualOutputList = baseDeviceParamValMapper.selectLatestBaseDeviceParamValList(actualOutputQuery);
long actualOutput = 0;
if (actualOutputList != null && !actualOutputList.isEmpty()) {
actualOutput = parseLong(actualOutputList.get(0).getParamValue());
}
// 为什么这样做:安灯是参数消费方,不应该继续直接注入明细 Mapper 感知底层单表/分表。
double plannedTime = queryLatestDoubleValue(device.getDeviceCode(), "机台状态-总计划生产时间");
double actualTime = queryLatestDoubleValue(device.getDeviceCode(), "机台状态-实际生产时间");
long targetOutput = queryLatestLongValue(device.getDeviceCode(), "机台状态-设定产出数量");
long actualOutput = queryLatestLongValue(device.getDeviceCode(), "机台状态-实际产出数量");
// 计算OEE三个指标
double availability = plannedTime > 0 ? (actualTime / plannedTime) : 0;
@ -354,25 +320,8 @@ public class AndonDashboardServiceImpl implements IAndonDashboardService {
long lineRunning = 0;
for (BaseDeviceLedger device : lineDeviceList) {
// 查询该设备的总计划生产时间
BaseDeviceParamVal plannedQuery = new BaseDeviceParamVal();
plannedQuery.setDeviceCode(device.getDeviceCode());
plannedQuery.setParamName("机台状态-总计划生产时间");
List<BaseDeviceParamVal> plannedList = baseDeviceParamValMapper.selectLatestBaseDeviceParamValList(plannedQuery);
if (plannedList != null && !plannedList.isEmpty()) {
double plannedTime = parseDouble(plannedList.get(0).getParamValue());
linePlanned += (long) plannedTime;
}
// 查询该设备的实际生产时间
BaseDeviceParamVal actualQuery = new BaseDeviceParamVal();
actualQuery.setDeviceCode(device.getDeviceCode());
actualQuery.setParamName("机台状态-实际生产时间");
List<BaseDeviceParamVal> actualList = baseDeviceParamValMapper.selectLatestBaseDeviceParamValList(actualQuery);
if (actualList != null && !actualList.isEmpty()) {
double actualTime = parseDouble(actualList.get(0).getParamValue());
lineRunning += (long) actualTime;
}
linePlanned += (long) queryLatestDoubleValue(device.getDeviceCode(), "机台状态-总计划生产时间");
lineRunning += (long) queryLatestDoubleValue(device.getDeviceCode(), "机台状态-实际生产时间");
}
totalRunning += lineRunning;
@ -588,6 +537,16 @@ public class AndonDashboardServiceImpl implements IAndonDashboardService {
.doubleValue();
}
private double queryLatestDoubleValue(String deviceCode, String paramName) {
BaseDeviceParamVal latestVal = deviceParamReadFacade.getLatestParamValue(deviceCode, null, paramName);
return latestVal == null ? 0D : parseDouble(latestVal.getParamValue());
}
private long queryLatestLongValue(String deviceCode, String paramName) {
BaseDeviceParamVal latestVal = deviceParamReadFacade.getLatestParamValue(deviceCode, null, paramName);
return latestVal == null ? 0L : parseLong(latestVal.getParamValue());
}
private Number getNumber(Map<String, Object> data, String upperKey, String lowerKey) {
Object value = data.get(upperKey);
if (value == null) {

@ -2,7 +2,9 @@ package com.aucma.report.controller;
import com.aucma.common.core.controller.BaseController;
import com.aucma.common.core.domain.AjaxResult;
import com.aucma.common.core.page.PageDomain;
import com.aucma.common.core.page.TableDataInfo;
import com.aucma.common.core.page.TableSupport;
import com.aucma.report.domain.DeviceParamReportQuery;
import com.aucma.report.service.IDeviceParamAnalysisService;
import org.springframework.beans.factory.annotation.Autowired;
@ -11,6 +13,9 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collections;
import java.util.List;
/**
* Controller
*
@ -38,8 +43,7 @@ public class DeviceParamAnalysisController extends BaseController {
@PreAuthorize("@ss.hasPermi('baseDeviceParamVal:trace:list')")
@GetMapping("/anomaly/list")
public TableDataInfo anomalyList(DeviceParamReportQuery query) {
startPage();
return getDataTable(deviceParamAnalysisService.selectParamAnomalyList(query));
return buildPageData(deviceParamAnalysisService.selectParamAnomalyList(query));
}
/**
@ -57,7 +61,22 @@ public class DeviceParamAnalysisController extends BaseController {
@PreAuthorize("@ss.hasPermi('baseDeviceParamVal:trace:list')")
@GetMapping("/switch/list")
public TableDataInfo switchList(DeviceParamReportQuery query) {
startPage();
return getDataTable(deviceParamAnalysisService.selectSwitchTraceList(query));
return buildPageData(deviceParamAnalysisService.selectSwitchTraceList(query));
}
private TableDataInfo buildPageData(List<?> fullList) {
List<?> safeList = fullList == null ? Collections.emptyList() : fullList;
PageDomain pageDomain = TableSupport.buildPageRequest();
int pageNum = Math.max(pageDomain.getPageNum(), 1);
int pageSize = Math.max(pageDomain.getPageSize(), 1);
int fromIndex = (pageNum - 1) * pageSize;
int toIndex = Math.min(fromIndex + pageSize, safeList.size());
List<?> pageRows = fromIndex >= safeList.size() ? Collections.emptyList() : safeList.subList(fromIndex, toIndex);
TableDataInfo rspData = new TableDataInfo();
rspData.setCode(200);
rspData.setMsg("查询成功");
rspData.setRows(pageRows);
rspData.setTotal(safeList.size());
return rspData;
}
}

@ -1,10 +1,7 @@
package com.aucma.report.mapper;
import com.aucma.report.domain.DeviceParamReportQuery;
import com.aucma.report.domain.vo.DeviceParamAnomalyVo;
import com.aucma.report.domain.vo.DeviceParamOptionVo;
import com.aucma.report.domain.vo.DeviceParamSpcPointVo;
import com.aucma.report.domain.vo.DeviceParamSwitchTraceVo;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@ -21,19 +18,4 @@ public interface DeviceParamAnalysisMapper {
*
*/
List<DeviceParamOptionVo> selectParamOptions(DeviceParamReportQuery query);
/**
*
*/
List<DeviceParamAnomalyVo> selectParamAnomalyList(DeviceParamReportQuery query);
/**
*
*/
List<DeviceParamSpcPointVo> selectSpcPoints(DeviceParamReportQuery query);
/**
*
*/
List<DeviceParamSwitchTraceVo> selectSwitchTraceList(DeviceParamReportQuery query);
}

@ -1,5 +1,9 @@
package com.aucma.report.service.impl;
import com.aucma.base.domain.BaseDeviceParam;
import com.aucma.base.domain.BaseDeviceParamVal;
import com.aucma.base.mapper.BaseDeviceParamMapper;
import com.aucma.base.service.IDeviceParamReadFacade;
import com.aucma.report.domain.DeviceParamReportQuery;
import com.aucma.report.domain.vo.DeviceParamAnomalyVo;
import com.aucma.report.domain.vo.DeviceParamOptionVo;
@ -15,24 +19,41 @@ import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* Service
*
* @author Codex
*/
@Service
public class DeviceParamAnalysisServiceImpl implements IDeviceParamAnalysisService {
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static final List<String> SWITCH_PARAM_NAMES = Arrays.asList(
"机台状态-模具数据名称", "机台状态-物料描述", "机台状态-产品描述", "机台状态-产品描述1");
private static final List<String> STATUS_PARAM_NAMES = Arrays.asList(
"机台状态-三色灯机器运行", "机台状态-三色灯机器暂停", "机台状态-三色灯机器待机", "机台状态-三色灯机器报警");
private static final String OUTPUT_PARAM_NAME = "机台状态-实际产出数量";
@Autowired
private DeviceParamAnalysisMapper deviceParamAnalysisMapper;
@Autowired
private BaseDeviceParamMapper baseDeviceParamMapper;
@Autowired
private IDeviceParamReadFacade deviceParamReadFacade;
@Override
public List<DeviceParamOptionVo> selectParamOptions(DeviceParamReportQuery query) {
normalizeQuery(query);
@ -42,13 +63,78 @@ public class DeviceParamAnalysisServiceImpl implements IDeviceParamAnalysisServi
@Override
public List<DeviceParamAnomalyVo> selectParamAnomalyList(DeviceParamReportQuery query) {
normalizeQuery(query);
return deviceParamAnalysisMapper.selectParamAnomalyList(query);
Date beginTime = parseDate(query.getStartTime());
Date endTime = parseDate(query.getEndTime());
if (beginTime == null || endTime == null) {
return Collections.emptyList();
}
List<BaseDeviceParam> alertParams = loadAlertParams(query.getDeviceCode(), query.getParamCode());
if (alertParams.isEmpty()) {
return Collections.emptyList();
}
Map<String, List<BaseDeviceParam>> alertParamsByDevice = new HashMap<String, List<BaseDeviceParam>>();
for (BaseDeviceParam alertParam : alertParams) {
if (StringUtils.isBlank(alertParam.getDeviceCode())) {
continue;
}
if (!alertParamsByDevice.containsKey(alertParam.getDeviceCode())) {
alertParamsByDevice.put(alertParam.getDeviceCode(), new ArrayList<BaseDeviceParam>());
}
alertParamsByDevice.get(alertParam.getDeviceCode()).add(alertParam);
}
List<DeviceParamAnomalyVo> resultList = new ArrayList<DeviceParamAnomalyVo>();
for (Map.Entry<String, List<BaseDeviceParam>> entry : alertParamsByDevice.entrySet()) {
List<BaseDeviceParamVal> historyList = deviceParamReadFacade.listHistoryParamValues(
entry.getKey(), null, null, beginTime, endTime, null);
if (historyList.isEmpty()) {
continue;
}
historyList.sort(Comparator.comparing(this::resolveEventTime));
Map<String, BaseDeviceParam> alertConfigMap = buildAlertConfigMap(entry.getValue());
Map<String, List<NumericSample>> sampleMap = new HashMap<String, List<NumericSample>>();
for (BaseDeviceParamVal history : historyList) {
BaseDeviceParam config = alertConfigMap.get(buildParamKey(history.getParamCode(), history.getParamName()));
if (config == null) {
continue;
}
Double numericValue = parseNumeric(history.getParamValue());
if (numericValue == null) {
continue;
}
String paramKey = buildParamKey(config.getParamCode(), config.getParamName());
if (!sampleMap.containsKey(paramKey)) {
sampleMap.put(paramKey, new ArrayList<NumericSample>());
}
sampleMap.get(paramKey).add(new NumericSample(history, numericValue.doubleValue()));
}
for (BaseDeviceParam alertParam : entry.getValue()) {
DeviceParamAnomalyVo anomalyVo = buildAnomalyVo(
alertParam,
sampleMap.get(buildParamKey(alertParam.getParamCode(), alertParam.getParamName()))
);
if (anomalyVo != null) {
resultList.add(anomalyVo);
}
}
}
resultList.sort(Comparator
.comparing(DeviceParamAnomalyVo::getOverLimitCount, Comparator.nullsLast(Comparator.reverseOrder()))
.thenComparing(DeviceParamAnomalyVo::getAbnormalDurationMinutes, Comparator.nullsLast(Comparator.reverseOrder()))
.thenComparing(DeviceParamAnomalyVo::getLastAbnormalTime, Comparator.nullsLast(Comparator.reverseOrder())));
return resultList;
}
@Override
public DeviceParamSpcReportVo getSpcReport(DeviceParamReportQuery query) {
normalizeQuery(query);
List<DeviceParamSpcPointVo> points = deviceParamAnalysisMapper.selectSpcPoints(query);
BaseDeviceParam config = loadSingleParam(query.getDeviceCode(), query.getParamCode());
List<BaseDeviceParamVal> historyList = deviceParamReadFacade.listHistoryParamValues(
query.getDeviceCode(), query.getParamCode(), null, parseDate(query.getStartTime()), parseDate(query.getEndTime()), 1500);
List<DeviceParamSpcPointVo> points = buildSpcPoints(historyList, config);
DeviceParamSpcReportVo reportVo = new DeviceParamSpcReportVo();
reportVo.setPoints(points != null ? points : new ArrayList<DeviceParamSpcPointVo>());
if (points == null || points.isEmpty()) {
@ -56,6 +142,7 @@ public class DeviceParamAnalysisServiceImpl implements IDeviceParamAnalysisServi
reportVo.setOutOfControlCount(0);
return reportVo;
}
DeviceParamSpcPointVo firstPoint = points.get(0);
reportVo.setDeviceCode(firstPoint.getDeviceCode());
reportVo.setDeviceName(firstPoint.getDeviceName());
@ -77,7 +164,6 @@ public class DeviceParamAnalysisServiceImpl implements IDeviceParamAnalysisServi
min = Math.min(min, value);
}
// 这里把SPC口径固化在服务层是为了保证控制限和过程能力前后端只算一次。
double mean = sum / values.size();
double variance = 0D;
for (Double value : values) {
@ -87,7 +173,6 @@ public class DeviceParamAnalysisServiceImpl implements IDeviceParamAnalysisServi
double cl = mean;
double ucl = mean + 3 * stdDev;
double lcl = mean - 3 * stdDev;
int outOfControlCount = 0;
for (Double value : values) {
if (value > ucl || value < lcl) {
@ -112,12 +197,39 @@ public class DeviceParamAnalysisServiceImpl implements IDeviceParamAnalysisServi
@Override
public List<DeviceParamSwitchTraceVo> selectSwitchTraceList(DeviceParamReportQuery query) {
normalizeQuery(query);
return deviceParamAnalysisMapper.selectSwitchTraceList(query);
Date beginTime = parseDate(query.getStartTime());
Date endTime = parseDate(query.getEndTime());
if (beginTime == null || endTime == null) {
return Collections.emptyList();
}
int observeMinutes = query.getObserveMinutes() == null ? 30 : query.getObserveMinutes();
Date observeEndTime = addMinutes(endTime, observeMinutes);
List<BaseDeviceParam> switchParams = loadSwitchParams(query.getDeviceCode(), query.getSwitchType());
if (switchParams.isEmpty()) {
return Collections.emptyList();
}
Map<String, List<BaseDeviceParam>> switchParamsByDevice = new HashMap<String, List<BaseDeviceParam>>();
for (BaseDeviceParam switchParam : switchParams) {
if (StringUtils.isBlank(switchParam.getDeviceCode())) {
continue;
}
if (!switchParamsByDevice.containsKey(switchParam.getDeviceCode())) {
switchParamsByDevice.put(switchParam.getDeviceCode(), new ArrayList<BaseDeviceParam>());
}
switchParamsByDevice.get(switchParam.getDeviceCode()).add(switchParam);
}
Map<String, List<BaseDeviceParam>> alertParamsByDevice = loadAlertParamsByDevice(query.getDeviceCode());
List<DeviceParamSwitchTraceVo> resultList = new ArrayList<DeviceParamSwitchTraceVo>();
for (Map.Entry<String, List<BaseDeviceParam>> entry : switchParamsByDevice.entrySet()) {
appendSwitchTraceRows(resultList, entry.getKey(), entry.getValue(), alertParamsByDevice.get(entry.getKey()), beginTime, endTime, observeEndTime, observeMinutes, query.getSwitchType());
}
resultList.sort(Comparator.comparing(DeviceParamSwitchTraceVo::getChangeTime, Comparator.nullsLast(Comparator.reverseOrder())));
return resultList;
}
/**
* SQL
*/
private void normalizeQuery(DeviceParamReportQuery query) {
if (query == null) {
return;
@ -134,7 +246,6 @@ public class DeviceParamAnalysisServiceImpl implements IDeviceParamAnalysisServi
} else if (observeMinutes < 1) {
query.setObserveMinutes(1);
} else if (observeMinutes > 1440) {
// 观察窗口限制在1天内避免单次查询把整库相关数据扫出来。
query.setObserveMinutes(1440);
}
}
@ -172,22 +283,12 @@ public class DeviceParamAnalysisServiceImpl implements IDeviceParamAnalysisServi
}
}
private String trimToNull(String text) {
return StringUtils.isBlank(text) ? null : text.trim();
}
private Double calcCpk(double mean, double stdDev, Double upperLimit, Double lowerLimit) {
if (stdDev <= 0D) {
return 0D;
}
Double cpu = null;
Double cpl = null;
if (upperLimit != null) {
cpu = (upperLimit - mean) / (3 * stdDev);
}
if (lowerLimit != null) {
cpl = (mean - lowerLimit) / (3 * stdDev);
}
Double cpu = upperLimit == null ? null : (upperLimit - mean) / (3 * stdDev);
Double cpl = lowerLimit == null ? null : (mean - lowerLimit) / (3 * stdDev);
if (cpu == null && cpl == null) {
return null;
}
@ -206,4 +307,472 @@ public class DeviceParamAnalysisServiceImpl implements IDeviceParamAnalysisServi
}
return BigDecimal.valueOf(value).setScale(4, RoundingMode.HALF_UP).doubleValue();
}
private List<BaseDeviceParam> loadAlertParams(String deviceCode, String paramCode) {
BaseDeviceParam query = new BaseDeviceParam();
query.setDeviceCode(trimToNull(deviceCode));
query.setParamCode(trimToNull(paramCode));
query.setIsFlag(1L);
query.setAlertEnabled("1");
List<BaseDeviceParam> paramList = baseDeviceParamMapper.selectBaseDeviceParamList(query);
if (paramList == null || paramList.isEmpty()) {
return Collections.emptyList();
}
List<BaseDeviceParam> resultList = new ArrayList<BaseDeviceParam>();
for (BaseDeviceParam param : paramList) {
if (!isNumericParam(param)) {
continue;
}
if (param.getUpperLimit() == null && param.getLowerLimit() == null) {
continue;
}
resultList.add(param);
}
return resultList;
}
private Map<String, List<BaseDeviceParam>> loadAlertParamsByDevice(String deviceCode) {
List<BaseDeviceParam> alertParams = loadAlertParams(deviceCode, null);
Map<String, List<BaseDeviceParam>> resultMap = new HashMap<String, List<BaseDeviceParam>>();
for (BaseDeviceParam alertParam : alertParams) {
if (StringUtils.isBlank(alertParam.getDeviceCode())) {
continue;
}
if (!resultMap.containsKey(alertParam.getDeviceCode())) {
resultMap.put(alertParam.getDeviceCode(), new ArrayList<BaseDeviceParam>());
}
resultMap.get(alertParam.getDeviceCode()).add(alertParam);
}
return resultMap;
}
private BaseDeviceParam loadSingleParam(String deviceCode, String paramCode) {
BaseDeviceParam query = new BaseDeviceParam();
query.setDeviceCode(trimToNull(deviceCode));
query.setParamCode(trimToNull(paramCode));
query.setIsFlag(1L);
List<BaseDeviceParam> paramList = baseDeviceParamMapper.selectBaseDeviceParamList(query);
if (paramList == null || paramList.isEmpty()) {
return null;
}
return paramList.get(0);
}
private List<BaseDeviceParam> loadSwitchParams(String deviceCode, String switchType) {
BaseDeviceParam query = new BaseDeviceParam();
query.setDeviceCode(trimToNull(deviceCode));
query.setIsFlag(1L);
List<BaseDeviceParam> paramList = baseDeviceParamMapper.selectBaseDeviceParamList(query);
if (paramList == null || paramList.isEmpty()) {
return Collections.emptyList();
}
List<BaseDeviceParam> resultList = new ArrayList<BaseDeviceParam>();
List<String> switchParamNames = resolveSwitchParamNames(switchType);
for (BaseDeviceParam param : paramList) {
if (switchParamNames.contains(param.getParamName())) {
resultList.add(param);
}
}
return resultList;
}
private List<DeviceParamSpcPointVo> buildSpcPoints(List<BaseDeviceParamVal> historyList, BaseDeviceParam config) {
if (historyList == null || historyList.isEmpty()) {
return Collections.emptyList();
}
historyList.sort(Comparator.comparing(this::resolveEventTime));
List<DeviceParamSpcPointVo> pointList = new ArrayList<DeviceParamSpcPointVo>();
for (BaseDeviceParamVal history : historyList) {
Double numericValue = parseNumeric(history.getParamValue());
if (numericValue == null) {
continue;
}
DeviceParamSpcPointVo pointVo = new DeviceParamSpcPointVo();
pointVo.setDeviceCode(history.getDeviceCode());
pointVo.setDeviceName(config != null && StringUtils.isNotBlank(config.getDeviceName()) ? config.getDeviceName() : history.getDeviceCode());
pointVo.setParamCode(history.getParamCode());
pointVo.setParamName(config != null && StringUtils.isNotBlank(config.getParamName()) ? config.getParamName() : history.getParamName());
pointVo.setUpperLimit(config != null ? config.getUpperLimit() : null);
pointVo.setLowerLimit(config != null ? config.getLowerLimit() : null);
pointVo.setCollectTime(resolveEventTime(history));
pointVo.setParamValue(numericValue);
pointList.add(pointVo);
}
return pointList;
}
private DeviceParamAnomalyVo buildAnomalyVo(BaseDeviceParam alertParam, List<NumericSample> sampleList) {
if (sampleList == null || sampleList.isEmpty()) {
return null;
}
sampleList.sort(Comparator.comparing(sample -> resolveEventTime(sample.history)));
AlertEvent currentEvent = null;
List<AlertEvent> eventList = new ArrayList<AlertEvent>();
for (NumericSample sample : sampleList) {
boolean upperAbnormal = alertParam.getUpperLimit() != null && sample.value > alertParam.getUpperLimit();
boolean lowerAbnormal = alertParam.getLowerLimit() != null && sample.value < alertParam.getLowerLimit();
if (!upperAbnormal && !lowerAbnormal) {
if (currentEvent != null) {
eventList.add(currentEvent);
currentEvent = null;
}
continue;
}
if (currentEvent == null) {
currentEvent = new AlertEvent();
currentEvent.startTime = resolveEventTime(sample.history);
currentEvent.maxValue = sample.value;
currentEvent.minValue = sample.value;
}
currentEvent.endTime = resolveEventTime(sample.history);
currentEvent.maxValue = Math.max(currentEvent.maxValue, sample.value);
currentEvent.minValue = Math.min(currentEvent.minValue, sample.value);
currentEvent.upperAbnormal = currentEvent.upperAbnormal || upperAbnormal;
currentEvent.lowerAbnormal = currentEvent.lowerAbnormal || lowerAbnormal;
}
if (currentEvent != null) {
eventList.add(currentEvent);
}
if (eventList.isEmpty()) {
return null;
}
DeviceParamAnomalyVo anomalyVo = new DeviceParamAnomalyVo();
anomalyVo.setDeviceCode(alertParam.getDeviceCode());
anomalyVo.setDeviceName(alertParam.getDeviceName());
anomalyVo.setParamCode(alertParam.getParamCode());
anomalyVo.setParamName(alertParam.getParamName());
anomalyVo.setAlertLevel(alertParam.getAlertLevel());
anomalyVo.setAlertLevelName(resolveAlertLevelName(alertParam.getAlertLevel()));
anomalyVo.setUpperLimit(alertParam.getUpperLimit());
anomalyVo.setLowerLimit(alertParam.getLowerLimit());
anomalyVo.setOverLimitCount(Long.valueOf(eventList.size()));
long upperCount = 0L;
long lowerCount = 0L;
double durationMinutes = 0D;
Double maxValue = null;
Double minValue = null;
Date firstTime = null;
Date lastTime = null;
for (AlertEvent event : eventList) {
if (event.upperAbnormal) {
upperCount++;
}
if (event.lowerAbnormal) {
lowerCount++;
}
durationMinutes += calcDurationMinutes(event.startTime, event.endTime, alertParam.getReadFrequency());
maxValue = maxValue == null ? event.maxValue : Math.max(maxValue, event.maxValue);
minValue = minValue == null ? event.minValue : Math.min(minValue, event.minValue);
if (firstTime == null || firstTime.after(event.startTime)) {
firstTime = event.startTime;
}
if (lastTime == null || lastTime.before(event.endTime)) {
lastTime = event.endTime;
}
}
anomalyVo.setUpperAnomalyCount(upperCount);
anomalyVo.setLowerAnomalyCount(lowerCount);
anomalyVo.setAbnormalDurationMinutes(scale(durationMinutes));
anomalyVo.setMaxValue(scale(maxValue));
anomalyVo.setMinValue(scale(minValue));
anomalyVo.setFirstAbnormalTime(firstTime);
anomalyVo.setLastAbnormalTime(lastTime);
return anomalyVo;
}
private void appendSwitchTraceRows(List<DeviceParamSwitchTraceVo> resultList, String deviceCode, List<BaseDeviceParam> switchParams,
List<BaseDeviceParam> alertParams, Date beginTime, Date endTime, Date observeEndTime,
int observeMinutes, String switchType) {
List<String> switchParamNames = extractSwitchParamNames(switchType, switchParams);
List<BaseDeviceParamVal> switchHistory = deviceParamReadFacade.listHistoryParamValuesByNames(
deviceCode, switchParamNames, beginTime, endTime, null);
if (switchHistory.isEmpty()) {
return;
}
switchHistory.sort(Comparator.comparing(this::resolveEventTime));
List<BaseDeviceParamVal> outputHistory = deviceParamReadFacade.listHistoryParamValues(
deviceCode, null, OUTPUT_PARAM_NAME, beginTime, observeEndTime, null);
outputHistory.sort(Comparator.comparing(this::resolveEventTime));
List<BaseDeviceParamVal> statusHistory = deviceParamReadFacade.listHistoryParamValuesByNames(
deviceCode, STATUS_PARAM_NAMES, beginTime, observeEndTime, null);
statusHistory.sort(Comparator.comparing(this::resolveEventTime));
List<BaseDeviceParamVal> alertHistory = deviceParamReadFacade.listHistoryParamValues(
deviceCode, null, null, beginTime, observeEndTime, null);
alertHistory.sort(Comparator.comparing(this::resolveEventTime));
Map<String, BaseDeviceParam> alertConfigMap = buildAlertConfigMap(alertParams);
Map<String, String> switchTypeMap = buildSwitchTypeMap(switchParams);
Map<String, BaseDeviceParamVal> previousValueMap = new HashMap<String, BaseDeviceParamVal>();
for (BaseDeviceParamVal history : switchHistory) {
String paramKey = buildParamKey(history.getParamCode(), history.getParamName());
BaseDeviceParamVal previous = previousValueMap.get(paramKey);
if (previous != null && !Objects.equals(normalizeValue(previous.getParamValue()), normalizeValue(history.getParamValue()))) {
DeviceParamSwitchTraceVo switchTraceVo = new DeviceParamSwitchTraceVo();
switchTraceVo.setDeviceCode(deviceCode);
switchTraceVo.setDeviceName(resolveDeviceName(switchParams, history));
switchTraceVo.setSwitchType(switchTypeMap.getOrDefault(paramKey, resolveSwitchType(history.getParamName())));
switchTraceVo.setParamCode(history.getParamCode());
switchTraceVo.setParamName(history.getParamName());
switchTraceVo.setChangeTime(resolveEventTime(history));
switchTraceVo.setBeforeValue(previous.getParamValue());
switchTraceVo.setAfterValue(history.getParamValue());
switchTraceVo.setObserveMinutes(observeMinutes);
Date windowEnd = addMinutes(resolveEventTime(history), observeMinutes);
switchTraceVo.setOutputIncrement(calcOutputIncrement(outputHistory, resolveEventTime(history), windowEnd));
switchTraceVo.setAbnormalCount(countAbnormalSamples(alertHistory, alertConfigMap, resolveEventTime(history), windowEnd));
switchTraceVo.setLatestStatus(resolveLatestStatus(statusHistory, resolveEventTime(history), windowEnd));
resultList.add(switchTraceVo);
}
previousValueMap.put(paramKey, history);
}
}
private double calcDurationMinutes(Date startTime, Date endTime, Long readFrequency) {
if (startTime == null) {
return 0D;
}
if (endTime != null && endTime.after(startTime)) {
return (endTime.getTime() - startTime.getTime()) / 60000D;
}
long frequency = readFrequency == null || readFrequency <= 0 ? 60000L : readFrequency.longValue();
return frequency / 60000D;
}
private Map<String, BaseDeviceParam> buildAlertConfigMap(List<BaseDeviceParam> alertParams) {
Map<String, BaseDeviceParam> resultMap = new HashMap<String, BaseDeviceParam>();
if (alertParams == null) {
return resultMap;
}
for (BaseDeviceParam alertParam : alertParams) {
resultMap.put(buildParamKey(alertParam.getParamCode(), alertParam.getParamName()), alertParam);
}
return resultMap;
}
private Map<String, String> buildSwitchTypeMap(List<BaseDeviceParam> switchParams) {
Map<String, String> resultMap = new HashMap<String, String>();
for (BaseDeviceParam switchParam : switchParams) {
resultMap.put(buildParamKey(switchParam.getParamCode(), switchParam.getParamName()), resolveSwitchType(switchParam.getParamName()));
}
return resultMap;
}
private List<String> extractSwitchParamNames(String switchType, List<BaseDeviceParam> switchParams) {
List<String> resultList = new ArrayList<String>();
for (BaseDeviceParam switchParam : switchParams) {
if (!resultList.contains(switchParam.getParamName())) {
resultList.add(switchParam.getParamName());
}
}
if (!resultList.isEmpty()) {
return resultList;
}
return resolveSwitchParamNames(switchType);
}
private List<String> resolveSwitchParamNames(String switchType) {
if ("MOLD".equalsIgnoreCase(switchType)) {
return Collections.singletonList("机台状态-模具数据名称");
}
if ("MATERIAL".equalsIgnoreCase(switchType)) {
return Collections.singletonList("机台状态-物料描述");
}
if ("PRODUCT".equalsIgnoreCase(switchType)) {
return Arrays.asList("机台状态-产品描述", "机台状态-产品描述1");
}
return SWITCH_PARAM_NAMES;
}
private Double calcOutputIncrement(List<BaseDeviceParamVal> outputHistory, Date beginTime, Date endTime) {
Double minValue = null;
Double maxValue = null;
for (BaseDeviceParamVal history : outputHistory) {
Date eventTime = resolveEventTime(history);
if (!between(eventTime, beginTime, endTime)) {
continue;
}
Double numericValue = parseNumeric(history.getParamValue());
if (numericValue == null) {
continue;
}
minValue = minValue == null ? numericValue : Math.min(minValue, numericValue);
maxValue = maxValue == null ? numericValue : Math.max(maxValue, numericValue);
}
if (minValue == null || maxValue == null) {
return 0D;
}
return scale(maxValue - minValue);
}
private Long countAbnormalSamples(List<BaseDeviceParamVal> alertHistory, Map<String, BaseDeviceParam> alertConfigMap,
Date beginTime, Date endTime) {
long count = 0L;
for (BaseDeviceParamVal history : alertHistory) {
Date eventTime = resolveEventTime(history);
if (!between(eventTime, beginTime, endTime)) {
continue;
}
BaseDeviceParam config = alertConfigMap.get(buildParamKey(history.getParamCode(), history.getParamName()));
if (config == null) {
continue;
}
Double numericValue = parseNumeric(history.getParamValue());
if (numericValue == null) {
continue;
}
if ((config.getUpperLimit() != null && numericValue > config.getUpperLimit())
|| (config.getLowerLimit() != null && numericValue < config.getLowerLimit())) {
count++;
}
}
return Long.valueOf(count);
}
private String resolveLatestStatus(List<BaseDeviceParamVal> statusHistory, Date beginTime, Date endTime) {
BaseDeviceParamVal latestStatus = null;
for (BaseDeviceParamVal history : statusHistory) {
Date eventTime = resolveEventTime(history);
if (!between(eventTime, beginTime, endTime)) {
continue;
}
if (!"TRUE".equalsIgnoreCase(normalizeValue(history.getParamValue()))) {
continue;
}
if (latestStatus == null || resolveEventTime(latestStatus).before(eventTime)) {
latestStatus = history;
}
}
if (latestStatus == null) {
return "未知";
}
return mapStatusName(latestStatus.getParamName());
}
private String mapStatusName(String paramName) {
if ("机台状态-三色灯机器运行".equals(paramName)) {
return "运行";
}
if ("机台状态-三色灯机器暂停".equals(paramName)) {
return "停机";
}
if ("机台状态-三色灯机器待机".equals(paramName)) {
return "待机";
}
if ("机台状态-三色灯机器报警".equals(paramName)) {
return "报警";
}
return "未知";
}
private String resolveSwitchType(String paramName) {
if ("机台状态-模具数据名称".equals(paramName)) {
return "模具";
}
if ("机台状态-物料描述".equals(paramName)) {
return "物料";
}
return "产品";
}
private String resolveDeviceName(List<BaseDeviceParam> switchParams, BaseDeviceParamVal history) {
if (switchParams != null && !switchParams.isEmpty() && StringUtils.isNotBlank(switchParams.get(0).getDeviceName())) {
return switchParams.get(0).getDeviceName();
}
return history.getDeviceCode();
}
private boolean isNumericParam(BaseDeviceParam param) {
if (param == null || StringUtils.isBlank(param.getParamType())) {
return false;
}
return Arrays.asList("1", "2", "3", "4", "5").contains(param.getParamType());
}
private String resolveAlertLevelName(String alertLevel) {
if ("1".equals(alertLevel)) {
return "一般";
}
if ("2".equals(alertLevel)) {
return "重要";
}
if ("3".equals(alertLevel)) {
return "紧急";
}
return "未配置";
}
private String buildParamKey(String paramCode, String paramName) {
if (StringUtils.isNotBlank(paramCode)) {
return "CODE:" + paramCode;
}
return "NAME:" + trimToNull(paramName);
}
private Double parseNumeric(String value) {
if (StringUtils.isBlank(value)) {
return null;
}
try {
return new BigDecimal(value.trim()).doubleValue();
} catch (NumberFormatException ex) {
return null;
}
}
private Date parseDate(String dateTime) {
if (StringUtils.isBlank(dateTime)) {
return null;
}
LocalDateTime parsed = LocalDateTime.parse(dateTime.trim(), DATE_TIME_FORMATTER);
return Date.from(parsed.atZone(ZoneId.systemDefault()).toInstant());
}
private String trimToNull(String text) {
return StringUtils.isBlank(text) ? null : text.trim();
}
private Date resolveEventTime(BaseDeviceParamVal history) {
if (history.getRecordTime() != null) {
return history.getRecordTime();
}
if (history.getCollectTime() != null) {
return history.getCollectTime();
}
return new Date(0L);
}
private Date addMinutes(Date baseTime, int minutes) {
return new Date(baseTime.getTime() + minutes * 60L * 1000L);
}
private boolean between(Date currentTime, Date beginTime, Date endTime) {
return currentTime != null
&& !currentTime.before(beginTime)
&& !currentTime.after(endTime);
}
private String normalizeValue(String value) {
return value == null ? null : value.trim();
}
private static final class NumericSample {
private final BaseDeviceParamVal history;
private final double value;
private NumericSample(BaseDeviceParamVal history, double value) {
this.history = history;
this.value = value;
}
}
private static final class AlertEvent {
private Date startTime;
private Date endTime;
private double maxValue;
private double minValue;
private boolean upperAbnormal;
private boolean lowerAbnormal;
}
}

@ -35,283 +35,4 @@
ORDER BY p.param_name, p.param_code
</select>
<select id="selectParamAnomalyList" parameterType="com.aucma.report.domain.DeviceParamReportQuery"
resultType="com.aucma.report.domain.vo.DeviceParamAnomalyVo">
WITH source_data AS (
SELECT
v.device_code,
d.device_name,
v.param_code,
p.param_name,
p.alert_level,
p.upper_limit,
p.lower_limit,
p.read_frequency,
NVL(v.record_time, v.collect_time) AS collect_time,
TO_NUMBER(v.param_value) AS param_value_num
FROM base_device_param_val v
INNER JOIN base_deviceparam p
ON p.device_code = v.device_code
AND p.param_code = v.param_code
LEFT JOIN base_deviceledger d
ON d.device_code = v.device_code
WHERE NVL(p.is_flag, 0) = 1
AND p.alert_enabled = '1'
AND (p.upper_limit IS NOT NULL OR p.lower_limit IS NOT NULL)
AND REGEXP_LIKE(v.param_value, '^-?[0-9]+(\.[0-9]+)?$')
<if test="deviceCode != null and deviceCode != ''">
AND v.device_code = #{deviceCode}
</if>
<if test="paramCode != null and paramCode != ''">
AND v.param_code = #{paramCode}
</if>
<if test="startTime != null and startTime != ''">
AND NVL(v.record_time, v.collect_time) &gt;= TO_DATE(#{startTime}, 'YYYY-MM-DD HH24:MI:SS')
</if>
<if test="endTime != null and endTime != ''">
AND NVL(v.record_time, v.collect_time) &lt;= TO_DATE(#{endTime}, 'YYYY-MM-DD HH24:MI:SS')
</if>
),
tag_data AS (
SELECT
source_data.*,
CASE
WHEN upper_limit IS NOT NULL AND param_value_num &gt; upper_limit THEN 1
WHEN lower_limit IS NOT NULL AND param_value_num &lt; lower_limit THEN 1
ELSE 0
END AS abnormal_flag
FROM source_data
),
seq_data AS (
SELECT
tag_data.*,
CASE
WHEN abnormal_flag = 1
AND NVL(LAG(abnormal_flag) OVER (PARTITION BY device_code, param_code ORDER BY collect_time), 0) = 0
THEN 1
ELSE 0
END AS group_start_flag
FROM tag_data
),
abnormal_data AS (
SELECT
seq_data.*,
SUM(group_start_flag) OVER (
PARTITION BY device_code, param_code
ORDER BY collect_time
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS group_no
FROM seq_data
WHERE abnormal_flag = 1
),
event_data AS (
/* 这里先按连续异常段分组,后面再汇总成设备+参数报表,避免一次超限被重复算多次。 */
SELECT
device_code,
device_name,
param_code,
param_name,
alert_level,
upper_limit,
lower_limit,
MAX(read_frequency) AS read_frequency,
group_no,
MIN(collect_time) AS event_start_time,
MAX(collect_time) AS event_end_time,
MAX(param_value_num) AS event_max_value,
MIN(param_value_num) AS event_min_value,
MAX(CASE WHEN upper_limit IS NOT NULL AND param_value_num &gt; upper_limit THEN 1 ELSE 0 END) AS has_upper_abnormal,
MAX(CASE WHEN lower_limit IS NOT NULL AND param_value_num &lt; lower_limit THEN 1 ELSE 0 END) AS has_lower_abnormal
FROM abnormal_data
GROUP BY device_code, device_name, param_code, param_name, alert_level, upper_limit, lower_limit, group_no
)
SELECT
device_code AS deviceCode,
device_name AS deviceName,
param_code AS paramCode,
param_name AS paramName,
alert_level AS alertLevel,
CASE alert_level
WHEN '1' THEN '一般'
WHEN '2' THEN '重要'
WHEN '3' THEN '紧急'
ELSE '未配置'
END AS alertLevelName,
upper_limit AS upperLimit,
lower_limit AS lowerLimit,
COUNT(1) AS overLimitCount,
SUM(has_upper_abnormal) AS upperAnomalyCount,
SUM(has_lower_abnormal) AS lowerAnomalyCount,
ROUND(SUM(
CASE
WHEN event_end_time &gt; event_start_time THEN (event_end_time - event_start_time) * 24 * 60
ELSE NVL(read_frequency, 60000) / 60000
END
), 2) AS abnormalDurationMinutes,
MAX(event_max_value) AS maxValue,
MIN(event_min_value) AS minValue,
MIN(event_start_time) AS firstAbnormalTime,
MAX(event_end_time) AS lastAbnormalTime
FROM event_data
GROUP BY
device_code, device_name, param_code, param_name, alert_level, upper_limit, lower_limit
ORDER BY overLimitCount DESC, abnormalDurationMinutes DESC, lastAbnormalTime DESC
</select>
<select id="selectSpcPoints" parameterType="com.aucma.report.domain.DeviceParamReportQuery"
resultType="com.aucma.report.domain.vo.DeviceParamSpcPointVo">
SELECT *
FROM (
SELECT
v.device_code AS deviceCode,
d.device_name AS deviceName,
v.param_code AS paramCode,
p.param_name AS paramName,
p.upper_limit AS upperLimit,
p.lower_limit AS lowerLimit,
NVL(v.record_time, v.collect_time) AS collectTime,
TO_NUMBER(v.param_value) AS paramValue
FROM base_device_param_val v
LEFT JOIN base_deviceparam p
ON p.device_code = v.device_code
AND p.param_code = v.param_code
LEFT JOIN base_deviceledger d
ON d.device_code = v.device_code
WHERE REGEXP_LIKE(v.param_value, '^-?[0-9]+(\.[0-9]+)?$')
AND v.device_code = #{deviceCode}
AND v.param_code = #{paramCode}
<if test="startTime != null and startTime != ''">
AND NVL(v.record_time, v.collect_time) &gt;= TO_DATE(#{startTime}, 'YYYY-MM-DD HH24:MI:SS')
</if>
<if test="endTime != null and endTime != ''">
AND NVL(v.record_time, v.collect_time) &lt;= TO_DATE(#{endTime}, 'YYYY-MM-DD HH24:MI:SS')
</if>
ORDER BY NVL(v.record_time, v.collect_time) ASC
)
WHERE ROWNUM &lt;= 1500
</select>
<select id="selectSwitchTraceList" parameterType="com.aucma.report.domain.DeviceParamReportQuery"
resultType="com.aucma.report.domain.vo.DeviceParamSwitchTraceVo">
WITH source_data AS (
SELECT
v.device_code,
d.device_name,
v.param_code,
v.param_name,
v.param_value,
NVL(v.record_time, v.collect_time) AS collect_time,
LAG(v.param_value) OVER (
PARTITION BY v.device_code, v.param_code
ORDER BY NVL(v.record_time, v.collect_time)
) AS prev_value
FROM base_device_param_val v
LEFT JOIN base_deviceledger d
ON d.device_code = v.device_code
WHERE 1 = 1
<if test="deviceCode != null and deviceCode != ''">
AND v.device_code = #{deviceCode}
</if>
<if test="startTime != null and startTime != ''">
AND NVL(v.record_time, v.collect_time) &gt;= TO_DATE(#{startTime}, 'YYYY-MM-DD HH24:MI:SS')
</if>
<if test="endTime != null and endTime != ''">
AND NVL(v.record_time, v.collect_time) &lt;= TO_DATE(#{endTime}, 'YYYY-MM-DD HH24:MI:SS')
</if>
<choose>
<when test="switchType == 'MOLD'">
AND v.param_name = '机台状态-模具数据名称'
</when>
<when test="switchType == 'MATERIAL'">
AND v.param_name = '机台状态-物料描述'
</when>
<when test="switchType == 'PRODUCT'">
AND (v.param_name = '机台状态-产品描述' OR v.param_name = '机台状态-产品描述1')
</when>
<otherwise>
AND (
v.param_name = '机台状态-模具数据名称'
OR v.param_name = '机台状态-物料描述'
OR v.param_name = '机台状态-产品描述'
OR v.param_name = '机台状态-产品描述1'
)
</otherwise>
</choose>
),
switch_events AS (
SELECT
device_code,
device_name,
param_code,
param_name,
collect_time,
prev_value,
param_value,
CASE
WHEN param_name = '机台状态-模具数据名称' THEN '模具'
WHEN param_name = '机台状态-物料描述' THEN '物料'
ELSE '产品'
END AS switch_type
FROM source_data
WHERE prev_value IS NOT NULL
AND NVL(prev_value, '#') &lt;&gt; NVL(param_value, '#')
)
SELECT
e.device_code AS deviceCode,
e.device_name AS deviceName,
e.switch_type AS switchType,
e.param_code AS paramCode,
e.param_name AS paramName,
e.collect_time AS changeTime,
e.prev_value AS beforeValue,
e.param_value AS afterValue,
#{observeMinutes} AS observeMinutes,
NVL((
SELECT ROUND(MAX(TO_NUMBER(p.param_value)) - MIN(TO_NUMBER(p.param_value)), 2)
FROM base_device_param_val p
WHERE p.device_code = e.device_code
AND p.param_name = '机台状态-实际产出数量'
AND REGEXP_LIKE(p.param_value, '^-?[0-9]+(\.[0-9]+)?$')
AND NVL(p.record_time, p.collect_time) BETWEEN e.collect_time AND e.collect_time + (#{observeMinutes} / 1440)
), 0) AS outputIncrement,
NVL((
SELECT COUNT(1)
FROM base_device_param_val av
INNER JOIN base_deviceparam ap
ON ap.device_code = av.device_code
AND ap.param_code = av.param_code
WHERE av.device_code = e.device_code
AND NVL(ap.is_flag, 0) = 1
AND ap.alert_enabled = '1'
AND REGEXP_LIKE(av.param_value, '^-?[0-9]+(\.[0-9]+)?$')
AND NVL(av.record_time, av.collect_time) BETWEEN e.collect_time AND e.collect_time + (#{observeMinutes} / 1440)
AND (
(ap.upper_limit IS NOT NULL AND TO_NUMBER(av.param_value) &gt; ap.upper_limit)
OR (ap.lower_limit IS NOT NULL AND TO_NUMBER(av.param_value) &lt; ap.lower_limit)
)
), 0) AS abnormalCount,
NVL((
SELECT device_status
FROM (
SELECT
CASE s.param_name
WHEN '机台状态-三色灯机器运行' THEN '运行'
WHEN '机台状态-三色灯机器暂停' THEN '停机'
WHEN '机台状态-三色灯机器待机' THEN '待机'
WHEN '机台状态-三色灯机器报警' THEN '报警'
ELSE '未知'
END AS device_status
FROM base_device_param_val s
WHERE s.device_code = e.device_code
AND s.param_name IN ('机台状态-三色灯机器运行', '机台状态-三色灯机器暂停', '机台状态-三色灯机器待机', '机台状态-三色灯机器报警')
AND UPPER(s.param_value) = 'TRUE'
AND NVL(s.record_time, s.collect_time) BETWEEN e.collect_time AND e.collect_time + (#{observeMinutes} / 1440)
ORDER BY NVL(s.record_time, s.collect_time) DESC
)
WHERE ROWNUM = 1
), '未知') AS latestStatus
FROM switch_events e
ORDER BY e.collect_time DESC
</select>
</mapper>

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save