news 2026/9/17 6:48:40

Serial-Studio 属性注册表实战:一次声明 Dataset 属性,派生序列化、编辑器表单与 API 四大表面

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Serial-Studio 属性注册表实战:一次声明 Dataset 属性,派生序列化、编辑器表单与 API 四大表面

Serial-Studio 属性注册表实战:一次声明 Dataset 属性,派生序列化、编辑器表单与 API 四大表面

【免费下载链接】Serial-StudioOpen-source telemetry dashboard. Supports UART, BLE, MQTT, Modbus, CAN Bus and more.项目地址: https://gitcode.com/GitHub_Trending/se/Serial-Studio

本文围绕 Serial-Studio 仓库中的规格 0036(Property registry,"declare once, derive everywhere",状态done)展开。该文档把"一个数据集属性要在 8 个互不可见的地方手工重复声明"的问题,转化为"一份 JSON 清单 + 一个 Python 生成器 + 一组防漂移校验"的完整技术方案。读完本篇,你将理解这套注册表如何把约 1,400 行手工维护的属性管线代码压缩为单一声明源,如何在保持热路径结构体与撤销语义完全不变的前提下派生项目文件序列化器、项目编辑器表单、API 字段处理与类型化 Schema,并能复现其--check漂移门禁与字节级基线回归测试。方案骨架来自 plan.md,需求与验收标准来自 spec.md,实现细节均已对照当前仓库源码核实。

问题背景:一个属性,八份声明

在实施注册表之前,Serial-Studio 中的一个 dataset(数据集)属性需要在八处、用八种互不相同的惯例手工声明,且彼此之间没有任何机制保证一致:

  1. Dataset结构体上的一个字段,带 C++ 默认值;
  2. Keys::命名空间中的一个 JSON 键常量——其拼写被刻意设计得与字段名不同(PltMin→ 磁盘上的"plotMin"Graph→ 字段pltVirtual→ 字段virtual_);
  3. 项目文件序列化器中的写出规则——有些字段总是写出,有些仅在非默认时写出,有些写出时还要夹取(clamp);
  4. 反序列化器中的读入规则,携带自己的默认值、旧键回退与读后归一化;
  5. 编辑器按实体划分的 id 枚举中的一个表单字段 id;
  6. 编辑器控制器中的一个表单行构建器:约十几个调用,分别设置控件类型、当前值、占位符、标签、描述与启用谓词;
  7. 编辑器写回 switch 中的一个提交分支,把编辑后的 variant 转换回结构体字段;
  8. API 字段应用器,从 JSON 参数重新解析同一字段、重新实现校验、返回自己的错误串——外加字段名、含义、枚举域在 API 命令描述里的英文散文段落(这是驱动 MCP 的 LLM 实际读到的"schema")。

规格文档在 dataset 实体上实测(41 个结构体字段、34 个表单 id):结构体/键/序列化器/表单构建器/提交 switch 合计约 1,410 行手工属性代码,含 API 字段应用器约 1,742 行——平均每属性约 41 行,散布在 5 个翻译单元。今天新增一个 dataset 字段,意味着在 5 个文件里做 5 处协同修改、遵循 3 种不同惯例,且没有任何东西检查它们是否一致。

测量过程中还发现了两个对现有 linter 和测试都不可见的真实缺陷:

  • overviewDisplay在每次保存时静默丢失:键已声明、加载时读取、可通过 API 设置、写进了 API 描述,但序列化器没有对应的写出。设置、保存、重新加载——值就消失了;
  • 四个属性的默认值随到达路径不同而不同:读路径回退值与结构体初始化值在fftSamples(256 vs -1)、fftSamplingRate(100 vs -1)、ledHigh(80 vs 0)、index(0 vs -1)上不一致,新建的数据集与从省略了这些键的项目文件加载的数据集会落到不同状态。

还有一个"散文即 schema"的功能性代价:API 命令只把两个身份参数声明为类型化 schema 属性,把约 43 个可打补丁的字段藏在英文段落里,导致生成的 JS/Lua SDK 无法设置任何 dataset 字段(生成器只看见两个声明的属性),帮助文本引导调用方去查的 schema-description 动词也只会返回那两个身份参数。

这正是规格 0028(命令注册表)为命令解决过的那类问题——命令证明了"清单声明一次 + 生成 + 漂移门禁"的可行,而实体属性是更大、增长更快、且 LLM 必须透过 MCP 推理的表面。

总体方案:JSON 清单 → 四个生成 C++ 翻译单元

plan.md 给出的一段式摘要:一份JSON 清单(dataset.json)一次性声明每个持久化/可编辑的数据集属性;一个新的生成器scripts/generate-property-registry.py从它发出四个入库的、可编译的 C++ 翻译单元:描述符表、项目 JSON 序列化/反序列化对、编辑器表单行构建器 + 提交分发器、API 字段应用器 + 类型化 schema 属性。

这是规格 0028 的形状——在app/rcc/下用 JSON 声明、生成 C++、用sanitize-commit.py--checkregistry-verify.py的规则函数做漂移门禁——但有一个简化:由于生成产物是真正的 C++ 翻译单元而非运行时读取的 JSON,tr()天然可用,不需要 0028 那样的 lupdate 桩。生成是原地替换:生成的函数保留现有名字与签名(DataModel::serialize(const Dataset&)DataModel::ProjectEditor::addGeneralSection(...)API::Handlers::applyDatasetUpdateParams(...)),因此所有现有调用方、基于 ADL 的序列化组合与撤销咽喉点都不受影响。凡是抵抗声明化的东西——跨实体校验、提交期副作用、实时选项提供者、结构性行——以名字从清单引用,在一个小型手写钩子翻译单元中实现一次。

两个被否决的备选方案:

  • C++constexpr描述符表:Python 生成器不得不解析 C++ 才能发出 API schema 和其余表面,且翻译/校验表达式会变得别扭;
  • 运行时反射属性袋:为了一个构建期问题,摧毁热路径上经过打包优化的结构体,毫无收益。

受影响文件与落地路径

plan 阶段的文件表使用app/src/...布局;在当前仓库中,这些产物落在core/树下的对应位置。已核实的落地清单:

文件变更
dataset.json(新,1,858 行)声明本体:42 个属性条目 + 选项源定义 + 分区顺序。现已加入 rcc.qrc(plan 中 v1 仅为构建期产物,后续规格改为运行时可读取)。
schema.json(新,660 行)清单自身的 JSON Schema,使registry-verify.py校验形状而非鸭子类型。
generate-property-registry.py(新,2,275 行)读取清单,发出四个生成翻译单元。--check= 字节比较、不写盘、漂移时退出码 1。确定性顺序、newline=""LF 策略、与generate-command-strings.py相同的头部/标记约定。
DatasetRegistry.h(新,生成,318 行)DataModel::Registry::DatasetProperty描述符结构 +constexpr表(id、field、jsonKey、apiName、type、default、section、控件类型、标志)+DatasetItem表单 id 枚举,取代手写 id 枚举块。
DatasetSerialization.cpp(新,生成)DataModel::serialize(const Dataset&)DataModel::read(Dataset&, const QJsonObject&)——从Frame.h/Frame.cpp移出,同名、同 ADL 行为。
DatasetForm.cpp(新,生成)ProjectEditor的 dataset 行构建器与提交分发器/子应用器,作为DataModel::ProjectEditor的成员函数,使tr()保留现有翻译上下文。
DatasetApiFields.cpp(新,生成,742 行)applyDatasetUpdateParams+ 按分组的子应用器 +consumed集合簿记 + dataset 动词的类型化 schema 属性表。
PropertyHooks.h / PropertyHooks.cpp(新,手写,244/650 行)命名逃生舱:校验器(datasetAliascolortransformLanguage)、可见性谓词(insidePainterGroupwaterfallEnabledwidgetRangeApplicable)、选项提供者(xAxisSourceswaterfallYSourcesdatasetWidgetsplotOptionsfftWindowsfftSampleCountsdisplayFormats)与提交副作用(onWidgetChangedonVirtualChangedonXAxisChangedonAliasRejected等)。每个只实现一次,所有表面调用同一个。
Frame.h/Frame.cpp删除手写的serialize(const Dataset&)read(Dataset&, ...)函数体;Keys::块不动;readDatasetAlarmBands/readDatasetFrequencyMarkers保持手写(嵌套实体,v1 范围外),由生成读取器经声明的subEntity钩子调用。
ProjectEditorForms.cpp/ProjectEditorCommit.cpp移除 dataset 行构建器(约 904–1494、1627–1663 行)与五个 dataset 子应用器;buildDatasetModel保留结构性脚手架(分区头、子编辑器启动行)并调用生成发射器;提交槽保留提示/树/发射编排,调用生成分发器。
ProjectHandlerEntities.cpp/ProjectHandler.cpp移除四个applyDataset*Fields函数与takeParam的 dataset 半区;dataset 动词 schema 从makeSchema({groupId, datasetId})+ 散文块切换到生成的类型化属性列表,散文只保留非字段枚举的引导段落。
registry-verify.py新增check_property_manifests(errors)规则函数(registry-verify.py#L1043)+main()中一行调用:清单对schema.json校验通过、id 唯一、jsonKey解析到真实Keys::常量、每个声明的钩子名存在于PropertyHooks.h、无字段被声明两次、每个Dataset结构体字段要么已声明要么显式列为 runtime-only。
code-verify.py新规则:在生成翻译单元与PropertyHooks.cpp之外引用手写的Keys::dataset 键即为错误(防止并行字段表重新长出来)。
sanitize-commit.py在 SDK 步骤与命令字符串步骤之间插入一个生成器run_python_step块(sanitize-commit.py#L432-L444):重新生成 →--check--check-snapshot,随后运行registry-verify.py
CMakeLists.txtset(SOURCES ...)注册四个生成翻译单元 +PropertyHooks.cpp
test_property_registry.py(新)AC2/AC3/AC6/AC11/AC12/AC13 覆盖。
tests/integration/baselines/(新)examples/语料库变更前捕获的project.exportJson入库基线(如BLE_Battery_Level.ca6a45dc.json等按项目哈希命名)。

plan 中"多选择简化(白捡的收益)"也已包含:ProjectEditorMultiSelect.cpp过去为了收集ParameterType → value映射而构建一个一次性表单模型;有了描述符表后直接读表,这段收割 hack 被删除。

清单解剖:一条属性条目如何驱动四个消费者

一个 manifest 条目对应一个属性。以下是PltMin(最小值)在 dataset.json 中的真实条目(含磁盘上的键拼写差异与旧键回退):

{ "id": "PltMin", "field": "pltMin", "jsonKey": "PltMin", "formId": "kDatasetView_PltMin", "api": { "name": "pltMin", "aliases": ["plotMin"], "expose": true }, "type": "double", "default": 0, "scope": "document", "section": "general", "widget": "FloatField", "label": "Minimum Value", "description": "Lower bound of the dataset value range; widgets and FFT fall back to it when their own range is left unset", "placeholder": 0, "persist": "always", "pairWith": "PltMax", "legacyKeys": ["Min", "Max"], "legacyPair": true, "readTransform": "toDouble", "writeTransform": "pairMin", "undo": { "label": "Edit Dataset", "coalesce": true } }

字段语义(与 plan 的对照):

  • id/field/jsonKey/api.name:分别映射到kDatasetView_PltMinDataset::pltMinKeys::PltMin(磁盘上拼作"plotMin")、API 参数名;
  • api.aliases:R12 要求——凡 API 名与 JSON 键名不同者,额外接受磁盘拼写(pltMin/plotMinwgtMin/widgetMinxAxisId/xAxissourceId/datasetSourceId),使"从 API 读回再原样写回"不再产生unknown_field警告与静默丢失。写出仍只发规范名;
  • scope: document | runtimeruntime表示仅实时帧的值(parsed/numeric/raw value),文档写出时跳过——因为serialize(const Dataset&)在项目文件写出与实时 API 帧广播之间共享,这个字段是强制的;
  • persist: always | whenNonDefault | whenTrue | never:驱动写出是无条件还是受!= default保护;
  • legacyKeys+legacyPair:读侧回退(Keys::Min/Keys::Max)与"倒置时互换"的 min/max 归一化(对应 plan 中的validate.pairMinMax);
  • undo.coalesce:编辑是否与前一个撤销步骤合并。true生成每字段键"dataset:<groupId>:<datasetId>:<id>"false生成空键使该步永不合并;
  • onCommit/enabledWhen/options:以名字引用PropertyHooks中的副作用、谓词与选项源;
  • pro: true(如Waterfall条目):Pro 门控属性,生成器发出与今天相同的#ifdef BUILD_COMMERCIAL保护,保证 GPL 构建永不暴露 Pro 属性。

四个生成消费者读同一条目:

  1. 序列化器——persist决定是否受默认值保护;读取器发射ss_jsr(obj, Keys::X, <default>),默认值取清单中的值,该值按构造与结构体初始化器相同——这正是关闭四个默认漂移缺陷的机制(fftSamplesfftSamplingRateledHighindex的读侧回退从此与新建数据集一致)。
  2. 表单——每条目一个统一的约 9 调用的行发射器,按section再按声明顺序排列;visibleWhen/enabledWhen解析为接收(const Dataset&, const ProjectModel&)PropertyHooks谓词;options解析为OptionSource
  3. 提交——对 id 枚举生成的switch,把行的 variant 转换为字段类型;带onCommit的条目在字段写入后调用命名钩子,钩子拥有副作用(表单重建、跨字段变更、延迟重建)。生成的分发器写入Dataset&并返回是否需要重建树,自身从不调用ProjectModel——这保住了今天"同步重建 vs 零定时器延迟重建"的拆分,避免提交期重入或编辑丢失。
  4. API——每条目一个生成应用器:对apiName与每个apiAlias执行takeParam,数值走SerialStudio::toDouble类型强制(遵循现有惯例)、声明的夹取、返回错误串的命名校验器、rebuildTree累加。consumed集合由生成包装器播种、未知字段警告由生成包装器发出,因此它再也不可能被忘掉(过去四个更新处理器各自重声明同一份七行参数消费闭包,警告只在作者记得播种时生效)。同一张表还发出 dataset 动词的类型化properties/requiredschema 块。

选项源:一套 OptionSource,四种适配器

编辑器在行的可编辑值中存储位置索引,而今天每个 combo 各自手写正向查找与反向查表,遵循四种互不兼容的约定。注册表在PropertyHooks.h引入一个OptionSource抽象与四个适配器,每个 combo 声明自己用哪个:

  • staticMap——键→标签映射,存储值为键(datasetWidgetsdisplayFormats);
  • parallelValues——标签列表 + 值列表,存储值为值(fftWindowsfftSampleCounts);
  • liveProvider——标签与值由当前项目状态计算(xAxisSourceswaterfallYSources);
  • tuple——一行驱动多个字段(plotOptionsplt+log),声明为单个条目带fields列表与元组选项源。

每个适配器暴露indexForValue()/valueForIndex(),因此生成的表单与提交代码中没有查找循环,API 的 schema 枚举输出的是域而非索引。清单中的真实条目印证了这一抽象:datasetWidgets选项源声明为extensibleMap(已安装的 dataset 作用域 widget 扩展包追加到这些行之后,无扩展包的机器看到的域与存储索引完全不变),并带notFoundIndex: 0与钩子名widgetExtensionOptions

撤销路由:咽喉点纹丝不动

这是 plan 中强调最多的不变量:没有任何生成代码直接触碰m_groups,也没有任何新的ProjectModel。三条写入路径全部收敛到现有的整结构体更新:

  • 编辑器:生成提交修改ProjectEditorm_selectedDataset副本,然后手写的onDatasetItemChanged调用ProjectModel::setNextUndoHint(label, coalesceKey)——label 与 key 都从清单条目产生——随后ProjectModel::updateDataset(groupId, datasetId, dataset, rebuildTree)
  • API:生成应用器打补丁到一个局部Dataset副本;datasetUpdate调用同一个ProjectModel::updateDataset(...)。批量编辑经由API::CommandRegistry::execute()中打开的现有DataModel::ProjectUndoFrame仍是一个步骤;
  • 多选择:不变;ProjectEditorMultiSelect.cpp中现有的ProjectUndoFrame+setNextUndoHint对仍然包裹扇出。

ProjectModel::updateDataset已经打开const ProjectUndoScope undo_scope{*this, tr("Edit Dataset")};两阶段捕获在作用域进入时运行ProjectHistory::stageCapture()、从ProjectModel::setModified(true)运行ProjectHistory::commitPending()。由于没有生成代码在这个漏斗之外引入setModified(true)code-verify.pyundo-scope-missing规则原样通过,规格 0031(撤销/重做)建立的 R1 保证是继承的而非重实现的。

热路径与线程影响

plan 对热路径的影响逐条给出结论,全部为"否",且都给出了依据:

  • 触碰热路径?否。FrameReaderCircularBufferFrameBuilder、span 快车道与 Dashboard 绘制路径全部不动。Dataset结构体未被修改——不加成员、不重排、不间接寻址;alignas(8)+static_assert(sizeof % alignof == 0)不变量不变,生成器被禁止发出任何改变结构体的东西。描述符表是constexpr,只被编辑器/API/序列化代码查询,绝不在逐帧路径上。一个需要标记的邻接关系:serialize(const Dataset&)在项目文件写出与实时 API 帧广播之间共享,因此它换了翻译单元——这正是scope字段的由来,也是--benchmark-hotpath尽管无解析代码变更仍作为回归门禁运行的原因。
  • 新的跨线程信号/槽?否。ProjectModel、ProjectEditor、API 命令执行全在主线程,与今天相同。
  • 缓存热路径标志的新输入?否。没有新标志;m_changeDrivenm_streamAvailablem_operationMode通过同样的updateDatasetgroupsChanged→ sync 路由把注册表驱动的编辑当作普通项目编辑看待。
  • 时间戳所有权:不动,不涉及帧数据或驱动边界。

数据模型与持久化

  • 无新Keys::条目、无 schema 版本升级、无迁移。清单以 C++ 标识符引用现有Keys::常量;registry-verify.py在引用常量不存在时失败。Keys::保持磁盘 JSON 键拼写的单一事实源——这是非目标里写死的约束。
  • 预期输出差异(仅有的两种):两个缺陷修复会改变保存字节的字节:overviewDisplay从此被写出(此前只读、只可 API 设置,从不序列化);fftSamplesfftSamplingRateledHighindex的读侧默认值变为结构体默认值。旧版 Serial Studio 忽略未知键,因此overviewDisplay的加入向前安全;默认对齐只影响省略了该键的文件——这些文件今天加载出的状态是新建数据集永远不会有的。往返基线测试断言的正是这个差异集合且不多不少。
  • 遗留别名保留Keys::Min/Keys::Max保持为三对 min/max 的只读回退,声明为对应条目的legacyKeysalarmLow/alarmHigh/alarmEnabled保持为只读的 v3.3 迁移输入,留在手写的readDatasetAlarmBands中(嵌套实体,v1 范围外)。
  • 嵌套实体保持手写alarmBandsfftMarkers):它们是另一种实体的集合、有各自的 v3.3 迁移;清单 v1 不具备实体嵌套能力,折叠它们是后续的机械工作。生成读写器经声明的subEntity钩子调用它们。

API / SDK 表面与下游派生

  • 无新命令、无改名。project.dataset.update/.add/.getBy*保留 id 与行为。
  • dataset 动词的 schema 获得每个声明字段的类型化properties(类型、描述、适用时的枚举域);散文描述保留引导段落(何时用virtualindex的含义、options 位标志),删除字段枚举——那现在是机器可读的。
  • SDK 后续贯通:api-schema.json 是运行时 dump(SerialStudio --dump-api-schema),generate-sdk.py从它构建 JS/Lua SDK。一旦 schema 声明了字段,SDK 生成器无需改动即可发出可用的 setter——规格中"生成的 SDK 无法设置任何 dataset 字段"的缺陷由此消失。维护流程顺序为:构建 →--dump-api-schemasanitize-commit.py
  • 该注册表还是 R6(MCP/gRPC schema,规格 0037)的声明底座:当前 generate-property-registry.py 的头部说明中,规格 0037 已复用同一脚本,从入库的 API 快照与同一清单派生 proto-fields.json(gRPC 字段号追加账本)与 serialstudio-typed.proto,并提供无构建的投影检查--check-snapshot(在 sanitize-commit.py 中作为第三步运行)——印证了"声明一次、下游持续派生"的设计意图。

QML / UI:零改动

没有任何 QML 变更。每个项目编辑器表单已由一个通用委托 TableDelegate.qml 渲染,它按行的控件类型角色做 switch;每字段的代码全在 C++ 侧。生成的行携带相同的角色、相同的控件类型词汇、相同的选择字段位置索引约定,因此委托不动。DatasetView.qml的手写部分(可视化切换条、报警带与频率标记对话框按钮)是结构性的,原样保留——plan 明确把"不是每行都是属性"列为约束:分区头、按子项导航行、子编辑器启动按钮保持手写,声明必须能与它们组合而非要求它们消失。

权衡与备选方案

plan 的决策表(决策 / 备选 / 选择及理由):

决策备选选择及理由
声明格式JSON 清单;C++constexpr表;X-macro 头JSON 清单——匹配 0028 先例,可被 Python 生成器与 R6 的 schema 发射器直接消费而无需 C++ 解析器,且把校验/编辑器元数据留在热路径包含的头文件之外。C++ 表会强迫生成器解析 C++(或强迫 R6 复制数据),X-macro 在 41 属性 × 15 属性面前不可读。
翻译0028 式 lupdate 桩;生成代码原生tr()原生tr()——生成的表单构建器是ProjectEditor成员函数,tr()解析到现有翻译上下文,所有已翻译的属性字符串保持有效。0028 需要桩只是因为它在运行时读清单。
生成代码形状原地替换函数体;新旧 API 并存原地替换——生成的函数保留今天的名与签名,调用方、ADL 序列化组合与撤销咽喉点不动,diff 可以按"这个块搬了且变成派生的"来审。
v1 实体范围仅 dataset;dataset + group;全部实体仅 dataset——最大表面(约 1,410 核心行、41 字段),走完了所有硬案例(选择字段、实时选项提供者、副作用、Pro 门控、遗留键、嵌套实体)。Group/action/source/output-widget 在发射器被证明后机械跟进。
v1 序列化派生它;留手写派生它——规格验收明确把项目 JSON 列为派生表面,且默认漂移缺陷正活在结构体与读取器之间的缝隙里。风险由基线往返语料库兜底。
清单进rcc.qrc入库打包;仅构建期v1 仅构建期——尚无运行时消费者。R6/R8 需要运行时 schema 服务时再加 qrc 行;现在打包是死重与二进制体积成本。(当前仓库中该行已存在,对应后续规格的运行时需求。)
硬案例内联进生成器;命名钩子命名钩子——生成器保持"笨"发射器,约 10 条定制规则以真实类型与调试器手写在一个翻译单元里实现一次,registry-verify.py检查每个被引用钩子存在。
漂移校验器新脚本;扩展registry-verify.py扩展registry-verify.py——已接入sanitize-commit.py、已有收集错误/退出码形状;一个新规则函数加一行调用即继承报告能力。
缺陷修复纳入本规格;拆独立前置提交纳入本规格、显式断言——它们是动机证据,基线测试可以钉死精确差异;拆开要第二次基线捕获。
嵌套实体派生;保持手写保持手写——它们是有自己 v3.3 迁移的另一种实体的集合;派生它们需要清单 v1 不具备的实体嵌套。

风险与缓解

plan 把七类风险逐一配对缓解措施,其中三条最值得关注:

  1. 静默的项目文件回归(后果最高):一个与手写版本微妙不同的生成序列化器会损坏用户项目。缓解:在任何代码变更之前为整个examples/语料库捕获project.exportJson基线(第一个任务),然后断言字节相等、仅容许两个已声明差异。该测试是序列化任务的门禁而非事后想法。当前仓库中可见两层基线:tests/integration/baselines/ 下的按项目哈希命名的导出基线文件,以及规格目录内 baseline-manifest.json 记录的语料库输入文件 sha256("provisional-static-baseline",证明维护者捕获时输入文件未变,不替代实时的project.open+project.exportJson基线)。
  2. 表单回归只在运行时显现:行消失、不该可编辑时变可编辑、combo 失去域。缓解:清单通过逐行转录现有构建器编写(而非重新推导意图),生成的文件与删除的代码做 diff 作为任务步骤,AC4 逐一走查每种控件类型的 dataset。
  3. 选择字段索引/值不匹配(最容易出错的转换,四种既有约定互不兼容):OptionSource适配器在任何 combo 属性被声明之前落地并评审;AC13 通过 API 按值设置每个枚举字段并读回。

其余风险:提交期重入(生成分发器永不重建,返回即可,重建时机归手写槽或命名onCommit钩子);serialize与实时帧广播共享(scope字段强制,registry-verify.py对既未声明又未列为 runtime-only 的Dataset字段失败,五个运行时字段必须被显式点名);构造闭包/组合根暴露(描述符表constexpr、无动态初始化、无单例可达,PropertyHooks选项提供者从调用方按引用取模型而非调instance(),任何需要单例的钩子是评审停机项);生成代码评审疲劳(约 1,400 行生成 C++ 按表面拆任务落地:表 → 序列化 → 表单 → 提交 → API,各自独立可验,被删除的手写块引用在任务中);code-verify.py风格规则作用于生成输出(生成器从第一次发射起就对着code-verify.py --check开发,clang-formatsanitize-commit.py中对生成文件照常运行,生成器必须格式稳定——生成输出已含clang-format off/on围栏以杜绝 reformat 与--check互相打架)。

测试与验证计划

按 plan 的组织方式:

  • 单元(可本地跑):无tests/scripts/表面(无 JS 解析器变更)。生成器本身跑两遍 diff(AC8),并对播种过的清单编辑跑--check(AC7)。
  • 集成(维护者运行,应用需带 API 服务器在 7777 端口启动)——test_property_registry.py:
    • AC2:对examples/与每个内置模板下的每个项目:project.openproject.exportJson,对入库基线字节比较,仅容许两个已声明差异;
    • AC3overviewDisplay存活保存/重载;省略fftSamplesfftSamplingRateledHighindex的项目加载到与新建 dataset 相同的值;
    • AC6/AC12:dataset 动词 schema 列出类型化字段属性;schema-description 动词返回可写字段列表;每个被描述的字段可设置可读;
    • AC11:经 API 读一个 dataset、把返回对象原样写回:无unknown_field警告、无值丢失;
    • AC13:按值设置每个枚举字段并读回不变。
    • test_project_undo.py(扩展,AC5):多字段project.dataset.update是一个撤销步骤;撤销恢复所有字段。
  • 维护者观察AC4——plot / FFT / bar-gauge-meter / compass / LED / waterfall / virtual / painter 子 dataset 的表单与前一构建逐字段一致;AC9——翻译抽取在ProjectEditor上下文中找到每个属性字符串,无孤儿。
  • 热路径:提交前跑一次--benchmark-hotpath
  • 静态python scripts/code-verify.py --check(含新硬编码键规则);python scripts/registry-verify.py(含新清单规则);python scripts/generate-property-registry.py --check;对手写 C++ diff(钩子 + 缩减后的调用点——而非生成主体)跑qt-cpp-review;提交前python scripts/sanitize-commit.py

这些验收标准在 spec.md 中已全部勾选为完成(规格状态done,2026-08-20 关闭),且生成产物、清单、校验脚本与测试文件在当前仓库中均可核实存在。

给读者的操作地图

  • 想理解声明本身:读 dataset.json(条目逐字段自解释,头部notes说明转录来源与缺陷标记语义),形状约束见 schema.json;
  • 想理解生成器契约:读 generate-property-registry.py 头部文档(四个输出路径、确定性策略、--check/--check-snapshot语义);
  • 想理解漂移门禁的流水线位置:读 sanitize-commit.py 与 registry-verify.py 的check_property_manifests
  • 想理解逃生舱:读 PropertyHooks.h(校验器、谓词、选项提供者、提交副作用的命名接口);
  • 想复现验收:tests/integration/test_property_registry.py+ tests/integration/baselines/ 基线,配合带 API 服务器的运行实例。

小结

规格 0036 的价值不在于"少写代码"这一表面数字(虽然 R11 确实要求把前后手写行数作为具体数字报告),而在于把一个跨五个翻译单元、三种惯例、八处声明且已经实际漂移出两个缺陷的表面,收敛为"一条 JSON 条目 + 一次再生成 + 三道门禁":清单对 schema 校验、生成器--check字节比较、注册表 verify 规则,外加基线往返测试钉死唯一的预期字节差异。它同时保留了该仓库最敏感的三条边界——Dataset结构体保持alignas(8)热路径形状不变、撤销语义完整继承自规格 0031、QML 委托零改动——并为后续表面(group 实体、MCP/gRPC schema、SDK setter、问题中心与内建助手的运行时 schema 服务)留下了声明即接口的扩展位。对于任何需要让"模型字段、持久化、表单、API"保持同步的 Qt/C++ 大型应用,这是一份可直接对照的工程范式。

【免费下载链接】Serial-StudioOpen-source telemetry dashboard. Supports UART, BLE, MQTT, Modbus, CAN Bus and more.项目地址: https://gitcode.com/GitHub_Trending/se/Serial-Studio

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 6:46:52

工业互联网定位技术选型与UWB/TDOA部署实战

简介&#xff1a;位置定位技术是工业互联网实现智能制造与智能物流的关键支撑。这份PPT以AGV自动搬运仓储为应用场景切入&#xff0c;系统讲解定位技术的定义、作用与分类&#xff0c;重点覆盖GPS、BDS、GLONASS、Galileo等室外定位系统&#xff0c;以及Wi-Fi、蓝牙、UWB等室内…

作者头像 李华
网站建设 2026/9/17 6:45:40

AI辅助STM32第一个工程:从代码到点灯的工程链路

第一次让AI帮我搭STM32工程&#xff0c;是在一个挺普通的晚上。我把需求敲给它&#xff1a;"用STM32F103C8T6&#xff0c;标准库&#xff0c;PA5接LED&#xff0c;主频72MHz&#xff0c;写一个闪烁程序。"它几秒钟吐回来六十多行代码&#xff0c;结构工整、注释齐全&…

作者头像 李华
网站建设 2026/9/17 6:45:34

Fluent Bit 内嵌 rbtree 库:零分配侵入式红黑树的源码级解析

Fluent Bit 内嵌 rbtree 库&#xff1a;零分配侵入式红黑树的源码级解析 【免费下载链接】fluent-bit Fast and Lightweight Logs, Metrics and Traces processor for Linux, BSD, OSX and Windows 项目地址: https://gitcode.com/GitHub_Trending/fl/fluent-bit 导读 …

作者头像 李华
网站建设 2026/9/17 6:45:16

海信2026新品解析:Dolby Vision 2、Soundbar与三色激光投影

直接说结论&#xff1a;海信这次2026年的新品布局&#xff0c;方向非常明确&#xff0c;就是要用一场“组合拳”把客厅影音体验的整体水位拉高。Dolby Vision 2从单纯画质技术升级为全链路校准方案&#xff0c;新Soundbar直接对标三星的旗舰级回音壁&#xff0c;投影仪产品线从…

作者头像 李华