fix:太阳能网控下发盐度设置,增加30分钟冻结。
This commit is contained in:
@@ -2084,6 +2084,7 @@ public class IotController extends BaseController {
|
|||||||
return R.fail("盐度缓存失败,请稍后重试");
|
return R.fail("盐度缓存失败,请稍后重试");
|
||||||
}
|
}
|
||||||
updateDeviceSalinityCompensation(device.getId(), request.getSalinityCompensation());
|
updateDeviceSalinityCompensation(device.getId(), request.getSalinityCompensation());
|
||||||
|
setSalinityFreeze(device.getSerialNum(), request.getSalinityCompensation());
|
||||||
log.info("太阳能网控盐度已缓存: userId={}, deviceId={}, deviceName={}, serialNum={}, salinityCompensation={}‰",
|
log.info("太阳能网控盐度已缓存: userId={}, deviceId={}, deviceName={}, serialNum={}, salinityCompensation={}‰",
|
||||||
userId, device.getId(), device.getDeviceName(), device.getSerialNum(), request.getSalinityCompensation());
|
userId, device.getId(), device.getDeviceName(), device.getSerialNum(), request.getSalinityCompensation());
|
||||||
return R.ok("设置成功");
|
return R.ok("设置成功");
|
||||||
@@ -2147,6 +2148,24 @@ public class IotController extends BaseController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置盐度冻结标记,30分钟内阻止设备上报的旧值回写 DB
|
||||||
|
*
|
||||||
|
* @param serialNum 设备序列号
|
||||||
|
* @param salinityCompensation 用户设定的盐度值
|
||||||
|
*/
|
||||||
|
private void setSalinityFreeze(String serialNum, Double salinityCompensation) {
|
||||||
|
if (serialNum != null && !serialNum.isBlank() && salinityCompensation != null) {
|
||||||
|
com.intc.common.redis.utils.RedisUtils.setCacheObject(
|
||||||
|
DeviceDataHandler.SALINITY_SET_FREEZE_KEY_PREFIX + serialNum,
|
||||||
|
salinityCompensation,
|
||||||
|
Duration.ofMinutes(DeviceDataHandler.SALINITY_FREEZE_MINUTES)
|
||||||
|
);
|
||||||
|
log.info("[盐度冻结] 已设置冻结标记: serialNum={}, value={}, freezeMinutes={}",
|
||||||
|
serialNum, salinityCompensation, DeviceDataHandler.SALINITY_FREEZE_MINUTES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Operation(summary = "太阳能网控属性下发")
|
@Operation(summary = "太阳能网控属性下发")
|
||||||
@SaCheckPermission("fishery:solarControl:command")
|
@SaCheckPermission("fishery:solarControl:command")
|
||||||
@PostMapping("/device/solar/property-command")
|
@PostMapping("/device/solar/property-command")
|
||||||
@@ -2203,6 +2222,9 @@ public class IotController extends BaseController {
|
|||||||
return R.fail("指令缓存失败,请稍后重试");
|
return R.fail("指令缓存失败,请稍后重试");
|
||||||
}
|
}
|
||||||
syncDeviceFieldsAfterSolarCommand(device.getId(), properties);
|
syncDeviceFieldsAfterSolarCommand(device.getId(), properties);
|
||||||
|
if (properties.containsKey("salinitySet")) {
|
||||||
|
setSalinityFreeze(device.getSerialNum(), toDouble(properties.get("salinitySet")));
|
||||||
|
}
|
||||||
result.put("mode", "cached");
|
result.put("mode", "cached");
|
||||||
result.put("cached", true);
|
result.put("cached", true);
|
||||||
result.put("sent", false);
|
result.put("sent", false);
|
||||||
@@ -2232,6 +2254,9 @@ public class IotController extends BaseController {
|
|||||||
|
|
||||||
syncDeviceFieldsAfterSolarCommand(device.getId(), properties);
|
syncDeviceFieldsAfterSolarCommand(device.getId(), properties);
|
||||||
removePendingSolarProperties(device, properties.keySet());
|
removePendingSolarProperties(device, properties.keySet());
|
||||||
|
if (properties.containsKey("salinitySet")) {
|
||||||
|
setSalinityFreeze(device.getSerialNum(), toDouble(properties.get("salinitySet")));
|
||||||
|
}
|
||||||
|
|
||||||
result.put("mode", "direct");
|
result.put("mode", "direct");
|
||||||
result.put("cached", false);
|
result.put("cached", false);
|
||||||
|
|||||||
@@ -72,6 +72,18 @@ public class DeviceDataHandler {
|
|||||||
*/
|
*/
|
||||||
private static final long SOLAR_PROPERTY_DELAY_MS = 5000L;
|
private static final long SOLAR_PROPERTY_DELAY_MS = 5000L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 盐度设定防回写冻结标记 Redis key 前缀
|
||||||
|
* value = 用户设定的盐度值,TTL = {@link #SALINITY_FREEZE_MINUTES} 分钟
|
||||||
|
* 用户修改盐度后写入此标记,冻结期内设备上报的旧值不会回写 DB,避免客户误判修改未生效
|
||||||
|
*/
|
||||||
|
public static final String SALINITY_SET_FREEZE_KEY_PREFIX = "device:salinity:set:freeze:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 盐度设定冻结时长(分钟),与设备上报周期匹配,超时后恢复正常回写逻辑
|
||||||
|
*/
|
||||||
|
public static final int SALINITY_FREEZE_MINUTES = 30;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TDengine 服务,用于存储设备时序数据
|
* TDengine 服务,用于存储设备时序数据
|
||||||
*/
|
*/
|
||||||
@@ -1058,6 +1070,8 @@ public class DeviceDataHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
syncDeviceFieldsAfterPropertySet(pendingProperty.deviceId, pendingProperty.properties);
|
syncDeviceFieldsAfterPropertySet(pendingProperty.deviceId, pendingProperty.properties);
|
||||||
|
// 补发成功后,如果包含盐度设定,写入冻结标记
|
||||||
|
setSalinityFreezeIfNeeded(deviceName, pendingProperty.properties);
|
||||||
clearPendingSolarProperty(pendingProperty);
|
clearPendingSolarProperty(pendingProperty);
|
||||||
log.info("[太阳能属性] {}补发成功并清除缓存: deviceName={}, cacheKey={}, deviceId={}, properties={}",
|
log.info("[太阳能属性] {}补发成功并清除缓存: deviceName={}, cacheKey={}, deviceId={}, properties={}",
|
||||||
trigger, deviceName, pendingProperty.redisKey, pendingProperty.deviceId, pendingProperty.properties);
|
trigger, deviceName, pendingProperty.redisKey, pendingProperty.deviceId, pendingProperty.properties);
|
||||||
@@ -1204,6 +1218,27 @@ public class DeviceDataHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果属性中包含 salinitySet,写入盐度冻结标记到 Redis
|
||||||
|
* 冻结期内设备上报的旧值不会被回写 DB
|
||||||
|
*
|
||||||
|
* @param deviceName 设备名称(serialNum)
|
||||||
|
* @param properties 下发的属性
|
||||||
|
*/
|
||||||
|
private void setSalinityFreezeIfNeeded(String deviceName, Map<String, Object> properties) {
|
||||||
|
if (StrUtil.isBlank(deviceName) || properties == null || !properties.containsKey("salinitySet")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Double value = toDouble(properties.get("salinitySet"));
|
||||||
|
if (value == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String freezeKey = SALINITY_SET_FREEZE_KEY_PREFIX + deviceName;
|
||||||
|
RedisUtils.setCacheObject(freezeKey, value, Duration.ofMinutes(SALINITY_FREEZE_MINUTES));
|
||||||
|
log.info("[盐度冻结] 已设置冻结标记: deviceName={}, value={}, freezeMinutes={}",
|
||||||
|
deviceName, value, SALINITY_FREEZE_MINUTES);
|
||||||
|
}
|
||||||
|
|
||||||
private static class PendingSolarProperty {
|
private static class PendingSolarProperty {
|
||||||
private final String redisKey;
|
private final String redisKey;
|
||||||
private final Long deviceId;
|
private final Long deviceId;
|
||||||
@@ -2877,6 +2912,25 @@ public class DeviceDataHandler {
|
|||||||
if ("salinitySet".equals(k)) {
|
if ("salinitySet".equals(k)) {
|
||||||
Double salinitySet = params.getDouble(k);
|
Double salinitySet = params.getDouble(k);
|
||||||
if (salinitySet != null) {
|
if (salinitySet != null) {
|
||||||
|
// === 盐度冻结检查:用户修改盐度后30分钟内,忽略设备上报的旧值回写 ===
|
||||||
|
String freezeKey = SALINITY_SET_FREEZE_KEY_PREFIX + deviceName;
|
||||||
|
Object freezeObj = RedisUtils.getCacheObject(freezeKey);
|
||||||
|
if (freezeObj != null) {
|
||||||
|
Double frozenValue = toDouble(freezeObj);
|
||||||
|
if (frozenValue != null && Math.abs(salinitySet - frozenValue) < 0.01) {
|
||||||
|
// 上报值与冻结值一致,设备已跟上新设置,提前解冻
|
||||||
|
RedisUtils.deleteObject(freezeKey);
|
||||||
|
log.info("[盐度冻结] 设备上报值={} 与冻结值={} 一致,提前解冻: deviceName={}",
|
||||||
|
salinitySet, frozenValue, deviceName);
|
||||||
|
} else {
|
||||||
|
// 冻结期内上报值与设定值不一致,跳过回写避免误导
|
||||||
|
log.debug("[盐度冻结] 冻结期内跳过设备旧值回写: deviceName={}, reportValue={}, frozenValue={}",
|
||||||
|
deviceName, salinitySet, frozenValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// === 冻结检查结束 ===
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Device device = deviceMapper.selectOne(
|
Device device = deviceMapper.selectOne(
|
||||||
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<Device>()
|
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<Device>()
|
||||||
|
|||||||
Reference in New Issue
Block a user