20260320:bug修复

This commit is contained in:
George 2026-03-20 08:11:55 +08:00
parent 681cd4a9db
commit 1cfabdd2b4
2 changed files with 57 additions and 4 deletions

View File

@ -72,8 +72,8 @@ public class DeviceStatusConsumer {
private SnowflakeIdWorker snowflakeIdWorker = new SnowflakeIdWorker(2); private SnowflakeIdWorker snowflakeIdWorker = new SnowflakeIdWorker(2);
private static final String IMEI_CMD = "AT+IMEI\\r\\n"; private static final String IMEI_CMD = "41542b494d45495c525c6e"; // AT+IMEI\r\n
private static final String ICCID_CMD = "AT+ICCID\\r\\n"; private static final String ICCID_CMD = "41542b49434349445c525c6e"; // AT+ICCID\r\n
private static final int AUTO_FETCH_COOLDOWN_SECONDS = 300; private static final int AUTO_FETCH_COOLDOWN_SECONDS = 300;
private static final String AUTO_FETCH_KEY_PREFIX = "iot:autoFetch:imeiIccid:"; private static final String AUTO_FETCH_KEY_PREFIX = "iot:autoFetch:imeiIccid:";
@ -131,6 +131,7 @@ public class DeviceStatusConsumer {
private void autoFetchImeiIccidIfNeeded(Device device) { private void autoFetchImeiIccidIfNeeded(Device device) {
if (device == null || StringUtils.isEmpty(device.getSerialNumber())) { if (device == null || StringUtils.isEmpty(device.getSerialNumber())) {
log.info("=>设备首次上线,设备为空{}", device);
return; return;
} }
String serialNumber = device.getSerialNumber(); String serialNumber = device.getSerialNumber();
@ -138,6 +139,7 @@ public class DeviceStatusConsumer {
boolean needImei = StringUtils.isEmpty(device.getImei()); boolean needImei = StringUtils.isEmpty(device.getImei());
boolean needIccid = StringUtils.isEmpty(device.getIccid()); boolean needIccid = StringUtils.isEmpty(device.getIccid());
if (!needImei && !needIccid) { if (!needImei && !needIccid) {
log.info("=>设备首次上线,设备已存在IMEI/ICCID,无需获取{}", device);
return; return;
} }
@ -145,6 +147,7 @@ public class DeviceStatusConsumer {
String key = AUTO_FETCH_KEY_PREFIX + serialNumber + ":imei"; String key = AUTO_FETCH_KEY_PREFIX + serialNumber + ":imei";
if (!redisCache.containsKey(key)) { if (!redisCache.containsKey(key)) {
redisCache.setCacheObject(key, "1", AUTO_FETCH_COOLDOWN_SECONDS, TimeUnit.SECONDS); redisCache.setCacheObject(key, "1", AUTO_FETCH_COOLDOWN_SECONDS, TimeUnit.SECONDS);
log.info("=>设备首次上线,下发AT指令获取IMEI,{}", device);
sendAtCommand(serialNumber, IMEI_CMD); sendAtCommand(serialNumber, IMEI_CMD);
} }
} }
@ -153,6 +156,7 @@ public class DeviceStatusConsumer {
String key = AUTO_FETCH_KEY_PREFIX + serialNumber + ":iccid"; String key = AUTO_FETCH_KEY_PREFIX + serialNumber + ":iccid";
if (!redisCache.containsKey(key)) { if (!redisCache.containsKey(key)) {
redisCache.setCacheObject(key, "1", AUTO_FETCH_COOLDOWN_SECONDS, TimeUnit.SECONDS); redisCache.setCacheObject(key, "1", AUTO_FETCH_COOLDOWN_SECONDS, TimeUnit.SECONDS);
log.info("=>设备首次上线,下发AT指令获取ICCID,{}", device);
sendAtCommand(serialNumber, ICCID_CMD); sendAtCommand(serialNumber, ICCID_CMD);
} }
} }

View File

@ -661,11 +661,21 @@ export default {
if (type === 'imei') this.imeiLoading = true; if (type === 'imei') this.imeiLoading = true;
if (type === 'iccid') this.iccidLoading = true; if (type === 'iccid') this.iccidLoading = true;
// FUNCTION_GET 下发链路最终会走 ByteUtils.convertToHexByteArray(command)。
// 因此这里必须传“十六进制字节串”,而不是原始 ASCII。
// 同时兼容两种输入:
// 1) 用户输入 AT+IMEI\\r\\n 这类带转义的 ASCII:会先还原成真实 \r\n,再转 hex(不带空格)
// 2) 用户直接输入 41 54 ... 或 41542b...:会清理空格后直接使用(后端也会再次清理)
const trimmed = String(cmd).trim();
const hexMessage = this.isHexLike(trimmed)
? this.normalizeHexString(trimmed)
: this.asciiCommandToHex(this.normalizeAtEscapes(trimmed));
await messagePost({ await messagePost({
message: cmd, message: hexMessage,
serialNumber: this.deviceInfo.serialNumber, serialNumber: this.deviceInfo.serialNumber,
topicName: '/function/get', topicName: '/function/get',
dataType: 'ascii', dataType: 'hex',
}); });
this.$message({ type: 'success', message: '已下发指令,等待设备回复解析…' }); this.$message({ type: 'success', message: '已下发指令,等待设备回复解析…' });
@ -680,6 +690,45 @@ export default {
} }
}, },
normalizeAtEscapes(str) {
// 输入框里用户通常会键入:AT+IMEI\\r\\n
// 但你当前的设备/链路期望的不是“真实 CR/LF(0d0a)”,而是把转义串当作占位符:
// 把 \\r\\n 期望转成 "/r/n"(即字符 '/','r','/','n',十六进制为 2f 72 2f 6e)。
if (!str) return '';
return String(str)
.replace(/\\r\\n/g, '/r/n')
.replace(/\\r/g, '/r')
.replace(/\\n/g, '/n');
},
isHexLike(hexStr) {
if (!hexStr) return false;
const cleaned = this.normalizeHexString(hexStr);
if (!cleaned) return false;
return /^[0-9a-fA-F]+$/.test(cleaned);
},
normalizeHexString(hexStr) {
if (!hexStr) return '';
return String(hexStr)
.trim()
.replace(/\s+/g, '')
.replace(/^0x/i, '');
},
asciiCommandToHex(asciiStr) {
// 把字符串按字节(低8位)转成纯16进制(无空格)
if (!asciiStr) return '';
let hex = '';
for (let i = 0; i < asciiStr.length; i++) {
const c = asciiStr.charCodeAt(i) & 0xff;
let hexByte = c.toString(16).toUpperCase();
if (hexByte.length === 1) hexByte = `0${hexByte}`;
hex += hexByte;
}
return hex;
},
async saveImeiIccid(type) { async saveImeiIccid(type) {
try { try {
if (!this.deviceInfo || !this.deviceInfo.deviceId) return; if (!this.deviceInfo || !this.deviceInfo.deviceId) return;