Files
2025-12-30 19:10:49 +08:00

7.8 KiB

libEngine.dll 调用指南

本文档详细说明了如何通过 C++ 和 Python 调用 libEngine.dll 进行发动机仿真。

1. 数据结构定义

无论使用哪种语言,都需要严格遵循以下数据结构定义(内存布局)。

1.1 Engine_Identity (身份/配置参数)

字段名 类型 (C++) 类型 (Python ctypes) 说明
Identity_Ok int c_int 身份验证位,通常设为 1
Cof_Eff_Low_Identity double c_double 低压部件效率系数,默认 1.0
Cof_Eff_High_Identity double c_double 高压部件效率系数,默认 1.0

1.2 EngInPut (输入控制量)

字段名 类型 (C++) 类型 (Python ctypes) 说明
Altp double c_double 飞行高度 (m)
Ma0 double c_double 飞行马赫数
dT0 double c_double 与标准大气温差 (K)
StepTime double c_double 仿真步长 (s)
Wf double c_double 主燃烧室供油量 (kg/h)
Wf_After double c_double 加力供油量 (kg/h)
Angle_FanVane double c_double 风扇导流叶片角度
Angle_CompVane double c_double 高压压气机导流叶片角度
A8 double c_double 喷口临界面积 (m^2)
AddPower double c_double 附加功率/起动功率 (W)

1.3 EngOutPut (输出状态量)

字段名 类型 (C++) 类型 (Python ctypes) 说明
NL double c_double 风扇相对转速 (%)
NH double c_double 高压相对转速 (%)
T1t double c_double 进气温度 (K)
P1t double c_double 进气总压 (kPa)
P1s double c_double 风扇进口静压 (kPa)
P3s double c_double 高压压气机后静压 (kPa)
T5t double c_double 涡轮后温度 (K)
P5t double c_double 涡轮后压力 (kPa)
Wf_Main double c_double 实际燃烧主燃油流量 (kg/h)
T5t_Gas double c_double 涡轮后气体温度 (无惯性) (K)

2. C++ 调用方法

在 C++ 中,通常使用 LoadLibraryGetProcAddress 进行显式调用(动态加载)。

函数原型

// 假设使用 stdcall 调用约定
typedef struct EngOutPut (__stdcall *CreateEngFunc)(int **pEngine, struct Engine_Identity m_Identity);
typedef struct EngOutPut (__stdcall *EngStepGoFunc)(int *pEngine, struct EngInPut m_EngInPut);
typedef struct EngOutPut (__stdcall *DestroyEngFunc)(int *pEngine);

示例代码

#include <windows.h>
#include <iostream>

// 定义结构体 (需与上述定义一致)
struct Engine_Identity {
    int Identity_Ok;
    double Cof_Eff_Low_Identity;
    double Cof_Eff_High_Identity;
};

struct EngInPut {
    double Altp, Ma0, dT0, StepTime;
    double Wf, Wf_After, Angle_FanVane, Angle_CompVane, A8, AddPower;
};

struct EngOutPut {
    double NL, NH, T1t, P1t, P1s, P3s, T5t, P5t, Wf_Main, T5t_Gas;
};

// 定义函数指针类型
typedef EngOutPut (__stdcall *CreateEngFunc)(int**, Engine_Identity);
typedef EngOutPut (__stdcall *EngStepGoFunc)(int*, EngInPut);
typedef EngOutPut (__stdcall *DestroyEngFunc)(int*);

int main() {
    // 1. 加载 DLL
    HMODULE hDll = LoadLibrary("libEngine.dll");
    if (!hDll) {
        std::cerr << "无法加载 DLL" << std::endl;
        return 1;
    }

    // 2. 获取函数地址
    CreateEngFunc CreateEng = (CreateEngFunc)GetProcAddress(hDll, "CreateEng");
    EngStepGoFunc EngStepGo = (EngStepGoFunc)GetProcAddress(hDll, "EngStepGo");
    DestroyEngFunc DestroyEng = (DestroyEngFunc)GetProcAddress(hDll, "DestroyEng");

    if (!CreateEng || !EngStepGo || !DestroyEng) {
        std::cerr << "无法获取函数地址" << std::endl;
        FreeLibrary(hDll);
        return 1;
    }

    // 3. 创建发动机实例
    int* hEngine = nullptr; // 句柄指针
    Engine_Identity identity = {1, 1.0, 1.0};
  
    // 注意:CreateEng 需要传入指针的地址 (&hEngine)
    CreateEng(&hEngine, identity);

    if (!hEngine) {
        std::cerr << "发动机创建失败" << std::endl;
        FreeLibrary(hDll);
        return 1;
    }

    // 4. 仿真循环
    EngInPut input = {0};
    input.StepTime = 0.02;
    input.Wf = 100.0;
    input.A8 = 0.1;
    input.AddPower = 90000.0; // 起动功率

    for (int i = 0; i < 100; ++i) {
        // 执行单步
        EngOutPut output = EngStepGo(hEngine, input);
      
        std::cout << "Step: " << i 
                  << " NH: " << output.NH 
                  << " T5t: " << output.T5t << std::endl;

        // 简单的起动逻辑示例
        if (output.NH > 0.25) {
            input.AddPower = 0.0;
            input.Wf = 300.0;
        }
    }

    // 5. 销毁与释放
    DestroyEng(hEngine);
    FreeLibrary(hDll);

    return 0;
}

3. Python 调用方法

Python 中推荐使用 ctypes 库进行调用。

核心要点

  1. 使用 ctypes.Structure 定义对应的 C 结构体。
  2. 使用 WinDLL 加载 DLL(因为是 __stdcall 调用约定)。
  3. 配置 argtypesrestype 以确保参数传递正确。

示例代码 (基于封装好的类)

import ctypes
from ctypes import *
import os

# --- 结构体定义 (略,见 core_model.py) ---

class EngineSim:
    def __init__(self, dll_path):
        self.lib = WinDLL(dll_path)
  
        # 配置函数原型
        # CreateEng: 传入 int** (POINTER(POINTER(c_int)))
        self.lib.CreateEng.argtypes = [POINTER(POINTER(c_int)), Engine_Identity]
        self.lib.CreateEng.restype = EngOutPut 

        # EngStepGo: 传入 int* (POINTER(c_int))
        self.lib.EngStepGo.argtypes = [POINTER(c_int), EngInPut]
        self.lib.EngStepGo.restype = EngOutPut

        # DestroyEng: 传入 int*
        self.lib.DestroyEng.argtypes = [POINTER(c_int)]
        self.lib.DestroyEng.restype = EngOutPut
  
        self.h_engine = None

    def create(self):
        self.h_engine = POINTER(c_int)() # 创建一个空指针用于接收句柄
        identity = Engine_Identity(1, 1.0, 1.0)
        # 传入指针的引用 byref
        self.lib.CreateEng(byref(self.h_engine), identity)

    def step(self, input_data):
        return self.lib.EngStepGo(self.h_engine, input_data)

    def close(self):
        if self.h_engine:
            self.lib.DestroyEng(self.h_engine)
            self.h_engine = None

# --- 使用 ---
if __name__ == "__main__":
    dll_path = os.path.join(os.path.dirname(__file__), "libEngine.dll")
    sim = EngineSim(dll_path)
    sim.create()
  
    inp = EngInPut()
    inp.StepTime = 0.02
    inp.Wf = 100.0
    inp.AddPower = 90000.0
    inp.A8 = 0.1
  
    for i in range(100):
        out = sim.step(inp)
        print(f"NH: {out.NH:.2f}, T5t: {out.T5t:.2f}")
  
    sim.close()

注意事项

  • 指针传递: CreateEng 在 C++ 中接收 int**,在 Python ctypes 中对应 byref(h_engine),其中 h_enginePOINTER(c_int)()
  • 调用约定: 必须使用 WinDLL 而不是 CDLL,除非 DLL 编译时使用的是 cdecl。根据现有代码推断为 stdcall