技术文章和时事通讯

Simulink 模型验证之持续集成

作者 David Boissy, Paul Urban, Krishna Balasubramanian, Pablo Romero Cumbreras, Colin Branch, and Jemima Pulipati, MathWorks


此文章分为两部分。本文为其中的上篇。第一部分介绍了如何利用 GitLab® 进行版本控制,以及如何利用 Jenkins 进行持续集成 (CI)。第二部分使用 GitLab 进行 Simulink 模型验证的持续集成将介绍使用 GitLab 进行版本控制和 CI。

持续集成 (CI) 越来越受欢迎,并且正在成为基于模型设计不可或缺的一部分。但什么是 CI?它有哪些优点?它试图解决什么问题?Simulink® 如何融入 CI 生态系统?如何在您的项目中充分利用 CI?

如果您熟悉基于模型的设计,但对 CI 很陌生,那么您可能会问自己这些问题。在这篇技术文章中,我们会探讨一个常见的 CI 工作流,并将其应用于基于模型的设计中。然后,我们将使用 Jenkins™、GitLab® 和 Simulink Test™ 演示此工作流的一个示例。

此示例中使用的工程可以直接在此下载

什么是 CI?

CI 是一种敏捷方法的最佳实践。采用该实践时,开发人员会定期将源代码变更提交并合并到一个中央仓库中。然后这些“变更集”会自动构建、验证和发布。图 1 展示了这个基本的 CI 工作流以及开发工作流。

图 1.CI 工作流。

图 1. CI 工作流。

在工作流的开发部分,模型和测试会经过开发、测试、合并、评审,然后提交到开发人员桌面端的版本控制系统。然后,版本控制系统会触发工作流的自动化 CI 部分。CI 工作流的关键部分包括:

构建:源代码和模型变成了目标文件和可执行文件。

测试:测试是作为质量阀而执行的。

打包:将可执行文件、文档、交付物以及其他可交付项包装在一起,以交付给最终用户。

部署:将软件包部署到生产环境中。

这四个步骤合在一起被称为 CI 管道。这种管道通常是自动化的。根据系统的不同,管道可能需要几分钟到几天的时间来完成。值得注意的是,这些步骤会创建大量交付物,例如材料清单、测试结果和报告。

CI 工作流通常与版本控制系统相关的开发人员工作流搭配使用。在这些工作流中,开发人员通常将其变更保存在本地仓库中,并在部署之前使用本地 CI 管道来认证变更。

CI 有哪些优点?

实施了 CI 的团队通常会列举以下优点:

  • 可重复性:CI 管道为构建、测试、打包和部署提供了一致且可重复的自动化流程。可重复的自动化使开发人员可以专注于必要的工作,并节省在项目上投入的时间。这也是降低风险的一个重要方面,通常是认证的必要条件。
  • 质量保证:手动测试虽然有效,但是它通常基于几天前的快照,并且缺乏可重复性。使用 CI 时,变更的测试使用的总是最新的代码库。
  • 缩短开发时间:具有内置质量保证的可重复流程可更快地交付高质量产品。自动化部署意味着您的代码随时可以用于生产环境。
  • 改进协作:使用 CI,开发人员有明确的流程来管理变更集,并将代码合并到生产线中。一致的流程使管理大型团队成为可能,并可降低培训新开发人员的成本。
  • 审计就绪的代码:CI 工作流提供了详尽的审核线索。对于通过 CI 管道的每一个变更,都可以确定是谁做出了变更、谁审核了变更、变更的性质、依赖关系、测试及其结果,以及在此过程中生成的任何数量的相关报告和交付物。

基于模型的设计如何融入 CI?

在设计上,CI 工作流和工具是语言和领域中立的。这意味着挑战在于“教会” CI 工具、系统和流程使用基于模型的设计的“语言”;换言之,使 Simulink® 和相关工具成为 CI 工作流的“通用语”。

这可以通过将基于模型的设计的三个关键组成部分集成到 CI 工作流中来完成:验证、代码生成和测试(图 2)。基于模型的设计强调早期验证,这在 CI 管道中体现为“构建”阶段之前的“验证”阶段。代码生成发生在“构建”阶段。生成代码的静态分析以及通过对其仿真来完成的动态测试在“测试”阶段完成。

图 2.基于模型的设计到 CI 管道的映射。

图 2. 基于模型的设计到 CI 管道的映射。

以下概述了我们如何“教会” CI 工作流使用基于模型的设计的“语言”:

开发:MATLAB®、Simulink、代码生成器和工具箱用于开发活动。使用 MATLAB 工程组织工作、与他人协作并与版本控制系统交互。

测试:使用 Simulink Check™ 在仿真和代码生成之前执行模型质量检查。使用 Simulink Test™ 开发、管理和执行基于仿真的测试。使用 Simulink Coverage™ 测量覆盖率和评估测试有效性。质量检查、测试结果和覆盖率指标又可进一步用作开发人员认证其工作的质量阀。

合并:使用 MATLAB 的“比较文件和文件夹”功能比较和合并 MATLAB 文件。使用模型比较工具比较和合并 Simulink 模型。

评审:评审是将变更提交到版本控制系统之前质量过程的最后一步。对 MATLAB 脚本和 Simulink 模型的变更将在这步进行评审。作为提交前的最后质量阀,来自预认证的测试结果也会进行评审。

提交:MATLAB 工程提供了与版本控制系统集成的接口。

验证:之前用作本地验证工具的 Simulink Check 也会被用于 CI 系统内的自动验证。

构建:使用 MATLAB Coder™、Simulink Coder™ 和 Embedder Coder® 生成用于软件在环 (SIL) 测试的代码。

测试:之前用作本地测试工具 Simulink Test 也会被用于 CI 系统内的自动测试。

打包和部署:打包是指将可执行文件、文档、交付物以及其他可交付项包装在一起,以交付给最终用户。部署是指发布打包的软件。在基于模型的设计工作流中,这些阶段在不同的组织和团队中有很大差异,但通常涉及将不同的版本和认证交付物打包到一个可交付给其他团队的产品中。

现代开发工具和实践使开发人员能够更早地创建更稳健的系统和测试功能并将其快速迭代。 当 CI 系统集成到工作流后,单元级测试和系统级测试将自动执行。这意味着开发人员可以专注于开发新功能,而不是验证功能是否已正确集成。 

以下案例研究描述了结合 CI 和基于模型的设计的工作流。

案例研究:在 CI 系统中验证、构建和测试的 Simulink 模型

在此示例中,我们使用基于模型的设计和 CI 对汽车车道跟随系统执行基于需求的测试(图 3)。

图 3.车道跟随系统模型。

图 3. 车道跟随系统模型。

每次 Jenkins 构建模型,我们将使用的管道(图 4)都会执行。

图 4.车道跟随示例的管道。

图 4. 车道跟随示例的管道。

管道中的各个阶段如下:

  1. 验证标准合规性:一个 MATLAB 单元脚本运行简单的模型顾问检查。该评估标会准确保模型没有未连接的线。
  2. 构建模型:一个 MATLAB 单元测试文件为我们的模型构建产品级 SIL 代码。如果构建成功且没有警告,则为通过评估标准。
  3. 执行测试用例:Simulink Test 中的一个测试套件使用多个驾驶场景来测试车道跟随控制器。使用三个评估标准来验证控制器的运行是否令人满意:
    • 防撞:在驾驶场景中,自主车辆在任何时候都不会与前车发生碰撞。
    • 安全距离保持:自主车辆和前车之间的时间间隔超过 1.5 秒。两辆车之间的时间间隔定义为算出的车头距与自主车辆速度的比率。
    • 车道跟随:与车道中心线的横向偏差在 0.2 米以内。
  4. 打包交付物:前面的每个阶段都会生成交付物,包括一份模型顾问报告、一个生成的可执行文件,以及一组可存档供将来使用或参考的测试结果。

工作流步骤

工作流包括以下步骤(图 5):

  1. 触发 Jenkins 构建,并观察验证和构建阶段是否通过。
  2. 检测到 Jenkins 测试用例失败。
  3. 在桌面端 MATLAB 上重现该问题。
  4. 通过放宽评估标准来修复模型中的问题。
  5. 在本地运行测试,确保测试用例通过。
  6. 合并和评审测试分支上的变更。
  7. 将变更提交到 Git 并触发 Jenkins 进行构建。
  8. 在 Jenkins 中验证、构建和测试
图 5.示例工作流。

图 5. 示例工作流。

左上角显示了我们第一次失败的 CI 循环。它显示了 CI 测试失败、本地重现、标准放宽以及 CI 工作流的成功完成。

工作流详细信息

  1. 我们首先通过选择 Build Now(立即构建)在 Jenkins 中触发构建。Simulink Check 检查和代码生成成功通过。
通过选择 Build Now(立即构建)在 Jenkins 中触发构建
  1. 接下来,我们在第二个验证阶段检测到测试用例失败。测试套件 LaneFollowingTestScenarios 中的测试用例 LFACC_Curve_CutInOut_TooClose 未通过评估标准。
在第二个验证阶段检测到测试用例失败
  1. 为了更好地了解失败原因,我们使用 Simulink Test 在本地将其重现。我们打开测试文件 LaneFollowingTestScenarios.mldatx 并运行测试案例 LFACC_Curve_CutInOut_TooClose。请注意,它未达到“安全距离”评估标准。也就是说,我们需要更灵活地设定前车与自主车辆之间的时间间隔。
使用 Simulink Test 在本地重现失败
  1. 了解问题后,我们现在修复问题。我们打开 LaneFollowingTestBenchExample.slx 模型并导航至名为 Collision Detection/Test Assessments 的 Test Sequence 模块。第一个评估断言:自主车辆和先导车辆之间的时间间隔低于 1.5 秒的时长每次不应超过 2 秒。
名为 Collision Detection/Test Assessments 的 Test Sequence 模块

该评估对于正在测试的激进驾驶操作来说过于严格。在本示例中,我们放宽了评估标准,以确保时间间隔低于 0.8 秒的时长每次不超过 5 秒。

名为 Collision Detection/Test Assessments 的 Test Sequence 模块
  1. 该问题在我们的仿真中似乎得到了修复。为了确认,我们进行本地测试,既保存模型并在测试管理器中重新运行测试。请注意,它通过了新的评估标准。
放宽评估标准
  1. 我们已经修复问题并在本地进行了验证。现在,我们使用模型比较工具来评审变更,然后再将其提交到版本控制。
评审变更

我们还可以使用模型比较工具的发布功能来评审代码。

通过新的评估标准
  1. 修复错误后,我们使用 MATLAB 工程将这些变更推送到 GitLab,并添加一条提交消息来记录对评估标准的变更。
提交

随后我们注意到最新的变更已提交至 GitLab。

GitLab 会自动触发 Jenkins 构建。Jenkins 工程控制板显示构建状态和进度。

构建状态
  1. Jenkins 构建运行。现在验证、构建和测试管道阶段已可通过。

我们来创建一条合并请求,将测试分支中的变更合并到主分支中。在 GitLab 中,在 Repository(仓库)下选择 Branches(分支),然后单击测试分支最新提交旁边的 Merge request(合并请求)。

我们填写表单并提交合并请求。

作为分支的所有者,我们可以点击 Merge(合并)按钮来接受合并请求。所有变更现已合并至主分支。

合并成功

使用示例:工具、资源和需求

以下部分概述了可帮助您入门的资源、所需的工具及其配置方法。

配置系统

Jenkins 是我们的 CI 系统,GitLab 是我们的版本控制系统。MATLAB、Jenkins 和 GitLab 必须得配置后才能协同工作。以下教程可提供设置方面的帮助。

配置我们的 MATLAB 工程

配置 Jenkins

配置 GitLab 以触发 Jenkins

这些教程专门针对 GitLab 和 Jenkins,但其中的概念也适用于其他版本控制和 CI 系统。

所需工具

本示例需要以下工具:

  • Jenkins 安装版本 2.7.3 或更高。使用 Jenkins 进行持续集成。
  • MATLAB Plugin for Jenkins 1.0.3 或更高版本。MATLAB、Simulink 和 Simulink Test 都利用此插件与 Jenkins 进行通信。前往 GitHub 了解更多信息。 
  • 所需的其他插件:
  • GitLab 帐户。使用 GitLab 作为源代码管理系统。GitLab 还提供云版本。MATLAB 工程包括用于与 GitLab 通信的 Git 接口。

CI 的许可注意事项

如果您计划在大量主机或云上执行 CI,请联系 continuous-integration@mathworks.com 寻求帮助。注意:转换工具(例如 MathWorks 的编码器和编译器产品)可能要求客户端访问许可证 (CAL)。

附录:配置 MATLAB、GitLab 和 Jenkins

步骤 1:配置 MATLAB 工程以使用源代码管理

我们示例中的第一步是配置我们的工程以使用 GitLab 的源代码管理。

  1. 创建一个名为 MBDExampleWithGitAndJenkins 的新目录,将示例加载到其中,然后打开 MATLAB 工程 MBDExampleWithGitAndJenkins.prj
  2. 在 GitLab 中,创建将作为远程仓库的新工程。将其命名为 MBDExampleWithGitAndJenkins,并记录托管它的 URL。
  3. 在 MATLAB 中,转换工程以使用源代码控制。在 Project(工程)选项卡上,单击 Use Source Control(使用源代码管理)。
使用源代码管理

单击 Add Project to Source Control(将工程添加到源代码管理)。

将工程添加到源代码管理
  1. 单击 Convert(转换)。
选择 Convert(转换)
  1. 完成后单击 Open Project(打开工程)。
打开工程

该工程现已处于本地 Git 源代码管理下。

步骤 2:提交变更并将本地仓库推送到 GitLab

  1. 在 Project(工程)选项卡上,单击 Remote(远程库)。
远程库
  1. 指定 GitLab 中的远程源的 URL。
远程源的 URL

单击 Validate(验证)以确保与远程仓库的连接成功,然后单击 OK(确定)。该工程现已配置为使用 GitLab 推送和拉取变更。

  1. 单击 Commit(提交)以执行初始提交。
选择 Commit(提交)
输入注释
  1. 单击 Push(推送),将所有变更从本地仓库推送到远程 GitLab 仓库。
选择 Push(推送)
  1. 刷新 GitLab 控制板并查看 MATLAB 工程的内容。
GitLab 控制板

步骤 3:创建测试分支

在此步骤中,我们会创建一个测试分支,用于在与主分支合并之前测试和验证变更。

  1. 单击 Branches(分支)。
选择 Branches(分支)
  1. 展开 Branch and Tag Creation(分支和标签创建)部分,将分支命名为 Test,然后单击 Create(创建)。
选择 Branch and Tag Creation(分支和标签创建)
  1. 在分支浏览器中查看 Test。从 Test 分支依次单击 Switch(切换)和 Close(关闭)。
在分支浏览器中查看 Test
  1. 在 MATLAB 中,选择 Push(推送)将这些变更推送到 GitLab,然后在 GitLab 中查看 Test 分支。
选择 Push(推送)将这些变更推送到 GitLab,然后在 GitLab 中查看 Test 分支

步骤 4:配置 Jenkins 以调用 MATLAB

  1. 安装两个必需的插件:
    • GitLab 插件 - 此插件允许 GitLab 触发 Jenkins 构建,并在 GitLab UI 中显示其结果。
    • MATLAB 插件 - 此插件将 MATLAB 与 Jenkins 集成,并提供 Jenkins 接口来调用 MATLAB 和 Simulink。
  2. 选择 New Item(新建任务)并创建一个名为 MBDExampleUsingGitAndJenkins 的新 FreeStyle(自由风格)工程。
  3. 在 Source Code Management(源代码管理)下,启用 Git,将 Jenkins 指向我们的 GitLab 仓库,然后输入要构建的 Test 分支。注意:需要登录名或密码以及 GitLab API 令牌。
源代码管理
  1. 配置构建触发器,使其在 GitLab 中的 Test 分支收到推送请求时运行构建。在 Build Triggers(构建触发器)部分中,选择 Advanced(高级)> secret token(机密令牌)。GitLab 使用此令牌请求构建并向 Jenkins 进行身份验证。记下机密令牌和 GitLab webhook。
  1. 配置构建环境。选择 Use MATLAB version(使用 MATLAB 版本),然后输入 MATLAB 根目录。
构建环境
  1. 配置构建步骤。

单击 Add build step(添加构建步骤),然后选择 Run MATLAB Command(运行 MATLAB 命令)。输入命令 openProject('SltestLaneFollowingExample.prj'); LaneFollowingExecModelAdvisor
以打开该工程并运行模型顾问检查。

单击 Add build step(添加构建步骤),然后再次选择 Run MATLAB Command(运行 MATLAB 命令)。输入命令:openProject('SltestLaneFollowingExample.prj'); LaneFollowingExecControllerBuild

单击 Add build step(添加构建步骤)并选择 Run MATLAB Tests(运行 MATLAB 测试)。 选择 TAP test results(TAP 测试结果)和 Cobertura code coverage(Cobertura 代码覆盖率)以完成构建配置。

构建配置

步骤 5:发布 TAP 结果

单击 Add post-build action(添加构建后操作)> Publish TAP Results(发布 TAP 结果)。输入将发布 TAP 测试结果的相对路径。

发布 TAP 结果

此操作将解析 TAP 测试结果,并且在选中 TAP Extended Test Results(TAP 扩展测试结果)后显示这些结果。输出包含已执行测试用例的概览、结果摘要和来自 MATLAB 控制台的日志。

TAP 扩展测试结果
TAP 扩展测试结果

TAP 插件还收集最新测试执行的结果,并显示如下所示的运行状况图表。您可以通过点击该图表访问以前的任何构建。

TAP 测试

步骤 6:发布 HTML 报告

单击 Add post-build action(添加构建后操作)> Publish HTML Results(发布 HTML 结果)。输入将发布 HTML 报告的相对根路径,以及该路径中索引页的文件名。

发布 HTML 报告

添加与要发布的 HTML 报告一样多的条目。在此场景中,有两个 Web 报告:模型顾问摘要报告和代码生成报告。这些是使用 MATLAB 内置函数创建的标准报告。您可以添加自定义 HTML 报告。

Jenkins 任务主页面上的每个 HTML 报告链接都指向最新构建的报告。 如果您选择了发布选项下的 Always link to last build(始终链接到最新构建)复选框,该插件将发布最新构建的报告,无论构建状态如何。如果未激活该复选框,该插件将只链接到最新的“成功”构建。

Project MBD 示例

步骤 7:配置 GitLab 以触发 Jenkins 执行构建

配置 GitLab,使其在主分支上发生新推送时触发 Jenkins 执行自动构建。导航至 Settings(设置)> Webhooks。使用在配置构建触发器时由 Jenkins 提供的 Webhook URL 和机密令牌,然后选择 Push events(推送事件)。

注意:在 URL 部分使用完全限定的域名代替 localhost,以便 GitLab 可以找到 Jenkins 安装。

A screenshot of the Webhooks pop-up with the U R L form filled in, Secret Token field unfilled, and ‘Push Events’ checked off under ‘Trigger’ with a form to fill in the Trigger.

Test(测试)下拉框中,选择 Push Events(推送事件)以测试集成。GitLab 应显示消息 Hook executed successfully:HTTP 200,并且 Jenkins 将启动构建。

A screenshot of the Project Hooks pop-up with the enabled webhook details.

步骤 8:配置 Jenkins 通过 GitLab 身份验证

要在 GitLab 上自动发布 Jenkins 构建状态,必须配置 Jenkins 使其请求能够通过 GitLab 的身份验证。

  1. 在 GitLab 上创建个人访问令牌,并选中 API 作用域。
个人访问令牌
  1. 复制令牌并在 Jenkins Configure System 下创建一个 GitLab 连接。
    注意:连接可以在多个 Jenkins 任务上重复使用。如果用户至少具有 maintainer(维护者)权限,则可以全局配置。
GitLab

步骤 9:将 Jenkins 集成到 GitLab 管道中

要将 Jenkins 集成到 GitLab 管道中,您必须在 Jenkins 中配置 GitLab 连接,并将任务状态发布到 GitLab。

  1. 在 Jenkins 任务的 General(常规)部分选择 GitLab Connection(GitLab 连接)。
GitLab
  1. 添加一个构建后操作,将构建状态发布到 GitLab。
    注意:此操作没有参数并将使用现有的 GitLab 连接在 GitLab 上发布构建状态。它还会为每个提交和合并请求创建双向可追溯性。
将构建状态发布到 GitLab

步骤 10:可视化基于需求的测试指标 (R2020b)

基于需求的测试指标让您可以评估基于需求的测试活动的状态和质量。您可以使用模型测试仪表板来可视化指标结果。

测试指标
  1. 根据如下所示的函数创建一个名为 collectModelTestingResults.m 的文件。此函数将初始化指标引擎基础架构,并收集所有可用的模型指标。
 function collectModelTestingResults() % metric capability added in R2020a if exist('metric') metricIDs = [..."ConditionCoverageBreakdown" "CoverageDataService"..."DecisionCoverageBreakdown" "ExecutionCoverageBreakdown"..."MCDCCoverageBreakdown" "OverallConditionCoverage"..."OverallDecisionCoverage" "OverallExecutionCoverage"..."OverallMCDCCoverage" "RequirementWithTestCase"..."RequirementWithTestCaseDistribution" "RequirementWithTestCasePercentage"..."RequirementsPerTestCase" "RequirementsPerTestCaseDistribution"..."TestCaseStatus" "TestCaseStatusDistribution"..."TestCaseStatusPercentage" "TestCaseTag"..."TestCaseTagDistribution" "TestCaseType"..."TestCaseTypeDistribution" "TestCaseWithRequirement"..."TestCaseWithRequirementDistribution" "TestCaseWithRequirementPercentage"..."TestCasesPerRequirement" "TestCasesPerRequirementDistribution"...]; % collect all metrics for initial reconcile E = metric.Engine(); execute(E, metricIDs); end end 
  1. 将此文件添加到您的工程和路径中。
  2. 配置 Jenkins 通过调用两次新添加的 collectModelTestingResults 函数来收集指标结果。第一次调用会初始化与 Simulink 测试管理器的指标集成。第二次调用使用导出的 Simulink 测试管理器结果收集指标结果。
    1. 单击 Add build step(添加构建步骤),然后再次选择 Run MATLAB Command(运行 MATLAB 命令)。输入命令:openProject('SltestLaneFollowingExample.prj'); collectModelTestingResults
      将此构建步骤置于 Run MATLAB Tests(运行 MATLAB 测试)构建步骤之前。
    2. 单击 Add build step(添加构建步骤),然后再次选择 Run MATLAB Command(运行 MATLAB 命令)。再次输入命令:openProject('SltestLaneFollowingExample.prj'); collectModelTestingResults
      将此构建步骤置于 Run MATLAB Tests(运行 MATLAB 测试)构建步骤之后。
测试指标

iii.在 Run MATLAB Tests(运行 MATLAB 测试)构建步骤中选中 Simulink Test Manager results(Simulink 测试管理器结果)。

测试结果
  1. 将指标结果归档到 derived 目录中。您还必须归档导出的测试管理器结果,因为这将允许在加载回 MATLAB 时对指标结果进行全面导航。

单击 Add post-build action(添加构建后操作),然后选择 Archive the artifacts(归档交付物)。输入路径 derived/**,matlabTestArtifacts/*.mldatx 来归档保存到该目录的所有文件。

构建后操作

注意:要在测试机器以外的机器上使用 MATLAB 查看这些结果,请执行以下操作:

  • 下载归档的交付物(derived 目录和测试结果 .mldatx 文件)。
  • 提取并复制到用于运行 CI 任务的工程的同一版本的本地副本中。
  • 在 MATLAB 中打开该工程并启动模型测试仪表板。

CI 生成的结果将显示在仪表板上。

出版年份 2022

查看文章,了解相关行业