EMQX属性抄读(property/get)下发主题
This commit is contained in:
parent
7d2fd92c32
commit
f1adde0f3b
@ -3019,6 +3019,26 @@ export const DeviceOnlineStatusSchema = {
|
||||
description: '设备在线状态'
|
||||
} 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 = {
|
||||
required: ['id', 'propertyList'],
|
||||
type: 'object',
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -1758,6 +1758,19 @@ export type DeviceMasterSwitchInput = {
|
||||
*/
|
||||
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 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 = {
|
||||
query?: {
|
||||
input?: DeviceUpgradeForApiInput;
|
||||
|
||||
@ -42,7 +42,9 @@ import {
|
||||
postAggregationDeviceDeviceCommandForApiAsync,
|
||||
postAggregationDeviceDeviceUpgradeForApiAsync,
|
||||
postAggregationDeviceEnableDeviceForApiAsync,
|
||||
postAggregationDeviceGetDevicePropertyReadResultForApiAsync,
|
||||
postAggregationDeviceGetDevicePropertyValueForApiAsync,
|
||||
postAggregationDeviceIssueDevicePropertyReadForApiAsync,
|
||||
postAggregationDeviceRecoverDeviceToOneNet,
|
||||
postAggregationDeviceRepushDeviceInfoToIoTplatform,
|
||||
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/plain,axios 会把纯数字体 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) => {
|
||||
// 检查是否为只写属性
|
||||
@ -2434,18 +2491,15 @@ const readData = async (property: ThingModelProperty) => {
|
||||
|
||||
property.loading = true;
|
||||
try {
|
||||
const result = await postAggregationDeviceGetDevicePropertyValueForApiAsync(
|
||||
{
|
||||
body: {
|
||||
id: String(commandRow.value.id),
|
||||
propertyList: { [identifier]: '' },
|
||||
},
|
||||
},
|
||||
const result = await readPropertyValues(
|
||||
String(commandRow.value.id),
|
||||
Number(commandRow.value.ioTPlatform),
|
||||
{ [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) {
|
||||
property.result =
|
||||
typeof propertyValue === 'string'
|
||||
@ -2519,23 +2573,18 @@ const submitBatchRead = async () => {
|
||||
prop.loading = true;
|
||||
});
|
||||
|
||||
// 调用批量抄读接口
|
||||
const result = await postAggregationDeviceGetDevicePropertyValueForApiAsync(
|
||||
{
|
||||
body: {
|
||||
id: String(commandRow.value.id),
|
||||
propertyList,
|
||||
},
|
||||
},
|
||||
// 调用批量抄读接口(EMQX 走异步抄读,内部自行发起 + 轮询)
|
||||
const result = await readPropertyValues(
|
||||
String(commandRow.value.id),
|
||||
Number(commandRow.value.ioTPlatform),
|
||||
propertyList,
|
||||
);
|
||||
|
||||
if (result.data !== undefined && result.data !== null) {
|
||||
if (result !== undefined && result !== null) {
|
||||
// 更新所有选中行的返回结果
|
||||
selectedProperties.forEach((prop) => {
|
||||
const identifier = getPropertyIdentifier(prop);
|
||||
const propertyValue = identifier
|
||||
? (result.data as any)[identifier]
|
||||
: undefined;
|
||||
const propertyValue = identifier ? result[identifier] : undefined;
|
||||
if (propertyValue !== undefined && propertyValue !== null) {
|
||||
prop.result =
|
||||
typeof propertyValue === 'string'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user