模型生成、装配、加载和启动
基本Api
目标代码中使用的主要模块:
-
targets.base_target-目标类的契约和管理模型操作的方法(启动,运行,停止)。 -
targets.base_models-目标接收和返回的数据模型。 -
targets.code_info-模型的生成C代码的低级描述。
以下Api用于独立执行模式的目标代码:
-
BaseTarget设置所需的目标接口。 自定义类是从它继承的。 即使目标不支持交互模式,方法start_stream和change_param它们仍然应该实现,但要针对独立的执行模式,它们应该返回一个可以理解的错误。 -
EngeeModel包含模型ID。 在实践中,它最常用model.name:用于命名项目目录、草图、二进制文件或固件。 -
ModelSettings包含启动模型的设置。 要生成一个项目,它们尤其重要model_settings.cmi和model_settings.is_ext_mode. 本章讨论独立执行模式场景,所以is_ext_mode必须是False或者应该明确拒绝。 -
ModelCode包含生成的模型代码。 对于C-靶标,使用model_code.c_code:它有源文件的字典和一个字段c_code_info用元数据。 -
TargetResponse-靶向方法的标准响应。 对于独立的执行模式方法,返回就足够了TargetResponse(detail="…"),在哪里detail简要描述已完成的阶段。 -
CMIParser从读取*EDM-Target*块参数model_settings.cmi. 方法get_target_block_options将*EDM-Target*块掩码的值转换为用户平台设置的Pydantic模型。 -
create_codegen_info转换详细的CodeInfo以更方便的方式进行模板CodeGenInfo:C函数名称init/step/terminate、基期、步骤函数列表、模型停止时间和其他运行时参数。
-
render_template_to_file在项目文件中生成Jinja2模板:例如,main.c,.ino,CMakeLists.txt或Makefile. -
recreate_dir删除并重新创建项目目录。 这对于纯生成很方便,但仅对目标创建的目录使用此API。 -
store_model_src将模型的C源保存在项目中。 生成的 AnyMathmain.c它被跳过,因为目标从模板创建自己的入口点。 -
store_filed_sources保存嵌入在*C函数*模型块中的用户源文件。 如果目标平台需要支持来自模型的自定义C代码,请使用它。 -
CMakeWrapper-准备包装CMake组装。 如果目标平台正在经历,则可以使用它CMakeLists.txt. 他们通常为Arduino,制造商的SDK或他们自己的构建系统创建自己的构建器类。
代
方法 generate_executable_code 绑定 代码生成 AnyMath 具有自定义平台。 它不应该编译项目。 他的任务是在yandex上创建一个完整的项目。 磁盘,然后他将能够组装 compile_model.
该方法通常执行以下操作:
-
储蓄
model_settings,model_code以及后续步骤将需要的其他数据。 -
通过读取*EDM-Target*块参数
CMIParser. -
检查是否选择了支持的执行模式。
-
创建一个干净的项目目录。
-
皈依者
model_code.c_code.c_code_info在CodeGenInfo. -
Renderit运行时模板:
main.c,.ino,CMakeLists.txt,Makefile或IDE项目文件。 -
通过以下方式保存模型的C源
store_model_src. -
复制驱动程序、标头、布局文件、启动文件、SDK绑定和其他平台文件。
-
通过保存自定义*C函数*文件
store_filed_sources如果他们得到支持。 -
申报表
TargetResponse(detail="generate_executable_code").
结构示例 generate_executable_code
def generate_executable_code(
self,
model: EngeeModel,
model_settings: ModelSettings,
model_code: ModelCode,
) -> TargetResponse:
if model_settings.is_ext_mode:
raise TargetException("MyTarget supports only independent execution.")
self.model_settings = model_settings
self.target_block = self.cmi_parser.get_target_block_options(
model_settings.cmi,
MyTargetBlock,
self.__class__.__name__,
)
self.project_path = Path(self.target_block.codegen_folder) / model.name
recreate_dir(self.project_path)
project_src = self.project_path / "src"
project_src.mkdir(parents=True, exist_ok=True)
codegen_info = create_codegen_info(
model_code.c_code.c_code_info,
model_settings.is_ext_mode,
)
render_template_to_file(
self.main_template,
project_src / "main.c",
codegen_info.model_dump(),
)
store_model_src(model_code.c_code, project_src, codegen_info)
store_filed_sources(model_settings.cmi, project_src)
return TargetResponse(detail="generate_executable_code")
Jinja2-模板
Jinja2是一个模板引擎:它获取一个带有替换和控制结构的文本文件,获取一个值字典,并生成一个最终文件。 在targets中,需要Jinja2来生成依赖于特定模型的C/C++/CMake代码。
模板是需要的,因为模型的函数名称、基期、步骤函数列表和停止时间是事先未知的。 它们仅在代码生成后出现。 AnyMath . 目标通过 CodeGenInfo 并将它们传递给模板。
模板通常使用:
-
{{ name }}-价值的替代; -
{% for item in items %} … {% endfor %}-循环; -
{% if condition %} … {% endif %}-条件片段。
模板应该只描述平台绑定。 模型代码已经在它保存的文件中。 store_model_src.
模板片段示例 main.c
#include "{{ model_name }}.h"
static unsigned long step_number = 0;
int main(void)
{
platform_timer_init({{ base_rate_us }}UL);
{{ init.cname }}();
while (1) {
unsigned long start_time = platform_time_us();
{% for step in steps %}
if ((step_number % {{ step.base_rate_scale }}UL) == 0UL) {
{{ step.cname }}();
}
{% endfor %}
step_number++;
{% if stop_time %}
if ((step_number * {{ base_rate_us }}UL) > {{ stop_time_us }}UL) {
{{ terminate.cname }}();
break;
}
{% endif %}
platform_wait_until_next_tick(start_time, {{ base_rate_us }}UL);
}
return 0;
}
传递给模板的值示例
codegen_info = create_codegen_info(
model_code.c_code.c_code_info,
model_settings.is_ext_mode,
)
context = codegen_info.model_dump()
render_template_to_file("templates/main.c", "build/src/main.c", context)
运行时模板
- 独立执行模式的运行时模板应包含以下信息
-
*程序或固件的入口点在哪里; *当模型初始化函数被调用时; *如何调用所有step函数; *基期如何维持; *如何处理多个计算步骤; *发生超限时会发生什么; *如何处理有限模拟时间; *当模型完成函数被调用时; *哪些平台驱动程序和标头可用于用户的C代码。
本文更详细地讨论了模型的调度程序和执行。 独立模式下的模型执行.
| Python目标代码创建项目,模型的运行时行为由C/C++运行时模板设置。 |
组装
方法 compile_model 它启动了一个基于平台的工具链,并将生成的项目转换为可以下载或运行的工件。
可能的选择:
-
arduino-cli compile; *CMake和交叉编译器; *制作或忍者; *CLI/SDK制造商; *构建本地可执行文件; *固件组装.hex,.bin,.elf.compile_model它必须验证项目是否已创建,运行构建,处理工具链错误,并将最终工件的路径保存在目标对象中。
通过装配的示例 CMakeWrapper
def compile_model(self, model: EngeeModel) -> TargetResponse:
if self.project_path is None or not self.project_path.exists():
raise TargetException(
"Project was not generated. Call generate_executable_code first."
)
self.cmake.build_all(str(self.project_path))
self.artifact_path = self.project_path / "build" / "my_firmware.elf"
if not self.artifact_path.exists():
raise TargetException(f"Build artifact not found: {self.artifact_path}")
return TargetResponse(detail="compile_model")
调用您自己的邮件导入器的示例
def compile_model(self, model: EngeeModel) -> TargetResponse:
self.builder.build_project(model.name)
self.artifact_path = self.builder.get_artifact_path(model.name)
return TargetResponse(detail="compile_model")
装载量
方法 upload_model 将构建结果传输到应该执行的位置。 对于微控制器,这通常是通过编程器固件。 对于Linux设备,通过SSH复制。 对于本地应用程序,如果二进制文件已经在正确的机器上,则此步骤可能不会执行任何操作。
将下载与构建分开是很重要的:自定义工具链错误应该发生在 compile_model,并且与设备,编程器或远程主机的通信错误在 upload_model.
固件下载示例
def upload_model(self, model: EngeeModel) -> TargetResponse:
if self.artifact_path is None or not self.artifact_path.exists():
raise TargetException("Build artifact not found. Compile model first.")
self.loader.flash(self.artifact_path)
return TargetResponse(detail="upload_model")
没有单独下载的本地应用程序示例
def upload_model(self, model: EngeeModel) -> TargetResponse:
return TargetResponse(detail="upload_model")
推出
方法 start_model 加载后调用。 在独立执行模式中,有两种可能的选择:
*下载后固件已经启动,方法只返回成功响应。; *收集的工件是一个应用程序,方法应该启动进程。
如果进程是从 start_model 目标应该保存进程描述符,以便以后 stop_model 我本可以阻止他的。 如果平台不支持在独立模式下远程停止模型, stop_model 它只能返回成功的响应,但应该在目标的自述文件中描述此行为。
启动后运行的固件示例
async def start_model(self, model: EngeeModel) -> TargetResponse:
return TargetResponse(detail="start_model")
启动本地进程的示例
async def start_model(self, model: EngeeModel) -> TargetResponse:
if self.artifact_path is None or not self.artifact_path.exists():
raise TargetException("Executable not found. Compile model first.")
self.current_process = subprocess.Popen([str(self.artifact_path)])
return TargetResponse(detail="start_model")