20260627:接入深度位移计,单个数据值推送

This commit is contained in:
zhugongping 2026-06-28 06:58:22 +08:00
parent 4e467edb2b
commit 8c12a70df2
4 changed files with 95 additions and 19 deletions

View File

@ -51,12 +51,15 @@ import java.util.Map;
@Component @Component
public class DcpPlatformAdapter { public class DcpPlatformAdapter {
private static final DateTimeFormatter ISO_OFFSET_MILLIS = // 组包形势
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); // 成对
private static final DateTimeFormatter DATE_TIME_MILLIS = private static final String ARRAY_MODE_REFERENCE_PAIR = "reference_pair";
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); // 单个
private static final DateTimeFormatter DATE_TIME_SECONDS = private static final String ARRAY_MODE_SINGLE = "single";
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static final DateTimeFormatter ISO_OFFSET_MILLIS = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
private static final DateTimeFormatter DATE_TIME_MILLIS = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
private static final DateTimeFormatter DATE_TIME_SECONDS = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Autowired @Autowired
private IPlatformInstanceService instanceService; private IPlatformInstanceService instanceService;
@ -105,7 +108,8 @@ public class DcpPlatformAdapter {
* 推送网关子设备上报数据到 DCP * 推送网关子设备上报数据到 DCP
* <p> * <p>
* deviceId 直接从子映射行获取,不再解析 JSON。 * deviceId 直接从子映射行获取,不再解析 JSON。
* reference 角色仅缓存基准数据,monitor 角色组装 [refItem, monitorItem] 推送。 * 数组字段由 requestSchema.arrayMode 控制组包方式:
* single 只推当前子设备,reference_pair 组装 [refItem, monitorItem] 推送。
*/ */
public void pushGatewayData(PlatformInstance platform, PlatformDeviceMapping mapping, public void pushGatewayData(PlatformInstance platform, PlatformDeviceMapping mapping,
PlatformDeviceSubMapping subMapping, PlatformDeviceSubMapping subMapping,
@ -140,15 +144,18 @@ public class DcpPlatformAdapter {
// 解析 extra_mapping 中的 itemFieldMappings 和 reportIntervalSec // 解析 extra_mapping 中的 itemFieldMappings 和 reportIntervalSec
int reportIntervalSec = 300; int reportIntervalSec = 300;
JSONArray itemFieldMappings = null; JSONArray itemFieldMappings = null;
String fieldName = "sensorData"; String fieldName = resolveArrayFieldName(requestSchema, null);
JSONObject extra = null;
if (StringUtils.hasText(mapping.getExtraMapping())) { if (StringUtils.hasText(mapping.getExtraMapping())) {
JSONObject extra = JSON.parseObject(mapping.getExtraMapping()); extra = JSON.parseObject(mapping.getExtraMapping());
reportIntervalSec = extra.getIntValue("reportIntervalSec", 300); reportIntervalSec = extra.getIntValue("reportIntervalSec", 300);
itemFieldMappings = extra.getJSONArray("itemFieldMappings"); itemFieldMappings = extra.getJSONArray("itemFieldMappings");
if (StringUtils.hasText(extra.getString("fieldName"))) { if (StringUtils.hasText(extra.getString("fieldName"))) {
fieldName = extra.getString("fieldName"); fieldName = extra.getString("fieldName");
} }
} }
JSONObject arrayFieldSchema = findArrayFieldSchema(requestSchema, fieldName);
String arrayMode = resolveArrayMode(arrayFieldSchema, extra);
// 从 onboard_config 取 sensorId // 从 onboard_config 取 sensorId
String sensorId = reportSerialNumber; String sensorId = reportSerialNumber;
@ -170,6 +177,13 @@ public class DcpPlatformAdapter {
JSONObject currentBodyFields = buildObjectBySchema(platform, mapping, requestSchema, reportData, fieldMappings); JSONObject currentBodyFields = buildObjectBySchema(platform, mapping, requestSchema, reportData, fieldMappings);
// 单点数组推送:sensorData 中只包含当前上报子设备这一条数据。
if (ARRAY_MODE_SINGLE.equals(arrayMode)) {
pushSingleArrayData(platform, mapping, subMapping, reportSerialNumber, baseUrl,
interfaceCode, fieldName, currentItem, currentBodyFields);
return;
}
if (subMapping.isReference()) { if (subMapping.isReference()) {
// 基准点上报:缓存当前基准点,并补推此前已缓存的监测点 // 基准点上报:缓存当前基准点,并补推此前已缓存的监测点
bufferService.saveReferenceData( bufferService.saveReferenceData(
@ -216,6 +230,25 @@ public class DcpPlatformAdapter {
} }
} }
private void pushSingleArrayData(PlatformInstance platform, PlatformDeviceMapping mapping,
PlatformDeviceSubMapping subMapping, String reportSerialNumber,
String baseUrl, String interfaceCode, String fieldName,
JSONObject currentItem, JSONObject currentBodyFields) {
String deviceId = subMapping.getPlatformDeviceId();
if (!StringUtils.hasText(deviceId)) {
log.warn("[DCP] 子设备 {} 未分配平台设备ID,跳过单点数组推送", reportSerialNumber);
return;
}
JSONArray sensorData = new JSONArray();
sensorData.add(currentItem);
JSONArray pushBody = buildArrayPushBody(mapping, currentBodyFields, fieldName, sensorData, deviceId);
PlatformPushLog logEntry = createLogEntry(platform, mapping, interfaceCode, reportSerialNumber);
doHttpPost(platform, baseUrl, JSON.toJSONString(pushBody), logEntry);
pushLogService.asyncSave(logEntry);
log.info("[DCP] 子设备 {} 单点数组已推送 deviceId={}", reportSerialNumber, deviceId);
}
/** /**
* 请求 schema 是否包含数组字段 * 请求 schema 是否包含数组字段
*/ */
@ -232,6 +265,33 @@ public class DcpPlatformAdapter {
return false; return false;
} }
private String resolveArrayFieldName(JSONArray requestSchema, String fallback) {
if (StringUtils.hasText(fallback)) {
return fallback;
}
JSONObject firstArrayField = findFirstArrayFieldSchema(requestSchema);
if (firstArrayField != null && StringUtils.hasText(firstArrayField.getString("name"))) {
return firstArrayField.getString("name");
}
return "sensorData";
}
private String resolveArrayMode(JSONObject arrayFieldSchema, JSONObject extra) {
String mode = null;
if (extra != null && StringUtils.hasText(extra.getString("arrayMode"))) {
mode = extra.getString("arrayMode");
} else if (arrayFieldSchema != null && StringUtils.hasText(arrayFieldSchema.getString("arrayMode"))) {
mode = arrayFieldSchema.getString("arrayMode");
}
if (!StringUtils.hasText(mode)) {
return ARRAY_MODE_REFERENCE_PAIR;
}
if (ARRAY_MODE_SINGLE.equals(mode) || ARRAY_MODE_REFERENCE_PAIR.equals(mode)) {
return mode;
}
throw new RuntimeException("不支持的数组组包模式 arrayMode=" + mode);
}
/** 解析基准点序列号 */ /** 解析基准点序列号 */
private String resolveRefSerialNumber(PlatformDeviceSubMapping subMapping, PlatformDeviceMapping mapping) { private String resolveRefSerialNumber(PlatformDeviceSubMapping subMapping, PlatformDeviceMapping mapping) {
try { try {
@ -633,6 +693,19 @@ public class DcpPlatformAdapter {
return null; return null;
} }
private JSONObject findFirstArrayFieldSchema(JSONArray requestSchema) {
if (requestSchema == null || requestSchema.isEmpty()) {
return null;
}
for (int i = 0; i < requestSchema.size(); i++) {
JSONObject schemaField = requestSchema.getJSONObject(i);
if ("array".equalsIgnoreCase(schemaField.getString("type"))) {
return schemaField;
}
}
return null;
}
private JSONObject copyJsonObject(JSONObject source) { private JSONObject copyJsonObject(JSONObject source) {
if (source == null || source.isEmpty()) { if (source == null || source.isEmpty()) {
return new JSONObject(); return new JSONObject();

View File

@ -295,7 +295,7 @@ public class PlatformFlowExecutor {
if (StringUtils.hasText(ctx.getCurrentMonitorSerialNumber())) { if (StringUtils.hasText(ctx.getCurrentMonitorSerialNumber())) {
return ctx.getCurrentMonitorSerialNumber(); return ctx.getCurrentMonitorSerialNumber();
} }
return resolveGatewaySerialNumber(ctx.getDeviceMapping()); return normalizeValueByType(fd.getString("type"), resolveGatewaySerialNumber(ctx.getDeviceMapping()));
} }
if ("gateway_serial_number".equals(df)) { if ("gateway_serial_number".equals(df)) {
return resolveGatewaySerialNumber(ctx.getDeviceMapping()); return resolveGatewaySerialNumber(ctx.getDeviceMapping());

View File

@ -127,13 +127,7 @@ public class PlatformDeviceMappingServiceImpl extends ServiceImpl<PlatformDevice
ctx.setReferenceSubMappingOnboardConfig(reference.getOnboardConfig()); ctx.setReferenceSubMappingOnboardConfig(reference.getOnboardConfig());
} }
try { flowExecutor.execute(onboardFlow, ctx);
flowExecutor.execute(onboardFlow, ctx);
} catch (Exception e) {
log.error("[Onboard] 网关子设备 {} 入网失败: {}", monitor.getSubSerialNumber(), e.getMessage(), e);
failedMessages.add(monitor.getSubSerialNumber() + ": " + e.getMessage());
continue;
}
// 从流程上下文取回平台分配的 ID // 从流程上下文取回平台分配的 ID
Object pointId = ctx.getFromContext("step1.id"); Object pointId = ctx.getFromContext("step1.id");

View File

@ -99,7 +99,7 @@
<el-divider content-position="left">入参定义(request_schema)</el-divider> <el-divider content-position="left">入参定义(request_schema)</el-divider>
<div class="schema-tip"> <div class="schema-tip">
定义接口需要的入参字段。参数来源使用业务化名称: 定义接口需要的入参字段。参数来源使用业务化名称:
固定值、认证参数、平台参数、上一步结果、当前设备、自动UUID、子设备传感器列表、基准点成对传感器列表、设备物模型、上报时间。 固定值、认证参数、平台参数、上一步结果、当前设备、自动UUID、子设备传感器列表、基准点成对传感器列表、设备物模型、上报时间。数组字段可配置组包模式。
</div> </div>
<el-table :data="requestSchemaRows" size="mini" border style="width:100%;margin-top:8px"> <el-table :data="requestSchemaRows" size="mini" border style="width:100%;margin-top:8px">
<el-table-column label="字段名" width="130"> <el-table-column label="字段名" width="130">
@ -171,6 +171,14 @@
<el-checkbox v-model="scope.row.required" /> <el-checkbox v-model="scope.row.required" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="组包模式" width="130">
<template slot-scope="scope">
<el-select v-if="scope.row.type === 'array'" v-model="scope.row.arrayMode" size="mini" clearable placeholder="默认配对" style="width:100%">
<el-option label="单条" value="single" />
<el-option label="基准配对" value="reference_pair" />
</el-select>
</template>
</el-table-column>
<el-table-column label="说明" width="140"> <el-table-column label="说明" width="140">
<template slot-scope="scope"> <template slot-scope="scope">
<el-input v-model="scope.row.desc" size="mini" placeholder="字段说明" /> <el-input v-model="scope.row.desc" size="mini" placeholder="字段说明" />
@ -424,7 +432,7 @@ export default {
} }
}, },
addRequestField() { addRequestField() {
this.requestSchemaRows.push({ name: '', type: 'string', source: 'fixed', value: '', field: '', contextKey: '', required: false, desc: '', itemSchema: [] }) this.requestSchemaRows.push({ name: '', type: 'string', source: 'fixed', value: '', field: '', contextKey: '', required: false, desc: '', arrayMode: '', itemSchema: [] })
}, },
addResponseField() { addResponseField() {
this.responseSchemaRows.push({ name: '', type: 'string', path: '', desc: '' }) this.responseSchemaRows.push({ name: '', type: 'string', path: '', desc: '' })
@ -457,6 +465,7 @@ export default {
// 将表格行序列化为 JSON 存入 form // 将表格行序列化为 JSON 存入 form
this.form.requestSchema = JSON.stringify(this.requestSchemaRows.filter(r => r.name).map(r => { this.form.requestSchema = JSON.stringify(this.requestSchemaRows.filter(r => r.name).map(r => {
const field = { name: r.name, type: r.type, source: r.source, required: !!r.required, desc: r.desc } const field = { name: r.name, type: r.type, source: r.source, required: !!r.required, desc: r.desc }
if (r.type === 'array' && r.arrayMode) field.arrayMode = r.arrayMode
if (r.type === 'array' && r.itemSchema && r.itemSchema.length > 0) { if (r.type === 'array' && r.itemSchema && r.itemSchema.length > 0) {
field.itemSchema = r.itemSchema.filter(s => s.name).map(s => { field.itemSchema = r.itemSchema.filter(s => s.name).map(s => {
const item = { name: s.name, type: s.type || 'string', source: s.source || 'thingsModel', desc: s.desc } const item = { name: s.name, type: s.type || 'string', source: s.source || 'thingsModel', desc: s.desc }