|
|
|
|
@@ -0,0 +1,274 @@
|
|
|
|
|
package com.klp.pocket.galvanize1.service.impl;
|
|
|
|
|
|
|
|
|
|
import com.klp.common.utils.StringUtils;
|
|
|
|
|
import com.klp.pocket.acid.domain.vo.AcidOeeCoilInfoByDateVo;
|
|
|
|
|
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.Klptcm1ProStoppageVo;
|
|
|
|
|
import com.klp.pocket.galvanize1.domain.bo.ProStoppageBo;
|
|
|
|
|
import com.klp.pocket.galvanize1.domain.vo.ProStoppageVo;
|
|
|
|
|
import com.klp.pocket.galvanize1.mapper.GalvanizeOeeMasterMapper;
|
|
|
|
|
import com.klp.pocket.galvanize1.service.IGalvanizeOeeService;
|
|
|
|
|
import com.klp.pocket.galvanize1.service.IProStoppageService;
|
|
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
|
|
|
|
import java.math.BigDecimal;
|
|
|
|
|
import java.math.RoundingMode;
|
|
|
|
|
import java.text.SimpleDateFormat;
|
|
|
|
|
import java.util.*;
|
|
|
|
|
|
|
|
|
|
@Slf4j
|
|
|
|
|
@RequiredArgsConstructor
|
|
|
|
|
@Service
|
|
|
|
|
public class GalvanizeOeeServiceImpl implements IGalvanizeOeeService {
|
|
|
|
|
|
|
|
|
|
private static final Set<String> CD_SERIES = new HashSet<>(Arrays.asList("C+", "C", "C-", "D+", "D", "D-"));
|
|
|
|
|
|
|
|
|
|
private final GalvanizeOeeMasterMapper galvanizeOeeMasterMapper;
|
|
|
|
|
private final IProStoppageService proStoppageService;
|
|
|
|
|
|
|
|
|
|
@Value("${oee.galvanize1.coil-create-by:duxinkuguan}")
|
|
|
|
|
private String galvanizeCreateBy;
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public List<AcidOeeDailySummaryVo> getDailySummary(String startDate, String endDate) {
|
|
|
|
|
List<AcidOeeDailySummaryVo> summaries = galvanizeOeeMasterMapper.selectDailySummary(startDate, endDate, galvanizeCreateBy);
|
|
|
|
|
if (summaries == null || summaries.isEmpty()) return Collections.emptyList();
|
|
|
|
|
|
|
|
|
|
Map<String, Long> downtimeByDate = aggregateDowntimeByDate(startDate, endDate);
|
|
|
|
|
Map<String, List<CoilInfo>> coilInfoByDate = getCoilNosByDate(startDate, endDate);
|
|
|
|
|
|
|
|
|
|
List<BigDecimal> dailyCycles = new ArrayList<>();
|
|
|
|
|
for (AcidOeeDailySummaryVo s : summaries) {
|
|
|
|
|
s.setLineId("DX1");
|
|
|
|
|
s.setLineName("镀锌一线");
|
|
|
|
|
Long downtime = downtimeByDate.getOrDefault(s.getStatDate(), 0L);
|
|
|
|
|
s.setDowntimeMin(downtime);
|
|
|
|
|
Long loading = s.getLoadingTimeMin() == null ? 0L : s.getLoadingTimeMin();
|
|
|
|
|
Long run = Math.max(0, loading - downtime);
|
|
|
|
|
s.setRunTimeMin(run);
|
|
|
|
|
BigDecimal ton = s.getTotalOutputTon();
|
|
|
|
|
if (run > 0 && ton != null && ton.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
|
|
dailyCycles.add(BigDecimal.valueOf(run).divide(ton, 6, RoundingMode.HALF_UP));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
dailyCycles.sort(BigDecimal::compareTo);
|
|
|
|
|
BigDecimal ideal = applyEightyPercent(median(dailyCycles));
|
|
|
|
|
|
|
|
|
|
for (AcidOeeDailySummaryVo s : summaries) {
|
|
|
|
|
if (ideal != null) s.setIdealCycleTimeMinPerTon(ideal);
|
|
|
|
|
List<CoilInfo> coilInfos = coilInfoByDate.get(s.getStatDate());
|
|
|
|
|
if (coilInfos != null) {
|
|
|
|
|
calculateQualityOutput(s, coilInfos);
|
|
|
|
|
} else {
|
|
|
|
|
s.setGoodOutputTon(BigDecimal.ZERO);
|
|
|
|
|
s.setGoodOutputCoil(0L);
|
|
|
|
|
s.setQualifiedOutputTon(BigDecimal.ZERO);
|
|
|
|
|
s.setQualifiedOutputCoil(0L);
|
|
|
|
|
s.setAbOutputTon(BigDecimal.ZERO);
|
|
|
|
|
s.setAbOutputCoil(0L);
|
|
|
|
|
s.setDefectOutputTon(BigDecimal.ZERO);
|
|
|
|
|
s.setDefectOutputCoil(0L);
|
|
|
|
|
s.setPendingOutputTon(s.getTotalOutputTon());
|
|
|
|
|
s.setPendingOutputCoil(s.getTotalOutputCoil());
|
|
|
|
|
}
|
|
|
|
|
calculateDerivedMetrics(s);
|
|
|
|
|
}
|
|
|
|
|
return summaries;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public List<Klptcm1ProStoppageVo> getStoppageEvents(String startDate, String endDate) {
|
|
|
|
|
ProStoppageBo bo = new ProStoppageBo();
|
|
|
|
|
bo.setStartDate(parseDate(startDate));
|
|
|
|
|
bo.setEndDate(parseDate(endDate));
|
|
|
|
|
List<ProStoppageVo> list = proStoppageService.queryList(bo);
|
|
|
|
|
List<Klptcm1ProStoppageVo> out = new ArrayList<>();
|
|
|
|
|
for (ProStoppageVo p : list) {
|
|
|
|
|
Klptcm1ProStoppageVo v = new Klptcm1ProStoppageVo();
|
|
|
|
|
v.setStopid(p.getStopid());
|
|
|
|
|
v.setShift(p.getShift());
|
|
|
|
|
v.setCrew(p.getCrew());
|
|
|
|
|
v.setArea(p.getArea());
|
|
|
|
|
v.setUnit(p.getUnit());
|
|
|
|
|
v.setSeton(p.getSeton());
|
|
|
|
|
v.setStartDate(p.getStartDate());
|
|
|
|
|
v.setEndDate(p.getEndDate());
|
|
|
|
|
v.setRemark(p.getRemark());
|
|
|
|
|
v.setStopType(p.getStopType());
|
|
|
|
|
long durationSec = p.getDuration() == null ? 0L : Math.round(p.getDuration());
|
|
|
|
|
v.setDuration(durationSec);
|
|
|
|
|
out.add(v);
|
|
|
|
|
}
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public List<AcidOeeLoss7Vo> getLoss7Summary(String startDate, String endDate) {
|
|
|
|
|
List<Klptcm1ProStoppageVo> events = getStoppageEvents(startDate, endDate);
|
|
|
|
|
if (events.isEmpty()) return Collections.emptyList();
|
|
|
|
|
Map<String, long[]> m = new HashMap<>();
|
|
|
|
|
long total = 0L;
|
|
|
|
|
for (Klptcm1ProStoppageVo e : events) {
|
|
|
|
|
String k = StringUtils.isBlank(e.getStopType()) ? "未分类" : e.getStopType();
|
|
|
|
|
long min = Math.max(1, (e.getDuration() == null ? 0L : e.getDuration()) / 60);
|
|
|
|
|
if (min <= 0) continue;
|
|
|
|
|
m.computeIfAbsent(k, x -> new long[2]);
|
|
|
|
|
m.get(k)[0] += min;
|
|
|
|
|
m.get(k)[1] += 1;
|
|
|
|
|
total += min;
|
|
|
|
|
}
|
|
|
|
|
List<AcidOeeLoss7Vo> out = new ArrayList<>();
|
|
|
|
|
for (Map.Entry<String, long[]> en : m.entrySet()) {
|
|
|
|
|
AcidOeeLoss7Vo vo = new AcidOeeLoss7Vo();
|
|
|
|
|
vo.setLossCategoryCode(en.getKey());
|
|
|
|
|
vo.setLossCategoryName(en.getKey());
|
|
|
|
|
vo.setLossTimeMin(en.getValue()[0]);
|
|
|
|
|
vo.setCount((int) en.getValue()[1]);
|
|
|
|
|
vo.setAvgDurationMin(BigDecimal.valueOf(en.getValue()[0]).divide(BigDecimal.valueOf(Math.max(1, en.getValue()[1])), 2, RoundingMode.HALF_UP));
|
|
|
|
|
vo.setLossTimeRate(total == 0 ? BigDecimal.ZERO : BigDecimal.valueOf(en.getValue()[0]).divide(BigDecimal.valueOf(total), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));
|
|
|
|
|
out.add(vo);
|
|
|
|
|
}
|
|
|
|
|
out.sort(Comparator.comparingLong(AcidOeeLoss7Vo::getLossTimeMin).reversed());
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public AcidOeeIdealCycleVo getIdealCycle(String startDate, String endDate) {
|
|
|
|
|
List<AcidOeeDailySummaryVo> daily = getDailySummary(startDate, endDate);
|
|
|
|
|
AcidOeeIdealCycleVo rsp = new AcidOeeIdealCycleVo();
|
|
|
|
|
rsp.setLineId("DX1");
|
|
|
|
|
rsp.setLineName("镀锌一线");
|
|
|
|
|
rsp.setStartDate(startDate);
|
|
|
|
|
rsp.setEndDate(endDate);
|
|
|
|
|
if (daily.isEmpty()) {
|
|
|
|
|
rsp.setDailyComparePoints(Collections.emptyList());
|
|
|
|
|
rsp.setSampleDays(0);
|
|
|
|
|
return rsp;
|
|
|
|
|
}
|
|
|
|
|
List<BigDecimal> dailyCycles = new ArrayList<>();
|
|
|
|
|
List<AcidOeeIdealCycleVo.DailyComparePointVo> points = new ArrayList<>();
|
|
|
|
|
for (AcidOeeDailySummaryVo d : daily) {
|
|
|
|
|
if (d.getRunTimeMin() == null || d.getRunTimeMin() <= 0 || d.getTotalOutputTon() == null || d.getTotalOutputTon().compareTo(BigDecimal.ZERO) <= 0) continue;
|
|
|
|
|
BigDecimal c = BigDecimal.valueOf(d.getRunTimeMin()).divide(d.getTotalOutputTon(), 6, RoundingMode.HALF_UP);
|
|
|
|
|
dailyCycles.add(c);
|
|
|
|
|
}
|
|
|
|
|
dailyCycles.sort(BigDecimal::compareTo);
|
|
|
|
|
BigDecimal med = median(dailyCycles);
|
|
|
|
|
BigDecimal ideal = applyEightyPercent(med);
|
|
|
|
|
for (AcidOeeDailySummaryVo d : daily) {
|
|
|
|
|
if (ideal == null || d.getTotalOutputTon() == null || d.getRunTimeMin() == null) continue;
|
|
|
|
|
AcidOeeIdealCycleVo.DailyComparePointVo p = new AcidOeeIdealCycleVo.DailyComparePointVo();
|
|
|
|
|
p.setStatDate(d.getStatDate());
|
|
|
|
|
p.setActualRunTimeMin(d.getRunTimeMin());
|
|
|
|
|
p.setTheoreticalTimeMin(ideal.multiply(d.getTotalOutputTon()));
|
|
|
|
|
points.add(p);
|
|
|
|
|
}
|
|
|
|
|
rsp.setIdealCycleTimeMinPerTon(ideal);
|
|
|
|
|
rsp.setMedianCycleTimeMinPerTon(med);
|
|
|
|
|
rsp.setSampleDays(daily.size());
|
|
|
|
|
rsp.setDailyComparePoints(points);
|
|
|
|
|
return rsp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Map<String, List<CoilInfo>> getCoilNosByDate(String startDate, String endDate) {
|
|
|
|
|
List<AcidOeeCoilInfoByDateVo> list = galvanizeOeeMasterMapper.selectCoilInfoByDate(startDate, endDate, galvanizeCreateBy);
|
|
|
|
|
Map<String, List<CoilInfo>> map = new HashMap<>();
|
|
|
|
|
for (AcidOeeCoilInfoByDateVo i : list) {
|
|
|
|
|
map.computeIfAbsent(i.getStatDate(), k -> new ArrayList<>()).add(new CoilInfo(i.getWeight(), i.getQualityStatus()));
|
|
|
|
|
}
|
|
|
|
|
return map;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void calculateQualityOutput(AcidOeeDailySummaryVo summary, List<CoilInfo> coilInfos) {
|
|
|
|
|
BigDecimal aTon = BigDecimal.ZERO, bTon = BigDecimal.ZERO, cdTon = BigDecimal.ZERO, oTon = BigDecimal.ZERO;
|
|
|
|
|
long a = 0, b = 0, cd = 0, o = 0;
|
|
|
|
|
for (CoilInfo c : coilInfos) {
|
|
|
|
|
BigDecimal w = c.weight == null ? BigDecimal.ZERO : c.weight;
|
|
|
|
|
String q = StringUtils.upperCase(StringUtils.trim(c.qualityStatus));
|
|
|
|
|
if (StringUtils.isBlank(q) || "O".equals(q)) {
|
|
|
|
|
oTon = oTon.add(w); o++; continue;
|
|
|
|
|
}
|
|
|
|
|
if (q.startsWith("A")) { aTon = aTon.add(w); a++; }
|
|
|
|
|
else if (q.startsWith("B")) { bTon = bTon.add(w); b++; }
|
|
|
|
|
else if (CD_SERIES.contains(q)) { cdTon = cdTon.add(w); cd++; }
|
|
|
|
|
else { oTon = oTon.add(w); o++; }
|
|
|
|
|
}
|
|
|
|
|
summary.setGoodOutputTon(aTon); summary.setGoodOutputCoil(a);
|
|
|
|
|
summary.setQualifiedOutputTon(bTon); summary.setQualifiedOutputCoil(b);
|
|
|
|
|
summary.setAbOutputTon(aTon.add(bTon)); summary.setAbOutputCoil(a + b);
|
|
|
|
|
summary.setDefectOutputTon(cdTon); summary.setDefectOutputCoil(cd);
|
|
|
|
|
summary.setPendingOutputTon(oTon); summary.setPendingOutputCoil(o);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void calculateDerivedMetrics(AcidOeeDailySummaryVo s) {
|
|
|
|
|
long loading = s.getLoadingTimeMin() == null ? 0 : s.getLoadingTimeMin();
|
|
|
|
|
long downtime = s.getDowntimeMin() == null ? 0 : s.getDowntimeMin();
|
|
|
|
|
if (loading > 0) s.setAvailability(BigDecimal.valueOf(loading - downtime).divide(BigDecimal.valueOf(loading), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));
|
|
|
|
|
long run = s.getRunTimeMin() == null ? 0 : s.getRunTimeMin();
|
|
|
|
|
BigDecimal ideal = s.getIdealCycleTimeMinPerTon();
|
|
|
|
|
BigDecimal ton = s.getTotalOutputTon();
|
|
|
|
|
if (run > 0 && ideal != null && ideal.compareTo(BigDecimal.ZERO) > 0 && ton != null && ton.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
|
|
s.setPerformanceTon(ideal.multiply(ton).divide(BigDecimal.valueOf(run), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));
|
|
|
|
|
}
|
|
|
|
|
if (ton != null && ton.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
|
|
BigDecimal a = Optional.ofNullable(s.getGoodOutputTon()).orElse(BigDecimal.ZERO);
|
|
|
|
|
BigDecimal b = Optional.ofNullable(s.getQualifiedOutputTon()).orElse(BigDecimal.ZERO);
|
|
|
|
|
BigDecimal cd = Optional.ofNullable(s.getDefectOutputTon()).orElse(BigDecimal.ZERO);
|
|
|
|
|
BigDecimal o = Optional.ofNullable(s.getPendingOutputTon()).orElse(BigDecimal.ZERO);
|
|
|
|
|
s.setQuality(a.divide(ton, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));
|
|
|
|
|
s.setQualifiedRate(a.add(b).divide(ton, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));
|
|
|
|
|
s.setDefectRate(cd.divide(ton, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));
|
|
|
|
|
s.setPendingRate(o.divide(ton, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));
|
|
|
|
|
}
|
|
|
|
|
if (s.getAvailability() != null && s.getPerformanceTon() != null && s.getQuality() != null) {
|
|
|
|
|
s.setOee(s.getAvailability().multiply(s.getPerformanceTon()).multiply(s.getQuality()).divide(BigDecimal.valueOf(10000), 4, RoundingMode.HALF_UP));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Map<String, Long> aggregateDowntimeByDate(String startDate, String endDate) {
|
|
|
|
|
List<Klptcm1ProStoppageVo> events = getStoppageEvents(startDate, endDate);
|
|
|
|
|
Map<String, Long> map = new HashMap<>();
|
|
|
|
|
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
|
|
for (Klptcm1ProStoppageVo e : events) {
|
|
|
|
|
if (e.getStartDate() == null || e.getDuration() == null || e.getDuration() <= 0) continue;
|
|
|
|
|
long min = Math.max(1, (e.getDuration() + 59) / 60);
|
|
|
|
|
map.merge(df.format(e.getStartDate()), min, Long::sum);
|
|
|
|
|
}
|
|
|
|
|
return map;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
return values.get(n / 2 - 1).add(values.get(n / 2)).divide(BigDecimal.valueOf(2), 6, RoundingMode.HALF_UP);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private BigDecimal applyEightyPercent(BigDecimal v) {
|
|
|
|
|
return v == null ? null : v.multiply(BigDecimal.valueOf(0.7)).setScale(6, RoundingMode.HALF_UP);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Date parseDate(String dateStr) {
|
|
|
|
|
if (StringUtils.isBlank(dateStr)) return null;
|
|
|
|
|
try {
|
|
|
|
|
return new SimpleDateFormat("yyyy-MM-dd").parse(dateStr);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static class CoilInfo {
|
|
|
|
|
final BigDecimal weight;
|
|
|
|
|
final String qualityStatus;
|
|
|
|
|
|
|
|
|
|
CoilInfo(BigDecimal weight, String qualityStatus) {
|
|
|
|
|
this.weight = weight;
|
|
|
|
|
this.qualityStatus = qualityStatus;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|