EMQX属性抄读(property/get)下发主题
This commit is contained in:
parent
7d2fd92c32
commit
f1adde0f3b
@ -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
@ -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;
|
||||||
|
|||||||
@ -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/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) => {
|
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'
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user