Qt IVI 模拟系统
当您开发新的 API 时,该 API 所需的服务可能尚未存在。这是因为,API 已经设计好,但服务本身仍在开发中。例如,对于像自动驾驶这样的新概念,UI 交互及其 API 已被设计,但自动驾驶服务尚未完成。这种开发周期需要将 API 开发与服务开发分离。动态后端系统提供了这种分离的架构。
一旦我们实现了这种分离,开发 API 的下一步就是提供一种模拟其行为的方法,以反映原始服务。Qt IVI 模拟系统使以下用例成为可能
- 在 QML 中轻松定义模拟逻辑
- 为任何 C++ API 提供灵活的模拟系统
- 模拟数据与模拟逻辑之间的明确分离
- 在运行时更改模拟的覆盖机制;对于自动测试很有用
- 与 IVIGenerator 工具的集成
架构
由于模拟系统是在 动态后端系统的基础上构建的,因此 API 分割遵循相同的方案
每个后端插件都需要实现后端接口,以为前端提供必要的功能。例如,对于QIviClimateControl前端 API,需要实现QIviClimateControlBackendInterface类。
在后端,来自前端的所有调用都转发到 QML,在那里我们可以脚本文本一个模拟行为。
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公司在芬兰以及其他国家的商标。所有其他商标均为其各自所有者的财产。