228 lines
7.8 KiB
Markdown
228 lines
7.8 KiB
Markdown
# 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++ 中,通常使用 `LoadLibrary` 和 `GetProcAddress` 进行显式调用(动态加载)。
|
||
|
|
|
||
|
|
### 函数原型
|
||
|
|
|
||
|
|
```cpp
|
||
|
|
// 假设使用 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);
|
||
|
|
```
|
||
|
|
|
||
|
|
### 示例代码
|
||
|
|
|
||
|
|
```cpp
|
||
|
|
#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. 配置 `argtypes` 和 `restype` 以确保参数传递正确。
|
||
|
|
|
||
|
|
### 示例代码 (基于封装好的类)
|
||
|
|
|
||
|
|
```python
|
||
|
|
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_engine` 是 `POINTER(c_int)()`。
|
||
|
|
* **调用约定**: 必须使用 `WinDLL` 而不是 `CDLL`,除非 DLL 编译时使用的是 `cdecl`。根据现有代码推断为 `stdcall`。
|