二级系统联合寻找数据
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
package com.klp.pocket.acid.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 酸轧线理论节拍(统计口径)视图对象
|
||||
* 通过历史“优良日”统计得到,替代不稳定的回归结果。
|
||||
*
|
||||
* @author klp
|
||||
* @date 2026-02-03
|
||||
*/
|
||||
@Data
|
||||
public class AcidOeeIdealCycleVo {
|
||||
|
||||
/** 产线ID(固定为 SY) */
|
||||
private String lineId;
|
||||
|
||||
/** 产线名称(酸轧线) */
|
||||
private String lineName;
|
||||
|
||||
/** 本次计算使用的数据区间(yyyy-MM-dd) */
|
||||
private String startDate;
|
||||
private String endDate;
|
||||
|
||||
/** 理论节拍(min/吨)- 固定值0.47 */
|
||||
private BigDecimal idealCycleTimeMinPerTon;
|
||||
|
||||
/** 中位数理论节拍(min/吨)- 从卷级节拍计算的中位数 */
|
||||
private BigDecimal medianCycleTimeMinPerTon;
|
||||
|
||||
/** 参与统计的"优良日"天数 */
|
||||
private Integer sampleDays;
|
||||
|
||||
/** 统计口径:吨数下限(吨) */
|
||||
private BigDecimal minWeightTon;
|
||||
|
||||
/** 统计口径:停机占比上限(0~1) */
|
||||
private BigDecimal maxDowntimeRate;
|
||||
|
||||
/** 日粒度对比点:理论耗时 vs 实际运转时间 */
|
||||
private List<DailyComparePointVo> dailyComparePoints;
|
||||
|
||||
@Data
|
||||
public static class DailyComparePointVo {
|
||||
private String statDate;
|
||||
private Long actualRunTimeMin;
|
||||
private BigDecimal theoreticalTimeMin;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
package com.klp.pocket.acid.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 酸轧线OEE回归数据视图对象
|
||||
* 用于理论节拍计算和前端散点图展示
|
||||
*
|
||||
* @author klp
|
||||
* @date 2026-01-30
|
||||
*/
|
||||
@Data
|
||||
public class AcidOeeRegressionVo {
|
||||
|
||||
/** 产线ID(固定为 SY) */
|
||||
private String lineId;
|
||||
|
||||
/** 产线名称(酸轧线) */
|
||||
private String lineName;
|
||||
|
||||
/** 回归斜率:分钟/吨(核心值,可作为理论节拍) */
|
||||
private BigDecimal slopeMinPerTon;
|
||||
|
||||
/** 回归斜率:分钟/卷(核心值,可作为理论节拍) */
|
||||
private BigDecimal slopeMinPerCoil;
|
||||
|
||||
/** 截距(分钟) */
|
||||
private BigDecimal interceptMin;
|
||||
|
||||
/** 拟合优度(R²) */
|
||||
private BigDecimal r2;
|
||||
|
||||
/** 参与回归样本数 */
|
||||
private Integer sampleCount;
|
||||
|
||||
/** 回归数据开始时间 */
|
||||
private String startTime;
|
||||
|
||||
/** 回归数据结束时间 */
|
||||
private String endTime;
|
||||
|
||||
/** 散点列表 */
|
||||
private List<RegressionPointVo> points;
|
||||
|
||||
/** 拟合线两个端点(前端可直接画线) */
|
||||
private List<RegressionLinePointVo> linePoints;
|
||||
|
||||
/**
|
||||
* 散点数据
|
||||
*/
|
||||
@Data
|
||||
public static class RegressionPointVo {
|
||||
/** 重量(吨,X轴) */
|
||||
private BigDecimal weightTon;
|
||||
/** 卷数(X轴) */
|
||||
private Long coilCount;
|
||||
/** 时长(分钟,Y轴) */
|
||||
private Long durationMin;
|
||||
/** 关联的actionId(可选) */
|
||||
private String actionId;
|
||||
/** 创建时间 */
|
||||
private String createTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拟合线端点
|
||||
*/
|
||||
@Data
|
||||
public static class RegressionLinePointVo {
|
||||
/** 重量(吨,X轴) */
|
||||
private BigDecimal weightTon;
|
||||
/** 卷数(X轴) */
|
||||
private Long coilCount;
|
||||
/** 时长(分钟,Y轴) */
|
||||
private BigDecimal durationMin;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.klp.pocket.acid.mapper;
|
||||
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeDailySummaryVo;
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeRegressionVo;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
@@ -27,17 +26,6 @@ public interface AcidOeeMapper {
|
||||
List<AcidOeeDailySummaryVo> selectDailySummary(@Param("startDate") String startDate,
|
||||
@Param("endDate") String endDate);
|
||||
|
||||
/**
|
||||
* 查询回归数据散点(用于理论节拍计算)
|
||||
* 返回:重量(吨)、卷数、时长(分钟)等
|
||||
*
|
||||
* @param startDate 开始日期(yyyy-MM-dd,可选)
|
||||
* @param endDate 结束日期(yyyy-MM-dd,可选)
|
||||
* @return 散点列表
|
||||
*/
|
||||
List<AcidOeeRegressionVo.RegressionPointVo> selectRegressionPoints(@Param("startDate") String startDate,
|
||||
@Param("endDate") String endDate);
|
||||
|
||||
/**
|
||||
* 查询每日的钢卷号和重量(用于良品/次品判定)
|
||||
*
|
||||
@@ -48,6 +36,16 @@ public interface AcidOeeMapper {
|
||||
List<CoilInfoByDate> selectCoilInfoByDate(@Param("startDate") String startDate,
|
||||
@Param("endDate") String endDate);
|
||||
|
||||
/**
|
||||
* 查询卷级生产节拍(min/吨),用于理论节拍计算。
|
||||
*
|
||||
* @param startDate 开始日期(yyyy-MM-dd)
|
||||
* @param endDate 结束日期(yyyy-MM-dd)
|
||||
* @return 每卷的生产节拍列表(min/吨)
|
||||
*/
|
||||
List<java.math.BigDecimal> selectCoilCycleMinPerTon(@Param("startDate") String startDate,
|
||||
@Param("endDate") String endDate);
|
||||
|
||||
/**
|
||||
* 卷号信息内部类(用于Mapper返回)
|
||||
*/
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.klp.pocket.acid.service;
|
||||
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeDailySummaryVo;
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeIdealCycleVo;
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeLoss7Vo;
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeRegressionVo;
|
||||
import com.klp.pocket.acid.domain.vo.Klptcm1ProStoppageVo;
|
||||
|
||||
import java.util.List;
|
||||
@@ -34,16 +34,6 @@ public interface IAcidOeeService {
|
||||
*/
|
||||
List<Klptcm1ProStoppageVo> getStoppageEvents(String startDate, String endDate);
|
||||
|
||||
/**
|
||||
* 查询理论节拍回归数据(吨和卷两个维度)
|
||||
* 用于性能稼动率计算和前端散点图展示
|
||||
*
|
||||
* @param startDate 开始日期(yyyy-MM-dd,可选,默认近6个月)
|
||||
* @param endDate 结束日期(yyyy-MM-dd,可选)
|
||||
* @return 回归数据(包含斜率、截距、散点等)
|
||||
*/
|
||||
AcidOeeRegressionVo getRegressionData(String startDate, String endDate);
|
||||
|
||||
/**
|
||||
* 查询7大损失汇总(按日期范围)
|
||||
*
|
||||
@@ -52,5 +42,14 @@ public interface IAcidOeeService {
|
||||
* @return 7大损失汇总列表
|
||||
*/
|
||||
List<AcidOeeLoss7Vo> getLoss7Summary(String startDate, String endDate);
|
||||
|
||||
/**
|
||||
* 查询理论节拍(统计口径:历史优良日中位数)
|
||||
*
|
||||
* @param startDate 开始日期(yyyy-MM-dd)
|
||||
* @param endDate 结束日期(yyyy-MM-dd)
|
||||
* @return 理论节拍与对比数据
|
||||
*/
|
||||
AcidOeeIdealCycleVo getIdealCycle(String startDate, String endDate);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ package com.klp.pocket.acid.service.impl;
|
||||
import com.baomidou.dynamic.datasource.annotation.DS;
|
||||
import com.klp.common.utils.StringUtils;
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeDailySummaryVo;
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeIdealCycleVo;
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeLoss7Vo;
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeRegressionVo;
|
||||
import com.klp.pocket.acid.domain.vo.Klptcm1ProStoppageVo;
|
||||
import com.klp.pocket.acid.domain.bo.Klptcm1ProStoppageBo;
|
||||
import com.klp.pocket.acid.mapper.AcidOeeMapper;
|
||||
@@ -19,7 +19,7 @@ import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Calendar;
|
||||
|
||||
/**
|
||||
* 酸轧线OEE Service实现类
|
||||
@@ -35,7 +35,8 @@ public class AcidOeeServiceImpl implements IAcidOeeService {
|
||||
|
||||
/** 酸轧成品库库区ID */
|
||||
private static final Long ACID_FINISHED_WAREHOUSE_ID = 1988150099140866050L;
|
||||
|
||||
/** 固定理论节拍(min/吨) */
|
||||
private static final BigDecimal FIXED_IDEAL_CYCLE = BigDecimal.valueOf(0.47);
|
||||
private final AcidOeeMapper acidOeeMapper;
|
||||
private final IKlptcm1ProStoppageService stoppageService;
|
||||
private final ICoilQualityJudgeService coilQualityJudgeService;
|
||||
@@ -55,7 +56,10 @@ public class AcidOeeServiceImpl implements IAcidOeeService {
|
||||
// 3. 查询产量明细,用于良品/次品判定
|
||||
Map<String, List<CoilInfo>> coilInfoByDate = getCoilNosByDate(startDate, endDate);
|
||||
|
||||
// 4. 填充每个日汇总的完整数据
|
||||
// 4. 理论节拍:使用固定值0.47
|
||||
BigDecimal idealCycleTon = FIXED_IDEAL_CYCLE;
|
||||
|
||||
// 5. 填充每个日汇总的完整数据
|
||||
for (AcidOeeDailySummaryVo summary : summaries) {
|
||||
String statDate = summary.getStatDate();
|
||||
summary.setLineId("SY");
|
||||
@@ -70,6 +74,11 @@ public class AcidOeeServiceImpl implements IAcidOeeService {
|
||||
Long runTime = Math.max(0, loadingTime - downtime);
|
||||
summary.setRunTimeMin(runTime);
|
||||
|
||||
// 理论节拍:若尚未填充,则统一使用“优良日统计”得到的节拍
|
||||
if (summary.getIdealCycleTimeMinPerTon() == null && idealCycleTon != null) {
|
||||
summary.setIdealCycleTimeMinPerTon(idealCycleTon);
|
||||
}
|
||||
|
||||
// 良品/次品判定(通过WMS)
|
||||
if (coilInfoByDate.containsKey(statDate)) {
|
||||
List<CoilInfo> coilInfos = coilInfoByDate.get(statDate);
|
||||
@@ -101,57 +110,84 @@ public class AcidOeeServiceImpl implements IAcidOeeService {
|
||||
return stoppageService.queryList(bo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询停机事件(可选:是否包含短停机 <5min)
|
||||
*/
|
||||
private List<Klptcm1ProStoppageVo> getStoppageEvents(String startDate, String endDate, boolean includeShortDuration) {
|
||||
Klptcm1ProStoppageBo bo = new Klptcm1ProStoppageBo();
|
||||
bo.setStartDate(parseDate(startDate));
|
||||
bo.setEndDate(parseDate(endDate));
|
||||
if (includeShortDuration) {
|
||||
// BaseEntity.params 用于透传查询开关
|
||||
bo.getParams().put("includeShortDuration", true);
|
||||
}
|
||||
return stoppageService.queryList(bo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AcidOeeRegressionVo getRegressionData(String startDate, String endDate) {
|
||||
// 1. 查询散点数据
|
||||
List<AcidOeeRegressionVo.RegressionPointVo> points = acidOeeMapper.selectRegressionPoints(startDate, endDate);
|
||||
public AcidOeeIdealCycleVo getIdealCycle(String startDate, String endDate) {
|
||||
// 1) 取基础日汇总(产量、负荷时间等)
|
||||
List<AcidOeeDailySummaryVo> daily = acidOeeMapper.selectDailySummary(startDate, endDate);
|
||||
AcidOeeIdealCycleVo rsp = new AcidOeeIdealCycleVo();
|
||||
rsp.setLineId("SY");
|
||||
rsp.setLineName("酸轧线");
|
||||
rsp.setStartDate(startDate);
|
||||
rsp.setEndDate(endDate);
|
||||
// 这里的 minWeightTon / maxDowntimeRate 字段暂不使用,可保留为前端说明字段
|
||||
|
||||
AcidOeeRegressionVo result = new AcidOeeRegressionVo();
|
||||
result.setLineId("SY");
|
||||
result.setLineName("酸轧线");
|
||||
result.setStartTime(startDate);
|
||||
result.setEndTime(endDate);
|
||||
result.setPoints(points);
|
||||
result.setSampleCount(points != null ? points.size() : 0);
|
||||
|
||||
if (points == null || points.isEmpty()) {
|
||||
// 没有数据时返回空结果
|
||||
return result;
|
||||
if (daily == null || daily.isEmpty()) {
|
||||
rsp.setIdealCycleTimeMinPerTon(null);
|
||||
rsp.setSampleDays(0);
|
||||
rsp.setDailyComparePoints(Collections.emptyList());
|
||||
return rsp;
|
||||
}
|
||||
|
||||
// 2. 计算回归(吨维度)
|
||||
RegressionResult tonResult = calculateRegression(
|
||||
points.stream().map(AcidOeeRegressionVo.RegressionPointVo::getWeightTon).filter(Objects::nonNull).collect(Collectors.toList()),
|
||||
points.stream().map(AcidOeeRegressionVo.RegressionPointVo::getDurationMin).filter(Objects::nonNull).collect(Collectors.toList())
|
||||
);
|
||||
if (tonResult != null) {
|
||||
result.setSlopeMinPerTon(tonResult.slope);
|
||||
result.setInterceptMin(tonResult.intercept);
|
||||
result.setR2(tonResult.r2);
|
||||
// 2) 聚合停机,补齐 runTime
|
||||
Map<String, Long> downtimeByDate = aggregateDowntimeByDate(startDate, endDate);
|
||||
for (AcidOeeDailySummaryVo d : daily) {
|
||||
Long downtime = downtimeByDate.getOrDefault(d.getStatDate(), 0L);
|
||||
d.setDowntimeMin(downtime);
|
||||
Long loading = d.getLoadingTimeMin() != null ? d.getLoadingTimeMin() : 0L;
|
||||
d.setRunTimeMin(Math.max(0, loading - downtime));
|
||||
}
|
||||
|
||||
// 3. 计算回归(卷维度)
|
||||
RegressionResult coilResult = calculateRegression(
|
||||
points.stream().map(p -> p.getCoilCount() != null ? BigDecimal.valueOf(p.getCoilCount()) : null).filter(Objects::nonNull).collect(Collectors.toList()),
|
||||
points.stream().map(AcidOeeRegressionVo.RegressionPointVo::getDurationMin).filter(Objects::nonNull).collect(Collectors.toList())
|
||||
);
|
||||
if (coilResult != null) {
|
||||
result.setSlopeMinPerCoil(coilResult.slope);
|
||||
}
|
||||
// 3) 卷级节拍 = (END_DATE - START_DATE)/出口重量,计算中位数(用于展示,不用于OEE计算)
|
||||
List<BigDecimal> coilCycles = acidOeeMapper.selectCoilCycleMinPerTon(startDate, endDate);
|
||||
coilCycles.removeIf(c -> c == null || c.compareTo(BigDecimal.ZERO) <= 0);
|
||||
coilCycles.sort(BigDecimal::compareTo);
|
||||
BigDecimal medianCycle = median(coilCycles);
|
||||
|
||||
// 理论节拍使用固定值0.47(用于OEE计算)
|
||||
rsp.setIdealCycleTimeMinPerTon(FIXED_IDEAL_CYCLE);
|
||||
// 中位数理论节拍(用于展示)
|
||||
rsp.setMedianCycleTimeMinPerTon(medianCycle);
|
||||
// 样本天数:当前查询区间内有产量的自然日数量(与传入的日期范围一一对应)
|
||||
rsp.setSampleDays(daily.size());
|
||||
|
||||
// 4. 生成拟合线端点(用于前端画线)
|
||||
if (tonResult != null && !points.isEmpty()) {
|
||||
List<AcidOeeRegressionVo.RegressionLinePointVo> linePoints = generateLinePoints(points, tonResult);
|
||||
result.setLinePoints(linePoints);
|
||||
// 4) 日粒度对比数据:理论耗时 vs 实际运转时间(用于前端展示"有效性")
|
||||
// 使用固定值0.47计算理论耗时
|
||||
List<AcidOeeIdealCycleVo.DailyComparePointVo> compare = new ArrayList<>();
|
||||
if (FIXED_IDEAL_CYCLE != null) {
|
||||
for (AcidOeeDailySummaryVo d : daily) {
|
||||
BigDecimal ton = d.getTotalOutputTon();
|
||||
Long run = d.getRunTimeMin();
|
||||
if (ton == null || run == null) continue;
|
||||
AcidOeeIdealCycleVo.DailyComparePointVo p = new AcidOeeIdealCycleVo.DailyComparePointVo();
|
||||
p.setStatDate(d.getStatDate());
|
||||
p.setActualRunTimeMin(run);
|
||||
p.setTheoreticalTimeMin(FIXED_IDEAL_CYCLE.multiply(ton));
|
||||
compare.add(p);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
rsp.setDailyComparePoints(compare);
|
||||
return rsp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AcidOeeLoss7Vo> getLoss7Summary(String startDate, String endDate) {
|
||||
// 1. 查询停机事件(含 stopType、duration 等)
|
||||
List<Klptcm1ProStoppageVo> events = getStoppageEvents(startDate, endDate);
|
||||
// 损失统计也建议包含短停机,避免损失时间与停机总时间口径不一致
|
||||
List<Klptcm1ProStoppageVo> events = getStoppageEvents(startDate, endDate, true);
|
||||
if (events == null || events.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@@ -162,9 +198,9 @@ public class AcidOeeServiceImpl implements IAcidOeeService {
|
||||
|
||||
for (Klptcm1ProStoppageVo event : events) {
|
||||
String stopType = event.getStopType();
|
||||
// stopType 为空时归入“未分类”,避免因为未录入原因导致损失时间被漏算
|
||||
if (StringUtils.isBlank(stopType)) {
|
||||
// 没有类型的记录暂时忽略
|
||||
continue;
|
||||
stopType = "未分类";
|
||||
}
|
||||
Long durationSec = event.getDuration();
|
||||
if (durationSec == null || durationSec <= 0) {
|
||||
@@ -225,18 +261,64 @@ public class AcidOeeServiceImpl implements IAcidOeeService {
|
||||
|
||||
/**
|
||||
* 按日期聚合停机时间
|
||||
* 修复:如果停机事件跨天,需要按实际跨天的分钟数分配到对应的日期
|
||||
*/
|
||||
private Map<String, Long> aggregateDowntimeByDate(String startDate, String endDate) {
|
||||
List<Klptcm1ProStoppageVo> events = getStoppageEvents(startDate, endDate);
|
||||
// 性能稼动率/运转时间口径:停机时间需要包含短停机(<5min),否则 runTime 被高估
|
||||
List<Klptcm1ProStoppageVo> events = getStoppageEvents(startDate, endDate, true);
|
||||
Map<String, Long> downtimeMap = new HashMap<>();
|
||||
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
|
||||
for (Klptcm1ProStoppageVo event : events) {
|
||||
if (event.getStartDate() != null && event.getDuration() != null) {
|
||||
String date = dateFormat.format(event.getStartDate());
|
||||
// duration单位是秒,转换为分钟
|
||||
Long minutes = event.getDuration() / 60;
|
||||
downtimeMap.merge(date, minutes, Long::sum);
|
||||
if (event.getStartDate() == null || event.getDuration() == null || event.getDuration() <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Date eventStart = event.getStartDate();
|
||||
long durationSec = event.getDuration();
|
||||
long durationMin = (durationSec + 59) / 60; // 向上取整,避免丢失秒数
|
||||
|
||||
// 计算停机结束时间
|
||||
cal.setTime(eventStart);
|
||||
cal.add(Calendar.SECOND, (int) durationSec);
|
||||
Date eventEnd = cal.getTime();
|
||||
|
||||
// 如果停机事件在同一天,直接累加
|
||||
String startDateStr = dateFormat.format(eventStart);
|
||||
String endDateStr = dateFormat.format(eventEnd);
|
||||
|
||||
if (startDateStr.equals(endDateStr)) {
|
||||
// 同一天,直接累加
|
||||
downtimeMap.merge(startDateStr, durationMin, Long::sum);
|
||||
} else {
|
||||
// 跨天:按实际跨天的分钟数分配到对应的日期
|
||||
cal.setTime(eventStart);
|
||||
cal.set(Calendar.HOUR_OF_DAY, 0);
|
||||
cal.set(Calendar.MINUTE, 0);
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
Date dayStart = cal.getTime();
|
||||
|
||||
Date currentDayStart = dayStart;
|
||||
|
||||
while (currentDayStart.before(eventEnd)) {
|
||||
cal.setTime(currentDayStart);
|
||||
cal.add(Calendar.DAY_OF_MONTH, 1);
|
||||
Date nextDayStart = cal.getTime();
|
||||
|
||||
// 计算当前天的停机分钟数
|
||||
Date dayEnd = nextDayStart.before(eventEnd) ? nextDayStart : eventEnd;
|
||||
long dayMinutes = Math.max(0, (dayEnd.getTime() - Math.max(currentDayStart.getTime(), eventStart.getTime())) / (1000 * 60));
|
||||
|
||||
if (dayMinutes > 0) {
|
||||
String dateKey = dateFormat.format(currentDayStart);
|
||||
downtimeMap.merge(dateKey, dayMinutes, Long::sum);
|
||||
}
|
||||
|
||||
currentDayStart = nextDayStart;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,7 +409,11 @@ public class AcidOeeServiceImpl implements IAcidOeeService {
|
||||
Long runTime = summary.getRunTimeMin() != null ? summary.getRunTimeMin() : 0L;
|
||||
BigDecimal idealCycleTon = summary.getIdealCycleTimeMinPerTon();
|
||||
BigDecimal totalOutputTon = summary.getTotalOutputTon();
|
||||
if (runTime > 0 && idealCycleTon != null && totalOutputTon != null && totalOutputTon.compareTo(BigDecimal.ZERO) > 0) {
|
||||
if (runTime > 0
|
||||
&& idealCycleTon != null
|
||||
&& idealCycleTon.compareTo(BigDecimal.ZERO) > 0
|
||||
&& totalOutputTon != null
|
||||
&& totalOutputTon.compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal idealTime = idealCycleTon.multiply(totalOutputTon);
|
||||
BigDecimal performanceTon = idealTime.divide(BigDecimal.valueOf(runTime), 4, RoundingMode.HALF_UP)
|
||||
.multiply(BigDecimal.valueOf(100));
|
||||
@@ -337,7 +423,10 @@ public class AcidOeeServiceImpl implements IAcidOeeService {
|
||||
// 性能稼动率(卷维度)
|
||||
Long totalOutputCoil = summary.getTotalOutputCoil() != null ? summary.getTotalOutputCoil() : 0L;
|
||||
BigDecimal idealCycleCoil = summary.getIdealCycleTimeMinPerCoil();
|
||||
if (runTime > 0 && idealCycleCoil != null && totalOutputCoil > 0) {
|
||||
if (runTime > 0
|
||||
&& idealCycleCoil != null
|
||||
&& idealCycleCoil.compareTo(BigDecimal.ZERO) > 0
|
||||
&& totalOutputCoil > 0) {
|
||||
BigDecimal idealTime = idealCycleCoil.multiply(BigDecimal.valueOf(totalOutputCoil));
|
||||
BigDecimal performanceCoil = idealTime.divide(BigDecimal.valueOf(runTime), 4, RoundingMode.HALF_UP)
|
||||
.multiply(BigDecimal.valueOf(100));
|
||||
@@ -363,102 +452,15 @@ public class AcidOeeServiceImpl implements IAcidOeeService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算线性回归(最小二乘法)
|
||||
*/
|
||||
private RegressionResult calculateRegression(List<BigDecimal> xValues, List<Long> yValues) {
|
||||
if (xValues.size() != yValues.size() || xValues.isEmpty()) {
|
||||
return null;
|
||||
private BigDecimal median(List<BigDecimal> values) {
|
||||
if (values == null || values.isEmpty()) return null;
|
||||
int n = values.size();
|
||||
if (n % 2 == 1) {
|
||||
return values.get(n / 2);
|
||||
}
|
||||
|
||||
int n = xValues.size();
|
||||
BigDecimal sumX = BigDecimal.ZERO;
|
||||
BigDecimal sumY = BigDecimal.ZERO;
|
||||
BigDecimal sumXY = BigDecimal.ZERO;
|
||||
BigDecimal sumX2 = BigDecimal.ZERO;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
BigDecimal x = xValues.get(i);
|
||||
BigDecimal y = BigDecimal.valueOf(yValues.get(i));
|
||||
sumX = sumX.add(x);
|
||||
sumY = sumY.add(y);
|
||||
sumXY = sumXY.add(x.multiply(y));
|
||||
sumX2 = sumX2.add(x.multiply(x));
|
||||
}
|
||||
|
||||
BigDecimal nDecimal = BigDecimal.valueOf(n);
|
||||
BigDecimal denominator = nDecimal.multiply(sumX2).subtract(sumX.multiply(sumX));
|
||||
if (denominator.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// slope = (n*ΣXY - ΣX*ΣY) / (n*ΣX² - (ΣX)²)
|
||||
BigDecimal slope = nDecimal.multiply(sumXY).subtract(sumX.multiply(sumY))
|
||||
.divide(denominator, 6, RoundingMode.HALF_UP);
|
||||
|
||||
// intercept = (ΣY - slope*ΣX) / n
|
||||
BigDecimal intercept = sumY.subtract(slope.multiply(sumX))
|
||||
.divide(nDecimal, 6, RoundingMode.HALF_UP);
|
||||
|
||||
// 计算R²
|
||||
BigDecimal meanY = sumY.divide(nDecimal, 6, RoundingMode.HALF_UP);
|
||||
BigDecimal ssTotal = BigDecimal.ZERO;
|
||||
BigDecimal ssResidual = BigDecimal.ZERO;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
BigDecimal x = xValues.get(i);
|
||||
BigDecimal y = BigDecimal.valueOf(yValues.get(i));
|
||||
BigDecimal predictedY = slope.multiply(x).add(intercept);
|
||||
BigDecimal diff = y.subtract(meanY);
|
||||
ssTotal = ssTotal.add(diff.multiply(diff));
|
||||
BigDecimal residual = y.subtract(predictedY);
|
||||
ssResidual = ssResidual.add(residual.multiply(residual));
|
||||
}
|
||||
|
||||
BigDecimal r2 = BigDecimal.ONE;
|
||||
if (ssTotal.compareTo(BigDecimal.ZERO) > 0) {
|
||||
r2 = BigDecimal.ONE.subtract(ssResidual.divide(ssTotal, 6, RoundingMode.HALF_UP));
|
||||
}
|
||||
|
||||
return new RegressionResult(slope, intercept, r2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成拟合线端点
|
||||
*/
|
||||
private List<AcidOeeRegressionVo.RegressionLinePointVo> generateLinePoints(
|
||||
List<AcidOeeRegressionVo.RegressionPointVo> points,
|
||||
RegressionResult result) {
|
||||
if (points.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 找到X轴的最小值和最大值
|
||||
BigDecimal minX = points.stream()
|
||||
.map(AcidOeeRegressionVo.RegressionPointVo::getWeightTon)
|
||||
.filter(Objects::nonNull)
|
||||
.min(BigDecimal::compareTo)
|
||||
.orElse(BigDecimal.ZERO);
|
||||
|
||||
BigDecimal maxX = points.stream()
|
||||
.map(AcidOeeRegressionVo.RegressionPointVo::getWeightTon)
|
||||
.filter(Objects::nonNull)
|
||||
.max(BigDecimal::compareTo)
|
||||
.orElse(BigDecimal.ZERO);
|
||||
|
||||
// 计算对应的Y值
|
||||
BigDecimal y1 = result.slope.multiply(minX).add(result.intercept);
|
||||
BigDecimal y2 = result.slope.multiply(maxX).add(result.intercept);
|
||||
|
||||
AcidOeeRegressionVo.RegressionLinePointVo p1 = new AcidOeeRegressionVo.RegressionLinePointVo();
|
||||
p1.setWeightTon(minX);
|
||||
p1.setDurationMin(y1);
|
||||
|
||||
AcidOeeRegressionVo.RegressionLinePointVo p2 = new AcidOeeRegressionVo.RegressionLinePointVo();
|
||||
p2.setWeightTon(maxX);
|
||||
p2.setDurationMin(y2);
|
||||
|
||||
return Arrays.asList(p1, p2);
|
||||
BigDecimal a = values.get(n / 2 - 1);
|
||||
BigDecimal b = values.get(n / 2);
|
||||
return a.add(b).divide(BigDecimal.valueOf(2), 6, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -477,20 +479,7 @@ public class AcidOeeServiceImpl implements IAcidOeeService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 回归结果内部类
|
||||
*/
|
||||
private static class RegressionResult {
|
||||
final BigDecimal slope;
|
||||
final BigDecimal intercept;
|
||||
final BigDecimal r2;
|
||||
|
||||
RegressionResult(BigDecimal slope, BigDecimal intercept, BigDecimal r2) {
|
||||
this.slope = slope;
|
||||
this.intercept = intercept;
|
||||
this.r2 = r2;
|
||||
}
|
||||
}
|
||||
// 回归相关逻辑已下线:理论节拍统一由“优良日统计口径”产生
|
||||
|
||||
/**
|
||||
* 内部统计类:某一 stopType 的总损失时间与次数
|
||||
|
||||
@@ -89,8 +89,12 @@ public class Klptcm1ProStoppageServiceImpl implements IKlptcm1ProStoppageService
|
||||
lqw.le(Klptcm1ProStoppage::getStartDate, endDateWithTime);
|
||||
}
|
||||
lqw.eq(bo.getDURATION() != null, Klptcm1ProStoppage::getDuration, bo.getDURATION());
|
||||
// 只查询持续时间大于等于5分钟(300秒)的停机记录
|
||||
lqw.ge(Klptcm1ProStoppage::getDuration, 300);
|
||||
// 默认只统计持续时间大于等于5分钟(300秒)的停机记录;
|
||||
// 若 params.includeShortDuration=true,则放开短停机,用于 OEE 性能稼动率的“完整停机时间”统计。
|
||||
boolean includeShort = params != null && Boolean.TRUE.equals(params.get("includeShortDuration"));
|
||||
if (!includeShort) {
|
||||
lqw.ge(Klptcm1ProStoppage::getDuration, 300);
|
||||
}
|
||||
lqw.eq(bo.getInsDate() != null, Klptcm1ProStoppage::getInsDate, bo.getInsDate());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getStopType()), Klptcm1ProStoppage::getStopType, bo.getStopType());
|
||||
//倒序
|
||||
|
||||
Reference in New Issue
Block a user