package org.wcs.plugin.plc.communication;

import HslCommunication.Authorization;
import HslCommunication.Core.Types.OperateResult;
import HslCommunication.Core.Types.OperateResultExOne;
import HslCommunication.Profinet.Siemens.SiemensPLCS;
import HslCommunication.Profinet.Siemens.SiemensS7Net;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.wcs.constant.enums.plc.StackerTaskTypeEnum;
import org.wcs.mapper.intf.AppBasePlcService;
import org.wcs.model.bo.tuple.Tuple2;
import org.wcs.model.bo.tuple.Tuple3;
import org.wcs.model.po.app.AppBasePlc;
import org.wcs.plugin.plc.common.PlcCommonData;
import org.wcs.plugin.plc.constant.PlcConfig;
import org.wcs.plugin.plc.constant.PlcStackerTaskStatusEnum;
import org.wcs.plugin.plc.constant.PlcTypeEnum;
import org.wcs.plugin.plc.model.*;
import org.wcs.utils.AppListUtils;
import org.wcs.utils.AppStringUtils;
import org.wcs.utils.SpringUtils;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 西门子PLC通讯类
 */
@Slf4j
public class PlcSiemensCommunication implements IPlcCommunication {

    private boolean isActive = false; // 是否已经初始化
    private Map<Integer, SiemensS7Net> plcS7Net = new ConcurrentHashMap<>(); // PLC 读写操作map
    private Map<Integer, Boolean> plcIsConnect = new ConcurrentHashMap<>();


    /**
     * 存储plc基础信息
     * <plcId, PlcBaseInfo>
     */
    private Map<Integer, PlcBaseInfo> plcBaseInfoMap = new ConcurrentHashMap<>();

    /**
     * redis 数据操作类
     */
    private StringRedisTemplate stringRedisTemplate;



    /**
     * 初始化
     */
    @Override
    public void initialize() {
        if(isActive) {
            return;
        } else {
            if (Authorization.SetAuthorizationCode("df8e8eee-4039-4e61-9517-9c617737dd1a")){
                log.info("HslCommunication 组件组册成功！");
                isActive = true;
            }
            else {
                log.info("HslCommunication 组件组册失败，当前应用只能连续使用24小时，请联系本公司");
            }
        }
        setPlcInfo(); // 设置PLC信息
        setPlcS7Net(); // 设置PLC连接
    }

    /**
     * 设置PLC信息
     */
    private void setPlcInfo() {
        if(!plcBaseInfoMap.isEmpty()) {
            return;
        }
        plcBaseInfoMap = new HashMap<>();
        AppBasePlcService bean = SpringUtils.getBean(AppBasePlcService.class);
        List<AppBasePlc> basePlcs = bean.queryAll();
        if(basePlcs == null) {
            return;
        }
        if(basePlcs.isEmpty()) {
            plcBaseInfoMap.put(0, null);
            return;
        }
        for (AppBasePlc basePlc : basePlcs) {
            PlcBaseInfo baseInfo = new PlcBaseInfo();
            baseInfo.setPlcId(basePlc.getPlcId());
            baseInfo.setPlcName(basePlc.getPlcName());
            baseInfo.setPlcType(basePlc.getPlcType());
            baseInfo.setPlcVersion(basePlc.getPlcVersion());
            baseInfo.setPlcStatus(basePlc.getPlcStatus());
            baseInfo.setIp(basePlc.getIp());
            baseInfo.setPort(basePlc.getPort());
            baseInfo.setSlot(basePlc.getSlot());
            baseInfo.setRack(basePlc.getRack());
            plcBaseInfoMap.put(baseInfo.getPlcId(), baseInfo);
        }
    }


    /**
     * 设置PLC连接
     */
    private void setPlcS7Net() {
        if(!plcS7Net.isEmpty()) {
            return;
        }
        plcS7Net = new HashMap<>(); // 初始化PLC连接
        plcBaseInfoMap.forEach((plcId, plcBaseInfo) -> {
            if(!Objects.equals(plcBaseInfo.getPlcType(), PlcTypeEnum.SIEMENS.getCode())) {
                return;
            }
            SiemensPLCS plcVersion = switch (plcBaseInfo.getPlcVersion()) {
                case "S200" -> SiemensPLCS.S200;
                case "S200Smart" -> SiemensPLCS.S200Smart;
                case "S1500" -> SiemensPLCS.S1500;
                case "S300" -> SiemensPLCS.S300;
                case "S400" -> SiemensPLCS.S400;
                default -> SiemensPLCS.S1200;
            };
            SiemensS7Net siemensS7Net = new SiemensS7Net(plcVersion, plcBaseInfo.getIp());
            siemensS7Net.setPort(plcBaseInfo.getPort());
            siemensS7Net.setRack(plcBaseInfo.getRack().byteValue());
            siemensS7Net.setSlot(plcBaseInfo.getSlot().byteValue());
            siemensS7Net.setConnectTimeOut(2000);
            OperateResult operateResult = siemensS7Net.ConnectServer();
            plcS7Net.put(plcId, siemensS7Net);
            plcIsConnect.put(plcId, operateResult.IsSuccess);
        });
    }


    /**
     * 查找该地址的信息
     * @return < 异常信息，DB地址，plc连接实例>
     */
    private Tuple3<String, String, SiemensS7Net> getDbPlcData(String dbName) {
        if(stringRedisTemplate == null) {
            stringRedisTemplate = SpringUtils.getBean(StringRedisTemplate.class);
        }
        String dbJson = stringRedisTemplate.opsForValue().get(PlcCommonData.PLC_DB_ADDRESS_REDIS_KEY + ":" + dbName);
        if(AppStringUtils.isEmpty(dbJson)) {
            return new Tuple3<>("PLC数据库地址不存在，名称：" + dbName, null, null);
        }
        SiemensDBAddress siemensDBAddress = AppStringUtils.stringToObject(dbJson, SiemensDBAddress.class);
        if(siemensDBAddress == null) {
            return new Tuple3<>("DB名称对应的地址不存在，名称：" + dbName, null, null);
        }
        SiemensS7Net siemensS7Net = plcS7Net.get(siemensDBAddress.getPlcId());
        if(siemensS7Net == null) {
            return new Tuple3<>("PLC连接不存在，PLC编号：" + siemensDBAddress.getPlcId(), null, null);
        }
        Boolean isConnected = plcIsConnect.get(siemensDBAddress.getPlcId());
        if(isConnected == null || !isConnected) {
            OperateResult operateResult = siemensS7Net.ConnectServer();
            plcIsConnect.put(siemensDBAddress.getPlcId(), operateResult.IsSuccess);
        }
        return new Tuple3<>(null, siemensDBAddress.getDbAddress(), siemensS7Net);
    }

    /**
     * 获取堆垛机状态
     * @param stackerId 堆垛机ID
     * @return 堆垛机状态
     */
    @Override
    public Tuple2<String, StackerStatus> readStackerStatus(Integer stackerId) {
        String dbName = "*堆垛机状态" + stackerId;
        Tuple3<String, String, SiemensS7Net> plcData = getDbPlcData(dbName);
        if(AppStringUtils.isNotEmpty(plcData.item1)) {
            return new Tuple2<>(plcData.item1, null);
        }
        SiemensS7Net siemensS7Net = plcData.item3;
        OperateResultExOne<byte[]> readResult = siemensS7Net.Read(plcData.item2, (short)50);
        if(!readResult.IsSuccess) {
            return new Tuple2<>(readResult.Message, null);
        }
        byte[] readBytes = readResult.Content; // 读到的数据
        StackerStatus stackerStatus = new StackerStatus();
        stackerStatus.setPlcTaskId(siemensS7Net.getByteTransform().TransInt32(readBytes, 0)); // PLC任务ID
        stackerStatus.setControlModel(siemensS7Net.getByteTransform().TransInt16(readBytes, 4)); // 控制模式
        stackerStatus.setDeviceStatus(siemensS7Net.getByteTransform().TransInt16(readBytes, 6)); // 设备状态
        stackerStatus.setLaneId(siemensS7Net.getByteTransform().TransInt16(readBytes, 8)); // 当前巷道
        stackerStatus.setLRow(siemensS7Net.getByteTransform().TransInt16(readBytes, 10)); // 当前排
        stackerStatus.setLLine(siemensS7Net.getByteTransform().TransInt16(readBytes, 12)); // 当前列
        stackerStatus.setLLayer(siemensS7Net.getByteTransform().TransInt16(readBytes, 14)); // 当前层
        stackerStatus.setLDepth(siemensS7Net.getByteTransform().TransInt16(readBytes, 16)); // 当前深度
        stackerStatus.setCode1(siemensS7Net.getByteTransform().TransInt32(readBytes, 18)); // 码1
        stackerStatus.setErrCode(siemensS7Net.getByteTransform().TransInt16(readBytes, 22)); // 错误码
        stackerStatus.setCode2(siemensS7Net.getByteTransform().TransInt32(readBytes, 44)); // 码2
        return new Tuple2<>(null, stackerStatus);
    }

    /**
     * 写入堆垛机任务
     *
     * @param stackerId 堆垛机ID
     * @param task 堆垛机任务
     * @return 写入结果
     */
    @Override
    public String writeStackerTask(Integer stackerId, StackerTask... task) {
       if(stackerId == null || task == null || task.length < 1) {
           return "传入的参数不正确";
       }
       String dbName = "*堆垛机写任务" + stackerId;
       String taskConfirmDbName = "*堆垛机写任务确认" + stackerId;
       Tuple3<String, String, SiemensS7Net> plcData = getDbPlcData(dbName);
       if(AppStringUtils.isNotEmpty(plcData.item1)) {
           return plcData.item1;
       }
       Tuple3<String, String, SiemensS7Net> plcDataTaskConfirm = getDbPlcData(taskConfirmDbName);
       if(AppStringUtils.isNotEmpty(plcDataTaskConfirm.item1)) {
           return plcDataTaskConfirm.item1;
       }
        String[] writeTaskDbs = plcData.item2.split(";");
        if(writeTaskDbs.length !=  task.length) {
            return "传入的任务数量与堆垛机接受的任务数量不一致";
        }
        SiemensS7Net siemensS7Net = plcData.item3;
        boolean isWrite = false; // 是否写入任务
        for (int i = 0; i < task.length; i++) {
            StackerTask stackerTask = task[i];
            if(stackerTask == null) {
                continue;
            }
            /* 根据位置和任务类型转换排数据 */
            switch (StackerTaskTypeEnum.getByCode(Integer.valueOf(stackerTask.getTaskType()))) {
                case StackerTaskTypeEnum.ERROR:
                    stackerTask.setGetRow((short)(stackerTask.getGetRow() - task.length + i + 1));
                    stackerTask.setSetRow((short)(stackerTask.getSetRow() - task.length + i + 1));
                    break;
                case StackerTaskTypeEnum.IN:
                    stackerTask.setGetRow((short)(stackerTask.getGetRow() - task.length + i + 1));
                    break;
                case StackerTaskTypeEnum.OUT:
                    stackerTask.setSetRow((short)(stackerTask.getSetRow() - task.length + i + 1));
                    break;
                case StackerTaskTypeEnum.MOVE:
                    break;
                case null:
                    return "不支持的堆垛机任务类型";
            }
            List<Byte> bytes = new ArrayList<>();
            bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(stackerTask.getPlcTaskId()))); // plc 任务号
            bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(stackerTask.getTaskType())));  // 任务类型
            bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(stackerTask.getGetStand())));  // 取货站台
            bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(stackerTask.getGetLaneId())));  // 取货巷道
            bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(stackerTask.getSetLaneId())));  // 卸货巷道
            bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(stackerTask.getSetStand())));  // 卸货站台
            bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(stackerTask.getGetRow())));  // 取货排
            bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(stackerTask.getGetLine())));  // 取货列
            bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(stackerTask.getGetLayer())));  //  取货层
            bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(stackerTask.getGetDepth())));  // 取货深度
            bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(stackerTask.getSetRow())));  // 放货排
            bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(stackerTask.getSetLine()))); // 放货列
            bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(stackerTask.getSetLayer())));  // 放货层
            bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(stackerTask.getSetDepth())));  // 放货深度
            bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(stackerTask.getVehicleSize())));  // 货尺寸
            bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(stackerTask.getWeight()))); // 货重量
            bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(stackerTask.getVehicleNo()))); // 载具号
            // 写入任务
            OperateResult operateResult = siemensS7Net.Write(writeTaskDbs[i], AppListUtils.transfer(bytes));
            if (!operateResult.IsSuccess) {
                return operateResult.Message;
            }
            isWrite = true;
        }
        if(!isWrite) {
            return "没有写入任务，请检查是否传入的任务都为空";
        }
        if(AppStringUtils.isNotEmpty(plcDataTaskConfirm.item2)) {
            // 写入任务确认
            OperateResult operateResult = plcDataTaskConfirm.item3.Write(plcDataTaskConfirm.item2, (short) 1);
            if (!operateResult.IsSuccess) {
                return operateResult.Message;
            }
        }
        return null;
    }

    /**
     * 获取堆垛机任务状态
     * @param stackerId 堆垛机ID
     * @return 堆垛机任务状态
     */
    @Override
    public Map<Integer, PlcStackerTaskStatus> readStackerTaskStatus(Integer stackerId) {
        if(stackerId == null) {
            return null;
        }
        String dbName = "*堆垛机过账区" + stackerId;
        Tuple3<String, String, SiemensS7Net> plcData = getDbPlcData(dbName);
        if(AppStringUtils.isNotEmpty(plcData.item1)) {
            return null;
        }
        SiemensS7Net siemensS7Net = plcData.item3;
        OperateResultExOne<byte[]> readResult = siemensS7Net.Read(plcData.item2, (short)(PlcConfig.PLC_CONFIG_CHECK_ACCOUNT_LENGTH * 6));
        if(!readResult.IsSuccess) {
            return null;
        }
        byte[] bytes = readResult.Content;
        Map<Integer, PlcStackerTaskStatus> taskStatusMap = new HashMap<>();
        for (int i = 0; i < PlcConfig.PLC_CONFIG_CHECK_ACCOUNT_LENGTH; i++) {
            PlcStackerTaskStatus taskStatusItem = new PlcStackerTaskStatus();
            int taskId = siemensS7Net.getByteTransform().TransInt32(bytes, i*6);
            short taskStatus = siemensS7Net.getByteTransform().TransInt16(bytes, 4 + i*6);
            taskStatusItem.setTaskId(taskId);
            taskStatusItem.setStackerTaskStatusEnum(PlcStackerTaskStatusEnum.getByCode((int) taskStatus));
            taskStatusMap.put(i, taskStatusItem);
        }
        return taskStatusMap;
    }

    /**
     * 清除任务状态
     * @param stackerId 堆垛机ID
     * @param taskIndex   任务索引
     * @return 清除结果
     */
    @Override
    public String clearStackerTaskStatus(Integer stackerId, Integer taskIndex) {
        if(stackerId == null || taskIndex == null) {
            return "传入的参数存在问题";
        }
        String dbName = "*堆垛机过账区" + stackerId;
        Tuple3<String, String, SiemensS7Net> plcData = getDbPlcData(dbName);
        if(AppStringUtils.isNotEmpty(plcData.item1)) {
            return plcData.item1;
        }
        SiemensS7Net siemensS7Net = plcData.item3;
        String dbAddress = plcData.item2;
        if(AppStringUtils.isEmpty(dbAddress)) {
            return "过账区地址不正确";
        }
        String[] addressDetails = dbAddress.split("\\.");
        if(addressDetails.length != 2) {
            return "过账区地址不正确";
        }
        String dbAddressHeader = addressDetails[0];
        int dbAddressSuffix = Integer.parseInt(addressDetails[1]);
        int newDbAddressSuffix = dbAddressSuffix + taskIndex * 6;
        String newDbAddress = dbAddressHeader + "." + newDbAddressSuffix;
        OperateResult operateResult = siemensS7Net.Write(newDbAddress, (int)0);
        if(!operateResult.IsSuccess) {
            return operateResult.Message;
        }
        return null;
    }

    /**
     * 获取任务输送机状态
     * @param conveyId 输送机ID
     * @return 输送机状态
     */
    @Override
    public Tuple2<String, TaskConveyStatus> readTaskConveyStatus(String conveyId) {
        String dbName = "*输送机状态" + conveyId;
        Tuple3<String, String, SiemensS7Net> plcData = getDbPlcData(dbName);
        if(AppStringUtils.isNotEmpty(plcData.item1)) {
            return new Tuple2<>(plcData.item1, null);
        }
        SiemensS7Net siemensS7Net = plcData.item3;
        OperateResultExOne<byte[]> readResult = siemensS7Net.Read(plcData.item2, (short)(22 + PlcConfig.PLC_CONFIG_TASK_CONVEY_CODE_LENGTH));
        if(!readResult.IsSuccess) {
            return new Tuple2<>(readResult.Message, null);
        }
        byte[] readBytes = readResult.Content; // 读到的数据
        TaskConveyStatus taskConveyStatus = new TaskConveyStatus();
        taskConveyStatus.setConveyNo(siemensS7Net.getByteTransform().TransInt16(readBytes, 0)); // 输送机编号
        taskConveyStatus.setPlcTaskId(siemensS7Net.getByteTransform().TransInt32(readBytes, 2)); // plc任务号
        taskConveyStatus.setTaskAllow(siemensS7Net.getByteTransform().TransInt16(readBytes, 6)); // 任务允许
        taskConveyStatus.setControlModel(siemensS7Net.getByteTransform().TransInt16(readBytes, 8)); // 控制模式
        taskConveyStatus.setDeviceStatus(siemensS7Net.getByteTransform().TransInt16(readBytes, 10)); // 设备状态
        taskConveyStatus.setSensor(siemensS7Net.getByteTransform().TransInt16(readBytes, 12));
        taskConveyStatus.setVehicleSize(siemensS7Net.getByteTransform().TransInt16(readBytes, 14)); // 货尺寸
        taskConveyStatus.setWeight(siemensS7Net.getByteTransform().TransInt32(readBytes, 16)); // 货重量
        taskConveyStatus.setErrCode(siemensS7Net.getByteTransform().TransInt16(readBytes, 20)); // 错误码
        taskConveyStatus.setVehicleNo(siemensS7Net.getByteTransform().TransString(readBytes, 22, PlcConfig.PLC_CONFIG_TASK_CONVEY_CODE_LENGTH, StandardCharsets.US_ASCII)); // 载具号
        return new Tuple2<>(null, taskConveyStatus);
    }

    /**
     * 获取任务输送机状态 ---- 同时获取多个
     * @param conveyId 输送机ID
     * @return 输送机状态列表
     */
    @Override
    public Tuple2<String, List<TaskConveyStatus>> readTaskMultiConveyStatus(String conveyId) {
        String dbName = "*输送机状态" + conveyId;
        Tuple3<String, String, SiemensS7Net> plcData = getDbPlcData(dbName);
        if(AppStringUtils.isNotEmpty(plcData.item1)) {
            return new Tuple2<>(plcData.item1, null);
        }
        SiemensS7Net siemensS7Net = plcData.item3;
        List<TaskConveyStatus> taskConveyStatusList = new ArrayList<>();
        String[] dbAddresses = plcData.item2.split(";");
        for (String address : dbAddresses) {
            OperateResultExOne<byte[]> readResult = siemensS7Net.Read(address, (short)(22 + PlcConfig.PLC_CONFIG_TASK_CONVEY_CODE_LENGTH));
            if(!readResult.IsSuccess) {
                return new Tuple2<>(readResult.Message, null);
            }
            byte[] readBytes = readResult.Content; // 读到的数据
            TaskConveyStatus taskConveyStatus = new TaskConveyStatus();
            taskConveyStatus.setConveyNo(siemensS7Net.getByteTransform().TransInt16(readBytes, 0)); // 输送机编号
            taskConveyStatus.setPlcTaskId(siemensS7Net.getByteTransform().TransInt32(readBytes, 2)); // plc任务号
            taskConveyStatus.setTaskAllow(siemensS7Net.getByteTransform().TransInt16(readBytes, 6)); // 任务允许
            taskConveyStatus.setControlModel(siemensS7Net.getByteTransform().TransInt16(readBytes, 8)); // 控制模型
            taskConveyStatus.setDeviceStatus(siemensS7Net.getByteTransform().TransInt16(readBytes, 10)); // 设备状态
            taskConveyStatus.setSensor(siemensS7Net.getByteTransform().TransInt16(readBytes, 12));
            taskConveyStatus.setVehicleSize(siemensS7Net.getByteTransform().TransInt16(readBytes, 14)); // 货尺寸
            taskConveyStatus.setWeight(siemensS7Net.getByteTransform().TransInt32(readBytes, 16)); // 货重量
            taskConveyStatus.setErrCode(siemensS7Net.getByteTransform().TransInt16(readBytes, 20)); // 错误码
            taskConveyStatus.setVehicleNo(siemensS7Net.getByteTransform().TransString(readBytes, 22, PlcConfig.PLC_CONFIG_TASK_CONVEY_CODE_LENGTH, StandardCharsets.US_ASCII)); // 载具号
            taskConveyStatusList.add(taskConveyStatus);
        }
        return new Tuple2<>(null, taskConveyStatusList);
    }

    /**
     * 写入任务输送机任务
     * @param conveyId 输送机ID
     * @param task 任务
     * @return 错误信息
     */
    @Override
    public String writeTaskConveyTask(String conveyId, TaskConveyTask task) {
        if(conveyId == null || task == null) {
            return "输送机ID和任务不能为空";
        }
        String dbName = "*输送机写任务" + conveyId;
        Tuple3<String, String, SiemensS7Net> plcData = getDbPlcData(dbName);
        if(AppStringUtils.isNotEmpty(plcData.item1)) {
            return plcData.item1;
        }
        SiemensS7Net siemensS7Net = plcData.item3;
        List<Byte> bytes = new ArrayList<>();
        bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(task.getPlcTaskId()))); // plc任务号
        bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(task.getTaskType()))); // 任务类型
        bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(task.getOrigin()))); // 源站台
        bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(task.getDestination()))); // 目的站台
        bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(task.getVehicleSize() == null ? 0 : task.getVehicleSize()))); // 货尺寸
        bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(task.getWeight() == null ? 0 : task.getWeight()))); // 货重量
        bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(AppStringUtils.toFixLength(task.getVehicleNo(), PlcConfig.PLC_CONFIG_TASK_CONVEY_CODE_LENGTH, ' '), StandardCharsets.US_ASCII))); // 载具号
        OperateResult operateResult = siemensS7Net.Write(plcData.item2, AppListUtils.transfer(bytes));
        if(!operateResult.IsSuccess) {
            return operateResult.Message;
        }
        return null;
    }

    /**
     * 读取扫描器信息
     * @param scannerId 扫码编号
     * @return 扫码信息
     */
    @Override
    public Tuple2<String, ScanInfo> readScannerInfo(String scannerId) {
        String dbName = "*扫码器数据" + scannerId;
        Tuple3<String, String, SiemensS7Net> plcData = getDbPlcData(dbName);
        if(AppStringUtils.isNotEmpty(plcData.item1)) {
            return new Tuple2<>(plcData.item1, null);
        }
        SiemensS7Net siemensS7Net = plcData.item3;
        OperateResultExOne<byte[]> readResult = siemensS7Net.Read(plcData.item2, (short)(20 + PlcConfig.PLC_CONFIG_TASK_CONVEY_CODE_LENGTH));
        if(!readResult.IsSuccess) {
            return new Tuple2<>(readResult.Message, null);
        }
        byte[] readBytes = readResult.Content; // 读到的数据
        ScanInfo scanInfo = new ScanInfo();
        scanInfo.setTag(siemensS7Net.getByteTransform().TransInt16(readBytes, 0));
        scanInfo.setVehicleSize(siemensS7Net.getByteTransform().TransInt16(readBytes, 2));
        scanInfo.setLength(siemensS7Net.getByteTransform().TransInt32(readBytes, 4));
        scanInfo.setWidth(siemensS7Net.getByteTransform().TransInt32(readBytes, 8));
        scanInfo.setHeight(siemensS7Net.getByteTransform().TransInt32(readBytes, 12));
        scanInfo.setWeight(siemensS7Net.getByteTransform().TransInt32(readBytes, 16));
        scanInfo.setVehicleNo(siemensS7Net.getByteTransform().TransString(readBytes, 20 , PlcConfig.PLC_CONFIG_TASK_CONVEY_CODE_LENGTH, StandardCharsets.US_ASCII));
        return new Tuple2<>(null, scanInfo);
    }

    /**
     * 写入扫码器任务
     * @param scannerId 扫描器编号
     * @param task 任务
     * @return 写入结果
     */
    @Override
    public String writeScannerTask(String scannerId, ScanTask task) {
        if(AppStringUtils.isEmpty(scannerId) || task == null) {
            return "传入的扫码器编号或者任务为空";
        }
        String dbName = "*扫码器写任务" + scannerId;
        Tuple3<String, String, SiemensS7Net> plcData = getDbPlcData(dbName);
        if(AppStringUtils.isNotEmpty(plcData.item1)) {
            return plcData.item1;
        }
        SiemensS7Net siemensS7Net = plcData.item3;
        List<Byte> bytes = new ArrayList<>();
        bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(task.getPlcTaskId() == null ? 0 : task.getPlcTaskId())));
        bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(task.getTaskType() == null ? 0 : task.getTaskType())));
        bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(task.getOrigin() == null ? 0 : task.getOrigin())));
        bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(task.getDestination() == null ? 0 : task.getDestination())));
        bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(task.getVehicleSize() == null ? 0 : task.getVehicleSize())));
        bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(task.getLength() == null ? 0 : task.getLength())));
        bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(task.getWidth() == null ? 0 : task.getWidth())));
        bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(task.getHeight() == null ? 0 : task.getHeight())));
        bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(task.getWeight() == null ? 0 : task.getWeight())));
        bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(AppStringUtils.toFixLength(task.getVehicleNo(), PlcConfig.PLC_CONFIG_TASK_CONVEY_CODE_LENGTH, ' '), StandardCharsets.US_ASCII)));
        OperateResult operateResult = siemensS7Net.Write(plcData.item2, AppListUtils.transfer(bytes));
        if(!operateResult.IsSuccess) {
            return operateResult.Message;
        }
        return null;
    }

    /**
     * 写入值往指定的DB地址名称
     * @param name 数据库名称
     * @param value 数据库值
     * @return 写入结果
     */
    @Override
    public String writeWithName(String name, Object value) {
        Tuple3<String, String, SiemensS7Net> plcData = getDbPlcData(name);
        if(AppStringUtils.isNotEmpty(plcData.item1)) {
            return plcData.item1;
        }
        SiemensS7Net siemensS7Net = plcData.item3;
        List<Byte> bytes = new ArrayList<>();
        Class<?> valueClass = value.getClass();
        switch (valueClass.getName()) {
            case "java.lang.Integer":
                bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte((Integer)value)));
                break;
            case "java.lang.Long":
                bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte((Long)value)));
                break;
            case "java.lang.Short":
                bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte((Short)value)));
                break;
            case "java.lang.Double":
                bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte((Double)value)));
                break;
            case "java.lang.Float":
                bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte((Float)value)));
                break;
            case "java.lang.Byte":
                bytes.add((Byte)value);
                break;
            case "java.lang.String":
                bytes.addAll(AppListUtils.transfer(siemensS7Net.getByteTransform().TransByte(AppStringUtils.toFixLength((String)value, PlcConfig.PLC_CONFIG_TASK_CONVEY_CODE_LENGTH, ' '), StandardCharsets.US_ASCII)));
                break;
            default:
                return "不支持的数据类型";
        }
        OperateResult operateResult = siemensS7Net.Write(plcData.item2, AppListUtils.transfer(bytes));
        if(!operateResult.IsSuccess) {
            return operateResult.Message;
        }
        return null;
    }

    /**
     * 读取值从指定的DB地址名称
     * @param name 数据库名称
     * @param clazz 数据类型
     * @return 读取结果
     */
    @Override
    public <T> Tuple2<String, T> readWithName(String name, Class<T> clazz) {
        // 获取T类型
        Type[] actualTypeArguments = ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments();
        if(actualTypeArguments.length < 1) {
            return new Tuple2<>("数据类型异常", null);
        }
        Type type = actualTypeArguments[0];
        if (!(type instanceof Class)) {
            return new Tuple2<>("不支持的复杂泛型类型", null);
        }
        Tuple3<String, String, SiemensS7Net> plcData = getDbPlcData(name);
        if(AppStringUtils.isNotEmpty(plcData.item1)) {
            return new Tuple2<>(plcData.item1, null);
        }
        SiemensS7Net siemensS7Net = plcData.item3;
        // 直接使用传入的clazz进行类型判断
        if(clazz == Integer.class) {
            OperateResultExOne<Integer> operateResult = siemensS7Net.ReadInt32(plcData.item2);
            if(!operateResult.IsSuccess) {
                return new Tuple2<>(operateResult.Message, null);
            }
            @SuppressWarnings("unchecked")
            T result = (T) operateResult.Content;
            return new Tuple2<>(null, result);
        }
        if(clazz == Long.class) {
            OperateResultExOne<Long> operateResult = siemensS7Net.ReadInt64(plcData.item2);
            if(!operateResult.IsSuccess) {
                return new Tuple2<>(operateResult.Message, null);
            }
            @SuppressWarnings("unchecked")
            T result = (T) operateResult.Content;
            return new Tuple2<>(null, result);
        }
        if(clazz == Short.class) {
            OperateResultExOne<Short> operateResult = siemensS7Net.ReadInt16(plcData.item2);
            if(!operateResult.IsSuccess) {
                return new Tuple2<>(operateResult.Message, null);
            }
            @SuppressWarnings("unchecked")
            T result = (T) operateResult.Content;
            return new Tuple2<>(null, result);
        }
        if(clazz == Byte.class) {
            OperateResultExOne<Byte> operateResult = siemensS7Net.ReadByte(plcData.item2);
            if(!operateResult.IsSuccess) {
                return new Tuple2<>(operateResult.Message, null);
            }
            @SuppressWarnings("unchecked")
            T result = (T) operateResult.Content;
            return new Tuple2<>(null, result);
        }
        if(clazz == Double.class) {
            OperateResultExOne<Double> operateResult = siemensS7Net.ReadDouble(plcData.item2);
            if(!operateResult.IsSuccess) {
                return new Tuple2<>(operateResult.Message, null);
            }
            @SuppressWarnings("unchecked")
            T result = (T) operateResult.Content;
            return new Tuple2<>(null, result);
        }
        if(clazz == Float.class) {
            OperateResultExOne<Float> operateResult = siemensS7Net.ReadFloat(plcData.item2);
            if(!operateResult.IsSuccess) {
                return new Tuple2<>(operateResult.Message, null);
            }
            @SuppressWarnings("unchecked")
            T result = (T) operateResult.Content;
            return new Tuple2<>(null, result);
        }
        return new Tuple2<>("不支持的数据类型", null);
    }


}
