EMQX属性抄读(property/get)下发主题

This commit is contained in:
ChenYi 2026-07-17 23:35:59 +08:00
parent 7d2fd92c32
commit f1adde0f3b
4 changed files with 146 additions and 22 deletions

View File

@ -3019,6 +3019,26 @@ export const DeviceOnlineStatusSchema = {
description: '设备在线状态' description: '设备在线状态'
} as const; } as const;
export const DevicePropertyReadResultForApiInputSchema = {
required: ['id'],
type: 'object',
properties: {
id: {
type: 'string',
format: 'uuid'
},
requestId: {
type: 'string',
description: '发起抄读时返回的关联 Id',
nullable: true
}
},
additionalProperties: false,
description: `查询 EMQX 异步抄读结果的入参。
EMQX broker OneNET property/get get_reply
"发起(拿 RequestId→ 轮询本接口取结果"`
} as const;
export const DevicePropertyValueForApiInputSchema = { export const DevicePropertyValueForApiInputSchema = {
required: ['id', 'propertyList'], required: ['id', 'propertyList'],
type: 'object', type: 'object',

File diff suppressed because one or more lines are too long

View File

@ -1758,6 +1758,19 @@ export type DeviceMasterSwitchInput = {
*/ */
export type DeviceOnlineStatus = 'Online' | 'Offline'; export type DeviceOnlineStatus = 'Online' | 'Offline';
/**
* EMQX
* EMQX broker OneNET property/get get_reply
* "发起(拿 RequestId→ 轮询本接口取结果"
*/
export type DevicePropertyReadResultForApiInput = {
id: string;
/**
* Id
*/
requestId?: (string) | null;
};
/** /**
* *
*/ */
@ -7515,6 +7528,28 @@ export type PostAggregationDeviceGetDevicePropertyValueForApiAsyncResponse = ({
export type PostAggregationDeviceGetDevicePropertyValueForApiAsyncError = unknown; export type PostAggregationDeviceGetDevicePropertyValueForApiAsyncError = unknown;
export type PostAggregationDeviceIssueDevicePropertyReadForApiAsyncData = {
query?: {
input?: DevicePropertyValueForApiInput;
};
};
export type PostAggregationDeviceIssueDevicePropertyReadForApiAsyncResponse = (string);
export type PostAggregationDeviceIssueDevicePropertyReadForApiAsyncError = unknown;
export type PostAggregationDeviceGetDevicePropertyReadResultForApiAsyncData = {
query?: {
input?: DevicePropertyReadResultForApiInput;
};
};
export type PostAggregationDeviceGetDevicePropertyReadResultForApiAsyncResponse = ({
[key: string]: unknown;
});
export type PostAggregationDeviceGetDevicePropertyReadResultForApiAsyncError = unknown;
export type PostAggregationDeviceDeviceUpgradeForApiAsyncData = { export type PostAggregationDeviceDeviceUpgradeForApiAsyncData = {
query?: { query?: {
input?: DeviceUpgradeForApiInput; input?: DeviceUpgradeForApiInput;

View File

@ -42,7 +42,9 @@ import {
postAggregationDeviceDeviceCommandForApiAsync, postAggregationDeviceDeviceCommandForApiAsync,
postAggregationDeviceDeviceUpgradeForApiAsync, postAggregationDeviceDeviceUpgradeForApiAsync,
postAggregationDeviceEnableDeviceForApiAsync, postAggregationDeviceEnableDeviceForApiAsync,
postAggregationDeviceGetDevicePropertyReadResultForApiAsync,
postAggregationDeviceGetDevicePropertyValueForApiAsync, postAggregationDeviceGetDevicePropertyValueForApiAsync,
postAggregationDeviceIssueDevicePropertyReadForApiAsync,
postAggregationDeviceRecoverDeviceToOneNet, postAggregationDeviceRecoverDeviceToOneNet,
postAggregationDeviceRepushDeviceInfoToIoTplatform, postAggregationDeviceRepushDeviceInfoToIoTplatform,
postAggregationDeviceUpdateDeviceSecKeyForApiAsync, postAggregationDeviceUpdateDeviceSecKeyForApiAsync,
@ -2413,6 +2415,61 @@ const sendCommand = async (property: ThingModelProperty) => {
} }
}; };
/** EMQX 异步抄读轮询:间隔与最大等待时长。后端"待应答"标记存活 60s这里略短于它即可 */
const EMQX_READ_POLL_INTERVAL_MS = 1000;
const EMQX_READ_POLL_TIMEOUT_MS = 50_000;
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
/**
* 抄读一批属性屏蔽平台差异统一返回 { 标准物模型标识符: }
*
* OneNET 有平台侧属性快照可同步查询直接返回
* EMQX 只是 broker没有这种快照只能下发 property/get 等设备 get_reply 应答
* 故走"发起拿 RequestId → 轮询取结果"两步两者返回的键值对形状一致调用方无需关心
*/
const readPropertyValues = async (
deviceId: string,
platform: number,
propertyList: Record<string, string>,
): Promise<Record<string, any> | null> => {
if (platform !== IOT_PLATFORM_EMQX) {
const result = await postAggregationDeviceGetDevicePropertyValueForApiAsync({
body: { id: deviceId, propertyList },
});
return (result.data as any) ?? null;
}
const issued = await postAggregationDeviceIssueDevicePropertyReadForApiAsync({
body: { id: deviceId, propertyList },
});
// requestId Id JSON
// string text/plainaxios JSON.parse number
// Number.MAX_SAFE_INTEGER
const requestId = (issued.data as any)?.requestId;
if (!requestId) {
throw new Error('发起抄读失败,未获取到抄读关联 Id');
}
// MQTT + EMQX
const deadline = Date.now() + EMQX_READ_POLL_TIMEOUT_MS;
while (Date.now() < deadline) {
await sleep(EMQX_READ_POLL_INTERVAL_MS);
const polled =
await postAggregationDeviceGetDevicePropertyReadResultForApiAsync({
body: { id: deviceId, requestId },
});
// null
if (polled.data !== undefined && polled.data !== null) {
return polled.data as any;
}
}
throw new Error('抄读超时,设备未在规定时间内应答');
};
// //
const readData = async (property: ThingModelProperty) => { const readData = async (property: ThingModelProperty) => {
// //
@ -2434,18 +2491,15 @@ const readData = async (property: ThingModelProperty) => {
property.loading = true; property.loading = true;
try { try {
const result = await postAggregationDeviceGetDevicePropertyValueForApiAsync( const result = await readPropertyValues(
{ String(commandRow.value.id),
body: { Number(commandRow.value.ioTPlatform),
id: String(commandRow.value.id), { [identifier]: '' },
propertyList: { [identifier]: '' },
},
},
); );
if (result.data !== undefined && result.data !== null) { if (result !== undefined && result !== null) {
// //
const propertyValue = (result.data as any)[identifier]; const propertyValue = result[identifier];
if (propertyValue !== undefined && propertyValue !== null) { if (propertyValue !== undefined && propertyValue !== null) {
property.result = property.result =
typeof propertyValue === 'string' typeof propertyValue === 'string'
@ -2519,23 +2573,18 @@ const submitBatchRead = async () => {
prop.loading = true; prop.loading = true;
}); });
// // EMQX +
const result = await postAggregationDeviceGetDevicePropertyValueForApiAsync( const result = await readPropertyValues(
{ String(commandRow.value.id),
body: { Number(commandRow.value.ioTPlatform),
id: String(commandRow.value.id),
propertyList, propertyList,
},
},
); );
if (result.data !== undefined && result.data !== null) { if (result !== undefined && result !== null) {
// //
selectedProperties.forEach((prop) => { selectedProperties.forEach((prop) => {
const identifier = getPropertyIdentifier(prop); const identifier = getPropertyIdentifier(prop);
const propertyValue = identifier const propertyValue = identifier ? result[identifier] : undefined;
? (result.data as any)[identifier]
: undefined;
if (propertyValue !== undefined && propertyValue !== null) { if (propertyValue !== undefined && propertyValue !== null) {
prop.result = prop.result =
typeof propertyValue === 'string' typeof propertyValue === 'string'