Serial Studio 扩展机制完全指南:从 Extension Manager 到自建扩展仓库
【免费下载链接】Serial-StudioOpen-source telemetry dashboard. Supports UART, BLE, MQTT, Modbus, CAN Bus and more.项目地址: https://gitcode.com/GitHub_Trending/se/Serial-Studio
Serial Studio 的扩展(Extensions)机制允许用户在官方遥测仪表盘的基础上追加主题、帧解析器、项目模板、QML 仪表组件与外部插件,从而为 UART、BLE、MQTT、Modbus、CAN Bus 等数据源定制化处理与可视化。本文以官方文档 Extensions.md 为骨架,结合仓库中的 ExtensionManager、ExtensionHandler 与 ExtensionCatalog 等实现,完整讲解扩展的安装、运行、卸载、更新、仓库管理、info.json元数据格式、插件状态持久化以及 API 编程接口,读完即可在真实项目中使用或自行发布扩展。
扩展类型总览
Serial Studio 共支持五种扩展类型,覆盖从外观到数据处理的全链路定制:
| 类型 | 作用 | 落地形态 |
|---|---|---|
| Themes | 定制整套界面外观,包括代码编辑器的语法配色 | 主题 JSON 文件 + 编辑器 XML 配色 |
| Frame parsers | 用 JavaScript 函数把自定义二进制或文本协议解码为结构化数据 | 复制到工作区 Extensions 文件夹的.js文件 |
| Project templates | 面向常见传感器配置的现成.ssproj项目文件 | 复制到工作区 Extensions 文件夹的.ssproj文件 |
| Widgets | 用 QML 编写、像内置组件一样渲染实时数据的仪表可视化 | 随实体类型注册到项目编辑器 Widget 列表 |
| Plugins | 连接 Serial Studio API 服务器的外部程序(Python、原生二进制等) | 独立外部进程 |
关键区别在于Widgets 运行在 Serial Studio 进程内部,拥有与应用本身相同的权限;而Plugins 是独立的外部进程,通过 API 服务器与主程序通信。两者在文档中各有独立篇幅:Widget-Extension-Development.md 与 Plugin-Development.md。
所有扩展都从在线仓库下载并安装到本地,通过Extension Manager(扩展管理器)对话框完成浏览、安装、更新与卸载。
打开扩展管理器
点击工具栏中的Extensions按钮即可打开扩展管理器,它包含三个视图:
- 网格视图(Grid view):浏览可用扩展,支持搜索与类型筛选。
- 详情视图(Detail view):展示 README、元数据、截图,以及安装/卸载控制按钮。
- 仓库设置(Repository settings):管理仓库 URL(仅 Pro 版本提供)。
从源码看,这个界面由Misc::ExtensionManager单例驱动,该类通过一组 Q_PROPERTY 向 QML 暴露状态:count(当前筛选后的扩展数量)、extensions(网格视图数据)、selectedExtension(选中项)、searchFilter/filterCategory/filterType(三类筛选条件)、downloadProgress(下载进度 0.0–1.0)、runningPlugins(运行中的插件)等,见 ExtensionManager.h。类型筛选中使用的友好名称由friendlyTypeName()映射(如frame-parser→ "Frame Parser"),见 ExtensionManager.cpp。
安装扩展
完整安装流程如下:
- 打开扩展管理器。
- 点击Refresh拉取最新目录(对应源码中的
refreshRepositories(),它并行请求所有仓库的manifest.json并合并结果,见 ExtensionManager.cpp)。 - 浏览或搜索扩展,使用类型下拉框按 Themes、Plugins 等筛选。
- 点击扩展卡片进入详情视图。
- 点击Install,进度条会显示下载状态。
- 安装完成后,不同类型的扩展按各自方式生效:
- Themes:出现在 Preferences(偏好设置)的主题下拉框中。
- Plugins:可从详情视图的Run按钮启动。
- Frame parsers:被复制为
.js文件到工作区的 Extensions 文件夹;在帧解析器编辑器的工具栏使用Open按钮(导入外部脚本文件)即可加载到项目。 - Project templates:被复制为
.ssproj文件到工作区的 Extensions 文件夹;在工具栏使用Open Project加载。 - Widgets:重启后出现在项目编辑器的 Widget 列表中(按其声明的实体类型归类);首次渲染时 Serial Studio 会询问是否允许其运行。
安装操作的底层实现在installExtension():它会先读取m_installer.installedInfo(),若扩展已安装则保留其本地记录的type(避免更新时把新版本写到旧目录旁导致卸载残留),然后交给ExtensionInstaller::install()完成下载与落盘,见 ExtensionManager.cpp。下载超时被设置为 15 秒(m_nam.setTransferTimeout(15 * 1000),见 ExtensionManager.cpp),网络不可用时管理器会快速失败而不是长时间挂起。
卸载扩展
- 打开扩展管理器,找到已安装的扩展(卡片上有Installed徽标)。
- 点击卡片,然后点击Uninstall。
- 对于插件:若正在运行,请先停止它。
- 若卸载的正是当前主题,Serial Studio 会自动回退到Default主题。
注意:卸载是不可逆的破坏性操作——安装文件会被彻底删除。源码中的uninstallExtension()在删除前会校验扩展确实已安装(isInstalled(id)),删除后还会校验isInstalled(id)以确认删除成功,见 ExtensionHandler.cpp。API 层还支持dryRun: true预览将要删除的内容而不真正提交(详见下文 API 章节)。
更新扩展
当仓库中存在更新版本时,扩展卡片会显示Update徽标。点击卡片后按Install即可完成更新。版本判断使用数值化比较:ExtensionCatalog::compareVersions()对远程版本与本地已安装版本做严格大于比较,避免字符串比较把1.10.0误判为比1.9.0新(见 ExtensionManager.cpp)。
此外仓库还实现了扩展自动更新组件 ExtensionAutoUpdater,通过updateCheckEnabled与automaticUpdates两个属性控制是否检查更新、是否静默安装可用更新。
插件:外部进程实时接入
插件是连接到 Serial Studio 以接收实时数据、计算统计信息、显示自定义可视化的外部进程,支持两种连接方式:
- gRPC(端口 8888):高性能二进制流,推荐用于实时帧数据,详见 gRPC-Server.md。
- TCP/JSON(端口 7777):基于 JSON 的协议,详见 API-Reference.md。
运行插件
- 从扩展管理器安装插件。
- 点击插件卡片打开详情视图。
- 点击Run,插件启动,其输出出现在日志面板中(源码通过
PluginRunner的outputChanged信号把子进程 stdout/stderr 转发给 UI,见 PluginRunner.h)。 - 点击Stop终止插件。
插件运行前置条件
- 必须启用 API 服务器(若未启用,Serial Studio 会弹出提示)。
launchPlugin()前的checkLaunchPreconditions()会先校验 API 服务器状态,gRPC 插件还会走ensureApiServerForLaunch()单独确认 8888 端口可用,见 ExtensionManager.h。 - Python 插件需要 Python 3.6+。
- 使用 gRPC 的插件需要
grpcioPython 包;仓库中的插件自带启动脚本,首次运行时会在本地虚拟环境中自动安装它。 - Serial Studio 退出时会自动停止所有运行中的插件(
stopAllPlugins(),见 ExtensionManager.h)。
平台支持
插件可以在元数据中用platforms字段声明针对不同操作系统与 CPU 架构的专属入口。Serial Studio 会根据当前平台自动选择正确的二进制或脚本。平台键使用os/arch或os/*格式(*表示通用构建):
- macOS:
darwin/*(始终为通用构建)。 - Linux:
linux/x86_64、linux/arm64。 - Windows:
windows/*、windows/x86_64。
平台判定由ExtensionCatalog::currentPlatformKey()完成,platformSupported()与resolvePlatform()负责过滤与覆盖合并,见 ExtensionCatalog.h。若插件不支持当前平台,Install 按钮会被禁用并显示Unavailable徽标。
想自己开发插件?参见 Plugin-Development.md,其中包含完整的
info.json字段参考、代码示例、状态持久化与分发说明。
仓库管理
仓库(Repository)是一个指向manifest.json文件的 URL,该文件列出了仓库内所有可用的扩展。Serial Studio 自带一个默认社区仓库(源码中的kDefaultRepoUrl,见 ExtensionManager.cpp)。仓库 URL 列表持久化在 QSettings 的ExtensionRepositories键中,首次启动为空时自动填充默认仓库。
管理仓库(Pro)
自定义仓库管理仅在 Serial Studio Pro 中提供,可支持:
- 公司内部扩展:在内网或私有仓库托管私有扩展。
- 社区合集:托管在任意位置的第三方扩展集合。
- 本地开发:开发扩展时指向本地文件夹。
管理步骤:
- 打开扩展管理器。
- 点击工具栏中的Repos按钮。
- 添加 URL、浏览选择本地文件夹,或移除已有条目。
- 点击Reset恢复默认社区仓库。
安全性方面,addRepository()会先调用ExtensionCatalog::isTrustedRepoUrl()校验——仓库必须是本地文件夹或 https:// URL,否则直接拒绝并弹窗警告(见 ExtensionManager.cpp)。resetRepositories()在恢复默认仓库前会先停止所有插件并卸载全部扩展(见 ExtensionManager.cpp)。本地仓库通过browseLocalRepo()弹出目录选择器加入,且本地仓库的 manifest 由loadLocalManifest()直接读取文件而非走网络请求(见 ExtensionManager.cpp)。
创建扩展:目录结构与元数据
仓库目录结构
一个典型的扩展仓库如下:
my-repo/ manifest.json theme/my-theme/ info.json my-theme.json code-editor/my-theme.xml plugin/my-plugin/ info.json plugin.py run.sh run.cmd frame-parser/my-parser/ info.json my-parser.js widget/com.example.level-bar/ info.json LevelBar.qmlmanifest.json
manifest 列出仓库内每个扩展info.json的相对路径:
{ "version": 1, "repository": "My Extensions", "extensions": [ "theme/my-theme/info.json", "plugin/my-plugin/info.json" ] }info.json基础格式
每个扩展都有一份包含元数据的info.json:
{ "id": "my-theme", "type": "theme", "title": "My Custom Theme", "description": "A dark theme with company branding.", "author": "Your Name", "version": "1.0.0", "license": "MIT", "category": "Dark", "screenshot": "screenshot.png", "files": [ "info.json", "my-theme.json", "code-editor/my-theme.xml" ] }插件info.json
插件在基础字段上增加entry、runtime,可选增加platforms与grpc字段。完整字段参考与示例见 Plugin-Development.md。
Widgetinfo.json
组件(Widget)额外包含一个widget块,声明实体作用域、QML 入口文件、接受的数据以及设置项。仓库自带的示例扩展 examples/widget-extension/info.json 展示了完整结构——包括apiVersion、hostCompat(宿主版本兼容范围)、scope(实体作用域,如dataset)、qml入口、accepts(接受的数据集数量与值类型)、defaultSize以及config设置数组(支持choice、bool等类型,每个设置项含id、type、label、description、default与可选options)。完整参考、数据模型与信任模型见 Widget-Extension-Development.md。
字段参考表
| 字段 | 必填 | 说明 |
|---|---|---|
id | 是 | 唯一标识符(小写、连字符)。 |
type | 是 | theme、frame-parser、project-template、widget或plugin。 |
title | 是 | 扩展管理器中显示的名称。 |
description | 是 | 显示在卡片上的简短描述。 |
author | 是 | 作者名或组织名。 |
version | 是 | 语义化版本字符串(例如"1.0.0")。 |
license | 否 | 许可证标识符(例如"MIT")。 |
category | 否 | 用于筛选的分类。 |
screenshot | 否 | 预览图的相对路径。 |
files | 是 | 要下载/安装的相对文件路径数组。 |
entry | 插件 | 脚本或二进制的入口点。 |
runtime | 插件 | 解释器命令(例如"python3")。原生二进制留空。 |
terminal | 插件 | 设为true在系统终端窗口中启动。 |
grpc | 插件 | 设为true表示插件使用 gRPC 流式传输(端口 8888)而非 TCP/JSON。 |
platforms | 插件 | 按平台覆盖entry、runtime与files。 |
从实现看,files列表在 catalog v2 条目中还可附带 sha256 摘要与文件大小:ExtensionCatalog::parseFileList()会校验每个文件的路径组件是否安全(isSafePathComponent()/isPathSafe()防止路径逃逸出扩展目录),下载后通过digestMatches()校验摘要,见 ExtensionCatalog.h。这意味着扩展安装具备完整性校验与路径安全防护。
托管方式
GitHub:创建仓库并分享 manifest 的 raw URL:
https://raw.githubusercontent.com/your-org/extensions/main/manifest.json本地文件夹:在 Repository Settings 中使用 Browse 选择包含manifest.json的文件夹。
任意 Web 服务器:通过 HTTP(S) 托管文件。files中的相对路径会基于info.json的 URL 解析(resolveFileUrl(),见 ExtensionCatalog.h)。
插件状态持久化
插件状态(打开的窗口、设置、配置)保存在项目文件中,与组件布局数据并列。这意味着不同项目可以拥有不同的插件配置。
Serial Studio 永远不会自行调用extensions.saveState:插件必须显式调用它(典型时机:设备断开时、插件自身停止时、或 Serial Studio 退出前)状态才会持久化。状态在插件启动或新设备连接时恢复。Serial Studio 关闭时仍在运行的插件,会在下次仪表盘可用时(设备连接或文件播放器打开)被自动重新启动,除非用户手动停止过它们。
从源码看,这一机制由ProjectModel::savePluginState()/pluginState()落到项目文件,API 层extensions.saveState/extensions.loadState命令分别调用二者,见 ExtensionHandler.cpp。PluginRunner通过restorableIds()记录可恢复的插件,restoreRunningPlugins()在仪表盘就绪时恢复它们,见 PluginRunner.h。
代码层面的状态持久化 API 用法详见 Plugin-Development.md。
API 访问:用命令管理扩展
扩展管理器可通过 TCP 端口 7777 的 API 或端口 8888 的 gRPC 访问。这些命令由 ExtensionHandler.cpp 在命令注册表中统一注册:
| 命令 | 说明 |
|---|---|
extensions.list | 列出所有可用扩展。返回count与addons数组,见 ExtensionHandler.cpp。 |
extensions.getInfo | 获取指定扩展的详细信息(参数extensionId)。返回结果附带installed、updateAvailable、installedVersion字段,见 ExtensionHandler.cpp。 |
extensions.install | 按索引安装扩展(参数addonIndex)。 |
extensions.uninstall | 按索引卸载扩展(参数addonIndex)。破坏性操作:不可逆地删除扩展文件。传dryRun:true可预览删除内容而不实际提交——dry run 返回willRemove: true与警告信息,见 ExtensionHandler.cpp。 |
extensions.refresh | 从所有仓库刷新目录。 |
extensions.saveState | 保存插件状态到项目(参数pluginId、state)。 |
extensions.loadState | 从项目加载插件状态(参数pluginId)。 |
extensions.listRepositories | 列出仓库 URL(仅 Pro)。 |
extensions.addRepository | 添加仓库 URL(仅 Pro,参数url)。 |
extensions.removeRepository | 按索引移除仓库(仅 Pro,参数index)。 |
注意仓库相关命令(listRepositories/addRepository/removeRepository)被#ifdef BUILD_COMMERCIAL包裹,仅编译进 Pro 构建,见 ExtensionHandler.cpp。
已安装扩展的存放位置
扩展安装在工作区文件夹的Extensions/子目录下(默认是 Documents/Serial Studio,可在设置中修改):
~/Documents/Serial Studio/Extensions/ installed.json theme/my-theme/ info.json my-theme.json code-editor/my-theme.xml plugin/my-plugin/ info.json plugin.py run.shinstalled.json记录安装清单,由ExtensionInstaller维护(工作区路径由 WorkspaceManager 提供,ExtensionManager通过Core::services().workspaceManager获取)。框架解析器与项目模板正是从该目录被复制到工作区 Extensions 文件夹后供编辑器加载的。
结语
Serial Studio 的扩展体系把"界面定制、协议解析、可视化组件、外部处理进程"四类需求统一收敛到一套仓库 +info.json元数据的机制中:用户侧通过扩展管理器即可完成浏览、安装、运行、更新、卸载的完整生命周期管理,开发者侧则可用几行 JSON 元数据 + 一个 manifest 发布自己的扩展。若深入阅读 ExtensionManager.cpp、ExtensionCatalog.h 与 ExtensionInstaller.h,还能看到版本数值比较、路径安全校验、下载摘要校验、插件进程生命周期管理、状态按项目持久化等工程细节——这些正是从"能用"走向"可靠分发"的关键设计。
【免费下载链接】Serial-StudioOpen-source telemetry dashboard. Supports UART, BLE, MQTT, Modbus, CAN Bus and more.项目地址: https://gitcode.com/GitHub_Trending/se/Serial-Studio
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考