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.session.Session;
|
||||
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.redis.RedisKeyBuilder;
|
||||
import com.fastbee.common.utils.StringUtils;
|
||||
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.domain.Device;
|
||||
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 io.netty.buffer.Unpooled;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -57,22 +62,62 @@ public class PowerOnTriggerTaskExecutor {
|
||||
@Resource
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
@Resource
|
||||
private IDeviceMessageService deviceMessageService;
|
||||
|
||||
@Resource
|
||||
private IDeviceService deviceService;
|
||||
|
||||
/**
|
||||
* 每台设备同一时刻仅允许一个"上电触发轮询"流水线运行,避免重复触发/并发下发导致应答错配
|
||||
*/
|
||||
private final ConcurrentHashMap<String, AtomicBoolean> running = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 设置上电触发标识(设备注册/上线成功后调用)
|
||||
* 注册之后处理
|
||||
* 1、设置上电触发标识(设备注册/上线成功后调用)
|
||||
* 2、下发IMEI/ICCID读取指令
|
||||
* @param serialNumber 设备编号
|
||||
*/
|
||||
public void setPowerOnTriggerFlag(String serialNumber) {
|
||||
@Async(FastBeeConstant.TASK.TRIGGER_POWER_ON_TASK_ASYNC)
|
||||
public void registerAfterHandle(String serialNumber) {
|
||||
if (StringUtils.isEmpty(serialNumber)) {
|
||||
return;
|
||||
}
|
||||
// 1、设置上电触发redis标识
|
||||
String key = RedisKeyBuilder.buildPowerOnTriggerFlagKey(serialNumber);
|
||||
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())) {
|
||||
mqttClient.publish(0, false, topicName, deviceMessage.getMessage().toString());
|
||||
} else if (transport.equals(ServerType.TCP.getCode())) {
|
||||
//处理TCP下发
|
||||
// 处理TCP下发
|
||||
ModbusPollMsg modbusPollMsg = new ModbusPollMsg();
|
||||
modbusPollMsg.setSerialNumber(serialNumber);
|
||||
modbusPollMsg.setProductId(deviceAndProtocol.getProductId());
|
||||
|
||||
@ -56,7 +56,7 @@ public class TcpHandlerInterceptor implements HandlerInterceptor<Message> {
|
||||
// 注册报文处理完成后,设置上电触发标识
|
||||
int messageId = Integer.parseInt(request.getMessageId());
|
||||
if (messageId == TcpDtu.注册报文 && session.isRegistered()) {
|
||||
powerOnTriggerTaskExecutor.setPowerOnTriggerFlag(session.getClientId());
|
||||
powerOnTriggerTaskExecutor.registerAfterHandle(session.getClientId());
|
||||
log.debug("设备注册成功,设置上电触发标识,设备:{}", session.getClientId());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package com.fastbee.iot.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.fastbee.common.core.device.DeviceAndProtocol;
|
||||
import com.fastbee.common.core.thingsModel.ThingsModelValuesInput;
|
||||
import com.fastbee.framework.mybatis.mapper.BaseMapperX;
|
||||
@ -10,7 +9,6 @@ import com.fastbee.iot.model.*;
|
||||
import com.fastbee.iot.model.ThingsModels.ThingsModelValuesOutput;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@ -38,6 +36,16 @@ public interface DeviceMapper extends BaseMapperX<Device> {
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* 根据设备编号查询轻量信息: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);
|
||||
}
|
||||
|
||||
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" />
|
||||
</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">
|
||||
<result property="deviceId" column="device_id" />
|
||||
<result property="deviceName" column="device_name" />
|
||||
@ -449,6 +459,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
where device_id = #{deviceId}
|
||||
</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">
|
||||
<include refid="selectDeviceVo"/>
|
||||
where serial_number = #{serialNumber}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user