diff --git a/bin/script/send_modbus_and_register.py b/bin/script/send_modbus_and_register.py index 5cd70dea..7eea8590 100644 --- a/bin/script/send_modbus_and_register.py +++ b/bin/script/send_modbus_and_register.py @@ -6,15 +6,17 @@ from binascii import unhexlify TARGET_HOST = 'localhost' # 请根据实际情况修改 TARGET_PORT = 8888 # 请根据实际情况修改 -# 元数组 modbus 指令 +# 元数组 modbus 指令 DEF7B23FD985 D9C6983D30F8 MODBUS_DATA_HEX = ( - "7e80DCDC9891706E7e", - "0103204135317841ab8e39bf27bccd3f1fb19ac2b231bbc0b6f82040ae3207c3f9f80efa23", - "020320413b9fa441acd34dc0024a003fa2a59ac2af34a0c18e17ea413167dbc3f9c7fe6d40", - "0303204139565641ab21ddbff6af333fe26800c2aec626c18685244176ee82c3f9bd7568c8", - "040320413a0d3e41a534d3bf461ccd403d7e00c2ade1e4c0d819c441ce9ca7c3f9a4d260bf" + "7e80DEF7B23FD9857e" + "04031c41aa0c7941d78000402000220000000042b121523fb1fb373eb5668c4CDA", + "05031c419b24c741e18000400bf3910000000042a83e6840824127c08779583613", + "06031c418eb64a41cb00003ff8d8130000000042aaaa05bf95093cc090a1de5801" + ) + + # 重要-四川远德MQTT推送 (倾角计) # "7e803042593632517e", # "01033041011b7b0000000041eaafb742d19bd74513d545450dd9a4450ec53d00000000420ccf64420c74a9420c74a9c2d200009d99" @@ -28,6 +30,10 @@ MODBUS_DATA_HEX = ( # "01031c41aa0c7941d78000402000220000000042b121523fb1fb373eb5668c9cd8", # "02031c419b24c741e18000400bf3910000000042a83e6840824127c0877958c7d0", # "03031c418eb64a41cb00003ff8d8130000000042aaaa05bf95093cc090a1de8803" +# "04031c41aa0c7941d78000402000220000000042b121523fb1fb373eb5668c4CDA", +# "05031c419b24c741e18000400bf3910000000042a83e6840824127c08779583613", +# "06031c418eb64a41cb00003ff8d8130000000042aaaa05bf95093cc090a1de5801" + # 01 03 1c 4268 442f 41d3 0000 418b 6685 0000 0000 4026 f4ca c00d 1fd3 c2ad 2a9d 6311 # 从机地址 功能码 字节长度 2个字节 CRC校验 diff --git a/fastbee-open-api/src/main/java/com/fastbee/controller/platform/PlatformDeviceMappingController.java b/fastbee-open-api/src/main/java/com/fastbee/controller/platform/PlatformDeviceMappingController.java index 306bae39..23dd647f 100644 --- a/fastbee-open-api/src/main/java/com/fastbee/controller/platform/PlatformDeviceMappingController.java +++ b/fastbee-open-api/src/main/java/com/fastbee/controller/platform/PlatformDeviceMappingController.java @@ -73,6 +73,14 @@ public class PlatformDeviceMappingController extends BaseController { return success("入网成功"); } + @PreAuthorize("@ss.hasAnyPermi('platform:deviceMapping:query,platform:deviceMapping:list')") + @GetMapping("/static-level/reference-points") + @ApiOperation("查询已配置的静力水准基准点(跨网关基准点下拉框数据源)") + public AjaxResult listStaticLevelReferencePoints(@RequestParam Long platformId, + @RequestParam(required = false) Long excludeMappingId) { + return success(mappingService.listConfiguredReferencePoints(platformId, excludeMappingId)); + } + // ==================== 子映射管理 ==================== @PreAuthorize("@ss.hasAnyPermi('platform:deviceMapping:query,platform:deviceMapping:list')") diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/adapter/dcp/DcpPlatformAdapter.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/adapter/dcp/DcpPlatformAdapter.java index 45c97e4f..696c0eb7 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/adapter/dcp/DcpPlatformAdapter.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/adapter/dcp/DcpPlatformAdapter.java @@ -18,6 +18,7 @@ import com.fastbee.iot.platform.flow.FlowContext; import com.fastbee.iot.platform.model.json.*; import com.fastbee.iot.platform.flow.PlatformFlowExecutor; import com.fastbee.iot.platform.mapper.PlatformFlowMapper; +import com.fastbee.iot.platform.mapper.PlatformDeviceMappingMapper; import com.fastbee.iot.platform.mapper.PlatformInterfaceMapper; import com.fastbee.iot.platform.mapper.PlatformUserAuthMapper; import com.fastbee.iot.platform.service.IPlatformDeviceSubMappingService; @@ -35,6 +36,7 @@ import java.time.OffsetDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; +import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; @@ -69,6 +71,8 @@ public class DcpPlatformAdapter { private PlatformInterfaceMapper interfaceMapper; @Autowired private PlatformUserAuthMapper platformUserAuthMapper; + @Autowired + private PlatformDeviceMappingMapper mappingMapper; @Autowired private IPlatformInstanceService instanceService; @@ -198,7 +202,10 @@ public class DcpPlatformAdapter { bufferService.saveReferenceData(platform.getId(), fieldName, reportSerialNumber, currentItem, reportIntervalSec); int replayCount = replayPendingMonitorData(platform, userAuth, mapping, fieldName, currentItem, reportIntervalSec, baseUrl, interfaceCode); - log.info("[DCP] 基准点 {} 已缓存,补推监测点 {} 条", reportSerialNumber, replayCount); + int crossCount = replaySiblingGatewayMonitors(platform, userAuth, mapping, fieldName, currentItem, + reportIntervalSec, reportSerialNumber); + log.info("[DCP] 基准点 {} 已缓存,补推监测点 {} 条(其中跨网关 {} 条)", + reportSerialNumber, replayCount + crossCount, crossCount); return; } @@ -294,8 +301,15 @@ public class DcpPlatformAdapter { throw new RuntimeException("不支持的数组组包模式 arrayMode=" + mode); } - /** 解析基准点序列号 */ + /** 解析基准点序列号:跨网关配置 > 本网关基准点行 > onboard_config > 网关序列号 */ private String resolveRefSerialNumber(PlatformDeviceSubMapping subMapping, PlatformDeviceMapping mapping) { + // 跨网关基准点:前端在下拉框中选择的、属于其他网关的基准点序列号 + ExtraMappingConfig extra = mapping.getExtraMappingParsed(); + if (extra != null && extra.getStaticLevel() != null + && StringUtils.hasText(extra.getStaticLevel().getReferenceSerialNumber())) { + return extra.getStaticLevel().getReferenceSerialNumber(); + } + try { PlatformDeviceSubMapping reference = subMappingService.findReference(mapping.getId()); if (reference != null && StringUtils.hasText(reference.getSubSerialNumber())) { @@ -315,17 +329,69 @@ public class DcpPlatformAdapter { } /** - * 基准点后到时,补推此前缓存的监测点数据。 + * 基准点后到时,补推此前缓存的监测点数据(本网关 + 引用该基准点的其他网关)。 */ private int replayPendingMonitorData(PlatformInstance platform, PlatformUserAuth userAuth,PlatformDeviceMapping mapping, String fieldName, JSONObject referenceItem, int reportIntervalSec, String baseUrl, String interfaceCode) { + return replayMonitorsOfMapping(platform, userAuth, mapping, fieldName, referenceItem, + reportIntervalSec, baseUrl, interfaceCode); + } + + /** + * 跨网关补推:基准点上报后,补推其他网关中引用该基准点、且已缓存等待的监测点。 + * + * @param refSerialNumber 本次上报的基准点序列号 + */ + private int replaySiblingGatewayMonitors(PlatformInstance platform, PlatformUserAuth userAuth, + PlatformDeviceMapping refMapping, String fieldName, + JSONObject referenceItem, int reportIntervalSec, + String refSerialNumber) { + List siblings = mappingMapper.selectList(Wrappers.lambdaQuery() + .eq(PlatformDeviceMapping::getPlatformId, platform.getId()) + .ne(PlatformDeviceMapping::getId, refMapping.getId()) + .eq(PlatformDeviceMapping::getSensorType, "static_level") + .eq(PlatformDeviceMapping::getDeviceType, 1) + .eq(PlatformDeviceMapping::getNetStatus, 1)); + if (siblings == null || siblings.isEmpty()) { + return 0; + } + + int replayCount = 0; + for (PlatformDeviceMapping sibling : siblings) { + // 仅处理配置了跨网关基准点、且指向本次上报基准点的网关 + ExtraMappingConfig extra = sibling.getExtraMappingParsed(); + if (extra == null || extra.getStaticLevel() == null + || !refSerialNumber.equals(extra.getStaticLevel().getReferenceSerialNumber())) { + continue; + } + PlatformInterface pushInterface = interfaceMapper.selectById(sibling.getPushInterfaceId()); + if (pushInterface == null) { + log.warn("[DCP] 跨网关补推跳过:网关 {} 未配置推送接口", sibling.getSerialNumber()); + continue; + } + String baseUrl = platform.getBaseUrl() + resolvePushPath(sibling, pushInterface); + String interfaceCode = resolveInterfaceCode(sibling.getSensorType(), pushInterface); + replayCount += replayMonitorsOfMapping(platform, userAuth, sibling, fieldName, referenceItem, + reportIntervalSec, baseUrl, interfaceCode); + } + return replayCount; + } + + /** + * 补推指定网关下所有已缓存的监测点:合并为一次 HTTP 请求推送, + * 全部成功才清空缓存,失败则整批保留缓存等待下次基准点上报。 + */ + private int replayMonitorsOfMapping(PlatformInstance platform, PlatformUserAuth userAuth, PlatformDeviceMapping mapping, + String fieldName, JSONObject referenceItem, + int reportIntervalSec, String baseUrl, String interfaceCode) { List subMappings = subMappingService.selectByParentId(mapping.getId()); if (subMappings == null || subMappings.isEmpty()) { return 0; } - int replayCount = 0; + JSONArray pushBodyArray = new JSONArray(); + List pushedSerials = new ArrayList<>(); for (PlatformDeviceSubMapping monitor : subMappings) { if (monitor == null || !monitor.isMonitor() || !StringUtils.hasText(monitor.getSubSerialNumber())) { continue; @@ -353,23 +419,32 @@ public class DcpPlatformAdapter { JSONArray sensorData = new JSONArray(); sensorData.add(referenceItem); sensorData.add(monitorItem); - JSONArray pushBody = buildArrayPushBody(mapping, bodyFields, fieldName, + JSONArray deviceBodyArr = buildArrayPushBody(mapping, bodyFields, fieldName, sensorData, monitor.getPlatformDeviceId()); - - PlatformPushLog logEntry = createLogEntry(platform, mapping, interfaceCode, monitor.getSubSerialNumber()); - doHttpPost(platform, userAuth, mapping, baseUrl, JSON.toJSONString(pushBody), logEntry); - pushLogService.asyncSave(logEntry); - if (logEntry.getStatus() != null && logEntry.getStatus() == 1) { - bufferService.removeMonitorData(platform.getId(), fieldName, monitor.getSubSerialNumber()); - replayCount++; - log.info("[DCP] 基准点后到,已补推监测点 {} deviceId={}", - monitor.getSubSerialNumber(), monitor.getPlatformDeviceId()); - } else { - log.warn("[DCP] 基准点后到,补推监测点 {} 失败,保留缓存等待下次基准点上报", - monitor.getSubSerialNumber()); + if (!deviceBodyArr.isEmpty()) { + pushBodyArray.add(deviceBodyArr.get(0)); + pushedSerials.add(monitor.getSubSerialNumber()); } } - return replayCount; + + if (pushBodyArray.isEmpty()) { + return 0; + } + + PlatformPushLog logEntry = createLogEntry(platform, mapping, interfaceCode, mapping.getSerialNumber()); + doHttpPost(platform, userAuth, mapping, baseUrl, JSON.toJSONString(pushBodyArray), logEntry); + pushLogService.asyncSave(logEntry); + + if (logEntry.getStatus() != null && logEntry.getStatus() == 1) { + pushedSerials.forEach(serial -> bufferService.removeMonitorData(platform.getId(), fieldName, serial)); + log.info("[DCP] 基准点后到,已合并补推监测点 {} 条 mappingId={} 序列号={}", + pushedSerials.size(), mapping.getId(), String.join(",", pushedSerials)); + return pushedSerials.size(); + } + + log.warn("[DCP] 基准点后到,合并补推 {} 条失败,整批保留缓存等待下次基准点上报 mappingId={}", + pushedSerials.size(), mapping.getId()); + return 0; } // ==================== 普通推送 ==================== diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/job/PlatformPushScheduledJob.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/job/PlatformPushScheduledJob.java index 14835bf0..4f45c1e5 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/job/PlatformPushScheduledJob.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/job/PlatformPushScheduledJob.java @@ -223,7 +223,12 @@ public class PlatformPushScheduledJob { } for (PlatformDeviceSubMapping subMapping : subMappings) { - if (subMapping == null || subMapping.getPlatformDeviceId() == null) { + if (subMapping == null) { + continue; + } + // 基准点本身不单独推送(无平台设备ID),但仍需进入推送入口以刷新基准点缓冲, + // 否则跨网关/同网关的监测点配对时读不到新鲜基准点数据 + if (subMapping.getPlatformDeviceId() == null && !subMapping.isReference()) { continue; } try { diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/service/IPlatformDeviceMappingService.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/service/IPlatformDeviceMappingService.java index eac312ab..52671d99 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/service/IPlatformDeviceMappingService.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/service/IPlatformDeviceMappingService.java @@ -5,6 +5,7 @@ import com.fastbee.iot.platform.domain.PlatformDeviceMapping; import java.util.Date; import java.util.List; +import java.util.Map; public interface IPlatformDeviceMappingService extends IService { @@ -18,4 +19,11 @@ public interface IPlatformDeviceMappingService extends IService> listConfiguredReferencePoints(Long platformId, Long excludeMappingId); } diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/service/impl/PlatformDeviceMappingServiceImpl.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/service/impl/PlatformDeviceMappingServiceImpl.java index 895a1e54..7272aa7f 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/service/impl/PlatformDeviceMappingServiceImpl.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/platform/service/impl/PlatformDeviceMappingServiceImpl.java @@ -12,6 +12,8 @@ import com.fastbee.iot.platform.mapper.PlatformDeviceMappingMapper; import com.fastbee.iot.platform.mapper.PlatformFlowMapper; import com.fastbee.iot.platform.mapper.PlatformInstanceMapper; import com.fastbee.iot.platform.mapper.PlatformUserAuthMapper; +import com.fastbee.iot.platform.model.json.ExtraMappingConfig; +import com.fastbee.iot.platform.model.json.OnboardConfig; import com.fastbee.iot.platform.service.IPlatformDeviceMappingService; import com.fastbee.iot.platform.service.IPlatformDeviceSubMappingService; import com.fastbee.iot.platform.service.IPlatformUserAuthService; @@ -221,13 +223,30 @@ public class PlatformDeviceMappingServiceImpl extends ServiceImpl> listConfiguredReferencePoints(Long platformId, Long excludeMappingId) { + List gateways = this.list(Wrappers.lambdaQuery() + .eq(platformId != null, PlatformDeviceMapping::getPlatformId, platformId) + .ne(excludeMappingId != null, PlatformDeviceMapping::getId, excludeMappingId) + .eq(PlatformDeviceMapping::getSensorType, "static_level") + .eq(PlatformDeviceMapping::getDeviceType, 1)); + List> result = new ArrayList<>(); + for (PlatformDeviceMapping gateway : gateways) { + List subs = subMappingService.selectByParentId(gateway.getId()); + if (subs == null) { + continue; + } + for (PlatformDeviceSubMapping sub : subs) { + if (sub == null || !sub.isReference() || !StringUtils.hasText(sub.getSubSerialNumber())) { + continue; + } + Map item = new java.util.LinkedHashMap<>(); + item.put("referenceSerialNumber", sub.getSubSerialNumber()); + item.put("referenceDeviceName", sub.getSubDeviceName()); + // 供前端生成监测点入网参数 refSensorId,保持与历史数据一致 + item.put("referenceSensorId", resolveSubSensorId(sub)); + item.put("gatewaySerialNumber", gateway.getSerialNumber()); + item.put("gatewayDeviceName", StringUtils.hasText(gateway.getDeviceName()) + ? gateway.getDeviceName() : gateway.getSerialNumber()); + result.add(item); + } + } + return result; + } } diff --git a/yack-web/src/api/platform/deviceMapping.js b/yack-web/src/api/platform/deviceMapping.js index 920f19e4..064ec002 100644 --- a/yack-web/src/api/platform/deviceMapping.js +++ b/yack-web/src/api/platform/deviceMapping.js @@ -36,3 +36,8 @@ export function listSubMappingsBatch(parentIds) { export function syncSubMappings(parentId, data) { return request({ url: `/platform/device-mapping/${parentId}/sub-mappings`, method: 'post', data }) } + +// 查询已配置的静力水准基准点(跨网关基准点下拉框数据源) +export function listStaticLevelReferencePoints(params) { + return request({ url: '/platform/device-mapping/static-level/reference-points', method: 'get', params }) +} diff --git a/yack-web/src/views/platform/device-mapping/index.vue b/yack-web/src/views/platform/device-mapping/index.vue index 2a4aaa49..6f3814d6 100644 --- a/yack-web/src/views/platform/device-mapping/index.vue +++ b/yack-web/src/views/platform/device-mapping/index.vue @@ -284,6 +284,17 @@ 秒 + + + + +
+ 本网关子设备全部为监测点时使用:选择其他网关已配置的基准点后,本网关子设备无需再指定基准点。 +
+
@@ -335,7 +346,7 @@