Răsfoiți Sursa

解决停机统计错误,导致稼动率计算错误的BUG

wukai 2 luni în urmă
părinte
comite
003971f75a

+ 54 - 0
jjt-admin/src/test/java/com/jjt/stop/StopTest.java

@@ -0,0 +1,54 @@
+package com.jjt.stop;
+
+import com.jjt.JjtApplication;
+import com.jjt.biz.domain.TwinDevice;
+import com.jjt.biz.service.ITwinDeviceService;
+import com.jjt.calc.service.ITwinCalcStopService;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import javax.annotation.Resource;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.List;
+
+/**
+ * StopTest$
+ *
+ * @author wukai
+ * @date 2025/3/20 00:48
+ */
+@SpringBootTest(classes = JjtApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+public class StopTest {
+    @Resource
+    ITwinDeviceService deviceService;
+    @Resource
+    ITwinCalcStopService stopService;
+
+    @Test
+    void test() {
+        TwinDevice search = new TwinDevice();
+        //查询所有在线的设备
+        search.setOnline("1");
+        //TODO 临时设置单个设备,上线需取消
+        search.setDeviceId(1L);
+        List<TwinDevice> list = deviceService.selectTwinDeviceList(search);
+
+        String st = "2025-03-16";
+        String ed = "2025-03-16";
+        LocalDate localDate = LocalDate.parse(st);
+        LocalDate endDate = LocalDate.parse(ed);
+        LocalDateTime start = LocalDateTime.of(localDate, LocalTime.MIN).plusHours(7);
+        LocalDateTime end = LocalDateTime.of(endDate.plusDays(1), LocalTime.MIN).plusHours(6);
+        LocalDateTime curr = LocalDateTime.now();
+        if (end.isAfter(curr)) {
+            end = curr.minusHours(1);
+        }
+        do {
+            int i = start.getHour();
+            stopService.process(start, start.plusHours(1), list);
+            start = start.plusHours(1);
+        } while (!start.isAfter(end));
+    }
+}

+ 5 - 6
jjt-admin/src/test/java/com/jjt/task/TaskTest.java

@@ -48,11 +48,10 @@ public class TaskTest {
 //        dayService.day(localDate);
 //        dayService.day(LocalDate.parse("2025-02-23"));
 //        dayService.day(LocalDate.parse("2025-02-24"));
-//        dayService.day(LocalDate.parse("2025-02-25"));
-        for (int i = 1; i < 17; i++) {
+//        dayService.day(LocalDate.parse("2025-03-16"));
+        for (int i = 1; i < 19; i++) {
             String d = "2025-03-" + (i < 10 ? "0" + i : i);
             dayService.day(LocalDate.parse(d));
-
         }
 ////        empCalcService.calc(DateUtils.toDate(localDate));
 //        LocalDateTime ldt = LocalDateTime.of(localDate, LocalTime.MIN);
@@ -70,8 +69,8 @@ public class TaskTest {
     @Test
     public void test() {
         iotService.setToken();
-        String st = "2025-02-23";
-        String ed = "2025-02-26";
+        String st = "2025-03-16";
+        String ed = "2025-03-16";
         LocalDate localDate = LocalDate.parse(st);
         LocalDate endDate = LocalDate.parse(ed);
         LocalDateTime start = LocalDateTime.of(localDate, LocalTime.MIN).plusHours(7);
@@ -85,7 +84,7 @@ public class TaskTest {
             System.err.println(start.toLocalDate().toString() + "\t" + i);
             calcHourService.calc(start.toLocalDate().toString(), i);
             if (i == 6) {
-                dayService.day(start.toLocalDate());
+                dayService.day(start.toLocalDate().minusDays(1));
             }
             start = start.plusHours(1);
         } while (!start.isAfter(end));

+ 37 - 50
jjt-biz/src/main/java/com/jjt/calc/service/impl/TwinCalcAlarmsServiceImpl.java

@@ -129,9 +129,7 @@ public class TwinCalcAlarmsServiceImpl implements ITwinCalcAlarmsService {
         List<TwinCalcAlarms> insertList = new ArrayList<>();
         List<TwinCalcAlarms> updateList = new ArrayList<>();
         List<TwinRecordAlarms> recordList = new ArrayList<>();
-        //需要删除的数据
-        List<Long> delIds = new ArrayList<>();
-        deviceList.forEach(device -> {
+        for (TwinDevice device : deviceList) {
             List<TwinRecordAlarms> list = new ArrayList<>();
             if (oldMap.get(device.getDeviceId()) != null) {
                 oldMap.get(device.getDeviceId()).forEach(old -> {
@@ -147,52 +145,23 @@ public class TwinCalcAlarmsServiceImpl implements ITwinCalcAlarmsService {
                 list.addAll(newMap.get(device.getDeviceId()));
             }
 
-            Map<Integer, List<TwinRecordAlarms>> groupList = list.stream().collect(Collectors.groupingBy(TwinRecordAlarms::getAlarmType));
-            for (Map.Entry<Integer, List<TwinRecordAlarms>> entry : groupList.entrySet()) {
-                List<TwinRecordAlarms> temp = entry.getValue();
-                if (temp.size() == 1) {
-                    //只有一条记录,则直接记录下来
-                    recordList.add(temp.get(0));
-                }
-                TwinRecordAlarms lastAlarms = null;
-                for (int i = 0; i < temp.size(); i++) {
-                    TwinRecordAlarms currAlarms = temp.get(i);
-                    if (lastAlarms == null) {
-                        lastAlarms = currAlarms;
-                    } else {
-                        //1.第一轮,下一条开始时间-上一条结束时间小于 seconds 则两条记录合并
-                        if (currAlarms.getStartTime().getTime() - lastAlarms.getEndTime().getTime() < legalTime) {
-                            lastAlarms.setEndTime(currAlarms.getEndTime());
-                            lastAlarms.setCalcStatus(currAlarms.getCalcStatus());
-                        } else {
-                            //2.判断持续时间,如果大于seconds则记录
-                            if (lastAlarms.getEndTime().getTime() - lastAlarms.getStartTime().getTime() > legalTime) {
-                                recordList.add(lastAlarms);
-                            }
-                            //如果无效数据是未统计完成的记录,则需要删除统计里面的数据
-                            if (StringUtils.isNotEmpty(lastAlarms.getRemark())) {
-                                delIds.add(Long.parseLong(lastAlarms.getRemark()));
-                            }
-                            lastAlarms = currAlarms;
-                        }
-                        if (i + 1 == temp.size()) {
-                            //补一次最后一条记录,如果最后一条记录是统计未完成的,或者持续时间大于seconds的,则补录
-                            if ("1".equals(lastAlarms.getCalcStatus()) || lastAlarms.getEndTime().getTime() - lastAlarms.getStartTime().getTime() > legalTime) {
-                                recordList.add(lastAlarms);
-                            }
-                            //如果无效数据是未统计完成的记录,则需要删除统计里面的数据
-                            if ("1".equals(lastAlarms.getCalcStatus()) && StringUtils.isNotEmpty(lastAlarms.getRemark())) {
-                                delIds.add(Long.parseLong(lastAlarms.getRemark()));
-                            }
-                        }
-                    }
+            //1.先循环一轮,处理记录合并
+            List<TwinRecordAlarms> list1 = mergeList(list, legalTime);
+            List<TwinRecordAlarms> list2 = new ArrayList<>();
+            //2.再循环合并后的记录,删除无效数据
+            for (TwinRecordAlarms alarm : list1) {
+                if ((alarm.getEndTime().getTime() - alarm.getStartTime().getTime()) > legalTime) {
+                    list2.add(alarm);
+
                 }
             }
-        });
-        for (TwinRecordAlarms stop : recordList) {
+            //3.还要循环一轮,进行记录合并
+            recordList.addAll(mergeList(list2, legalTime));
+        }
+        for (TwinRecordAlarms alarm : recordList) {
             TwinCalcAlarms v = new TwinCalcAlarms();
-            BeanUtils.copyProperties(stop, v);
-            if (StringUtils.isNotEmpty(stop.getRemark())) {
+            BeanUtils.copyProperties(alarm, v);
+            if (StringUtils.isNotEmpty(alarm.getRemark())) {
                 v.setCalcStatus("0");
                 v.setRemark("");
                 updateList.add(v);
@@ -208,16 +177,34 @@ public class TwinCalcAlarmsServiceImpl implements ITwinCalcAlarmsService {
             updateList.forEach(mapper::updateTwinCalcAlarms);
             sqlSession.commit();
         }
-        if (delIds.size() > 0) {
-            deleteTwinCalcAlarmsByIds(delIds.toArray(new Long[]{}));
-        }
-
 
         //统计完成删除记录数据,避免记录超长
         alarmsService.deleteTwinRecordAlarms(date, startTime.getHour());
     }
 
     /**
+     * 合并数据记录
+     */
+    private List<TwinRecordAlarms> mergeList(List<TwinRecordAlarms> list, int legalTime) {
+        List<TwinRecordAlarms> result = new ArrayList<>();
+        TwinRecordAlarms lastAlarm = null;
+
+        for (TwinRecordAlarms alarm : list) {
+            if (lastAlarm != null && alarm.getAlarmType().equals(lastAlarm.getAlarmType()) && (alarm.getStartTime().getTime() - lastAlarm.getEndTime().getTime()) < legalTime) {
+                //判断同一个类型,并且下一条记录的开始时间-上一条记录的结束时间 小于数据合法时间
+                lastAlarm.setEndTime(alarm.getEndTime());
+                //需要设置统计未完成状态 为当前记录的
+                lastAlarm.setCalcStatus(alarm.getCalcStatus());
+            } else {
+                result.add(alarm);
+                lastAlarm = alarm;
+            }
+        }
+        return result;
+    }
+
+
+    /**
      * 按开始和结束时间查询数据
      *
      * @param sTime 开始时间

+ 13 - 38
jjt-biz/src/main/java/com/jjt/calc/service/impl/TwinCalcDayServiceImpl.java

@@ -31,7 +31,6 @@ import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.BiConsumer;
 import java.util.stream.Collectors;
 
@@ -159,8 +158,6 @@ public class TwinCalcDayServiceImpl implements ITwinCalcDayService {
             day.calcHours(hours);
             //计算停机数据
             List<TwinCalcStop> stops = stopList.stream().filter(s -> s.getDeviceId().equals(entry.getKey())).collect(Collectors.toList());
-            AtomicLong closeA = new AtomicLong(0L);
-            AtomicLong closeB = new AtomicLong(0L);
             //A班停机计数
             BiConsumer<Integer, Integer> updateDayA = (type, count) -> {
                 switch (type) {
@@ -198,7 +195,6 @@ public class TwinCalcDayServiceImpl implements ITwinCalcDayService {
                 boolean isAshift = !stop.getStartTime().after(endTimeA) && !stop.getEndTime().before(startTimeA);
                 Date shiftStart = isAshift ? startTimeA : startTimeB;
                 Date shiftEnd = isAshift ? endTimeA : endTimeB;
-                AtomicLong totalStopTime = isAshift ? closeA : closeB;
                 BiConsumer<Integer, Integer> eventCounter = isAshift ? updateDayA : updateDayB;
 
                 // 调整停机时间
@@ -209,40 +205,33 @@ public class TwinCalcDayServiceImpl implements ITwinCalcDayService {
                     stop.setStartTime(shiftStart);
                 }
 
-                // 计算并更新停机时间和事件计数
-                long stopTime = (stop.getEndTime().getTime() - stop.getStartTime().getTime()) / 1000;
-                updateStopCounters(stopTime, stop.getStopType(), totalStopTime, eventCounter);
+                // 计算并更新停机事件计数
+                eventCounter.accept(stop.getStopType(), 1);
             });
 
             //计算稼动率
             //AB班排班时间,要乘以设备总数
-            long teamTimeA = 12L * 60 * 60;
-            long teamTimeB = 12L * 60 * 60;
-            long totalTeamTime = teamTimeA + teamTimeB;
+            BigDecimal teamTimeA = day.getOpenTimeA().add(day.getCloseTimeA());
+            BigDecimal teamTimeB = day.getOpenTimeB().add(day.getCloseTimeB());
+            BigDecimal totalTeamTime = teamTimeA.add(teamTimeB);
 
-            long openTimeA = teamTimeA - closeA.get();
-            long openTimeB = teamTimeB - closeB.get();
-            long totalOpenTime = openTimeA + openTimeB;
-            BigDecimal ea = BigDecimal.valueOf(openTimeA).divide(BigDecimal.valueOf(teamTimeA), 4, RoundingMode.HALF_UP);
-            BigDecimal eb = BigDecimal.valueOf(openTimeB).divide(BigDecimal.valueOf(teamTimeB), 4, RoundingMode.HALF_UP);
-            BigDecimal ee = BigDecimal.valueOf(totalOpenTime).divide(BigDecimal.valueOf(totalTeamTime), 4, RoundingMode.HALF_UP);
-            day.setOpenTimeA(BigDecimal.valueOf(openTimeA));
-            day.setOpenTimeB(BigDecimal.valueOf(openTimeB));
-            day.setCloseTimeA(BigDecimal.valueOf(closeA.get()));
-            day.setCloseTimeB(BigDecimal.valueOf(closeB.get()));
+            BigDecimal totalOpenTime = day.getOpenTimeA().add(day.getOpenTimeB());
+            BigDecimal ea = day.getOpenTimeA().divide(teamTimeA, 4, RoundingMode.HALF_UP);
+            BigDecimal eb = day.getOpenTimeB().divide(teamTimeB, 4, RoundingMode.HALF_UP);
+            BigDecimal ee = totalOpenTime.divide(totalTeamTime, 4, RoundingMode.HALF_UP);
+            day.setOpenTimeA(day.getOpenTimeA());
+            day.setOpenTimeB(day.getOpenTimeB());
+            day.setCloseTimeA(day.getCloseTimeA());
+            day.setCloseTimeB(day.getCloseTimeB());
             day.setEfficiencyA(ea);
             day.setEfficiencyB(eb);
             day.setEfficiency(ee);
             //如果米长为0,稼动率处理
             //20250317 潘工提的需求,把没有开机的设备也需要计算稼动
             if (day.getLengthA().doubleValue() == 0d) {
-                day.setOpenTimeA(BigDecimal.ZERO);
-                day.setCloseTimeA(BigDecimal.valueOf(teamTimeA));
                 day.setEfficiencyA(BigDecimal.ZERO);
             }
             if (day.getLengthB().doubleValue() == 0d) {
-                day.setOpenTimeB(BigDecimal.ZERO);
-                day.setCloseTimeB(BigDecimal.valueOf(teamTimeB));
                 day.setEfficiencyB(BigDecimal.ZERO);
             }
             if (day.getLength().doubleValue() == 0d) {
@@ -259,20 +248,6 @@ public class TwinCalcDayServiceImpl implements ITwinCalcDayService {
         }
     }
 
-    /**
-     * 用于更新停机时间计数器和事件计数
-     *
-     * @param stopTime            停机时间
-     * @param stopType            停机类型
-     * @param totalTimeCounter    停机总时间
-     * @param eventCounterUpdater 抽象方法
-     */
-    private void updateStopCounters(long stopTime, int stopType, AtomicLong
-            totalTimeCounter, BiConsumer<Integer, Integer> eventCounterUpdater) {
-        totalTimeCounter.addAndGet(stopTime);
-        eventCounterUpdater.accept(stopType, 1);
-    }
-
 
     /**
      * 统计今天的产量数据

+ 42 - 53
jjt-biz/src/main/java/com/jjt/calc/service/impl/TwinCalcStopServiceImpl.java

@@ -20,7 +20,6 @@ import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
 import java.time.LocalDateTime;
-import java.time.ZoneOffset;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -121,32 +120,35 @@ public class TwinCalcStopServiceImpl implements ITwinCalcStopService {
         String config = configService.selectConfigByKey("data.legal.time");
         int legalTime = Integer.parseInt(config) * 1000;
 
-        Date date = Date.from(startTime.toLocalDate().atStartOfDay(ZoneOffset.of("+8")).toInstant());
+        Date date = DateUtils.toDate(startTime.toLocalDate());
         Date startDate = DateUtils.toDate(startTime);
         Date endDate = DateUtils.toDate(endTime);
-        List<TwinCalcStop> insertList = new ArrayList<>();
-        List<TwinCalcStop> updateList = new ArrayList<>();
+
+
         //查询上次未统计完整的数据
         TwinCalcStop calcSearch = new TwinCalcStop();
         calcSearch.setCalcStatus("1");
         List<TwinCalcStop> oldList = selectTwinCalcStopList(calcSearch);
         Map<Long, List<TwinCalcStop>> oldMap = oldList.stream().collect(Collectors.groupingBy(TwinCalcStop::getDeviceId, LinkedHashMap::new, Collectors.toList()));
+
         //查询本次数据
         TwinRecordStop recordSearch = new TwinRecordStop();
         recordSearch.setDataDate(date);
         recordSearch.setHour(startTime.getHour());
         List<TwinRecordStop> newList = stopService.selectTwinRecordStopList(recordSearch);
         Map<Long, List<TwinRecordStop>> newMap = newList.stream().collect(Collectors.groupingBy(TwinRecordStop::getDeviceId, LinkedHashMap::new, Collectors.toList()));
+
         //查询统计数据
         TwinCalcHour hourSearch = new TwinCalcHour();
         hourSearch.setDataDate(date);
         hourSearch.setHour(startTime.getHour());
         List<TwinCalcHour> hourList = calcHourService.selectTwinCalcHourList(hourSearch);
-
+        //处理设备数据
+        List<TwinCalcStop> insertList = new ArrayList<>();
+        List<TwinCalcStop> updateList = new ArrayList<>();
         List<TwinCalcHour> calcStopTimeList = new ArrayList<>();
+
         List<TwinRecordStop> recordList = new ArrayList<>();
-        //需要删除的数据
-        List<Long> delIds = new ArrayList<>();
         for (TwinDevice device : deviceList) {
             //先处理上次未统计完整的数据
             List<TwinRecordStop> list = new ArrayList<>();
@@ -166,50 +168,18 @@ public class TwinCalcStopServiceImpl implements ITwinCalcStopService {
                 list.addAll(newMap.get(device.getDeviceId()));
             }
 
-            Map<Integer, List<TwinRecordStop>> groupList = list.stream().collect(Collectors.groupingBy(TwinRecordStop::getStopType));
-            for (Map.Entry<Integer, List<TwinRecordStop>> entry : groupList.entrySet()) {
-                List<TwinRecordStop> temp = entry.getValue();
-                if (temp.size() == 1) {
-                    //只有一条记录,则直接记录下来
-                    recordList.add(temp.get(0));
-                }
-                TwinRecordStop lastStop = null;
-                for (int i = 0; i < temp.size(); i++) {
-                    TwinRecordStop currStop = temp.get(i);
-                    if (lastStop == null) {
-                        lastStop = currStop;
-                    } else {
-                        //1.第一轮,下一条开始时间-上一条结束时间小于 seconds 则两条记录合并
-                        if (currStop.getStartTime().getTime() - lastStop.getEndTime().getTime() < legalTime) {
-                            lastStop.setEndTime(currStop.getEndTime());
-                            lastStop.setCalcStatus(currStop.getCalcStatus());
-                        } else {
-                            //2.判断持续时间,如果大于seconds则记录
-                            if (lastStop.getEndTime().getTime() - lastStop.getStartTime().getTime() > legalTime) {
-                                recordList.add(lastStop);
-                            }
-
-                            if (StringUtils.isNotEmpty(lastStop.getRemark())) {
-                                //如果无效数据是未统计完成的记录,则需要删除统计里面的数据
-                                delIds.add(Long.parseLong(lastStop.getRemark()));
-                            }
-
-                            lastStop = currStop;
-                        }
-                        if (i + 1 == temp.size()) {
-                            //补一次最后一条记录,如果最后一条记录是统计未完成的,或者持续时间大于seconds的,则补录
-                            if ("1".equals(lastStop.getCalcStatus()) || lastStop.getEndTime().getTime() - lastStop.getStartTime().getTime() > legalTime) {
-                                recordList.add(lastStop);
-                            }
+            //1.先循环一轮,处理记录合并
+            List<TwinRecordStop> list1 = mergeList(list, legalTime);
+            List<TwinRecordStop> list2 = new ArrayList<>();
+            //2.再循环合并后的记录,删除无效数据
+            for (TwinRecordStop stop : list1) {
+                if ((stop.getEndTime().getTime() - stop.getStartTime().getTime()) > legalTime) {
+                    list2.add(stop);
 
-                            //如果无效数据是未统计完成的记录,则需要删除统计里面的数据
-                            if ("1".equals(lastStop.getCalcStatus()) && StringUtils.isNotEmpty(lastStop.getRemark())) {
-                                delIds.add(Long.parseLong(lastStop.getRemark()));
-                            }
-                        }
-                    }
                 }
             }
+            //3.还要循环一轮,进行记录合并
+            recordList.addAll(mergeList(list2, legalTime));
         }
         for (TwinRecordStop stop : recordList) {
             //2.复制记录
@@ -222,6 +192,7 @@ public class TwinCalcStopServiceImpl implements ITwinCalcStopService {
                 stop.setStartTime(startDate);
             }
             long stopTime = stop.getEndTime().getTime() - stop.getStartTime().getTime();
+
             calcHour.setDeviceId(stop.getDeviceId());
             calcHour.setCloseTime(stopTime);
             calcStopTimeList.add(calcHour);
@@ -244,8 +215,9 @@ public class TwinCalcStopServiceImpl implements ITwinCalcStopService {
             Long closeTime = 0L;
             if (resultMap.get(hour.getDeviceId()) != null) {
                 closeTime = resultMap.get(hour.getDeviceId());
+                closeTime = Math.min(closeTime / 1000, 3600);
             }
-            closeTime = closeTime / 1000;
+
             Long openTime = 3600 - closeTime;
             hour.setCloseTime(closeTime);
             hour.setOpenTime(openTime);
@@ -265,14 +237,31 @@ public class TwinCalcStopServiceImpl implements ITwinCalcStopService {
             sqlSession.commit();
         }
 
-        if (delIds.size() > 0) {
-            deleteTwinCalcStopByIds(delIds.toArray(new Long[]{}));
-        }
-
         //统计完成删除记录数据,避免记录超长
         stopService.deleteTwinRecordStop(date, startTime.getHour());
     }
 
+    /**
+     * 合并数据记录
+     */
+    private List<TwinRecordStop> mergeList(List<TwinRecordStop> list, int legalTime) {
+        List<TwinRecordStop> result = new ArrayList<>();
+        TwinRecordStop lastStop = null;
+
+        for (TwinRecordStop stop : list) {
+            if (lastStop != null && stop.getStopType().equals(lastStop.getStopType()) && (stop.getStartTime().getTime() - lastStop.getEndTime().getTime()) < legalTime) {
+                //判断同一个类型,并且下一条记录的开始时间-上一条记录的结束时间 小于数据合法时间
+                lastStop.setEndTime(stop.getEndTime());
+                //需要设置统计未完成状态 为当前记录的
+                lastStop.setCalcStatus(stop.getCalcStatus());
+            } else {
+                result.add(stop);
+                lastStop = stop;
+            }
+        }
+        return result;
+    }
+
 
     /**
      * 按开始和结束时间查询数据