docs: 升级设计方案迭代 — Agent 协作节点 + CTSDB 双实现 + 业务链路图
- 04-规则引擎:新增第十二节 Agent 协作节点设计(ACP 协议调用内部数字员工) - 04-规则引擎:Provider 清单新增 agent_request / agent_judge 预留标注 - 07-数据存储:重写为基于 aiot-iot-data-storage 已有 CTSDB/TDengine 双实现演进 - 01-整体架构:新增第五节端到端业务链路图(设备→网关→总线→规则引擎→动作) - 全文档统一:TDengine 硬引用改为"时序库(CTSDB/TDengine)" - 00-总览:融合策略表新增 Agent 协作行,不引入表新增 TB AI Node 直连 LLM - 00-总览:修正设计原则表残留的 EntityRelation 引用 - 新增 assets 目录(drawio 图表资源) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@@ -20,49 +20,50 @@
|
||||
|
||||
## 二、设计原则
|
||||
|
||||
| 原则 | 说明 |
|
||||
|------|------|
|
||||
| **南向屏蔽,北向统一** | 协议层允许混乱,业务层必须干净(继承现有铁律) |
|
||||
| **JetLinks 为主骨架** | 规则引擎、条件引擎、SPI 扩展采用 JetLinks 模式 |
|
||||
| **ThingsBoard 补关键设计** | 属性三元分类、RPC 状态机、EntityRelation、告警状态机 |
|
||||
| **保留现有消息总线** | Local/Redis/RocketMQ 三实现不动,消费端接入新引擎 |
|
||||
| **渐进式迁移** | 新旧规则可并行运行,不要求一次性切换 |
|
||||
| 原则 | 说明 |
|
||||
| --------------------- | ----------------------------------- |
|
||||
| **南向屏蔽,北向统一** | 协议层允许混乱,业务层必须干净(继承现有铁律) |
|
||||
| **JetLinks 为主骨架** | 规则引擎、条件引擎、SPI 扩展采用 JetLinks 模式 |
|
||||
| **ThingsBoard 补关键设计** | 属性三元分类、RPC 状态机、告警状态机 |
|
||||
| **保留现有消息总线** | Local/Redis/RocketMQ 三实现不动,消费端接入新引擎 |
|
||||
| **渐进式迁移** | 新旧规则可并行运行,不要求一次性切换 |
|
||||
|
||||
---
|
||||
|
||||
## 三、文档索引
|
||||
|
||||
| 编号 | 文档 | 内容 |
|
||||
|------|------|------|
|
||||
| 01 | [[01-整体架构设计]] | 模块划分、分层架构、依赖关系、部署拓扑 |
|
||||
| 02 | [[02-子系统与设备归属模型]] | 租户→项目→子系统→设备层级、设备归属管理 |
|
||||
| 03 | [[03-物模型规范v2]] | 属性三元分类、派生物模型、数据类型增强 |
|
||||
| 04 | [[04-规则引擎方案]] | DAG 编排、SPI Provider、脚本节点、抖动抑制 |
|
||||
| 05 | [[05-告警体系设计]] | 两级存储、状态机、传播机制、通知集成 |
|
||||
| 06 | [[06-设备影子与RPC]] | Shared 属性同步、持久化 RPC 状态机、离线指令队列 |
|
||||
| 07 | [[07-数据存储方案]] | 存储策略插件化、写入缓冲、Key 压缩、TDengine 优化 |
|
||||
| 08 | [[08-协议与编解码扩展]] | 协议包热加载、Codec SPI、透传编解码 |
|
||||
| 编号 | 文档 | 内容 |
|
||||
| --- | ----------------- | ------------------------------- |
|
||||
| 01 | [[01-整体架构设计]] | 模块划分、分层架构、依赖关系、部署拓扑 |
|
||||
| 02 | [[02-子系统与设备归属模型]] | 租户→项目→子系统→设备层级、设备归属管理 |
|
||||
| 03 | [[03-物模型规范v2]] | 属性三元分类、派生物模型、数据类型增强 |
|
||||
| 04 | [[04-规则引擎方案]] | DAG 编排、SPI Provider、脚本节点、抖动抑制 |
|
||||
| 05 | [[05-告警体系设计]] | 两级存储、状态机、传播机制、通知集成 |
|
||||
| 06 | [[06-设备影子与RPC]] | Shared 属性同步、持久化 RPC 状态机、离线指令队列 |
|
||||
| 07 | [[07-数据存储方案]] | 时序库双实现(CTSDB/TDengine)、写入缓冲、缓存优化 |
|
||||
| 08 | [[08-协议与编解码扩展]] | 协议包热加载、Codec SPI、透传编解码 |
|
||||
|
||||
---
|
||||
|
||||
## 四、融合策略速查表
|
||||
|
||||
| 设计点 | 取自 | 说明 |
|
||||
| ------ | -------------------------- | ------------------------------------------ |
|
||||
| 规则引擎框架 | JetLinks SceneRule DAG | 串行/并行/分支编排 |
|
||||
| 条件引擎 | JetLinks ReactorQL 思路 | 编译期过滤器,支持指标对比 |
|
||||
| 脚本节点 | TB TBEL 思路 | Aviator/QLExpress 实现,Action Provider 注册 |
|
||||
| 抖动抑制 | JetLinks ShakeLimit | 7 参数模型,参数化配置 |
|
||||
| 消息总线 | 保留现有 | Local/Redis/RocketMQ 三实现 |
|
||||
| 子系统模型 | 简化层级:租户→项目→子系统→设备 | 设备 FK 归属子系统,本次只做设备-子系统关系 |
|
||||
| 设备影子 | TB 属性三元分类 + RPC | Client/Server/Shared + 持久化状态机 |
|
||||
| 告警系统 | JetLinks 两级存储 + TB 状态机 | Record + History + Create→Update→Clear→ACK |
|
||||
| 物模型继承 | JetLinks 派生物模型 | 设备级覆盖产品定义 |
|
||||
| 存储策略 | JetLinks 策略模式 | 产品级配置存储后端 |
|
||||
| 协议扩展 | JetLinks 协议热加载 | JAR + ClassLoader 隔离 |
|
||||
| SPI 扩展 | JetLinks Provider + TB 注解 | 动态注册 + 元数据自描述 |
|
||||
| 可观测性 | JetLinks + TB Micrometer | 连接数/吞吐量/规则耗时 |
|
||||
| 写入缓冲 | JetLinks PersistenceBuffer | 内存+文件双层批量写入 |
|
||||
| 设计点 | 取自 | 说明 |
|
||||
| -------- | -------------------------- | ------------------------------------------ |
|
||||
| 规则引擎框架 | JetLinks SceneRule DAG | 串行/并行/分支编排 |
|
||||
| 条件引擎 | JetLinks ReactorQL 思路 | 编译期过滤器,支持指标对比 |
|
||||
| 脚本节点 | TB TBEL 思路 | Aviator/QLExpress 实现,Action Provider 注册 |
|
||||
| 抖动抑制 | JetLinks ShakeLimit | 7 参数模型,参数化配置 |
|
||||
| 消息总线 | 保留现有 | Local/Redis/RocketMQ 三实现 |
|
||||
| 子系统模型 | 简化层级:租户→项目→子系统→设备 | 设备 FK 归属子系统,本次只做设备-子系统关系 |
|
||||
| 设备影子 | TB 属性三元分类 + RPC | Client/Server/Shared + 持久化状态机 |
|
||||
| 告警系统 | JetLinks 两级存储 + TB 状态机 | Record + History + Create→Update→Clear→ACK |
|
||||
| 物模型继承 | JetLinks 派生物模型 | 设备级覆盖产品定义 |
|
||||
| 存储策略 | 已有双实现 + JetLinks 策略模式 | CTSDB/TDengine 可切换,DAO 接口统一 |
|
||||
| 协议扩展 | JetLinks 协议热加载 | JAR + ClassLoader 隔离 |
|
||||
| SPI 扩展 | JetLinks Provider + TB 注解 | 动态注册 + 元数据自描述 |
|
||||
| 可观测性 | JetLinks + TB Micrometer | 连接数/吞吐量/规则耗时 |
|
||||
| 写入缓冲 | JetLinks PersistenceBuffer | 内存+文件双层批量写入 |
|
||||
| Agent 协作 | TB 4.2 AI Node 思路 + ACP 协议 | IoT 内先实现 Agent 调用,后续抽取为平台 ai 模块 |
|
||||
|
||||
---
|
||||
|
||||
@@ -75,6 +76,7 @@
|
||||
| Edge 同步 | TB | 无边缘计算需求 |
|
||||
| 完整 87 节点 | TB | 20-25 个覆盖核心场景 |
|
||||
| 全栈响应式 | JetLinks | 现有 Spring MVC 生态稳定,不做全量迁移 |
|
||||
| TB AI Node 直连 LLM | TB 4.2 | 不直接对接模型,改为 ACP 协议调用内部 Agent 服务 |
|
||||
| ReactorQL 原版 | JetLinks | 依赖 Project Reactor,改用表达式引擎替代核心思路 |
|
||||
| EntityRelation 图关系 | TB | 过度设计,改用简洁的 subsystem_id FK 归属 |
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
│ Codec SPI(Alink/JT808/Camera3D11/...) │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ 存储层(Storage) │
|
||||
│ MySQL(业务实体) │ TDengine(时序数据) │ Redis(缓存) │
|
||||
│ MySQL(业务实体) │ 时序库 CTSDB/TDengine │ Redis(缓存) │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
@@ -81,7 +81,7 @@ viewsh-module-iot/
|
||||
│ │ └── statistics/ # 统计(增强)
|
||||
│ ├── dal/ # 数据访问
|
||||
│ │ ├── mysql/ # 关系库
|
||||
│ │ ├── tdengine/ # 时序库(策略模式增强)
|
||||
│ │ ├── tsdb/ # 时序库(CTSDB/TDengine 双实现)
|
||||
│ │ └── redis/ # 缓存
|
||||
│ └── framework/ # 框架层
|
||||
│ ├── observe/ # 【新增】可观测性(Micrometer)
|
||||
@@ -120,21 +120,226 @@ server 额外依赖:
|
||||
|------|------|------|
|
||||
| **IotProjectDO** | `iot_project` | 项目(架构预留,本次不开放 API) |
|
||||
| **IotSubsystemDO** | `iot_subsystem` | 子系统(设备归属单元) |
|
||||
| **IotAlarmHistoryDO** | TDengine `alarm_history` | 告警时序归档 |
|
||||
| **IotAlarmHistoryDO** | 时序库 `alarm_history` | 告警时序归档(CTSDB/TDengine) |
|
||||
| **IotDeviceRpcDO** | `iot_device_rpc` | 持久化 RPC 指令 |
|
||||
| **IotRuleChainDO** | `iot_rule_chain` | 规则链(DAG 图定义) |
|
||||
| **IotRuleNodeDO** | `iot_rule_node` | 规则节点 |
|
||||
|
||||
---
|
||||
|
||||
## 五、关键技术选型变更
|
||||
## 五、端到端业务链路
|
||||
|
||||
从设备上报到最终动作的完整链路,覆盖 v2.0 所有设计点:
|
||||
|
||||
![[iot-e2e-business-flow.png]]
|
||||
|
||||
```
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
设备接入层(Gateway)
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
物理设备 网关
|
||||
┌──────┐ MQTT/HTTP/TCP ┌─────────────────────────────────────────┐
|
||||
│ 传感器 │ ──── 原始报文 ────→ │ Codec SPI │
|
||||
│ 控制器 │ │ ├── ALink Codec(阿里协议) │
|
||||
│ 摄像头 │ │ ├── JT808 Codec(定位协议) │
|
||||
│ 工牌 │ │ └── Camera3D11 Codec(摄像头协议) │
|
||||
└──────┘ │ ↓ 解码 │
|
||||
│ IotDeviceMessage(统一消息模型) │
|
||||
│ {deviceKey, productKey, type, │
|
||||
│ identifier, data, timestamp} │
|
||||
└──────────────┬──────────────────────────┘
|
||||
│
|
||||
═══════════════════════════════════════════════╪═══════════════════════════════
|
||||
消息总线(Message Bus)│
|
||||
═══════════════════════════════════════════════╪═══════════════════════════════
|
||||
│
|
||||
┌───────────────────────────────┤
|
||||
│ 三种实现按部署模式选择 │
|
||||
│ ├── LocalMessageBus(单机) │
|
||||
│ ├── RedisStreamMessageBus │
|
||||
│ └── RocketMQMessageBus │
|
||||
└───────────────────────────────┤
|
||||
↓
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
核心服务层(Core Services)
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
IotDeviceMessageSubscriber(统一消息消费者)
|
||||
│
|
||||
├──→ ① 设备状态更新
|
||||
│ └── 在线/离线状态 → Redis 标记
|
||||
│ └── 上线时触发:
|
||||
│ ├── Shared 属性同步(pending → 下发) ← 06-设备影子
|
||||
│ └── Pending RPC 补发(限速 5 条/秒) ← 06-设备影子
|
||||
│
|
||||
├──→ ② 属性三元分类写入 ← 03-物模型v2
|
||||
│ ├── CLIENT 属性 → Redis iot:device_property:{id}:client
|
||||
│ ├── SERVER 属性 → Redis iot:device_property:{id}:server(规则引擎写)
|
||||
│ └── SHARED 属性 → Redis iot:device_property:{id}:shared + MySQL
|
||||
│
|
||||
├──→ ③ 时序数据持久化 ← 07-数据存储
|
||||
│ └── PersistenceBuffer(内存+文件双层)
|
||||
│ → 批量写入时序库(CTSDB 异步批量 / TDengine PersistenceBuffer)
|
||||
│
|
||||
├──→ ④ 派生物模型合并 ← 03-物模型v2
|
||||
│ └── 设备级覆盖产品定义(derive_metadata JSON)
|
||||
│
|
||||
└──→ ⑤ 规则引擎触发 ← 04-规则引擎
|
||||
│
|
||||
↓
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
规则引擎层(Rule Engine)
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
IotRuleEngineMessageHandler(统一入口)
|
||||
│
|
||||
│ 按 subsystemId + productId + deviceId 匹配规则链(全量缓存)
|
||||
│ 同时匹配 subsystemId=NULL 的全局规则链
|
||||
│
|
||||
↓ ← 链级 try-catch 隔离,单链异常不影响其他链(工程评审决议 #3)
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ RuleChain DAG 执行 │
|
||||
│ │
|
||||
│ ┌─────────┐ │
|
||||
│ │ Trigger │ 设备属性上报 / 设备事件 / 设备上下线 / 定时 / 手动 │
|
||||
│ └────┬────┘ │
|
||||
│ ↓ │
|
||||
│ ┌─────────┐ │
|
||||
│ │ Enrich │ 数据富化(可选) │
|
||||
│ │ │ ├── 读取设备最新属性(Redis) │
|
||||
│ │ │ ├── 读取属性历史(时序库) │
|
||||
│ │ │ └── 读取关联设备/资产信息 │
|
||||
│ │ │ 结果注入 → RuleContext.metadata │
|
||||
│ └────┬────┘ │
|
||||
│ ↓ │
|
||||
│ ┌──────────┐ │
|
||||
│ │ Condition │ 条件评估 │
|
||||
│ │ │ ├── Expression:Aviator 表达式 │
|
||||
│ │ │ │ temperature > 40 && humidity < 20 │
|
||||
│ │ │ ├── Script:脚本条件(复杂逻辑) │
|
||||
│ │ │ ├── TimeRange:时间范围过滤 │
|
||||
│ │ │ └── DeviceState:在线/离线判断 │
|
||||
│ └────┬────┘ │
|
||||
│ ↓ │
|
||||
│ ┌──────────────┐ │
|
||||
│ │ ShakeLimit │ 抖动抑制(可选) │
|
||||
│ │ │ 10 秒窗口内连续 3 次触发才真正执行 │
|
||||
│ │ │ ├── 固定窗口 / 滚动窗口 │
|
||||
│ │ │ ├── 取第一条 / 取最后一条 │
|
||||
│ │ │ └── 连续模式:不满足则重置计数 │
|
||||
│ └────┬────┘ │
|
||||
│ ↓ │
|
||||
│ ┌─────────────┐ │
|
||||
│ │ Branch │ 分支执行 │
|
||||
│ │ ├── 分支A │ executeAnyway=false → if/else-if 语义 │
|
||||
│ │ │ 条件组 │ executeAnyway=true → 重叠触发 │
|
||||
│ │ │ ↓ │ │
|
||||
│ │ │ Actions │ │
|
||||
│ │ ├── 分支B │ │
|
||||
│ │ │ ... │ │
|
||||
│ └──┴──┬──┴────┘ │
|
||||
│ ↓ │
|
||||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||||
│ │ Actions(动作层) │ │
|
||||
│ │ │ │
|
||||
│ │ ┌── 设备控制 ──────────────────────────────────────────┐ │ │
|
||||
│ │ │ device_property_set 设置设备属性(Shared 属性下发) │ │ │
|
||||
│ │ │ device_service_invoke 调用设备服务(持久化 RPC) │ │ │
|
||||
│ │ │ ↓ │ │ │
|
||||
│ │ │ RPC 状态机:QUEUED → SENT → SUCCESS │ │ │
|
||||
│ │ │ 设备离线 → 持久化等待 → 上线补发 │ │ │
|
||||
│ │ └───────────────────────────────────────────────────────┘ │ │
|
||||
│ │ │ │
|
||||
│ │ ┌── 告警处理 ──────────────────────────────────────────┐ │ │
|
||||
│ │ │ alarm_trigger 触发告警(ACTIVE 状态) │ │ │
|
||||
│ │ │ ├── upsert AlarmRecord(MySQL 幂等) │ │ │
|
||||
│ │ │ ├── append AlarmHistory(时序库) │ │ │
|
||||
│ │ │ └── 告警传播(沿子系统层级向上) │ │ │
|
||||
│ │ │ alarm_clear 清除告警(CLEARED 状态) │ │ │
|
||||
│ │ │ 竞态保护:Redis 缓存 + 分布式锁 │ │ │
|
||||
│ │ └───────────────────────────────────────────────────────┘ │ │
|
||||
│ │ │ │
|
||||
│ │ ┌── 数据转发 ──────────────────────────────────────────┐ │ │
|
||||
│ │ │ http_push HTTP 推送(Webhook) │ │ │
|
||||
│ │ │ mq_push MQ 推送(RocketMQ/Kafka/RabbitMQ) │ │ │
|
||||
│ │ │ redis_push Redis 推送 │ │ │
|
||||
│ │ │ tcp_push TCP 推送 │ │ │
|
||||
│ │ └───────────────────────────────────────────────────────┘ │ │
|
||||
│ │ │ │
|
||||
│ │ ┌── 通知 ──────────────────────────────────────────────┐ │ │
|
||||
│ │ │ notify 短信 / 邮件 / 站内信 / 企业微信 / 钉钉 │ │ │
|
||||
│ │ │ 模板变量:${alarm.name} ${device.name} ... │ │ │
|
||||
│ │ └───────────────────────────────────────────────────────┘ │ │
|
||||
│ │ │ │
|
||||
│ │ ┌── 数据处理 ──────────────────────────────────────────┐ │ │
|
||||
│ │ │ script Aviator 脚本(数据转换、单位换算、字段映射) │ │ │
|
||||
│ │ │ 沙箱:超时 3s + 循环上限 1000 + 黑名单 │ │ │
|
||||
│ │ │ enrich 数据富化(读取属性/历史/关联设备) │ │ │
|
||||
│ │ │ delay 延迟执行 │ │ │
|
||||
│ │ │ log 日志记录 │ │ │
|
||||
│ │ └───────────────────────────────────────────────────────┘ │ │
|
||||
│ │ │ │
|
||||
│ │ ┌── Agent 协作(预留)─────────────────────────────────┐ │ │
|
||||
│ │ │ agent_request ACP 协议调用内部 Agent 服务 │ │ │
|
||||
│ │ │ ├── 异常检测 Agent │ │ │
|
||||
│ │ │ ├── 保洁质检 Agent │ │ │
|
||||
│ │ │ ├── 能耗分析 Agent │ │ │
|
||||
│ │ │ └── 结果注入 metadata → 后续节点引用 │ │ │
|
||||
│ │ └───────────────────────────────────────────────────────┘ │ │
|
||||
│ └───────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
存储层(Storage)
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ MySQL │ 时序库(CTSDB/TDengine)│ Redis │
|
||||
│ ├── iot_subsystem │ ├── device_message │ ├── 属性缓存│
|
||||
│ ├── iot_device(+subsystem_id) │ ├── product_property │ │ (三元分类)│
|
||||
│ ├── iot_rule_chain/node/link │ ├── alarm_history │ ├── 设备在线│
|
||||
│ ├── iot_alarm_record │ ├── rule_debug_log │ ├── 告警缓存│
|
||||
│ ├── iot_device_rpc │ └── 通过 DAO 接口 │ ├── 规则缓存│
|
||||
│ ├── iot_agent_service │ 双实现可切换 │ └── RPC 超时│
|
||||
│ └── iot_project(预留) │ │ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 典型场景走读
|
||||
|
||||
**场景:温度传感器上报 → 异常检测 → 告警 → 通知**
|
||||
|
||||
```
|
||||
1. 传感器通过 MQTT 上报 {"temperature": 52, "humidity": 18}
|
||||
2. Gateway ALink Codec 解码 → IotDeviceMessage
|
||||
3. 消息总线投递 → IotDeviceMessageSubscriber 消费
|
||||
4. 属性写入 Redis(CLIENT scope)+ 时序库存储
|
||||
5. 规则引擎匹配:该设备属于"安防子系统",命中 2 条规则链
|
||||
6. 规则链 A(场景联动):
|
||||
├── Trigger: device_property(温度上报)
|
||||
├── Enrich: 读取近 1 小时均值温度 → metadata.avgTemp = 38
|
||||
├── Condition: temperature > 40 → true
|
||||
├── ShakeLimit: 30 秒内第 3 次触发 → 通过
|
||||
├── AgentRequest: anomaly-detector → {"level":"critical","reason":"偏离均值 14°C"}
|
||||
├── AlarmTrigger: 创建 CRITICAL 告警(upsert Record + append History)
|
||||
├── 告警传播: 安防子系统 → 1 楼 → 楼宇 A
|
||||
└── Notify: 短信通知安防负责人
|
||||
7. 规则链 B(数据转发):
|
||||
├── Trigger: device_property
|
||||
└── HttpPush: 推送到第三方监控平台
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、关键技术选型变更
|
||||
|
||||
| 领域 | v1.0(现有) | v2.0(目标) |
|
||||
|------|-------------|-------------|
|
||||
| 规则引擎条件 | SpEL 字符串表达式 | Aviator 表达式引擎(编译缓存+类型安全) |
|
||||
| 脚本执行 | 无 | Aviator/QLExpress(轻量+沙箱) |
|
||||
| 规则编排 | 平铺动作列表 | DAG(RuleModel,参考 JetLinks) |
|
||||
| 告警存储 | 单表 MySQL | MySQL(AlarmRecord 当前)+ TDengine(AlarmHistory 时序) |
|
||||
| 告警存储 | 单表 MySQL | MySQL(AlarmRecord 当前)+ 时序库(AlarmHistory,CTSDB/TDengine) |
|
||||
| 设备属性 | Redis Hash(无分类) | Redis Hash + AttributeScope 三元分类 |
|
||||
| RPC | 单次下发,丢失不重试 | 持久化 RPC + 状态机(QUEUED→SENT→DELIVERED→SUCCESS) |
|
||||
| 设备组织 | groupIds JSON 数组 | 子系统归属(subsystem_id FK)+ 分组保留并存 |
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
## 一、组织层级架构
|
||||
|
||||
![[diagram-subsystem-er.png]]
|
||||
|
||||
```
|
||||
租户(Tenant) ← 已有,TenantBaseDO
|
||||
└── 项目(Project) ← 架构预留,本次不实现
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
## 一、属性三元分类(借鉴 ThingsBoard)
|
||||
|
||||
![[diagram-attribute-classification.png]]
|
||||
|
||||
### 1.1 设计动机
|
||||
|
||||
现有系统所有属性不区分来源和可见性,设备上报的状态、平台计算的指标、下发给设备的配置混在一起。引入 **AttributeScope** 三元分类:
|
||||
@@ -40,7 +42,7 @@ iot:device_property:{deviceId}:server → {identifier: value}
|
||||
iot:device_property:{deviceId}:shared → {identifier: value}
|
||||
```
|
||||
|
||||
**TDengine 不变**:时序数据仍按 `product_property_{productId}` 超级表存储,scope 作为普通列(TINYINT)标记。
|
||||
**时序库不变**:时序数据仍按 `product_property_{productId}` 存储(CTSDB 为 measurement,TDengine 为超级表),scope 作为字段标记。
|
||||
|
||||
### 1.4 物模型属性定义扩展
|
||||
|
||||
@@ -108,9 +110,9 @@ List<ThingModelDO> getEffectiveThingModel(Long deviceId) {
|
||||
|
||||
### 3.1 新增 TIMESTAMP 类型
|
||||
|
||||
| 物模型类型 | TDengine 类型 | 说明 |
|
||||
|-----------|-------------|------|
|
||||
| `timestamp` | TIMESTAMP | 毫秒级时间戳(新增) |
|
||||
| 物模型类型 | CTSDB (InfluxDB) | TDengine | 说明 |
|
||||
|-----------|-----------------|----------|------|
|
||||
| `timestamp` | TIMESTAMP | TIMESTAMP | 毫秒级时间戳(新增) |
|
||||
|
||||
### 3.2 struct/array 支持嵌套校验
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
|
||||
## 二、核心架构
|
||||
|
||||
![[diagram-rule-action-tree.png]]
|
||||
|
||||
```
|
||||
设备消息(IotDeviceMessage)
|
||||
↓ 消息总线消费
|
||||
@@ -139,6 +141,7 @@ interface ConditionEvaluator {
|
||||
| `delay` | DelayAction | **新增** | 延迟执行 |
|
||||
| `enrich` | EnrichAction | **新增** | 数据富化(读取属性/时序) |
|
||||
| `log` | LogAction | **新增** | 日志记录 |
|
||||
| `agent_request` | AgentRequestAction | **预留** | 调用外部 Agent 服务(ACP 协议) |
|
||||
|
||||
**条件评估器(Condition):**
|
||||
|
||||
@@ -148,6 +151,7 @@ interface ConditionEvaluator {
|
||||
| `script` | ScriptCondition | 脚本条件(复杂逻辑) |
|
||||
| `time_range` | TimeRangeCondition | 时间范围(当日时间/日期区间) |
|
||||
| `device_state` | DeviceStateCondition | 设备在线/离线状态 |
|
||||
| `agent_judge` | AgentConditionEvaluator | **预留** 调用 Agent 做复杂条件判断 |
|
||||
|
||||
---
|
||||
|
||||
@@ -248,7 +252,7 @@ class ShakeLimitConfig {
|
||||
| 富化类型 | 配置 | 说明 |
|
||||
|---------|------|------|
|
||||
| `device_property` | deviceId + identifiers | 读取设备最新属性(从 Redis) |
|
||||
| `device_history` | deviceId + identifier + timeRange | 读取属性历史(从 TDengine) |
|
||||
| `device_history` | deviceId + identifier + timeRange | 读取属性历史(从时序库) |
|
||||
| `related_device` | relationType + identifiers | 读取关联设备属性 |
|
||||
| `asset_info` | assetId 或 relationType | 读取所属资产信息 |
|
||||
|
||||
@@ -306,7 +310,7 @@ class ShakeLimitConfig {
|
||||
|
||||
规则链 `debug_mode=true` 时:
|
||||
- 每个节点执行前后记录 `RuleContext` 快照
|
||||
- 写入 TDengine `rule_debug_log` 表
|
||||
- 写入时序库 `rule_debug_log` 表
|
||||
- 前端可回放执行路径,查看每步的输入/输出/耗时
|
||||
- 生产环境建议关闭(性能开销)
|
||||
|
||||
@@ -372,3 +376,319 @@ Map<String, List<Long>> ruleChainIndex; // key → ruleChainIds
|
||||
```
|
||||
|
||||
不使用延迟加载(@Cacheable),避免首次消息处理的延迟和 TTL 窗口期数据不一致。
|
||||
|
||||
---
|
||||
|
||||
## 十二、Agent 协作节点
|
||||
|
||||
> 参考:ThingsBoard 4.2 AI Request Node 思路 + ACP 智能体通信协议
|
||||
> 本阶段在 IoT 模块内实现 Agent 调用能力,后续成熟后再抽取为平台级 `viewsh-module-ai` 模块。
|
||||
|
||||
### 12.1 设计背景
|
||||
|
||||
TB 4.2 新增了 AI Request 规则节点,直接对接 LLM API(OpenAI/Gemini 等)。我们的场景不同:不直接对接模型,而是通过 **ACP(Agent Communication Protocol)协议** 调用内部已有的 Agent 服务(数字员工),由 Agent 内部决定调用哪个模型、如何处理。
|
||||
|
||||
```
|
||||
TB 方式: 规则节点 ──→ LLM API(OpenAI/Gemini)
|
||||
我们的方式:规则节点 ──→ AgentCallService ──→ AcpClient ──→ Agent 服务(数字员工)
|
||||
└── 全部在 IoT 模块内,后续抽取为独立 ai 模块
|
||||
```
|
||||
|
||||
### 12.2 模块内部结构
|
||||
|
||||
Agent 调用相关代码放在 `viewsh-module-iot-biz` 的 `agent` 包下,与规则引擎同模块部署:
|
||||
|
||||
```
|
||||
viewsh-module-iot-biz/
|
||||
└── src/main/java/.../iot/
|
||||
├── rule/ ← 规则引擎(已有)
|
||||
│ └── action/
|
||||
│ └── AgentRequestAction.java ← 规则节点
|
||||
└── agent/ ← Agent 调用(新增)
|
||||
├── dal/
|
||||
│ ├── IotAgentServiceDO.java ← 实体
|
||||
│ └── IotAgentServiceMapper.java
|
||||
├── service/
|
||||
│ ├── IotAgentServiceService.java ← Agent 注册表 CRUD
|
||||
│ └── IotAgentCallService.java ← 调用编排(组装请求 + 调 ACP + 解析响应)
|
||||
├── client/
|
||||
│ ├── AcpClient.java ← ACP 协议客户端接口
|
||||
│ └── AcpHttpClient.java ← HTTPS 实现(先做同步,后续加 WSS/SSE)
|
||||
└── controller/
|
||||
└── IotAgentServiceController.java ← 管理后台 CRUD
|
||||
```
|
||||
|
||||
**后续抽取路径**:当 Ops 等其他模块也需要调 Agent 时,将 `agent/` 包整体迁移到 `viewsh-module-ai`,IoT 侧改为 Feign 调用。接口不变,只是调用方式从本地变远程。
|
||||
|
||||
### 12.3 调用架构
|
||||
|
||||
```
|
||||
┌───────────────────────────────────────────────────────┐
|
||||
│ viewsh-module-iot-biz │
|
||||
│ │
|
||||
│ 规则引擎 Agent 调用 │
|
||||
│ ┌─────────────────────┐ ┌──────────────────┐ │
|
||||
│ │ AgentRequestAction │──调用──→│ IotAgentCallService│ │
|
||||
│ │ (ActionProvider) │ │ ├── 查询 Agent 配置│ │
|
||||
│ └─────────────────────┘ │ ├── 组装 ACP 请求 │ │
|
||||
│ │ └── 解析响应 │ │
|
||||
│ └────────┬─────────┘ │
|
||||
│ │ │
|
||||
│ ┌────────↓─────────┐ │
|
||||
│ │ AcpClient │ │
|
||||
│ │ HTTPS 同步调用 │ │
|
||||
│ └────────┬─────────┘ │
|
||||
│ │ │
|
||||
└──────────────────────────────────────────┼─────────────┘
|
||||
│ ACP 协议
|
||||
┌─────────↓──────────┐
|
||||
│ 外部 Agent 服务 │
|
||||
│ ├── 异常检测 Agent │
|
||||
│ ├── 保洁质检 Agent │
|
||||
│ ├── 能耗分析 Agent │
|
||||
│ └── 工单派发 Agent │
|
||||
└────────────────────┘
|
||||
```
|
||||
|
||||
### 12.4 数据模型
|
||||
|
||||
```sql
|
||||
CREATE TABLE iot_agent_service (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
name VARCHAR(128) NOT NULL COMMENT 'Agent 服务名称',
|
||||
code VARCHAR(64) NOT NULL COMMENT 'Agent 编码(业务标识)',
|
||||
description TEXT COMMENT 'Agent 能力描述',
|
||||
-- ACP 连接
|
||||
acp_endpoint VARCHAR(512) NOT NULL COMMENT 'ACP 接入点地址',
|
||||
agent_aid VARCHAR(256) NOT NULL COMMENT '目标 Agent AID(身份标识)',
|
||||
auth_mode VARCHAR(32) DEFAULT 'free' COMMENT '认证方式(free/token/pki)',
|
||||
auth_config JSON COMMENT '认证配置',
|
||||
-- 能力声明
|
||||
input_format JSON COMMENT '输入格式说明(JSON Schema,可选)',
|
||||
output_format JSON COMMENT '输出格式说明(JSON Schema,可选)',
|
||||
capabilities JSON COMMENT '能力标签 ["anomaly_detection","time_series"]',
|
||||
-- 调用约束
|
||||
timeout_seconds INT DEFAULT 60 COMMENT '默认超时(秒)',
|
||||
max_concurrency INT DEFAULT 10 COMMENT '最大并发调用数',
|
||||
-- 通用字段
|
||||
status TINYINT DEFAULT 1,
|
||||
tenant_id BIGINT NOT NULL,
|
||||
creator VARCHAR(64),
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updater VARCHAR(64),
|
||||
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
deleted BIT DEFAULT 0,
|
||||
UNIQUE KEY uk_code_tenant (code, tenant_id, deleted)
|
||||
) COMMENT 'Agent 服务注册表(后续迁移至 ai 模块)';
|
||||
```
|
||||
|
||||
### 12.5 核心实现
|
||||
|
||||
#### AcpClient(ACP 协议客户端)
|
||||
|
||||
```java
|
||||
/**
|
||||
* ACP 协议客户端 —— 封装与 Agent 的通信
|
||||
* 本阶段只实现 HTTPS 同步模式,后续按需扩展 WSS/SSE
|
||||
*/
|
||||
public interface AcpClient {
|
||||
/**
|
||||
* 同步调用:建 Session → 发消息 → 等响应 → 关 Session
|
||||
*/
|
||||
AcpResponse call(AcpRequest request);
|
||||
}
|
||||
|
||||
@Data @Builder
|
||||
class AcpRequest {
|
||||
private String endpoint; // ACP 接入点地址
|
||||
private String targetAid; // 目标 Agent AID
|
||||
private String authMode;
|
||||
private String authConfig;
|
||||
private JsonNode payload; // 业务数据
|
||||
private Map<String, String> metadata; // 附加上下文
|
||||
private int timeoutSeconds;
|
||||
}
|
||||
|
||||
@Data
|
||||
class AcpResponse {
|
||||
private boolean success;
|
||||
private String sessionId;
|
||||
private JsonNode data; // Agent 返回结果
|
||||
private String errorMessage;
|
||||
private long latencyMs;
|
||||
}
|
||||
```
|
||||
|
||||
#### IotAgentCallService(调用编排)
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class IotAgentCallService {
|
||||
|
||||
private final IotAgentServiceService agentServiceService;
|
||||
private final AcpClient acpClient;
|
||||
|
||||
/**
|
||||
* 调用 Agent 服务
|
||||
* @param agentCode Agent 编码
|
||||
* @param payload 业务数据
|
||||
* @param context 调用上下文(来源、设备信息等)
|
||||
* @param timeoutOverride 超时覆盖(null 则使用 Agent 默认超时)
|
||||
*/
|
||||
public AgentCallResult call(String agentCode, JsonNode payload,
|
||||
Map<String, String> context, Integer timeoutOverride) {
|
||||
// 1. 查询 Agent 配置
|
||||
IotAgentServiceDO agent = agentServiceService.getByCode(agentCode);
|
||||
if (agent == null || agent.getStatus() != 1) {
|
||||
return AgentCallResult.failure("Agent not found or disabled: " + agentCode);
|
||||
}
|
||||
|
||||
// 2. ACP 调用
|
||||
AcpRequest request = AcpRequest.builder()
|
||||
.endpoint(agent.getAcpEndpoint())
|
||||
.targetAid(agent.getAgentAid())
|
||||
.authMode(agent.getAuthMode())
|
||||
.authConfig(agent.getAuthConfig())
|
||||
.payload(payload)
|
||||
.metadata(context)
|
||||
.timeoutSeconds(timeoutOverride != null ? timeoutOverride : agent.getTimeoutSeconds())
|
||||
.build();
|
||||
|
||||
AcpResponse response = acpClient.call(request);
|
||||
|
||||
// 3. 封装结果
|
||||
return AgentCallResult.of(response);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### AgentRequestAction(规则引擎 SPI 节点)
|
||||
|
||||
```java
|
||||
@Component
|
||||
public class AgentRequestAction implements ActionProvider {
|
||||
|
||||
private final IotAgentCallService agentCallService;
|
||||
|
||||
@Override
|
||||
public String getType() { return "agent_request"; }
|
||||
|
||||
@Override
|
||||
public ActionResult execute(RuleContext ctx, JsonNode config) {
|
||||
// 1. 解析配置
|
||||
String agentCode = config.get("agentCode").asText();
|
||||
JsonNode payload = resolvePayload(config.get("payload"), ctx);
|
||||
Integer timeout = config.has("timeoutSeconds")
|
||||
? config.get("timeoutSeconds").asInt() : null;
|
||||
|
||||
// 2. 构建调用上下文
|
||||
Map<String, String> context = Map.of(
|
||||
"source", "iot-rule-engine",
|
||||
"deviceId", String.valueOf(ctx.getDeviceId()),
|
||||
"productId", String.valueOf(ctx.getProductId()),
|
||||
"subsystemId", String.valueOf(ctx.getSubsystemId()),
|
||||
"ruleChainId", String.valueOf(ctx.getRuleChainId())
|
||||
);
|
||||
|
||||
// 3. 调用
|
||||
AgentCallResult result = agentCallService.call(agentCode, payload, context, timeout);
|
||||
|
||||
// 4. 结果注入
|
||||
if (result.isSuccess()) {
|
||||
String outputTo = config.path("outputTo").asText("metadata.agentResult");
|
||||
injectResult(ctx, outputTo, result.getData());
|
||||
return ActionResult.success();
|
||||
}
|
||||
return ActionResult.failure(result.getErrorMessage());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 12.6 模板变量解析
|
||||
|
||||
规则引擎统一提供模板解析能力(Agent 节点与脚本节点共用),借鉴 TB 的 `TbNodeUtils.processPattern()`:
|
||||
|
||||
| 模板语法 | 含义 | 示例 |
|
||||
|---------|------|------|
|
||||
| `$[identifier]` | 消息数据字段 | `$[temperature]` → 设备上报的温度值 |
|
||||
| `$[*]` | 整个消息体 JSON | 传递全部上报数据 |
|
||||
| `${key}` | 元数据字段 | `${deviceName}` → 设备名称 |
|
||||
|
||||
### 12.7 节点配置格式
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "agent_request",
|
||||
"config": {
|
||||
"agentCode": "anomaly-detector",
|
||||
"payload": {
|
||||
"taskType": "anomaly_detection",
|
||||
"deviceName": "${deviceName}",
|
||||
"subsystem": "${subsystemCode}",
|
||||
"data": {
|
||||
"temperature": "$[temperature]",
|
||||
"humidity": "$[humidity]"
|
||||
},
|
||||
"context": {
|
||||
"recentAvgTemp": "${avgTemp}",
|
||||
"deviceModel": "${productName}"
|
||||
}
|
||||
},
|
||||
"timeoutSeconds": 60,
|
||||
"outputTo": "metadata.agentResult"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 12.8 Agent 结果在规则链中的使用
|
||||
|
||||
Agent 返回的结果注入 `metadata.agentResult` 后,后续节点可直接引用:
|
||||
|
||||
```
|
||||
设备上报 → [Enrich: 读取近1小时均值]
|
||||
→ [AgentRequest: anomaly-detector]
|
||||
→ [Expression: metadata.agentResult.level == 'critical']
|
||||
├── true → [AlarmTrigger: 严重告警]
|
||||
└── false → [Expression: metadata.agentResult.level == 'warning']
|
||||
├── true → [Notify: 发送预警通知]
|
||||
└── false → 结束
|
||||
```
|
||||
|
||||
### 12.9 典型业务场景
|
||||
|
||||
| 场景 | Agent | 触发方式 | Agent 输入 | Agent 输出 | 后续动作 |
|
||||
| ------ | ---------------------- | ------- | --------- | ----------------- | --------- |
|
||||
| 设备异常检测 | anomaly-detector | 属性上报 | 温湿度+历史均值 | level + reason | 告警/通知 |
|
||||
| 保洁质量验收 | clean-quality-checker | 保洁完成事件 | 区域图片+清洁标准 | pass/fail + score | 标记完成/派返工单 |
|
||||
| 能耗优化 | energy-advisor | 定时(每小时) | 用电数据+设备列表 | 调度建议 | 设备控制指令 |
|
||||
| 预测性维护 | predictive-maintenance | 定时(每天) | 30 天运行参数 | 剩余寿命+劣化趋势 | 提前告警 |
|
||||
|
||||
### 12.10 与 TB AI Node 的差异
|
||||
|
||||
| 维度 | TB 4.2 AI Node | 我们的 Agent 节点 |
|
||||
|------|---------------|-----------------|
|
||||
| 对接对象 | LLM API(OpenAI 等 9 个 Provider) | 内部 Agent 服务(数字员工) |
|
||||
| 协议 | 各厂商私有 HTTP API | ACP 统一协议 |
|
||||
| 抽象层 | LangChain4j SDK | AcpClient(IoT 内置) |
|
||||
| 管理对象 | AI Model(endpoint + apiKey) | Agent Service(ACP 地址 + 能力声明) |
|
||||
| Prompt 管理 | 节点配置中写 SystemPrompt/UserPrompt | 由 Agent 内部管理,规则引擎只传业务 Payload |
|
||||
|
||||
### 12.11 后续演进路径
|
||||
|
||||
```
|
||||
阶段 1(本次) 阶段 2(其他模块需要时)
|
||||
┌─────────────────────┐ ┌──────────────────────────────┐
|
||||
│ iot-biz/agent/ │ │ viewsh-module-ai(独立模块) │
|
||||
│ ├── dal/ │ 迁移 │ ├── ai-api/(Feign 接口) │
|
||||
│ ├── service/ │ ─────→ │ ├── ai-biz/(实现) │
|
||||
│ ├── client/ │ │ │ ├── Agent 注册表 │
|
||||
│ └── controller/ │ │ │ ├── AcpClient │
|
||||
│ │ │ │ └── 调用日志审计 │
|
||||
│ iot_agent_service 表│ 改前缀 │ └── ai_agent_service 表 │
|
||||
└─────────────────────┘ └──────────────────────────────┘
|
||||
↑ Feign 调用
|
||||
┌─────┼─────────┐
|
||||
IoT Ops 其他模块
|
||||
```
|
||||
|
||||
**迁移原则**:接口不变,只改调用方式。`IotAgentCallService` 的方法签名保持一致,IoT 侧改为通过 Feign 代理调用即可。
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
## 一、告警状态机(5 状态)
|
||||
|
||||
![[diagram-alarm-state-machine.png]]
|
||||
|
||||
```
|
||||
┌──── 持续触发 ────┐
|
||||
↓ │
|
||||
@@ -72,7 +74,7 @@ CREATE TABLE iot_alarm_record (
|
||||
|
||||
**幂等策略**(工程评审决议):保持 BIGINT 自增 ID(与全局一致),通过 `record_key = MD5(deviceId + "-" + alarmConfigId)` 唯一索引实现幂等。写入时使用 `INSERT ... ON DUPLICATE KEY UPDATE`。
|
||||
|
||||
### 2.2 AlarmHistory(TDengine,时序归档)
|
||||
### 2.2 AlarmHistory(时序库,CTSDB/TDengine)
|
||||
|
||||
每次告警状态变化都追加一条记录,用于趋势分析和审计。
|
||||
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
|
||||
---
|
||||
|
||||
![[diagram-rpc-state-machine.png]]
|
||||
|
||||
---
|
||||
|
||||
## 二、Shared 属性同步机制
|
||||
|
||||
### 2.1 流程
|
||||
|
||||
@@ -1,187 +1,199 @@
|
||||
# 07-数据存储方案
|
||||
|
||||
> 存储策略插件化 + 写入缓冲 + TDengine 优化
|
||||
> 基于 aiot-iot-data-storage 已有实现演进:时序数据库可切换(CTSDB/TDengine) + 写入缓冲 + 缓存优化
|
||||
|
||||
---
|
||||
|
||||
## 一、存储策略插件化(借鉴 JetLinks)
|
||||
## 一、时序数据库现状
|
||||
|
||||
### 1.1 设计动机
|
||||
![[diagram-tsdb-architecture.png]]
|
||||
|
||||
现有系统所有产品设备数据统一存 TDengine,无法针对不同场景选择最优存储。引入策略模式,支持产品级配置。
|
||||
![[diagram-write-buffer.png]]
|
||||
|
||||
### 1.2 策略接口
|
||||
### 1.1 已有双实现(aiot-iot-data-storage)
|
||||
|
||||
`aiot-iot-data-storage` 项目已实现 **CTSDB 和 TDengine 双后端**,通过配置切换:
|
||||
|
||||
```yaml
|
||||
viewsh.iot.tsdb.type: ctsdb # 或 "tdengine"
|
||||
```
|
||||
|
||||
```
|
||||
TsDbAutoConfiguration(策略选择器)
|
||||
├── tsdb.type = ctsdb → CTSDB DAO 实现(InfluxDB 协议)
|
||||
└── tsdb.type = tdengine → TDengine DAO 实现(JDBC)
|
||||
```
|
||||
|
||||
### 1.2 核心 DAO 接口(已定义)
|
||||
|
||||
```java
|
||||
interface ThingsDataStorageStrategy {
|
||||
String getId(); // "tdengine-column" / "tdengine-row"
|
||||
int getOrder(); // 优先级
|
||||
|
||||
void saveProperties(Long deviceId, Map<String, Object> properties, LocalDateTime reportTime);
|
||||
List<PropertyRecord> queryHistory(Long deviceId, String identifier, TimeRange range, int limit);
|
||||
Map<String, Object> getLatest(Long deviceId);
|
||||
void defineTable(Long productId, List<ThingModelDO> thingModels);
|
||||
void alterTable(Long productId, List<ThingModelDO> oldModels, List<ThingModelDO> newModels);
|
||||
// 设备属性时序存储
|
||||
interface IotTsDbDevicePropertyDao {
|
||||
List<TsDbTableField> getTableFields(Long productId);
|
||||
void createPropertyTable(Long productId, List<TsDbTableField> fields);
|
||||
void alterPropertyTable(Long productId, List<TsDbTableField> oldFields, List<TsDbTableField> newFields);
|
||||
void insert(IotDeviceDO device, Map<String, Object> properties, Long reportTime);
|
||||
List<Map<String, Object>> selectHistory(IotDevicePropertyHistoryReqVO reqVO, Long productId);
|
||||
}
|
||||
|
||||
// 设备消息日志存储
|
||||
interface IotTsDbDeviceMessageDao {
|
||||
void createSchema();
|
||||
boolean schemaExists();
|
||||
void insert(IotDeviceMessageDO message);
|
||||
PageResult<IotDeviceMessageDO> selectPage(PageParam page, IotDeviceMessagePageReqVO reqVO);
|
||||
Long selectCountByCreateTime(LocalDateTime createTime);
|
||||
List<IotDeviceMessageDO> selectListByRequestIdsAndReply(Long deviceId, List<String> requestIds, Boolean reply);
|
||||
List<HourlyMessageCountDTO> selectDeviceMessageCountGroupByDate(LocalDateTime startTime, LocalDateTime endTime);
|
||||
}
|
||||
```
|
||||
|
||||
### 1.3 内置策略
|
||||
### 1.3 CTSDB 实现(基于 InfluxDB 协议)
|
||||
|
||||
| 策略 ID | 说明 | 适用场景 |
|
||||
| ----------------- | --------------- | -------------- |
|
||||
| `tdengine-column` | 列模式(现有方案),每属性一列 | 属性固定、查询频繁 |
|
||||
| `tdengine-row` | 行模式,每属性一行 | 属性动态变化、物模型频繁变更 |
|
||||
| 维度 | 说明 |
|
||||
|------|------|
|
||||
| SDK | `influxdb-client-java` |
|
||||
| 协议 | HTTP REST API(`http://host:8086`) |
|
||||
| 认证 | Token 方式 |
|
||||
| 查询语言 | Flux |
|
||||
| 建表 | Schema-on-write(自动,无需预建) |
|
||||
| 写入 | 异步批量:1000 条/批 + 1s 自动刷新 + 3 次重试 |
|
||||
| 防注入 | `FluxQuerySanitizer` 转义特殊字符 |
|
||||
| 多租户 | Flux 查询中 `tenant_id` 字段过滤 |
|
||||
|
||||
### 1.4 列模式 vs 行模式
|
||||
|
||||
**列模式(现有,保留为默认)**:
|
||||
```sql
|
||||
CREATE STABLE product_property_{productId} (
|
||||
ts TIMESTAMP, report_time TIMESTAMP,
|
||||
temperature INT, humidity FLOAT, status TINYINT -- 每属性一列
|
||||
) TAGS (device_id BIGINT);
|
||||
```
|
||||
- 优点:查询快,单行包含所有属性
|
||||
- 缺点:物模型变更需 ALTER TABLE,TDengine 不支持缩短字段
|
||||
|
||||
**行模式(新增)**:
|
||||
```sql
|
||||
CREATE STABLE product_property_row_{productId} (
|
||||
ts TIMESTAMP, report_time TIMESTAMP,
|
||||
identifier NCHAR(64), -- 属性标识符
|
||||
value_int INT, -- 整型值
|
||||
value_float FLOAT, -- 浮点值
|
||||
value_double DOUBLE, -- 双精度值
|
||||
value_bool TINYINT, -- 布尔值
|
||||
value_str NCHAR(1024), -- 字符串值
|
||||
scope TINYINT -- 属性域(1=CLIENT, 2=SERVER, 3=SHARED)
|
||||
) TAGS (device_id BIGINT);
|
||||
```
|
||||
- 优点:物模型变更无需 DDL,属性可任意扩展
|
||||
- 缺点:查询需按 identifier 过滤,同时查多属性需 PIVOT
|
||||
|
||||
### 1.5 产品级配置
|
||||
|
||||
```sql
|
||||
ALTER TABLE iot_product ADD COLUMN store_policy VARCHAR(32) DEFAULT 'tdengine-column';
|
||||
```yaml
|
||||
# CTSDB 连接配置
|
||||
viewsh.iot.tsdb.ctsdb:
|
||||
url: http://${CTSDB_HOST:localhost}:${CTSDB_PORT:8086}
|
||||
token: ${CTSDB_TOKEN:}
|
||||
org: ${CTSDB_ORG:aiot}
|
||||
bucket: ${CTSDB_BUCKET:aiot_platform}
|
||||
```
|
||||
|
||||
`ThingsDataStorageRouter` 按 productId 路由到对应策略:
|
||||
```java
|
||||
ThingsDataStorageStrategy getStrategy(Long productId) {
|
||||
String policy = productService.getProduct(productId).getStorePolicy();
|
||||
return strategyMap.get(policy); // 策略注册表
|
||||
}
|
||||
### 1.4 TDengine 实现
|
||||
|
||||
| 维度 | 说明 |
|
||||
|------|------|
|
||||
| SDK | `taos-jdbcdriver` 3.7.9 |
|
||||
| 协议 | JDBC(WebSocket / REST) |
|
||||
| 认证 | 用户名密码 |
|
||||
| 查询语言 | TDengine SQL |
|
||||
| 建表 | 需要预建超级表(CREATE STABLE) |
|
||||
| 写入 | 同步写入(通过 Mapper XML) |
|
||||
| 多租户 | `@InterceptorIgnore` 绕过 JSqlParser |
|
||||
|
||||
### 1.5 数据模型(两套实现共用)
|
||||
|
||||
```
|
||||
measurement/超级表: device_message
|
||||
├── TAG: device_id
|
||||
└── FIELDS: id, report_time, tenant_id, server_id, upstream,
|
||||
reply, identifier, request_id, method, params, data, code, msg
|
||||
|
||||
measurement/超级表: product_property_{productId}
|
||||
├── TAG: device_id
|
||||
└── FIELDS: report_time + 动态属性列(从物模型生成)
|
||||
```
|
||||
|
||||
### 1.6 物模型类型映射
|
||||
|
||||
| 物模型类型 | CTSDB (InfluxDB) | TDengine |
|
||||
|---------|-----------------|----------|
|
||||
| INT | INT | INT |
|
||||
| FLOAT | FLOAT | FLOAT |
|
||||
| DOUBLE | DOUBLE | DOUBLE |
|
||||
| ENUM | TINYINT | TINYINT |
|
||||
| BOOL | BOOL | TINYINT |
|
||||
| TEXT | STRING | VARCHAR(1024) |
|
||||
| DATE | TIMESTAMP | TIMESTAMP |
|
||||
| STRUCT | STRING (JSON) | VARCHAR(1024) |
|
||||
| ARRAY | STRING (JSON) | VARCHAR(1024) |
|
||||
|
||||
---
|
||||
|
||||
## 二、写入缓冲(借鉴 JetLinks PersistenceBuffer)
|
||||
## 二、v2.0 存储扩展
|
||||
|
||||
### 2.1 设计动机
|
||||
### 2.1 新增时序表
|
||||
|
||||
设备高频上报时(如 1Hz × 1000 台),每条消息直接写 TDengine 会产生大量小批次 INSERT。引入写入缓冲,批量合并后写入。
|
||||
在现有 `device_message` 和 `product_property_*` 基础上,v2.0 需新增两张时序表,同样遵循 DAO 接口双实现模式:
|
||||
|
||||
### 2.2 PersistenceBuffer 设计
|
||||
| 时序表 | 用途 | TAG | 关键字段 |
|
||||
|--------|------|-----|---------|
|
||||
| `alarm_history` | 告警状态变化归档 | device_id | alarm_record_id, severity, state, trigger_data |
|
||||
| `rule_debug_log` | 规则调试日志 | device_id | rule_chain_id, node_id, input_data, output_data, duration_ms |
|
||||
|
||||
需新增对应 DAO 接口:
|
||||
|
||||
```java
|
||||
// 告警历史时序存储
|
||||
interface IotTsDbAlarmHistoryDao {
|
||||
void insert(AlarmHistoryDO history);
|
||||
List<AlarmHistoryDO> selectByRecordId(Long alarmRecordId, TimeRange range);
|
||||
List<AlarmHistoryDO> selectByDeviceId(Long deviceId, TimeRange range);
|
||||
}
|
||||
|
||||
// 规则调试日志时序存储
|
||||
interface IotTsDbRuleDebugLogDao {
|
||||
void insert(RuleDebugLogDO log);
|
||||
List<RuleDebugLogDO> selectByChainId(Long ruleChainId, TimeRange range);
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 写入缓冲
|
||||
|
||||
CTSDB 侧已有 InfluxDB WriteApi 的异步批量写入(1000 条/批 + 1s 刷新),TDengine 侧需补齐 PersistenceBuffer:
|
||||
|
||||
```java
|
||||
class PersistenceBuffer<T> {
|
||||
// 配置
|
||||
int bufferSize = 200; // 数量触发阈值
|
||||
Duration bufferTimeout = Duration.ofSeconds(3); // 时间触发阈值
|
||||
int parallelism = 4; // 并行写出线程数
|
||||
int bufferSize; // 数量触发阈值
|
||||
Duration bufferTimeout; // 时间触发阈值
|
||||
int parallelism; // 并行写出线程数
|
||||
BlockingQueue<T> queue; // 内存队列
|
||||
ScheduledExecutorService timer;// 定时刷新
|
||||
|
||||
// 内部结构
|
||||
BlockingQueue<T> queue; // 内存队列
|
||||
ScheduledExecutorService timer; // 定时刷新
|
||||
|
||||
// 写入方法
|
||||
void write(T item) {
|
||||
queue.add(item);
|
||||
if (queue.size() >= bufferSize) {
|
||||
flush();
|
||||
}
|
||||
if (queue.size() >= bufferSize) { flush(); }
|
||||
}
|
||||
|
||||
// 批量刷出
|
||||
void flush() {
|
||||
List<T> batch = new ArrayList<>();
|
||||
queue.drainTo(batch, bufferSize);
|
||||
if (!batch.isEmpty()) {
|
||||
batchWriter.accept(batch); // 回调:批量写入 TDengine
|
||||
}
|
||||
if (!batch.isEmpty()) { batchWriter.accept(batch); }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 应用点
|
||||
| 缓冲实例 | 写入目标 | bufferSize | timeout | 备注 |
|
||||
|---------|---------|-----------|---------|------|
|
||||
| 属性写入缓冲 | `product_property_*` | 200 | 3s | CTSDB 已内置批量,TDengine 需要 |
|
||||
| 消息日志缓冲 | `device_message` | 500 | 5s | 同上 |
|
||||
| 告警历史缓冲 | `alarm_history` | 100 | 3s | 新增 |
|
||||
| 规则调试日志缓冲 | `rule_debug_log` | 100 | 5s | 新增 |
|
||||
|
||||
| 缓冲实例 | 写入目标 | bufferSize | timeout |
|
||||
|---------|---------|-----------|---------|
|
||||
| 属性写入缓冲 | TDengine `product_property_*` | 200 | 3s |
|
||||
| 消息日志缓冲 | TDengine `device_message` | 500 | 5s |
|
||||
| 告警历史缓冲 | TDengine `alarm_history` | 100 | 3s |
|
||||
| 规则调试日志缓冲 | TDengine `rule_debug_log` | 100 | 5s |
|
||||
### 2.3 数据保留策略
|
||||
|
||||
| 时序表 | 建议保留期 | CTSDB 实现 | TDengine 实现 |
|
||||
|--------|----------|-----------|--------------|
|
||||
| `device_message` | 90 天 | Bucket Retention Policy | `KEEP 90d` |
|
||||
| `product_property_*` | 365 天 | Bucket Retention Policy | `KEEP 365d` |
|
||||
| `alarm_history` | 365 天 | Bucket Retention Policy | `KEEP 365d` |
|
||||
| `rule_debug_log` | 7 天 | Bucket Retention Policy | `KEEP 7d` |
|
||||
|
||||
### 2.4 属性作用域扩展
|
||||
|
||||
v2.0 引入属性三元分类(CLIENT/SERVER/SHARED),时序数据需新增 `scope` 维度:
|
||||
|
||||
- **CTSDB**:写入时增加 `scope` 字段(INT,作为 FIELD),查询时按 scope 过滤
|
||||
- **TDengine**:超级表增加 `scope TINYINT` 列
|
||||
|
||||
两套 DAO 实现的 `insert()` 方法均需接收 `AttributeScope` 参数。
|
||||
|
||||
---
|
||||
|
||||
## 三、TDengine 优化
|
||||
## 三、Redis 缓存优化
|
||||
|
||||
### 3.1 消息超级表优化
|
||||
|
||||
现有 `device_message` 表的 `params` 和 `data` 字段为 `NCHAR(2048)`,大消息会截断。
|
||||
|
||||
优化方案:
|
||||
```sql
|
||||
-- 增大到 4096,覆盖绝大多数场景
|
||||
ALTER STABLE device_message MODIFY COLUMN params NCHAR(4096);
|
||||
ALTER STABLE device_message MODIFY COLUMN data NCHAR(4096);
|
||||
```
|
||||
|
||||
### 3.2 新增超级表
|
||||
|
||||
```sql
|
||||
-- 告警历史(两级告警的时序部分)
|
||||
CREATE STABLE IF NOT EXISTS alarm_history (
|
||||
ts TIMESTAMP,
|
||||
alarm_record_id NCHAR(64),
|
||||
alarm_config_id BIGINT,
|
||||
severity TINYINT,
|
||||
state NCHAR(16),
|
||||
trigger_data NCHAR(4096),
|
||||
details NCHAR(2048),
|
||||
operator NCHAR(64),
|
||||
remark NCHAR(256)
|
||||
) TAGS (device_id BIGINT);
|
||||
|
||||
-- 规则调试日志
|
||||
CREATE STABLE IF NOT EXISTS rule_debug_log (
|
||||
ts TIMESTAMP,
|
||||
rule_chain_id BIGINT,
|
||||
node_id BIGINT,
|
||||
node_type NCHAR(64),
|
||||
input_data NCHAR(4096),
|
||||
output_data NCHAR(4096),
|
||||
duration_ms INT,
|
||||
success BOOL,
|
||||
error_msg NCHAR(512)
|
||||
) TAGS (device_id BIGINT);
|
||||
```
|
||||
|
||||
### 3.3 数据保留策略
|
||||
|
||||
| 超级表 | 建议保留期 | 配置方式 |
|
||||
|--------|----------|---------|
|
||||
| `device_message` | 90 天 | TDengine `KEEP 90d` |
|
||||
| `product_property_*` | 365 天 | TDengine `KEEP 365d` |
|
||||
| `alarm_history` | 365 天 | TDengine `KEEP 365d` |
|
||||
| `rule_debug_log` | 7 天 | TDengine `KEEP 7d` |
|
||||
|
||||
---
|
||||
|
||||
## 四、Redis 缓存优化
|
||||
|
||||
### 4.1 修复生产隐患
|
||||
### 3.1 修复生产隐患
|
||||
|
||||
| 问题 | 现状 | 修复 |
|
||||
|------|------|------|
|
||||
@@ -189,7 +201,7 @@ CREATE STABLE IF NOT EXISTS rule_debug_log (
|
||||
| 部分缓存无 TTL | `iot:device_property:*`、`iot:device_server_id` 常驻 | 设备删除时主动清理 |
|
||||
| 告警缓存竞态 | 无并发保护 | 分布式锁 + RecordCache |
|
||||
|
||||
### 4.2 新增 Redis Key
|
||||
### 3.2 新增 Redis Key
|
||||
|
||||
| Key | 类型 | 用途 | TTL |
|
||||
|-----|------|------|-----|
|
||||
@@ -201,9 +213,9 @@ CREATE STABLE IF NOT EXISTS rule_debug_log (
|
||||
|
||||
---
|
||||
|
||||
## 五、可观测性埋点(Micrometer)
|
||||
## 四、可观测性埋点(Micrometer)
|
||||
|
||||
### 5.1 核心指标
|
||||
### 4.1 核心指标
|
||||
|
||||
| 指标名 | 类型 | 标签 | 说明 |
|
||||
|--------|------|------|------|
|
||||
@@ -217,19 +229,17 @@ CREATE STABLE IF NOT EXISTS rule_debug_log (
|
||||
| `iot.buffer.size` | Gauge | bufferName | 缓冲区当前大小 |
|
||||
| `iot.rpc.status` | Counter | status | RPC 状态分布 |
|
||||
|
||||
### 5.2 集成方式
|
||||
### 4.2 集成方式
|
||||
|
||||
```java
|
||||
@Component
|
||||
class IotMetrics {
|
||||
private final MeterRegistry registry;
|
||||
|
||||
// 设备上线
|
||||
void recordDeviceOnline(String protocol) {
|
||||
Metrics.gauge("iot.device.online", Tags.of("protocol", protocol), onlineCount);
|
||||
}
|
||||
|
||||
// 规则执行耗时
|
||||
void recordRuleExecution(Long chainId, String nodeType, Duration duration) {
|
||||
Timer.builder("iot.rule.execution")
|
||||
.tag("ruleChainId", String.valueOf(chainId))
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
|
||||
---
|
||||
|
||||
![[diagram-protocol-hotload.png]]
|
||||
|
||||
---
|
||||
|
||||
## 二、Codec SPI 增强
|
||||
|
||||
### 2.1 现有接口保留
|
||||
|
||||
BIN
开发者文档/03-IoT领域/升级设计方案/assets/diagram-alarm-state-machine.png
Normal file
|
After Width: | Height: | Size: 147 KiB |
94
开发者文档/03-IoT领域/升级设计方案/assets/diagram-alarm-state-machine.svg
Normal file
@@ -0,0 +1,94 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 960 470" width="960" height="470">
|
||||
<style>
|
||||
text { font-family: "Helvetica Neue", Helvetica, Arial, "PingFang SC", "Microsoft YaHei", sans-serif; }
|
||||
</style>
|
||||
<defs>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#2563eb"/>
|
||||
</marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#16a34a"/>
|
||||
</marker>
|
||||
<marker id="arrow-orange" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#ea580c"/>
|
||||
</marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#9333ea"/>
|
||||
</marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#dc2626"/>
|
||||
</marker>
|
||||
<marker id="arrow-gray" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#6b7280"/>
|
||||
</marker>
|
||||
<marker id="arrow-cyan" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#0ea5e9"/>
|
||||
</marker>
|
||||
<filter id="shadow" x="-4%" y="-4%" width="108%" height="108%">
|
||||
<feDropShadow dx="0" dy="2" stdDeviation="3" flood-color="#000" flood-opacity="0.08"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect width="960" height="470" fill="#ffffff"/>
|
||||
<text x="480" y="36" text-anchor="middle" fill="#111827" font-size="18" font-weight="700">告警状态机 Alarm State Machine</text>
|
||||
<text x="480" y="56" text-anchor="middle" fill="#6b7280" font-size="11">05-告警体系设计 | 5 状态 + 竞态保护 + 层级传播</text>
|
||||
<circle cx="100" cy="160" r="12" fill="#111827"/>
|
||||
<text x="100" y="140" text-anchor="middle" fill="#6b7280" font-size="10">告警触发</text>
|
||||
<rect x="210" y="130" width="140" height="60" rx="30" fill="#fee2e2" stroke="#dc2626" stroke-width="2" filter="url(#shadow)"/>
|
||||
<text x="280" y="156" text-anchor="middle" fill="#dc2626" font-size="13" font-weight="700">ACTIVE</text>
|
||||
<text x="280" y="174" text-anchor="middle" fill="#dc2626" font-size="10">活跃</text>
|
||||
<rect x="450" y="70" width="140" height="60" rx="30" fill="#fed7aa" stroke="#ea580c" stroke-width="2" filter="url(#shadow)"/>
|
||||
<text x="520" y="96" text-anchor="middle" fill="#ea580c" font-size="13" font-weight="700">ACKNOWLEDGED</text>
|
||||
<text x="520" y="114" text-anchor="middle" fill="#ea580c" font-size="10">已确认</text>
|
||||
<rect x="450" y="210" width="140" height="60" rx="30" fill="#dcfce7" stroke="#16a34a" stroke-width="2" filter="url(#shadow)"/>
|
||||
<text x="520" y="236" text-anchor="middle" fill="#16a34a" font-size="13" font-weight="700">CLEARED</text>
|
||||
<text x="520" y="254" text-anchor="middle" fill="#16a34a" font-size="10">已清除</text>
|
||||
<rect x="690" y="130" width="140" height="60" rx="30" fill="#f3f4f6" stroke="#6b7280" stroke-width="2" filter="url(#shadow)"/>
|
||||
<text x="760" y="156" text-anchor="middle" fill="#6b7280" font-size="13" font-weight="700">RESOLVED</text>
|
||||
<text x="760" y="174" text-anchor="middle" fill="#6b7280" font-size="10">已解决</text>
|
||||
<circle cx="900" cy="160" r="14" fill="none" stroke="#111827" stroke-width="2"/>
|
||||
<circle cx="900" cy="160" r="9" fill="#111827"/>
|
||||
<text x="900" y="140" text-anchor="middle" fill="#6b7280" font-size="10">归档</text>
|
||||
<line x1="112" y1="160" x2="200" y2="160" stroke="#dc2626" stroke-width="2" marker-end="url(#arrow-red)"/>
|
||||
<rect x="135" y="148" width="60" height="16" rx="3" fill="#fff" fill-opacity="0.9"/>
|
||||
<text x="165" y="160" text-anchor="middle" fill="#dc2626" font-size="9">trigger</text>
|
||||
<path d="M 350,145 L 440,110" stroke="#ea580c" stroke-width="2" fill="none" marker-end="url(#arrow-orange)"/>
|
||||
<rect x="365" y="112" width="60" height="16" rx="3" fill="#fff" fill-opacity="0.9"/>
|
||||
<text x="395" y="124" text-anchor="middle" fill="#ea580c" font-size="9">用户确认</text>
|
||||
<path d="M 350,175 L 440,225" stroke="#16a34a" stroke-width="2" fill="none" marker-end="url(#arrow-green)"/>
|
||||
<rect x="362" y="192" width="65" height="16" rx="3" fill="#fff" fill-opacity="0.9"/>
|
||||
<text x="394" y="204" text-anchor="middle" fill="#16a34a" font-size="9">条件恢复</text>
|
||||
<line x1="520" y1="130" x2="520" y2="200" stroke="#16a34a" stroke-width="2" marker-end="url(#arrow-green)"/>
|
||||
<rect x="525" y="155" width="60" height="16" rx="3" fill="#fff" fill-opacity="0.9"/>
|
||||
<text x="555" y="167" text-anchor="middle" fill="#16a34a" font-size="9">条件恢复</text>
|
||||
<path d="M 590,100 L 680,145" stroke="#6b7280" stroke-width="2" fill="none" marker-end="url(#arrow-gray)"/>
|
||||
<rect x="610" y="108" width="55" height="16" rx="3" fill="#fff" fill-opacity="0.9"/>
|
||||
<text x="637" y="120" text-anchor="middle" fill="#6b7280" font-size="9">手动解决</text>
|
||||
<path d="M 590,230 L 680,175" stroke="#6b7280" stroke-width="2" fill="none" marker-end="url(#arrow-gray)"/>
|
||||
<rect x="610" y="198" width="55" height="16" rx="3" fill="#fff" fill-opacity="0.9"/>
|
||||
<text x="637" y="210" text-anchor="middle" fill="#6b7280" font-size="9">手动解决</text>
|
||||
<line x1="830" y1="160" x2="880" y2="160" stroke="#111827" stroke-width="2" marker-end="url(#arrow-gray)"/>
|
||||
<path d="M 280,130 C 280,80 340,80 340,130" stroke="#dc2626" stroke-width="1.5" fill="none" marker-end="url(#arrow-red)"/>
|
||||
<text x="310" y="82" text-anchor="middle" fill="#dc2626" font-size="9">重复触发</text>
|
||||
<text x="310" y="94" text-anchor="middle" fill="#dc2626" font-size="8">(计数+1)</text>
|
||||
<text x="60" y="320" fill="#111827" font-size="12" font-weight="600">告警级别</text>
|
||||
<rect x="60" y="335" width="100" height="28" rx="14" fill="#fee2e2" stroke="#dc2626" stroke-width="1.2"/>
|
||||
<text x="110" y="354" text-anchor="middle" fill="#dc2626" font-size="10" font-weight="600">CRITICAL</text>
|
||||
<rect x="170" y="335" width="100" height="28" rx="14" fill="#fed7aa" stroke="#ea580c" stroke-width="1.2"/>
|
||||
<text x="220" y="354" text-anchor="middle" fill="#ea580c" font-size="10" font-weight="600">MAJOR</text>
|
||||
<rect x="280" y="335" width="100" height="28" rx="14" fill="#fef9c3" stroke="#eab308" stroke-width="1.2"/>
|
||||
<text x="330" y="354" text-anchor="middle" fill="#eab308" font-size="10" font-weight="600">MINOR</text>
|
||||
<rect x="390" y="335" width="100" height="28" rx="14" fill="#dbeafe" stroke="#2563eb" stroke-width="1.2"/>
|
||||
<text x="440" y="354" text-anchor="middle" fill="#2563eb" font-size="10" font-weight="600">WARNING</text>
|
||||
<rect x="500" y="335" width="100" height="28" rx="14" fill="#f3f4f6" stroke="#6b7280" stroke-width="1.2"/>
|
||||
<text x="550" y="354" text-anchor="middle" fill="#6b7280" font-size="10" font-weight="600">INFO</text>
|
||||
<rect x="620" y="310" width="310" height="65" rx="8" fill="#f3f4f6" stroke="#d1d5db" stroke-width="1"/>
|
||||
<text x="775" y="330" text-anchor="middle" fill="#111827" font-size="11" font-weight="600">竞态保护机制</text>
|
||||
<text x="775" y="348" text-anchor="middle" fill="#6b7280" font-size="10">Redis 缓存 + 分布式锁 (Redisson)</text>
|
||||
<text x="775" y="364" text-anchor="middle" fill="#6b7280" font-size="10">AlarmRecord record_key UK 幂等</text>
|
||||
<rect x="60" y="390" width="400" height="55" rx="8" fill="#dbeafe" stroke="#4479A1" stroke-width="1.2"/>
|
||||
<text x="260" y="412" text-anchor="middle" fill="#4479A1" font-size="11" font-weight="600">MySQL AlarmRecord (当前状态 Upsert)</text>
|
||||
<text x="260" y="430" text-anchor="middle" fill="#6b7280" font-size="10">record_key = MD5(deviceId + alarmConfigId)</text>
|
||||
<rect x="500" y="390" width="430" height="55" rx="8" fill="#ede9fe" stroke="#9333ea" stroke-width="1.2"/>
|
||||
<text x="715" y="412" text-anchor="middle" fill="#9333ea" font-size="11" font-weight="600">TimeSeries AlarmHistory (审计轨迹 Append)</text>
|
||||
<text x="715" y="430" text-anchor="middle" fill="#6b7280" font-size="10">每次状态变更追加记录 (ts, state, trigger_data)</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 112 KiB |
@@ -0,0 +1,73 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 960 500" width="960" height="500">
|
||||
<style>
|
||||
text { font-family: "Helvetica Neue", Helvetica, Arial, "PingFang SC", "Microsoft YaHei", sans-serif; }
|
||||
</style>
|
||||
<defs>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#2563eb"/>
|
||||
</marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#16a34a"/>
|
||||
</marker>
|
||||
<marker id="arrow-orange" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#ea580c"/>
|
||||
</marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#9333ea"/>
|
||||
</marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#dc2626"/>
|
||||
</marker>
|
||||
<marker id="arrow-gray" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#6b7280"/>
|
||||
</marker>
|
||||
<marker id="arrow-cyan" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#0ea5e9"/>
|
||||
</marker>
|
||||
<filter id="shadow" x="-4%" y="-4%" width="108%" height="108%">
|
||||
<feDropShadow dx="0" dy="2" stdDeviation="3" flood-color="#000" flood-opacity="0.08"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect width="960" height="500" fill="#ffffff"/>
|
||||
<text x="480" y="36" text-anchor="middle" fill="#111827" font-size="18" font-weight="700">属性三元分类数据流向</text>
|
||||
<text x="480" y="56" text-anchor="middle" fill="#6b7280" font-size="11">03-物模型规范 / 06-设备影子 | CLIENT / SERVER / SHARED 读写流向</text>
|
||||
<rect x="40" y="140" width="140" height="200" rx="10" fill="#fff7ed" stroke="#ea580c" stroke-width="2" filter="url(#shadow)"/>
|
||||
<text x="110" y="170" text-anchor="middle" fill="#ea580c" font-size="14" font-weight="700">IoT 设备</text>
|
||||
<text x="110" y="195" text-anchor="middle" fill="#6b7280" font-size="10">传感器数据上报</text>
|
||||
<text x="110" y="215" text-anchor="middle" fill="#6b7280" font-size="10">状态属性汇报</text>
|
||||
<text x="110" y="240" text-anchor="middle" fill="#6b7280" font-size="10">接收配置下发</text>
|
||||
<text x="110" y="265" text-anchor="middle" fill="#6b7280" font-size="10">执行 RPC 指令</text>
|
||||
<rect x="780" y="140" width="140" height="200" rx="10" fill="#eff6ff" stroke="#2563eb" stroke-width="2" filter="url(#shadow)"/>
|
||||
<text x="850" y="170" text-anchor="middle" fill="#2563eb" font-size="14" font-weight="700">IoT 平台</text>
|
||||
<text x="850" y="195" text-anchor="middle" fill="#6b7280" font-size="10">规则引擎计算</text>
|
||||
<text x="850" y="215" text-anchor="middle" fill="#6b7280" font-size="10">管理后台 UI</text>
|
||||
<text x="850" y="240" text-anchor="middle" fill="#6b7280" font-size="10">REST API</text>
|
||||
<text x="850" y="265" text-anchor="middle" fill="#6b7280" font-size="10">Agent 服务</text>
|
||||
<rect x="280" y="95" width="400" height="65" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="480" y="118" text-anchor="middle" fill="#16a34a" font-size="14" font-weight="700">CLIENT 属性</text>
|
||||
<text x="480" y="136" text-anchor="middle" fill="#6b7280" font-size="10">设备写入 | 平台只读 | Redis iot:device_property:{id}:client</text>
|
||||
<path d="M 180,180 L 270,127" stroke="#16a34a" stroke-width="2" fill="none" marker-end="url(#arrow-green)"/>
|
||||
<rect x="195" y="138" width="40" height="16" rx="3" fill="#fff" fill-opacity="0.9"/>
|
||||
<text x="215" y="150" text-anchor="middle" fill="#16a34a" font-size="9">写入</text>
|
||||
<path d="M 680,127 L 770,180" stroke="#2563eb" stroke-width="1.5" fill="none" stroke-dasharray="5,3" marker-end="url(#arrow-blue)"/>
|
||||
<rect x="695" y="140" width="40" height="16" rx="3" fill="#fff" fill-opacity="0.9"/>
|
||||
<text x="715" y="152" text-anchor="middle" fill="#2563eb" font-size="9">读取</text>
|
||||
<rect x="280" y="205" width="400" height="65" rx="8" fill="#dbeafe" stroke="#2563eb" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="480" y="228" text-anchor="middle" fill="#2563eb" font-size="14" font-weight="700">SERVER 属性</text>
|
||||
<text x="480" y="248" text-anchor="middle" fill="#6b7280" font-size="10">平台写入 | 平台只读 | Redis iot:device_property:{id}:server</text>
|
||||
<path d="M 770,230 L 690,237" stroke="#2563eb" stroke-width="2" fill="none" marker-end="url(#arrow-blue)"/>
|
||||
<rect x="710" y="218" width="40" height="16" rx="3" fill="#fff" fill-opacity="0.9"/>
|
||||
<text x="730" y="230" text-anchor="middle" fill="#2563eb" font-size="9">写入</text>
|
||||
<rect x="280" y="315" width="400" height="65" rx="8" fill="#ede9fe" stroke="#9333ea" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="480" y="338" text-anchor="middle" fill="#9333ea" font-size="14" font-weight="700">SHARED 属性</text>
|
||||
<text x="480" y="358" text-anchor="middle" fill="#6b7280" font-size="10">平台写入 | 设备+平台读取 | Redis + MySQL 双写</text>
|
||||
<path d="M 770,280 L 690,337" stroke="#9333ea" stroke-width="2" fill="none" marker-end="url(#arrow-purple)"/>
|
||||
<rect x="710" y="295" width="40" height="16" rx="3" fill="#fff" fill-opacity="0.9"/>
|
||||
<text x="730" y="307" text-anchor="middle" fill="#9333ea" font-size="9">写入</text>
|
||||
<path d="M 280,347 L 180,290" stroke="#9333ea" stroke-width="2" fill="none" marker-end="url(#arrow-purple)"/>
|
||||
<rect x="195" y="310" width="60" height="16" rx="3" fill="#fff" fill-opacity="0.9"/>
|
||||
<text x="225" y="322" text-anchor="middle" fill="#9333ea" font-size="9">配置下发</text>
|
||||
<rect x="280" y="420" width="400" height="55" rx="8" fill="#f3f4f6" stroke="#d1d5db" stroke-width="1"/>
|
||||
<text x="480" y="442" text-anchor="middle" fill="#111827" font-size="12" font-weight="600">存储策略</text>
|
||||
<text x="480" y="460" text-anchor="middle" fill="#6b7280" font-size="10">CLIENT/SERVER: 仅 Redis | SHARED: Redis + MySQL 双写 (持久化)</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.1 KiB |
BIN
开发者文档/03-IoT领域/升级设计方案/assets/diagram-protocol-hotload.png
Normal file
|
After Width: | Height: | Size: 104 KiB |
65
开发者文档/03-IoT领域/升级设计方案/assets/diagram-protocol-hotload.svg
Normal file
@@ -0,0 +1,65 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 960 480" width="960" height="480">
|
||||
<style>
|
||||
text { font-family: "Helvetica Neue", Helvetica, Arial, "PingFang SC", "Microsoft YaHei", sans-serif; }
|
||||
</style>
|
||||
<defs>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#2563eb"/>
|
||||
</marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#16a34a"/>
|
||||
</marker>
|
||||
<marker id="arrow-orange" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#ea580c"/>
|
||||
</marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#9333ea"/>
|
||||
</marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#dc2626"/>
|
||||
</marker>
|
||||
<marker id="arrow-gray" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#6b7280"/>
|
||||
</marker>
|
||||
<marker id="arrow-cyan" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#0ea5e9"/>
|
||||
</marker>
|
||||
<filter id="shadow" x="-4%" y="-4%" width="108%" height="108%">
|
||||
<feDropShadow dx="0" dy="2" stdDeviation="3" flood-color="#000" flood-opacity="0.08"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect width="960" height="480" fill="#ffffff"/>
|
||||
<text x="480" y="36" text-anchor="middle" fill="#111827" font-size="18" font-weight="700">协议包热加载流程</text>
|
||||
<text x="480" y="56" text-anchor="middle" fill="#6b7280" font-size="11">08-协议与编解码扩展 | JAR 上传 → ClassLoader → SPI 注册</text>
|
||||
<rect x="340" y="80" width="280" height="40" rx="8" fill="#dbeafe" stroke="#2563eb" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="480" y="105" text-anchor="middle" fill="#2563eb" font-size="12" font-weight="600">上传协议包 JAR</text>
|
||||
<rect x="340" y="155" width="280" height="50" rx="8" fill="#fed7aa" stroke="#ea580c" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="480" y="176" text-anchor="middle" fill="#ea580c" font-size="12" font-weight="600">校验 JAR 完整性</text>
|
||||
<text x="480" y="194" text-anchor="middle" fill="#6b7280" font-size="10">签名验证 + META-INF 检查</text>
|
||||
<rect x="340" y="235" width="280" height="50" rx="8" fill="#ede9fe" stroke="#9333ea" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="480" y="256" text-anchor="middle" fill="#9333ea" font-size="12" font-weight="600">创建隔离 ClassLoader</text>
|
||||
<text x="480" y="274" text-anchor="middle" fill="#6b7280" font-size="10">独立类加载器避免冲突</text>
|
||||
<rect x="340" y="315" width="280" height="50" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="480" y="336" text-anchor="middle" fill="#16a34a" font-size="12" font-weight="600">ServiceLoader 发现 Codec</text>
|
||||
<text x="480" y="354" text-anchor="middle" fill="#6b7280" font-size="10">扫描 META-INF/services</text>
|
||||
<rect x="340" y="395" width="280" height="50" rx="8" fill="#fee2e2" stroke="#dc2626" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="480" y="416" text-anchor="middle" fill="#dc2626" font-size="12" font-weight="600">注册到 CodecRegistry</text>
|
||||
<text x="480" y="434" text-anchor="middle" fill="#6b7280" font-size="10">codecId -> Codec 实例映射</text>
|
||||
<line x1="480" y1="125" x2="480" y2="155" stroke="#2563eb" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
|
||||
<line x1="480" y1="205" x2="480" y2="235" stroke="#2563eb" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
|
||||
<line x1="480" y1="285" x2="480" y2="315" stroke="#2563eb" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
|
||||
<line x1="480" y1="365" x2="480" y2="395" stroke="#2563eb" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
|
||||
<rect x="700" y="320" width="200" height="45" rx="8" fill="#fee2e2" stroke="#dc2626" stroke-width="1.2"/>
|
||||
<text x="800" y="340" text-anchor="middle" fill="#dc2626" font-size="11" font-weight="600">卸载协议包</text>
|
||||
<text x="800" y="356" text-anchor="middle" fill="#6b7280" font-size="10">关闭 ClassLoader + 移除注册</text>
|
||||
<path d="M 620,340 L 690,340" stroke="#dc2626" stroke-width="1.5" fill="none" marker-end="url(#arrow-red)"/>
|
||||
<rect x="60" y="180" width="200" height="120" rx="8" fill="#f3f4f6" stroke="#d1d5db" stroke-width="1"/>
|
||||
<text x="160" y="200" text-anchor="middle" fill="#111827" font-size="11" font-weight="600">内置 Codec (Spring Bean)</text>
|
||||
<text x="160" y="220" text-anchor="middle" fill="#6b7280" font-size="10">ALink Codec</text>
|
||||
<text x="160" y="238" text-anchor="middle" fill="#6b7280" font-size="10">JT808 Codec</text>
|
||||
<text x="160" y="256" text-anchor="middle" fill="#6b7280" font-size="10">Camera3D11 Codec</text>
|
||||
<text x="160" y="274" text-anchor="middle" fill="#6b7280" font-size="10">Transparent Codec</text>
|
||||
<path d="M 260,275 L 330,275" stroke="#6b7280" stroke-width="1.2" fill="none" stroke-dasharray="5,3" marker-end="url(#arrow-gray)"/>
|
||||
<rect x="268" y="258" width="55" height="16" rx="3" fill="#fff" fill-opacity="0.9"/>
|
||||
<text x="296" y="270" text-anchor="middle" fill="#6b7280" font-size="8">共存注册</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.4 KiB |
BIN
开发者文档/03-IoT领域/升级设计方案/assets/diagram-rpc-state-machine.png
Normal file
|
After Width: | Height: | Size: 131 KiB |
91
开发者文档/03-IoT领域/升级设计方案/assets/diagram-rpc-state-machine.svg
Normal file
@@ -0,0 +1,91 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 960 500" width="960" height="500">
|
||||
<style>
|
||||
text { font-family: "Helvetica Neue", Helvetica, Arial, "PingFang SC", "Microsoft YaHei", sans-serif; }
|
||||
</style>
|
||||
<defs>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#2563eb"/>
|
||||
</marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#16a34a"/>
|
||||
</marker>
|
||||
<marker id="arrow-orange" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#ea580c"/>
|
||||
</marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#9333ea"/>
|
||||
</marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#dc2626"/>
|
||||
</marker>
|
||||
<marker id="arrow-gray" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#6b7280"/>
|
||||
</marker>
|
||||
<marker id="arrow-cyan" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#0ea5e9"/>
|
||||
</marker>
|
||||
<filter id="shadow" x="-4%" y="-4%" width="108%" height="108%">
|
||||
<feDropShadow dx="0" dy="2" stdDeviation="3" flood-color="#000" flood-opacity="0.08"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect width="960" height="500" fill="#ffffff"/>
|
||||
<text x="480" y="36" text-anchor="middle" fill="#111827" font-size="18" font-weight="700">RPC 状态机 (持久化指令投递)</text>
|
||||
<text x="480" y="56" text-anchor="middle" fill="#6b7280" font-size="11">06-设备影子与RPC | 离线排队 + 上线补发 + 超时重试</text>
|
||||
<circle cx="80" cy="200" r="12" fill="#111827"/>
|
||||
<text x="80" y="180" text-anchor="middle" fill="#6b7280" font-size="10">创建指令</text>
|
||||
<rect x="165" y="172" width="130" height="56" rx="28" fill="#f3f4f6" stroke="#6b7280" stroke-width="2" filter="url(#shadow)"/>
|
||||
<text x="230" y="196" text-anchor="middle" fill="#6b7280" font-size="12" font-weight="700">QUEUED</text>
|
||||
<text x="230" y="214" text-anchor="middle" fill="#6b7280" font-size="10">排队中</text>
|
||||
<rect x="370" y="172" width="120" height="56" rx="28" fill="#dbeafe" stroke="#2563eb" stroke-width="2" filter="url(#shadow)"/>
|
||||
<text x="430" y="196" text-anchor="middle" fill="#2563eb" font-size="12" font-weight="700">SENT</text>
|
||||
<text x="430" y="214" text-anchor="middle" fill="#2563eb" font-size="10">已发送</text>
|
||||
<rect x="565" y="172" width="130" height="56" rx="28" fill="#ede9fe" stroke="#9333ea" stroke-width="2" filter="url(#shadow)"/>
|
||||
<text x="630" y="196" text-anchor="middle" fill="#9333ea" font-size="12" font-weight="700">DELIVERED</text>
|
||||
<text x="630" y="214" text-anchor="middle" fill="#9333ea" font-size="10">已送达</text>
|
||||
<rect x="770" y="92" width="120" height="56" rx="28" fill="#dcfce7" stroke="#16a34a" stroke-width="2" filter="url(#shadow)"/>
|
||||
<text x="830" y="116" text-anchor="middle" fill="#16a34a" font-size="12" font-weight="700">SUCCESS</text>
|
||||
<text x="830" y="134" text-anchor="middle" fill="#16a34a" font-size="10">成功</text>
|
||||
<rect x="770" y="252" width="120" height="56" rx="28" fill="#fee2e2" stroke="#dc2626" stroke-width="2" filter="url(#shadow)"/>
|
||||
<text x="830" y="276" text-anchor="middle" fill="#dc2626" font-size="12" font-weight="700">FAILURE</text>
|
||||
<text x="830" y="294" text-anchor="middle" fill="#dc2626" font-size="10">失败</text>
|
||||
<rect x="370" y="332" width="120" height="56" rx="28" fill="#fed7aa" stroke="#ea580c" stroke-width="2" filter="url(#shadow)"/>
|
||||
<text x="430" y="356" text-anchor="middle" fill="#ea580c" font-size="12" font-weight="700">TIMEOUT</text>
|
||||
<text x="430" y="374" text-anchor="middle" fill="#ea580c" font-size="10">超时</text>
|
||||
<rect x="170" y="332" width="120" height="56" rx="28" fill="#e5e7eb" stroke="#6b7280" stroke-width="2" filter="url(#shadow)"/>
|
||||
<text x="230" y="356" text-anchor="middle" fill="#6b7280" font-size="12" font-weight="700">EXPIRED</text>
|
||||
<text x="230" y="374" text-anchor="middle" fill="#6b7280" font-size="10">已过期</text>
|
||||
<line x1="92" y1="200" x2="155" y2="200" stroke="#6b7280" stroke-width="2" marker-end="url(#arrow-gray)"/>
|
||||
<line x1="295" y1="200" x2="360" y2="200" stroke="#2563eb" stroke-width="2" marker-end="url(#arrow-blue)"/>
|
||||
<rect x="305" y="183" width="50" height="16" rx="3" fill="#fff" fill-opacity="0.9"/>
|
||||
<text x="330" y="195" text-anchor="middle" fill="#2563eb" font-size="9">设备在线</text>
|
||||
<line x1="490" y1="200" x2="555" y2="200" stroke="#9333ea" stroke-width="2" marker-end="url(#arrow-purple)"/>
|
||||
<rect x="498" y="183" width="55" height="16" rx="3" fill="#fff" fill-opacity="0.9"/>
|
||||
<text x="525" y="195" text-anchor="middle" fill="#9333ea" font-size="9">设备确认</text>
|
||||
<path d="M 695,185 L 760,135" stroke="#16a34a" stroke-width="2" fill="none" marker-end="url(#arrow-green)"/>
|
||||
<rect x="702" y="145" width="55" height="16" rx="3" fill="#fff" fill-opacity="0.9"/>
|
||||
<text x="729" y="157" text-anchor="middle" fill="#16a34a" font-size="9">执行成功</text>
|
||||
<path d="M 695,215 L 760,265" stroke="#dc2626" stroke-width="2" fill="none" marker-end="url(#arrow-red)"/>
|
||||
<rect x="702" y="237" width="55" height="16" rx="3" fill="#fff" fill-opacity="0.9"/>
|
||||
<text x="729" y="249" text-anchor="middle" fill="#dc2626" font-size="9">执行失败</text>
|
||||
<line x1="430" y1="228" x2="430" y2="322" stroke="#ea580c" stroke-width="2" marker-end="url(#arrow-orange)"/>
|
||||
<rect x="435" y="268" width="70" height="16" rx="3" fill="#fff" fill-opacity="0.9"/>
|
||||
<text x="470" y="280" text-anchor="middle" fill="#ea580c" font-size="9">响应超时</text>
|
||||
<path d="M 370,360 C 310,360 310,220 365,200" stroke="#2563eb" stroke-width="1.5" fill="none" stroke-dasharray="5,3" marker-end="url(#arrow-blue)"/>
|
||||
<rect x="298" y="278" width="60" height="16" rx="3" fill="#fff" fill-opacity="0.9"/>
|
||||
<text x="328" y="290" text-anchor="middle" fill="#2563eb" font-size="9">重试 (3次)</text>
|
||||
<line x1="370" y1="360" x2="295" y2="360" stroke="#6b7280" stroke-width="2" marker-end="url(#arrow-gray)"/>
|
||||
<rect x="305" y="343" width="55" height="16" rx="3" fill="#fff" fill-opacity="0.9"/>
|
||||
<text x="332" y="355" text-anchor="middle" fill="#6b7280" font-size="9">超过上限</text>
|
||||
<rect x="60" y="420" width="280" height="60" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1"/>
|
||||
<text x="200" y="440" text-anchor="middle" fill="#16a34a" font-size="11" font-weight="600">上线补发机制</text>
|
||||
<text x="200" y="458" text-anchor="middle" fill="#6b7280" font-size="10">设备上线 -> 拉取 QUEUED -> 限速 5msg/s</text>
|
||||
<text x="200" y="472" text-anchor="middle" fill="#6b7280" font-size="9">防止设备上线瞬间过载</text>
|
||||
<rect x="380" y="420" width="280" height="60" rx="8" fill="#dbeafe" stroke="#2563eb" stroke-width="1"/>
|
||||
<text x="520" y="440" text-anchor="middle" fill="#2563eb" font-size="11" font-weight="600">持久化保证</text>
|
||||
<text x="520" y="458" text-anchor="middle" fill="#6b7280" font-size="10">所有状态变更持久化到 iot_device_rpc</text>
|
||||
<text x="520" y="472" text-anchor="middle" fill="#6b7280" font-size="9">支持审计追踪与断点续传</text>
|
||||
<rect x="700" y="420" width="230" height="60" rx="8" fill="#fed7aa" stroke="#ea580c" stroke-width="1"/>
|
||||
<text x="815" y="440" text-anchor="middle" fill="#ea580c" font-size="11" font-weight="600">过期策略</text>
|
||||
<text x="815" y="458" text-anchor="middle" fill="#6b7280" font-size="10">expireTime 到达 -> 标记 EXPIRED</text>
|
||||
<text x="815" y="472" text-anchor="middle" fill="#6b7280" font-size="9">过期指令不再下发设备</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.8 KiB |
BIN
开发者文档/03-IoT领域/升级设计方案/assets/diagram-rule-action-tree.png
Normal file
|
After Width: | Height: | Size: 96 KiB |
67
开发者文档/03-IoT领域/升级设计方案/assets/diagram-rule-action-tree.svg
Normal file
@@ -0,0 +1,67 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 960 410" width="960" height="410">
|
||||
<style>
|
||||
text { font-family: "Helvetica Neue", Helvetica, Arial, "PingFang SC", "Microsoft YaHei", sans-serif; }
|
||||
</style>
|
||||
<defs>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#2563eb"/>
|
||||
</marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#16a34a"/>
|
||||
</marker>
|
||||
<marker id="arrow-orange" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#ea580c"/>
|
||||
</marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#9333ea"/>
|
||||
</marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#dc2626"/>
|
||||
</marker>
|
||||
<marker id="arrow-gray" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#6b7280"/>
|
||||
</marker>
|
||||
<marker id="arrow-cyan" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#0ea5e9"/>
|
||||
</marker>
|
||||
<filter id="shadow" x="-4%" y="-4%" width="108%" height="108%">
|
||||
<feDropShadow dx="0" dy="2" stdDeviation="3" flood-color="#000" flood-opacity="0.08"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect width="960" height="410" fill="#ffffff"/>
|
||||
<text x="480" y="36" text-anchor="middle" fill="#111827" font-size="18" font-weight="700">规则引擎动作分类</text>
|
||||
<text x="480" y="56" text-anchor="middle" fill="#6b7280" font-size="11">04-规则引擎方案 | 7 大类动作节点 + SPI 扩展</text>
|
||||
<rect x="370" y="80" width="220" height="45" rx="8" fill="#ede9fe" stroke="#9333ea" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="480" y="96" text-anchor="middle" fill="#9333ea" font-size="12" font-weight="600">Actions 动作层</text>
|
||||
<text x="480" y="112" text-anchor="middle" fill="#6b7280" font-size="10">SPI Provider 注册</text>
|
||||
<rect x="75" y="190" width="150" height="110" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
|
||||
<text x="150" y="210" text-anchor="middle" fill="#16a34a" font-size="12" font-weight="600">设备控制</text>
|
||||
<text x="150" y="230" text-anchor="middle" fill="#6b7280" font-size="10">device_property_set</text>
|
||||
<text x="150" y="246" text-anchor="middle" fill="#6b7280" font-size="10">device_service_invoke</text>
|
||||
<path d="M 480,125 L 150,180" stroke="#9333ea" stroke-width="1.2" fill="none" marker-end="url(#arrow-purple)"/>
|
||||
<rect x="255" y="190" width="150" height="110" rx="8" fill="#fee2e2" stroke="#dc2626" stroke-width="1.5"/>
|
||||
<text x="330" y="210" text-anchor="middle" fill="#dc2626" font-size="12" font-weight="600">告警处理</text>
|
||||
<text x="330" y="230" text-anchor="middle" fill="#6b7280" font-size="10">alarm_trigger</text>
|
||||
<text x="330" y="246" text-anchor="middle" fill="#6b7280" font-size="10">alarm_clear</text>
|
||||
<path d="M 480,125 L 330,180" stroke="#9333ea" stroke-width="1.2" fill="none" marker-end="url(#arrow-purple)"/>
|
||||
<rect x="435" y="190" width="150" height="85" rx="8" fill="#dbeafe" stroke="#2563eb" stroke-width="1.5"/>
|
||||
<text x="510" y="210" text-anchor="middle" fill="#2563eb" font-size="12" font-weight="600">消息通知</text>
|
||||
<text x="510" y="230" text-anchor="middle" fill="#6b7280" font-size="10">notify (多通道)</text>
|
||||
<path d="M 480,125 L 510,180" stroke="#9333ea" stroke-width="1.2" fill="none" marker-end="url(#arrow-purple)"/>
|
||||
<rect x="615" y="190" width="150" height="110" rx="8" fill="#f3f4f6" stroke="#6b7280" stroke-width="1.5"/>
|
||||
<text x="690" y="210" text-anchor="middle" fill="#6b7280" font-size="12" font-weight="600">数据转发</text>
|
||||
<text x="690" y="230" text-anchor="middle" fill="#6b7280" font-size="10">http_push</text>
|
||||
<text x="690" y="246" text-anchor="middle" fill="#6b7280" font-size="10">mq_push</text>
|
||||
<text x="690" y="262" text-anchor="middle" fill="#6b7280" font-size="10">redis_push</text>
|
||||
<text x="690" y="278" text-anchor="middle" fill="#6b7280" font-size="10">tcp_push</text>
|
||||
<path d="M 480,125 L 690,180" stroke="#9333ea" stroke-width="1.2" fill="none" marker-end="url(#arrow-purple)"/>
|
||||
<rect x="795" y="190" width="140" height="85" rx="8" fill="#ede9fe" stroke="#9333ea" stroke-width="1.5"/>
|
||||
<text x="865" y="210" text-anchor="middle" fill="#9333ea" font-size="12" font-weight="600">数据处理</text>
|
||||
<text x="865" y="230" text-anchor="middle" fill="#6b7280" font-size="10">script (Aviator)</text>
|
||||
<text x="865" y="246" text-anchor="middle" fill="#6b7280" font-size="10">enrich / delay / log</text>
|
||||
<path d="M 480,125 L 865,180" stroke="#9333ea" stroke-width="1.2" fill="none" marker-end="url(#arrow-purple)"/>
|
||||
<rect x="400" y="330" width="160" height="60" rx="8" fill="#e0f2fe" stroke="#0ea5e9" stroke-width="1.5" stroke-dasharray="5,3"/>
|
||||
<text x="480" y="355" text-anchor="middle" fill="#0ea5e9" font-size="12" font-weight="600">Agent 协作 (预留)</text>
|
||||
<text x="480" y="375" text-anchor="middle" fill="#6b7280" font-size="10">agent_request (ACP)</text>
|
||||
<line x1="480" y1="125" x2="480" y2="320" stroke="#0ea5e9" stroke-width="1.2" marker-end="url(#arrow-cyan)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
BIN
开发者文档/03-IoT领域/升级设计方案/assets/diagram-subsystem-er.png
Normal file
|
After Width: | Height: | Size: 135 KiB |
94
开发者文档/03-IoT领域/升级设计方案/assets/diagram-subsystem-er.svg
Normal file
@@ -0,0 +1,94 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 960 570" width="960" height="570">
|
||||
<style>
|
||||
text { font-family: "Helvetica Neue", Helvetica, Arial, "PingFang SC", "Microsoft YaHei", sans-serif; }
|
||||
</style>
|
||||
<defs>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#2563eb"/>
|
||||
</marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#16a34a"/>
|
||||
</marker>
|
||||
<marker id="arrow-orange" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#ea580c"/>
|
||||
</marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#9333ea"/>
|
||||
</marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#dc2626"/>
|
||||
</marker>
|
||||
<marker id="arrow-gray" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#6b7280"/>
|
||||
</marker>
|
||||
<marker id="arrow-cyan" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#0ea5e9"/>
|
||||
</marker>
|
||||
<filter id="shadow" x="-4%" y="-4%" width="108%" height="108%">
|
||||
<feDropShadow dx="0" dy="2" stdDeviation="3" flood-color="#000" flood-opacity="0.08"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect width="960" height="570" fill="#ffffff"/>
|
||||
<text x="480" y="36" text-anchor="middle" fill="#111827" font-size="18" font-weight="700">子系统与设备归属 ER 关系图</text>
|
||||
<text x="480" y="56" text-anchor="middle" fill="#6b7280" font-size="11">02-子系统与设备归属模型 | 租户 → 项目 → 子系统 → 设备</text>
|
||||
<rect x="60" y="90" width="180" height="114" rx="6" fill="#ffffff" stroke="#ea580c" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<rect x="60" y="90" width="180" height="32" rx="6" fill="#fed7aa" stroke="#ea580c" stroke-width="1.5"/>
|
||||
<rect x="60" y="116" width="180" height="6" fill="#fed7aa"/>
|
||||
<text x="150" y="111" text-anchor="middle" fill="#ea580c" font-size="13" font-weight="700">iot_tenant</text>
|
||||
<text x="70" y="140" text-decoration="underline" fill="#374151" font-size="10">id BIGINT PK</text>
|
||||
<text x="70" y="158" fill="#374151" font-size="10">name VARCHAR</text>
|
||||
<text x="70" y="176" fill="#374151" font-size="10">status TINYINT</text>
|
||||
<text x="70" y="194" fill="#374151" font-size="10">...</text>
|
||||
<rect x="60" y="280" width="180" height="132" rx="6" fill="#ffffff" stroke="#ea580c" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<rect x="60" y="280" width="180" height="32" rx="6" fill="#fed7aa" stroke="#ea580c" stroke-width="1.5"/>
|
||||
<rect x="60" y="306" width="180" height="6" fill="#fed7aa"/>
|
||||
<text x="150" y="301" text-anchor="middle" fill="#ea580c" font-size="13" font-weight="700">iot_project</text>
|
||||
<text x="70" y="330" text-decoration="underline" fill="#374151" font-size="10">id BIGINT PK</text>
|
||||
<text x="70" y="348" font-style="italic" fill="#374151" font-size="10">tenant_id BIGINT FK</text>
|
||||
<text x="70" y="366" fill="#374151" font-size="10">name VARCHAR</text>
|
||||
<text x="70" y="384" fill="#374151" font-size="10">code VARCHAR UK</text>
|
||||
<text x="70" y="402" fill="#374151" font-size="10">status TINYINT</text>
|
||||
<rect x="380" y="230" width="200" height="168" rx="6" fill="#ffffff" stroke="#9333ea" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<rect x="380" y="230" width="200" height="32" rx="6" fill="#ede9fe" stroke="#9333ea" stroke-width="1.5"/>
|
||||
<rect x="380" y="256" width="200" height="6" fill="#ede9fe"/>
|
||||
<text x="480" y="251" text-anchor="middle" fill="#9333ea" font-size="13" font-weight="700">iot_subsystem</text>
|
||||
<text x="390" y="280" text-decoration="underline" fill="#374151" font-size="10">id BIGINT PK</text>
|
||||
<text x="390" y="298" font-style="italic" fill="#374151" font-size="10">project_id BIGINT FK</text>
|
||||
<text x="390" y="316" font-style="italic" fill="#374151" font-size="10">tenant_id BIGINT FK</text>
|
||||
<text x="390" y="334" fill="#374151" font-size="10">name VARCHAR</text>
|
||||
<text x="390" y="352" fill="#374151" font-size="10">code VARCHAR UK NOT NULL</text>
|
||||
<text x="390" y="370" fill="#374151" font-size="10">type TINYINT</text>
|
||||
<text x="390" y="388" fill="#374151" font-size="10">status TINYINT</text>
|
||||
<rect x="700" y="160" width="200" height="186" rx="6" fill="#ffffff" stroke="#2563eb" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<rect x="700" y="160" width="200" height="32" rx="6" fill="#dbeafe" stroke="#2563eb" stroke-width="1.5"/>
|
||||
<rect x="700" y="186" width="200" height="6" fill="#dbeafe"/>
|
||||
<text x="800" y="181" text-anchor="middle" fill="#2563eb" font-size="13" font-weight="700">iot_device</text>
|
||||
<text x="710" y="210" text-decoration="underline" fill="#374151" font-size="10">id BIGINT PK</text>
|
||||
<text x="710" y="228" font-style="italic" fill="#374151" font-size="10">product_id BIGINT FK</text>
|
||||
<text x="710" y="246" font-style="italic" fill="#374151" font-size="10">subsystem_id BIGINT FK</text>
|
||||
<text x="710" y="264" font-style="italic" fill="#374151" font-size="10">tenant_id BIGINT FK</text>
|
||||
<text x="710" y="282" fill="#374151" font-size="10">device_key VARCHAR UK</text>
|
||||
<text x="710" y="300" fill="#374151" font-size="10">device_name VARCHAR</text>
|
||||
<text x="710" y="318" fill="#374151" font-size="10">status TINYINT</text>
|
||||
<text x="710" y="336" fill="#374151" font-size="10">last_online_time DATETIME</text>
|
||||
<rect x="700" y="420" width="200" height="150" rx="6" fill="#ffffff" stroke="#16a34a" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<rect x="700" y="420" width="200" height="32" rx="6" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
|
||||
<rect x="700" y="446" width="200" height="6" fill="#dcfce7"/>
|
||||
<text x="800" y="441" text-anchor="middle" fill="#16a34a" font-size="13" font-weight="700">iot_product</text>
|
||||
<text x="710" y="470" text-decoration="underline" fill="#374151" font-size="10">id BIGINT PK</text>
|
||||
<text x="710" y="488" font-style="italic" fill="#374151" font-size="10">tenant_id BIGINT FK</text>
|
||||
<text x="710" y="506" fill="#374151" font-size="10">product_key VARCHAR UK</text>
|
||||
<text x="710" y="524" fill="#374151" font-size="10">name VARCHAR</text>
|
||||
<text x="710" y="542" fill="#374151" font-size="10">protocol_type TINYINT</text>
|
||||
<text x="710" y="560" fill="#374151" font-size="10">status TINYINT</text>
|
||||
<line x1="150" y1="185" x2="150" y2="270" stroke="#ea580c" stroke-width="1.5" marker-end="url(#arrow-orange)"/>
|
||||
<text x="160" y="230" fill="#ea580c" font-size="9" font-weight="600">1 : N</text>
|
||||
<line x1="240" y1="320" x2="370" y2="320" stroke="#9333ea" stroke-width="1.5" marker-end="url(#arrow-purple)"/>
|
||||
<text x="300" y="313" fill="#9333ea" font-size="9" font-weight="600">1 : N</text>
|
||||
<line x1="580" y1="310" x2="690" y2="310" stroke="#2563eb" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
|
||||
<text x="630" y="303" fill="#2563eb" font-size="9" font-weight="600">1 : N</text>
|
||||
<path d="M 800,420 L 800,380" stroke="#16a34a" stroke-width="1.5" fill="none" marker-end="url(#arrow-green)"/>
|
||||
<text x="810" y="400" fill="#16a34a" font-size="9" font-weight="600">1 : N</text>
|
||||
<rect x="60" y="520" width="840" height="30" rx="6" fill="#f3f4f6" stroke="#d1d5db" stroke-width="1"/>
|
||||
<text x="480" y="540" text-anchor="middle" fill="#6b7280" font-size="10">设备通过 subsystem_id FK 归属子系统 | 子系统决定规则链匹配、告警传播、用户权限范围 | iot_project 本次架构预留</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.6 KiB |
BIN
开发者文档/03-IoT领域/升级设计方案/assets/diagram-tsdb-architecture.png
Normal file
|
After Width: | Height: | Size: 114 KiB |
72
开发者文档/03-IoT领域/升级设计方案/assets/diagram-tsdb-architecture.svg
Normal file
@@ -0,0 +1,72 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 960 475" width="960" height="475">
|
||||
<style>
|
||||
text { font-family: "Helvetica Neue", Helvetica, Arial, "PingFang SC", "Microsoft YaHei", sans-serif; }
|
||||
</style>
|
||||
<defs>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#2563eb"/>
|
||||
</marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#16a34a"/>
|
||||
</marker>
|
||||
<marker id="arrow-orange" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#ea580c"/>
|
||||
</marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#9333ea"/>
|
||||
</marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#dc2626"/>
|
||||
</marker>
|
||||
<marker id="arrow-gray" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#6b7280"/>
|
||||
</marker>
|
||||
<marker id="arrow-cyan" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#0ea5e9"/>
|
||||
</marker>
|
||||
<filter id="shadow" x="-4%" y="-4%" width="108%" height="108%">
|
||||
<feDropShadow dx="0" dy="2" stdDeviation="3" flood-color="#000" flood-opacity="0.08"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect width="960" height="475" fill="#ffffff"/>
|
||||
<text x="480" y="36" text-anchor="middle" fill="#111827" font-size="18" font-weight="700">时序库双实现架构</text>
|
||||
<text x="480" y="56" text-anchor="middle" fill="#6b7280" font-size="11">07-数据存储方案 | CTSDB / TDengine 可切换 + DAO 接口统一</text>
|
||||
<rect x="280" y="80" width="400" height="50" rx="8" fill="#f3f4f6" stroke="#111827" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="480" y="99" text-anchor="middle" fill="#111827" font-size="12" font-weight="600">IotTimeSeriesDAO (统一接口)</text>
|
||||
<text x="480" y="115" text-anchor="middle" fill="#6b7280" font-size="10">saveDeviceMessage / queryHistory / createSchema</text>
|
||||
<line x1="380" y1="130" x2="380" y2="165" stroke="#2563eb" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
|
||||
<line x1="580" y1="130" x2="580" y2="165" stroke="#9333ea" stroke-width="1.5" marker-end="url(#arrow-purple)"/>
|
||||
<rect x="80" y="175" width="380" height="200" rx="8" fill="#dbeafe" stroke="#2563eb" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="270" y="198" text-anchor="middle" fill="#2563eb" font-size="14" font-weight="700">CTSDB (InfluxDB 兼容)</text>
|
||||
<rect x="100" y="210" width="340" height="25" rx="4" fill="#eff6ff"/>
|
||||
<text x="270" y="228" text-anchor="middle" fill="#2563eb" font-size="10">IotCtsdbTimeSeriesDAOImpl</text>
|
||||
<text x="270" y="255" text-anchor="middle" fill="#374151" font-size="11" font-weight="600">特点</text>
|
||||
<text x="270" y="275" text-anchor="middle" fill="#6b7280" font-size="10">异步批量写入 (1000条/批 + 1s 自动刷新)</text>
|
||||
<text x="270" y="295" text-anchor="middle" fill="#6b7280" font-size="10">HTTP REST API 交互</text>
|
||||
<text x="270" y="315" text-anchor="middle" fill="#6b7280" font-size="10">自动 schema 创建 (基于物模型)</text>
|
||||
<text x="270" y="335" text-anchor="middle" fill="#6b7280" font-size="10">保留策略: 90天自动过期</text>
|
||||
<text x="270" y="360" text-anchor="middle" fill="#2563eb" font-size="10" font-weight="600">spring.profiles: ctsdb</text>
|
||||
<rect x="500" y="175" width="380" height="200" rx="8" fill="#ede9fe" stroke="#9333ea" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="690" y="198" text-anchor="middle" fill="#9333ea" font-size="14" font-weight="700">TDengine (JDBC)</text>
|
||||
<rect x="520" y="210" width="340" height="25" rx="4" fill="#faf5ff"/>
|
||||
<text x="690" y="228" text-anchor="middle" fill="#9333ea" font-size="10">IotTdEngineTimeSeriesDAOImpl</text>
|
||||
<text x="690" y="255" text-anchor="middle" fill="#374151" font-size="11" font-weight="600">特点</text>
|
||||
<text x="690" y="275" text-anchor="middle" fill="#6b7280" font-size="10">同步 JDBC 写入 + PersistenceBuffer</text>
|
||||
<text x="690" y="295" text-anchor="middle" fill="#6b7280" font-size="10">超级表 (STable) 自动建表</text>
|
||||
<text x="690" y="315" text-anchor="middle" fill="#6b7280" font-size="10">子表按设备自动创建</text>
|
||||
<text x="690" y="335" text-anchor="middle" fill="#6b7280" font-size="10">保留策略: DURATION + KEEP 配置</text>
|
||||
<text x="690" y="360" text-anchor="middle" fill="#9333ea" font-size="10" font-weight="600">spring.profiles: tdengine</text>
|
||||
<text x="480" y="405" text-anchor="middle" fill="#111827" font-size="12" font-weight="600">存储表结构</text>
|
||||
<rect x="120" y="415" width="170" height="40" rx="6" fill="#f3f4f6" stroke="#d1d5db" stroke-width="1"/>
|
||||
<text x="205" y="432" text-anchor="middle" fill="#374151" font-size="10" font-weight="600">device_message</text>
|
||||
<text x="205" y="448" text-anchor="middle" fill="#6b7280" font-size="9">设备上行消息</text>
|
||||
<rect x="320" y="415" width="170" height="40" rx="6" fill="#f3f4f6" stroke="#d1d5db" stroke-width="1"/>
|
||||
<text x="405" y="432" text-anchor="middle" fill="#374151" font-size="10" font-weight="600">product_property_{id}</text>
|
||||
<text x="405" y="448" text-anchor="middle" fill="#6b7280" font-size="9">产品属性时序</text>
|
||||
<rect x="560" y="415" width="170" height="40" rx="6" fill="#f3f4f6" stroke="#d1d5db" stroke-width="1"/>
|
||||
<text x="645" y="432" text-anchor="middle" fill="#374151" font-size="10" font-weight="600">alarm_history</text>
|
||||
<text x="645" y="448" text-anchor="middle" fill="#6b7280" font-size="9">告警状态变更</text>
|
||||
<rect x="780" y="415" width="170" height="40" rx="6" fill="#f3f4f6" stroke="#d1d5db" stroke-width="1"/>
|
||||
<text x="865" y="432" text-anchor="middle" fill="#374151" font-size="10" font-weight="600">rule_debug_log</text>
|
||||
<text x="865" y="448" text-anchor="middle" fill="#6b7280" font-size="9">规则调试日志</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.1 KiB |
BIN
开发者文档/03-IoT领域/升级设计方案/assets/diagram-write-buffer.png
Normal file
|
After Width: | Height: | Size: 77 KiB |
66
开发者文档/03-IoT领域/升级设计方案/assets/diagram-write-buffer.svg
Normal file
@@ -0,0 +1,66 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 960 310" width="960" height="310">
|
||||
<style>
|
||||
text { font-family: "Helvetica Neue", Helvetica, Arial, "PingFang SC", "Microsoft YaHei", sans-serif; }
|
||||
</style>
|
||||
<defs>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#2563eb"/>
|
||||
</marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#16a34a"/>
|
||||
</marker>
|
||||
<marker id="arrow-orange" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#ea580c"/>
|
||||
</marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#9333ea"/>
|
||||
</marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#dc2626"/>
|
||||
</marker>
|
||||
<marker id="arrow-gray" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#6b7280"/>
|
||||
</marker>
|
||||
<marker id="arrow-cyan" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#0ea5e9"/>
|
||||
</marker>
|
||||
<filter id="shadow" x="-4%" y="-4%" width="108%" height="108%">
|
||||
<feDropShadow dx="0" dy="2" stdDeviation="3" flood-color="#000" flood-opacity="0.08"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect width="960" height="310" fill="#ffffff"/>
|
||||
<text x="480" y="36" text-anchor="middle" fill="#111827" font-size="18" font-weight="700">写入缓冲管道 PersistenceBuffer</text>
|
||||
<text x="480" y="56" text-anchor="middle" fill="#6b7280" font-size="11">07-数据存储方案 | 内存队列 + 文件持久化 + 批量写入时序库</text>
|
||||
<rect x="40" y="100" width="160" height="50" rx="8" fill="#fed7aa" stroke="#ea580c" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="120" y="119" text-anchor="middle" fill="#ea580c" font-size="12" font-weight="600">设备消息</text>
|
||||
<text x="120" y="135" text-anchor="middle" fill="#6b7280" font-size="10">IotDeviceMessage</text>
|
||||
<line x1="200" y1="125" x2="260" y2="125" stroke="#2563eb" stroke-width="2" marker-end="url(#arrow-blue)"/>
|
||||
<rect x="270" y="85" width="200" height="80" rx="8" fill="#dbeafe" stroke="#2563eb" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="370" y="110" text-anchor="middle" fill="#2563eb" font-size="13" font-weight="700">内存队列 L1</text>
|
||||
<text x="370" y="128" text-anchor="middle" fill="#6b7280" font-size="10">ConcurrentLinkedQueue</text>
|
||||
<text x="370" y="145" text-anchor="middle" fill="#6b7280" font-size="10">最大 1000 条 / 1s 自动刷新</text>
|
||||
<line x1="470" y1="125" x2="530" y2="125" stroke="#2563eb" stroke-width="2" marker-end="url(#arrow-blue)"/>
|
||||
<rect x="478" y="107" width="50" height="16" rx="3" fill="#fff" fill-opacity="0.9"/>
|
||||
<text x="503" y="119" text-anchor="middle" fill="#2563eb" font-size="9">批量刷新</text>
|
||||
<rect x="540" y="85" width="180" height="80" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="630" y="110" text-anchor="middle" fill="#16a34a" font-size="13" font-weight="700">批量写入器</text>
|
||||
<text x="630" y="128" text-anchor="middle" fill="#6b7280" font-size="10">BatchInsert 1000条/批</text>
|
||||
<text x="630" y="145" text-anchor="middle" fill="#6b7280" font-size="10">异步/同步 (按实现)</text>
|
||||
<line x1="720" y1="125" x2="780" y2="125" stroke="#16a34a" stroke-width="2" marker-end="url(#arrow-green)"/>
|
||||
<ellipse cx="850" cy="105" rx="60" ry="12" fill="#ede9fe" stroke="#9333ea" stroke-width="1.5"/>
|
||||
<rect x="790" y="105" width="120" height="45" fill="#ede9fe" stroke="none"/>
|
||||
<line x1="790" y1="105" x2="790" y2="150" stroke="#9333ea" stroke-width="1.5"/>
|
||||
<line x1="910" y1="105" x2="910" y2="150" stroke="#9333ea" stroke-width="1.5"/>
|
||||
<ellipse cx="850" cy="150" rx="60" ry="12" fill="#e9d5ff" stroke="#9333ea" stroke-width="1.5"/>
|
||||
<text x="850" y="132" text-anchor="middle" fill="#9333ea" font-size="12" font-weight="700">TimeSeries DB</text>
|
||||
<line x1="370" y1="165" x2="370" y2="210" stroke="#dc2626" stroke-width="1.5" marker-end="url(#arrow-red)"/>
|
||||
<rect x="270" y="220" width="200" height="65" rx="8" fill="#fee2e2" stroke="#dc2626" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="370" y="242" text-anchor="middle" fill="#dc2626" font-size="13" font-weight="700">文件持久化 L2</text>
|
||||
<text x="370" y="260" text-anchor="middle" fill="#6b7280" font-size="10">内存溢出 / 进程崩溃时写磁盘</text>
|
||||
<text x="370" y="275" text-anchor="middle" fill="#6b7280" font-size="10">重启后自动重放</text>
|
||||
<path d="M 470,252 L 530,145" stroke="#dc2626" stroke-width="1.5" fill="none" stroke-dasharray="5,3" marker-end="url(#arrow-red)"/>
|
||||
<rect x="485" y="188" width="50" height="16" rx="3" fill="#fff" fill-opacity="0.9"/>
|
||||
<text x="510" y="200" text-anchor="middle" fill="#dc2626" font-size="9">重启重放</text>
|
||||
<rect x="338" y="172" width="65" height="16" rx="3" fill="#fff" fill-opacity="0.9"/>
|
||||
<text x="370" y="184" text-anchor="middle" fill="#dc2626" font-size="9">溢出降级</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.2 KiB |
BIN
开发者文档/03-IoT领域/升级设计方案/assets/iot-e2e-business-flow.png
Normal file
|
After Width: | Height: | Size: 449 KiB |
308
开发者文档/03-IoT领域/升级设计方案/assets/iot-e2e-business-flow.svg
Normal file
@@ -0,0 +1,308 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 1100" width="1200" height="1100">
|
||||
<style>
|
||||
text { font-family: "Helvetica Neue", Helvetica, Arial, "PingFang SC", "Microsoft YaHei", sans-serif; }
|
||||
</style>
|
||||
<defs>
|
||||
<marker id="arrow-blue" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#2563eb"/>
|
||||
</marker>
|
||||
<marker id="arrow-green" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#16a34a"/>
|
||||
</marker>
|
||||
<marker id="arrow-orange" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#ea580c"/>
|
||||
</marker>
|
||||
<marker id="arrow-purple" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#9333ea"/>
|
||||
</marker>
|
||||
<marker id="arrow-red" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#dc2626"/>
|
||||
</marker>
|
||||
<marker id="arrow-gray" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#6b7280"/>
|
||||
</marker>
|
||||
<marker id="arrow-cyan" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#0ea5e9"/>
|
||||
</marker>
|
||||
<filter id="shadow" x="-4%" y="-4%" width="108%" height="108%">
|
||||
<feDropShadow dx="0" dy="2" stdDeviation="3" flood-color="#000" flood-opacity="0.08"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect width="1200" height="1100" fill="#ffffff"/>
|
||||
<text x="600" y="36" text-anchor="middle" fill="#111827" font-size="20" font-weight="700">IoT 平台端到端业务链路</text>
|
||||
<text x="600" y="56" text-anchor="middle" fill="#6b7280" font-size="12">v2.0 架构 | 设备 → 网关 → 总线 → 核心处理 → 规则引擎 → 动作执行 → 存储</text>
|
||||
<rect x="40" y="72" width="1120" height="70" rx="6" fill="#fff7ed" fill-opacity="0.5" stroke="#fed7aa" stroke-width="1" stroke-dasharray="6,4"/>
|
||||
<text x="56" y="88" fill="#ea580c" font-size="10" font-weight="600" letter-spacing="0.06em">物理设备层 DEVICE LAYER</text>
|
||||
<rect x="105" y="96" width="100" height="38" rx="6" fill="#ffffff" stroke="#d1d5db" stroke-width="1.2"/>
|
||||
<circle cx="133" cy="115" r="10" fill="#dc2626" fill-opacity="0.15" stroke="#dc2626" stroke-width="1"/>
|
||||
<circle cx="133" cy="115" r="4" fill="#dc2626"/>
|
||||
<text x="169" y="119" text-anchor="middle" fill="#374151" font-size="10" font-weight="600">温湿度传感器</text>
|
||||
<rect x="285" y="96" width="100" height="38" rx="6" fill="#ffffff" stroke="#d1d5db" stroke-width="1.2"/>
|
||||
<circle cx="313" cy="115" r="10" fill="#2563eb" fill-opacity="0.15" stroke="#2563eb" stroke-width="1"/>
|
||||
<circle cx="313" cy="115" r="4" fill="#2563eb"/>
|
||||
<text x="349" y="119" text-anchor="middle" fill="#374151" font-size="10" font-weight="600">3D 摄像头</text>
|
||||
<rect x="465" y="96" width="100" height="38" rx="6" fill="#ffffff" stroke="#d1d5db" stroke-width="1.2"/>
|
||||
<circle cx="493" cy="115" r="10" fill="#16a34a" fill-opacity="0.15" stroke="#16a34a" stroke-width="1"/>
|
||||
<circle cx="493" cy="115" r="4" fill="#16a34a"/>
|
||||
<text x="529" y="119" text-anchor="middle" fill="#374151" font-size="10" font-weight="600">智能控制器</text>
|
||||
<rect x="645" y="96" width="100" height="38" rx="6" fill="#ffffff" stroke="#d1d5db" stroke-width="1.2"/>
|
||||
<circle cx="673" cy="115" r="10" fill="#ea580c" fill-opacity="0.15" stroke="#ea580c" stroke-width="1"/>
|
||||
<circle cx="673" cy="115" r="4" fill="#ea580c"/>
|
||||
<text x="709" y="119" text-anchor="middle" fill="#374151" font-size="10" font-weight="600">GPS 定位器</text>
|
||||
<rect x="825" y="96" width="100" height="38" rx="6" fill="#ffffff" stroke="#d1d5db" stroke-width="1.2"/>
|
||||
<circle cx="853" cy="115" r="10" fill="#9333ea" fill-opacity="0.15" stroke="#9333ea" stroke-width="1"/>
|
||||
<circle cx="853" cy="115" r="4" fill="#9333ea"/>
|
||||
<text x="889" y="119" text-anchor="middle" fill="#374151" font-size="10" font-weight="600">Modbus 设备</text>
|
||||
<rect x="995" y="96" width="100" height="38" rx="6" fill="#ffffff" stroke="#d1d5db" stroke-width="1.2"/>
|
||||
<circle cx="1023" cy="115" r="10" fill="#6b7280" fill-opacity="0.15" stroke="#6b7280" stroke-width="1"/>
|
||||
<circle cx="1023" cy="115" r="4" fill="#6b7280"/>
|
||||
<text x="1059" y="119" text-anchor="middle" fill="#374151" font-size="10" font-weight="600">自定义设备</text>
|
||||
<line x1="155" y1="134" x2="155" y2="175" stroke="#2563eb" stroke-width="1.2" marker-end="url(#arrow-blue)"/>
|
||||
<line x1="335" y1="134" x2="335" y2="175" stroke="#2563eb" stroke-width="1.2" marker-end="url(#arrow-blue)"/>
|
||||
<line x1="515" y1="134" x2="515" y2="175" stroke="#2563eb" stroke-width="1.2" marker-end="url(#arrow-blue)"/>
|
||||
<line x1="695" y1="134" x2="695" y2="175" stroke="#2563eb" stroke-width="1.2" marker-end="url(#arrow-blue)"/>
|
||||
<line x1="875" y1="134" x2="875" y2="175" stroke="#2563eb" stroke-width="1.2" marker-end="url(#arrow-blue)"/>
|
||||
<line x1="1045" y1="134" x2="1045" y2="175" stroke="#2563eb" stroke-width="1.2" marker-end="url(#arrow-blue)"/>
|
||||
<rect x="40" y="158" width="1120" height="90" rx="6" fill="#eff6ff" fill-opacity="0.4" stroke="#bfdbfe" stroke-width="1" stroke-dasharray="6,4"/>
|
||||
<text x="56" y="174" fill="#2563eb" font-size="10" font-weight="600" letter-spacing="0.06em">设备接入层 DEVICE GATEWAY & CODEC</text>
|
||||
<rect x="105" y="182" width="150" height="55" rx="8" fill="#dbeafe" stroke="#2563eb" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="180" y="203" text-anchor="middle" fill="#2563eb" font-size="13" font-weight="600">MQTT Broker</text>
|
||||
<text x="180" y="220" text-anchor="middle" fill="#6b7280" font-size="10">MQTT 3.1 / 5.0</text>
|
||||
<rect x="315" y="182" width="150" height="55" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="390" y="203" text-anchor="middle" fill="#16a34a" font-size="13" font-weight="600">HTTP 网关</text>
|
||||
<text x="390" y="220" text-anchor="middle" fill="#6b7280" font-size="10">REST / CoAP</text>
|
||||
<rect x="525" y="182" width="150" height="55" rx="8" fill="#ede9fe" stroke="#9333ea" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="600" y="203" text-anchor="middle" fill="#9333ea" font-size="13" font-weight="600">TCP 网关</text>
|
||||
<text x="600" y="220" text-anchor="middle" fill="#6b7280" font-size="10">自定义协议</text>
|
||||
<rect x="735" y="182" width="150" height="55" rx="8" fill="#fed7aa" stroke="#ea580c" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="810" y="203" text-anchor="middle" fill="#ea580c" font-size="13" font-weight="600">EMQX 桥接</text>
|
||||
<text x="810" y="220" text-anchor="middle" fill="#6b7280" font-size="10">集群桥接</text>
|
||||
<rect x="935" y="182" width="210" height="55" rx="8" fill="#ffffff" stroke="#d1d5db" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="1040" y="200" text-anchor="middle" fill="#111827" font-size="12" font-weight="600">协议解码 Codec SPI</text>
|
||||
<text x="1040" y="216" text-anchor="middle" fill="#6b7280" font-size="10">ALink | JT808 | Camera3D</text>
|
||||
<text x="1040" y="230" text-anchor="middle" fill="#9333ea" font-size="9">热加载 JAR 扩展</text>
|
||||
<line x1="885" y1="210" x2="925" y2="210" stroke="#9333ea" stroke-width="1.2" fill="none" marker-end="url(#arrow-purple)"/>
|
||||
<line x1="180" y1="237" x2="180" y2="265" stroke="#2563eb" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
|
||||
<line x1="390" y1="237" x2="390" y2="265" stroke="#2563eb" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
|
||||
<line x1="600" y1="237" x2="600" y2="265" stroke="#2563eb" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
|
||||
<line x1="810" y1="237" x2="810" y2="265" stroke="#2563eb" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
|
||||
<path d="M 1040,237 L 1040,255 L 600,255 L 600,265" stroke="#2563eb" stroke-width="2" fill="none" marker-end="url(#arrow-blue)"/>
|
||||
<rect x="730" y="247" width="200" height="16" rx="4" fill="#ffffff" fill-opacity="0.95" stroke="#2563eb" stroke-width="0.5"/>
|
||||
<text x="830" y="259" text-anchor="middle" fill="#2563eb" font-size="10" font-weight="600">IotDeviceMessage 统一消息模型</text>
|
||||
<rect x="140" y="275" width="920" height="48" rx="10" fill="#111827" stroke="#374151" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="600" y="296" text-anchor="middle" fill="#ffffff" font-size="14" font-weight="600">消息总线 Message Bus</text>
|
||||
<text x="600" y="312" text-anchor="middle" fill="#9ca3af" font-size="10">LocalMessageBus | RedisStreamMessageBus | RocketMQMessageBus</text>
|
||||
<line x1="210" y1="323" x2="210" y2="365" stroke="#2563eb" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
|
||||
<line x1="410" y1="323" x2="410" y2="365" stroke="#2563eb" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
|
||||
<line x1="600" y1="323" x2="600" y2="365" stroke="#2563eb" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
|
||||
<line x1="790" y1="323" x2="790" y2="365" stroke="#2563eb" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
|
||||
<line x1="990" y1="323" x2="990" y2="365" stroke="#2563eb" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
|
||||
<rect x="40" y="350" width="1120" height="100" rx="6" fill="#f0fdf4" fill-opacity="0.4" stroke="#bbf7d0" stroke-width="1" stroke-dasharray="6,4"/>
|
||||
<text x="56" y="366" fill="#16a34a" font-size="10" font-weight="600" letter-spacing="0.06em">核心处理层 IotDeviceMessageSubscriber</text>
|
||||
<rect x="130" y="375" width="160" height="60" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="210" y="400" text-anchor="middle" fill="#16a34a" font-size="12" font-weight="600">设备上下线</text>
|
||||
<text x="210" y="418" text-anchor="middle" fill="#6b7280" font-size="10">Redis 状态标记</text>
|
||||
<rect x="330" y="375" width="160" height="60" rx="8" fill="#dbeafe" stroke="#2563eb" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="410" y="400" text-anchor="middle" fill="#2563eb" font-size="12" font-weight="600">属性三分类</text>
|
||||
<text x="410" y="418" text-anchor="middle" fill="#6b7280" font-size="10">CLIENT|SERVER|SHARED</text>
|
||||
<rect x="520" y="375" width="160" height="60" rx="8" fill="#ede9fe" stroke="#9333ea" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="600" y="400" text-anchor="middle" fill="#9333ea" font-size="12" font-weight="600">时序数据写入</text>
|
||||
<text x="600" y="418" text-anchor="middle" fill="#6b7280" font-size="10">PersistenceBuffer</text>
|
||||
<rect x="710" y="375" width="160" height="60" rx="8" fill="#fed7aa" stroke="#ea580c" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="790" y="400" text-anchor="middle" fill="#ea580c" font-size="12" font-weight="600">物模型合并</text>
|
||||
<text x="790" y="418" text-anchor="middle" fill="#6b7280" font-size="10">产品 + 设备扩展</text>
|
||||
<rect x="910" y="375" width="160" height="60" rx="8" fill="#fee2e2" stroke="#dc2626" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="990" y="400" text-anchor="middle" fill="#dc2626" font-size="12" font-weight="600">规则引擎触发</text>
|
||||
<text x="990" y="418" text-anchor="middle" fill="#6b7280" font-size="10">转发至规则链</text>
|
||||
<path d="M 210,435 L 210,460 L 80,460 L 80,960 L 125,960" stroke="#16a34a" stroke-width="1.2" stroke-dasharray="5,3" fill="none" marker-end="url(#arrow-green)"/>
|
||||
<rect x="56" y="452" width="60" height="16" rx="3" fill="#ffffff" fill-opacity="0.95"/>
|
||||
<text x="86" y="464" text-anchor="middle" fill="#16a34a" font-size="8">写入 Redis</text>
|
||||
<path d="M 410,435 L 410,460 L 100,460 L 100,990 L 125,990" stroke="#2563eb" stroke-width="1.2" stroke-dasharray="5,3" fill="none" marker-end="url(#arrow-blue)"/>
|
||||
<rect x="76" y="475" width="60" height="16" rx="3" fill="#ffffff" fill-opacity="0.95"/>
|
||||
<text x="106" y="487" text-anchor="middle" fill="#2563eb" font-size="8">写入 Redis</text>
|
||||
<path d="M 600,435 L 600,462 L 1135,462 L 1135,955 L 555,955" stroke="#9333ea" stroke-width="1.2" stroke-dasharray="5,3" fill="none" marker-end="url(#arrow-purple)"/>
|
||||
<rect x="1105" y="470" width="55" height="16" rx="3" fill="#ffffff" fill-opacity="0.95"/>
|
||||
<text x="1132" y="482" text-anchor="middle" fill="#9333ea" font-size="8">写入 TSDB</text>
|
||||
<line x1="990" y1="435" x2="990" y2="480" stroke="#dc2626" stroke-width="2" marker-end="url(#arrow-red)"/>
|
||||
<rect x="40" y="490" width="1120" height="185" rx="6" fill="#faf5ff" fill-opacity="0.4" stroke="#e9d5ff" stroke-width="1" stroke-dasharray="6,4"/>
|
||||
<text x="56" y="506" fill="#9333ea" font-size="10" font-weight="600" letter-spacing="0.06em">规则引擎层 RULE ENGINE — DAG 编排</text>
|
||||
<rect x="60" y="518" width="170" height="65" rx="8" fill="#ede9fe" stroke="#9333ea" stroke-width="1.5" filter="url(#shadow)"/>
|
||||
<text x="145" y="540" text-anchor="middle" fill="#9333ea" font-size="12" font-weight="600">规则链匹配</text>
|
||||
<text x="145" y="556" text-anchor="middle" fill="#6b7280" font-size="9">子系统 + 产品 + 设备</text>
|
||||
<text x="145" y="568" text-anchor="middle" fill="#6b7280" font-size="9">+ 全局规则合并</text>
|
||||
<rect x="255" y="518" width="130" height="65" rx="8" fill="#ffffff" stroke="#9333ea" stroke-width="1.2"/>
|
||||
<text x="320" y="540" text-anchor="middle" fill="#9333ea" font-size="12" font-weight="600">触发节点</text>
|
||||
<text x="320" y="556" text-anchor="middle" fill="#6b7280" font-size="9">设备状态 / 属性</text>
|
||||
<text x="320" y="568" text-anchor="middle" fill="#6b7280" font-size="9">事件 / 定时 / 手动</text>
|
||||
<rect x="405" y="518" width="130" height="65" rx="8" fill="#ffffff" stroke="#9333ea" stroke-width="1.2"/>
|
||||
<text x="470" y="540" text-anchor="middle" fill="#9333ea" font-size="12" font-weight="600">数据丰富</text>
|
||||
<text x="470" y="556" text-anchor="middle" fill="#6b7280" font-size="9">读取历史均值</text>
|
||||
<text x="470" y="568" text-anchor="middle" fill="#6b7280" font-size="9">关联设备信息</text>
|
||||
<rect x="555" y="518" width="130" height="65" rx="8" fill="#ffffff" stroke="#9333ea" stroke-width="1.2"/>
|
||||
<text x="620" y="540" text-anchor="middle" fill="#9333ea" font-size="12" font-weight="600">条件判断</text>
|
||||
<text x="620" y="556" text-anchor="middle" fill="#6b7280" font-size="9">表达式 / 脚本</text>
|
||||
<text x="620" y="568" text-anchor="middle" fill="#6b7280" font-size="9">时间范围 / 状态</text>
|
||||
<rect x="705" y="518" width="130" height="65" rx="8" fill="#ffffff" stroke="#9333ea" stroke-width="1.2"/>
|
||||
<text x="770" y="540" text-anchor="middle" fill="#9333ea" font-size="12" font-weight="600">抖动过滤</text>
|
||||
<text x="770" y="556" text-anchor="middle" fill="#6b7280" font-size="9">窗口期 N 秒</text>
|
||||
<text x="770" y="568" text-anchor="middle" fill="#6b7280" font-size="9">触发 M 次通过</text>
|
||||
<rect x="855" y="518" width="130" height="65" rx="8" fill="#ffffff" stroke="#9333ea" stroke-width="1.2"/>
|
||||
<text x="920" y="540" text-anchor="middle" fill="#9333ea" font-size="12" font-weight="600">分支执行</text>
|
||||
<text x="920" y="556" text-anchor="middle" fill="#6b7280" font-size="9">条件分流</text>
|
||||
<text x="920" y="568" text-anchor="middle" fill="#6b7280" font-size="9">并行动作链</text>
|
||||
<line x1="230" y1="550" x2="245" y2="550" stroke="#9333ea" stroke-width="1.5" marker-end="url(#arrow-purple)"/>
|
||||
<line x1="385" y1="550" x2="395" y2="550" stroke="#9333ea" stroke-width="1.5" marker-end="url(#arrow-purple)"/>
|
||||
<line x1="535" y1="550" x2="545" y2="550" stroke="#9333ea" stroke-width="1.5" marker-end="url(#arrow-purple)"/>
|
||||
<line x1="685" y1="550" x2="695" y2="550" stroke="#9333ea" stroke-width="1.5" marker-end="url(#arrow-purple)"/>
|
||||
<line x1="835" y1="550" x2="845" y2="550" stroke="#9333ea" stroke-width="1.5" marker-end="url(#arrow-purple)"/>
|
||||
<text x="56" y="610" fill="#9333ea" font-size="10" font-weight="600">动作执行 ACTION NODES</text>
|
||||
<rect x="70" y="620" width="120" height="45" rx="6" fill="#fee2e2" stroke="#dc2626" stroke-width="1.2"/>
|
||||
<text x="130" y="639" text-anchor="middle" fill="#dc2626" font-size="11" font-weight="600">告警触发</text>
|
||||
<text x="130" y="655" text-anchor="middle" fill="#6b7280" font-size="9">alarm_trigger</text>
|
||||
<rect x="220" y="620" width="120" height="45" rx="6" fill="#fed7aa" stroke="#ea580c" stroke-width="1.2"/>
|
||||
<text x="280" y="639" text-anchor="middle" fill="#ea580c" font-size="11" font-weight="600">告警清除</text>
|
||||
<text x="280" y="655" text-anchor="middle" fill="#6b7280" font-size="9">alarm_clear</text>
|
||||
<rect x="370" y="620" width="120" height="45" rx="6" fill="#dbeafe" stroke="#2563eb" stroke-width="1.2"/>
|
||||
<text x="430" y="639" text-anchor="middle" fill="#2563eb" font-size="11" font-weight="600">消息通知</text>
|
||||
<text x="430" y="655" text-anchor="middle" fill="#6b7280" font-size="9">短信/邮件/钉钉</text>
|
||||
<rect x="520" y="620" width="120" height="45" rx="6" fill="#dcfce7" stroke="#16a34a" stroke-width="1.2"/>
|
||||
<text x="580" y="639" text-anchor="middle" fill="#16a34a" font-size="11" font-weight="600">设备控制</text>
|
||||
<text x="580" y="655" text-anchor="middle" fill="#6b7280" font-size="9">RPC / 属性下发</text>
|
||||
<rect x="670" y="620" width="120" height="45" rx="6" fill="#f3f4f6" stroke="#6b7280" stroke-width="1.2"/>
|
||||
<text x="730" y="639" text-anchor="middle" fill="#6b7280" font-size="11" font-weight="600">数据转发</text>
|
||||
<text x="730" y="655" text-anchor="middle" fill="#6b7280" font-size="9">HTTP/MQ/Redis/TCP</text>
|
||||
<rect x="820" y="620" width="120" height="45" rx="6" fill="#ede9fe" stroke="#9333ea" stroke-width="1.2"/>
|
||||
<text x="880" y="639" text-anchor="middle" fill="#9333ea" font-size="11" font-weight="600">脚本处理</text>
|
||||
<text x="880" y="655" text-anchor="middle" fill="#6b7280" font-size="9">Aviator 沙箱</text>
|
||||
<rect x="980" y="620" width="120" height="45" rx="6" fill="#e0f2fe" stroke="#0ea5e9" stroke-width="1.2"/>
|
||||
<text x="1040" y="639" text-anchor="middle" fill="#0ea5e9" font-size="11" font-weight="600">Agent 协作</text>
|
||||
<text x="1040" y="655" text-anchor="middle" fill="#6b7280" font-size="9">ACP 智能体</text>
|
||||
<line x1="920" y1="583" x2="920" y2="600" stroke="#9333ea" stroke-width="1.5"/>
|
||||
<line x1="100" y1="600" x2="1070" y2="600" stroke="#9333ea" stroke-width="1.2"/>
|
||||
<line x1="130" y1="600" x2="130" y2="610" stroke="#9333ea" stroke-width="1.2" marker-end="url(#arrow-purple)"/>
|
||||
<line x1="280" y1="600" x2="280" y2="610" stroke="#9333ea" stroke-width="1.2" marker-end="url(#arrow-purple)"/>
|
||||
<line x1="430" y1="600" x2="430" y2="610" stroke="#9333ea" stroke-width="1.2" marker-end="url(#arrow-purple)"/>
|
||||
<line x1="580" y1="600" x2="580" y2="610" stroke="#9333ea" stroke-width="1.2" marker-end="url(#arrow-purple)"/>
|
||||
<line x1="730" y1="600" x2="730" y2="610" stroke="#9333ea" stroke-width="1.2" marker-end="url(#arrow-purple)"/>
|
||||
<line x1="880" y1="600" x2="880" y2="610" stroke="#9333ea" stroke-width="1.2" marker-end="url(#arrow-purple)"/>
|
||||
<line x1="1040" y1="600" x2="1040" y2="610" stroke="#9333ea" stroke-width="1.2" marker-end="url(#arrow-purple)"/>
|
||||
<rect x="40" y="700" width="540" height="125" rx="6" fill="#fef2f2" fill-opacity="0.4" stroke="#fecaca" stroke-width="1" stroke-dasharray="6,4"/>
|
||||
<text x="56" y="716" fill="#dc2626" font-size="10" font-weight="600" letter-spacing="0.06em">告警状态机 ALARM STATE MACHINE</text>
|
||||
<rect x="75" y="730" width="110" height="36" rx="18" fill="#fee2e2" stroke="#dc2626" stroke-width="1.5"/>
|
||||
<text x="130" y="753" text-anchor="middle" fill="#dc2626" font-size="10" font-weight="600">活跃 ACTIVE</text>
|
||||
<rect x="215" y="730" width="110" height="36" rx="18" fill="#fed7aa" stroke="#ea580c" stroke-width="1.5"/>
|
||||
<text x="270" y="753" text-anchor="middle" fill="#ea580c" font-size="10" font-weight="600">已确认 ACK</text>
|
||||
<rect x="355" y="730" width="110" height="36" rx="18" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
|
||||
<text x="410" y="753" text-anchor="middle" fill="#16a34a" font-size="10" font-weight="600">已清除</text>
|
||||
<rect x="480" y="730" width="90" height="36" rx="18" fill="#f3f4f6" stroke="#6b7280" stroke-width="1.5"/>
|
||||
<text x="530" y="753" text-anchor="middle" fill="#6b7280" font-size="10" font-weight="600">已解决</text>
|
||||
<line x1="185" y1="748" x2="205" y2="748" stroke="#ea580c" stroke-width="1.5" marker-end="url(#arrow-orange)"/>
|
||||
<line x1="325" y1="748" x2="345" y2="748" stroke="#16a34a" stroke-width="1.5" marker-end="url(#arrow-green)"/>
|
||||
<line x1="465" y1="748" x2="470" y2="748" stroke="#6b7280" stroke-width="1.5" marker-end="url(#arrow-gray)"/>
|
||||
<text x="70" y="792" fill="#dc2626" font-size="10" font-weight="600">双层存储:</text>
|
||||
<rect x="60" y="798" width="240" height="18" rx="4" fill="#fee2e2"/>
|
||||
<text x="180" y="812" text-anchor="middle" fill="#dc2626" font-size="9">MySQL AlarmRecord 当前状态 (Upsert)</text>
|
||||
<rect x="310" y="798" width="250" height="18" rx="4" fill="#fee2e2"/>
|
||||
<text x="435" y="812" text-anchor="middle" fill="#dc2626" font-size="9">TimeSeries AlarmHistory 审计轨迹 (Append)</text>
|
||||
<line x1="130" y1="665" x2="130" y2="690" stroke="#dc2626" stroke-width="1.5" marker-end="url(#arrow-red)"/>
|
||||
<line x1="280" y1="665" x2="280" y2="690" stroke="#ea580c" stroke-width="1.5" marker-end="url(#arrow-orange)"/>
|
||||
<rect x="600" y="700" width="260" height="125" rx="6" fill="#eff6ff" fill-opacity="0.4" stroke="#bfdbfe" stroke-width="1" stroke-dasharray="6,4"/>
|
||||
<text x="616" y="716" fill="#2563eb" font-size="10" font-weight="600" letter-spacing="0.06em">通知体系 NOTIFICATION</text>
|
||||
<rect x="620" y="730" width="75" height="20" rx="4" fill="#dbeafe"/>
|
||||
<text x="657" y="744" text-anchor="middle" fill="#2563eb" font-size="9">短信 SMS</text>
|
||||
<rect x="700" y="730" width="75" height="20" rx="4" fill="#dbeafe"/>
|
||||
<text x="737" y="744" text-anchor="middle" fill="#2563eb" font-size="9">邮件 Email</text>
|
||||
<rect x="620" y="755" width="75" height="20" rx="4" fill="#dbeafe"/>
|
||||
<text x="657" y="769" text-anchor="middle" fill="#2563eb" font-size="9">钉钉机器人</text>
|
||||
<rect x="700" y="755" width="75" height="20" rx="4" fill="#dbeafe"/>
|
||||
<text x="737" y="769" text-anchor="middle" fill="#2563eb" font-size="9">企业微信</text>
|
||||
<rect x="620" y="780" width="75" height="20" rx="4" fill="#dbeafe"/>
|
||||
<text x="657" y="794" text-anchor="middle" fill="#2563eb" font-size="9">站内消息</text>
|
||||
<rect x="700" y="780" width="75" height="20" rx="4" fill="#dbeafe"/>
|
||||
<text x="737" y="794" text-anchor="middle" fill="#2563eb" font-size="9">模板变量</text>
|
||||
<path d="M 430,665 L 430,685 L 730,685 L 730,690" stroke="#2563eb" stroke-width="1.5" fill="none" marker-end="url(#arrow-blue)"/>
|
||||
<rect x="880" y="700" width="280" height="125" rx="6" fill="#f0fdf4" fill-opacity="0.4" stroke="#bbf7d0" stroke-width="1" stroke-dasharray="6,4"/>
|
||||
<text x="896" y="716" fill="#16a34a" font-size="10" font-weight="600" letter-spacing="0.06em">设备控制 RPC 状态机</text>
|
||||
<rect x="900" y="732" width="72" height="28" rx="14" fill="#ffffff" stroke="#6b7280" stroke-width="1.2"/>
|
||||
<text x="936" y="751" text-anchor="middle" fill="#6b7280" font-size="10" font-weight="600">排队</text>
|
||||
<rect x="1000" y="732" width="60" height="28" rx="14" fill="#ffffff" stroke="#2563eb" stroke-width="1.2"/>
|
||||
<text x="1030" y="751" text-anchor="middle" fill="#2563eb" font-size="10" font-weight="600">已发送</text>
|
||||
<rect x="1088" y="732" width="60" height="28" rx="14" fill="#ffffff" stroke="#16a34a" stroke-width="1.2"/>
|
||||
<text x="1118" y="751" text-anchor="middle" fill="#16a34a" font-size="10" font-weight="600">成功</text>
|
||||
<line x1="972" y1="746" x2="990" y2="746" stroke="#2563eb" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
|
||||
<line x1="1060" y1="746" x2="1078" y2="746" stroke="#16a34a" stroke-width="1.5" marker-end="url(#arrow-green)"/>
|
||||
<text x="1020" y="780" text-anchor="middle" fill="#6b7280" font-size="9">离线排队 → 上线重发 (5msg/s)</text>
|
||||
<text x="1020" y="795" text-anchor="middle" fill="#6b7280" font-size="9">超时重试 (3次) → 过期清理</text>
|
||||
<text x="1020" y="810" text-anchor="middle" fill="#6b7280" font-size="9">Shared 属性同步 + 持久化 RPC</text>
|
||||
<path d="M 580,665 L 580,685 L 1020,685 L 1020,690" stroke="#16a34a" stroke-width="1.5" fill="none" marker-end="url(#arrow-green)"/>
|
||||
<rect x="40" y="845" width="540" height="70" rx="6" fill="#fff7ed" fill-opacity="0.5" stroke="#fed7aa" stroke-width="1" stroke-dasharray="6,4"/>
|
||||
<text x="56" y="861" fill="#ea580c" font-size="10" font-weight="600" letter-spacing="0.06em">组织架构 ORGANIZATION HIERARCHY</text>
|
||||
<rect x="50" y="872" width="110" height="34" rx="6" fill="#ffffff" stroke="#ea580c" stroke-width="1.2"/>
|
||||
<text x="105" y="894" text-anchor="middle" fill="#ea580c" font-size="10" font-weight="600">租户 Tenant</text>
|
||||
<rect x="180" y="872" width="110" height="34" rx="6" fill="#ffffff" stroke="#ea580c" stroke-width="1.2"/>
|
||||
<text x="235" y="894" text-anchor="middle" fill="#ea580c" font-size="10" font-weight="600">项目 Project</text>
|
||||
<rect x="310" y="872" width="110" height="34" rx="6" fill="#ffffff" stroke="#ea580c" stroke-width="1.2"/>
|
||||
<text x="365" y="894" text-anchor="middle" fill="#ea580c" font-size="10" font-weight="600">子系统</text>
|
||||
<rect x="455" y="872" width="110" height="34" rx="6" fill="#ffffff" stroke="#ea580c" stroke-width="1.2"/>
|
||||
<text x="510" y="894" text-anchor="middle" fill="#ea580c" font-size="10" font-weight="600">设备 Device</text>
|
||||
<line x1="160" y1="889" x2="170" y2="889" stroke="#ea580c" stroke-width="1.5" marker-end="url(#arrow-orange)"/>
|
||||
<line x1="290" y1="889" x2="300" y2="889" stroke="#ea580c" stroke-width="1.5" marker-end="url(#arrow-orange)"/>
|
||||
<line x1="420" y1="889" x2="445" y2="889" stroke="#ea580c" stroke-width="1.5" marker-end="url(#arrow-orange)"/>
|
||||
<rect x="600" y="845" width="560" height="70" rx="6" fill="#eff6ff" fill-opacity="0.4" stroke="#bfdbfe" stroke-width="1" stroke-dasharray="6,4"/>
|
||||
<text x="616" y="861" fill="#2563eb" font-size="10" font-weight="600" letter-spacing="0.06em">应用层 APPLICATION LAYER</text>
|
||||
<rect x="645" y="872" width="110" height="34" rx="6" fill="#dbeafe" stroke="#2563eb" stroke-width="1.2"/>
|
||||
<text x="700" y="894" text-anchor="middle" fill="#2563eb" font-size="10" font-weight="600">管理后台 UI</text>
|
||||
<rect x="785" y="872" width="110" height="34" rx="6" fill="#dcfce7" stroke="#16a34a" stroke-width="1.2"/>
|
||||
<text x="840" y="894" text-anchor="middle" fill="#16a34a" font-size="10" font-weight="600">REST API</text>
|
||||
<rect x="925" y="872" width="110" height="34" rx="6" fill="#ede9fe" stroke="#9333ea" stroke-width="1.2"/>
|
||||
<text x="980" y="894" text-anchor="middle" fill="#9333ea" font-size="10" font-weight="600">WebSocket</text>
|
||||
<rect x="1045" y="872" width="110" height="34" rx="6" fill="#fed7aa" stroke="#ea580c" stroke-width="1.2"/>
|
||||
<text x="1100" y="894" text-anchor="middle" fill="#ea580c" font-size="10" font-weight="600">Feign 内部调用</text>
|
||||
<rect x="40" y="935" width="1120" height="130" rx="6" fill="#f3f4f6" fill-opacity="0.5" stroke="#d1d5db" stroke-width="1" stroke-dasharray="6,4"/>
|
||||
<text x="56" y="951" fill="#6b7280" font-size="10" font-weight="600" letter-spacing="0.06em">存储层 STORAGE LAYER</text>
|
||||
<ellipse cx="200" cy="968" rx="70" ry="14" fill="#dbeafe" stroke="#4479A1" stroke-width="1.5"/>
|
||||
<rect x="130" y="968" width="140" height="60" fill="#dbeafe" stroke="none"/>
|
||||
<line x1="130" y1="968" x2="130" y2="1028" stroke="#4479A1" stroke-width="1.5"/>
|
||||
<line x1="270" y1="968" x2="270" y2="1028" stroke="#4479A1" stroke-width="1.5"/>
|
||||
<ellipse cx="200" cy="1028" rx="70" ry="14" fill="#bfdbfe" stroke="#4479A1" stroke-width="1.5"/>
|
||||
<text x="200" y="994" text-anchor="middle" fill="#4479A1" font-size="13" font-weight="700">MySQL</text>
|
||||
<text x="200" y="1008" text-anchor="middle" fill="#6b7280" font-size="9">设备 | 规则 | 告警 | RPC</text>
|
||||
<text x="200" y="1020" text-anchor="middle" fill="#6b7280" font-size="9">产品 | 子系统 | Agent</text>
|
||||
<ellipse cx="480" cy="968" rx="70" ry="14" fill="#ede9fe" stroke="#9333ea" stroke-width="1.5"/>
|
||||
<rect x="410" y="968" width="140" height="60" fill="#ede9fe" stroke="none"/>
|
||||
<line x1="410" y1="968" x2="410" y2="1028" stroke="#9333ea" stroke-width="1.5"/>
|
||||
<line x1="550" y1="968" x2="550" y2="1028" stroke="#9333ea" stroke-width="1.5"/>
|
||||
<ellipse cx="480" cy="1028" rx="70" ry="14" fill="#e9d5ff" stroke="#9333ea" stroke-width="1.5"/>
|
||||
<text x="480" y="994" text-anchor="middle" fill="#9333ea" font-size="13" font-weight="700">TimeSeries DB</text>
|
||||
<text x="480" y="1008" text-anchor="middle" fill="#6b7280" font-size="9">CTSDB / TDengine</text>
|
||||
<text x="480" y="1020" text-anchor="middle" fill="#6b7280" font-size="9">设备消息 | 告警历史 | 调试日志</text>
|
||||
<ellipse cx="760" cy="968" rx="70" ry="14" fill="#fee2e2" stroke="#DC382D" stroke-width="1.5"/>
|
||||
<rect x="690" y="968" width="140" height="60" fill="#fee2e2" stroke="none"/>
|
||||
<line x1="690" y1="968" x2="690" y2="1028" stroke="#DC382D" stroke-width="1.5"/>
|
||||
<line x1="830" y1="968" x2="830" y2="1028" stroke="#DC382D" stroke-width="1.5"/>
|
||||
<ellipse cx="760" cy="1028" rx="70" ry="14" fill="#fecaca" stroke="#DC382D" stroke-width="1.5"/>
|
||||
<text x="760" y="994" text-anchor="middle" fill="#DC382D" font-size="13" font-weight="700">Redis</text>
|
||||
<text x="760" y="1008" text-anchor="middle" fill="#6b7280" font-size="9">CLIENT | SERVER | SHARED</text>
|
||||
<text x="760" y="1020" text-anchor="middle" fill="#6b7280" font-size="9">在线状态 | 规则缓存 | RPC 超时</text>
|
||||
<rect x="890" y="962" width="255" height="88" rx="8" fill="#ffffff" stroke="#d1d5db" stroke-width="1.2"/>
|
||||
<text x="1018" y="982" text-anchor="middle" fill="#111827" font-size="12" font-weight="600">可观测性 Observability</text>
|
||||
<text x="1018" y="998" text-anchor="middle" fill="#6b7280" font-size="10">Micrometer 指标采集</text>
|
||||
<text x="1018" y="1014" text-anchor="middle" fill="#6b7280" font-size="9">连接数 | 消息吞吐 | 规则执行耗时</text>
|
||||
<text x="1018" y="1030" text-anchor="middle" fill="#6b7280" font-size="9">告警延迟 | RPC 成功率 | 编解码错误</text>
|
||||
<rect x="40" y="1072" width="1120" height="2" fill="#e5e7eb"/>
|
||||
<g transform="translate(60, 1078)">
|
||||
<text x="0" y="10" fill="#111827" font-size="10" font-weight="600">图例:</text>
|
||||
<line x1="45" y1="6" x2="75" y2="6" stroke="#2563eb" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
|
||||
<text x="81" y="10" fill="#6b7280" font-size="9">数据上行</text>
|
||||
<line x1="140" y1="6" x2="170" y2="6" stroke="#16a34a" stroke-width="1.5" marker-end="url(#arrow-green)"/>
|
||||
<text x="176" y="10" fill="#6b7280" font-size="9">存储写入</text>
|
||||
<line x1="240" y1="6" x2="270" y2="6" stroke="#dc2626" stroke-width="1.5" marker-end="url(#arrow-red)"/>
|
||||
<text x="276" y="10" fill="#6b7280" font-size="9">告警流</text>
|
||||
<line x1="330" y1="6" x2="360" y2="6" stroke="#ea580c" stroke-width="1.5" marker-end="url(#arrow-orange)"/>
|
||||
<text x="366" y="10" fill="#6b7280" font-size="9">控制/组织</text>
|
||||
<line x1="430" y1="6" x2="460" y2="6" stroke="#9333ea" stroke-width="1.5" marker-end="url(#arrow-purple)"/>
|
||||
<text x="466" y="10" fill="#6b7280" font-size="9">规则引擎</text>
|
||||
<line x1="535" y1="6" x2="565" y2="6" stroke="#6b7280" stroke-width="1.2" stroke-dasharray="5,3"/>
|
||||
<text x="571" y="10" fill="#6b7280" font-size="9">异步/持久化</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 31 KiB |
481
开发者文档/03-IoT领域/升级设计方案/assets/端到端业务链路.drawio
Normal file
@@ -0,0 +1,481 @@
|
||||
<mxfile host="embed.diagrams.net">
|
||||
<diagram id="iyRB7K1gAj4k8ZFcetGC" name="第 1 页">
|
||||
<mxGraphModel dx="1247" dy="673" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="title" parent="1" style="text;html=1;fontSize=22;fontStyle=1;align=center;verticalAlign=middle;fillColor=none;strokeColor=none;fontColor=#1a1a2e;" value="IoT v2.0 端到端业务链路" vertex="1">
|
||||
<mxGeometry height="35" width="600" x="100" y="10" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="subtitle" parent="1" style="text;html=1;fontSize=11;align=center;verticalAlign=middle;fillColor=none;strokeColor=none;fontColor=#888888;" value="从设备上报到最终动作的完整数据流 | 时序库 CTSDB / TDengine 双实现可切换" vertex="1">
|
||||
<mxGeometry height="18" width="600" x="100" y="42" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="sec1" parent="1" style="text;html=1;fontSize=13;fontStyle=1;align=left;verticalAlign=middle;fillColor=#e8eaf6;strokeColor=none;fontColor=#3949ab;rounded=1;" value="① 设备接入层(Gateway)" vertex="1">
|
||||
<mxGeometry height="24" width="760" x="20" y="68" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dev_box" parent="1" style="rounded=1;arcSize=12;fillColor=#f5f5f5;strokeColor=#bdbdbd;dashed=1;" value="" vertex="1">
|
||||
<mxGeometry height="90" width="130" x="30" y="100" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dev_t" parent="1" style="text;html=1;fontSize=10;fontStyle=1;align=center;fillColor=none;strokeColor=none;fontColor=#616161;" value="物理设备" vertex="1">
|
||||
<mxGeometry height="16" width="80" x="55" y="103" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="d1" parent="1" style="rounded=1;fillColor=#78909c;strokeColor=#546e7a;fontColor=#fff;fontSize=9;" value="传感器" vertex="1">
|
||||
<mxGeometry height="22" width="54" x="38" y="123" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="d2" parent="1" style="rounded=1;fillColor=#78909c;strokeColor=#546e7a;fontColor=#fff;fontSize=9;" value="控制器" vertex="1">
|
||||
<mxGeometry height="22" width="54" x="98" y="123" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="d3" parent="1" style="rounded=1;fillColor=#78909c;strokeColor=#546e7a;fontColor=#fff;fontSize=9;" value="摄像头" vertex="1">
|
||||
<mxGeometry height="22" width="54" x="38" y="150" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="d4" parent="1" style="rounded=1;fillColor=#78909c;strokeColor=#546e7a;fontColor=#fff;fontSize=9;" value="工牌 ..." vertex="1">
|
||||
<mxGeometry height="22" width="54" x="98" y="150" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="e_dev_gw" edge="1" parent="1" source="dev_box" style="endArrow=classic;strokeColor=#5c6bc0;strokeWidth=2;exitX=1;exitY=0.5;entryX=0;entryY=0.35;" target="gw_box" value="">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="proto" parent="1" style="text;html=1;fontSize=8;align=center;fillColor=none;strokeColor=none;fontColor=#5c6bc0;fontStyle=2;" value="MQTT / HTTP / TCP
原始报文 →" vertex="1">
|
||||
<mxGeometry height="24" width="100" x="168" y="127" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="gw_box" parent="1" style="rounded=1;arcSize=8;fillColor=#e8eaf6;strokeColor=#5c6bc0;strokeWidth=2;" value="" vertex="1">
|
||||
<mxGeometry height="95" width="500" x="275" y="98" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="gw_t" parent="1" style="text;html=1;fontSize=11;fontStyle=1;align=left;fillColor=none;strokeColor=none;fontColor=#3949ab;" value="网关(Gateway)" vertex="1">
|
||||
<mxGeometry height="18" width="140" x="285" y="100" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="codec" parent="1" style="rounded=1;fillColor=#5c6bc0;strokeColor=#3949ab;fontColor=#fff;fontSize=10;fontStyle=1;" value="Codec SPI" vertex="1">
|
||||
<mxGeometry height="26" width="85" x="290" y="122" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="c1" parent="1" style="rounded=1;fillColor=#7986cb;strokeColor=#5c6bc0;fontColor=#fff;fontSize=9;" value="ALink" vertex="1">
|
||||
<mxGeometry height="22" width="60" x="385" y="122" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="c2" parent="1" style="rounded=1;fillColor=#7986cb;strokeColor=#5c6bc0;fontColor=#fff;fontSize=9;" value="JT808" vertex="1">
|
||||
<mxGeometry height="22" width="60" x="450" y="122" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="c3" parent="1" style="rounded=1;fillColor=#7986cb;strokeColor=#5c6bc0;fontColor=#fff;fontSize=9;" value="Camera3D11" vertex="1">
|
||||
<mxGeometry height="22" width="80" x="515" y="122" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="c4" parent="1" style="rounded=1;fillColor=#7986cb;strokeColor=#5c6bc0;fontColor=#fff;fontSize=9;" value="自定义 ..." vertex="1">
|
||||
<mxGeometry height="22" width="65" x="600" y="122" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="decode_arr" parent="1" style="text;html=1;fontSize=8;align=left;fillColor=none;strokeColor=none;fontColor=#5c6bc0;fontStyle=2;" value="解码 ↓" vertex="1">
|
||||
<mxGeometry height="14" width="50" x="290" y="148" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="msg_model" parent="1" style="rounded=1;fillColor=#3949ab;strokeColor=#283593;fontColor=#ffffff;fontSize=9;align=center;whiteSpace=wrap;" value="IotDeviceMessage(统一消息模型){ deviceKey, productKey, type, identifier, data, timestamp }" vertex="1">
|
||||
<mxGeometry height="24" width="475" x="290" y="162" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="sec2" parent="1" style="text;html=1;fontSize=13;fontStyle=1;align=left;verticalAlign=middle;fillColor=#fce4ec;strokeColor=none;fontColor=#c62828;rounded=1;" value="② 消息总线(Message Bus)— 三种实现按部署模式选择" vertex="1">
|
||||
<mxGeometry height="24" width="760" x="20" y="205" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="e_msg_bus" edge="1" parent="1" style="endArrow=classic;strokeColor=#e53935;strokeWidth=2;" value="">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="528" y="186" as="sourcePoint" />
|
||||
<mxPoint x="400" y="236" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="bus_box" parent="1" style="rounded=1;arcSize=12;fillColor=#ffebee;strokeColor=#e53935;strokeWidth=2;" value="" vertex="1">
|
||||
<mxGeometry height="36" width="640" x="80" y="235" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bus1" parent="1" style="rounded=1;fillColor=#e53935;strokeColor=#c62828;fontColor=#fff;fontSize=9;" value="LocalMessageBus(单机)" vertex="1">
|
||||
<mxGeometry height="24" width="175" x="95" y="241" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bus2" parent="1" style="rounded=1;fillColor=#e53935;strokeColor=#c62828;fontColor=#fff;fontSize=9;" value="RedisStreamMessageBus" vertex="1">
|
||||
<mxGeometry height="24" width="175" x="313" y="241" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="bus3" parent="1" style="rounded=1;fillColor=#e53935;strokeColor=#c62828;fontColor=#fff;fontSize=9;" value="RocketMQMessageBus" vertex="1">
|
||||
<mxGeometry height="24" width="175" x="531" y="241" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="sec3" parent="1" style="text;html=1;fontSize=13;fontStyle=1;align=left;verticalAlign=middle;fillColor=#e8f5e9;strokeColor=none;fontColor=#2e7d32;rounded=1;" value="③ 核心服务层(Core Services)" vertex="1">
|
||||
<mxGeometry height="24" width="760" x="20" y="283" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="e_bus_sub" edge="1" parent="1" style="endArrow=classic;strokeColor=#43a047;strokeWidth=2;" value="">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="400" y="271" as="sourcePoint" />
|
||||
<mxPoint x="400" y="314" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="subscriber" parent="1" style="rounded=1;fillColor=#2e7d32;strokeColor=#1b5e20;fontColor=#ffffff;fontSize=12;fontStyle=1;" value="IotDeviceMessageSubscriber(统一消息消费者)" vertex="1">
|
||||
<mxGeometry height="30" width="500" x="150" y="314" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b1" parent="1" style="rounded=1;arcSize=10;fillColor=#e8f5e9;strokeColor=#66bb6a;" value="" vertex="1">
|
||||
<mxGeometry height="105" width="148" x="20" y="362" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b1t" parent="1" style="text;html=1;fontSize=10;fontStyle=1;align=center;fillColor=#43a047;strokeColor=none;fontColor=#fff;rounded=1;" value="① 设备状态更新" vertex="1">
|
||||
<mxGeometry height="18" width="140" x="24" y="365" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b1a" parent="1" style="text;html=1;fontSize=8;align=left;fillColor=none;strokeColor=none;fontColor=#333;" value="在线/离线 → Redis 标记" vertex="1">
|
||||
<mxGeometry height="14" width="136" x="28" y="388" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b1b" parent="1" style="text;html=1;fontSize=8;align=left;fillColor=none;strokeColor=none;fontColor=#666;fontStyle=2;" value="上线触发:" vertex="1">
|
||||
<mxGeometry height="14" width="60" x="28" y="404" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b1c" parent="1" style="text;html=1;fontSize=7;align=left;fillColor=none;strokeColor=none;fontColor=#333;" value="· Shared 属性同步" vertex="1">
|
||||
<mxGeometry height="12" width="136" x="28" y="419" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b1d" parent="1" style="text;html=1;fontSize=7;align=left;fillColor=none;strokeColor=none;fontColor=#333;" value="· Pending RPC 补发(5条/秒)" vertex="1">
|
||||
<mxGeometry height="12" width="136" x="28" y="432" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b1ref" parent="1" style="text;html=1;fontSize=7;align=center;fillColor=#fff3e0;strokeColor=none;fontColor=#e65100;rounded=1;" value="← 06-设备影子" vertex="1">
|
||||
<mxGeometry height="14" width="80" x="44" y="448" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b2" parent="1" style="rounded=1;arcSize=10;fillColor=#e8f5e9;strokeColor=#66bb6a;" value="" vertex="1">
|
||||
<mxGeometry height="105" width="148" x="178" y="362" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b2t" parent="1" style="text;html=1;fontSize=10;fontStyle=1;align=center;fillColor=#43a047;strokeColor=none;fontColor=#fff;rounded=1;" value="② 属性三元分类写入" vertex="1">
|
||||
<mxGeometry height="18" width="140" x="182" y="365" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b2a" parent="1" style="text;html=1;fontSize=8;align=left;fillColor=none;strokeColor=none;fontColor=#333;" value="CLIENT → Redis :client" vertex="1">
|
||||
<mxGeometry height="14" width="136" x="186" y="390" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b2b" parent="1" style="text;html=1;fontSize=8;align=left;fillColor=none;strokeColor=none;fontColor=#333;" value="SERVER → Redis :server" vertex="1">
|
||||
<mxGeometry height="14" width="136" x="186" y="407" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b2c" parent="1" style="text;html=1;fontSize=8;align=left;fillColor=none;strokeColor=none;fontColor=#333;" value="SHARED → Redis + MySQL" vertex="1">
|
||||
<mxGeometry height="14" width="136" x="186" y="424" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b2ref" parent="1" style="text;html=1;fontSize=7;align=center;fillColor=#fff3e0;strokeColor=none;fontColor=#e65100;rounded=1;" value="← 03-物模型v2" vertex="1">
|
||||
<mxGeometry height="14" width="80" x="202" y="448" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b3" parent="1" style="rounded=1;arcSize=10;fillColor=#e8f5e9;strokeColor=#66bb6a;" value="" vertex="1">
|
||||
<mxGeometry height="105" width="148" x="336" y="362" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b3t" parent="1" style="text;html=1;fontSize=10;fontStyle=1;align=center;fillColor=#43a047;strokeColor=none;fontColor=#fff;rounded=1;" value="③ 时序数据持久化" vertex="1">
|
||||
<mxGeometry height="18" width="140" x="340" y="365" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b3a" parent="1" style="text;html=1;fontSize=9;align=center;fillColor=none;strokeColor=none;fontColor=#333;fontStyle=1;" value="PersistenceBuffer" vertex="1">
|
||||
<mxGeometry height="14" width="128" x="348" y="390" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b3b" parent="1" style="text;html=1;fontSize=8;align=center;fillColor=none;strokeColor=none;fontColor=#666;" value="内存队列 + 定时刷新" vertex="1">
|
||||
<mxGeometry height="14" width="128" x="348" y="406" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b3c" parent="1" style="text;html=1;fontSize=8;align=center;fillColor=none;strokeColor=none;fontColor=#43a047;fontStyle=2;" value="↓ 批量写入" vertex="1">
|
||||
<mxGeometry height="14" width="128" x="348" y="422" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b3d" parent="1" style="text;html=1;fontSize=8;align=center;fillColor=none;strokeColor=none;fontColor=#333;" value="CTSDB / TDengine(可切换)" vertex="1">
|
||||
<mxGeometry height="14" width="140" x="340" y="437" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b3ref" parent="1" style="text;html=1;fontSize=7;align=center;fillColor=#fff3e0;strokeColor=none;fontColor=#e65100;rounded=1;" value="← 07-数据存储" vertex="1">
|
||||
<mxGeometry height="12" width="80" x="370" y="453" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b4" parent="1" style="rounded=1;arcSize=10;fillColor=#e8f5e9;strokeColor=#66bb6a;" value="" vertex="1">
|
||||
<mxGeometry height="105" width="148" x="494" y="362" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b4t" parent="1" style="text;html=1;fontSize=10;fontStyle=1;align=center;fillColor=#43a047;strokeColor=none;fontColor=#fff;rounded=1;" value="④ 派生物模型合并" vertex="1">
|
||||
<mxGeometry height="18" width="140" x="498" y="365" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b4a" parent="1" style="text;html=1;fontSize=8;align=center;fillColor=none;strokeColor=none;fontColor=#333;" value="设备级覆盖产品定义" vertex="1">
|
||||
<mxGeometry height="14" width="128" x="506" y="392" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b4b" parent="1" style="text;html=1;fontSize=8;align=center;fillColor=none;strokeColor=none;fontColor=#666;fontStyle=2;" value="derive_metadata JSON" vertex="1">
|
||||
<mxGeometry height="14" width="128" x="506" y="410" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b4ref" parent="1" style="text;html=1;fontSize=7;align=center;fillColor=#fff3e0;strokeColor=none;fontColor=#e65100;rounded=1;" value="← 03-物模型v2" vertex="1">
|
||||
<mxGeometry height="14" width="80" x="528" y="432" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b5" parent="1" style="rounded=1;arcSize=10;fillColor=#fff3e0;strokeColor=#fb8c00;strokeWidth=2;" value="" vertex="1">
|
||||
<mxGeometry height="105" width="128" x="652" y="362" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b5t" parent="1" style="text;html=1;fontSize=10;fontStyle=1;align=center;fillColor=#fb8c00;strokeColor=none;fontColor=#fff;rounded=1;" value="⑤ 规则引擎触发" vertex="1">
|
||||
<mxGeometry height="18" width="120" x="656" y="365" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b5a" parent="1" style="text;html=1;fontSize=8;align=center;fillColor=none;strokeColor=none;fontColor=#333;" value="将消息转发到
规则引擎层处理" vertex="1">
|
||||
<mxGeometry height="28" width="108" x="664" y="390" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b5arr" parent="1" style="text;html=1;fontSize=18;fontStyle=1;align=center;fillColor=none;strokeColor=none;fontColor=#fb8c00;" value="↓" vertex="1">
|
||||
<mxGeometry height="24" width="50" x="690" y="420" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="b5ref" parent="1" style="text;html=1;fontSize=7;align=center;fillColor=#fff3e0;strokeColor=none;fontColor=#e65100;rounded=1;" value="← 04-规则引擎" vertex="1">
|
||||
<mxGeometry height="14" width="80" x="676" y="448" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="es1" edge="1" parent="1" source="subscriber" style="endArrow=classic;strokeColor=#43a047;strokeWidth=1.5;exitX=0.08;exitY=1;entryX=0.5;entryY=0;" target="b1" value="">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="es2" edge="1" parent="1" source="subscriber" style="endArrow=classic;strokeColor=#43a047;strokeWidth=1.5;exitX=0.24;exitY=1;entryX=0.5;entryY=0;" target="b2" value="">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="es3" edge="1" parent="1" source="subscriber" style="endArrow=classic;strokeColor=#43a047;strokeWidth=1.5;exitX=0.46;exitY=1;entryX=0.5;entryY=0;" target="b3" value="">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="es4" edge="1" parent="1" source="subscriber" style="endArrow=classic;strokeColor=#43a047;strokeWidth=1.5;exitX=0.68;exitY=1;entryX=0.5;entryY=0;" target="b4" value="">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="es5" edge="1" parent="1" source="subscriber" style="endArrow=classic;strokeColor=#fb8c00;strokeWidth=2;exitX=0.92;exitY=1;entryX=0.5;entryY=0;" target="b5" value="">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="sec4" parent="1" style="text;html=1;fontSize=13;fontStyle=1;align=left;verticalAlign=middle;fillColor=#fff3e0;strokeColor=none;fontColor=#e65100;rounded=1;" value="④ 规则引擎层(Rule Engine)— DAG 编排执行" vertex="1">
|
||||
<mxGeometry height="24" width="760" x="20" y="480" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="rule_handler" parent="1" style="rounded=1;fillColor=#e65100;strokeColor=#bf360c;fontColor=#ffffff;fontSize=11;fontStyle=1;" value="IotRuleEngineMessageHandler(统一入口)" vertex="1">
|
||||
<mxGeometry height="28" width="460" x="170" y="510" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="rule_note" parent="1" style="text;html=1;fontSize=8;align=center;fillColor=none;strokeColor=none;fontColor=#e65100;fontStyle=2;" value="按 subsystemId + productId + deviceId 匹配规则链(全量缓存)| 链级 try-catch 隔离" vertex="1">
|
||||
<mxGeometry height="14" width="560" x="120" y="540" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="eb5r" edge="1" parent="1" source="b5" style="endArrow=classic;strokeColor=#fb8c00;strokeWidth=2;exitX=0.5;exitY=1;entryX=0.8;entryY=0;" target="rule_handler" value="">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dag" parent="1" style="rounded=1;arcSize=6;fillColor=#fff8e1;strokeColor=#fb8c00;strokeWidth=2;" value="" vertex="1">
|
||||
<mxGeometry height="75" width="760" x="20" y="560" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dag_t" parent="1" style="text;html=1;fontSize=10;fontStyle=1;align=left;fillColor=none;strokeColor=none;fontColor=#e65100;" value="RuleChain DAG 执行管线" vertex="1">
|
||||
<mxGeometry height="16" width="200" x="30" y="563" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="p1" parent="1" style="rounded=1;fillColor=#ff8f00;strokeColor=#e65100;fontColor=#fff;fontSize=10;fontStyle=1;" value="Trigger
触发器" vertex="1">
|
||||
<mxGeometry height="40" width="85" x="35" y="585" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="pa1" parent="1" style="text;html=1;fontSize=14;fontStyle=1;align=center;fillColor=none;strokeColor=none;fontColor=#e65100;" value="→" vertex="1">
|
||||
<mxGeometry height="20" width="20" x="120" y="595" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="p2" parent="1" style="rounded=1;fillColor=#ff8f00;strokeColor=#e65100;fontColor=#fff;fontSize=10;fontStyle=1;" value="Enrich
数据富化" vertex="1">
|
||||
<mxGeometry height="40" width="90" x="140" y="585" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="pa2" parent="1" style="text;html=1;fontSize=14;fontStyle=1;align=center;fillColor=none;strokeColor=none;fontColor=#e65100;" value="→" vertex="1">
|
||||
<mxGeometry height="20" width="20" x="230" y="595" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="p3" parent="1" style="rounded=1;fillColor=#ff8f00;strokeColor=#e65100;fontColor=#fff;fontSize=10;fontStyle=1;" value="Condition
条件评估" vertex="1">
|
||||
<mxGeometry height="40" width="95" x="250" y="585" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="pa3" parent="1" style="text;html=1;fontSize=14;fontStyle=1;align=center;fillColor=none;strokeColor=none;fontColor=#e65100;" value="→" vertex="1">
|
||||
<mxGeometry height="20" width="20" x="345" y="595" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="p4" parent="1" style="rounded=1;fillColor=#ff8f00;strokeColor=#e65100;fontColor=#fff;fontSize=10;fontStyle=1;" value="ShakeLimit
抖动抑制" vertex="1">
|
||||
<mxGeometry height="40" width="95" x="365" y="585" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="pa4" parent="1" style="text;html=1;fontSize=14;fontStyle=1;align=center;fillColor=none;strokeColor=none;fontColor=#e65100;" value="→" vertex="1">
|
||||
<mxGeometry height="20" width="20" x="460" y="595" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="p5" parent="1" style="rhombus;fillColor=#ff8f00;strokeColor=#e65100;fontColor=#fff;fontSize=10;fontStyle=1;" value="Branch
分支" vertex="1">
|
||||
<mxGeometry height="46" width="90" x="480" y="582" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="pa5" parent="1" style="text;html=1;fontSize=14;fontStyle=1;align=center;fillColor=none;strokeColor=none;fontColor=#e65100;" value="→" vertex="1">
|
||||
<mxGeometry height="20" width="20" x="570" y="595" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="p6" parent="1" style="rounded=1;fillColor=#e65100;strokeColor=#bf360c;fontColor=#fff;fontSize=11;fontStyle=1;" value="Actions
动作执行" vertex="1">
|
||||
<mxGeometry height="40" width="100" x="590" y="585" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="sec5" parent="1" style="text;html=1;fontSize=13;fontStyle=1;align=left;verticalAlign=middle;fillColor=#fbe9e7;strokeColor=none;fontColor=#bf360c;rounded=1;" value="⑤ 动作层(Actions)— 6 大类别" vertex="1">
|
||||
<mxGeometry height="24" width="760" x="20" y="648" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ea_detail" edge="1" parent="1" style="endArrow=classic;strokeColor=#e65100;strokeWidth=2;" value="">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="640" y="625" as="sourcePoint" />
|
||||
<mxPoint x="400" y="648" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="a1" parent="1" style="rounded=1;arcSize=8;fillColor=#e3f2fd;strokeColor=#1e88e5;strokeWidth=1.5;" value="" vertex="1">
|
||||
<mxGeometry height="95" width="245" x="20" y="680" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a1t" parent="1" style="text;html=1;fontSize=10;fontStyle=1;align=center;fillColor=#1e88e5;strokeColor=none;fontColor=#fff;rounded=1;" value="设备控制" vertex="1">
|
||||
<mxGeometry height="17" width="235" x="25" y="683" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a1a" parent="1" style="text;html=1;fontSize=8;align=left;fillColor=none;strokeColor=none;fontColor=#333;" value="device_property_set — Shared 属性下发" vertex="1">
|
||||
<mxGeometry height="14" width="228" x="30" y="705" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a1b" parent="1" style="text;html=1;fontSize=8;align=left;fillColor=none;strokeColor=none;fontColor=#333;" value="device_service_invoke — 持久化 RPC 调用" vertex="1">
|
||||
<mxGeometry height="14" width="228" x="30" y="721" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a1c" parent="1" style="text;html=1;fontSize=7;align=center;fillColor=#e3f2fd;strokeColor=#90caf9;fontColor=#1565c0;rounded=1;" value="RPC 状态机: QUEUED → SENT → SUCCESS" vertex="1">
|
||||
<mxGeometry height="14" width="220" x="35" y="739" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a1d" parent="1" style="text;html=1;fontSize=7;align=center;fillColor=none;strokeColor=none;fontColor=#666;fontStyle=2;" value="设备离线 → 持久化等待 → 上线补发" vertex="1">
|
||||
<mxGeometry height="14" width="220" x="35" y="755" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a2" parent="1" style="rounded=1;arcSize=8;fillColor=#fce4ec;strokeColor=#e53935;strokeWidth=1.5;" value="" vertex="1">
|
||||
<mxGeometry height="95" width="245" x="275" y="680" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a2t" parent="1" style="text;html=1;fontSize=10;fontStyle=1;align=center;fillColor=#e53935;strokeColor=none;fontColor=#fff;rounded=1;" value="告警处理" vertex="1">
|
||||
<mxGeometry height="17" width="235" x="280" y="683" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a2a" parent="1" style="text;html=1;fontSize=8;align=left;fillColor=none;strokeColor=none;fontColor=#333;" value="alarm_trigger — 触发告警 (ACTIVE)" vertex="1">
|
||||
<mxGeometry height="14" width="228" x="283" y="705" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a2b" parent="1" style="text;html=1;fontSize=7;align=left;fillColor=none;strokeColor=none;fontColor=#666;" value=" upsert AlarmRecord (MySQL 幂等)" vertex="1">
|
||||
<mxGeometry height="12" width="228" x="283" y="720" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a2c" parent="1" style="text;html=1;fontSize=7;align=left;fillColor=none;strokeColor=none;fontColor=#666;" value=" append AlarmHistory (时序库)" vertex="1">
|
||||
<mxGeometry height="12" width="228" x="283" y="733" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a2d" parent="1" style="text;html=1;fontSize=7;align=left;fillColor=none;strokeColor=none;fontColor=#666;" value=" 告警传播(沿子系统层级向上)" vertex="1">
|
||||
<mxGeometry height="12" width="228" x="283" y="746" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a2e" parent="1" style="text;html=1;fontSize=8;align=left;fillColor=none;strokeColor=none;fontColor=#333;" value="alarm_clear — 清除告警 (CLEARED)" vertex="1">
|
||||
<mxGeometry height="12" width="228" x="283" y="760" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a3" parent="1" style="rounded=1;arcSize=8;fillColor=#e0f2f1;strokeColor=#00897b;strokeWidth=1.5;" value="" vertex="1">
|
||||
<mxGeometry height="95" width="250" x="530" y="680" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a3t" parent="1" style="text;html=1;fontSize=10;fontStyle=1;align=center;fillColor=#00897b;strokeColor=none;fontColor=#fff;rounded=1;" value="数据转发" vertex="1">
|
||||
<mxGeometry height="17" width="240" x="535" y="683" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a3a" parent="1" style="rounded=1;fillColor=#26a69a;strokeColor=#00897b;fontColor=#fff;fontSize=8;" value="http_push" vertex="1">
|
||||
<mxGeometry height="22" width="70" x="540" y="708" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a3b" parent="1" style="rounded=1;fillColor=#26a69a;strokeColor=#00897b;fontColor=#fff;fontSize=8;" value="mq_push" vertex="1">
|
||||
<mxGeometry height="22" width="70" x="618" y="708" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a3c" parent="1" style="rounded=1;fillColor=#26a69a;strokeColor=#00897b;fontColor=#fff;fontSize=8;" value="redis_push" vertex="1">
|
||||
<mxGeometry height="22" width="70" x="696" y="708" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a3d" parent="1" style="rounded=1;fillColor=#26a69a;strokeColor=#00897b;fontColor=#fff;fontSize=8;" value="tcp_push" vertex="1">
|
||||
<mxGeometry height="22" width="70" x="540" y="738" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a3e" parent="1" style="text;html=1;fontSize=7;align=left;fillColor=none;strokeColor=none;fontColor=#666;fontStyle=2;" value="Webhook / RocketMQ
Kafka / RabbitMQ" vertex="1">
|
||||
<mxGeometry height="20" width="150" x="618" y="740" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a4" parent="1" style="rounded=1;arcSize=8;fillColor=#f3e5f5;strokeColor=#8e24aa;strokeWidth=1.5;" value="" vertex="1">
|
||||
<mxGeometry height="68" width="245" x="20" y="785" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a4t" parent="1" style="text;html=1;fontSize=10;fontStyle=1;align=center;fillColor=#8e24aa;strokeColor=none;fontColor=#fff;rounded=1;" value="通知" vertex="1">
|
||||
<mxGeometry height="17" width="235" x="25" y="788" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a4a" parent="1" style="rounded=1;fillColor=#ab47bc;strokeColor=#8e24aa;fontColor=#fff;fontSize=9;" value="短信" vertex="1">
|
||||
<mxGeometry height="20" width="42" x="30" y="812" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a4b" parent="1" style="rounded=1;fillColor=#ab47bc;strokeColor=#8e24aa;fontColor=#fff;fontSize=9;" value="邮件" vertex="1">
|
||||
<mxGeometry height="20" width="42" x="78" y="812" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a4c" parent="1" style="rounded=1;fillColor=#ab47bc;strokeColor=#8e24aa;fontColor=#fff;fontSize=9;" value="站内信" vertex="1">
|
||||
<mxGeometry height="20" width="42" x="126" y="812" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a4d" parent="1" style="rounded=1;fillColor=#ab47bc;strokeColor=#8e24aa;fontColor=#fff;fontSize=9;" value="企微" vertex="1">
|
||||
<mxGeometry height="20" width="38" x="174" y="812" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a4e" parent="1" style="rounded=1;fillColor=#ab47bc;strokeColor=#8e24aa;fontColor=#fff;fontSize=9;" value="钉钉" vertex="1">
|
||||
<mxGeometry height="20" width="38" x="218" y="812" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a4f" parent="1" style="text;html=1;fontSize=7;align=center;fillColor=none;strokeColor=none;fontColor=#666;fontStyle=2;" value="模板变量: ${alarm.name} ${device.name}" vertex="1">
|
||||
<mxGeometry height="12" width="228" x="30" y="836" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a5" parent="1" style="rounded=1;arcSize=8;fillColor=#fff3e0;strokeColor=#fb8c00;strokeWidth=1.5;" value="" vertex="1">
|
||||
<mxGeometry height="68" width="245" x="275" y="785" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a5t" parent="1" style="text;html=1;fontSize=10;fontStyle=1;align=center;fillColor=#fb8c00;strokeColor=none;fontColor=#fff;rounded=1;" value="数据处理" vertex="1">
|
||||
<mxGeometry height="17" width="235" x="280" y="788" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a5a" parent="1" style="rounded=1;fillColor=#ffa726;strokeColor=#fb8c00;fontColor=#fff;fontSize=9;" value="script" vertex="1">
|
||||
<mxGeometry height="20" width="52" x="285" y="812" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a5b" parent="1" style="rounded=1;fillColor=#ffa726;strokeColor=#fb8c00;fontColor=#fff;fontSize=9;" value="enrich" vertex="1">
|
||||
<mxGeometry height="20" width="52" x="343" y="812" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a5c" parent="1" style="rounded=1;fillColor=#ffa726;strokeColor=#fb8c00;fontColor=#fff;fontSize=9;" value="delay" vertex="1">
|
||||
<mxGeometry height="20" width="52" x="401" y="812" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a5d" parent="1" style="rounded=1;fillColor=#ffa726;strokeColor=#fb8c00;fontColor=#fff;fontSize=9;" value="log" vertex="1">
|
||||
<mxGeometry height="20" width="52" x="459" y="812" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a5e" parent="1" style="text;html=1;fontSize=7;align=center;fillColor=none;strokeColor=none;fontColor=#666;fontStyle=2;" value="Aviator 沙箱: 超时3s / 循环上限1000 / 黑名单" vertex="1">
|
||||
<mxGeometry height="12" width="235" x="280" y="836" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a6" parent="1" style="rounded=1;arcSize=8;fillColor=#efebe9;strokeColor=#6d4c41;strokeWidth=1.5;dashed=1;dashPattern=6 3;" value="" vertex="1">
|
||||
<mxGeometry height="68" width="250" x="530" y="785" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a6t" parent="1" style="text;html=1;fontSize=10;fontStyle=1;align=center;fillColor=#6d4c41;strokeColor=none;fontColor=#fff;rounded=1;" value="Agent 协作" vertex="1">
|
||||
<mxGeometry height="17" width="240" x="535" y="788" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a6a" parent="1" style="text;html=1;fontSize=8;align=left;fillColor=none;strokeColor=none;fontColor=#333;" value="agent_request — ACP 协议" vertex="1">
|
||||
<mxGeometry height="14" width="230" x="540" y="812" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="a6b" parent="1" style="text;html=1;fontSize=7;align=left;fillColor=none;strokeColor=none;fontColor=#666;fontStyle=2;" value="异常检测 | 保洁质检 | 能耗分析 → 注入 metadata" vertex="1">
|
||||
<mxGeometry height="14" width="230" x="540" y="828" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="sec6" parent="1" style="text;html=1;fontSize=13;fontStyle=1;align=left;verticalAlign=middle;fillColor=#efebe9;strokeColor=none;fontColor=#4e342e;rounded=1;" value="⑥ 存储层(Storage)" vertex="1">
|
||||
<mxGeometry height="24" width="760" x="20" y="866" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="s_mysql" parent="1" style="rounded=1;arcSize=8;fillColor=#e3f2fd;strokeColor=#1e88e5;strokeWidth=1.5;" value="" vertex="1">
|
||||
<mxGeometry height="130" width="240" x="20" y="898" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="s_mysql_t" parent="1" style="text;html=1;fontSize=10;fontStyle=1;align=center;fillColor=#1e88e5;strokeColor=none;fontColor=#fff;rounded=1;" value="MySQL(业务实体)" vertex="1">
|
||||
<mxGeometry height="18" width="230" x="25" y="901" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="sm1" parent="1" style="text;html=1;fontSize=8;align=left;fillColor=none;strokeColor=none;fontColor=#333;" value="iot_subsystem(子系统)" vertex="1">
|
||||
<mxGeometry height="14" width="200" x="30" y="924" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="sm2" parent="1" style="text;html=1;fontSize=8;align=left;fillColor=none;strokeColor=none;fontColor=#333;" value="iot_device(+subsystem_id)" vertex="1">
|
||||
<mxGeometry height="14" width="200" x="30" y="940" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="sm3" parent="1" style="text;html=1;fontSize=8;align=left;fillColor=none;strokeColor=none;fontColor=#333;" value="iot_rule_chain / node / link" vertex="1">
|
||||
<mxGeometry height="14" width="200" x="30" y="956" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="sm4" parent="1" style="text;html=1;fontSize=8;align=left;fillColor=none;strokeColor=none;fontColor=#333;" value="iot_alarm_record" vertex="1">
|
||||
<mxGeometry height="14" width="200" x="30" y="972" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="sm5" parent="1" style="text;html=1;fontSize=8;align=left;fillColor=none;strokeColor=none;fontColor=#333;" value="iot_device_rpc / iot_agent_service" vertex="1">
|
||||
<mxGeometry height="14" width="200" x="30" y="988" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="sm6" parent="1" style="text;html=1;fontSize=8;align=left;fillColor=none;strokeColor=none;fontColor=#999;fontStyle=2;" value="iot_project(预留)" vertex="1">
|
||||
<mxGeometry height="14" width="200" x="30" y="1006" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="s_tsdb" parent="1" style="rounded=1;arcSize=8;fillColor=#e8f5e9;strokeColor=#43a047;strokeWidth=1.5;" value="" vertex="1">
|
||||
<mxGeometry height="130" width="260" x="270" y="898" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="s_tsdb_t" parent="1" style="text;html=1;fontSize=10;fontStyle=1;align=center;fillColor=#43a047;strokeColor=none;fontColor=#fff;rounded=1;" value="时序库(CTSDB / TDengine 双实现)" vertex="1">
|
||||
<mxGeometry height="18" width="250" x="275" y="901" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="st1" parent="1" style="text;html=1;fontSize=8;align=left;fillColor=none;strokeColor=none;fontColor=#333;" value="device_message(消息日志·90天)" vertex="1">
|
||||
<mxGeometry height="14" width="240" x="280" y="926" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="st2" parent="1" style="text;html=1;fontSize=8;align=left;fillColor=none;strokeColor=none;fontColor=#333;" value="product_property_*(属性时序·365天)" vertex="1">
|
||||
<mxGeometry height="14" width="240" x="280" y="943" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="st3" parent="1" style="text;html=1;fontSize=8;align=left;fillColor=none;strokeColor=none;fontColor=#333;" value="alarm_history(告警归档·365天)" vertex="1">
|
||||
<mxGeometry height="14" width="240" x="280" y="960" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="st4" parent="1" style="text;html=1;fontSize=8;align=left;fillColor=none;strokeColor=none;fontColor=#333;" value="rule_debug_log(调试日志·7天)" vertex="1">
|
||||
<mxGeometry height="14" width="240" x="280" y="977" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="st_dao" parent="1" style="text;html=1;fontSize=8;align=center;fillColor=#c8e6c9;strokeColor=#a5d6a7;fontColor=#2e7d32;rounded=1;fontStyle=1;" value="DAO 接口双实现 · 统一数据模型" vertex="1">
|
||||
<mxGeometry height="14" width="240" x="285" y="996" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="st_switch" parent="1" style="text;html=1;fontSize=7;align=center;fillColor=none;strokeColor=none;fontColor=#2e7d32;fontStyle=2;" value="viewsh.iot.tsdb.type: ctsdb (InfluxDB) / tdengine (JDBC)" vertex="1">
|
||||
<mxGeometry height="12" width="240" x="285" y="1012" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="s_redis" parent="1" style="rounded=1;arcSize=8;fillColor=#fce4ec;strokeColor=#e53935;strokeWidth=1.5;" value="" vertex="1">
|
||||
<mxGeometry height="130" width="240" x="540" y="898" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="s_redis_t" parent="1" style="text;html=1;fontSize=10;fontStyle=1;align=center;fillColor=#e53935;strokeColor=none;fontColor=#fff;rounded=1;" value="Redis(缓存 / 状态)" vertex="1">
|
||||
<mxGeometry height="18" width="230" x="545" y="901" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="sr1" parent="1" style="text;html=1;fontSize=7;align=left;fillColor=none;strokeColor=none;fontColor=#333;" value="iot:device_property:{did}:client/server/shared" vertex="1">
|
||||
<mxGeometry height="14" width="225" x="550" y="926" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="sr2" parent="1" style="text;html=1;fontSize=8;align=left;fillColor=none;strokeColor=none;fontColor=#333;" value="设备在线状态标记" vertex="1">
|
||||
<mxGeometry height="14" width="200" x="550" y="943" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="sr3" parent="1" style="text;html=1;fontSize=8;align=left;fillColor=none;strokeColor=none;fontColor=#333;" value="iot:alarm:cache / lock:{recordId}" vertex="1">
|
||||
<mxGeometry height="14" width="225" x="550" y="960" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="sr4" parent="1" style="text;html=1;fontSize=8;align=left;fillColor=none;strokeColor=none;fontColor=#333;" value="规则链全量缓存" vertex="1">
|
||||
<mxGeometry height="14" width="200" x="550" y="977" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="sr5" parent="1" style="text;html=1;fontSize=8;align=left;fillColor=none;strokeColor=none;fontColor=#333;" value="iot:rpc:pending:{deviceId}(RPC 队列)" vertex="1">
|
||||
<mxGeometry height="14" width="225" x="550" y="994" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="sr6" parent="1" style="text;html=1;fontSize=7;align=left;fillColor=none;strokeColor=none;fontColor=#999;fontStyle=2;" value="SCAN 遍历替代 keys() 全量扫描" vertex="1">
|
||||
<mxGeometry height="12" width="225" x="550" y="1012" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="buf_box" parent="1" style="rounded=1;arcSize=8;fillColor=#f1f8e9;strokeColor=#7cb342;strokeWidth=1;dashed=1;" value="" vertex="1">
|
||||
<mxGeometry height="48" width="760" x="20" y="1038" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="buf_t" parent="1" style="text;html=1;fontSize=8;fontStyle=1;align=left;fillColor=none;strokeColor=none;fontColor=#558b2f;" value="PersistenceBuffer 写入缓冲(TDengine 侧需补齐,CTSDB 已内置异步批量 1000条/批+1s刷新)" vertex="1">
|
||||
<mxGeometry height="14" width="740" x="30" y="1042" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="buf1" parent="1" style="rounded=1;fillColor=#aed581;strokeColor=#7cb342;fontColor=#33691e;fontSize=8;" value="属性写入 200条/3s" vertex="1">
|
||||
<mxGeometry height="20" width="160" x="40" y="1060" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="buf2" parent="1" style="rounded=1;fillColor=#aed581;strokeColor=#7cb342;fontColor=#33691e;fontSize=8;" value="消息日志 500条/5s" vertex="1">
|
||||
<mxGeometry height="20" width="160" x="215" y="1060" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="buf3" parent="1" style="rounded=1;fillColor=#aed581;strokeColor=#7cb342;fontColor=#33691e;fontSize=8;" value="告警历史 100条/3s" vertex="1">
|
||||
<mxGeometry height="20" width="160" x="390" y="1060" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="buf4" parent="1" style="rounded=1;fillColor=#aed581;strokeColor=#7cb342;fontColor=#33691e;fontSize=8;" value="调试日志 100条/5s" vertex="1">
|
||||
<mxGeometry height="20" width="160" x="565" y="1060" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="ver" parent="1" style="text;html=1;fontSize=8;align=right;fillColor=none;strokeColor=none;fontColor=#bdbdbd;" value="IoT Module v2.0 | 时序库 CTSDB/TDengine 双实现可切换" vertex="1">
|
||||
<mxGeometry height="14" width="320" x="460" y="1092" as="geometry" />
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||