Qt IVI 模拟系统

当您开发新的 API 时,该 API 所需的服务可能尚未存在。这是因为,API 已经设计好,但服务本身仍在开发中。例如,对于像自动驾驶这样的新概念,UI 交互及其 API 已被设计,但自动驾驶服务尚未完成。这种开发周期需要将 API 开发与服务开发分离。动态后端系统提供了这种分离的架构。

一旦我们实现了这种分离,开发 API 的下一步就是提供一种模拟其行为的方法,以反映原始服务。Qt IVI 模拟系统使以下用例成为可能

  • 在 QML 中轻松定义模拟逻辑
  • 为任何 C++ API 提供灵活的模拟系统
  • 模拟数据与模拟逻辑之间的明确分离
  • 在运行时更改模拟的覆盖机制;对于自动测试很有用
  • 与 IVIGenerator 工具的集成

架构

由于模拟系统是在 动态后端系统的基础上构建的,因此 API 分割遵循相同的方案

"Relationship between the Feature and the Backend"

每个后端插件都需要实现后端接口,以为前端提供必要的功能。例如,对于QIviClimateControl前端 API,需要实现QIviClimateControlBackendInterface类。

在后端,来自前端的所有调用都转发到 QML,在那里我们可以脚本文本一个模拟行为。

"QtIvi Simulation System"

QML API

Qt IVI 模拟系统的核心是QIviSimulationEngine。该引擎扩展了QQmlApplicationEngine,并为连接 C++ 和 QML 逻辑提供了额外的功能。

每个后端都使用自己的模拟引擎来分离前端代码和后端 QML 代码。为了在 QML 和 C++ 对象之间提供绑定,必须在QIviSimulationEngine下以某个名称注册 C++ 实例。对于每个已注册的 C++ 实例,引擎创建一个代理对象,并将其作为 QML 类型公开。这些 QML 类型可用于提供函数的行为或更新属性。

例如,假设您的功能有一个名为increment()的函数和一个名为counter的属性。实际行为是在后端实现的。

void increment() {
    counter++;
}

当我们自动生成类时,不能自动生成increment()的实际行为,因为没有办法告诉自动生成器这个函数应该有什么样的行为。为了定义这种行为,您需要用 C++ 实现完整的后端。

QIviSimulationEngine使得这项任务更加灵活,因为它将所有 C++ 调用转发到 QML,允许您使用 QML 通过脚本文本定义行为。因此,您可以覆盖行为并获得默认行为,而不需要触摸任何 C++ 代码。

有关使用模拟引擎的更多信息,请参阅QIviSimulationEngine

分离数据与逻辑

模拟系统可以使您将模拟业务逻辑与模拟数据分开。模拟数据存储在JSON文件中,供QIviSimulationEngine::loadSimulationData()函数加载。一旦加载了模拟数据,IviSimulator全局对象将为所有QML模拟文件提供内容。

例如,您可以使用IviSimulator::findData函数只读取特定接口的数据

property var settings : IviSimulator.findData(IviSimulator.simulationData, "QIviClimateControl")

边界检查

IviSimulator全局对象还提供了便于进行边界检查的函数。属性边界在JSON文件中定义,而QML代码保持通用,以便与多个不同的边界检查一起工作

function setAirConditioningEnabled(airConditioningEnabled) {
    if (IviSimulator.checkSettings(airConditioningEnabled, settings["airConditioningEnabled"])) {
        console.log("SIMULATION airConditioningEnabled changed to: " + airConditioningEnabled);
        backend.airConditioningEnabled = airConditioningEnabled
    } else {
        setError("SIMULATION changing airConditioningEnabled is not possible: provided: " + airConditioningEnabled + " constraint: " + IviSimulator.constraint_string(settings["airConditioningEnabled"]));
    }
}

使用IviSimulator::checkSettings()函数检查指定的airConditioningEnabled值是否在JSON文件中定义的边界内。如果值在边界内,则更新;否则返回一个错误,并与人读形式一起返回约束条件。

有关模拟和数据格式的更多信息,请参阅IviSimulator

覆盖机制

对于应用开发或单元测试,在后台触发某种行为通常是有用的。例如,在实现错误恢复的消息框时,应用开发者可能需要一种简单触发此特定错误条件的方法。假设后端开发者为此用例提供的模拟行为不足。

在这种情况下,Qt IVI模拟系统提供了覆盖系统,通过环境变量加载您自己的模拟行为文件或数据文件。

每个QIviSimulationEngine都可以有额外的标识符来覆盖默认行为文件或数据文件,使用以下环境变量:

QTIVI_SIMULATION_OVERRIDE=<identifier>=<file>[;<identifier>=<file>]
QTIVI_SIMULATION_DATA_OVERRIDE=<identifier>=<file>[;<identifier>=<file>]

与IVIGenerator集成

模拟系统已经集成到IVIGenerator工具中,并在使用backend_simulator格式生成代码时自动使用。

自动生成的插件使用QFace模块名称作为QIviSimulationEngine标识符,以允许在运行时进行覆盖。

然后,所有在config_simulator中定义的边界注释都转换为JSON文件,并作为资源文件嵌入到后端。

为每个接口创建一个QML模拟文件,提供默认实现以检查每个属性的边界。

定义您自己的模拟文件

使用simulationFile注释,您还可以通过定义自己的QML文件来使用自动生成的QML模拟文件。

注意:如果您QFace文件提供了多个接口,相应的模拟文件必须为这些接口提供模拟。

要重用现有接口的自动生成模拟文件作为起始点,您可以使用QML导入语句加载这些自动生成的模拟文件:

import 'qrc:/simulation/'

之后,您应该能够像常规QML文件一样加载您的修改后的模拟文件。

©2020 Qt公司有限公司在此处提供的文档贡献是该所有者的版权。此处提供的文档是根据自由软件基金会发布的、由GNU自由文档许可证版本1.3规定的条款许可的。Qt及其相关标志是Qt公司在芬兰以及其他国家的商标。所有其他商标均为其各自所有者的财产。