Qt接口框架模拟系统

在开发新的API时,可能不会立即存在API所需要的服务。这是因为,API已经设计完成,但服务本身仍在开发中。例如,针对自动驾驶等新概念,用户界面交互及其API已经设计,但自动驾驶服务尚未完成。这种开发周期需要将API开发与服务开发分离。动态后端系统提供了这种分离的架构。

一旦实现了这种分离,开发API的下一步就是模拟其行为,以反映原始服务。Qt接口框架模拟系统支持以下用例

  • 在QML中定义简单的模拟逻辑。
  • 一个灵活的系统,为任何C++ API提供模拟。
  • 模拟数据和模拟逻辑之间的清晰分离。
  • 在运行时更改模拟的机制;适用于自动化测试。
  • 与Qt接口框架生成器工具的集成。

架构

由于模拟系统基于动态后端系统构建,API分片遵循相同的模式

"Relationship between the Feature and the Backend"

每个后端插件都需要实现后端接口,以便向前端提供必要的功能。例如,为QIfClimateControl前端API提供的QIfClimateControlBackendInterface类。

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

"QtInterfaceFramework Simulation System"

QML API

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

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

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

void increment() {
    counter++;
}

当我们自动生成类时,不能自动生成increment()的实际行为,因为没有方法可以告诉autogenerator函数应具有的行为类型。为了定义这种行为,您需要用C++实现完整的后端。

QIfSimulationEngine (无需翻译)使这项任务更加灵活:它使用脚本将所有的C++调用转发到QML,允许您使用QML来定义行为。因此,您可以覆盖行为,也可以通过脚本指定默认行为,而不需要修改任何C++代码。

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

分离数据与逻辑

模拟系统使您可以将模拟业务逻辑与模拟数据分离。模拟数据存储在JSON文件中。这是为了使QIfSimulationEngine::loadSimulationData()函数能够加载。一旦加载了模拟数据,IfSimulator全局对象就会提供内容给所有QML模拟文件。更多关于数据格式的信息可以在此处找到。

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

property var settings : IfSimulator.findData(IfSimulator.simulationData, "QIfClimateControl")

边界检查

IfSimulator全局对象还提供了一些函数,以便更容易地进行边界检查。边界属性在JSON文件中定义,而QML代码保持通用,以配合多种不同的边界检查。

function setAirConditioningEnabled(airConditioningEnabled) {
    if (IfSimulator.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: " + IfSimulator.constraint_string(settings["airConditioningEnabled"]));
    }
}

使用IfSimulator::checkSettings()函数检查指定的airConditioningEnabled值是否在JSON文件中定义的边界内。如果值在边界内,则更新该值;否则返回错误,并附带以人类可读形式展示的约束。

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

覆盖机制

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

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

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

QTIF_SIMULATION_OVERRIDE=<identifier>=<file>[;<identifier>=<file>]
QTIF_SIMULATION_DATA_OVERRIDE=<identifier>=<file>[;<identifier>=<file>]

整合到Qt接口框架生成器

模拟系统已经整合到Qt接口框架生成器工具中,并且在使用backend_simulator格式生成代码时自动使用。

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

然后,将config_simulator中定义的所有边界注释转换成一个JSON文件,并将其嵌入为资源文件到后端。

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

自定义您的模拟文件

使用由simulationFile注释定义的QML文件可能并不总是方便。您也可以使用自己的QML文件来定义。

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

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

import 'qrc:/simulation/'

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

© 2024 Qt公司有限公司。本文件中包含的文档贡献归各自所有者拥有。本文件提供的文档是根据自由软件开发基金会发布的版本1.3的GNU自由文档许可证条款许可的。Qt及其相应标志是芬兰以及全球其他国家的Qt公司商标。商标。所有其他商标为各自所有者的财产。