package com.pwt;

import cn.hutool.core.thread.NamedThreadFactory;
import cn.hutool.core.thread.ThreadUtil;
import com.pwt.cloud.params.ChangeFinishParam;
import com.pwt.cloud.utils.CloudUtil;
import com.pwt.common.utils.spring.SpringUtils;
import com.pwt.netty.BusinessException;
import com.pwt.netty.ChargerServer;
import com.pwt.netty.MessageSender;
import com.pwt.netty.Vehicle2Server;
import com.pwt.netty.common.StringUtils;
import com.pwt.netty.common.charger.ResponseMessage;
import com.pwt.netty.common.charger.client.ChargerChargeStatusData;
import com.pwt.netty.common.charger.client.ClientResponseData;
import com.pwt.netty.common.constants.ExchangeWorkState;
import com.pwt.netty.common.plc.PlcCommand;
import com.pwt.netty.common.vehicle2.client.HeartBeatRequest;
import com.pwt.netty.common.vehicle2.client.PowerSendRequest;
import com.pwt.netty.handler.ChargerServerHandler;
import com.pwt.netty.message2.*;
import com.pwt.system.service.IPwtConfigService;
import com.pwt.web.domain.PwtChangeBatteryRecord;
import com.pwt.web.domain.PwtHolderBatteryStateCache;
import com.pwt.web.domain.PwtVehicle;
import com.pwt.web.service.IPwtChangeBatteryRecordService;
import com.pwt.web.service.IPwtVehicleService;
import lombok.Data;
import lombok.Getter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

import static com.pwt.Globals.*;
import static com.pwt.netty.MessageSender.*;
import static com.pwt.netty.common.constants.Constants.*;
import static com.pwt.netty.handler.ChargerServerHandler.*;

@Service
@Slf4j
public class StationService {

    private ChargerServer chargerServer;

    private IPwtChangeBatteryRecordService changeBatteryRecordService;

    @Autowired
    public void setChargerServer(ChargerServer chargerServer) {
        this.chargerServer = chargerServer;
    }

    @Autowired
    public void setChangeBatteryRecordService(IPwtChangeBatteryRecordService changeBatteryRecordService) {
        this.changeBatteryRecordService = changeBatteryRecordService;
    }

    // T 状态，排序筛选
    static class Temp {
        public String gunId;
        public int status;
        public int SOC;
        // 充电次数
        public int times;
        // 仓位号
        public int holder;

        public String batterNo;

        public int ratedCapacity;

        public int getStatusWeight() {
            switch (status) {
                // 待机，停止为2
                case 2:
                case 0:
                    return 2;
                // 充电中为1
                case 1:
                    return 1;
                default:
                    return 0;
            }
        }
    }

    private final Lock lock = new ReentrantLock();

    private final Condition rfidCondition = lock.newCondition();


    private final Condition powerDataCond = lock.newCondition();

    public volatile static int GET_SOC = 0;

    private IPwtVehicleService vehicleService;

    /**
     * 0 初始化 1 解锁 2 上锁
     */
    private volatile int lockStatus = 0;


    @Autowired
    public void setVehicleService(IPwtVehicleService vehicleService) {
        this.vehicleService = vehicleService;
    }


    // 0空闲中 1 已扫码 2 已获取RFID 3 换电中
    private final AtomicInteger exchangeState = new AtomicInteger(ExchangeWorkState.IDLE.getValue());
    private volatile String currentRfid;
    private volatile PwtVehicle currentVehicle;

    private volatile PowerSendRequest powerData;

    private volatile int nextGetHolderIndex = -1;

    // 是否在换电中
    private volatile boolean isExchangeState = false;

    //endregion

    public int getExchangeState() {
        return exchangeState.get();
    }

    @Data
    @ToString
    static class BatteryInfo2 {
        private String batteryNo;
        private int powerCap;
        private int soc;
        private int holderNo;
    }

    public void startExchange() {
        if (this.future != null) {
            return;
        }

        if (!exchangeState.compareAndSet(ExchangeWorkState.IDLE.getValue(), ExchangeWorkState.SCANNED_CODE.getValue())) {
            return;
        }
        Future<?> f = Executor.submit(new ExchangeTask());
        Globals.exchangeFuture = f;
        this.future = f;
    }

    private void resetState() {
        future = null;
        this.powerData = null;
        this.nextGetHolderIndex = -1;
        currentRfid = null;
        currentVehicle = null;
        lockStatus = 0;
        this.holderInfo = null;
        refreshHolderInfo();
        VehicleInfo vehicleInfo = new VehicleInfo();
        uiData.setVehicleInfo(vehicleInfo);
        VehicleState vehicleState = new VehicleState();
        uiData.setVehicleState(vehicleState);
        Map<String, Object> data = new HashMap<>();
        data.put(MSG_VEHICLE_INFO, vehicleInfo);
        data.put(MSG_VEHICLE_FLOW_STATE, vehicleState);
        MessageSender.send(data);

    }

    public void rfid(String rfid) {
        if (rfid == null || (currentRfid != null && currentRfid.equals(rfid))) {
            return;
        }
        if (!exchangeState.compareAndSet(ExchangeWorkState.SCANNED_CODE.getValue(), ExchangeWorkState.RFID.getValue())) {
            return;
        }
        if (!lock.tryLock()) {
            return;
        }
        try {
            currentRfid = rfid;
            rfidCondition.signal();
        } finally {
            lock.unlock();
        }
    }


    private static final ExecutorService Executor = Executors.newFixedThreadPool(1, new NamedThreadFactory("exchangeWorker", true));
    private static final ScheduledExecutorService PUSH_EXECUTOR = Executors.newScheduledThreadPool(1, new NamedThreadFactory("refreshDataWorker", true));

    //region 工作任务

    //endregion
    public PwtVehicle getCurrentVehicle() {
        return this.currentVehicle;
    }

    // region 车辆控制器 调用

    public void setVehicleLockState(HeartBeatRequest vehicleState) {

        if (exchangeState.get() < ExchangeWorkState.RFID.getValue()) {
            return;
        }

        final int oldStatus = lockStatus;
        if (vehicleState.getLockStatus() != oldStatus) {
            this.lockStatus = vehicleState.getLockStatus();
            log.info("[station]:电池锁:{}", lockStatus);
            Globals.plcClient.sendCommand(PlcCommand.W_Lock, lockStatus == 1);
        }

        if (this.currentVehicle != null) {
            VehicleInfo info = new VehicleInfo();
            info.setLockState(lockStatus);
            info.setLicensePlate(this.currentVehicle.getVehicleNo());
            uiData.setVehicleInfo(info);
            MessageSender.send(MSG_VEHICLE_INFO, info);
        }
    }

    public void setVehiclePowerData(PowerSendRequest powerData) {

        if (exchangeState.get() != ExchangeWorkState.RFID.getValue()) {
            return;
        }

        // 如果开始了换电流程，且已经获取了度电数据
        if (this.powerData != null) {
            return;
        }
        if (!lock.tryLock()) {
            return;
        }
        try {
            this.powerData = powerData;
            powerDataCond.signal();
        } finally {
            lock.unlock();
        }
    }

    public boolean isCurrentVin(String vin) {
        if (StringUtils.isEmpty(vin)) {
            return false;
        }
        if (this.currentVehicle == null) {
            return false;
        }
        return vin.equals(this.currentVehicle.getVin());
    }

    // endregion

    private void changeVehicleState(int state) {
        VehicleState vehicleState = new VehicleState();
        vehicleState.setValue(state);
        uiData.setVehicleState(vehicleState);
        MessageSender.send(MSG_VEHICLE_FLOW_STATE, vehicleState);
    }

    public void setNextGetHolderIndex(int getIndex) {
        this.nextGetHolderIndex = getIndex;
        log.info("设置下一次取电：{}:, {}", getIndex, this.nextGetHolderIndex);
    }

    private Future future;

    /**
     * 换仓
     *
     * @param source 取电池仓
     * @param target 放电池仓
     */
    public void moveBattery(int source, int target, boolean isAuto) throws BusinessException {
        if (this.future != null) {
            if (exchangeState.get() == ExchangeWorkState.MOVING.getValue()) {
                throw new BusinessException("正在换仓中");
            } else {
                throw new BusinessException("正在换电中");
            }
        }
        if (!exchangeState.compareAndSet(ExchangeWorkState.IDLE.getValue(), ExchangeWorkState.MOVING.getValue())) {
            if (exchangeState.get() == ExchangeWorkState.MOVING.getValue()) {
                throw new BusinessException("正在换仓中");
            } else {
                throw new BusinessException("正在换电中");
            }
        }

        String gunId;
        try {
            // 换仓时 source必须有电池，target必须没有电池
            boolean[] holderState = plcClient.getHolderState();
            if (holderState == null || holderState.length == 0) {
                throw new BusinessException("获取电池仓状态失败");
            }
            if (source > holderState.length || target > holderState.length) {
                throw new BusinessException("获取电池仓状态失败, 编号超出范围");
            }

            if (!holderState[source - 1]) {
                throw new BusinessException("取电池仓没有电池");
            }

            if (holderState[target - 1]) {
                throw new BusinessException("放电池仓有电池");
            }

            gunId = HOLDER_AND_GUN_MAP.get(source);
            if (StringUtils.isEmpty(gunId)) {
                throw new BusinessException("获取仓位枪号失败");
            }
        } catch (Exception e) {
            exchangeState.set(ExchangeWorkState.IDLE.getValue());
            throw e;
        }
        Future<?> f = Executor.submit(new MoveBatteryTask(source, target, gunId, isAuto));
        Globals.exchangeFuture = f;
        if (Globals.tempBatteryPlace != null && GUN_AND_HOLDER_MAP.get(Globals.tempBatteryPlace) == source) {
            // 新换电池临时存放仓位信息
            Globals.tempBatteryPlace = ChargerServerHandler.HOLDER_AND_GUN_MAP.get(target);
        }
    }

    private void sendAssist(int assistIndex) {
        Assist assist = new Assist();

        switch (assistIndex) {
            case 3:
                assist.setText("请司机挂空挡，拉手刹，关闭钥匙");
                assist.setMedia(Globals.ASSIST_MEDIA_BASE_PATH + "3.mp3");
                break;
            case 4:
                assist.setMedia(Globals.ASSIST_MEDIA_BASE_PATH + "4.mp3");
                break;
            case 5:
                assist.setMedia(Globals.ASSIST_MEDIA_BASE_PATH + "5.mp3");
                break;
            case 6:
                assist.setMedia(Globals.ASSIST_MEDIA_BASE_PATH + "6.mp3");
                break;
            case 7:
                break;
            case 8:
                assist.setMedia(Globals.ASSIST_MEDIA_BASE_PATH + "8.mp3");
                break;
            case 9:
                assist.setMedia(Globals.ASSIST_MEDIA_BASE_PATH + "9.mp3");
                break;
            case 10:
                assist.setMedia(Globals.ASSIST_MEDIA_BASE_PATH + "10.mp3");
                break;
            case 11:
                assist.setMedia(Globals.ASSIST_MEDIA_BASE_PATH + "11.mp3");
                break;
            case 12:
                assist.setMedia(Globals.ASSIST_MEDIA_BASE_PATH + "12.mp3");
                break;
            case 13:
                assist.setMedia(Globals.ASSIST_MEDIA_BASE_PATH + "13.mp3");
                break;
            case 14:
                assist.setMedia(Globals.ASSIST_MEDIA_BASE_PATH + "14.mp3");
                break;
            case 15:
                break;
            case 16:
                assist.setText("换电完成，请驶离");
                assist.setMedia(Globals.ASSIST_MEDIA_BASE_PATH + "16.mp3");
                break;
            case 18:
                log.error("正在换电中...");
//                assist.setText("正在换电中");
                assist.setMedia(Globals.ASSIST_MEDIA_BASE_PATH + "18.mp3");
                break;
            case 19:
                assist.setText("换电异常，请不要移动车辆");
                assist.setMedia(Globals.ASSIST_MEDIA_BASE_PATH + "19.mp3");
                break;
            default:
                return;
        }
        MessageSender.send(MSG_ASSIST, assist);
    }

    private class ExchangeTask implements Runnable {
        @Override
        public void run() {
            if (!lock.tryLock()) {
                log.info("[换电]失败: 当前正在换仓中");
                return;
            }
            log.info("[换电]0. 扫码进站");
            changeVehicleState(VEHICLE_FLOW_IN);
            BatteryInfo2 getBatteryInfo2 = null;
            BatteryInfo2 putBatteryInfo2 = null;

            LocalDateTime startDateTime = LocalDateTime.now();
            try {
                long start;
                log.info("[换电]1. 等待rfid");
                if (currentRfid == null) {
                    if (!rfidCondition.await(10, TimeUnit.SECONDS)) {
                        sendAssist(3);
                    }
                }
                if (currentRfid == null) {
                    if (!rfidCondition.await(30, TimeUnit.SECONDS)) {
                        sendAssist(4);
                    }
                }
                log.info("[换电]1.1 校验rfid:{}", currentRfid);
                PwtVehicle vehicle = vehicleService.getVinByRfid(currentRfid);
                if (vehicle == null) {
                    //  T 校验失败，报警,语音提示
                    if (log.isInfoEnabled()) {
                        log.info("[换电]1.1.1 rfid校验失败，rfid:{}", currentRfid);
                    }
                    try {
                        Thread.sleep(2000);
                    }catch (Exception e){
                        throw new RuntimeException(e);
                    }
                    sendAssist(5);
                    return;
                }
                currentVehicle = vehicle;
                // 向云平台发送换电开始
                if (!CloudUtil.changeStart(currentVehicle.getVehicleNo())) {
                    log.error("云平台校验不通过，不允许换电");
                    return;
                }

                log.info("[换电]2. 等待度电数据");
                if (powerData == null) {
                    if (!powerDataCond.await(2, TimeUnit.MINUTES)) {
                        sendAssist(6);
                    }
                }
                final PowerSendRequest pd = powerData;
                log.info("[换电]2. 计算取电仓位");
                int getNum;
                Temp temp;
                String getGunId;

                if (nextGetHolderIndex == -1) {
                    List<Temp> temps = new ArrayList<>();

                    ChargerServerHandler.CHARGER_CHARGE_STATUS_DATA_MAP.forEach((key, value) -> {
                        Integer holderIndex = ChargerServerHandler.GUN_AND_HOLDER_MAP.get(key);
                        if (holderIndex == null) {
                            return;
                        }
                        if (value.getSOC() >= GET_SOC && value.getChargingStatus() != 3 && Globals.holderBatteryStateCacheMap.containsKey(holderIndex)) {
                            PwtHolderBatteryStateCache cache = Globals.holderBatteryStateCacheMap.get(holderIndex);
                            Temp tmp = new Temp();
                            tmp.gunId = key;
                            tmp.holder = holderIndex;
                            tmp.SOC = value.getSOC();
                            tmp.times = value.getChargingTimes();
                            tmp.status = value.getChargingStatus();
                            if (cache != null) {
                                tmp.ratedCapacity = cache.getRatedCapacity();
                            }
                            tmp.batterNo = value.getBatteryNumber();
                            temps.add(tmp);
                        }
                    });

                    if (temps.size() == 0) {
                        log.info("[换电]2.1. 未找到合适的电池!");
                        sendAssist(8);
                        return;
                    }

//                    temps.sort((o1, o2) -> {
//                        if (o1.getStatusWeight() > o2.getStatusWeight()) {
//                            return -1;
//                        }
//
//                        if (o1.getStatusWeight() < o2.getStatusWeight()) {
//                            return 1;
//                        }
//                        if (o1.SOC == o2.SOC) {
//                            return 0;
//                        }
//                        return o1.SOC > o2.SOC ? -1 : 1;
//                    });

//                    // TODO 仓位选择策略
//                    Collections.sort(temps, (o1, o2) -> {
//                        int result = o2.getStatusWeight() - o1.getStatusWeight();
//                        if (o1.getStatusWeight() == o2.getStatusWeight()) {
//                            result = o1.times - o2.times;
//                            if (o1.times == o2.times) {
//                                result = o1.holder - o2.holder;
//                            }
//                        }
//                        return result;
//                    });

                    // TODO 仓位选择策略
                    /**
                     * 优先选取其他仓位电池 1,3仓位电池后取
                     */
                    Collections.sort(temps, (o1, o2) -> {
                        // 先判断是否为1号和3号仓位电池，如果是则放到最后在进行拿取
                        if ((o1.holder == 1 || o1.holder == 3) && !(o2.holder == 1 || o2.holder == 3)) {
                            return 1;
                        } else if (!(o1.holder == 1 || o1.holder == 3) && (o2.holder == 1 || o2.holder == 3)) {
                            return -1;
                        }

                        // 如果电量SOC不同，按照SOC降序排列
                        if (o1.SOC != o2.SOC) {
                            return o2.SOC - o1.SOC;
                        }

                        // 如果状态权重相同，将其他仓位的电池放在1号和3号仓位电池的后面
                        int statusWeightComparison = o2.getStatusWeight() - o1.getStatusWeight();
                        if (statusWeightComparison != 0) {
                            return statusWeightComparison;
                        }

                        // 其他情况下，按照持枪人员编号升序排列
                        return o1.holder - o2.holder;
                    });



//                    // TODO 仓位选择策略
//                    Collections.sort(temps, (o1, o2) -> {
//                        int result = o2.SOC - o1.SOC;
//                        if (o1.SOC == o2.SOC) {
//                            result = o2.getStatusWeight() - o1.getStatusWeight();
//                            if (o1.getStatusWeight() == o2.getStatusWeight()) {
//                                result = o1.holder - o2.holder;
//                            }
//                        }
//                        return result;
//                    });




                    temp = temps.get(0);
                    getGunId = temp.gunId;
                    getNum = ChargerServerHandler.GUN_AND_HOLDER_MAP.get(temp.gunId);

                } else {
                    log.info("[换电]2.1. 人工指定换电仓位: {}", nextGetHolderIndex);

                    // 使用下一次换取的仓位(UI指定的换取仓位)
                    getNum = nextGetHolderIndex;
                    PwtHolderBatteryStateCache cache = Globals.holderBatteryStateCacheMap.get(getNum);
                    getGunId = ChargerServerHandler.HOLDER_AND_GUN_MAP.get(getNum);
                    ChargerChargeStatusData chargerChargeStatusData = CHARGER_CHARGE_STATUS_DATA_MAP.get(getGunId);
                    temp = new Temp();
                    temp.gunId = getGunId;
                    temp.SOC = chargerChargeStatusData.getSOC();
                    temp.status = chargerChargeStatusData.getChargingStatus();
                    temp.batterNo = chargerChargeStatusData.getBatteryNumber();
                    temp.ratedCapacity = cache.getRatedCapacity();
                }

                if (getGunId.equals(ChargerServer.delayStartInfo)) {
                    chargerServer.cancelDelayTask();
                }


                getBatteryInfo2 = new BatteryInfo2();
                getBatteryInfo2.setSoc(temp.SOC);
                getBatteryInfo2.setPowerCap(temp.ratedCapacity);
                getBatteryInfo2.setBatteryNo(temp.batterNo);
                getBatteryInfo2.setHolderNo(getNum);

                log.info("[换电]2.2. 已找到合适的电池!{}", getBatteryInfo2);

                // T 如果放电池仓 充电机坏了，如果处理
                log.info("[换电]3. 计算放电池仓位!");

                // T 放电池仓
                int putNum = -1;
                boolean[] holderState = plcClient.getHolderState();
                if (holderState == null || holderState.length == 0) {
                    log.info("[换电]3.1. 获取仓位状态失败!");
                    return;
                }

                for (int i = 0; i < holderState.length; i++) {
                    if (!holderState[i]) {
                        putNum = i + 1;
                        break;
                    }
                }

                log.info("[换电]3.1. 已找到放电池仓位!{}", putNum);
                if (putNum == -1) {
                    return;
                }
                // TEST
                putBatteryInfo2 = new BatteryInfo2();
                putBatteryInfo2.setBatteryNo(powerData.getBatterySn());
                putBatteryInfo2.setPowerCap(powerData.getBatteryRatedCapacity());
                putBatteryInfo2.setSoc(powerData.getBatterySOC());
                putBatteryInfo2.setHolderNo(putNum);
                //获取当前仓位对应的枪号
                String putGunId = ChargerServerHandler.HOLDER_AND_GUN_MAP.get(putNum);

                if (putGunId.equals(ChargerServer.delayStartInfo)) {
                    chargerServer.cancelDelayTask();
                }

                log.info("[换电]4. 等待换电机器人");
                int count = 0;
                start = System.currentTimeMillis();
                // 等待远方就地
                while (Globals.plcClient.isRemoteEnabled() != 1) {
                    if (Thread.interrupted()) {
                        return;
                    }
                    long d = (System.currentTimeMillis() - start) / 1000;
                    if (d >= 10 && d <= 40 && count == 0) {
                        count++;
                        log.info("[换电]4.1. 换电机器人不在远程状态");
                        sendAssist(9);
                    }
                    if (d >= 40) {
                        log.info("[换电]4.2. 换电机器人响应超时");
                        sendAssist(9);
                        return;
                    }
                    Thread.sleep(1000);
                }

                if (temp.status == 1) {
                    log.info("[换电]5. 换取电池正在充电中");
                    int streamId = (int) (System.currentTimeMillis() / 1000);
                    try {
                        ResponseMessage resp = chargerServer.stopCharge(getGunId.substring(0, getGunId.length() - 1), getGunId, streamId);
                        ClientResponseData data = (ClientResponseData) resp.getMessageData();
                        log.info("[换电]5.1. 发送停止充电成功:{}", data);
                    } catch (Exception e) {
                        log.error("[换电]5.1. 发送停止充电失败", e);
                    }

                    start = System.currentTimeMillis();
                    while (ChargerServerHandler.CHARGER_CHARGE_STATUS_DATA_MAP.get(getGunId).getChargingStatus() != 2) {
                        if (Thread.interrupted()) {
                            return;
                        }
                        if ((System.currentTimeMillis() - start) / 1000 >= 30) {
                            log.info("[换电]5.2. 充电机停止充电超时");
                            return;
                        }
                        Thread.sleep(1000);
                    }
                }

                log.info("[换电]6. 通知车辆定位");
                sendAssist(10);
                start = System.currentTimeMillis();
                // 通知车辆定位
                while (!Globals.plcClient.sendCommand(PlcCommand.W_Position, true)) {
                    if (Thread.interrupted()) {
                        return;
                    }
                    if ((System.currentTimeMillis() - start) / 1000 >= 20) {
                        log.info("[换电]6.1. 通知车辆定位超时");
                        sendAssist(11);
                        return;
                    }
                    Thread.sleep(1000);
                }

                // 延迟等待定位结果
                Thread.sleep(8000);

                start = System.currentTimeMillis();
                // 判断是否就位
                log.info("[换电]7. 等待车辆定位");
                int position;
                while ((position = Globals.plcClient.isVehicleInPosition()) == -1) {
                    if (Thread.interrupted()) {
                        return;
                    }
                    if ((System.currentTimeMillis() - start) / 1000 >= 15) {
                        log.info("[换电]7.1. 车辆定位超时");
                        sendAssist(11);
                        return;
                    }
                    Thread.sleep(1000);
                }

                if (position == 0) {
                    log.info("[换电]7.1. 车辆未就位");
                    sendAssist(11);
                    return;
                }
                changeVehicleState(VEHICLE_FLOW_IN_POSITION);

                log.info("[换电]8. 车辆解锁");
                start = System.currentTimeMillis();
                count = 0;
                while (lockStatus != 1) {
                    if (Thread.interrupted()) {
                        return;
                    }
                    long d = (System.currentTimeMillis() - start) / 1000;
                    if (d >= 10 && d < 60 && count == 0) {
                        count = 1;
                        sendAssist(12);
                    }
                    if ((System.currentTimeMillis() - start) / 1000 >= 60) {
                        sendAssist(13);
                        log.info("[换电]8.1. 车辆解锁超时");
                        return;
                    }
                    Vehicle2Server.unlock(currentVehicle.getVin());
                    Thread.sleep(1000);
                }
                log.info("[换电]9. 设置仓位");
                start = System.currentTimeMillis();
                while (!Globals.plcClient.sendChangeHolderCommand((short) getNum, (short) putNum)) {
                    if (Thread.interrupted()) {
                        return;
                    }
                    long d = (System.currentTimeMillis() - start) / 1000;
                    if (d >= 15) {
                        log.info("[换电]9.1. 设置仓位超时");
                        return;
                    }
                    Thread.sleep(1000);
                }

                Thread.sleep(300);
                holderInfo = new ChangeHolderInfo(getNum, putNum);
                refreshHolderInfo();

                Globals.HOLDER_BATTERY_STATE_INFO.get(getNum);
                log.info("[换电]10. 通知开始换电");
                while (!Globals.plcClient.sendCommand(PlcCommand.W_DoCharge, true)) {
                    long d = (System.currentTimeMillis() - start) / 1000;
                    if (d >= 15) {
                        log.info("[换电]10.1. 通知开始换电超时");
                        return;
                    }
                    Thread.sleep(500);
                }

                log.info("[换电]11. 开始换电");
                exchangeState.set(ExchangeWorkState.EXCHANGING.getValue());
                // 通知车辆站控状态
                sendAssist(14);
//                isExchangeState = true;
//                // 开启换电中语音播报
//                ThreadUtil.execute(() -> {
//                    int count1 = 0; // 记录播报次数
//                    try {
//                        while (isExchangeState && count1 < 2) { // 当isExchangeState为true且播报次数少于2次时进入循环
//                            Thread.sleep(30000); // 间隔30秒
//                                sendAssist(18); // 换电中的语音播报
//                                count1++; // 增加播报次数
//                        }
//                    } catch (InterruptedException e) {
//                        isExchangeState = false; // 无论何时退出循环，都重置换电状态
//                    }
//                });
//                changeVehicleState(VEHICLE_FLOW_CHARGING);

                isExchangeState = true;
                // 开启换电中语音播报
                ThreadUtil.execute(() -> {
                    int count1 = 0; // 记录播报次数
                    try {
                        while (isExchangeState && count1 < 7) { // 当isExchangeState为true且播报次数少于4次时进入循环
                            // 检查锁状态
                            if (lockStatus == 2) {
                                sendAssist(19); // 触发报警操作，例如发送报警通知、记录日志、触发警报器等
                                isExchangeState = false; // 停止循环
                                break; // 跳出循环
                            }

                            Thread.sleep(20000); // 间隔20秒
                            sendAssist(18); // 换电中的语音播报
                            count1++; // 增加播报次数

                            // 监视lockStatus状态，如果状态不为1，则进入异常处理
                            if (lockStatus != 1) {
                                Thread.sleep(7000);
                                sendAssist(19);
                                throw new Exception("车辆锁定状态异常"); // 抛出异常
                            }
                        }
                    } catch (InterruptedException e) {
                        isExchangeState = false; // 无论何时退出循环，都重置换电状态
                    } catch (Exception e) {
                        // 处理异常，例如记录日志或进行其他操作
                        System.out.println("发生异常：" + e.getMessage());
                        isExchangeState = false; // 重置换电状态
                        throw new RuntimeException(e);
                    }
                });
                changeVehicleState(VEHICLE_FLOW_CHARGING);

                // 判断是否换电完成
                int step;
                boolean isSendCharge = false;

                String putChargeId = putGunId.substring(0, getGunId.length() - 1);
                while ((step = Globals.plcClient.getChargeStep()) != 50) {
                    if (Thread.interrupted()) {
                        return;
                    }
                    if (step >= 39 && !isSendCharge) {
                        isSendCharge = true;

                        // 刷新缓存
                        Globals.holderBatteryStateCacheMap.put(putNum,
                                new PwtHolderBatteryStateCache(putBatteryInfo2.getHolderNo(),
                                        putBatteryInfo2.getBatteryNo(), putBatteryInfo2.getPowerCap(), putBatteryInfo2.getSoc()));
                        if (powerData.getBatterySOC() == 100) {
                            break;
                        }
                        if (Globals.HOLDER_CONFIG_TYPE == 2 && (putNum == 1 || putNum == 3)) {
                            // 取另外一把枪 #1仓位是3号枪，
                            //
                            // #3仓位是1号枪
                            // 如果仓位是1，则另外一个枪号就是1
                            String anotherGunId = putGunId.substring(0, putGunId.length() - 1) + (putNum == 1 ? "1" : "3");
                            ChargerChargeStatusData chargerChargeStatusData = CHARGER_CHARGE_STATUS_DATA_MAP.get(anotherGunId);
                            if (chargerChargeStatusData.getChargingStatus() == 1) {
                                ChargerServer.delayStartInfo = new ChargerServer.DelayStartInfo(anotherGunId, putGunId);
                                log.info("[换电]11.1. 延迟启动, delayStartInfo:{}", ChargerServer.delayStartInfo.toString());
                            } else {
                                int streamId = (int) (System.currentTimeMillis() / 1000);
                                try {
                                    ResponseMessage resp = chargerServer.startCharge(putChargeId, putGunId, streamId, false);
                                    ClientResponseData data = (ClientResponseData) resp.getMessageData();
                                    log.info("[换电]11.1. 发送充电成功:{}", data);
                                } catch (Exception e) {
                                    log.error("[换电]11.1. 发送充电失败", e);
                                }
                            }
                        } else {
                            int streamId = (int) (System.currentTimeMillis() / 1000);
                            try {
                                ResponseMessage resp = chargerServer.startCharge(putChargeId, putGunId, streamId, false);
                                ClientResponseData data = (ClientResponseData) resp.getMessageData();
                                log.info("[换电]11.1. 发送充电成功:{}", data);
                            } catch (Exception e) {
                                log.error("[换电]11.1. 发送充电失败", e);
                            }
                        }
                    }
                    if (step >= 47 && lockStatus != 2) {
                        Vehicle2Server.lock(currentVehicle.getVin());
                    }
                    Thread.sleep(500);
                }
                //换电完成
                changeVehicleState(VEHICLE_FLOW_CHARGING_DONE);
                isExchangeState = false;
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                LocalDateTime endDateTime = LocalDateTime.now();
                if (exchangeState.get() == ExchangeWorkState.EXCHANGING.getValue() && getBatteryInfo2 != null && putBatteryInfo2 != null) {
                    PwtChangeBatteryRecord record = new PwtChangeBatteryRecord();
                    record.setVin(currentVehicle.getVin());
                    record.setVehicleNo(currentVehicle.getVehicleNo());
                    // 取电池
                    record.setExchangeBatterySn(getBatteryInfo2.getBatteryNo());
                    double getPower = (long) getBatteryInfo2.powerCap * getBatteryInfo2.getSoc() / 100D;
                    record.setExchangeBatteryPower(BigDecimal.valueOf(getPower));
                    record.setTakeBatteryCompartmentNo(getBatteryInfo2.getHolderNo());

                    // 放电池
                    record.setChangeBatterySn(putBatteryInfo2.getBatteryNo());
                    double putPower = (long) putBatteryInfo2.powerCap * putBatteryInfo2.getSoc() / 100D;
                    record.setChangeBatteryPower(BigDecimal.valueOf(putPower));
                    record.setPlaceBatteryCompartmentNo(putBatteryInfo2.getHolderNo());
                    record.setChangeStartTime(startDateTime);
                    record.setChangeEndTime(endDateTime);
                    //更新缓存信息
                    BatteryInfo2 finalPutBatteryInfo1 = putBatteryInfo2;
                    Optional<BatteryInfo> batteryInfoOptional = uiData.getBatteryInfos().stream().filter(e -> finalPutBatteryInfo1.getHolderNo() == Integer.parseInt(e.getHolderId()))
                            .max(Comparator.comparingLong(BaseData::getTimestamp));
                    if (batteryInfoOptional.isPresent()) {
                        BatteryInfo batteryInfo = batteryInfoOptional.get();
                        batteryInfo.setBatterySn(putBatteryInfo2.batteryNo);
                    }
                    Optional<BatteryStateInfo> batteryStateInfoOptional = uiData.getBatteryStates().stream().filter(e -> finalPutBatteryInfo1.getHolderNo() == Integer.parseInt(e.getHolderId()))
                            .max(Comparator.comparingLong(BaseData::getTimestamp));
                    if (batteryStateInfoOptional.isPresent()) {
                        BatteryStateInfo batteryStateInfo = batteryStateInfoOptional.get();
                        batteryStateInfo.setBatterySn(putBatteryInfo2.batteryNo);
                    }
                    try {
                        // 新换电池临时存放仓位信息
                        Globals.tempBatteryPlace = ChargerServerHandler.HOLDER_AND_GUN_MAP.get(putBatteryInfo2.getHolderNo());

                        // 插入换电记录
                        changeBatteryRecordService.insertPwtChangeBatteryRecord(record);
                        OperationData old = uiData.getOperationData();
                        OperationData newData = new OperationData();

//                        double changePower = record.getExchangeBatteryPower().subtract(record.getChangeBatteryPower()).doubleValue();
                        newData.setTotalExchangeCount(old.getTotalExchangeCount() + 1);
                        newData.setTotalExchangePower(old.getTotalExchangePower());
                        newData.setDays(old.getDays());
                        newData.setTotalRegisterCount(old.getTotalRegisterCount());
                        uiData.setOperationData(newData);
//                        log.info("完成换电记录:{}, power:{}", record, changePower);
                        // 发送云平台换电结束
                        ThreadUtil.execute(() -> {
                            ChangeFinishParam param = new ChangeFinishParam();
                            param.setVehicleCode(record.getVehicleNo());
                            param.setDownBatteryCode(record.getChangeBatterySn());
                            param.setDownBatteryLevel(record.getChangeBatteryPower().doubleValue());
                            param.setUpBatteryCode(record.getExchangeBatterySn());
                            param.setUpBatteryLevel(record.getExchangeBatteryPower().doubleValue());
                            CloudUtil.changeFinish(param);
                        });
                        MessageSender.send(MSG_OPERATION_DATA, newData);
                    } catch (Exception e) {
                        log.warn("插入换电记录失败:{}", record, e);
                    }
                }

                // 超过2级的故障或者强制结束换电
                if (!Thread.interrupted()) {
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        // 中断状态被重新设置
                        throw new RuntimeException(e);
                    }
                }

                if (plcClient.getChargeStep() != 39) {
                    ThreadUtil.execute(() -> {
                        int i = 0;
                        while (i < 3) {
                            try {
                                Thread.sleep(8000);
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                            sendAssist(16); // 换电完成，请司机驶离
                            ++i;
                        }
                    });
                    log.info("当前换电步骤：{}", plcClient.getChargeStep());
                }


                // 结算确认
                Vehicle2Server.settlement(currentVehicle.getVin());

                resetState();
                exchangeState.set(ExchangeWorkState.IDLE.getValue());
                //执行自动换仓策略
                IPwtConfigService pwtConfigService = SpringUtils.getBean(IPwtConfigService.class);
                Boolean isAutoChangeWarehouse = pwtConfigService.getIsAutoChangeWarehouse(1L);
                if (Objects.isNull(isAutoChangeWarehouse) || isAutoChangeWarehouse.equals(Boolean.TRUE)) {
                    log.info("换电完成，即将启动自动换仓策略");
                    ThreadUtil.execute(() -> {
                        int startTime = (int) System.currentTimeMillis() / 1000;
                        while (true) {
                            if (Globals.plcClient.getChargeStep() == 0) {
                                autoChangeWarehouse();
                                break;
                            }
                            int endTime = (int) System.currentTimeMillis() / 1000;
                            if (endTime - startTime >= 60) {
                                log.info("自动换仓超时");
                                break;
                            }
                        }
                    });
                }
                lock.unlock();
            }
        }
    }

    private void autoChangeWarehouse() {
        //判断1、3仓位是否都有电池
        Set<BatteryInfo> batteryInfos = uiData.getBatteryInfos();
        // 如果电池的数量 <> 实际仓位数 - 1
        if (batteryInfos.size() != ACTUAL_HOLDER_COUNT -1) {
            return;
        }
        // 计算空缺的仓位号
        int ableHolder = (ACTUAL_HOLDER_COUNT + 1) * ACTUAL_HOLDER_COUNT / 2;
        for (BatteryInfo batteryInfo : batteryInfos) {
            String holderId = batteryInfo.getHolderId();
            ableHolder -= Integer.parseInt(holderId);
        }
        // 空缺的仓位号是1或者3, 结束换仓
        if (ableHolder == 1 || ableHolder == 3) {
            return;
        }

        //判断1、3是否有冲满的
        List<ChargerGun> collect = uiData.getChargerGuns().stream().filter(e -> "1".equals(e.getHolderId()) || "3".equals(e.getHolderId())).collect(Collectors.toList());
        boolean hasCharged = false;
        for (ChargerGun chargerGun : collect) {
            log.error("执行换仓前的1,3仓状态：{}", chargerGun);
            if (chargerGun.getState().getState() >= 3) {
                hasCharged = true;
            }
        }

        //判断哪个在充，哪个在待机(判断充电中、故障)
        ChargerGun chargerGun = collect.stream().filter(e -> e.getState().getState() ==1).collect(Collectors.toList()).get(0);
        if (Objects.isNull(chargerGun)) {
            return;
        } else {
            if (hasCharged) {
                //延迟启动充电另一块，不调仓
                String anotherGunId = HOLDER_AND_GUN_MAP.get("1".equals(chargerGun.getHolderId()) ? 3 : 1);
                String putGunId = HOLDER_AND_GUN_MAP.get(Integer.parseInt(chargerGun.getHolderId()));
                ChargerServer.delayStartInfo = new ChargerServer.DelayStartInfo(anotherGunId, putGunId);
                chargerServer.newDelayTask(ChargerServer.delayStartInfo);
                log.info("延迟启动, delayStartInfo:{}", ChargerServer.delayStartInfo.toString());
                return;
            }
        }
        try {
            moveBattery(Integer.parseInt(chargerGun.getHolderId()), ableHolder, true);
        } catch (BusinessException e) {
            log.error("自动换仓失败！,{}", e.getMessage());
        }
    }

    private volatile ChangeHolderInfo holderInfo = null;

    public ChangeHolderInfo getHolderInfo() {
        return holderInfo;
    }

    private class MoveBatteryTask implements Runnable {

        private final int source;
        private final int target;
        private final String gunId;

        //是否是自动换仓
        private final boolean isAuto;

        public MoveBatteryTask(int source, int target, String gunId, boolean isAuto) {
            this.source = source;
            this.target = target;
            this.gunId = gunId;
            this.isAuto = isAuto;
        }

        @Override
        public void run() {
            if (!lock.tryLock()) {
                log.info("[换仓]失败: 当前正在换电中");
                return;
            }
            try {
                long start;
                // 手动换仓, 清空下一次换仓位置
                if (source == nextGetHolderIndex) {
                    nextGetHolderIndex = -1;
                }

                log.info("[换仓]0:开始换仓, source:{}, target:{}", source, target);
                holderInfo = new ChangeHolderInfo(source, target);
                refreshHolderInfo();

                // 1 确认充电枪未充电
                ChargerChargeStatusData chargerChargeStatusData = CHARGER_CHARGE_STATUS_DATA_MAP.get(gunId);
                if (chargerChargeStatusData == null) {
                    log.info("[换仓]1:获取充电枪状态失败");
                    return;
                }

                if (gunId.equals(ChargerServer.delayStartInfo)) {
                    chargerServer.cancelDelayTask();
                }

                if (chargerChargeStatusData.getChargingStatus() == 1) {
                    try {
                        ResponseMessage resp = chargerServer.stopCharge(gunId.substring(0, gunId.length() - 1), gunId, 0);
                        ClientResponseData data = (ClientResponseData) resp.getMessageData();
                        log.info("[换仓]1发送停止充电成功, {}", data);
                    } catch (Exception e) {
                        log.error("[换仓]1发送停止充电失败", e);
                        return;
                    }

                    start = System.currentTimeMillis();
                    //                ChargerServerHandler.setChargeStateCond(gunId, getGunCondition);
//                boolean await = getGunCondition.await(1, TimeUnit.MINUTES);
                    while (ChargerServerHandler.CHARGER_CHARGE_STATUS_DATA_MAP.get(gunId).getChargingStatus() != 2) {
                        try {
                            if ((System.currentTimeMillis() - start) / 1000 >= 30) {
                                log.info("[换仓]1.结束:等待充电枪状态超时");
                                return;

                            }
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            throw e;
                        }
                    }
                }

                if (Thread.interrupted()) {
                    log.info("[换仓]1.结束:线程中断");
                    return;
                }

                // 2 发送仓位数据
                if (!plcClient.sendChangeHolderCommand((short) source, (short) target)) {
                    log.info("[换仓]2.结束:发送仓位数据失败");
                    return;
                }

                if (Thread.interrupted()) {
                    log.info("[换仓]2.结束:线程中断");
                    return;
                }

                PwtHolderBatteryStateCache cache = Globals.holderBatteryStateCacheMap.get(source);
                // 3 发送换仓指令
                if (!plcClient.sendCommand(PlcCommand.W_MoveBattery, true)) {
                    log.info("[换仓]3.结束:发送换仓命令失败");
                }

                if (Thread.interrupted()) {
                    log.info("[换仓]3.结束:线程中断");
                    return;
                }

                // 4 等待换仓完成
                // 使用全局变量，防止循环依赖
                final CompletableFuture<Void> f = new CompletableFuture<>();
                Globals.moveBatteryFuture = f;
                try {
                    f.get(5, TimeUnit.MINUTES);
                } catch (ExecutionException e) {
                    //  this future completed exceptionally
                    log.info("[换仓]4.结束:等待换仓错误", e);
                    return;
                } catch (TimeoutException e) {
                    log.info("[换仓]4.结束:等待换仓超时");
                    return;
                } catch (CancellationException e) {
                    log.info("[换仓]4.结束:取消换仓");
                } finally {
                    Globals.moveBatteryFuture = null;
                }

                cache.setHolderId(target);
                Globals.holderBatteryStateCacheMap.put(target, cache);

                log.info("[换仓]4.结束:换仓完成");
                //如果是自动换仓，启动充电
                if (isAuto) {
                    String targetGun = HOLDER_AND_GUN_MAP.get(target);
                    try {
                        log.error("自动换仓结束启动充电：{}",targetGun);
                        chargerServer.startCharge(targetGun.substring(0, targetGun.length() - 1), targetGun, (int) System.currentTimeMillis() / 1000, false);
                    } catch (Exception e) {
                        log.error("自动换仓后充电失败，{}", e.getMessage());
                    }
                }
            } catch (InterruptedException e) {
                log.info("[换仓]结束:线程中断");
            } finally {
                future = null;
                holderInfo = null;
                refreshHolderInfo();
                exchangeState.set(ExchangeWorkState.IDLE.getValue());
                lock.unlock();
            }
        }


    }

    public static class ChangeHolderInfo {
        @Getter
        private final int getNum;

        @Getter
        private final int putNum;

        public ChangeHolderInfo(int getNum, int putNum) {
            this.getNum = getNum;
            this.putNum = putNum;
        }
    }

    public void completeExchange() {
        final Future<?> f = this.future;
        if (f != null) {
            future.cancel(true);
        }
    }

    public void startRefresh() {
        PUSH_EXECUTOR.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {

                OperationData operationData = new OperationData();
                Duration d = Duration.between(Globals.startDateTime, LocalDateTime.now());
                operationData.setDays(d.toDays());
                operationData.setTotalExchangePower(Globals.OP_EXCHANGE_POWER.get());
                operationData.setTotalExchangeCount(Globals.OP_EXCHANGE_COUNT.get());
                operationData.setTotalRegisterCount(Globals.OP_REGISTER_CAR_COUNT.get());
                Globals.uiData.setOperationData(operationData);
                MessageSender.send(MSG_OPERATION_DATA, operationData);
            }
        }, 5, 1, TimeUnit.SECONDS);
    }

    private void refreshHolderInfo() {
        ChangeHolderInfoMessage msg = new ChangeHolderInfoMessage();
        if (this.holderInfo != null) {
            msg.setGetNum(this.holderInfo.getNum);
            msg.setPutNum(this.holderInfo.putNum);
        }
        uiData.setChangeHolderInfo(msg);
        MessageSender.send(MSG_CHANGE_HOLDER_INFO, msg);
    }
}
