package com.unis.detectionnum.server.service.impl;

import com.google.common.collect.Maps;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.sero.util.queue.ByteQueue;
import com.unis.common.utils.LsBeanUtils;
import com.unis.detectionnum.common.vo.*;
import com.unis.detectionnum.server.domain.*;
import com.unis.detectionnum.server.entity.DeviceOperateDO;
import com.unis.detectionnum.server.message.entity.MessageVo;
import com.unis.detectionnum.server.message.utils.MessageAsyncSendUtils;
import com.unis.detectionnum.server.service.ScanPointsService;
import com.unis.detectionnum.server.utils.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ScheduledExecutorService;

/**
 * @Package: com.uins.sljc.server.service.impl
 * @Author: LiuFeng
 * @Description:
 * @Date: Created in 2019/8/15
 */
@Service("pointsService")
public class ScanPointsServiceImpl implements ScanPointsService {

    private static ScheduledExecutorService scheduler = null;

    private static final Logger log = LoggerFactory.getLogger(ScanPointsServiceImpl.class);

    @Autowired
    ScanPointsDomain pointsDomain;

    @Autowired
    DeviceInfoDomain deviceInfoDomain;

    @Autowired
    DeviceOperateDomain deviceOperateDomain;

    @Autowired
    AlarmParamDomain alarmParamDomain;

    @Autowired
    DCfxxsjyDomain dCfxxsjyDomain;

    @Autowired
    DeviceAlarmDomain deviceAlarmDomain;

    @Autowired
    DeviceParamServiceImpl deviceParamService;

    @Autowired
    MessageAsyncSendUtils messageAsyncSendUtils;

    @Autowired
    ScanPointsDomain scanPointsDomain;

    @Autowired
    ScanUtils scanUtils;

    SocketUtils socketUtils = new SocketUtils();

    /**
     * 开启扫描
     *
     * @param socket    socket
     * @param scanStart 扫描开始时间
     * @param sbCode    设备id
     * @throws IOException
     * @throws InterruptedException
     */
    @Override
    public void Completescan(Socket socket, Date scanStart, String sbCode) throws SocketTimeoutException, IOException, InterruptedException {
        /**
         * 第一次扫描根据扫描结果返回仓房，高（h）、ax（x左侧的距离）、by（y方向向上的距离）
         *   ax1（x右侧的距离）、by1(y方向向下的距离)
         */
        Map<String, Double> getCfParam = firstSend(socket);

        //解析出仓房高度
        double h = getCfParam.get("h");
        if (h < 0 || h > 10) {
            h = 8;
        }
        //获取右边长
        double extentRight = getCfParam.get("extentRight");
        extentRight = 24;
//        extentRight = getEven(extentRight);
        //获取左边长
        double extentLeft = getCfParam.get("extentLeft");
        extentLeft = 30;
//        extentLeft = getEven(extentLeft);
        log.info("设备扫描高度为：" + h + " 右边长为：" + extentRight + " 左边长：" + extentLeft);

        //封装扫描数据
        DeviceOperateVO currentOperate = getOperate(sbCode, h, scanStart, 1, extentRight, extentLeft);

        double smExtent = currentOperate.getSmcd();
        double smWide = currentOperate.getSmkd();
        double cfHeight = currentOperate.getCfgd();
        double zLXGD = currentOperate.getZLXGD();
        double wd = currentOperate.getWd();
        currentOperate.setId(UUIDUtils.getUUID());
        //保存扫描数据
        String operateid = deviceOperateDomain.insert(currentOperate);
        //第一次扫描测粮面信息
        //1.产生数据并处理，将数据入库，2.发送命令并处理返回的数据入库
        log.info("***第一次扫描开始返回最高点z***扫描code：" + operateid);
//        dataProcessing(h, smExtent, smWide, extentAx, wideBy, "", sbCode, socket, cfHeight,zLXGD,wd);
        double cfsmgdz = scanUtils.dataProcessingOne(h, smExtent, smWide, extentRight, extentLeft, operateid, sbCode, socket, cfHeight, zLXGD, wd);
        //第一次扫描完删除临时数据xyz
//        scanPointsDomain.deleteByoperateId(operateid);
        log.info("***第一次扫描结束***");
        //第二次扫描测粮面信息
        log.info("***第二次扫描开始根据第一次扫描最高点z***");
        secondScanByMaxZ(sbCode, currentOperate, scanStart, socket, cfHeight, zLXGD, wd, cfsmgdz);
        log.info("***第二次扫描结束***");
        //扫描完成以后设备垂直向下
        socketUtils.sendRequest(socket, CommonDataMeasure.VERTICAL_DOWN);
        //删除第一次扫描的结果
//        deviceOperateDomain.deleteByOperateId(operateId);
    }

    /**
     * 初始化探头 获取 仓房的长 宽 高
     *
     * @param socket socket
     * @throws IOException
     * @throws InterruptedException
     */
    @Override
    public Map<String, Double> firstSend(Socket socket) throws SocketTimeoutException, IOException, InterruptedException {

        Map<String, Double> map = new HashMap<>();
        //计数
        int count = 1;
        //第一次扫描的命令
        List Oders = getFirstOder();
        for (int i = 0; i < Oders.size(); i++) {
            log.info("***初始化命令***  " + Oders.get(i));
        }
        //第一次扫描开始循环，先扫描垂直向下，然后水平向右，然后转九十度 可得长宽高
        for (int i = 0; i < Oders.size(); i++) {
            //发送十六进制的字符串并 返回十六进制的字符串
            log.info("***初始化命令发送***  " + Oders.get(i));
            String ret = socketUtils.sendRequest(socket, (String) Oders.get(i));
//            Thread.sleep(1000);
            //异常数据重发
            if (StringUtils.isEmpty(ret) || ret.contains(CommonDataMeasure.BAD_DATA)) {
                for (int a = 0; a < 3; a++) {
                    ret = socketUtils.sendRequest(socket, (String) Oders.get(i));
//                    Thread.sleep(1000);
                    if (!StringUtils.isEmpty(ret) && !ret.contains(CommonDataMeasure.BAD_DATA)) {
                        break;
                    }
                }
            }
            //信任度不够重发
            if (!ScanUtils.getTrust(ret)) {
                for (int a = 0; a < 3; a++) {
                    ret = socketUtils.sendRequest(socket, (String) Oders.get(i));
//                    Thread.sleep(1000);
                    if (ScanUtils.getTrust(ret)) {
                        break;
                    }
                }
            }
            //启动风扇
            if (i == 0) {
                fanStart(socket);
            }
            //获取仓房长，宽，高
            map = getExtentOrWide(ret, count, map);
            count++;
        }
        //返回长宽高 集合
        return map;
    }

    /**
     * 获取第一次扫描后的长宽高
     *
     * @param ret   扫描后返回的字符串
     * @param count 扫描次数
     * @return 返回长宽高集合
     */
    private Map<String, Double> getExtentOrWide(String ret, int count, Map<String, Double> map) {
        //获取返回的x,y,以及斜边距离z
        String[] return_str = ret.trim().split(" ", 14);
        //获取第一次扫描获取扫描高度
        if (count == 1) {
            int zsMz = DataMeasureUtils.hexToInt(return_str[7] + return_str[8] + return_str[9] + return_str[10]);
            double smh = DataMeasureUtils.mmToM(zsMz);
            map.put("h", smh);
            log.info("垂直向下h=" + map.get("h"));
        }
        //第2次扫描获取扫描距离
        if (count == 2) {
            double extentAx = DataMeasureUtils.mmToM(DataMeasureUtils.hexToInt(return_str[7] + return_str[8] + return_str[9] + return_str[10]));
            map.put("extentRight", extentAx);
            log.info("右边仓房长度extentRight=" + map.get("extentRight"));
        }
        //第3次扫描获取扫描距离
        if (count == 3) {
            double extentAx2 = DataMeasureUtils.mmToM(DataMeasureUtils.hexToInt(return_str[7] + return_str[8] + return_str[9] + return_str[10]));
            map.put("extentLeft", extentAx2);
            log.info("左边仓房长度extentLeft=" + map.get("extentLeft"));
        }
        return map;
    }

    /**
     * 返回第一次扫描的命令
     *
     * @return
     */
    private List getFirstOder() {
        //初始容器，用于存储命令
        List list = new ArrayList();
        list.add(0, CommonDataMeasure.VERTICAL_DOWN); //垂直向下；
        list.add(1, CommonDataMeasure.EXTENT_RIGHT); //右边仓房长度；A5 01 02 08 76 03 10 00;A5 01 02 08 76 03 20 00
        list.add(2, CommonDataMeasure.EXTENT_LEFT); //左边仓房长度；
        return list;
    }

    /**
     * 上次扫描未完成 从中间开始扫描
     */
    @Override
    public Double midland(Socket socket, Date scanStart, String sbcode, String operateId, double firstScanH, double cfHeight, double zLXGD, double wd, List<GetXYVO> noZList) throws SocketTimeoutException, InterruptedException, IOException {
        //判断第几次扫描0第一次，1是第二次；
        Double maxpz = scanUtils.getMaxpz(noZList, socket, sbcode, operateId, firstScanH, cfHeight);
        return maxpz;
    }

    /**
     * 上次扫描未完成 从中间开始扫描
     */
    @Override
    public void midland2(Socket socket, Date scanStart, String sbcode, DeviceOperateDO deviceOperateDO, double firstScanH, double cfHeight, double zLXGD, double wd, List<GetXYVO> noZList) throws SocketTimeoutException, InterruptedException, IOException {
        DeviceOperateVO currentOperates = new DeviceOperateVO();
        LsBeanUtils.copyAllProperties(deviceOperateDO, currentOperates);
        currentOperates.setLsDxgu(firstScanH);
        currentOperates.setSbKssj(scanStart);
        currentOperates.setSbJssj(new Date());
        currentOperates.setSbCode(sbcode);
        currentOperates.setSbSmcs(1);
        currentOperates.setFlag(1);
        String operateId = deviceOperateDO.getId();
//        currentOperates.setId(UUIDUtils.getUUID());
        //保存设备扫描记录
        deviceOperateDomain.updateByPrimaryKeySelective(currentOperates);

        //判断第几次扫描0第一次，1是第二次；
        scanUtils.getMaxpz2(noZList, socket, sbcode, operateId, currentOperates.getSmgd(), cfHeight);

        //新增代码
        generateAlarm(socket, sbcode, operateId, wd);
    }

    /**
     * 根据上次扫描的最高z值 计算出垂直h，扫描粮面信息,secondScanH第一次扫描的最高点
     */
    @Override
    public void secondScanByMaxZ(String sbcode, DeviceOperateVO currentOperates, Date scanStart, Socket socket, double cfHeight, double zLXGD, double wd, double secondScanH) throws SocketTimeoutException, IOException, InterruptedException {
        currentOperates.setLsDxgu(secondScanH);
        currentOperates.setSbKssj(scanStart);
        currentOperates.setSbJssj(new Date());
        currentOperates.setSbCode(sbcode);
        currentOperates.setSbSmcs(1);
        currentOperates.setFlag(1);
//        currentOperates.setId(UUIDUtils.getUUID());
        //更新设备扫描记录
        deviceOperateDomain.updateByPrimaryKeySelective(currentOperates);
        String operateId2 = currentOperates.getId();

        //第二次扫描测粮面信息
        //1.产生数据并处理，将数据入库setSmcd  setSmkd setSmax setSmby
        dataProcessing(cfHeight, currentOperates.getSmcd(), currentOperates.getSmkd(), currentOperates.getSmax(), currentOperates.getSmby(), operateId2, sbcode, socket, secondScanH, zLXGD, wd);

        //新增代码
        generateAlarm(socket, sbcode, operateId2, wd);
    }

    /**
     * 1.产生数据并处理，将数据入库，2.发送命令并处理返回的数据入库
     *
     * @param cfgd      第一次扫描的最高点处理高度
     * @param smExtent  扫描长
     * @param smWide    扫描宽
     * @param extentAx  ax
     * @param wideBy    by
     * @param operateId 操作id
     * @param sbCode    设备编码
     * @param socket    cfgd仓房高度
     *                  zLXGD 装粮线高度
     *                  wd 融重
     * @throws IOException
     * @throws InterruptedException
     */
    public void dataProcessing(double cfgd, double smExtent, double smWide, double extentAx, double wideBy, String operateId, String sbCode, Socket socket, double smgd, double zLXGD, double wd) throws SocketTimeoutException, IOException, InterruptedException {

        //生成扫描的点数 并入库
        List<GetXYVO> getXYS = getXY(cfgd, smExtent, smWide, extentAx, wideBy, operateId, sbCode, smgd);

        //根据操作记录获取扫描点数集合
        scanUtils.getMaxpz2(getXYS, socket, sbCode, operateId, smgd, cfgd);

    }

    /**
     * 发送报警信息  （待完善）
     *
     * @param alarmParamVO 报警参数
     * @throws IOException
     * @throws InterruptedException
     */
    @Override
    public void sendAlarm(AlarmParamVO alarmParamVO, String msg) {
        //两个通知短信电话号码
        String smsNumber1 = alarmParamVO.getSmsNumber1();
        String smsNumber2 = alarmParamVO.getSmsNumber2();
        //两个通知电话电话号码
        String telephoneNumber1 = alarmParamVO.getTelephoneNumber1();
        String telephoneNumber2 = alarmParamVO.getTelephoneNumber2();
        //获取仓房名称，缺少库点名称
        String cfBH = alarmParamVO.getCfbh();

        CallAlarmQueryVO callAlarmQueryVO = deviceAlarmDomain.getCallAlarm(cfBH);
        //cfmc：仓房名称，kdmc：库点名称，ryxm：人员信息
        //待完善  需要想导入客户中加入变量  kdmc  cfmc  ryxm
        String cfmc = callAlarmQueryVO.getCfmc();
        String kdmc = callAlarmQueryVO.getKdmc();
        /*
         * 调用短信平台发送告警信息
         */
        String contens = "您好，" + kdmc + "" + cfmc + msg;
        String creater = "管理员";
        String msgPhone = kdmc + cfmc;
        MessageVo messageVo = new MessageVo();
        messageVo.setContens(contens);
        messageVo.setCreater(creater);
        if (StringUtils.isNotBlank(smsNumber1)) {
            messageVo.setMobile(smsNumber1);
            messageAsyncSendUtils.asyncSend(messageVo);
        }
        if (StringUtils.isNotBlank(smsNumber2)) {
            messageVo.setMobile(smsNumber2);
            messageAsyncSendUtils.asyncSend(messageVo);
        }
        if (StringUtils.isNotBlank(telephoneNumber1)) {
            messageAsyncSendUtils.asyncSendPhone(telephoneNumber1, msgPhone);
        }
        if (StringUtils.isNotBlank(telephoneNumber2)) {
            messageAsyncSendUtils.asyncSendPhone(telephoneNumber2, msgPhone);
        }
    }

    /**
     * 生成告警信
     *
     * @param sbCode 设备编码
     */
    @Override
    public void generateAlarm(Socket socket, String sbCode, String operateId, double wd) throws SocketTimeoutException, IOException, InterruptedException {
        Map<String, Object> cfdata = scanPointsDomain.getCfdata(sbCode);
        double smExtent = Double.valueOf(cfdata.get("CFNC").toString());//仓房长度
        double smWide = Double.valueOf(cfdata.get("CFNK").toString());//仓房宽度
        double zLXGD = Double.valueOf(cfdata.get("ZLXGD").toString());//获取装粮线高度
        HashMap<String, Object> map = Maps.newHashMap();
        map.put("operateId",operateId);
        map.put("zLXGD",zLXGD);
        map.put("sbCode",sbCode);

        int byOperateIdCOUNT = pointsDomain.getByOperateIdCOUNT(map); //扫描点个数
        int byOperateIdCOUNTCopy = pointsDomain.getByOperateIdCOUNTCopy(map);//第一次扫描点个数

        //扫描平均等效高度
        double finalAvgZz = pointsDomain.getFinalAvgZz(map);
        //最后一次的z值的总和
        double finalAvgZ = pointsDomain.getFinalAvgZ(map);
        double finalAvgZCopy = pointsDomain.getFinalAvgZCopy(map);
        //倒数第二次的z值的总和
        double penultAvgZ = pointsDomain.getPenultAvgZ(map);
        double penultAvgZCpoy = pointsDomain.getPenultAvgZCopy(map);
        //根据仓房的长宽算出最后一次和倒数第二次的粮食的体积和重量  finalAvgZ * （（（左边长+右边长）*仓房宽 ）/ 有效点数）
        double finalLsTj = (finalAvgZ + finalAvgZCopy) * (smExtent * smWide / (byOperateIdCOUNT + byOperateIdCOUNTCopy));
        double finalLsZl = wd * finalLsTj / 1000 / 1000 * 1000;
        double penultLsTj = (penultAvgZ + penultAvgZCpoy) * (smExtent * smWide / (byOperateIdCOUNT + byOperateIdCOUNTCopy));
        double penultLsZl = wd * penultLsTj / 1000 / 1000 * 1000;

        //根据operateId 修改 粮食的体积和重量 (待测试，写完没测试)
        DeviceOperateVO currentOperate = new DeviceOperateVO();
        currentOperate.setId(operateId);
        currentOperate.setLsTj(finalLsTj);
        currentOperate.setLsZl(finalLsZl);
        currentOperate.setLsDxgu(finalAvgZz);
        currentOperate.setSbJssj(new Date());
        currentOperate.setFlag(1);
        deviceOperateDomain.updateTjAndZl(currentOperate);

        //计算告警值（粮食的平均重量）   (finalZSum/最后一次扫描的总点数-penultZSum/倒数第二次的总点数)*(a*b)*密度
        double alarmValue = finalLsZl - penultLsZl;

        //根据设备code获取设备仓房对应的告警参数
        AlarmParamVO alarmParamVO = alarmParamDomain.getAlarmParamBySbCode(sbCode);
        //如果告警值大于逐次报警阀值则生成报警记录，并报警
        boolean booleanTemp = false;
        if (alarmParamVO != null && alarmParamVO.getTimeThreshold() != null && (alarmValue > alarmParamVO.getTimeThreshold())) {
            //报警记录生成
            DeviceAlarmVO deviceAlarmVO = new DeviceAlarmVO();
            //当前体积重量和上一次的体积重量  （待完善）
            deviceAlarmVO.setId(UUIDUtils.getUUID());
            deviceAlarmVO.setLsCurrTj(finalLsTj);
            deviceAlarmVO.setLsLastTj(penultLsTj);
            deviceAlarmVO.setLsCurrZl(finalLsZl);
            deviceAlarmVO.setLsLastZl(penultLsZl);
            deviceAlarmVO.setSbCode(sbCode);
            deviceAlarmVO.setCreateDate(new Date());
            //报警类型  0:设备断开  1:总数  2:逐次数量
            deviceAlarmVO.setType(2);
            //报警状态  0：未处理  1：已处理
            deviceAlarmVO.setStatus("0");
            deviceAlarmDomain.saveDeviceAlarm(deviceAlarmVO);
            booleanTemp = true;
        } else if (alarmParamVO != null && alarmParamVO.getTotalStatus() == 1) {
            //获取总数报警阀值
            Double lsZl = alarmParamVO.getLsZl();
            //减去本次检测值
            double difference = lsZl - finalLsZl;
            if (difference > alarmParamVO.getTotalThreshold()) {
                //总数报警记录生成
                DeviceAlarmVO deviceAlarmVO = new DeviceAlarmVO();
                //当前体积重量和上一次的体积重量  （待完善）
                deviceAlarmVO.setId(UUIDUtils.getUUID());
                deviceAlarmVO.setLsCurrTj(finalLsTj);
                deviceAlarmVO.setLsLastTj(penultLsTj);
                deviceAlarmVO.setLsCurrZl(finalLsZl);
                deviceAlarmVO.setLsLastZl(penultLsZl);
                deviceAlarmVO.setSbCode(sbCode);
                deviceAlarmVO.setCreateDate(new Date());
                //报警类型  0:设备断开  1:总数  2:逐次数量
                deviceAlarmVO.setType(1);
                //报警状态  0：未处理  1：已处理
                deviceAlarmVO.setStatus("0");
                deviceAlarmDomain.saveDeviceAlarm(deviceAlarmVO);
                booleanTemp = true;
            }
        }
        if (booleanTemp) {
            //声光报警
//        callAlarm(socket);
            /**
             * 发送报警
             */
            sendAlarm(alarmParamVO, CommonDataMeasure.MSG);
        }
    }

    @Override
    public String getSbCodeByBh(String cfbh) {
        return pointsDomain.getSbCodeByBh(cfbh);
    }

    @Override
    public List<GetXYVO> getNotZList() {
        return pointsDomain.getNotZList();
    }

    @Override
    public String getOperateId(String sbCode) {
        return pointsDomain.getOperateId(sbCode);
    }

    @Override
    public List<GetXYVO> getNoZList(String operateId) {
        return pointsDomain.getNoZList(operateId);
    }

    @Override
    public List<GetXYVO> getNoZList2(String operateId) {
        return pointsDomain.getNoZList2(operateId);
    }


    /**
     * 根据x，y拼接命令
     *
     * @param getxy x，y集合
     * @return 命令集合
     */
    public List<String> concatOders(List<GetXYVO> getxy) {

        //定义初始化命令字符串
        String order = "";

        //初始化命令集合
        List<String> orders = new ArrayList();

        for (int i = 0; i < getxy.size(); i++) {
            String x = DataMeasureUtils.lowHigh((int) getxy.get(i).getPointX());
            String y = DataMeasureUtils.lowHigh((int) getxy.get(i).getPointY());

            order = CommonDataMeasure.HEAD + " " + CommonDataMeasure.SLAVE_ID + " " + CommonDataMeasure.CODE_ID + " " +
                    x + " " + y + " " + CommonDataMeasure.CHECK_CODE;

            orders.add(order);
        }
        return orders;
    }

    /**
     * 获取x y 入库x，y 并入库扫描记录
     *
     * @param extent           长
     * @param wide             宽
     * @param cfgd             高
     * @param houseLengthLeft  仓房左边长
     * @param houseLengthRight 仓房右边长
     * @return 返回x，y集合
     * h仓房高度,extent仓房长度,wide仓房宽度,extentAx,wideBy,scanStart,sbCode,times zLXGD装粮线高度
     */
    public List<GetXYVO> getXY(double cfgd, double extent,
                               double wide, double houseLengthRight, double houseLengthLeft, String operateId, String sbCode, double zLXGD) {
        List<GetXYVO> getScanPoints = DataMeasureUtils.getXYStepts1(cfgd, extent, wide, houseLengthRight, houseLengthLeft, zLXGD, operateId, sbCode);
        //x ， y 入库
        pointsDomain.insertPointsXOrY(getScanPoints);
        return getScanPoints;
    }

    @Override
    /**
     * 发送报警
     */
    public void callAlarm(Socket socket) throws IOException, InterruptedException {
        //打开报警
        String openAlarm = CommonDataMeasure.ALARM_ON;
        //关闭报警
        String closeAlarm = CommonDataMeasure.ALARM_OFF;

        socketUtils.sendRequest(socket, openAlarm);
        //延长十秒
        Thread.sleep(10000);
        socketUtils.sendRequest(socket, closeAlarm);

    }

    /**
     * 启动风扇
     */
    public void fanStart(Socket socket) throws IOException, InterruptedException {
        // 打开风扇
        String openFan = CommonDataMeasure.FAN_ON;
        // 关闭风扇
        String closeFan = CommonDataMeasure.FAN_OFF;

        socketUtils.sendRequest(socket, openFan);
        //延长十秒
        Thread.sleep(10000);
        socketUtils.sendRequest(socket, closeFan);
    }

    /**
     * 之前版本
     */
    @Transactional
    @Override
    public Map<String, Object> savePointsData(String ip, Integer port, Integer slaveId, Date startTime)
            throws ModbusTransportException, ErrorResponseException, ModbusInitException, IOException, InterruptedException {
        //1.获取采样总点数
        Number pointsTotal = Modbus4jUtils.readHoldingRegister(ip, port, slaveId, Register.READ_POINTS, DataType.TWO_BYTE_INT_SIGNED);
        log.info("pointsTotal:" + pointsTotal);
        if (pointsTotal.intValue() > 0) {

            //2.根据设备ip，port获取设备信息
            DeviceInfoQueryVO paramsInfoVO = new DeviceInfoQueryVO();
            paramsInfoVO.setSbIpdz(ip);
            paramsInfoVO.setSbDkh(port + "");
            DeviceInfoVO deviceInfo = deviceInfoDomain.getDeviceInfoByQuery(paramsInfoVO);
            String sbCode = deviceInfo.getSbCode();

            //3.读取扫描记录数据
            DeviceOperateVO currentOperate = getDeviceOperte(ip, port, slaveId);
            currentOperate.setSbKssj(startTime);
            currentOperate.setSbJssj(new Date());
            currentOperate.setSbCode(sbCode);

            //根据设备code获取设备仓房对应的告警参数
            AlarmParamVO alarmParamVO = alarmParamDomain.getAlarmParamBySbCode(sbCode);
            //根据设备code获取上次扫描数据
            DeviceOperateVO lastOperate = deviceOperateDomain.getLastOperate(sbCode);
            //当前粮食重量与上次粮食重量差值
            Double DifferenceZl = Math.abs(currentOperate.getLsZl() - lastOperate.getLsZl()) / 10;
            //重量差值大于逐次报警阀值则生成报警记录，并报警
            if (DifferenceZl > alarmParamVO.getTimeThreshold()) {
                //报警记录生成
                DeviceAlarmVO deviceAlarmVO = new DeviceAlarmVO();
                deviceAlarmVO.setLsCurrTj(currentOperate.getLsTj());
                deviceAlarmVO.setLsLastTj(lastOperate.getLsTj());
                deviceAlarmVO.setLsCurrZl(currentOperate.getLsZl());
                deviceAlarmVO.setLsLastZl(lastOperate.getLsZl());
                deviceAlarmVO.setSbCode(sbCode);
                deviceAlarmVO.setCreateDate(new Date());
                //报警类型  0:设备断开  1:总数  2:逐次数量
                deviceAlarmVO.setType(2);
                deviceAlarmDomain.saveDeviceAlarm(deviceAlarmVO);

                /**
                 * 发送报警
                 */


            }

            //保存设备扫描记录
            String operateId = deviceOperateDomain.insert(currentOperate);


            //4.获取监测点数信息
            List<ScanPointsVO> sScanPointsList = new ArrayList<>();
            //计算出取值次数，总点数*6=总点数站占寄存器地址数，每次取125*2个寄存器
            int readTimes = (int) Math.ceil(pointsTotal.intValue() * 3 / (Register.POINTS_VALUES_READ_LENGTH * 2));
            log.info("readTimes:" + readTimes);

            String str = "yyyMMdd";
            SimpleDateFormat sdf = new SimpleDateFormat(str);

            String textFileName1 = sdf.format(new Date()) + currentOperate.getSbSmcs() + "_handle_data1";
            //取每个点数据
            for (int i = 0; i < pointsTotal.intValue(); i++) {
                ScanPointsVO scanPoints = new ScanPointsVO();
                Number x = Modbus4jUtils.readHoldingRegister(ip, port, slaveId, Register.POINTS_VALUES_START + i * 3, DataType.TWO_BYTE_INT_SIGNED);
                scanPoints.setPointX(x.doubleValue());
                Number y = Modbus4jUtils.readHoldingRegister(ip, port, slaveId, Register.POINTS_VALUES_START + i * 3 + 1, DataType.TWO_BYTE_INT_SIGNED);
                scanPoints.setPointY(y.doubleValue());
                Number z = Modbus4jUtils.readHoldingRegister(ip, port, slaveId, Register.POINTS_VALUES_START + i * 3 + 2, DataType.TWO_BYTE_INT_SIGNED);
                scanPoints.setPointZ(z.doubleValue() / 10);

                scanPoints.setSbCode(sbCode);
                scanPoints.setOperateId(operateId);
                sScanPointsList.add(scanPoints);

                FileUtils.writerData(textFileName1, scanPoints.toString());
            }

            //5.存入扫描点位数据库
            pointsDomain.insertPointsList(sScanPointsList);
            //save2Text(textFileName,sScanPointsList);
            return ReturnResult.successResult("点数数据获取成功");

        } else {
            return ReturnResult.errorResult("总点数为0");
        }
    }


    @Override
    public List<ScanPointsVO> getPointList(ScanPointsQueryVO pointsQueryVO) {

        return pointsDomain.getPointListByQuery(pointsQueryVO);
    }


    private DeviceOperateVO getDeviceOperte(String ip, int port, int slaveId) throws ModbusTransportException, ErrorResponseException, ModbusInitException {

        DeviceOperateVO operate = new DeviceOperateVO();

        Number lsdxgu = Modbus4jUtils.readHoldingRegister(ip, port, slaveId, Register.LSDXGU, DataType.TWO_BYTE_INT_SIGNED);

        Number scanTimes = Modbus4jUtils.readHoldingRegister(ip, port, slaveId, Register.SCAN_TIMES, DataType.TWO_BYTE_INT_SIGNED);

        ByteQueue tjQueue = Modbus4jUtils.readHoldingRegisters(ip, port, slaveId, Register.LSTJ_FIRST, 2);

        ByteQueue zlQueue = Modbus4jUtils.readHoldingRegisters(ip, port, slaveId, Register.LSZL_FIRST, 2);

        String str = "yyyMMdd";
        SimpleDateFormat sdf = new SimpleDateFormat(str);
        String fileName = sdf.format(new Date()) + scanTimes + "_operate";

        FileUtils.writerData(fileName, lsdxgu);
        FileUtils.writerData(fileName, scanTimes);
        FileUtils.writerData(fileName, tjQueue);
        FileUtils.writerData(fileName, zlQueue);

        operate.setLsDxgu(lsdxgu.intValue() / 10.0);
        String[] tjs = tjQueue.toString().substring(1, tjQueue.toString().length() - 1).split(",");
        operate.setLsTj(Double.valueOf(new BigInteger(tjs[5] + tjs[6] + tjs[3] + tjs[4], 16).toString(10)));
        String[] zls = zlQueue.toString().substring(1, zlQueue.toString().length() - 1).split(",");
        operate.setLsZl(Double.valueOf(new BigInteger(zls[5] + zls[6] + zls[3] + zls[4], 16).toString(10)));
        operate.setSbSmcs(scanTimes.intValue());
        operate.setSbKssj(new Date());
        operate.setRemark("备注");
        return operate;
    }

    private List<ScanPointsVO> getPointsData(String pointsStr, Double lsdxgu) {

        List<ScanPointsVO> sScanPointsList = new ArrayList<>();
        if (StringUtils.isNotBlank(pointsStr) && pointsStr.contains(",")) {
            String[] datas = pointsStr.split(",");
            for (int index = 0; index < datas.length / 6; index++) {
                ScanPointsVO sScanPoints = new ScanPointsVO();
                String pointX = new BigInteger(datas[index * 6] + datas[index * 6 + 1], 16).toString(10);
                sScanPoints.setPointX(Double.valueOf(pointX));

                String pointY = new BigInteger(datas[index * 6 + 2] + datas[index * 6 + 3], 16).toString(10);
                sScanPoints.setPointY(Double.valueOf(pointY));

                String pointZ = new BigInteger(datas[index * 6 + 4] + datas[index * 6 + 5], 16).toString(10);
                Double z = Double.valueOf(pointZ) / 10;
                if (lsdxgu - z > lsdxgu / 4) {
                    z = lsdxgu;
                }

                sScanPoints.setPointZ(z);
                sScanPoints.setCreateDate(new Date());
                sScanPointsList.add(sScanPoints);
            }
        }

        return sScanPointsList;

    }

    @Override
    public List<DeviceInfoVO> getDeviceInfoList(DeviceInfoQueryVO deviceInfoQueryVO) {

        return deviceInfoDomain.getDeviceInfoList(deviceInfoQueryVO);
    }

    @Override
    public List<DeviceOperateVO> getDeviceOperateList(DeviceInfoQueryVO deviceInfoQueryVO) {

        return deviceOperateDomain.getDeviceOperateList(deviceInfoQueryVO);
    }

    /**
     * <p>Title: test</p>
     * <p>Description: </p>
     *
     * @see com.unis.detectionnum.server.service.ScanPointsService#test()
     */
    @Override
    public void test() {
        System.out.println(12344555);

    }

    /**
     * <p>Title: recordList</p>
     * <p>Description: </p>
     *
     * @param scanDataQueryVO
     * @return
     * @see com.unis.detectionnum.server.service.ScanPointsService#(com.unis.detectionnum.common.vo.ScanDataQueryVO)
     */
    @Override
    public List<ScanDataRecordVO> recordList(ScanDataQueryVO scanDataQueryVO) {
        List<ScanDataRecordVO> scanDataRecordVOList = deviceOperateDomain.recordList(scanDataQueryVO);
        if (scanDataRecordVOList != null) {
            for (int i = 0; i < scanDataRecordVOList.size(); i++) {
                ScanDataRecordVO scanDataRecordVO = scanDataRecordVOList.get(i);
                if (scanDataRecordVO.getCfbh() != null) {
                    String cfbh = scanDataRecordVO.getCfbh();
                    Map<String, Object> byCfbn = deviceOperateDomain.getByCfbn("%" + cfbh + "%");
                    if (byCfbn != null && StringUtils.isNotBlank(byCfbn.get("userName").toString())) {
                        scanDataRecordVO.setUserName(byCfbn.get("userName").toString());
                    }
                }
            }
        }
        return scanDataRecordVOList;
    }

    /**
     * <p>Title: getPointDataInfo</p>
     * <p>Description: </p>
     *
     * @param operateId
     * @return
     */
    @Override
    public ArrayList<ArrayList<Double>> getPointDataInfo(Map<String, Object> map) {
        // TODO Auto-generated method stub
        return pointsDomain.getPointDataInfo(map);
    }

    /**
     * 两个double类型的数相除，保留两位小数
     *
     * @param a
     * @param b
     * @param scale
     * @return
     */
    private static double divide(double a, double b, int scale) {
        BigDecimal bd1 = new BigDecimal(Double.toString(a));
        BigDecimal bd2 = new BigDecimal(Double.toString(b));
        return bd1.divide(bd2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    @Override
    public DeviceOperateVO getOperate(String sbCode, Double h, Date scanStart, Integer sbSmcs, double extentRight, double extentLeft) {
        Map<String, Object> cfdata = scanPointsDomain.getCfdata(sbCode);
        double smExtent = Double.valueOf(cfdata.get("CFNC").toString());//extentAx
        double smWide = Double.valueOf(cfdata.get("CFNK").toString());//wideBy
        double cfHeight = Double.valueOf(cfdata.get("JD").toString());//wideBy获取设备高度
        double zLXGD = 0;//获取装粮食线高度
//        double zLXGD = Double.valueOf(cfdata.get("ZLXGD").toString());//获取装粮食线高度
        double wd = Double.valueOf(cfdata.get("WD").toString());//融重
        smWide = getEven(smWide);
        //生成扫描记录
        DeviceOperateVO currentOperate = new DeviceOperateVO();
        currentOperate.setLsDxgu(h);
        currentOperate.setSbKssj(scanStart);
        currentOperate.setSbJssj(new Date());
        currentOperate.setSbCode(sbCode);
        currentOperate.setSbSmcs(sbSmcs);
        currentOperate.setSmgd(h);
        currentOperate.setSmcd(smExtent);
        currentOperate.setSmkd(smWide);
        currentOperate.setSmax(extentRight);//仓房右边长
        currentOperate.setSmby(extentLeft);//仓房左边长
        currentOperate.setFlag(0);
        currentOperate.setCfgd(cfHeight);
        currentOperate.setZLXGD(zLXGD);
        currentOperate.setWd(wd);
        return currentOperate;
    }

    @Override
    public Map<String, Object> getCfByid(String id) {
        Map<String, Object> cfByid = deviceOperateDomain.getCfByid(id);
        return cfByid;
    }

    /**
     * 获取偶数
     *
     * @param num
     * @return 返回偶数
     */
    private double getEven(double num) {
        int x = (int) num;
        if (x % 2 != 0) {
            x = x - 1;
        }
        return x;
    }
}
