3. 版本命名与编号规范设计

在 TwinCAT 项目里,版本号并不是一个“写在说明文档里好看”的装饰字段,它同时承担三种职责:对外沟通兼容性、对内标识可追溯的构建、以及在自动化构建流水线中作为唯一坐标。如果只用一套数字承担全部职责,很快就会陷入“版本号到底代表什么”的混乱。因此本节把版本标识拆成三层:语义化版本(SemVer)、内部版本号和构建号,并给出它们在 TwinCAT 工程中的具体落地规则。

三层版本标识的职责边界

语义化版本采用 MAJOR.MINOR.PATCH 三段式,含义是公开承诺:MAJOR 变化表示存在不兼容的接口或行为变更,MINOR 表示向后兼容地新增功能,PATCH 表示向后兼容的问题修复。它面向的是“使用方”,包括上位系统、PLC 之间的接口调用方、以及最终客户。

内部版本号是团队内部用于区分同一语义版本下不同开发阶段的标识,常见形式是 MAJOR.MINOR.PATCH-阶段.序号,例如 2.4.0-alpha.3、2.4.0-rc.1。它回答的是“这是 2.4.0 的哪个中间状态”,而不是“对外兼容性如何”。

构建号是流水线自动生成的单调递增整数,例如 build.1847。它不表达兼容性,只表达“这是第几次构建产物”,用于把某个二进制、某个库文件、某次测试报告精确对应到一次构建记录。三者关系可以用下面的结构图表示。

TwinCAT 项目三层版本标识结构 语义化版本 SemVer MAJOR.MINOR.PATCH 对外兼容性承诺 例:2.4.0 面向:接口调用方 / 客户 内部版本号 MAJOR.MINOR.PATCH-阶段.序号 开发阶段标识 例:2.4.0-rc.1 面向:开发 / 测试团队 构建号 Build build.NNNN 流水线单调递增 例:build.1847 面向:构建 / 追溯系统 完整版本串(写入项目信息与产物元数据) 2.4.0-rc.1+build.1847 PLC 项目信息 Project > Info Version / Company 库文件与 GSD Library Version Device Description 构建产物命名 *.tszip / *.library CI 归档与追溯

语义化版本在 TwinCAT 中的判定规则

在 TwinCAT 工程中,MAJOR 的触发条件应当明确写进团队规范,而不是凭感觉。建议把以下变更判定为 MAJOR:PLC 库中公开的 FUNCTION_BLOCK 或 FUNCTION 的输入输出变量被删除或改名;VAR_IN_OUT 的引用类型发生变化;枚举值被删除或重新编号;ADS 接口的索引组、索引偏移或数据结构长度改变。这些都会让已有调用方在编译或运行期失败。

MINOR 对应“新增但不破坏”:新增功能块、新增方法、新增可选输入、新增枚举成员且追加在末尾。PATCH 对应“修复但不改变接口”:修正内部逻辑、修正注释、修正不影响接口的常量值。需要特别注意的是,TwinCAT 中修改 VAR_GLOBAL 常量区的初值,如果该常量被外部通过 ADS 读取,也可能被视为行为变更,应至少升 MINOR 并在变更记录中说明。

内部版本号与构建号的生成规则

内部版本号建议只使用四个阶段:alpha、beta、rc、release。其中 release 阶段可以省略后缀,直接使用三段式。序号从 1 开始递增,每次进入新阶段时重置。例如从 2.4.0-alpha.3 进入 beta 阶段,应变为 2.4.0-beta.1,而不是 2.4.0-beta.4。

构建号由 CI 流水线生成,规则是“每次触发构建就加一”,与语义版本无关。这样即使一天内对同一语义版本构建了 20 次,也能通过 build.1847 精确定位到某一次产物。构建号不应手工填写,也不应回退。若流水线重建历史构建,应分配新的构建号,而不是复用旧号。

落地到 TwinCAT 项目的具体位置

在 TwinCAT 3 中,版本信息至少应写入以下位置,并保持同源:

  • PLC 项目信息:在 PLC 项目右键 Project > Info 中填写 Version、Company、Description。Version 字段填写完整版本串,例如 2.4.0-rc.1+build.1847。
  • 库文件版本:若项目以库形式发布,在库管理器中设置 Library Version,建议与语义化版本一致,构建号写入库的 Description 或自定义属性。
  • GSD/ESI 设备描述:EtherCAT 从站描述文件的版本字段应与语义化版本对齐,避免主站组态时出现版本不匹配告警。
  • 构建产物命名:CI 归档的 .tszip 或库文件命名建议采用 项目名_2.4.0-rc.1+build.1847.tszip,使文件名本身即可追溯。

版本号与 TwinCAT 工程文件的同步示例

下面给出一个可参考的版本信息配置片段,用于在 PLC 项目中集中维护版本常量,便于运行时通过 ADS 读取并上报给上位系统。

// 版本信息常量定义(示例)
VAR_GLOBAL CONSTANT
    gcVersionMajor   : UINT := 2;
    gcVersionMinor   : UINT := 4;
    gcVersionPatch   : UINT := 0;
    gcVersionStage   : STRING(8) := 'rc.1';
    gcBuildNumber    : UDINT := 1847;
END_VAR

// 运行时拼接完整版本串(示例)
sFullVersion := CONCAT(
    CONCAT(
        CONCAT(
            CONCAT(
                CONCAT(
                    CONCAT(UINT_TO_STRING(gcVersionMajor), '.'),
                    UINT_TO_STRING(gcVersionMinor)),
                '.'),
            UINT_TO_STRING(gcVersionPatch)),
        '-'),
    gcVersionStage);
// 结果:2.4.0-rc.1

这段代码的价值在于:版本号不再只存在于项目信息对话框里,而是成为 PLC 程序可读取的常量。上位系统可以在连接建立后读取该常量,与自身期望版本比对,从而在运行期发现版本错配,而不是等到功能异常才排查。

建议把 gcBuildNumber 的赋值交给 CI 脚本自动替换,而不是手工修改。常见做法是在构建前用脚本读取流水线变量并写入 .tcgvl 或对应的常量文件,避免人为遗漏。

版本号变更的检查清单

在提交版本号变更前,建议按以下顺序检查,确保三层标识一致且可追溯:

检查项 判断依据 示例
接口是否破坏 公开变量、枚举、ADS 索引是否变化 删除输入变量 → MAJOR +1
是否仅新增功能 旧调用方无需修改即可编译 新增方法 → MINOR +1
是否仅修复 接口与行为契约不变 修正内部计算 → PATCH +1
阶段是否正确 alpha/beta/rc 序号是否重置 alpha.3 → beta.1
构建号是否唯一 CI 自动生成且单调递增 build.1847 → build.1848
三处信息是否同源 项目信息、库版本、产物命名一致 均为 2.4.0-rc.1+build.1847
不要用构建号代替语义化版本对外发布。构建号只保证唯一性,不表达兼容性;把 build.1847 当作版本号交给客户,会让对方无法判断升级风险。

把三层标识分开之后,TwinCAT 项目的版本治理就有了清晰的判断依据:对外看语义化版本,对内看内部版本号,追溯看构建号。三者各司其职,才能在多人协作、多版本并行和自动化构建的场景下保持可解释、可追溯、可复现。