[{"data":1,"prerenderedAt":247},["ShallowReactive",2],{"work-\u002Fworks\u002Fhaiyan-io-work":3,"work-surround-\u002Fworks\u002Fhaiyan-io-work":238},{"id":4,"title":5,"body":6,"cover":217,"date":218,"demo":219,"description":220,"draft":221,"extension":222,"featured":223,"meta":224,"navigation":223,"path":225,"repo":219,"seo":226,"stack":227,"stem":236,"updated":219,"__hash__":237},"works\u002Fworks\u002Fhaiyan-io-work.md","海燕边缘网关 haiyan-io",{"type":7,"value":8,"toc":205},"minimark",[9,17,20,27,32,35,42,49,52,59,69,72,83,89,93,104,107,111,118,121,124,134,141,144,147,158,161,167,170,173,187,190,193,198,201],[10,11,12,16],"p",{},[13,14,15],"code",{},"haiyan-io"," 是一套面向智能硬件与工业现场的边缘网关。它接住 Modbus、OPC UA、私有 TCP\u002F串口和 MQTT 直连设备，把不同的地址、帧格式与采集方式翻译成统一物模型，再通过 MQTT、REST、WebSocket 和 gRPC 向上层系统提供一致的数据与指令接口。",[10,18,19],{},"我不想把它做成一个只完成“协议 A 转协议 B”的转发程序。真实网关还要面对断网、设备掉线、慢消费者、配置变更、告警联动、远程升级和多台网关运维。这个项目交付的是一套可以独立运行在现场的边缘执行环境。",[10,21,22],{},[23,24],"img",{"alt":25,"src":26},"haiyan-io 边缘网关总体架构","\u002Fimages\u002Fworks\u002Fhaiyan-io\u002Fsystem-overview.svg",[28,29,31],"h2",{"id":30},"先统一设备再讨论协议","先统一设备，再讨论协议",[10,33,34],{},"核心模型只有三层：设备、采集组和点位。采集组拥有独立周期，点位描述数据类型、比例变换、质量戳、单位与是否可写。驱动只负责把现场协议转换成这套模型，不需要知道数据最终会走 MQTT、REST 还是其他北向通道。",[10,36,37,38,41],{},"南向驱动使用静态 trait 插件：每类协议实现工厂与连接器，",[13,39,40],{},"hyd"," 再通过 Cargo feature 决定编入哪些驱动。当前已实现模拟器、Modbus TCP\u002FRTU、OPC UA、MQTT 设备直连，以及可复用的私有 TCP\u002F串口协议框架。",[10,43,44,45,48],{},"这里刻意没有采用 Rust 动态库插件。边缘设备更看重可预测部署和故障边界；编译期注册能换来单二进制交付、类型安全和按 feature 裁剪体积。私有请求应答协议也不必从头写完整驱动：实现 ",[13,46,47],{},"FrameCodec"," 的请求编码、帧定界、响应解码与写入编码即可，传输、缓冲、超时、重连和调度由公共框架接管。",[28,50,51],{"id":51},"一条数据如何穿过网关",[10,53,54,55,58],{},"轮询型设备由独立 Tokio 任务持有连接，并按采集组最近到期时间调度；MQTT 直连等主动上报设备则切换到事件推送模式。两种形态最后都会进入同一个 ",[13,56,57],{},"Core::ingest","：",[60,61,67],"pre",{"className":62,"code":64,"language":65,"meta":66},[63],"language-text","现场设备\n  -> 南向驱动（轮询或事件推送）\n  -> Core::ingest\n     -> 设备影子：REST \u002F WebSocket 实时查询\n     -> 有界消息总线\n        -> 规则引擎：告警、恢复与联动控制\n        -> 持久队列：MQTT 可靠上云\n        -> gRPC \u002F WebSocket 实时流\n","text","",[13,68,64],{"__ignoreMap":66},[10,70,71],{},"每台设备由自己的监督器管理。连接失败按 1 秒到 60 秒指数退避，连续采集失败触发重连，任务 panic 只重建当前设备。Modbus RTU 多从站可以共享 RS485 总线，但访问仍保持串行，避免请求应答协议被并发写乱。",[10,73,74,75,78,79,82],{},"下行指令走相反方向：北向请求进入统一 ",[13,76,77],{},"CommandRouter","，路由到目标设备任务，由驱动执行写点位或协议动作，再沿原通道返回带 ",[13,80,81],{},"request_id"," 的结果。超时会得到明确错误应答，业务系统不需要猜测指令是否仍在执行。",[10,84,85],{},[23,86],{"alt":87,"src":88},"haiyan-io 遥测、规则与指令闭环","\u002Fimages\u002Fworks\u002Fhaiyan-io\u002Fedge-data-flow.svg",[28,90,92],{"id":91},"断网时数据先留在现场","断网时，数据先留在现场",[10,94,95,96,99,100,103],{},"MQTT 上云采用 QoS 1，但 QoS 本身不足以解决进程重启和长时间离线。遥测进入北向桥接前先写入 redb 持久队列，发布后直到收到对应 PubAck 才删除；Broker 恢复后，积压数据按序继续发送。每条消息携带单调递增的 ",[13,97,98],{},"seq","，云端可以按 ",[13,101,102],{},"(gateway_id, seq)"," 做幂等去重。",[10,105,106],{},"这套语义是“至少一次”，不是“绝不重复”。磁盘队列有容量上限，空间耗尽时优先保留新数据并丢弃最旧项。进程内的广播与命令通道同样有界：慢 WebSocket 客户端只影响自己的订阅，不会让内存无限增长。",[28,108,110],{"id":109},"云断了本地规则仍然工作","云断了，本地规则仍然工作",[10,112,113,114,117],{},"规则引擎支持上下限、回差、连续采样确认和滑动窗口 ",[13,115,116],{},"avg\u002Fmin\u002Fmax"," 聚合，也可以用通配设备匹配同类点位。告警触发与恢复事件会进入 MQTT、WebSocket、gRPC 和 REST；活动告警与历史台账写入 redb，重启后仍可恢复，并能记录确认人。",[10,119,120],{},"规则触发或恢复时还能调用统一指令路由写回设备。温度越界后关闭加热器、液位恢复后重新启泵，不必等待云端往返；即使外网中断，网关仍能在本地完成采集、判断和控制闭环。",[28,122,123],{"id":123},"把管理能力装进同一个二进制",[10,125,126,127,130,131,133],{},"网关内置 Vue 3 管理界面，release 构建时通过 ",[13,128,129],{},"rust-embed"," 打进 Rust 二进制。部署现场只需要 ",[13,132,40],{},"、一份 TOML 配置和数据目录，不必额外安装 Node 或单独维护 Web 服务。",[10,135,136,137,140],{},"界面覆盖设备状态、实时点位、写指令、告警台账、规则与设备配置、配置备份、口令修改和 OTA。服务端逐请求强制权限，管理员可以变更配置，viewer 只能查询与订阅。登录使用 JWT，口令以 argon2id 哈希保存；脚本集成可以使用独立机器令牌，敏感字段支持 ",[13,138,139],{},"${VAR}"," 展开，REST 与 MQTT 管理通道记录结构化审计日志。",[10,142,143],{},"配置更新也不等同于整机重启。管理面写回 TOML 时会尽量保留手写注释，先完整校验再替换文件；热重载按设备做增量 diff，未变化的采集任务保持运行，错误配置不会落盘。",[28,145,146],{"id":146},"从单台网关走向一组网关",[10,148,149,150,153,154,157],{},"现场设备常在 NAT 后，中心侧无法直接访问每台网关。",[13,151,152],{},"hy-console"," 和 ",[13,155,156],{},"gw-admin"," 复用 MQTT 管理通道完成网关发现、分组、规则下发、远程重启和 OTA 推送。升级包按块传输，网关组装后校验 SHA-256 与 ed25519 签名，再进入候选槽位。",[10,159,160],{},"网关本地采用 A\u002FB 槽位和独立启动器。新版本只有稳定运行达到确认时间才会晋升；此前反复崩溃，启动器回到上一版本。控制台还能按波次灰度升级，等待前一批网关重新上线并通过恢复门控后再继续，失败超过阈值则终止后续批次。",[10,162,163],{},[23,164],{"alt":165,"src":166},"haiyan-io 网关群控与 OTA 拓扑","\u002Fimages\u002Fworks\u002Fhaiyan-io\u002Ffleet-ota-topology.svg",[28,168,169],{"id":169},"自动化验证",[10,171,172],{},"整理这篇项目时，我执行了完整 workspace 测试：",[60,174,178],{"className":175,"code":176,"language":177,"meta":66,"style":66},"language-powershell shiki shiki-themes tokyo-night github-light","cargo test --workspace --locked\n","powershell",[13,179,180],{"__ignoreMap":66},[181,182,185],"span",{"class":183,"line":184},"line",1,[181,186,176],{},[10,188,189],{},"共 115 项测试通过。除了模型、配置和协议序列化等单元测试，还包含真实 TCP 私有协议往返、Modbus 位读改写、MQTT 设备全链路、OPC UA 读写与加密会话、gRPC 查询\u002F指令\u002F流、REST 与 WebSocket 鉴权、MQTT 管理通道、持久队列、告警恢复以及 OTA 包和槽位状态机。",[28,191,192],{"id":192},"这个项目解决了什么",[10,194,195,197],{},[13,196,15],{}," 最有价值的地方不是支持了多少协议，而是把协议接入之后的工程问题放在同一套边界里处理：数据怎样统一、断网怎样保存、故障怎样隔离、规则怎样在本地闭环、配置怎样不停机生效、升级失败怎样回来，以及几十台网关怎样被同一处管理。",[10,199,200],{},"它既是一台边缘设备的完整运行时，也是承载现场协议、硬件与云平台连接的统一基础。比起再写一个能读到寄存器的 Demo，我更在意它在网络不可靠、设备会掉线、配置会出错的现实条件下，仍然知道自己该做什么。",[202,203,204],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}",{"title":66,"searchDepth":206,"depth":206,"links":207},3,[208,210,211,212,213,214,215,216],{"id":30,"depth":209,"text":31},2,{"id":51,"depth":209,"text":51},{"id":91,"depth":209,"text":92},{"id":109,"depth":209,"text":110},{"id":123,"depth":209,"text":123},{"id":146,"depth":209,"text":146},{"id":169,"depth":209,"text":169},{"id":192,"depth":209,"text":192},"\u002Fimages\u002Fworks\u002Fhaiyan-io\u002Fproject-cover.svg","2026-06-07",null,"基于 Rust 的智能硬件边缘网关，把多种现场协议收敛为统一物模型，并提供可靠上云、本地闭环、Web 运维与网关群控。",false,"md",true,{},"\u002Fworks\u002Fhaiyan-io-work",{"title":5,"description":220},[228,229,230,231,232,233,234,235],"Rust","Tokio","Axum","MQTT","Modbus","OPC UA","gRPC","Vue 3","works\u002Fhaiyan-io-work","WzvYSbHSLhdx15emcMcmsJL5yvWNbZ9TVlDjPO2L4Mo",[239,243],{"title":240,"path":241,"stem":242,"children":-1},"Video Stream - Rust 视频流服务","\u002Fworks\u002Fvideo-stream","works\u002Fvideo-stream",{"title":244,"path":245,"stem":246,"children":-1},"Nexus ID - 多租户身份与 OIDC 平台","\u002Fworks\u002Fauth-one","works\u002Fauth-one",1787046421621]