LotStepStatSpeedTime.java

package com.mycim.valueobject.wip;

import java.nio.ByteBuffer;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;

public class LotStepStatSpeedTime {
    //STOP CODE
    //只要当前状态为此值,则会把所有数据dump 到sum区,并且停止当前时间
    public static final int STOP_CODE = 0xABABAC;

    //基址
    private final int WAIT_TIME_AREA = 0;

    private final int HOLD_TIME_AREA = 8;

    private final int RUN_TIME_AREA = 16;

    //偏移量
    private final int NEXT_COUNT_AREA = 64;

    private final ByteBuffer bf;

    private List<Long> operTimeArr;

    // //获取最近一次操作的时间
    // private long lastOperTime;

    private final int MAX_LENGTH = 128;

    public LotStepStatSpeedTime(byte[] dataStruct) {
        if (dataStruct != null) {
            bf = ByteBuffer.wrap(dataStruct);
        } else {
            bf = ByteBuffer.allocate(MAX_LENGTH);
        }
        initBaseData();
    }

    private void initBaseData() {
        operTimeArr = Arrays.asList(bf.getLong(WAIT_TIME_AREA), bf.getLong(HOLD_TIME_AREA), bf.getLong(RUN_TIME_AREA));
    }
    //原来的字段统计了 批次总时间,
    //需求#39709 lot query页面显示的W.T,R.T,H.T的值显示为lot在当前step在对应状态下累计的时间
    //按照加字段的方式感觉不妥。 所以采用以下 二进制结构。
    //为了防止发生ABA hold->waiting->hold->waiting->running
    //设计两个区块 时间区 和 统计区。
    //时间区存放 最后一次操作时间。
    //统计区存放 ABA AB BA 中间所消耗的时间
    /**冗余区 以后开发 去记录 banked  active 等时间**/
    //LAST WITETIME |LAST HOLDTIME |LAST RUN TIME   | 冗余
    //0-8           |8-16          |16-24           | 24-64  时间区 可多存放5个状态时间
    //SUM WITETIME  |SUM HOLDTIME  |SUM RUN TIME    | 冗余
    //64-72         |72-80         |80-88           | 88-128 ABA统计区 可多存放5个统计

    /** bytebuffer  模拟指针对数据块操作**/
    //java 默认使用大端
    //a&0xffff0000 取高8 先比较高4位
    //a&0x0000ffff 取低8 如果高4一样比较低4位
    //当前时间 - 状态时间 + 状态变更中间时间损耗
    // now  - a0xffff + (a<<64)&0xffff
    //例如 上面数据结构 为bf 如果hold 是最大值 则说明hold 是最新一次的操作
    // hold在当前步骤损耗时间, now - long(bf[8]) + long(bf[72])
    //如果不是最新操作 则直接取统计区的值

    /**
     * by mingcz
     *
     * @param currentStat 当前操作状态
     * @param beforeStat  特殊计算可能会用到。这里暂时没用,保留
     * @return
     */
    public byte[] createOrUpdateDataStruct(String currentStat, String beforeStat) {
        //防止 ababc c与b 计算时间误差。
        //更新 差值 更新时间
        long now = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
        for (int i = 0; i < operTimeArr.size(); i++) {
            long item = operTimeArr.get(i);
            //排除0
            if (item == 0) {
                continue;
            }
            long speedTime = now - item;
            //写入统计区
            //找到之前保存的时间
            int countArea = NEXT_COUNT_AREA + i * 8;
            long saveCount = bf.getLong(countArea);
            //上个状态时间 到这次变更的时间统计
            bf.putLong(countArea, saveCount + speedTime);
            //时间区写0
            bf.putLong(i * 8, 0L);
        }


        //根据状态写入值
        if (String.valueOf(STOP_CODE).equals(currentStat)) {
            //一些冻结的逻辑
        } else if (LotStatus.isHold(currentStat)) {
            bf.putLong(HOLD_TIME_AREA, now);
        } else if (LotStatus.isRunning(currentStat)) {
            bf.putLong(RUN_TIME_AREA, now);
        } else if (
                LotStatus.isWaiting(currentStat)||
                LotStatus.isRunCardWaitMerge(currentStat)||
                LotStatus.isRunCardWaitFinish(currentStat)) {
            bf.putLong(WAIT_TIME_AREA, now);
        } else if (LotStatus.isRunningHold(currentStat)) {
            bf.putLong(HOLD_TIME_AREA, now);
            bf.putLong(RUN_TIME_AREA, now);
        } else if (LotStatus.isRunCardHold(currentStat)) {
            bf.putLong(HOLD_TIME_AREA, now);
        }else if (LotStatus.isRunCardFinish(currentStat)){
            //当前状态是 runcardfinish 逻辑
        }else if (LotStatus.isTerminated(currentStat)){
            //当前状态是terminated 的逻辑
        }
        // if (LotStatus.isRunning(beforeStat)) {//发生cancel move running 时间依然要统计  后面需求变更临时注释 2021-8-11
        //     bf.putLong(RUN_TIME_AREA, now);
        // }
        return bf.array();
    }


    //解析step 每个操作 花费时间
    public Map<String, String> resolveDataStruct() {
        //兼容之前创建的单子 bytebuffer 为空
        Map<String, String> returnMap = new HashMap();
        returnMap.put("current_step_h_t", "0.0");
        returnMap.put("current_step_w_t", "0.0");
        returnMap.put("current_step_r_t", "0.0");
        if (bf == null) {
            return returnMap;
        }
        //时间取有值的 now -时间区 + 统计区
        returnMap.clear();
        returnMap.put("current_step_h_t",
            calcBtwLastOperTime.apply(bf.getLong(HOLD_TIME_AREA), bf.getLong(HOLD_TIME_AREA + NEXT_COUNT_AREA)));
        returnMap.put("current_step_w_t",
            calcBtwLastOperTime.apply(bf.getLong(WAIT_TIME_AREA), bf.getLong(WAIT_TIME_AREA + NEXT_COUNT_AREA)));
        returnMap.put("current_step_r_t",
            calcBtwLastOperTime.apply(bf.getLong(RUN_TIME_AREA), bf.getLong(RUN_TIME_AREA + NEXT_COUNT_AREA)));
        return returnMap;

    }

    //毫秒格式化 为小时 分钟秒 友善提示
    private final Function<Long, String> formatMilli = (y) -> {
        // 格式 23h 43m 5s
        // long totalSeconds = y / 1000;
        // long currentSeconds = totalSeconds % 60;
        // long totalMinutes = totalSeconds / 60;
        // long currentMintes = totalMinutes % 60;
        // long totalHours = totalMinutes / 60;
        // return String.format("%dh %dm %ds", totalHours, currentMintes, currentSeconds);
        return String.format("%.2f",y==0?0.0:(y+0.0)/(3600000));
    };

    //计算now - 最近操作时间
    private final BiFunction<Long, Long, String> calcBtwLastOperTime = (y, x) -> {
        long nowMilli = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
        Long millis = (y == 0 ? 0 : nowMilli - y) + x;
        return formatMilli.apply(millis);
    };

}