Newer
Older
smartwell_demos / src / main / java / com / casic / service / impl / ThirdDataServiceImpl.java
package com.casic.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.casic.dao.AlarmRecordMapper;
import com.casic.dao.BusWellInfoMapper;
import com.casic.dao.DataH2sMapper;
import com.casic.enums.H2sParamsEnum;
import com.casic.model.AlarmRecord;
import com.casic.model.BusWellInfo;
import com.casic.model.DataH2s;
import com.casic.model.User;
import com.casic.service.ThirdDataService;
import com.casic.util.RedisCommon;
import com.casic.util.SMSSendUtil;
import com.casic.util.WebSocket;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
@RequiredArgsConstructor
public class ThirdDataServiceImpl extends ServiceImpl<DataH2sMapper, DataH2s> implements ThirdDataService, H2sParamsEnum {

    private final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA);
    private final RedisCommon redisCommon;
    private final AlarmRecordMapper alarmRecordMapper;
    private final BusWellInfoMapper wellMapper;
    private final WebSocket webSocket;
    private final SMSSendUtil smsUtil;
    private final DataScopeBuilder dataScopeBuilder;

    @Value("${casic.device.apn}")
    private String apn;

    private final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingDeque<>(10));


    @Override
    public Object h2sData(Map<String, String> h2sDataMap) {
        try {
            if (h2sDataMap.containsKey(ICCID)) {
                //开机上报->存储映射管理->查询是否有下发配置-进行回应
                return powerOnReport(h2sDataMap);
            } else if (h2sDataMap.containsKey(H2S_VALUE)) {                //数据上报
                if (ObjectUtils.isNotEmpty(h2sDataMap.get(DEVCODE))) {
                    String devcode = ((String) h2sDataMap.get(DEVCODE)).toUpperCase();
                    //清除离线-清除报警-报警-存库-查询是否有下发配置-进行回应
                    return dataSave(h2sDataMap, devcode);
                }
            }
        } catch (DataAccessException dae) {
            log.error("设备上报数据异常,设备传入的json是{},异常信息{}", h2sDataMap, dae.getMessage());
        }
        return defaultDeliveryConfig(null);
    }

    @Override
    public void configConfirm(Map<String, String> configConfirmMap) {
        if (configConfirmMap.containsKey(DEVCODE) && !StringUtils.isEmpty(configConfirmMap.get(DEVCODE))) {
            String devcode = ((String) configConfirmMap.get(DEVCODE)).toUpperCase();
            this.baseMapper.configConfirm(devcode);
        }
    }

    private Map<String, Object> defaultDeliveryConfig(Map<String, Object> configDataMap) {
        Map<String, Object> map = new HashMap();
        //必带参数,否则会被thingsboard舍弃
        map.put("method", "setParams");
        //判断是否有下发配置必须
        map.put("status", ObjectUtils.isNotEmpty(configDataMap) ? 200 : 201);
        if (ObjectUtils.isNotEmpty(configDataMap) && configDataMap.containsKey("interval")) {
            configDataMap.put("interval", String.valueOf(configDataMap.get("interval")));
            configDataMap.put("port", String.valueOf(configDataMap.get("port")));
            configDataMap.put("apn", apn);
            configDataMap.put("period", String.valueOf(configDataMap.get("period")) );
            configDataMap.put("repeat", String.valueOf(configDataMap.get("repeat")) );
            configDataMap.put("thresh", String.valueOf(configDataMap.get("thresh")) );
        }
        map.put("params", configDataMap);
        //可省略
        map.put("timeout", 30000);
        if (ObjectUtils.isNotEmpty(configDataMap)) {
            log.info("发送配置信息到things board: {}", map);
        }
        return map;
    }

    /**
     * 开机上报->储存,是否有下发配置,选择合适的格式返回
     *
     * @param h2sDataMap
     */
    private Map<String, Object> powerOnReport(Map<String, String> h2sDataMap) {
        /**
         * 三码上报
         */
        String iccid = (String) h2sDataMap.get(ICCID);
        String imei = (String) h2sDataMap.get(IMEI);
        String devcode = (String) h2sDataMap.get(DEVCODE);
        //清除离线
        this.baseMapper.clearOnline(devcode);
        //先查,是否有数据,有数据直接覆盖,否则插入数据
        String isImei = this.baseMapper.getImeiByDevcode(devcode);
        int save = StringUtils.isEmpty(isImei) ? this.baseMapper.addImeiDevcode(devcode, imei, iccid) :
                this.baseMapper.updateImeiDevcode(devcode, imei, iccid);
        if (save > 0) {
            //保存三码关系
            Map<String, Object> configDataMap = redisCommon.getMsg((String) h2sDataMap.get(DEVCODE));
            if (ObjectUtils.isNotEmpty(configDataMap)) {
                return defaultDeliveryConfig(configDataMap);
            }
        } else {
            throw new RuntimeException("三码上传保存异常,异常数据为" + JSON.toJSON(h2sDataMap));
        }
        return defaultDeliveryConfig(null);
    }

    private Map<String, Object> dataSave(Map<String, String> h2sDataMap, String devcode) {
        DataH2s dataH2s = initH2sData(h2sDataMap, devcode);
        this.baseMapper.clearOnline(devcode);
        if (ObjectUtils.isNotEmpty(dataH2s)) {
            checkAlarm(dataH2s);
            Map<String, Object> configDataMap = redisCommon.getMsg(devcode);
            if (ObjectUtils.isNotEmpty(configDataMap)) {
                log.info("有需要下发的配置项: {}", configDataMap);
                return defaultDeliveryConfig(configDataMap);
            }
        }
        return defaultDeliveryConfig(null);
    }

    private DataH2s initH2sData(Map<String, String> h2sDataMap, String devcode) {
        DataH2s dataH2s = new DataH2s();
        String wellCode = this.baseMapper.getWellCode(devcode);
        dataH2s.setWellCode(wellCode);
        dataH2s.setDevcode(devcode);
        dataH2s.setSnr(h2sDataMap.get(SIGNAL_STRENGTH));
        dataH2s.setStrength((String) h2sDataMap.get(H2S_VALUE));
        dataH2s.setCell((String) h2sDataMap.get(BATTERY_VALUE));
        dataH2s.setUptime(dateFormat.format(new Date()));
        dataH2s.setLogtime(dateFormat.format(new Date()));
        this.baseMapper.insert(dataH2s);
        return dataH2s;
    }

    /**
     * 检查是否有报警
     *
     * @param dataH2s
     */
    private void checkAlarm(DataH2s dataH2s) {
        final Float ruleValue = this.baseMapper.getRuleValue("14");
        if (ruleValue == null) {
            return;
        }
        if (!StringUtils.isEmpty(dataH2s.getStrength()) && !"null".equals(dataH2s.getStrength())) {
            String status = "0";
            int currAlarmCount = this.baseMapper.cancelAlarm(dataH2s.getDevcode(), status);
            if (Float.parseFloat(dataH2s.getStrength()) > ruleValue) {
                Long id = this.baseMapper.getDeviceId(dataH2s.getDevcode());
                alarmRecordMapper.insert(buildAlarmRecord(dataH2s, id));

                // 推送websocket 一直超限可以一直推送websocket消息
                threadPoolExecutor.execute(() -> sendAlarm("硫化氢浓度超限", dataH2s.getDevcode(), dataH2s.getStrength(),
                        this.baseMapper.getDeptIdsByDevcode(dataH2s.getDevcode())));

                // 是新报警的时候才推送报警短信
                if (currAlarmCount == 0) {
                    threadPoolExecutor.execute(() -> sendAlarmSms("检测到硫化氢浓度超限", dataH2s.getWellCode(), dataH2s.getStrength(), dataH2s.getUptime()));
                }
            }
        }
    }

    private AlarmRecord buildAlarmRecord(DataH2s dataH2s, Long deviceId) {
        AlarmRecord alarmRecord = AlarmRecord.builder()
                .devcode(dataH2s.getDevcode())
                .wellCode(dataH2s.getWellCode())
                .alarmMessage("浓度超限")
                .alarmValue(dataH2s.getStrength())
                .deviceId(deviceId)
                .alarmType("1")
                .alarmContent("浓度超限")
                .status("1")
                .build();
        return alarmRecord;
    }

    // PC推送
    private void sendAlarm(String msg, String devCode, String value, Long deptId) {
        List<String> userIds = new ArrayList<>();
        List<User> userList = dataScopeBuilder.DataScopeProvider(deptId);
        for (User user : userList) {
            userIds.add(user.getId().toString());
        }
        if (userIds.size() > 0) {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("message", msg);
            map.put("type", "alarm");
            map.put("deviceNo", devCode);
            map.put("value", value);
            webSocket.sendListMessage(userIds, JSON.toJSONString(map));
        } else {
            log.info("告警消息找不到责任人,pc端未推送:" + msg);
        }
    }

    private void sendAlarmSms(String msg, String wellCode, String value, String alarmTime) {
        BusWellInfo wellInfo = wellMapper.getWellListByCode(wellCode);
        if (null != wellInfo && ObjectUtil.isNotEmpty(wellInfo.getTel())) {
            // 拼接报警信息
            String content = msg + "," + // 发现硫化氢泄漏,
                    "点位:" + wellInfo.getWellName() + ",详细位置:" + wellInfo.getPosition() + "," + // 点位:名称,位置,
                    "浓度值:" + value + "ppm," + // 浓度值
                    "时间:" + alarmTime + "," + // 时间
                    "请及时处理。"; // 请及时处理。

            smsUtil.sendSms(wellInfo.getTel(), content);
        }
    }
}