当前位置: 首页 > news >正文

【物联网控制体系项目实战】—— 整体架构流程与 WS 实现

文章目录

  • 前言
    • 背景
  • 一、读者定位与你能学到什么
  • 二、术语表
  • 三、核心角色与标识
  • 四、架构与时序图(核心要点)
    • 4.1 架构图(1/2):控制面(扫码 → 设备连流)
    • 4.2 架构图(2/2):数据面(前端直连流服务SDK订阅)
    • 4.3 端到端时序
  • 五、WebSocket 设计
    • 5.1 关键代码示例与参数说明(通用化示例)
    • 5.2 参数解释:
    • 5.3 常见事件与处理:
    • 5.4 为什么是“WS + HTTP”的双通道
    • 5.5 WS实际场景案例:
      • 5.5.1 实时语言转文字
      • 5.5.2 实时任务执行时间计算(准确 + 流畅)
      • 5.6 断网情况分析
  • 六、错误处理与容灾
  • 七、安全与鉴权
  • 八、工程化与最佳实践
  • 九、非技术叙事:用户视角
  • 总结


前言

这是一份面向“产品、研发、测试、运维”的通用 IoT 控制架构说明,旨在帮助非实时音视频/物联网背景的同学也能快速理解“扫码绑定 → 任务控制 → 实时数据展示”的端到端路径。文中所有域名、路径、字段均为通用化示例,落地请以实际环境为准。

背景

  • 用户使用手机扫码进入“控制器”页面,服务端将“手机会话与设备会话”绑定到同一房间/通道。

  • 前端通过 WebSocket 接收设备侧变更通知,通过 HTTP 发起任务指令(如开启采集/录制/转写)。

  • 终端设备连接流媒体/能力服务执行任务,成功后通知服务端。

  • 前端刷新任务信息可选择进入会中控制与展示(实时发言人语音转文字)。

物联网(IoT)系统本质上是传统前后端通信模式在终端场景的延伸


一、读者定位与你能学到什么

  • 产品/项目:能快速画出“控制面/数据面”的核心链路,理解关键 ID 的作用与安全边界。
  • 前端:能复用 WS 设计与时间同步策略,掌握实时转写 UI 的正确数据模型。
  • 后端:能清楚网关/任务路由/票据发放在整体中的位置与幂等设计。
  • 运维/测试:能据此制定健康检查、重连策略、告警与压测模型。

二、术语表

术语含义
token扫码后颁发的短期令牌,鉴权前端与 WS。
spaceId房间/通道标识,将“手机与设备”聚合在同一广播域。
mac设备唯一标识,用于单点指令路由与事件过滤。
clientID前端实例标识(每标签页/实例唯一),便于并发会话定位。
taskId一次任务(如一次会议、一次转写任务)的唯一标识。
流服务(Stream Service)提供“实时音频→文本”的处理与订阅能力。
通知网关(Notify/WS)面向前端的统一消息路由/广播能力。

三、核心角色与标识

角色与标识含义
前端(手机 Web)扫码进入控制器页,承载控制、展示、长连接。
设备(终端)执行能力(采集、录制、转写、推/订阅流等)。
服务端会话绑定、任务路由(业务服务器将前端发起的任务请求准确转发到目标终端设备)、WS 广播、token票据发放。
spaceId(房间)用于 WS 频道广播,将“手机与终端设备”绑定到同一房间。
mac(设备唯一标识)用于单点通知/精确路由到设备(在同一 spaceId 房间内可能有多台设备,mac 用于“单点路由与过滤”)。
taskId(任务Id)一次任务的唯一标识。
clientID(每个标签页面生成唯一ID)用户级会话的精准定位器。

四、架构与时序图(核心要点)

4.1 架构图(1/2):控制面(扫码 → 设备连流)

请添加图片描述

4.2 架构图(2/2):数据面(前端直连流服务SDK订阅)

请添加图片描述

4.3 端到端时序

请添加图片描述

五、WebSocket 设计

  • 连接:wss://<host>/ws/notify?bizType=iot&platform=web&token=<token>&clientID=<uuid>
  • 建链后订阅:{ topic: 'room', tags: ['spaceId:<spaceId>'] }
  • 心跳:每 ~120-150s 发送 /heartbeat
  • 重连:最多 N 次退避重连(固定 3 秒间隔——简单退避);重连后续订
  • 推送约定:
    • /app/setting/change:房间配置/任务变更(客户端应“拉列表”同步)
    • /task/create/fail:创建任务失败

发送封装(示例):

class WSClient {constructor(url: string) { /* ... */ }sendRequest(url: string, body?: any) {this.socket.send(JSON.stringify({ id: uuid(), url, body: body || {} }));}
}

5.1 关键代码示例与参数说明(通用化示例)

import { v4 as uuid } from 'uuid';type MessageHandler = (msg: any, raw: MessageEvent) => void;export class NotifyWS {private socket!: WebSocket;private url: string;public onmessage?: MessageHandler;public onopen?: () => void;// 心跳与重连private heartbeatTimer: any = null;private reconnectAttempts = 0;private reconnectLock = false;constructor(params: {host: string;token: string;           // 服务端下发的短期票据bizType?: string;        // 业务类型(例:iot)platform?: string;       // 客户端平台(例:web)clientID?: string;       // 客户端唯一标识}) {const { host, token, bizType = 'iot', platform = 'web', clientID = uuid().replace(/-/g, '') } = params;const query = new URLSearchParams({ bizType, platform, token, clientID });this.url = `wss://${host}/ws/notify?${query.toString()}`;this.init();}private init() {this.socket = new WebSocket(this.url);this.socket.onopen = () => {this.onopen?.();this.startHeartbeat();};this.socket.onmessage = (evt: MessageEvent) => {const data = typeof evt.data === 'string' ? JSON.parse(evt.data) : evt.data;this.onmessage?.(data, evt);};this.socket.onerror = () => {this.tryReconnect();};this.socket.onclose = () => {this.stopHeartbeat();};}// 订阅频道(示例:房间广播)subscribeBySpace(spaceId: string) {this.send('/notify/api/v1/subscribe', {channels: [{ topic: 'room', tags: [`spaceId:${spaceId}`] }],});}// 心跳保活(120~150s 之间皆可)private startHeartbeat() {this.heartbeatTimer && clearInterval(this.heartbeatTimer);this.heartbeatTimer = setInterval(() => {if (this.socket?.readyState === WebSocket.OPEN) {this.send('/notify/api/v1/heartbeat');}}, 135 * 1000);}private stopHeartbeat() {if (this.heartbeatTimer) {clearInterval(this.heartbeatTimer);this.heartbeatTimer = null;}}// 退避重连(简单示例)private tryReconnect() {if (this.reconnectLock) return;this.reconnectAttempts += 1;if (this.reconnectAttempts > 3) {// 放弃重连,交由外层兜底return;}this.reconnectLock = true;setTimeout(() => {this.init();this.reconnectLock = false;}, 3000);}// 发送统一封装send(url: string, body?: any) {const msg = { id: uuid(), url, body: body || {} };this.socket?.send(JSON.stringify(msg));}// 主动断开close() {this.send('/notify/api/v1/disconnect');this.stopHeartbeat();this.socket?.close();}
}

5.2 参数解释:

  • bizType:业务线标识,网关可按此做路由/隔离。
  • platform:客户端平台标识,便于统计与策略(如心跳频率)。
  • token:服务端下发的短期令牌,用于 WS 鉴权。
  • clientID:客户端唯一标识,用于定位端与并发会话控制。
  • topic:订阅主题(如 room 表示房间维度消息)。
  • tags:订阅标签(如 spaceId:<id> 将手机与设备拉入同一房间)。

5.3 常见事件与处理:

  • /app/setting/change:收到后调用 GET /controller/task/list?mac=... 拉取权威任务态;
  • /task/create/fail:展示错误并重置 UI;
  • 其他业务事件可扩展:如配置推送、设备在线状态、能力告警等。

5.4 为什么是“WS + HTTP”的双通道

  • WS:用于“事件驱动”,例如任务状态更新、配置变更、失败通知;延迟低、无需轮询。
  • HTTP:用于“权威读取/指令下发”,例如任务创建/结束、刷新任务列表;具备幂等、鉴权、审计优势。
  • 组合优势:WS 触发 + HTTP 拉取权威数据,既实时又可控,便于审计与回放。

HTTP API(通用契约草案)

  • h5扫码授权与会话绑定

    • GET /controller/space/auth?projectionCode=...
    • Resp: { token: string, spaceId: string, mac: string }
  • 任务控制

    • POST /controller/task/create → Body: { taskType, config, organizerId, mac }
    • POST /controller/task/end → Body: { taskId, mac }
    • GET /controller/task/list?mac=...
  • 流媒体/能力接入

    • GET /stream/address{ address, forwardAddress }
    • GET /stream/token?taskId=...{ token, streamInfo, isOrganizer, isLogin }

说明:实际路径与字段以后端为准;建议所有接口支持 Header token (token 来源二维码扫码获取)认证。

5.5 WS实际场景案例:

5.5.1 实时语言转文字

  1. 接收信息:系统持续接收ASR数据流

  2. 判断句子状态:每条数据包含 sentenceEnd 和 finalResult 字段

  3. 两种处理模式:

  • 句子未结束:按 sid 重复替换更新(同一行实时更新)
  • 句子已结束:锁定显示,开始新的一行

关键技术点:

  • sid作为唯一标识:相同sid = 同一句话的不同版本

  • 去重替换机制:_asrDataMap[_asr.sid] = _asr 实现覆盖

  • 状态区分:finalResult 控制UI样式(实时vs最终)

  • 句子边界:sentenceEnd 决定是否开始新行

5.5.2 实时任务执行时间计算(准确 + 流畅)

混合算法:

// 服务端周期性推送服务端的权威累计时长 time(ms)
statusTime = timeFromServer;
lastSyncTs = Date.now(); // 本地每秒显示:
liveTime = statusTime + (Date.now() - lastSyncTs); // 服务端推送时长 + 流逝时间(当前时间点 - 推送时长的时间点)

策略:

  • running:启 1s 定时器刷新;
  • paused:停止定时器、保持静态;
  • 收到新状态时重置 statusTime/lastSyncTs,自动校准误差。

优势:以服务器为准保证准确性;本地叠加保证显示流畅;断线后收到新状态即可校准。

5.6 断网情况分析

特征服务端主动断开网络彻底断开
触发速度即时(毫秒级)延迟(依赖心跳超时)
错误信息含关闭码(如1000,1006)无任何错误回调
重连策略立即重连指数退避(2s,4s,8s…)
UI提示“服务端升级中,请稍候”“网络不稳定,正在尝试重连…”
检测方式onclose事件心跳超时

六、错误处理与容灾

  • WS:断线自动重连 + 续订;心跳保活。
  • 创建失败:收到 /task/create/fail 时,提示并重置 UI。
  • 录制/能力异常:状态进入 reiniting 时提示重连,禁用操作按钮,恢复后解禁。
  • Token 失效:本地 checkToken() 拦截,展示错误页,停止后续动作。

七、安全与鉴权

  • 扫码后的 token 作为短期票据,仅用在控制器/WS/任务接口。
  • node 层可提供“白名单接口跳过 Cookie→Token”的能力(如 resistTokenApi),若请求头带 token 则直通;若无则尝试由 Cookie 衍生,兼容 Web/第三方系统。
  • 任务控制(暂停/恢复/结束)应在服务端校验角色与权限。

八、工程化与最佳实践

  • WS 只做“事件触发”,权威数据统一通过 HTTP 拉取(读写分离、幂等)。
  • 子组件对外只暴露必要事件(如 timeUpdatehandleAction),避免父层耦合流媒体能力细节。
  • 所有单点消息需配合 clientID 校验,避免串扰。
  • 严格管理定时器与 WS 生命周期(mount/unmount 清理)。

九、非技术叙事:用户视角

  • 打开控制器,扫码绑定当前会议室设备(这是“把手机与设备放进同一房间(spaceId)”)。
  • 点击“开启任务”,后台把你的请求准确转发到当前设备(靠设备 mac 找到它)。
  • 设备开始工作,后端通过“通知网关”告诉你:成功了,来看看任务信息吧。
  • 页面刷新任务详情,拿到 taskId,进入会中页;此时你能看到“谁在说话、说了什么、说了多久”。
  • 期间若网络抖动,系统会自动重连,恢复后继续同步。
  • 会议结束时,你点击停止,后台通知设备收尾,随后你能查看回放。

总结

  • 总体流程

  • 扫码后:GET /controller/space/auth{ token, spaceId, mac }

  • WS:连接后订阅 topic=room, tags=[spaceId:...]

  • 开启任务:POST /controller/task/create { mac, config, organizerId }

  • 接受任务推送:/app/setting/change → 前端 GET /controller/task/list

  • 会中:获取流票据与地址 → 订阅实时转写与状态 ,混合算法显示时长

  • 结束任务:POST /controller/task/end { taskId, mac }

http://www.dtcms.com/a/520373.html

相关文章:

  • dedecms网站后台模板做汽车网站费用
  • 做网站就上房山华网天下大型网站如何开发
  • 从「能用」到「可靠」:深入探讨C++异常安全
  • 如何让AI更好地理解中文PDF中的复杂格式?
  • Mount Image Pro,在取证安全的环境中挂载和访问镜像文件内容
  • 四元数(Quaternion)之Eigen::Quaternion使用详解(5)
  • 太平洋建设集团有限公司网站wordpress标签扩展
  • 二级域名解析网站天津效果图制作公司
  • Linux iptables:四表五链 + 实用配置
  • Ceph 简介
  • idea开启远程调试
  • UE5 蓝图-6:汽车蓝图项目的文件夹组织与运行效果图,
  • 编程竞赛小技巧
  • CrewAI 核心概念 团队(Crews)篇
  • 小九源码-springboot100-基于springboot的房屋租赁管理系统
  • 珠宝网站建设公司微信公众号推文模板素材
  • 自己可以做类似淘宝客网站吗北京公司网站制作流程
  • winform迁移:从.net framework 到 .net9
  • 计算机视觉领域顶会顶刊
  • 华为OD, 测试面经
  • 好听的公司名字大全附子seo教程
  • AiOnly深度体验:从注册到视频生成,我与“火山即梦”的创作之旅
  • 电商网站建设思维导图澧县网站建设
  • 网站app怎么制作建英语网站
  • 阮一峰《TypeScript 教程》学习笔记——泛型
  • 数据结构——三十、图的深度优先遍历(DFS)(王道408)
  • Linux中的DKMS机制
  • springboot基于Java的高校超市管理系统设计与实现(代码+数据库+LW)
  • Qt 文件与目录操作详解:QFile, QDir, QFileInfo, 与 QTextStream
  • 【软件设计师】数据结构