diff --git a/READMD.md b/READMD.md index 46203b8..fa4ebe8 100644 --- a/READMD.md +++ b/READMD.md @@ -1,37 +1,62 @@ # WH Telemetry Protocol -... +WH Telemetry Protocol(简称 WHTP)是一套以 JSON 为载体的遥测数据上行协议,强调自描述能力与轻量化设计,适用于物联网设备与云端之间的高效数据传输。 ## 总览 -... +WHTP 消息由根级字段与 `fields` 数组两部分组成。根级字段描述消息本身及其环境;`fields` 数组承载具体测量项。根级字段概览如下: + +| 字段 | 必选 | 类型 | 说明 | +| --------- | ---- | --------------- | ---------------------------------------------- | +| version | 是 | number | 协议主版本号 | +| msg_type | 是 | string | 消息类别,目前仅 `"telemetry"` | +| mode | 否 | string | 工作模式,`"descriptive"`(默认)或 `"strict"` | +| dev_id | 是 | string | 设备唯一标识(UUID v4) | +| dev_type | 否 | string / object | 设备型号标识 | +| timestamp | 是 | Timestamp | 消息生成时刻 | +| fields | 是 | Field[] | 采集字段数组 | +| metadata | 否 | object | 消息级元数据 | ## 消息字段 ### Version (`version`) -... +必填。正整数,仅表示协议主版本号。当前版本为 **1**。 ### 消息类型 (`msg_type`) +必填。字符串。目前仅允许 `"telemetry"`,用于上传遥测数据。 + ### 消息模式 (`mode`) -仅 descriptive 有良好定义 -... (TODO) +可选,字符串,默认 `"descriptive"`。 + +* `descriptive` —— 自描述模式,允许在字段 `metadata` 中携带全部上下文信息,消息本身即可被独立解析。 +* `strict` —— 严格模式,不允许出现字段级 `metadata`,字段定义需通过外部文档约定,适用于带宽受限或固定 Schema 场景。(目前无实际意义, 需要与二进制协议配合) ### 设备 ID (`dev_id`) -UUID +必填。设备唯一标识,采用 UUID v4 格式(如 `148413b4-c352-49a9-9c48-9d15276a99e7`),在设备生命周期内保持不变。 ### 设备类型 (`dev_type`) -... +```typescript +type DeviceType = string | { type_id: string } | { type_name: string } +``` + +可选。用于标识设备类别或型号。支持三种写法: + +1. **字符串**:例如 `"device_type_name"`;与 `{ "type_name": string }` 等价 +2. **对象** `{ "type_id": UUID }`;UUID 为设备型号的唯一标识, 由平台分配 +3. **对象** `{ "type_name": string }`。 + +以上三种写法在语义上等价,由数据生产方按需选择。 ### 消息时间戳 (`timestamp`) ```typescript type Timestamp = - | number // UNIX seconds (default), same as `unix_s` + | number // UNIX seconds (default), same as `{ unix_s: number }` | { unix_s: number } | { unix_ms: number } | { unix_us: number } @@ -41,53 +66,100 @@ type Timestamp = ### field 字段 (`fields`) -see ... +必填。`Field` 对象数组,数组中每个元素代表一个测量项,其 `id` 在同一条消息内必须唯一。关于 `Field` 对象的定义详见后文 "field 字段" 章节。 ### 消息元数据 (`metadata`) +```typescript +interface MessageMetadata { + enums?: EnumDefinition[]; + location?: Location; + battery?: number; + seq?: number; + user?: Record; +} +``` + #### 全局枚举 (`enums`) -See data type and enum section +可选。全局枚举定义表,用于在字段 `metadata.data_type` 中以 `enum:` 形式引用。 + +```typescript +type EnumDefinition = { id: string; value: Record }; +``` + +示例: + +```jsonc +"metadata": { + "enums": [ + { + "id": "air_quality", + "value": { "good": 1, "moderate": 2, "bad": 3 } + } + ] +} +``` #### 位置信息 (`location`) -... +可选。表示消息采集时的设备地理位置: + +```jsonc +{ + "coordinates": [24.123456, 120.123456], // 纬度、经度(度) + "coordinate_system": "wgs84", // 可选,指定坐标系, 默认 "gcj02" 可选项为 wgs84, gcj02, bd09 + "accuracy": 10, // 可选,水平精度(米) + "altitude": 100 // 可选,海拔高度(米) +} +``` #### 电量 (`battery`) -... +可选。数字,范围 0–100,对应设备剩余电量百分比。 #### 序列号 (`seq`) -... +可选。无符号整数。每上传一条消息递增 1,用于检测丢包或乱序。达到上限后循环计数(默认为 32 位无符号整数回绕)。 #### 用户自定义信息 (`user`) -... +可选。任意键值对,为数据生产方保留的自定义信息区: ## field 字段 ### 标识符 (`id`) -... (TODO, just like symbol name convention in common programming languages) +必填。ASCII 字符串,仅允许字母、数字与下划线,且不得以数字开头。长度建议 1–64 字符。例如 `temperature_sensor`。 ### 值 (`value`) -符合 `DataType` 的值 (见 `data_type` 章节) +字段实际测量值,其类型必须与 `metadata.data_type` 完全对应,形态分三类: + +1. **标量** — `string | number | boolean | enum` + ```jsonc + "value": 42 + ``` +2. **等间隔数组 (array)** — `T[]`,通过 `sample_interval` 指定采样间隔 + ```jsonc + "value": [1, 2, 3], + "metadata": { "data_type": "array", "sample_interval": 0.5 } + ``` +3. **非等间隔(irregular)** — `{ v: T[], t: Timestamp[] }` + ```jsonc + "value": { "v": [1,2,3], "t": [1715145600,1715145601,1715145603] }, + "metadata": { "data_type": "irregular" } + ``` + `t` 数组必须与 `v` 数组长度相同, 且 `t` 数组中的时间戳必须严格递增, 否则会被视为非法数据 + +`null` 与 `undefined` 不被支持,嵌套数组(如 `array>`)亦非法。 ### 元数据 (`metadata`) -#### 数据类型 +#### 数据类型 (`data_type`) ##### 枚举类型 (`enums`) -```typescript -type EnumDefinition = { - id: string, - value: Record -} -``` - ```haskell newtype Enum = number ``` @@ -135,36 +207,138 @@ type DataType = #### 标签 (`label`) -UTF-8 字符串 +可选。字段展示名,UTF-8 字符串,便于人机界面直观呈现。 + +示例:`"label": "温度"` #### 错误码 (`error_code`) -... +可选。非零整数。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 | 厂商自定义 | — | 厂商或项目私有错误码区间 | #### 错误信息 (`error_msg`) -... +可选。UTF-8 字符串,对 `error_code` 进行人类可读的补充说明。 #### 置信度 (`confidence`) -... +```typescript +type Confidence = number | number[] +``` + +数组形式 (`number[]`) 仅对数组 (等间隔/非等间隔) 有意义, 表示每个采样点的置信度, 其长度应与采样点数量相同; 若不指定, 则默认为 1 #### 采样间隔 (`sample_interval`) -仅对 `ArrayType` 有意义, 建议每个 `ArrayType` 都显式填写该字段, 若不指定, 则默认为 1s +可选。采样间隔, 用于描述 `value` 为等间隔数组时, 数组中每个元素的时间间隔 (建议所有等间隔数组都显式填写该字段) ```typescript -type SampleIntervalObject = { ms?: number, s?: number, m?: number, h?: number, d?: number, w?: number } -type SampleInterval = +type TimeDelta = { ms?: number, s?: number, m?: number, h?: number, d?: number, w?: number } +type SampleInterval = | number // seconds - | SampleIntervalObject + | TimeDelta ``` -- 若使用 `SampleIntervalObject`, 则总采样间隔为 +- 若使用 `TimeDelta`, 则总采样间隔为 `ms + s * 1000 + m * 60 * 1000 + h * 60 * 60 * 1000 + d * 24 * 60 * 60 * 1000 + w * 7 * 24 * 60 * 60 * 1000`ms (各个 field 不允许小数) - 若使用 `number`, 则表示采样间隔为 `number` 秒 (允许小数) -- 若不指定, 则默认为 1s + +若不指定, 则默认为 1s + +示例:`"sample_interval": 0.5`(两次采样间隔 0.5 秒) #### 单位 (`unit`) -建议使用 [UCUM](https://ucum.org/ucum) +可选。测量值单位。推荐使用 [UCUM](https://ucum.org/ucum) 码(如 `"Cel"` 代表摄氏度),也支持自由字符串。 + +示例:`"unit": "Cel"` + +#### 内联枚举 (`enum`) + +可选。当 `data_type` 为 `enum:this` 及其衍生类型时, 用于定义内联枚举值 + +```typescript +type InlineEnum = Record +``` + +#### 用户自定义信息 (`user`) + +可选。任意键值对,为数据生产方保留的自定义信息区: + +```typescript +type UserData = Record +``` + +## 伪代码类型定义 + +以下以 TypeScript 伪代码展示核心结构,帮助快速对接实现: + +```typescript +// 时间戳 +type Timestamp = + | number // UNIX seconds + | { unix_s: number } + | { unix_ms: number } + | { unix_us: number } + | { unix_ns: number } + | { iso8601: string }; + +// 采样间隔 +type SampleInterval = number | { ms?: number; s?: number; m?: number; h?: number; d?: number; w?: number }; + +// 枚举定义 +type EnumDefinition = { id: string; value: Record }; + +// Field 元数据 +interface FieldMetadata { + data_type: string; + timestamp?: Timestamp; + sample_interval?: SampleInterval; + unit?: string; + label?: string; + confidence?: number | number[]; + error_code?: number; + error_msg?: string; + enum?: Record; // inline enum when data_type === "enum:this" + user?: Record; +} + +// Field +interface Field { + id: string; + value: DataType; + metadata: FieldMetadata; +} + +// 根消息 +interface Message { + version: 1; + msg_type: "telemetry"; + mode?: "descriptive" | "strict"; // 仅支持 descriptive 模式 + dev_id: string; + dev_type?: string | { type_id: string } | { type_name: string }; + timestamp: Timestamp; + fields: Field[]; + metadata?: MessageMetadata; +} +``` diff --git a/example.jsonc b/example.jsonc index 9f1c7c8..cd6a8da 100644 --- a/example.jsonc +++ b/example.jsonc @@ -171,7 +171,7 @@ // defaults to 0 for non-error cases // // well-defined error codes are in separate documentation - "error_code": 1001, + "error_code": 64, // optional // a custom error message "error_msg": "sensor disconnected",