feat(device): 新增发送紧急通知功能
- 在 AppDeviceXinghanController 中添加 sendAlarmMessage接口 - 在 DeviceXinghanBizService 中实现 sendAlarmMessage 方法 - 新增 XinghanSendAlarmMessageRule 类用于处理紧急通知发送逻辑 - 在 DeviceRedisKeyConstants 中添加 DEVICE_ALARM_MESSAGE_KEY_PREFIX 常量 - 修改 XinghanDeviceDataRule 和 XinghanSendMsgRule 中的相关逻辑
This commit is contained in:
@ -6,7 +6,9 @@ import com.fuyuanshen.app.domain.dto.DeviceInstructDto;
|
|||||||
import com.fuyuanshen.common.core.domain.R;
|
import com.fuyuanshen.common.core.domain.R;
|
||||||
import com.fuyuanshen.common.core.validate.AddGroup;
|
import com.fuyuanshen.common.core.validate.AddGroup;
|
||||||
import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessAnnotation;
|
import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessAnnotation;
|
||||||
|
import com.fuyuanshen.common.ratelimiter.annotation.FunctionAccessBatcAnnotation;
|
||||||
import com.fuyuanshen.common.web.core.BaseController;
|
import com.fuyuanshen.common.web.core.BaseController;
|
||||||
|
import com.fuyuanshen.equipment.domain.dto.AppDeviceSendMsgBo;
|
||||||
import com.fuyuanshen.web.service.device.DeviceBJQBizService;
|
import com.fuyuanshen.web.service.device.DeviceBJQBizService;
|
||||||
import com.fuyuanshen.web.service.device.DeviceXinghanBizService;
|
import com.fuyuanshen.web.service.device.DeviceXinghanBizService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@ -33,6 +35,15 @@ public class AppDeviceXinghanController extends BaseController {
|
|||||||
return toAjax(appDeviceService.registerPersonInfo(bo));
|
return toAjax(appDeviceService.registerPersonInfo(bo));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送紧急通知
|
||||||
|
*/
|
||||||
|
@PostMapping(value = "/sendAlarmMessage")
|
||||||
|
@FunctionAccessBatcAnnotation(value = "sendAlarmMessage", timeOut = 5, batchMaxTimeOut = 10)
|
||||||
|
public R<Void> sendAlarmMessage(@RequestBody AppDeviceSendMsgBo bo) {
|
||||||
|
return toAjax(appDeviceService.sendAlarmMessage(bo));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传设备logo图片
|
* 上传设备logo图片
|
||||||
*/
|
*/
|
||||||
|
@ -47,4 +47,9 @@ public class DeviceRedisKeyConstants {
|
|||||||
* 告警
|
* 告警
|
||||||
*/
|
*/
|
||||||
public static final String DEVICE_ALARM_KEY_PREFIX = ":alarm";
|
public static final String DEVICE_ALARM_KEY_PREFIX = ":alarm";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 告警信息
|
||||||
|
*/
|
||||||
|
public static final String DEVICE_ALARM_MESSAGE_KEY_PREFIX = ":alarmMessage";
|
||||||
}
|
}
|
||||||
|
@ -14,11 +14,13 @@ import com.fuyuanshen.global.mqtt.base.MqttXinghanJson;
|
|||||||
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
|
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
|
||||||
import com.fuyuanshen.global.mqtt.constants.LightingCommandTypeConstants;
|
import com.fuyuanshen.global.mqtt.constants.LightingCommandTypeConstants;
|
||||||
import com.fuyuanshen.global.mqtt.constants.XingHanCommandTypeConstants;
|
import com.fuyuanshen.global.mqtt.constants.XingHanCommandTypeConstants;
|
||||||
|
import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
@ -58,6 +60,7 @@ public class XinghanDeviceDataRule implements MqttMessageRule {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(MqttRuleContext context) {
|
public void execute(MqttRuleContext context) {
|
||||||
|
String functionAccess = FUNCTION_ACCESS_KEY + context.getDeviceImei();
|
||||||
try {
|
try {
|
||||||
// Latitude, longitude
|
// Latitude, longitude
|
||||||
//主灯档位,激光灯档位,电量百分比,充电状态,电池剩余续航时间
|
//主灯档位,激光灯档位,电量百分比,充电状态,电池剩余续航时间
|
||||||
@ -65,8 +68,10 @@ public class XinghanDeviceDataRule implements MqttMessageRule {
|
|||||||
|
|
||||||
// 发送设备状态和位置信息到Redis
|
// 发送设备状态和位置信息到Redis
|
||||||
asyncSendDeviceDataToRedisWithFuture(context.getDeviceImei(),deviceStatus);
|
asyncSendDeviceDataToRedisWithFuture(context.getDeviceImei(),deviceStatus);
|
||||||
|
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("处理上报数据命令时出错", e);
|
log.error("处理上报数据命令时出错", e);
|
||||||
|
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(20));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,118 @@
|
|||||||
|
package com.fuyuanshen.global.mqtt.rule.xinghan;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fuyuanshen.common.json.utils.JsonUtils;
|
||||||
|
import com.fuyuanshen.common.redis.utils.RedisUtils;
|
||||||
|
import com.fuyuanshen.global.mqtt.base.MqttMessageRule;
|
||||||
|
import com.fuyuanshen.global.mqtt.base.MqttRuleContext;
|
||||||
|
import com.fuyuanshen.global.mqtt.config.MqttGateway;
|
||||||
|
import com.fuyuanshen.global.mqtt.constants.MqttConstants;
|
||||||
|
import com.fuyuanshen.global.mqtt.constants.XingHanCommandTypeConstants;
|
||||||
|
import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY;
|
||||||
|
import static com.fuyuanshen.common.core.constant.GlobalConstants.GLOBAL_REDIS_KEY;
|
||||||
|
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_ALARM_MESSAGE_KEY_PREFIX;
|
||||||
|
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 星汉设备发送紧急通知 下发规则:
|
||||||
|
* <p>
|
||||||
|
* 1. 设备上行 sta_BreakNews=cover! => 仅标记成功<br>
|
||||||
|
* 2. 设备上行 sta_BreakNews=数字 => GBK编码,每行文字为一包,一共4包,第一字节为包序号
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class XinghanSendAlarmMessageRule implements MqttMessageRule {
|
||||||
|
|
||||||
|
private final MqttGateway mqttGateway;
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandType() {
|
||||||
|
return XingHanCommandTypeConstants.XingHan_BREAK_NEWS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(MqttRuleContext ctx) {
|
||||||
|
String functionAccess = FUNCTION_ACCESS_KEY + ctx.getDeviceImei();
|
||||||
|
try {
|
||||||
|
XinghanSendAlarmMessageRule.MqttXinghanAlarmMsgJson payload = objectMapper.convertValue(
|
||||||
|
ctx.getPayloadDict(), XinghanSendAlarmMessageRule.MqttXinghanAlarmMsgJson.class);
|
||||||
|
|
||||||
|
String respText = payload.getStaBreakNews();
|
||||||
|
log.info("设备上报紧急通知握手: {} ", respText);
|
||||||
|
|
||||||
|
// 1. cover! —— 成功标记
|
||||||
|
if ("cover!".equalsIgnoreCase(respText)) {
|
||||||
|
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20));
|
||||||
|
log.info("设备 {} 发送紧急通知完成", ctx.getDeviceImei());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 2. 数字 —— 下发数据块
|
||||||
|
int blockIndex;
|
||||||
|
try {
|
||||||
|
blockIndex = Integer.parseInt(respText);
|
||||||
|
} catch (NumberFormatException ex) {
|
||||||
|
log.warn("设备 {} 紧急通知上报非法块号:{}", ctx.getDeviceImei(), respText);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 将发送的信息原文本以List<String>形式存储在Redis中
|
||||||
|
String data = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + ctx.getDeviceImei() + DEVICE_ALARM_MESSAGE_KEY_PREFIX);
|
||||||
|
if (data.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
ArrayList<Integer> intData = new ArrayList<>();
|
||||||
|
intData.add(blockIndex);
|
||||||
|
// 获取块原内容 转成GBK 再转成无符号十进制整数
|
||||||
|
String blockTxt = data.split(",")[blockIndex-1];
|
||||||
|
// 再按 GBK 编码把字符串转成字节数组,并逐个转为无符号十进制整数
|
||||||
|
for (byte b : blockTxt.getBytes(GBK)) {
|
||||||
|
intData.add(b & 0xFF); // b & 0xFF 得到 0~255 的整数
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
map.put("ins_BreakNews", intData);
|
||||||
|
|
||||||
|
String topic = MqttConstants.GLOBAL_PUB_KEY + ctx.getDeviceImei();
|
||||||
|
String json = JsonUtils.toJsonString(map);
|
||||||
|
mqttGateway.sendMsgToMqtt(topic, 1, json);
|
||||||
|
log.info("发送设备紧急通知=>topic:{},payload:{}",
|
||||||
|
MqttConstants.GLOBAL_PUB_KEY + ctx.getDeviceImei(),
|
||||||
|
JsonUtils.toJsonString(map));
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("处理发送设备紧急通知时出错", e);
|
||||||
|
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(20));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Charset GBK = Charset.forName("GBK");
|
||||||
|
|
||||||
|
/* ---------- DTO ---------- */
|
||||||
|
|
||||||
|
@Data
|
||||||
|
private static class MqttXinghanAlarmMsgJson {
|
||||||
|
/**
|
||||||
|
* 设备上行:
|
||||||
|
* 数字 -> 请求对应块号
|
||||||
|
* cover! -> 写入成功
|
||||||
|
*/
|
||||||
|
@JsonProperty("sta_BreakNews")
|
||||||
|
private String staBreakNews;
|
||||||
|
}
|
||||||
|
}
|
@ -57,7 +57,7 @@ public class XinghanSendMsgRule implements MqttMessageRule {
|
|||||||
|
|
||||||
// 1. genius! —— 成功标记
|
// 1. genius! —— 成功标记
|
||||||
if ("genius!".equalsIgnoreCase(respText)) {
|
if ("genius!".equalsIgnoreCase(respText)) {
|
||||||
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(20));
|
RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20));
|
||||||
log.info("设备 {} 发送消息完成", ctx.getDeviceImei());
|
log.info("设备 {} 发送消息完成", ctx.getDeviceImei());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -39,8 +39,7 @@ import static com.fuyuanshen.common.core.constant.GlobalConstants.GLOBAL_REDIS_K
|
|||||||
import static com.fuyuanshen.common.core.utils.Bitmap80x12Generator.buildArr;
|
import static com.fuyuanshen.common.core.utils.Bitmap80x12Generator.buildArr;
|
||||||
import static com.fuyuanshen.common.core.utils.Bitmap80x12Generator.generateFixedBitmapData;
|
import static com.fuyuanshen.common.core.utils.Bitmap80x12Generator.generateFixedBitmapData;
|
||||||
import static com.fuyuanshen.common.core.utils.ImageToCArrayConverter.convertHexToDecimal;
|
import static com.fuyuanshen.common.core.utils.ImageToCArrayConverter.convertHexToDecimal;
|
||||||
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_BOOT_LOGO_KEY_PREFIX;
|
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.*;
|
||||||
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@ -62,7 +61,7 @@ public class DeviceXinghanBizService {
|
|||||||
"ins_DetectGrade", Map.of(1, "低档", 2, "中档", 3, "高档"),
|
"ins_DetectGrade", Map.of(1, "低档", 2, "中档", 3, "高档"),
|
||||||
"ins_LightGrade", Map.of(1, "强光", 2, "弱光"),
|
"ins_LightGrade", Map.of(1, "强光", 2, "弱光"),
|
||||||
"ins_SOSGrade", Map.of(1, "爆闪模式", 2, "红蓝模式"),
|
"ins_SOSGrade", Map.of(1, "爆闪模式", 2, "红蓝模式"),
|
||||||
"ins_ShakeBit", Map.of(0, "未静止报警", 1, "正在静止报警")
|
"ins_ShakeBit", Map.of(1, "开启报警")
|
||||||
// 再加 4、5、6…… 档,直接往 Map 里塞即可
|
// 再加 4、5、6…… 档,直接往 Map 里塞即可
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -166,6 +165,7 @@ public class DeviceXinghanBizService {
|
|||||||
list.add(bo.getPosition());
|
list.add(bo.getPosition());
|
||||||
list.add(bo.getCode());
|
list.add(bo.getCode());
|
||||||
RedisUtils.setCacheList(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + deviceObj.getDeviceImei() + ":app_send_message_data", list);
|
RedisUtils.setCacheList(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + deviceObj.getDeviceImei() + ":app_send_message_data", list);
|
||||||
|
RedisUtils.expire(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + deviceObj.getDeviceImei() + ":app_send_message_data", Duration.ofSeconds(5 * 60L));
|
||||||
|
|
||||||
Map<String, Object> payload = Map.of("ins_TexTrans",
|
Map<String, Object> payload = Map.of("ins_TexTrans",
|
||||||
Collections.singletonList(0));
|
Collections.singletonList(0));
|
||||||
@ -195,6 +195,62 @@ public class DeviceXinghanBizService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送报警信息
|
||||||
|
* @param bo
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int sendAlarmMessage(AppDeviceSendMsgBo bo) {
|
||||||
|
try {
|
||||||
|
List<Long> deviceIds = bo.getDeviceIds();
|
||||||
|
if (deviceIds == null || deviceIds.isEmpty()) {
|
||||||
|
throw new ServiceException("请选择设备");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Long deviceId : deviceIds) {
|
||||||
|
Device device = deviceMapper.selectById(deviceId);
|
||||||
|
if (device == null) {
|
||||||
|
throw new ServiceException("设备不存在" + deviceId);
|
||||||
|
}
|
||||||
|
if(isDeviceOffline(device.getDeviceImei())){
|
||||||
|
throw new ServiceException(device.getDeviceName()+",设备已断开连接");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
|
||||||
|
RedisUtils.setCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + device.getDeviceImei() + DEVICE_ALARM_MESSAGE_KEY_PREFIX, bo.getSendMsg(),Duration.ofSeconds(5 * 60L));
|
||||||
|
|
||||||
|
Map<String, Object> payload = Map.of("ins_BreakNews",
|
||||||
|
Collections.singletonList(0));
|
||||||
|
String topic = MqttConstants.GLOBAL_PUB_KEY + device.getDeviceImei();
|
||||||
|
String json = JsonUtils.toJsonString(payload);
|
||||||
|
|
||||||
|
try {
|
||||||
|
mqttGateway.sendMsgToMqtt(topic, 1, json);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("发送紧急通知失败, topic={}, payload={}", topic, json, e);
|
||||||
|
throw new ServiceException("发送紧急通知失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
log.info("发送紧急通知=>topic:{},payload:{}", MqttConstants.GLOBAL_PUB_KEY+device.getDeviceImei(),json);
|
||||||
|
|
||||||
|
UpdateWrapper<Device> updateWrapper = new UpdateWrapper<>();
|
||||||
|
updateWrapper.eq("id", deviceId)
|
||||||
|
.eq("binding_user_id", AppLoginHelper.getUserId())
|
||||||
|
.set("send_msg", bo.getSendMsg());
|
||||||
|
deviceMapper.update(updateWrapper);
|
||||||
|
recordDeviceLog(device.getId(), device.getDeviceName(), "发送紧急通知", bo.getSendMsg(), AppLoginHelper.getUserId());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.info("设备发送告警信息信息失败:{}" ,deviceId);
|
||||||
|
throw new ServiceException("设备发送告警信息信息失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (Exception e){
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new ServiceException("发送告警信息指令失败");
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------------------------------- 私有通用方法 ---------------------------------- */
|
/* ---------------------------------- 私有通用方法 ---------------------------------- */
|
||||||
|
|
||||||
private void sendCommand(DeviceInstructDto dto,
|
private void sendCommand(DeviceInstructDto dto,
|
||||||
|
Reference in New Issue
Block a user