20260323:优化设备上线之后一秒内发送IMEI/ICCID
This commit is contained in:
parent
479c0aab6c
commit
25abae984d
@ -0,0 +1,67 @@
|
|||||||
|
package com.fastbee.web.controller.backend;
|
||||||
|
|
||||||
|
import com.fastbee.common.core.domain.AjaxResult;
|
||||||
|
import com.fastbee.common.utils.StringUtils;
|
||||||
|
import io.swagger.annotations.Api;
|
||||||
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 调试/后台接口
|
||||||
|
*
|
||||||
|
* @author Sumuxi
|
||||||
|
* @date 2026/3/23
|
||||||
|
*/
|
||||||
|
@Api(tags = "后台接口")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/backend")
|
||||||
|
public class BackendController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private RedisTemplate<String, Object> redisTemplate;
|
||||||
|
|
||||||
|
@ApiOperation("设置缓存值(调试/运维)")
|
||||||
|
@PreAuthorize("@ss.hasPermi('monitor:cache:remove')")
|
||||||
|
@PostMapping("/set")
|
||||||
|
public AjaxResult setCacheValue(@RequestParam("key") String key, @RequestParam("value") String value, @RequestParam(required = false) Long expireSeconds) {
|
||||||
|
if (StringUtils.isEmpty(key)) {
|
||||||
|
return AjaxResult.error("key不能为空");
|
||||||
|
}
|
||||||
|
if (value == null || value.isEmpty()) {
|
||||||
|
return AjaxResult.error("value不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expireSeconds == null) {
|
||||||
|
redisTemplate.opsForValue().set(key, value);
|
||||||
|
return AjaxResult.success("ok");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expireSeconds > 0) {
|
||||||
|
redisTemplate.opsForValue().set(key, value, expireSeconds, TimeUnit.SECONDS);
|
||||||
|
} else {
|
||||||
|
// expireSeconds <= 0 视为不设置过期时间
|
||||||
|
redisTemplate.opsForValue().set(key, value);
|
||||||
|
}
|
||||||
|
return AjaxResult.success("ok");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除缓存值
|
||||||
|
@ApiOperation("删除缓存值(调试/运维)")
|
||||||
|
@PreAuthorize("@ss.hasPermi('monitor:cache:remove')")
|
||||||
|
@PostMapping("/remove")
|
||||||
|
public AjaxResult removeCacheValue(@RequestParam("key") String key) {
|
||||||
|
if (StringUtils.isEmpty(key)) {
|
||||||
|
return AjaxResult.error("key不能为空");
|
||||||
|
}
|
||||||
|
redisTemplate.delete(key);
|
||||||
|
return AjaxResult.success("ok");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,12 +3,17 @@ package com.fastbee.iot.data.executor;
|
|||||||
import com.fastbee.base.service.ISessionStore;
|
import com.fastbee.base.service.ISessionStore;
|
||||||
import com.fastbee.base.session.Session;
|
import com.fastbee.base.session.Session;
|
||||||
import com.fastbee.common.constant.FastBeeConstant;
|
import com.fastbee.common.constant.FastBeeConstant;
|
||||||
|
import com.fastbee.common.core.mq.message.DeviceMessage;
|
||||||
import com.fastbee.common.core.protocol.Message;
|
import com.fastbee.common.core.protocol.Message;
|
||||||
import com.fastbee.common.core.redis.RedisKeyBuilder;
|
import com.fastbee.common.core.redis.RedisKeyBuilder;
|
||||||
import com.fastbee.common.utils.StringUtils;
|
import com.fastbee.common.utils.StringUtils;
|
||||||
import com.fastbee.common.utils.gateway.protocol.ByteUtils;
|
import com.fastbee.common.utils.gateway.protocol.ByteUtils;
|
||||||
|
import com.fastbee.iot.data.service.IDeviceMessageService;
|
||||||
import com.fastbee.iot.data.service.impl.MessageManager;
|
import com.fastbee.iot.data.service.impl.MessageManager;
|
||||||
|
import com.fastbee.iot.domain.Device;
|
||||||
import com.fastbee.iot.domain.ModbusJob;
|
import com.fastbee.iot.domain.ModbusJob;
|
||||||
|
import com.fastbee.iot.enums.DeviceType;
|
||||||
|
import com.fastbee.iot.service.IDeviceService;
|
||||||
import com.fastbee.iot.service.IModbusJobService;
|
import com.fastbee.iot.service.IModbusJobService;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -57,22 +62,62 @@ public class PowerOnTriggerTaskExecutor {
|
|||||||
@Resource
|
@Resource
|
||||||
private RedisTemplate<String, Object> redisTemplate;
|
private RedisTemplate<String, Object> redisTemplate;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IDeviceMessageService deviceMessageService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IDeviceService deviceService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 每台设备同一时刻仅允许一个"上电触发轮询"流水线运行,避免重复触发/并发下发导致应答错配
|
* 每台设备同一时刻仅允许一个"上电触发轮询"流水线运行,避免重复触发/并发下发导致应答错配
|
||||||
*/
|
*/
|
||||||
private final ConcurrentHashMap<String, AtomicBoolean> running = new ConcurrentHashMap<>();
|
private final ConcurrentHashMap<String, AtomicBoolean> running = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置上电触发标识(设备注册/上线成功后调用)
|
* 注册之后处理
|
||||||
|
* 1、设置上电触发标识(设备注册/上线成功后调用)
|
||||||
|
* 2、下发IMEI/ICCID读取指令
|
||||||
* @param serialNumber 设备编号
|
* @param serialNumber 设备编号
|
||||||
*/
|
*/
|
||||||
public void setPowerOnTriggerFlag(String serialNumber) {
|
@Async(FastBeeConstant.TASK.TRIGGER_POWER_ON_TASK_ASYNC)
|
||||||
|
public void registerAfterHandle(String serialNumber) {
|
||||||
if (StringUtils.isEmpty(serialNumber)) {
|
if (StringUtils.isEmpty(serialNumber)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 1、设置上电触发redis标识
|
||||||
String key = RedisKeyBuilder.buildPowerOnTriggerFlagKey(serialNumber);
|
String key = RedisKeyBuilder.buildPowerOnTriggerFlagKey(serialNumber);
|
||||||
redisTemplate.opsForValue().set(key, "1", TRIGGER_FLAG_EXPIRE_SECONDS, TimeUnit.SECONDS);
|
redisTemplate.opsForValue().set(key, "1", TRIGGER_FLAG_EXPIRE_SECONDS, TimeUnit.SECONDS);
|
||||||
log.debug("设置上电触发标识,设备:{}", serialNumber);
|
//log.debug("设置上电触发标识,设备:{}", serialNumber);
|
||||||
|
|
||||||
|
// 2、下发IMEI/ICCID读取指令
|
||||||
|
if (redisTemplate.opsForValue().get("get_imei_iccid") == null){
|
||||||
|
log.warn("无缓存标识,无法下发IMEI/ICCID指令");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Device device = deviceService.selectDeviceInfoBySerialNumber(serialNumber);
|
||||||
|
if (device == null) {
|
||||||
|
log.warn("设备不存在,无法下发指令,设备编号:{}", serialNumber);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(device.getDeviceType() == DeviceType.DIRECT_DEVICE.getCode() || device.getDeviceType() == DeviceType.GATEWAY.getCode())) {
|
||||||
|
log.warn("设备类型不为直连或网关,无需下发指令,设备编号:{}", serialNumber);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 等设备上线
|
||||||
|
try { Thread.sleep(1000); } catch (InterruptedException e) { log.error("Thread sleep interrupted", e); }
|
||||||
|
|
||||||
|
DeviceMessage deviceMessage = new DeviceMessage();
|
||||||
|
deviceMessage.setSerialNumber(serialNumber);
|
||||||
|
deviceMessage.setTopicName("/function/get");
|
||||||
|
deviceMessage.setDataType("hex");
|
||||||
|
if (StringUtils.isEmpty(device.getImei())) {
|
||||||
|
deviceMessage.setMessage("41542b494d45495c725c6e");
|
||||||
|
deviceMessageService.messagePost(deviceMessage);
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(device.getIccid())) {
|
||||||
|
deviceMessage.setMessage("41542b49434349445c725c6e");
|
||||||
|
deviceMessageService.messagePost(deviceMessage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -93,7 +93,7 @@ public class DeviceMessageServiceImpl implements IDeviceMessageService {
|
|||||||
if (transport.equals(ServerType.MQTT.getCode())) {
|
if (transport.equals(ServerType.MQTT.getCode())) {
|
||||||
mqttClient.publish(0, false, topicName, deviceMessage.getMessage().toString());
|
mqttClient.publish(0, false, topicName, deviceMessage.getMessage().toString());
|
||||||
} else if (transport.equals(ServerType.TCP.getCode())) {
|
} else if (transport.equals(ServerType.TCP.getCode())) {
|
||||||
//处理TCP下发
|
// 处理TCP下发
|
||||||
ModbusPollMsg modbusPollMsg = new ModbusPollMsg();
|
ModbusPollMsg modbusPollMsg = new ModbusPollMsg();
|
||||||
modbusPollMsg.setSerialNumber(serialNumber);
|
modbusPollMsg.setSerialNumber(serialNumber);
|
||||||
modbusPollMsg.setProductId(deviceAndProtocol.getProductId());
|
modbusPollMsg.setProductId(deviceAndProtocol.getProductId());
|
||||||
|
|||||||
@ -56,7 +56,7 @@ public class TcpHandlerInterceptor implements HandlerInterceptor<Message> {
|
|||||||
// 注册报文处理完成后,设置上电触发标识
|
// 注册报文处理完成后,设置上电触发标识
|
||||||
int messageId = Integer.parseInt(request.getMessageId());
|
int messageId = Integer.parseInt(request.getMessageId());
|
||||||
if (messageId == TcpDtu.注册报文 && session.isRegistered()) {
|
if (messageId == TcpDtu.注册报文 && session.isRegistered()) {
|
||||||
powerOnTriggerTaskExecutor.setPowerOnTriggerFlag(session.getClientId());
|
powerOnTriggerTaskExecutor.registerAfterHandle(session.getClientId());
|
||||||
log.debug("设备注册成功,设置上电触发标识,设备:{}", session.getClientId());
|
log.debug("设备注册成功,设置上电触发标识,设备:{}", session.getClientId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
package com.fastbee.iot.mapper;
|
package com.fastbee.iot.mapper;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
|
||||||
import com.fastbee.common.core.device.DeviceAndProtocol;
|
import com.fastbee.common.core.device.DeviceAndProtocol;
|
||||||
import com.fastbee.common.core.thingsModel.ThingsModelValuesInput;
|
import com.fastbee.common.core.thingsModel.ThingsModelValuesInput;
|
||||||
import com.fastbee.framework.mybatis.mapper.BaseMapperX;
|
import com.fastbee.framework.mybatis.mapper.BaseMapperX;
|
||||||
@ -10,7 +9,6 @@ import com.fastbee.iot.model.*;
|
|||||||
import com.fastbee.iot.model.ThingsModels.ThingsModelValuesOutput;
|
import com.fastbee.iot.model.ThingsModels.ThingsModelValuesOutput;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
import org.springframework.stereotype.Repository;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -38,6 +36,16 @@ public interface DeviceMapper extends BaseMapperX<Device> {
|
|||||||
*/
|
*/
|
||||||
public DeviceStatistic selectDeviceProductAlertCount(Long deptId);
|
public DeviceStatistic selectDeviceProductAlertCount(Long deptId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据设备编号查询轻量信息:deviceType/imei/iccid
|
||||||
|
*
|
||||||
|
* <p>用于只需要判断/下发 IMEI/ICCID 的场景,避免查询 DeviceResult 中的大量无关字段。</p>
|
||||||
|
*
|
||||||
|
* @param serialNumber 设备编号
|
||||||
|
* @return 设备轻量对象(只保证 deviceType/imei/iccid 字段可用)
|
||||||
|
*/
|
||||||
|
Device selectDeviceInfoBySerialNumber(String serialNumber);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据设备编号查询设备
|
* 根据设备编号查询设备
|
||||||
*
|
*
|
||||||
|
|||||||
@ -266,6 +266,13 @@ public interface IDeviceService {
|
|||||||
*/
|
*/
|
||||||
public void batchChangeStatus(List<String> serialNumbers, DeviceStatus status);
|
public void batchChangeStatus(List<String> serialNumbers, DeviceStatus status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据设备编号查询轻量信息:deviceType/imei/iccid
|
||||||
|
*
|
||||||
|
* @param serialNumber 设备编号
|
||||||
|
* @return 设备轻量对象(只保证 deviceType/imei/iccid 字段可用)
|
||||||
|
*/
|
||||||
|
Device selectDeviceInfoBySerialNumber(String serialNumber);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据设备编号查询设备信息 -不带缓存物模型值
|
* 根据设备编号查询设备信息 -不带缓存物模型值
|
||||||
|
|||||||
@ -1555,6 +1555,10 @@ public class DeviceServiceImpl implements IDeviceService {
|
|||||||
deviceCache.updateBatchDeviceStatusCache(serialNumbers, status);
|
deviceCache.updateBatchDeviceStatusCache(serialNumbers, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Device selectDeviceInfoBySerialNumber(String serialNumber) {
|
||||||
|
return deviceMapper.selectDeviceInfoBySerialNumber(serialNumber);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据设备编号查询设备信息 -不带缓存物模型值
|
* 根据设备编号查询设备信息 -不带缓存物模型值
|
||||||
*
|
*
|
||||||
|
|||||||
@ -40,6 +40,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
<result property="iccid" column="iccid" />
|
<result property="iccid" column="iccid" />
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
轻量查询结果映射:仅用于 IMEI/ICCID 读取场景。
|
||||||
|
目的:避免 selectDeviceResultBySerialNumber 拉取过多无关字段。
|
||||||
|
-->
|
||||||
|
<resultMap type="com.fastbee.iot.domain.Device" id="DeviceImeiIccidTypeResult">
|
||||||
|
<result property="deviceType" column="device_type" />
|
||||||
|
<result property="imei" column="imei" />
|
||||||
|
<result property="iccid" column="iccid" />
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
<resultMap type="com.fastbee.iot.model.DeviceShortOutput" id="DeviceShortResult">
|
<resultMap type="com.fastbee.iot.model.DeviceShortOutput" id="DeviceShortResult">
|
||||||
<result property="deviceId" column="device_id" />
|
<result property="deviceId" column="device_id" />
|
||||||
<result property="deviceName" column="device_name" />
|
<result property="deviceName" column="device_name" />
|
||||||
@ -449,6 +459,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
where device_id = #{deviceId}
|
where device_id = #{deviceId}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="selectDeviceInfoBySerialNumber" parameterType="String" resultMap="DeviceImeiIccidTypeResult">
|
||||||
|
select
|
||||||
|
p.device_type as device_type,
|
||||||
|
d.imei as imei,
|
||||||
|
d.iccid as iccid
|
||||||
|
from iot_device d
|
||||||
|
left join iot_product p on p.product_id = d.product_id
|
||||||
|
where d.serial_number = #{serialNumber}
|
||||||
|
limit 1
|
||||||
|
</select>
|
||||||
|
|
||||||
<select id="selectDeviceBySerialNumber" parameterType="String" resultMap="DeviceResult">
|
<select id="selectDeviceBySerialNumber" parameterType="String" resultMap="DeviceResult">
|
||||||
<include refid="selectDeviceVo"/>
|
<include refid="selectDeviceVo"/>
|
||||||
where serial_number = #{serialNumber}
|
where serial_number = #{serialNumber}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user