From ff4d97f0cc555afbbd34a58ea670248adeda55fb Mon Sep 17 00:00:00 2001 From: crosstyan Date: Mon, 7 Jul 2025 17:50:11 +0800 Subject: [PATCH] Update example.jsonc timestamps and correct location coordinates order in README.md; enhance documentation clarity and add notes for timestamp usage. --- READMD.md | 106 +++++++++++----- example.jsonc | 16 +-- schema.json | 338 -------------------------------------------------- 3 files changed, 80 insertions(+), 380 deletions(-) diff --git a/READMD.md b/READMD.md index 19c0977..55a8a9e 100644 --- a/READMD.md +++ b/READMD.md @@ -32,7 +32,7 @@ WHTP 消息由根级字段与 `fields` 数组两部分组成。根级字段描 可选,字符串,默认 `"descriptive"`。 * `descriptive` —— 自描述模式,允许在字段 `metadata` 中携带全部上下文信息,消息本身即可被独立解析。 -* `strict` —— 严格模式,不允许出现字段级 `metadata`,字段定义需通过外部文档约定,适用于带宽受限或固定 Schema 场景。(目前无实际意义, 需要与二进制协议配合) +* `strict` (RFU, reserved for future use) —— 严格模式,不允许出现字段级 `metadata`,字段定义需通过外部文档约定,适用于带宽受限或固定 Schema 场景。(目前无实际意义, 需要与二进制协议配合) ### 设备 ID (`dev_id`) @@ -50,9 +50,14 @@ type DeviceType = string | { type_id: string } | { type_name: string } 2. **对象** `{ "type_id": UUID }`;UUID 为设备型号的唯一标识, 由平台分配 3. **对象** `{ "type_name": string }`。 -以上三种写法在语义上等价,由数据生产方按需选择。 +> [!NOTE] +> +> 以上三种写法在语义上等价, 由数据生产方按需选择 +> +> - 对于长期稳定的系统, 推荐使用 `{ "type_id": UUID }` 格式, 作为稳定的唯一标识符 +> - 对于临时场景, 可以使用 `string` 或 `{ "type_name": string }` 格式 -### 消息时间戳 (`timestamp`) +### 根级时间戳 (`timestamp`) ```typescript type Timestamp = @@ -72,11 +77,14 @@ type Timestamp = - `unix_ms` → UNIX 毫秒 - `unix_us` → UNIX 微秒 - `unix_ns` → UNIX 纳秒 - - `iso8601` → ISO8601 标准格式(推荐 UTC,Z 结尾) + - `iso8601` → [ISO8601](https://en.wikipedia.org/wiki/ISO_8601) 标准格式(必须为 UTC,以 Z 结尾或包含 +00:00 时区偏移) > [!WARNING] > `"timestamp": "2025-01-01T00:00:00Z"` 为非法, 应使用 `"timestamp": { "iso8601": "2025-01-01T00:00:00Z" }` 或 `"timestamp": 1715145600` +> [!NOTE] +> 根级 (root-level) 时间戳表示消息生成或入队时刻, 字段级 (field-level) 时间戳表示实际数据采样时刻, 应使用字段级时间戳进行所有时间序列分析 + ### field 字段 (`fields`) 必填。`Field` 对象数组,数组中每个元素代表一个测量项,其 `id` 在同一条消息内必须唯一。关于 `Field` 对象的定义详见后文 "field 字段" 章节。 @@ -100,10 +108,11 @@ interface MessageMetadata { 可选。全局枚举定义表,用于在字段 `fields[*].metadata.data_type` 中以 `enum:` 形式引用。 ```typescript -type EnumDefinition = { id: string; value: Record }; +type InlineEnum = Record +type EnumDefinition = { id: string; value: InlineEnum }; ``` -示例: +示例 ```jsonc "metadata": { @@ -116,16 +125,37 @@ type EnumDefinition = { id: string; value: Record }; } ``` +等价于 + +```cpp +enum class AirQuality { + GOOD = 1, + MODERATE = 2, + BAD = 3, +}; +``` + #### 位置信息 (`location`) -可选。表示消息采集时的设备地理位置: +可选。表示消息采集时的设备地理位置 + +```typescript +type Location = { + coordinates: [number, number]; // 经度 (longitude)、纬度 (latitude)(度); 顺序不可颠倒 + coordinate_system?: "wgs84" | "gcj02" | "bd09"; // 可选,指定坐标系, 默认 "gcj02" + accuracy?: number; // 可选,水平精度(米) + altitude?: number; // 可选,海拔高度(米) +} +``` + +示例 ```jsonc { - "coordinates": [24.123456, 120.123456], // 纬度、经度(度) - "coordinate_system": "wgs84", // 可选,指定坐标系, 默认 "gcj02" 可选项为 wgs84, gcj02, bd09 - "accuracy": 10, // 可选,水平精度(米) - "altitude": 100 // 可选,海拔高度(米) + "coordinates": [120.123456, 24.123456], // 经度、纬度(度) + "coordinate_system": "wgs84", + "accuracy": 10, + "altitude": 100 } ``` @@ -139,7 +169,11 @@ type EnumDefinition = { id: string; value: Record }; #### 用户自定义信息 (`user`) -可选。任意键值对,为数据生产方保留的自定义信息区: +可选。任意键值对,为数据生产方保留的自定义信息区 + +```typescript +type UserData = Record +``` ## 消息描述字段 (fields) @@ -167,7 +201,7 @@ type EnumDefinition = { id: string; value: Record }; ``` `t` 数组必须与 `v` 数组长度相同, 且 `t` 数组中的时间戳必须严格递增, 否则会被视为非法数据 -`null` 与 `undefined` 不被支持,嵌套数组(如 `array>`)亦非法。 +`null` 与 `undefined` 不被支持,嵌套数组(如 `array>`)亦为非法。 ### 元数据 (`metadata`) @@ -283,26 +317,26 @@ type BatchType T = ArrayType T | IrregularType T 可选。非零整数。0 表示无错误;其他值与以下错误码表对应,用于描述测量异常。 -| 代码 | 分类 | 名称 | 描述 | -| ---------- | ---------- | --------------------- | -------------------------------------------------------- | -| 0 | 通用 | OK | 无错误(**不应在 `error_code` 字段出现**,仅表字段正常) | -| 1 | 通用 | UNKNOWN_ERROR | 未知错误,无法归类 | -| 2 | 通用 | UNSUPPORTED_DATA_TYPE | 不支持的数据类型或格式 | -| 3 | 通用 | INVALID_VALUE | 字段值非法(超范围、格式错等) | -| 16 | 设备 | DEVICE_BUSY | 设备繁忙,暂无法提供数据 | -| 17 | 设备 | DEVICE_INIT_FAILED | 设备初始化失败 | -| 32 | 传输 | PACKET_LOST | 报文丢失(重传失败) | -| 33 | 传输 | CRC_ERROR | 校验和 / CRC 错误 | -| 48 | 供电 | LOW_BATTERY | 电量过低,测量值可能不可靠 | -| 64 | 传感器 | SENSOR_DISCONNECTED | 传感器掉线或未接入 | -| 65 | 传感器 | SENSOR_MALFUNCTION | 传感器故障(无响应、数据乱流等) | -| 66 | 传感器 | SENSOR_OUT_OF_RANGE | 超出测量范围 | -| 80 | 校准 | CALIBRATION_REQUIRED | 需要校准或校准过期 | -| 96 | 存储 | STORAGE_FULL | 本地存储空间不足 | -| 112 | GNSS | GPS_FIX_LOST | GNSS 信号丢失,无位置信息 | -| 128–511 | 预留 | — | 预留给未来通用错误 | -| 512–1023 | 应用 | — | 留给具体应用 / 产品线自定义 | -| 1024–65535 | 厂商自定义 | — | 厂商或项目私有错误码区间 | +| 代码 | 分类 | 名称 | 描述 | +| ---------- | ---------- | --------------------- | ------------------------------------------ | +| 0 | 通用 | OK | 无错误(**不应在 `error_code` 字段出现**) | +| 1 | 通用 | UNKNOWN_ERROR | 未知错误,无法归类 | +| 2 | 通用 | UNSUPPORTED_DATA_TYPE | 不支持的数据类型或格式 | +| 3 | 通用 | INVALID_VALUE | 字段值非法(超范围、格式错等) | +| 16 | 设备 | DEVICE_BUSY | 设备繁忙,暂无法提供数据 | +| 17 | 设备 | DEVICE_INIT_FAILED | 设备初始化失败 | +| 32 | 传输 | PACKET_LOST | 报文丢失(重传失败) | +| 33 | 传输 | CRC_ERROR | 校验和 / CRC 错误 | +| 48 | 供电 | LOW_BATTERY | 电量过低,测量值可能不可靠 | +| 64 | 传感器 | SENSOR_DISCONNECTED | 传感器掉线或未接入 | +| 65 | 传感器 | SENSOR_MALFUNCTION | 传感器故障(无响应、数据乱流等) | +| 66 | 传感器 | SENSOR_OUT_OF_RANGE | 超出测量范围 | +| 80 | 校准 | CALIBRATION_REQUIRED | 需要校准或校准过期 | +| 96 | 存储 | STORAGE_FULL | 本地存储空间不足 | +| 112 | GNSS | GPS_FIX_LOST | GNSS 信号丢失,无位置信息 | +| 128–511 | 预留 | — | 预留给未来通用错误 | +| 512–1023 | 应用 | — | 留给具体应用 / 产品线自定义 | +| 1024–65535 | 厂商自定义 | — | 厂商或项目私有错误码区间 | #### 错误信息 (`error_msg`) @@ -321,11 +355,15 @@ type Confidence = number | number[] 置信度的取值范围为 0 至 1,其中 0 表示完全不可信,1 表示完全可信(100%)。 +> [!NOTE] +> 当一个字段包含非零的 `error_code` 时, 强烈建议将置信度设置为 0 以表示该字段值不可靠 + > [!WARNING] > - 对于单值(scalar)数据,不应填写数组形式的置信度,若填写则无意义,解析器将会忽略 > - 对于批量数据,既可填写单一置信度(所有采样点共享),也可填写置信度数组(每个采样点单独指定) > - 如未指定 confidence 字段,则默认所有采样点的置信度为 1 + #### 采样间隔 (`sample_interval`) 可选。采样间隔, 用于描述 `value` 为等间隔数组时, 数组中每个元素的时间间隔 (建议所有等间隔数组都显式填写该字段) @@ -414,7 +452,7 @@ interface Field { interface Message { version: 1; msg_type: "telemetry"; - mode?: "descriptive" | "strict"; // 仅支持 descriptive 模式 + mode?: "descriptive" | "strict"; // 目前仅支持 descriptive 模式 dev_id: string; dev_type?: string | { type_id: string } | { type_name: string }; timestamp: Timestamp; diff --git a/example.jsonc b/example.jsonc index cd6a8da..2d3333e 100644 --- a/example.jsonc +++ b/example.jsonc @@ -138,11 +138,11 @@ // any legal timestamp format is supported "t": [ 1715145600, - 1715145600, - 1715145600, - 1715145600, - 1715145600, - 1715145600 + 1715145602, + 1715145604, + 1715145606, + 1715145608, + 1715145610 ] }, "metadata": { @@ -200,9 +200,9 @@ // optional "location": { "coordinates": [ - 24.123456, - 120.123456 - ], // latitude and longitude in degrees + 120.123456, + 24.123456 + ], // longitude and latitude in degrees // optional, defaults to "gcj02" "coordinate_system": "wgs84", // "wgs84", "gcj02", "bd09" // optional diff --git a/schema.json b/schema.json index 05086f5..e69de29 100644 --- a/schema.json +++ b/schema.json @@ -1,338 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://example.com/telemetry.schema.json", - "title": "Telemetry Message", - "description": "Schema describing a telemetry message exchanged by devices.", - "type": "object", - "additionalProperties": false, - "properties": { - "version": { - "type": "integer", - "minimum": 1, - "description": "Protocol semantic version (integer)." - }, - "msg_type": { - "type": "string", - "enum": [ - "telemetry" - ], - "description": "Currently only the literal string 'telemetry' is supported." - }, - "dev_id": { - "$ref": "#/$defs/uuid" - }, - "dev_type": { - "description": "Device type identifier (string or one-of helper objects).", - "oneOf": [ - { - "type": "string" - }, - { - "type": "object", - "required": [ - "type_id" - ], - "additionalProperties": false, - "properties": { - "type_id": { - "$ref": "#/$defs/uuid" - } - } - }, - { - "type": "object", - "required": [ - "type_name" - ], - "additionalProperties": false, - "properties": { - "type_name": { - "type": "string" - } - } - } - ] - }, - "timestamp": { - "$ref": "#/$defs/timestamp" - }, - "fields": { - "type": "array", - "description": "Array of discrete or batch field objects.", - "items": { - "oneOf": [ - { - "$ref": "#/$defs/discrete_field" - }, - { - "$ref": "#/$defs/batch_field" - } - ] - } - }, - "enum": { - "type": "array", - "items": { - "$ref": "#/$defs/enum_definition" - } - }, - "location": { - "$ref": "#/$defs/location" - }, - "battery": { - "type": "integer", - "minimum": 0, - "maximum": 100, - "description": "Battery percentage (0-100)." - }, - "sqe": { - "type": "integer", - "minimum": 0, - "maximum": 4294967295, - "description": "Sample sequence counter (uint32)." - } - }, - "required": [ - "version", - "msg_type", - "dev_id", - "dev_type", - "timestamp", - "fields" - ], - "$defs": { - "uuid": { - "type": "string", - "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$", - "description": "UUID v4 string." - }, - "timestamp": { - "description": "UNIX epoch (seconds) or an object with a single alternative timestamp representation.", - "oneOf": [ - { - "type": "number" - }, - { - "type": "object", - "minProperties": 1, - "maxProperties": 1, - "additionalProperties": false, - "properties": { - "unix_ns": { - "type": "number" - }, - "unix_us": { - "type": "number" - }, - "unix_ms": { - "type": "number" - }, - "unix_s": { - "type": "number" - }, - "iso8601": { - "type": "string", - "format": "date-time" - } - } - } - ] - }, - "sample_interval": { - "oneOf": [ - { - "type": "number", - "description": "Interval in seconds." - }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "ms": { - "type": "number" - }, - "s": { - "type": "number" - }, - "m": { - "type": "number" - }, - "h": { - "type": "number" - }, - "d": { - "type": "number" - }, - "w": { - "type": "number" - } - } - } - ] - }, - "confidence": { - "oneOf": [ - { - "type": "number", - "minimum": 0, - "maximum": 1 - }, - { - "type": "array", - "items": { - "type": "number", - "minimum": 0, - "maximum": 1 - } - } - ] - }, - "discrete_field": { - "type": "object", - "additionalProperties": false, - "required": [ - "name", - "data_type", - "value", - "timestamp" - ], - "properties": { - "name": { - "type": "string" - }, - "data_type": { - "type": "string" - }, - "value": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "number" - }, - { - "type": "boolean" - } - ] - }, - "timestamp": { - "$ref": "#/$defs/timestamp" - }, - "unit": { - "type": "string" - }, - "confidence": { - "$ref": "#/$defs/confidence" - } - } - }, - "batch_field": { - "type": "object", - "additionalProperties": false, - "required": [ - "name", - "array_type", - "value", - "timestamp" - ], - "properties": { - "name": { - "type": "string" - }, - "array_type": { - "type": "string" - }, - "value": { - "type": "array", - "items": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "number" - }, - { - "type": "boolean" - } - ] - } - }, - "timestamp": { - "$ref": "#/$defs/timestamp" - }, - "sample_interval": { - "$ref": "#/$defs/sample_interval" - }, - "unit": { - "type": "string" - }, - "confidence": { - "$ref": "#/$defs/confidence" - } - } - }, - "enum_definition": { - "type": "object", - "additionalProperties": false, - "required": [ - "identifier", - "value" - ], - "properties": { - "identifier": { - "type": "string" - }, - "value": { - "type": "object", - "additionalProperties": { - "type": "number" - } - } - } - }, - "location": { - "type": "object", - "additionalProperties": false, - "required": [ - "coordinates" - ], - "properties": { - "coordinates": { - "type": "array", - "minItems": 2, - "maxItems": 2, - "items": [ - { - "type": "number", - "minimum": -90, - "maximum": 90 - }, - { - "type": "number", - "minimum": -180, - "maximum": 180 - } - ] - }, - "coordinate_system": { - "type": "string", - "enum": [ - "wgs84", - "gcj02", - "bd09" - ], - "default": "gcj02" - }, - "accuracy": { - "type": "number", - "minimum": 0 - }, - "altitude": { - "type": "number" - } - } - } - } -} \ No newline at end of file