|
|
|
|
@ -2,6 +2,7 @@ package com.aucma.report.service.impl;
|
|
|
|
|
|
|
|
|
|
import com.aucma.base.support.DeviceParamTableRouter;
|
|
|
|
|
import com.aucma.report.domain.vo.InjectionOeeAnalysisVo;
|
|
|
|
|
import com.aucma.report.domain.vo.ParamRawPoint;
|
|
|
|
|
import com.aucma.report.mapper.InjectionOeeMapper;
|
|
|
|
|
import com.aucma.report.service.IInjectionOeeService;
|
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
|
@ -20,6 +21,7 @@ import java.util.Calendar;
|
|
|
|
|
import java.util.Collections;
|
|
|
|
|
import java.util.Comparator;
|
|
|
|
|
import java.util.Date;
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
|
|
@ -27,7 +29,7 @@ import java.util.Map;
|
|
|
|
|
* 注塑车间 OEE 业务逻辑层实现。
|
|
|
|
|
*
|
|
|
|
|
* <p>OEE = A(设备利用率) * P(性能稼动率) * Q(良品率)。A 是 OEE 的组成项,不等同于 OEE;
|
|
|
|
|
* 页面在展示完整 OEE 的同时,保留总利用率、周利用率、本日利用率,方便按设备定位时间利用不足。</p>
|
|
|
|
|
* 页面在展示完整 OEE 的同时,按日利用率、自然周利用率、总利用率单选查询,方便按设备定位时间利用不足。</p>
|
|
|
|
|
*
|
|
|
|
|
* @author Antigravity
|
|
|
|
|
*/
|
|
|
|
|
@ -49,6 +51,10 @@ public class InjectionOeeServiceImpl implements IInjectionOeeService {
|
|
|
|
|
private static final String SHIFT_DAY = "DAY";
|
|
|
|
|
private static final String SHIFT_NIGHT = "NIGHT";
|
|
|
|
|
|
|
|
|
|
private static final String AVAILABILITY_DAY = "DAY";
|
|
|
|
|
private static final String AVAILABILITY_WEEK = "WEEK";
|
|
|
|
|
private static final String AVAILABILITY_TOTAL = "TOTAL";
|
|
|
|
|
|
|
|
|
|
private static final String OLD_DEVICE_CODE_PREFIX = "OLD-";
|
|
|
|
|
private static final double OLD_DEVICE_ESTIMATE_MIN_RATE = 0.70D;
|
|
|
|
|
private static final double OLD_DEVICE_ESTIMATE_MAX_RATE = 0.90D;
|
|
|
|
|
@ -64,8 +70,8 @@ public class InjectionOeeServiceImpl implements IInjectionOeeService {
|
|
|
|
|
/** OLD 手工设备采集稀疏,基线窗口放宽到 24 小时。 */
|
|
|
|
|
private static final int OLD_BASELINE_HOURS = -24;
|
|
|
|
|
|
|
|
|
|
/** 周利用率固定看最近 7 个生产日。 */
|
|
|
|
|
private static final int WEEK_DAYS = 6;
|
|
|
|
|
/** 周期时间有效样本下限,样本过少时用 1.0 兜底,避免少量异常点放大 P。 */
|
|
|
|
|
private static final int MIN_CYCLE_SAMPLE_COUNT = 10;
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
private InjectionOeeMapper injectionOeeMapper;
|
|
|
|
|
@ -74,7 +80,11 @@ public class InjectionOeeServiceImpl implements IInjectionOeeService {
|
|
|
|
|
private DeviceParamTableRouter deviceParamTableRouter;
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public List<InjectionOeeAnalysisVo> getInjectionOeeAnalysis(String deviceCode, String beginTimeStr, String endTimeStr, String shiftType) {
|
|
|
|
|
public List<InjectionOeeAnalysisVo> getInjectionOeeAnalysis(String deviceCode,
|
|
|
|
|
String beginTimeStr,
|
|
|
|
|
String endTimeStr,
|
|
|
|
|
String shiftType,
|
|
|
|
|
String availabilityType) {
|
|
|
|
|
List<InjectionOeeAnalysisVo> targetDevices = injectionOeeMapper.selectTargetDevices(deviceCode);
|
|
|
|
|
if (CollectionUtils.isEmpty(targetDevices)) {
|
|
|
|
|
return Collections.emptyList();
|
|
|
|
|
@ -84,47 +94,49 @@ public class InjectionOeeServiceImpl implements IInjectionOeeService {
|
|
|
|
|
sdf.setLenient(false);
|
|
|
|
|
|
|
|
|
|
String normalizedShiftType = normalizeShiftType(shiftType);
|
|
|
|
|
String normalizedAvailabilityType = normalizeAvailabilityType(availabilityType);
|
|
|
|
|
Date todayDate = truncateToDay(new Date());
|
|
|
|
|
Date weekBeginDate = addDays(todayDate, -WEEK_DAYS);
|
|
|
|
|
DateRange totalRange = resolveTotalRange(beginTimeStr, endTimeStr, sdf, todayDate);
|
|
|
|
|
Date defaultReportDate = addDays(todayDate, -1);
|
|
|
|
|
DateRange selectedRange = resolveSelectedRange(beginTimeStr, endTimeStr, sdf, defaultReportDate, todayDate);
|
|
|
|
|
DateRange availabilityRange = resolveAvailabilityRange(selectedRange, normalizedAvailabilityType, todayDate);
|
|
|
|
|
|
|
|
|
|
List<MetricWindow> totalWindows = buildShiftWindows(totalRange.beginDate, totalRange.endDate, normalizedShiftType);
|
|
|
|
|
List<MetricWindow> weekWindows = buildShiftWindows(weekBeginDate, todayDate, normalizedShiftType);
|
|
|
|
|
List<MetricWindow> todayWindows = buildShiftWindows(todayDate, todayDate, normalizedShiftType);
|
|
|
|
|
List<MetricWindow> metricWindows = buildShiftWindows(availabilityRange.beginDate,
|
|
|
|
|
availabilityRange.endDate, normalizedShiftType);
|
|
|
|
|
Date queryBeginTime = firstWindowBegin(metricWindows);
|
|
|
|
|
Date queryEndTime = lastWindowEnd(metricWindows);
|
|
|
|
|
QualityResult quality = calculateQuality(queryBeginTime, queryEndTime);
|
|
|
|
|
MetricCache metricCache = loadMetricCache(targetDevices, metricWindows);
|
|
|
|
|
|
|
|
|
|
List<InjectionOeeAnalysisVo> results = new ArrayList<>();
|
|
|
|
|
for (InjectionOeeAnalysisVo device : targetDevices) {
|
|
|
|
|
InjectionOeeAnalysisVo vo = buildOeeRow(device, normalizedShiftType, totalWindows, weekWindows, todayWindows);
|
|
|
|
|
InjectionOeeAnalysisVo vo = buildOeeRow(device, normalizedShiftType, normalizedAvailabilityType,
|
|
|
|
|
metricWindows, quality, metricCache);
|
|
|
|
|
results.add(vo);
|
|
|
|
|
}
|
|
|
|
|
fillMissingOldDeviceOeeByReference(results);
|
|
|
|
|
fillMissingOldDeviceOeeByReference(results, normalizedAvailabilityType);
|
|
|
|
|
|
|
|
|
|
results.sort(Comparator.comparing(InjectionOeeAnalysisVo::getDEVICE_CODE));
|
|
|
|
|
results.sort(this::compareByDeviceDisplayOrder);
|
|
|
|
|
return results;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private InjectionOeeAnalysisVo buildOeeRow(InjectionOeeAnalysisVo device,
|
|
|
|
|
String shiftType,
|
|
|
|
|
List<MetricWindow> totalWindows,
|
|
|
|
|
List<MetricWindow> weekWindows,
|
|
|
|
|
List<MetricWindow> todayWindows) {
|
|
|
|
|
String availabilityType,
|
|
|
|
|
List<MetricWindow> metricWindows,
|
|
|
|
|
QualityResult quality,
|
|
|
|
|
MetricCache metricCache) {
|
|
|
|
|
String deviceCode = device.getDEVICE_CODE();
|
|
|
|
|
|
|
|
|
|
CounterSummary totalRun = selectCounterSummary(deviceCode, PARAM_RUN_SECONDS, totalWindows);
|
|
|
|
|
CounterSummary totalShots = selectCounterSummary(deviceCode, PARAM_SHOTS, totalWindows);
|
|
|
|
|
CounterSummary weekRun = selectCounterSummary(deviceCode, PARAM_RUN_SECONDS, weekWindows);
|
|
|
|
|
CounterSummary todayRun = selectCounterSummary(deviceCode, PARAM_RUN_SECONDS, todayWindows);
|
|
|
|
|
List<ParamRawPoint> runRawPoints = metricCache.getRunRawPoints(deviceCode);
|
|
|
|
|
List<ParamRawPoint> shotsRawPoints = metricCache.getShotRawPoints(deviceCode);
|
|
|
|
|
|
|
|
|
|
Date queryBeginTime = firstWindowBegin(totalWindows);
|
|
|
|
|
Date queryEndTime = lastWindowEnd(totalWindows);
|
|
|
|
|
List<String> tableSuffixes = resolveTableSuffixes(queryBeginTime, queryEndTime);
|
|
|
|
|
CounterSummary totalRun = calculateCounterSummary(deviceCode, metricWindows, runRawPoints);
|
|
|
|
|
CounterSummary totalShots = calculateCounterSummary(deviceCode, metricWindows, shotsRawPoints);
|
|
|
|
|
|
|
|
|
|
BigDecimal cavities = resolveCavities(deviceCode, queryEndTime, tableSuffixes);
|
|
|
|
|
BigDecimal cavities = resolveCavities(deviceCode, metricCache);
|
|
|
|
|
boolean cavitiesEstimated = cavities.compareTo(BigDecimal.ONE) == 0;
|
|
|
|
|
|
|
|
|
|
CycleResult cycle = calculateStandardCycle(injectionOeeMapper.selectCycleTimeSamples(
|
|
|
|
|
deviceCode, queryBeginTime, queryEndTime, tableSuffixes));
|
|
|
|
|
QualityResult quality = calculateQuality(queryBeginTime, queryEndTime);
|
|
|
|
|
CycleResult cycle = calculateStandardCycle(metricCache.getCycleStats(deviceCode));
|
|
|
|
|
|
|
|
|
|
BigDecimal plannedSeconds = BigDecimal.valueOf(totalRun.planSeconds);
|
|
|
|
|
BigDecimal runSeconds = BigDecimal.valueOf(totalRun.value);
|
|
|
|
|
@ -149,9 +161,7 @@ public class InjectionOeeServiceImpl implements IInjectionOeeService {
|
|
|
|
|
vo.setQUALITY(toDouble(qualityRate));
|
|
|
|
|
vo.setOEE(toDouble(oee));
|
|
|
|
|
|
|
|
|
|
vo.setTOTAL_AVAILABILITY(toDouble(capRate(availability)));
|
|
|
|
|
vo.setWEEK_AVAILABILITY(toDouble(capRate(calcRate(BigDecimal.valueOf(weekRun.value), BigDecimal.valueOf(weekRun.planSeconds)))));
|
|
|
|
|
vo.setTODAY_AVAILABILITY(toDouble(capRate(calcRate(BigDecimal.valueOf(todayRun.value), BigDecimal.valueOf(todayRun.planSeconds)))));
|
|
|
|
|
applyAvailabilityTypeValue(vo, availabilityType, capRate(availability));
|
|
|
|
|
|
|
|
|
|
vo.setPLANNED_TIME_MINUTES(totalRun.planSeconds / 60);
|
|
|
|
|
vo.setDOWNTIME_MINUTES(Math.max(0L, (totalRun.planSeconds - totalRun.value) / 60));
|
|
|
|
|
@ -175,41 +185,308 @@ public class InjectionOeeServiceImpl implements IInjectionOeeService {
|
|
|
|
|
return vo;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private CounterSummary selectCounterSummary(String deviceCode, String paramName, List<MetricWindow> windows) {
|
|
|
|
|
/**
|
|
|
|
|
* 一次性加载本次 OEE 页面需要的设备参数缓存,避免按设备循环触发多次相同结构的 SQL。
|
|
|
|
|
*/
|
|
|
|
|
private MetricCache loadMetricCache(List<InjectionOeeAnalysisVo> devices,
|
|
|
|
|
List<MetricWindow> metricWindows) {
|
|
|
|
|
MetricCache metricCache = new MetricCache();
|
|
|
|
|
|
|
|
|
|
loadRawPointCache(metricCache.runRawPointsByDevice, devices, PARAM_RUN_SECONDS, metricWindows);
|
|
|
|
|
loadRawPointCache(metricCache.shotRawPointsByDevice, devices, PARAM_SHOTS, metricWindows);
|
|
|
|
|
|
|
|
|
|
Date queryBeginTime = firstWindowBegin(metricWindows);
|
|
|
|
|
Date queryEndTime = lastWindowEnd(metricWindows);
|
|
|
|
|
List<String> tableSuffixes = resolveTableSuffixes(queryBeginTime, queryEndTime);
|
|
|
|
|
loadCavityCache(metricCache.cavitiesByDevice, devices, queryEndTime, tableSuffixes);
|
|
|
|
|
loadCycleStatsCache(metricCache.cycleStatsByDevice, devices, queryBeginTime, queryEndTime, tableSuffixes);
|
|
|
|
|
return metricCache;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void loadRawPointCache(Map<String, List<ParamRawPoint>> targetMap,
|
|
|
|
|
List<InjectionOeeAnalysisVo> devices,
|
|
|
|
|
String paramName,
|
|
|
|
|
List<MetricWindow> windows) {
|
|
|
|
|
loadRawPointCache(targetMap, extractDeviceCodes(devices, false), paramName, windows,
|
|
|
|
|
AUTO_BASELINE_HOURS, false);
|
|
|
|
|
loadRawPointCache(targetMap, extractDeviceCodes(devices, true), paramName, windows,
|
|
|
|
|
OLD_BASELINE_HOURS, true);
|
|
|
|
|
for (List<ParamRawPoint> rawPoints : targetMap.values()) {
|
|
|
|
|
rawPoints.sort(Comparator.comparing(ParamRawPoint::getCollectTime));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void loadRawPointCache(Map<String, List<ParamRawPoint>> targetMap,
|
|
|
|
|
List<String> deviceCodes,
|
|
|
|
|
String paramName,
|
|
|
|
|
List<MetricWindow> windows,
|
|
|
|
|
int baselineHours,
|
|
|
|
|
boolean oldDevice) {
|
|
|
|
|
if (CollectionUtils.isEmpty(deviceCodes) || CollectionUtils.isEmpty(windows)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<QueryRange> queryRanges = buildMergedQueryRanges(windows, baselineHours);
|
|
|
|
|
for (QueryRange queryRange : queryRanges) {
|
|
|
|
|
List<String> tableSuffixes = resolveTableSuffixes(queryRange.beginTime, queryRange.endTime);
|
|
|
|
|
if (!oldDevice && CollectionUtils.isEmpty(tableSuffixes)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
List<ParamRawPoint> rawPoints = injectionOeeMapper.selectRawParamValuesBatch(
|
|
|
|
|
deviceCodes,
|
|
|
|
|
paramName,
|
|
|
|
|
queryRange.beginTime,
|
|
|
|
|
queryRange.endTime,
|
|
|
|
|
tableSuffixes,
|
|
|
|
|
oldDevice
|
|
|
|
|
);
|
|
|
|
|
mergeRawPointCache(targetMap, rawPoints);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void mergeRawPointCache(Map<String, List<ParamRawPoint>> targetMap, List<ParamRawPoint> rawPoints) {
|
|
|
|
|
if (CollectionUtils.isEmpty(rawPoints)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
for (ParamRawPoint rawPoint : rawPoints) {
|
|
|
|
|
if (rawPoint == null || !StringUtils.hasText(rawPoint.getDeviceCode())) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
List<ParamRawPoint> deviceRawPoints = targetMap.get(rawPoint.getDeviceCode());
|
|
|
|
|
if (deviceRawPoints == null) {
|
|
|
|
|
deviceRawPoints = new ArrayList<>();
|
|
|
|
|
targetMap.put(rawPoint.getDeviceCode(), deviceRawPoints);
|
|
|
|
|
}
|
|
|
|
|
deviceRawPoints.add(rawPoint);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void loadCavityCache(Map<String, BigDecimal> targetMap,
|
|
|
|
|
List<InjectionOeeAnalysisVo> devices,
|
|
|
|
|
Date endTime,
|
|
|
|
|
List<String> tableSuffixes) {
|
|
|
|
|
loadCavityCache(targetMap, extractDeviceCodes(devices, false), endTime, tableSuffixes, false);
|
|
|
|
|
loadCavityCache(targetMap, extractDeviceCodes(devices, true), endTime, tableSuffixes, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void loadCavityCache(Map<String, BigDecimal> targetMap,
|
|
|
|
|
List<String> deviceCodes,
|
|
|
|
|
Date endTime,
|
|
|
|
|
List<String> tableSuffixes,
|
|
|
|
|
boolean oldDevice) {
|
|
|
|
|
if (CollectionUtils.isEmpty(deviceCodes)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!oldDevice && CollectionUtils.isEmpty(tableSuffixes)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
List<Map<String, Object>> rows = injectionOeeMapper.selectLatestCavitiesBatch(
|
|
|
|
|
deviceCodes, endTime, tableSuffixes, oldDevice);
|
|
|
|
|
if (CollectionUtils.isEmpty(rows)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
for (Map<String, Object> row : rows) {
|
|
|
|
|
String deviceCode = toMapString(row, "DEVICE_CODE");
|
|
|
|
|
if (!StringUtils.hasText(deviceCode)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
BigDecimal cavities = toBigDecimal(getMapValue(row, "CAVITIES"));
|
|
|
|
|
if (cavities.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
|
|
targetMap.put(deviceCode, cavities);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void loadCycleStatsCache(Map<String, Map<String, Object>> targetMap,
|
|
|
|
|
List<InjectionOeeAnalysisVo> devices,
|
|
|
|
|
Date beginTime,
|
|
|
|
|
Date endTime,
|
|
|
|
|
List<String> tableSuffixes) {
|
|
|
|
|
loadCycleStatsCache(targetMap, extractDeviceCodes(devices, false), beginTime, endTime,
|
|
|
|
|
tableSuffixes, false);
|
|
|
|
|
loadCycleStatsCache(targetMap, extractDeviceCodes(devices, true), beginTime, endTime,
|
|
|
|
|
tableSuffixes, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void loadCycleStatsCache(Map<String, Map<String, Object>> targetMap,
|
|
|
|
|
List<String> deviceCodes,
|
|
|
|
|
Date beginTime,
|
|
|
|
|
Date endTime,
|
|
|
|
|
List<String> tableSuffixes,
|
|
|
|
|
boolean oldDevice) {
|
|
|
|
|
if (CollectionUtils.isEmpty(deviceCodes)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!oldDevice && CollectionUtils.isEmpty(tableSuffixes)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
List<Map<String, Object>> rows = injectionOeeMapper.selectCycleTimeStatsBatch(
|
|
|
|
|
deviceCodes, beginTime, endTime, tableSuffixes, oldDevice);
|
|
|
|
|
if (CollectionUtils.isEmpty(rows)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
for (Map<String, Object> row : rows) {
|
|
|
|
|
String deviceCode = toMapString(row, "DEVICE_CODE");
|
|
|
|
|
if (StringUtils.hasText(deviceCode)) {
|
|
|
|
|
targetMap.put(deviceCode, row);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private List<String> extractDeviceCodes(List<InjectionOeeAnalysisVo> devices, boolean oldDevice) {
|
|
|
|
|
List<String> deviceCodes = new ArrayList<>();
|
|
|
|
|
for (InjectionOeeAnalysisVo device : devices) {
|
|
|
|
|
if (device == null || !StringUtils.hasText(device.getDEVICE_CODE())) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (isOldDeviceCode(device.getDEVICE_CODE()) == oldDevice) {
|
|
|
|
|
deviceCodes.add(device.getDEVICE_CODE());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return deviceCodes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Object getMapValue(Map<String, Object> map, String key) {
|
|
|
|
|
if (map == null || key == null) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
if (map.containsKey(key)) {
|
|
|
|
|
return map.get(key);
|
|
|
|
|
}
|
|
|
|
|
return map.get(key.toLowerCase());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String toMapString(Map<String, Object> map, String key) {
|
|
|
|
|
Object value = getMapValue(map, key);
|
|
|
|
|
return value == null ? null : value.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 在内存中过滤出指定窗口的前序 baseline 点和窗口数据,根据单调累加器 reset 规则计算增量。
|
|
|
|
|
* 此算法在逻辑上与原有 SQL 规约(selectCounterDeltaByWindow)完全等价。
|
|
|
|
|
*/
|
|
|
|
|
private long calculateCounterDeltaInMemory(String deviceCode, MetricWindow window, List<ParamRawPoint> rawPoints) {
|
|
|
|
|
if (CollectionUtils.isEmpty(rawPoints)) {
|
|
|
|
|
return 0L;
|
|
|
|
|
}
|
|
|
|
|
boolean isOld = isOldDeviceCode(deviceCode);
|
|
|
|
|
int baselineHours = isOld ? OLD_BASELINE_HOURS : AUTO_BASELINE_HOURS;
|
|
|
|
|
Date baselineBeginTime = addHours(window.beginTime, baselineHours);
|
|
|
|
|
|
|
|
|
|
BigDecimal deltaSum = BigDecimal.ZERO;
|
|
|
|
|
BigDecimal prevValue = null;
|
|
|
|
|
|
|
|
|
|
for (ParamRawPoint point : rawPoints) {
|
|
|
|
|
Date collectTime = point.getCollectTime();
|
|
|
|
|
if (collectTime.before(baselineBeginTime)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (!collectTime.before(window.endTime)) {
|
|
|
|
|
// rawPoints 已按采集时间升序排列,越过窗口后无需继续扫描。
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BigDecimal currValue = point.getParamValue();
|
|
|
|
|
|
|
|
|
|
if (collectTime.before(window.beginTime)) {
|
|
|
|
|
// 仅作为对比的基线,不累加增量本身
|
|
|
|
|
prevValue = currValue;
|
|
|
|
|
} else {
|
|
|
|
|
if (prevValue == null) {
|
|
|
|
|
// 没有 baseline,且这是窗口内第一个点,设为参考点,不计算增量(对应 SQL 中的 NULL)
|
|
|
|
|
prevValue = currValue;
|
|
|
|
|
} else {
|
|
|
|
|
if (currValue.compareTo(prevValue) > 0) {
|
|
|
|
|
deltaSum = deltaSum.add(currValue.subtract(prevValue));
|
|
|
|
|
} else if (currValue.compareTo(prevValue) < 0) {
|
|
|
|
|
// 计数器在两次采集中间发生了重置,直接加当前值
|
|
|
|
|
deltaSum = deltaSum.add(currValue);
|
|
|
|
|
}
|
|
|
|
|
prevValue = currValue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return safeLong(deltaSum);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 计算指定窗口集合内参数的累计值和计划时间,并在内存中进行增量计算。
|
|
|
|
|
*/
|
|
|
|
|
private CounterSummary calculateCounterSummary(String deviceCode, List<MetricWindow> windows, List<ParamRawPoint> rawPoints) {
|
|
|
|
|
long value = 0L;
|
|
|
|
|
long planSeconds = 0L;
|
|
|
|
|
for (MetricWindow window : windows) {
|
|
|
|
|
value += selectCounterDelta(deviceCode, paramName, window);
|
|
|
|
|
value += calculateCounterDeltaInMemory(deviceCode, window, rawPoints);
|
|
|
|
|
planSeconds += window.planSeconds;
|
|
|
|
|
}
|
|
|
|
|
return new CounterSummary(value, planSeconds);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 回源月分表按窗口 delta-sum 计算累加器增量。
|
|
|
|
|
*
|
|
|
|
|
* <p>为什么按窗口单独算:白班/夜班存在 07:00、19:00 业务边界,直接按自然日汇总会把夜班跨日数据切碎。</p>
|
|
|
|
|
* 批量查询某个设备在指定窗口集下的所有去重原始点,只合并真正相邻/重叠的有效区间。
|
|
|
|
|
*/
|
|
|
|
|
private long selectCounterDelta(String deviceCode, String paramName, MetricWindow window) {
|
|
|
|
|
Date autoBaselineBeginTime = addHours(window.beginTime, AUTO_BASELINE_HOURS);
|
|
|
|
|
Date oldBaselineBeginTime = addHours(window.beginTime, OLD_BASELINE_HOURS);
|
|
|
|
|
List<String> tableSuffixes = resolveTableSuffixes(autoBaselineBeginTime, window.endTime);
|
|
|
|
|
BigDecimal delta = injectionOeeMapper.selectCounterDeltaByWindow(
|
|
|
|
|
deviceCode,
|
|
|
|
|
paramName,
|
|
|
|
|
window.beginTime,
|
|
|
|
|
window.endTime,
|
|
|
|
|
autoBaselineBeginTime,
|
|
|
|
|
oldBaselineBeginTime,
|
|
|
|
|
tableSuffixes
|
|
|
|
|
);
|
|
|
|
|
return safeLong(delta);
|
|
|
|
|
private List<ParamRawPoint> selectRawPoints(String deviceCode, String paramName, List<MetricWindow> windows) {
|
|
|
|
|
if (CollectionUtils.isEmpty(windows)) {
|
|
|
|
|
return Collections.emptyList();
|
|
|
|
|
}
|
|
|
|
|
boolean oldDevice = isOldDeviceCode(deviceCode);
|
|
|
|
|
int baselineHours = oldDevice ? OLD_BASELINE_HOURS : AUTO_BASELINE_HOURS;
|
|
|
|
|
|
|
|
|
|
List<QueryRange> queryRanges = buildMergedQueryRanges(windows, baselineHours);
|
|
|
|
|
if (CollectionUtils.isEmpty(queryRanges)) {
|
|
|
|
|
return Collections.emptyList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<ParamRawPoint> rawPoints = new ArrayList<>();
|
|
|
|
|
for (QueryRange queryRange : queryRanges) {
|
|
|
|
|
List<String> tableSuffixes = resolveTableSuffixes(queryRange.beginTime, queryRange.endTime);
|
|
|
|
|
rawPoints.addAll(injectionOeeMapper.selectRawParamValues(
|
|
|
|
|
deviceCode,
|
|
|
|
|
paramName,
|
|
|
|
|
queryRange.beginTime,
|
|
|
|
|
queryRange.endTime,
|
|
|
|
|
tableSuffixes,
|
|
|
|
|
oldDevice
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
rawPoints.sort(Comparator.comparing(ParamRawPoint::getCollectTime));
|
|
|
|
|
return rawPoints;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 将多个统计窗口扩展出基线区间后做合并,避免历史日期和本周/今日区间不连续时扫穿中间月份。
|
|
|
|
|
*/
|
|
|
|
|
private List<QueryRange> buildMergedQueryRanges(List<MetricWindow> windows, int baselineHours) {
|
|
|
|
|
List<QueryRange> ranges = new ArrayList<>();
|
|
|
|
|
for (MetricWindow window : windows) {
|
|
|
|
|
ranges.add(new QueryRange(addHours(window.beginTime, baselineHours), window.endTime));
|
|
|
|
|
}
|
|
|
|
|
if (ranges.isEmpty()) {
|
|
|
|
|
return Collections.emptyList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ranges.sort(Comparator.comparing(QueryRange::getBeginTime));
|
|
|
|
|
List<QueryRange> mergedRanges = new ArrayList<>();
|
|
|
|
|
for (QueryRange range : ranges) {
|
|
|
|
|
if (mergedRanges.isEmpty()) {
|
|
|
|
|
mergedRanges.add(range);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
QueryRange lastRange = mergedRanges.get(mergedRanges.size() - 1);
|
|
|
|
|
if (range.beginTime.after(lastRange.endTime)) {
|
|
|
|
|
mergedRanges.add(range);
|
|
|
|
|
} else if (range.endTime.after(lastRange.endTime)) {
|
|
|
|
|
lastRange.endTime = range.endTime;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return mergedRanges;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 使用新设备有效 OEE 结果的均值和中位数,为 OLD 设备缺失或无有效指标时临时补估算值。
|
|
|
|
|
*/
|
|
|
|
|
private void fillMissingOldDeviceOeeByReference(List<InjectionOeeAnalysisVo> list) {
|
|
|
|
|
private void fillMissingOldDeviceOeeByReference(List<InjectionOeeAnalysisVo> list, String availabilityType) {
|
|
|
|
|
if (CollectionUtils.isEmpty(list)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
@ -219,8 +496,6 @@ public class InjectionOeeServiceImpl implements IInjectionOeeService {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<BigDecimal> weekAvailabilityReferences = buildReferenceRates(list, InjectionOeeAnalysisVo::getWEEK_AVAILABILITY);
|
|
|
|
|
List<BigDecimal> todayAvailabilityReferences = buildReferenceRates(list, InjectionOeeAnalysisVo::getTODAY_AVAILABILITY);
|
|
|
|
|
List<BigDecimal> performanceReferences = buildReferenceRates(list, InjectionOeeAnalysisVo::getPERFORMANCE);
|
|
|
|
|
List<Long> shotReferences = buildReferenceLongValues(list, InjectionOeeAnalysisVo::getSHOTS);
|
|
|
|
|
List<Long> usedEstimatedShots = new ArrayList<>();
|
|
|
|
|
@ -233,12 +508,10 @@ public class InjectionOeeServiceImpl implements IInjectionOeeService {
|
|
|
|
|
|
|
|
|
|
BigDecimal estimateRate = buildOldDeviceEstimateFactor(item, missingOldDeviceIndex);
|
|
|
|
|
BigDecimal availability = buildOldDeviceEstimateRate(availabilityReferences, estimateRate);
|
|
|
|
|
BigDecimal weekAvailability = buildOldDeviceEstimateRate(weekAvailabilityReferences, estimateRate);
|
|
|
|
|
BigDecimal todayAvailability = buildOldDeviceEstimateRate(todayAvailabilityReferences, estimateRate);
|
|
|
|
|
BigDecimal performance = buildOldDeviceEstimateRate(performanceReferences, estimateRate);
|
|
|
|
|
Long shots = buildOldDeviceEstimateLong(shotReferences, estimateRate, usedEstimatedShots, missingOldDeviceIndex);
|
|
|
|
|
|
|
|
|
|
applyOldDeviceEstimate(item, availability, weekAvailability, todayAvailability, performance, shots);
|
|
|
|
|
applyOldDeviceEstimate(item, availabilityType, availability, performance, shots);
|
|
|
|
|
usedEstimatedShots.add(shots);
|
|
|
|
|
missingOldDeviceIndex++;
|
|
|
|
|
}
|
|
|
|
|
@ -257,8 +530,69 @@ public class InjectionOeeServiceImpl implements IInjectionOeeService {
|
|
|
|
|
|
|
|
|
|
private boolean isOldDevice(InjectionOeeAnalysisVo item) {
|
|
|
|
|
return item != null
|
|
|
|
|
&& item.getDEVICE_CODE() != null
|
|
|
|
|
&& item.getDEVICE_CODE().trim().startsWith(OLD_DEVICE_CODE_PREFIX);
|
|
|
|
|
&& isOldDeviceCode(item.getDEVICE_CODE());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean isOldDeviceCode(String deviceCode) {
|
|
|
|
|
return deviceCode != null
|
|
|
|
|
&& deviceCode.trim().startsWith(OLD_DEVICE_CODE_PREFIX);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void applyAvailabilityTypeValue(InjectionOeeAnalysisVo vo, String availabilityType, BigDecimal availability) {
|
|
|
|
|
if (AVAILABILITY_WEEK.equals(availabilityType)) {
|
|
|
|
|
vo.setWEEK_AVAILABILITY(toDouble(availability));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (AVAILABILITY_TOTAL.equals(availabilityType)) {
|
|
|
|
|
vo.setTOTAL_AVAILABILITY(toDouble(availability));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
vo.setTODAY_AVAILABILITY(toDouble(availability));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private int compareByDeviceDisplayOrder(InjectionOeeAnalysisVo left, InjectionOeeAnalysisVo right) {
|
|
|
|
|
String leftCode = left == null ? null : left.getDEVICE_CODE();
|
|
|
|
|
String rightCode = right == null ? null : right.getDEVICE_CODE();
|
|
|
|
|
int priorityCompare = Integer.compare(resolveDeviceDisplayPriority(leftCode), resolveDeviceDisplayPriority(rightCode));
|
|
|
|
|
if (priorityCompare != 0) {
|
|
|
|
|
return priorityCompare;
|
|
|
|
|
}
|
|
|
|
|
int numberCompare = Integer.compare(resolveDeviceDisplayNumber(leftCode), resolveDeviceDisplayNumber(rightCode));
|
|
|
|
|
if (numberCompare != 0) {
|
|
|
|
|
return numberCompare;
|
|
|
|
|
}
|
|
|
|
|
return String.valueOf(leftCode).compareTo(String.valueOf(rightCode));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private int resolveDeviceDisplayPriority(String deviceCode) {
|
|
|
|
|
if (deviceCode == null) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
String normalizedCode = deviceCode.trim();
|
|
|
|
|
if (normalizedCode.startsWith("YZM-")) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (normalizedCode.startsWith(OLD_DEVICE_CODE_PREFIX)) {
|
|
|
|
|
return 2;
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private int resolveDeviceDisplayNumber(String deviceCode) {
|
|
|
|
|
if (deviceCode == null) {
|
|
|
|
|
return Integer.MAX_VALUE;
|
|
|
|
|
}
|
|
|
|
|
String normalizedCode = deviceCode.trim();
|
|
|
|
|
int dashIndex = normalizedCode.lastIndexOf('-');
|
|
|
|
|
if (dashIndex < 0 || dashIndex == normalizedCode.length() - 1) {
|
|
|
|
|
return Integer.MAX_VALUE;
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
// 为什么按编号排序:现场设备编码 YZM-02 / OLD-02 比字典序更符合车间页面阅读习惯。
|
|
|
|
|
return Integer.parseInt(normalizedCode.substring(dashIndex + 1));
|
|
|
|
|
} catch (NumberFormatException e) {
|
|
|
|
|
return Integer.MAX_VALUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private List<BigDecimal> buildReferenceRates(List<InjectionOeeAnalysisVo> list, RateAccessor accessor) {
|
|
|
|
|
@ -325,9 +659,8 @@ public class InjectionOeeServiceImpl implements IInjectionOeeService {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void applyOldDeviceEstimate(InjectionOeeAnalysisVo item,
|
|
|
|
|
String availabilityType,
|
|
|
|
|
BigDecimal availability,
|
|
|
|
|
BigDecimal weekAvailability,
|
|
|
|
|
BigDecimal todayAvailability,
|
|
|
|
|
BigDecimal performance,
|
|
|
|
|
Long shots) {
|
|
|
|
|
BigDecimal quality = BigDecimal.valueOf(item.getQUALITY() == null ? 1D : item.getQUALITY().doubleValue());
|
|
|
|
|
@ -338,9 +671,7 @@ public class InjectionOeeServiceImpl implements IInjectionOeeService {
|
|
|
|
|
BigDecimal cavities = BigDecimal.valueOf(item.getCAVITIES() == null ? 1L : item.getCAVITIES().longValue());
|
|
|
|
|
|
|
|
|
|
item.setAVAILABILITY(toDouble(availability));
|
|
|
|
|
item.setTOTAL_AVAILABILITY(toDouble(availability));
|
|
|
|
|
item.setWEEK_AVAILABILITY(toDouble(weekAvailability));
|
|
|
|
|
item.setTODAY_AVAILABILITY(toDouble(todayAvailability));
|
|
|
|
|
applyAvailabilityTypeValue(item, availabilityType, availability);
|
|
|
|
|
item.setPERFORMANCE(toDouble(performance));
|
|
|
|
|
item.setDIAGNOSTIC_PERFORMANCE(toDouble(performance));
|
|
|
|
|
item.setOEE(toDouble(oee));
|
|
|
|
|
@ -482,8 +813,8 @@ public class InjectionOeeServiceImpl implements IInjectionOeeService {
|
|
|
|
|
return new MetricWindow(begin.getTime(), end.getTime(), SHIFT_PLAN_SECONDS, shiftType);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private BigDecimal resolveCavities(String deviceCode, Date endTime, List<String> tableSuffixes) {
|
|
|
|
|
BigDecimal cavities = injectionOeeMapper.selectLatestCavities(deviceCode, endTime, tableSuffixes);
|
|
|
|
|
private BigDecimal resolveCavities(String deviceCode, MetricCache metricCache) {
|
|
|
|
|
BigDecimal cavities = metricCache.cavitiesByDevice.get(deviceCode);
|
|
|
|
|
if (cavities == null || cavities.compareTo(BigDecimal.ZERO) <= 0) {
|
|
|
|
|
// 为什么默认 1:模腔数缺失时仍可计算开模数对应件数,但必须通过降级说明暴露给业务。
|
|
|
|
|
return BigDecimal.ONE;
|
|
|
|
|
@ -491,34 +822,24 @@ public class InjectionOeeServiceImpl implements IInjectionOeeService {
|
|
|
|
|
return cavities;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private CycleResult calculateStandardCycle(List<BigDecimal> cycleSamples) {
|
|
|
|
|
private CycleResult calculateStandardCycle(Map<String, Object> cycleStats) {
|
|
|
|
|
CycleResult result = new CycleResult();
|
|
|
|
|
List<BigDecimal> validSamples = new ArrayList<>();
|
|
|
|
|
if (cycleSamples != null) {
|
|
|
|
|
for (BigDecimal sample : cycleSamples) {
|
|
|
|
|
if (sample != null && sample.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
|
|
validSamples.add(sample);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (validSamples.size() < 20) {
|
|
|
|
|
if (cycleStats == null || cycleStats.isEmpty()) {
|
|
|
|
|
result.cycleSeconds = BigDecimal.ZERO;
|
|
|
|
|
result.estimated = true;
|
|
|
|
|
result.source = "标准周期兜底";
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<BigDecimal> secondSamples = new ArrayList<>();
|
|
|
|
|
BigDecimal sum = BigDecimal.ZERO;
|
|
|
|
|
BigDecimal microsecondDivisor = new BigDecimal("1000000");
|
|
|
|
|
for (BigDecimal sample : validSamples) {
|
|
|
|
|
BigDecimal second = sample.divide(microsecondDivisor, 6, RoundingMode.HALF_UP);
|
|
|
|
|
secondSamples.add(second);
|
|
|
|
|
sum = sum.add(second);
|
|
|
|
|
int sampleCount = toBigDecimal(cycleStats.get("SAMPLE_COUNT")).intValue();
|
|
|
|
|
if (sampleCount < MIN_CYCLE_SAMPLE_COUNT) {
|
|
|
|
|
result.cycleSeconds = BigDecimal.ZERO;
|
|
|
|
|
result.estimated = true;
|
|
|
|
|
result.source = "标准周期兜底";
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BigDecimal mean = sum.divide(BigDecimal.valueOf(secondSamples.size()), 6, RoundingMode.HALF_UP);
|
|
|
|
|
BigDecimal mean = toBigDecimal(cycleStats.get("AVG_SECONDS"));
|
|
|
|
|
if (mean.compareTo(BigDecimal.ZERO) <= 0) {
|
|
|
|
|
result.cycleSeconds = BigDecimal.ZERO;
|
|
|
|
|
result.estimated = true;
|
|
|
|
|
@ -526,32 +847,17 @@ public class InjectionOeeServiceImpl implements IInjectionOeeService {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BigDecimal varianceSum = BigDecimal.ZERO;
|
|
|
|
|
for (BigDecimal second : secondSamples) {
|
|
|
|
|
BigDecimal diff = second.subtract(mean);
|
|
|
|
|
varianceSum = varianceSum.add(diff.multiply(diff));
|
|
|
|
|
}
|
|
|
|
|
BigDecimal variance = varianceSum.divide(BigDecimal.valueOf(secondSamples.size() - 1), 6, RoundingMode.HALF_UP);
|
|
|
|
|
double cv = Math.sqrt(variance.doubleValue()) / mean.doubleValue();
|
|
|
|
|
if (cv >= 0.3) {
|
|
|
|
|
BigDecimal median = toBigDecimal(cycleStats.get("MEDIAN_SECONDS"));
|
|
|
|
|
if (median.compareTo(BigDecimal.ZERO) <= 0) {
|
|
|
|
|
result.cycleSeconds = BigDecimal.ZERO;
|
|
|
|
|
result.estimated = true;
|
|
|
|
|
result.source = "标准周期兜底";
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
secondSamples.sort(Comparator.naturalOrder());
|
|
|
|
|
int count = secondSamples.size();
|
|
|
|
|
BigDecimal median;
|
|
|
|
|
if (count % 2 == 1) {
|
|
|
|
|
median = secondSamples.get(count / 2);
|
|
|
|
|
} else {
|
|
|
|
|
median = secondSamples.get(count / 2 - 1).add(secondSamples.get(count / 2))
|
|
|
|
|
.divide(new BigDecimal("2"), 6, RoundingMode.HALF_UP);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.cycleSeconds = median;
|
|
|
|
|
result.cycleSeconds = median.setScale(6, RoundingMode.HALF_UP);
|
|
|
|
|
result.estimated = false;
|
|
|
|
|
// 为什么不用离散系数直接否决:注塑换模、空循环会拉高波动,中位数已经承担抗极端值职责。
|
|
|
|
|
result.source = "设备采集周期中位数";
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
@ -649,6 +955,16 @@ public class InjectionOeeServiceImpl implements IInjectionOeeService {
|
|
|
|
|
return SHIFT_ALL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String normalizeAvailabilityType(String availabilityType) {
|
|
|
|
|
if (AVAILABILITY_WEEK.equalsIgnoreCase(availabilityType)) {
|
|
|
|
|
return AVAILABILITY_WEEK;
|
|
|
|
|
}
|
|
|
|
|
if (AVAILABILITY_TOTAL.equalsIgnoreCase(availabilityType)) {
|
|
|
|
|
return AVAILABILITY_TOTAL;
|
|
|
|
|
}
|
|
|
|
|
return AVAILABILITY_DAY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String resolveShiftName(String shiftType) {
|
|
|
|
|
if (SHIFT_DAY.equals(shiftType)) {
|
|
|
|
|
return "白班";
|
|
|
|
|
@ -659,23 +975,60 @@ public class InjectionOeeServiceImpl implements IInjectionOeeService {
|
|
|
|
|
return "全部班次";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private DateRange resolveTotalRange(String beginTimeStr, String endTimeStr, SimpleDateFormat sdf, Date todayDate) {
|
|
|
|
|
Date beginDate = parseDay(beginTimeStr, sdf, todayDate);
|
|
|
|
|
Date endDate = parseDay(endTimeStr, sdf, todayDate);
|
|
|
|
|
private DateRange resolveSelectedRange(String beginTimeStr,
|
|
|
|
|
String endTimeStr,
|
|
|
|
|
SimpleDateFormat sdf,
|
|
|
|
|
Date defaultDate,
|
|
|
|
|
Date maxDate) {
|
|
|
|
|
Date beginDate = parseDay(beginTimeStr, sdf, defaultDate);
|
|
|
|
|
Date endDate = parseDay(endTimeStr, sdf, defaultDate);
|
|
|
|
|
if (beginDate.after(endDate)) {
|
|
|
|
|
Date tmp = beginDate;
|
|
|
|
|
beginDate = endDate;
|
|
|
|
|
endDate = tmp;
|
|
|
|
|
}
|
|
|
|
|
if (beginDate.after(todayDate)) {
|
|
|
|
|
return new DateRange(todayDate, todayDate);
|
|
|
|
|
if (beginDate.after(maxDate)) {
|
|
|
|
|
return new DateRange(maxDate, maxDate);
|
|
|
|
|
}
|
|
|
|
|
if (endDate.after(todayDate)) {
|
|
|
|
|
endDate = todayDate;
|
|
|
|
|
if (endDate.after(maxDate)) {
|
|
|
|
|
endDate = maxDate;
|
|
|
|
|
}
|
|
|
|
|
return new DateRange(beginDate, endDate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private DateRange resolveAvailabilityRange(DateRange selectedRange, String availabilityType, Date maxDate) {
|
|
|
|
|
if (AVAILABILITY_WEEK.equals(availabilityType)) {
|
|
|
|
|
Date weekBeginDate = beginOfNaturalWeek(selectedRange.endDate);
|
|
|
|
|
Date weekEndDate = endOfNaturalWeek(selectedRange.endDate);
|
|
|
|
|
if (weekEndDate.after(maxDate)) {
|
|
|
|
|
// 为什么截到当前日期:自然周未结束时,不能把未来班次计入计划时间分母。
|
|
|
|
|
weekEndDate = maxDate;
|
|
|
|
|
}
|
|
|
|
|
return new DateRange(weekBeginDate, weekEndDate);
|
|
|
|
|
}
|
|
|
|
|
if (AVAILABILITY_TOTAL.equals(availabilityType)) {
|
|
|
|
|
return selectedRange;
|
|
|
|
|
}
|
|
|
|
|
// 为什么用页面结束日:日利用率是用户选择日期的生产日,不再固定取服务器“今天”。
|
|
|
|
|
return new DateRange(selectedRange.endDate, selectedRange.endDate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Date beginOfNaturalWeek(Date date) {
|
|
|
|
|
Calendar cal = Calendar.getInstance();
|
|
|
|
|
cal.setTime(truncateToDay(date));
|
|
|
|
|
int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
|
|
|
|
|
int mondayOffset = (dayOfWeek + 5) % 7;
|
|
|
|
|
cal.add(Calendar.DATE, -mondayOffset);
|
|
|
|
|
return cal.getTime();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Date endOfNaturalWeek(Date date) {
|
|
|
|
|
Calendar cal = Calendar.getInstance();
|
|
|
|
|
cal.setTime(beginOfNaturalWeek(date));
|
|
|
|
|
cal.add(Calendar.DATE, 6);
|
|
|
|
|
return cal.getTime();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Date parseDay(String value, SimpleDateFormat sdf, Date defaultDate) {
|
|
|
|
|
if (!StringUtils.hasText(value)) {
|
|
|
|
|
return defaultDate;
|
|
|
|
|
@ -802,6 +1155,45 @@ public class InjectionOeeServiceImpl implements IInjectionOeeService {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static class QueryRange {
|
|
|
|
|
private final Date beginTime;
|
|
|
|
|
private Date endTime;
|
|
|
|
|
|
|
|
|
|
private QueryRange(Date beginTime, Date endTime) {
|
|
|
|
|
this.beginTime = beginTime;
|
|
|
|
|
this.endTime = endTime;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Date getBeginTime() {
|
|
|
|
|
return beginTime;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static class MetricCache {
|
|
|
|
|
private final Map<String, List<ParamRawPoint>> runRawPointsByDevice = new HashMap<>();
|
|
|
|
|
private final Map<String, List<ParamRawPoint>> shotRawPointsByDevice = new HashMap<>();
|
|
|
|
|
private final Map<String, BigDecimal> cavitiesByDevice = new HashMap<>();
|
|
|
|
|
private final Map<String, Map<String, Object>> cycleStatsByDevice = new HashMap<>();
|
|
|
|
|
|
|
|
|
|
private List<ParamRawPoint> getRunRawPoints(String deviceCode) {
|
|
|
|
|
return getRawPoints(runRawPointsByDevice, deviceCode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private List<ParamRawPoint> getShotRawPoints(String deviceCode) {
|
|
|
|
|
return getRawPoints(shotRawPointsByDevice, deviceCode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private List<ParamRawPoint> getRawPoints(Map<String, List<ParamRawPoint>> rawPointsByDevice,
|
|
|
|
|
String deviceCode) {
|
|
|
|
|
List<ParamRawPoint> rawPoints = rawPointsByDevice.get(deviceCode);
|
|
|
|
|
return rawPoints == null ? Collections.emptyList() : rawPoints;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Map<String, Object> getCycleStats(String deviceCode) {
|
|
|
|
|
return cycleStatsByDevice.get(deviceCode);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static class CycleResult {
|
|
|
|
|
private BigDecimal cycleSeconds = BigDecimal.ZERO;
|
|
|
|
|
private boolean estimated = true;
|
|
|
|
|
|