package com.zehong.system.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.util.concurrent.RateLimiter;
import com.zehong.common.constant.Constants;
import com.zehong.common.constant.RoboticArmConstans;
import com.zehong.common.core.domain.AjaxResult;
import com.zehong.common.utils.DateUtils;
import com.zehong.common.utils.StringUtils;
import com.zehong.common.utils.http.HttpUtils;
import com.zehong.system.domain.*;
import com.zehong.system.mapper.*;
import com.zehong.system.service.*;
import com.zehong.system.task.DeviceTaskScheduler;
import com.zehong.system.task.event.ReceivedNbCodeEvent;
import com.zehong.system.task.util.IotDataAndTokenUtil;
import org.quartz.SchedulerException;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 托盘绑定的设备列Service业务层处理
 *
 * @author zehong
 * @date 2025-06-29
 */
@Service
public class PalletDeviceBindingServiceImpl implements IPalletDeviceBindingService
{
    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(PalletDeviceBindingServiceImpl.class);
    @Resource
    private IUploadMesResultHistoryService uploadMesResultHistoryService;
    @Resource
    private PalletDeviceBindingMapper palletDeviceBindingMapper;
    @Resource
    private IPalletDeviceUploadHistoryService palletDeviceUploadHistoryService;

    @Resource
    private IPalletDeviceUploadFailureService iPalletDeviceUploadFailureService;

    @Resource
    private ISysConfigService sysConfigService;
    @Resource
    private TTrayInfoMapper tTrayInfoMapper;

    @Resource
    private TStoreyInfoMapper tStoreyInfoMapper;

    @Resource
    private DeviceTaskScheduler deviceTaskScheduler;

    @Resource
    private RobotArmCommandMapper robotArmCommandMapper;

    @Resource
    private ProductStandardInspectionMapper productStandardInspectionMapper;

    @Resource
    private ApplicationEventPublisher eventPublisher; // 新增事件发布器

    @Resource
    private IotDataAndTokenUtil iotDataAndTokenUtil;
    // 根据 IOT 接口文档的 QPS 限制调整，如果接口说限制 50 QPS，就设 45 留余量
    private static final RateLimiter RATE_LIMITER = RateLimiter.create(2);

    /**
     * 查询托盘绑定的设备列
     *
     * @param palletDeviceBindingId 托盘绑定的设备列ID
     * @return 托盘绑定的设备列
     */
    @Override
    public PalletDeviceBinding selectPalletDeviceBindingById(Long palletDeviceBindingId)
    {
        return palletDeviceBindingMapper.selectPalletDeviceBindingById(palletDeviceBindingId);
    }

    /**
     * 查询托盘绑定的设备列列表
     *
     * @param palletDeviceBinding 托盘绑定的设备列
     * @return 托盘绑定的设备列
     */
    @Override
    public List<PalletDeviceBinding> selectPalletDeviceBindingList(PalletDeviceBinding palletDeviceBinding)
    {
        return palletDeviceBindingMapper.selectPalletDeviceBindingList(palletDeviceBinding);
    }

    /**
     * 获取所有未解除绑定的托盘绑定的设备列
     *
     * @param trayId 托盘ID
     * @return 列表
     */
    @Override
    public List<PalletDeviceBinding> getAllExcludeUnbindingTimeByTrayId(Long trayId) {

        return palletDeviceBindingMapper.getAllExcludeUnbindingTimeByTrayId(trayId);
    }

    /**
     * 根据托盘码查询托盘绑定的设备列
     *
     * @param trayCode 托盘码
     * @return 列表
     */
    @Override
    public List<PalletDeviceBinding> listByTrayCode(String trayCode) {
        return palletDeviceBindingMapper.listByTrayCode(trayCode);
    }

    @Override
    public List<PalletDeviceBinding> listByTrayId(Long trayId) {
        return palletDeviceBindingMapper.listByTrayId(trayId);
    }

    @Override
    public List<PalletDeviceBinding> listByStoreyCode(String storeyCode) {
        return palletDeviceBindingMapper.listByStoreyCode(storeyCode);
    }

    /**
     * 批量更新设备码和未解除绑定时间
     *
     * @param palletDeviceBindingList 列表
     * @return 列表
     */
    @Override
    public void batchUpdateAdAndStatus(List<PalletDeviceBinding> palletDeviceBindingList) {
        palletDeviceBindingMapper.batchUpdateAdAndStatus(palletDeviceBindingList);
    }

    /**
     * 批量更新设备码
     *
     * @param palletDeviceBindingList 列表
     * @return 结果
     */
    @Override
    public void batchUpdateNbCode(List<PalletDeviceBinding> palletDeviceBindingList) {
        palletDeviceBindingMapper.batchUpdateNbCode(palletDeviceBindingList);
    }

    @Override
    public void batchUpdateIotStatus(List<PalletDeviceBinding> palletDeviceBindingList) {
        palletDeviceBindingMapper.batchUpdateIotStatus(palletDeviceBindingList);
    }

    /**
     * 新增托盘绑定的设备列
     *
     * @param palletDeviceBinding 托盘绑定的设备列
     * @return 结果
     */
    @Override
    public int insertPalletDeviceBinding(PalletDeviceBinding palletDeviceBinding)
    {
        palletDeviceBinding.setCreateTime(DateUtils.getNowDate());
        return palletDeviceBindingMapper.insertPalletDeviceBinding(palletDeviceBinding);
    }

    /**
     * 重置所有托盘绑定的设备列
     *
     * @param trayId 托盘ID
     * @return 结果
     */
    @Override
    public AjaxResult resetAll(Long trayId) {

        int i = palletDeviceBindingMapper.countErrorByTrayId(trayId);
        if (i > 0) {
            return AjaxResult.error("有错误设备未解除绑定，不允许重置");
        }

        palletDeviceBindingMapper.resetAll(trayId);

        tTrayInfoMapper.initStatusByTrayId(trayId);

        return AjaxResult.success();
    }

    /**
     * 批量添加托盘绑定的设备列
     *
     * @param palletDeviceBindingList 托盘绑定的设备列
     * @return 结果
     */
    @Override
    public List<PalletDeviceBinding> batchInsertPalletDeviceBinding(List<PalletDeviceBinding> palletDeviceBindingList) {

        check(palletDeviceBindingList);

        // 获取所有不为空的 deviceCode
        List<String> validDeviceCodes = palletDeviceBindingList.stream()
                .filter(item -> StringUtils.isNotBlank(item.getMotherboardCode()))
                .map(PalletDeviceBinding::getMotherboardCode)
                .collect(Collectors.toList());

        int i1 = palletDeviceBindingMapper.checkRepeat(validDeviceCodes);
        if(i1 > 0) {
            throw new RuntimeException("有重复绑定设备码");
        }

        palletDeviceBindingList.forEach(palletDeviceBinding -> {
            palletDeviceBinding.setUpdateTime(DateUtils.getNowDate());
            palletDeviceBinding.setCreateTime(DateUtils.getNowDate());
            palletDeviceBinding.setBindingTime(DateUtils.getNowDate());
            // 默认状态为未解除绑定
            palletDeviceBinding.setCalibrationUnbindStatus("0");
        });

        TTrayInfo tTrayInfo  = new TTrayInfo();
        tTrayInfo.setfTrayId(palletDeviceBindingList.get(0).getTrayId());
        tTrayInfo.setfStatus("4");
        tTrayInfoMapper.updateStatusByTrayId(tTrayInfo);
        int i = palletDeviceBindingMapper.batchInsertPalletDeviceBinding(palletDeviceBindingList);
        if(i <= 0) {
            throw new RuntimeException("添加失败");
        }

        eventPublisher.publishEvent(new ReceivedNbCodeEvent(this, null,palletDeviceBindingList.get(0).getTrayId()));
        return palletDeviceBindingList;
    }

    @Override
    public int batchUpdateDeviceCode(List<PalletDeviceBinding> palletDeviceBindingList) {
        check(palletDeviceBindingList);
        palletDeviceBindingList.forEach(palletDeviceBinding -> {
            palletDeviceBinding.setDeviceStatus("1");
            palletDeviceBinding.setUpdateTime(DateUtils.getNowDate());
        });

        tTrayInfoMapper.updateToBoundWhenStateIsIdle(palletDeviceBindingList.get(0).getTrayId());

        return palletDeviceBindingMapper.batchUpdateDeviceCode(palletDeviceBindingList);
    }


    private void check(List<PalletDeviceBinding> palletDeviceBindingList) {

        if(palletDeviceBindingList == null || palletDeviceBindingList.size() ==0) {
            throw new RuntimeException("数据不能为空");
        }

        boolean b = palletDeviceBindingList.stream().allMatch(item->StringUtils.isBlank(item.getMotherboardCode()));
        if(b) {
            throw new RuntimeException("不能所有设备号都为空！！！");
        }
    }

    @Override
    public int batchUpdateDeviceCodeAndUnbindingTime(List<PalletDeviceBinding> palletDeviceBindingList) {
        palletDeviceBindingList.forEach(palletDeviceBinding -> {
            palletDeviceBinding.setDeviceStatus("1");
            palletDeviceBinding.setUpdateTime(DateUtils.getNowDate());
        });

        tTrayInfoMapper.updateToBoundWhenStateIsIdle(palletDeviceBindingList.get(0).getTrayId());
        palletDeviceBindingMapper.batchUpdateDeviceCodeAndUnbindingTime(palletDeviceBindingList);
        eventPublisher.publishEvent(new ReceivedNbCodeEvent(this, null,palletDeviceBindingList.get(0).getTrayId()));
        log.info("主线程时间:{}",DateUtils.getTime());
        return 1;
    }

    /**
     * 实时获取物联网状态
     *
     * @param trayCode 托盘编号
     * @return 结果
     */
    @Override
    public AjaxResult realTimeObtainIotStatusByTrayCode(String trayCode) {

        if(StringUtils.isBlank(trayCode)){
            return AjaxResult.error("托盘编号不能为空");
        }

        PalletDeviceBinding palletDeviceBinding = new PalletDeviceBinding();
        palletDeviceBinding.setfTrayCode(trayCode);
        List<PalletDeviceBinding> palletDeviceBindings = palletDeviceBindingMapper.selectPalletDeviceBindingList(palletDeviceBinding);
        if(palletDeviceBindings == null || palletDeviceBindings.size() == 0){
            return AjaxResult.error("无此托盘");
        }

        // 2. 按主板码分组，并过滤掉无效数据（同时提前处理无需调接口的）
        // // 只留 nbCode 有效的 // 网络状态为 1
        Map<String, List<PalletDeviceBinding>> groupByMotherboard = palletDeviceBindings.stream()
                .filter(b -> org.apache.commons.lang3.StringUtils.isNotBlank(b.getNbCode()) && b.getNetworkStatus() != null && b.getNetworkStatus() == 1)
                .collect(Collectors.groupingBy(PalletDeviceBinding::getMotherboardCode));

        // 处理 nbCode 为空的（直接设状态为 2，加入待更新列表）
        for (PalletDeviceBinding binding : palletDeviceBindings) {
            if (org.apache.commons.lang3.StringUtils.isBlank(binding.getNbCode())) {
                binding.setIotStatus(2);
            }
            // 顺便处理网络状态异常（但不要覆盖后续 IOT 查询结果，网络异常只是前置标记）
            // 注意：如果网络状态 !=1，但 IOT 查询成功，应该以 IOT 为准，这里先不 set，留给后面判断
        }

        // 3. 获取 IOT Token（只获取一次）
        String iotToken = iotDataAndTokenUtil.getIOTToken();
        if (org.apache.commons.lang3.StringUtils.isBlank(iotToken)) {
            throw new RuntimeException("IOT token未获取");
        }

        // 4. 构造通用请求头
        Map<String, String> headers = new HashMap<>();
        headers.put("Authorization", iotToken);
        headers.put("Content-Type", "application/json");
        String iotApiUrl = sysConfigService.directSelectConfigByKey(Constants.IOT_DEVICES_SCAN);

        // 5. 循环处理每个主板码（带限流）
        for (Map.Entry<String, List<PalletDeviceBinding>> entry : groupByMotherboard.entrySet()) {
            String motherboardCode = entry.getKey();
            List<PalletDeviceBinding> sameBoardDevices = entry.getValue();
            // 取任意一个设备的 nbCode（因为同一主板下的 nbCode 理论上一致，但为了严谨取第一个非空的）
            String nbCode = sameBoardDevices.stream()
                    .map(PalletDeviceBinding::getNbCode)
                    .filter(org.apache.commons.lang3.StringUtils::isNotBlank)
                    .findFirst()
                    .orElse(null);

            if (nbCode == null) {
                continue;
            }

            // ★★★ Guava 限流（阻塞等待令牌） ★★★
            RATE_LIMITER.acquire();

            // 调用 IOT 接口
            Map<String, Object> params = new HashMap<>();
            params.put("number", nbCode);
            String result = null;
            try {
                result = HttpUtils.get(iotApiUrl, headers, params);
            } catch (Exception e) {
                log.error("调用IOT接口异常, motherboardCode: {}, nbCode: {}", motherboardCode, nbCode, e);
            }

            // 6. 根据结果设置状态（应用到该主板下的所有设备）
            Integer statusToSet;
            if (org.apache.commons.lang3.StringUtils.isNotBlank(result)) {
                Map<String, String> dataMap = iotDataAndTokenUtil.processIotData(result);
                if (dataMap != null && !dataMap.isEmpty()) {
                    statusToSet = 1; // IOT数据正常
                } else {
                    statusToSet = 0; // IOT数据为空
                }
            } else {
                statusToSet = 0; // 请求失败
            }

            // 将状态设置给该组所有设备，并加入待更新列表
            for (PalletDeviceBinding device : sameBoardDevices) {
                device.setIotStatus(statusToSet);
            }
        }

        return AjaxResult.success(palletDeviceBindings);
    }

    /**
     * 修改托盘绑定的设备列
     *
     * @param palletDeviceBinding 托盘绑定的设备列
     * @return 结果
     */
    @Override
    public int updatePalletDeviceBinding(PalletDeviceBinding palletDeviceBinding)
    {
        return palletDeviceBindingMapper.updatePalletDeviceBinding(palletDeviceBinding);
    }

    /**
     * 批量删除托盘绑定的设备列
     *
     * @param palletDeviceBindingIds 需要删除的托盘绑定的设备列ID
     * @return 结果
     */
    @Override
    public int deletePalletDeviceBindingByIds(Long[] palletDeviceBindingIds)
    {
        return palletDeviceBindingMapper.deletePalletDeviceBindingByIds(palletDeviceBindingIds);
    }

    /**
     * 删除托盘绑定的设备列信息
     *
     * @param palletDeviceBindingId 托盘绑定的设备列ID
     * @return 结果
     */
    @Override
    public int deletePalletDeviceBindingById(Long palletDeviceBindingId)
    {
        return palletDeviceBindingMapper.deletePalletDeviceBindingById(palletDeviceBindingId);
    }

    /**
     * 清理数据
     *
     * @param trayCode 托盘编号
     * @return 结果
     */
    @Override
    public AjaxResult clearDataByTrayCode(String trayCode) {

        if(StringUtils.isBlank(trayCode)){
            return AjaxResult.error("参数错误");
        }

        TTrayInfo tTrayInfo = tTrayInfoMapper.selectTTrayInfoByCode(trayCode);
        if(tTrayInfo == null){
            return AjaxResult.error("无此托盘");
        }
        if("0".equals(tTrayInfo.getfStatus()) || "3".equals(tTrayInfo.getfStatus())) {
            return AjaxResult.error("空闲 或  标定完成 状态不许清理数据");
        }

        List<RobotArmCommand> robotArmCommands = robotArmCommandMapper.selectIsRunningCommandByTrayCode(trayCode);
        if(robotArmCommands.size() != 0) {
            List<Long> collect = robotArmCommands.stream().map(RobotArmCommand::getRobotArmCommandId).collect(Collectors.toList());
            robotArmCommandMapper.batchUpdateStatus("4",collect);
        }

        String storeyCode = tTrayInfo.getfStoreyCode();

        if(StringUtils.isNotBlank(storeyCode)) {

            TStoreyInfo tStoreyInfo = tStoreyInfoMapper.selectTStoreyInfoByCode(storeyCode);
            try {
                deviceTaskScheduler.cleanExistingTasks(tStoreyInfo.getfStoreyId());
            } catch (SchedulerException e) {
                throw new RuntimeException(e);
            }

            tStoreyInfoMapper.unbindByCode(trayCode);
        }

        // 解绑设备
        palletDeviceBindingMapper.unbindAllDevice(tTrayInfo.getfTrayId());

        // 初始托盘状态
        tTrayInfoMapper.clearStoreyCodeByTrayId(tTrayInfo.getfTrayId());

        return AjaxResult.success();
    }

    /**
     * 批量解绑所有设备（不处理上传数据）
     *
     * @param trayId 托盘ID
     * @return 删除结果
     */
    @Override
    public AjaxResult unbindAllDeviceNoUploadMes(Long trayId) {

        if(trayId == null) {
            return AjaxResult.error("参数错误");
        }

        List<PalletDeviceBinding> palletDeviceBindings = palletDeviceBindingMapper.listByTrayId(trayId);
        if(palletDeviceBindings.size() != 0) {
            List<PalletDeviceBinding> mesDeviceDomains = palletDeviceBindings.stream().filter(item -> item.getMotherboardCode() != null).collect(Collectors.toList());

            if(mesDeviceDomains.size() != 0) {
                // 20251210 领导说 先 保存所有历史数据
                palletDeviceUploadHistoryService.batchInsertRealTimeData(mesDeviceDomains);
            }
        }
        // 解绑设备
        palletDeviceBindingMapper.unbindAllDevice(trayId);

        tStoreyInfoMapper.unbindByTrayId(trayId);
        // 初始托盘状态
        tTrayInfoMapper.clearStoreyCodeByTrayId(trayId);
        return AjaxResult.success();
    }

    /**
     * 批量解绑所有设备
     *
     * @param trayId 托盘ID
     * @return 删除结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized AjaxResult unbindAllDevice(Long trayId) {

        if(trayId == null) {
            return AjaxResult.error("参数错误");
        }
        // 所有设备都是正常的一键解绑
        initMesDataAndUpload(trayId);

        // 解绑设备
        palletDeviceBindingMapper.unbindAllDevice(trayId);

        tStoreyInfoMapper.unbindByTrayId(trayId);

        // 初始托盘状态 可能会出现手动直接下料 不会走自定义任务的清理
        tTrayInfoMapper.clearStoreyCodeByTrayId(trayId);

        return AjaxResult.success();
    }

    /**
     * 解绑设备
     *
     * @param palletDeviceBinding 托盘绑定的设备列ID
     * @return 删除结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized AjaxResult unbindDevice(PalletDeviceBinding palletDeviceBinding) {

        if(palletDeviceBinding.getPalletDeviceBindingId() == null || StringUtils.isBlank(palletDeviceBinding.getMotherboardCode())) {
            return AjaxResult.error("参数错误");
        }
        PalletDeviceBinding palletDeviceBindingDb = palletDeviceBindingMapper.selectPalletDeviceBindingById(palletDeviceBinding.getPalletDeviceBindingId());
        if(palletDeviceBindingDb == null) {
            return AjaxResult.error("数据不存在");
        }

        if(!palletDeviceBindingDb.getMotherboardCode().equals(palletDeviceBinding.getMotherboardCode())) {
            return AjaxResult.error("设备码错误");
        }

        int i = palletDeviceBindingMapper.unbindDeviceByCalibrationUnbindStatus(palletDeviceBindingDb.getPalletDeviceBindingId());
        if (i > 0) {
            return AjaxResult.success();
        }
        return AjaxResult.error();
    }

    // 成功之后 判断 是否还有设备，如果没有设备的话则 初始化托盘状态
//    int count = palletDeviceBindingMapper.countDeviceByTrayId(palletDeviceBindingDb.getTrayId());
//    if(count == 0) {
//        tTrayInfoMapper.initStatusByTrayId(palletDeviceBindingDb.getTrayId());
//    }

    /**
     * 初始化MES数据并上传
     */
    private void initMesDataAndUpload(Long trayId) {

        List<PalletDeviceBinding> palletDeviceBindings = palletDeviceBindingMapper.listByTrayId(trayId);

        if(palletDeviceBindings == null || palletDeviceBindings.size() == 0)  return;

        List<PalletDeviceBinding> mesDeviceDomains = palletDeviceBindings.stream().filter(item -> item.getMotherboardCode() != null).collect(Collectors.toList());
        if(mesDeviceDomains.size() == 0) return;

        String productStandardInspectionNumber = mesDeviceDomains.get(0).getProductStandardInspectionNumber();
        if(StringUtils.isBlank(productStandardInspectionNumber)) {
            throw new RuntimeException("产品标准检验编号不能为空");
        }

        ProductStandardInspection productStandardInspection = productStandardInspectionMapper.selectByProductStandardInspectionNumber(productStandardInspectionNumber);
        if(productStandardInspection == null) {
            throw new RuntimeException("标检单不存在");
        }
        // 统计合格数量 (1)
        long qualifiedCount = mesDeviceDomains.stream()
                .filter(device -> "1".equals(device.getStandardInspectResult()))
                .count();

        // 统计不合格数量 (0)
        long unqualifiedCount = mesDeviceDomains.stream()
                .filter(device -> "0".equals(device.getStandardInspectResult()))
                .count();

        Long qualifiedNum = productStandardInspection.getQualifiedNum();
        if(qualifiedNum == null) {
            qualifiedNum = 0L;
        }
        Long unQualifiedNum = productStandardInspection.getUnQualifiedNum();
        if(unQualifiedNum == null) {
            unQualifiedNum = 0L;
        }

        productStandardInspection.setQualifiedNum(qualifiedCount + qualifiedNum);
        productStandardInspection.setUnQualifiedNum(unqualifiedCount + unQualifiedNum);

        if(productStandardInspection.getQualifiedNum() >= productStandardInspection.getQuantity()) {
            productStandardInspection.setStatus("STANDARD_INSPECTION_COMPLETED");
        }

        productStandardInspectionMapper.updateProductStandardInspection(productStandardInspection);

        // 20251210 领导说 先 保存所有历史数据
        palletDeviceUploadHistoryService.batchInsertRealTimeData(mesDeviceDomains);

        String mesUploadAddress = sysConfigService.directSelectConfigByKey(RoboticArmConstans.UPLOAD_MES_ADDRESS);
        if(StringUtils.isNotBlank(mesUploadAddress)) {
            try {
                String result = HttpUtils.sendPost(mesUploadAddress, JSON.toJSONString(mesDeviceDomains));
                UploadMesResultHistory uploadMesResultHistory = new UploadMesResultHistory();
                uploadMesResultHistory.setMessage(result);
                uploadMesResultHistoryService.insertUploadMesResultHistory(uploadMesResultHistory);
                if(StringUtils.isNotBlank(result)) {
                    JSONObject jsonObject = JSON.parseObject(result);
                    if(jsonObject.getInteger("code") != 200) {
                        String data = jsonObject.getString("data");
                        // 处理错误数据逻辑，再弄个错误数据的表
                        if(StringUtils.isNotBlank(data)) {
                            // 存失败记录
                            processPalletDeviceUploadFailure(mesDeviceDomains,data);
                        } else{
                            iPalletDeviceUploadFailureService.batchInsertRealTimeData(mesDeviceDomains);
                        }
                    }
                }
            } catch (Exception e) {
                iPalletDeviceUploadFailureService.batchInsertRealTimeData(mesDeviceDomains);
            }
        }
    }

    /**
     * 处理托盘设备上传历史数据
     */
    private void processPalletDeviceUploadFailure(List<PalletDeviceBinding> palletDeviceBindings,String data){
        JSONObject jsonObject = JSON.parseObject(data);

        if (!jsonObject.containsKey("failedCodes")) {
            return;
        } else {
            log.info("上传失败；没有返回正确数据");
        }

        JSONArray failedCodes = jsonObject.getJSONArray("failedCodes");
        if (failedCodes == null || failedCodes.isEmpty()) {
            return;
        }

        // 使用Set提高查找效率
        Set<String> failedCodeSet = failedCodes.stream()
                .map(Object::toString)
                .collect(Collectors.toSet());

        List<PalletDeviceBinding> palletDeviceUploadFailureList = palletDeviceBindings.stream()
                .filter(binding -> failedCodeSet.contains(binding.getMotherboardCode()))
                .collect(Collectors.toList());

        if (!palletDeviceUploadFailureList.isEmpty()) {
            iPalletDeviceUploadFailureService.batchInsertRealTimeData(palletDeviceUploadFailureList);
        }
    }
}
