feifei.xu f188055228 清理无用代码、合并工具链、修复注释与日志问题
- 删除破损文件: main.py/main.ui/main_ui.py
- 删除 runtime_hook.py(归宿主维护),清除 tools/ 目录
- 合并 export_runtime/pack_zip/verify_companions 到 build_pyd.py
- 修复注释: docstring 参数与实际签名不一致、过时引用、拼写错误
- 修复日志: 消除静默吞异常、删冗余 log+raise、补缺失日志
- 精简 .gitignore
2026-07-21 18:14:34 +08:00
2026-06-02 15:43:22 +08:00
2026-06-04 15:39:36 +08:00

MIL SDK 文档

MIL SDK 是一个用于处理 MILModel-In-the-Loop)仿真数据的 Python 工具包,主要提供 Excel 数据文件的读取、解析、生成和更新功能。

目录


概述

项目简介

MIL SDK 专为 MIL 仿真数据处理场景设计,提供以下核心能力:

  • 数据读取 - 从特定格式的 Excel 文件中解析仿真信号数据
  • 用例管理 - 读取和解析测试用例模板
  • 用例生成 - 根据数据和模板生成测试用例 Excel 文件
  • 数据对比 - 比较新旧仿真数据差异,自动更新信号列

技术栈

类别 技术选型
语言 Python 3.10+
主要依赖 openpyxl >= 3.0.0
测试框架 pytest >= 7.0.0
类型检查 mypy >= 1.0.0
代码规范 ruff >= 0.1.0

版本信息

  • 当前版本:0.1.0
  • 许可证:MIT

项目架构

目录结构

mil_sdk/
├── src/
│   └── core/                      # 核心模块
│       ├── __init__.py           # 公共 API 导出
│       ├── base.py               # 核心数据模型
│       ├── exceptions.py         # 自定义异常
│       ├── logging_config.py     # 日志配置
│       ├── mil_read_data_excel.py    # 读取仿真数据 Excel
│       ├── mil_read_case_excel.py    # 读取测试用例 Excel
│       ├── mil_create_data_excel.py # 生成测试用例 Excel
│       └── mil_update_excel.py       # 更新用例 Excel
├── tests/                        # 测试模块
│   ├── conftest.py              # pytest 配置和 fixtures
│   ├── test_base.py            # 基础数据模型测试
│   ├── test_mil_case_excel.py   # 用例 Excel 测试
│   └── test_mil_data_excel.py   # 数据 Excel 测试
├── main.py                      # 入口文件
├── pyproject.toml               # 项目配置
├── requirements.txt            # 依赖清单
└── SDK_DOCUMENTATION.md         # 本文档

模块职责

模块 文件 职责
数据模型 base.py 定义 DataLog、SignalData、ExcelDataResult 等核心数据结构
异常体系 exceptions.py 定义 MILSDKError 及其子类,统一异常处理
日志模块 logging_config.py 提供日志配置和获取功能
数据读取 mil_read_data_excel.py 解析 MIL 仿真数据 Excel,提取信号数据
用例读取 mil_read_case_excel.py 解析测试用例模板 Excel
用例生成 mil_create_data_excel.py 根据模板生成测试用例 Excel
用例更新 mil_update_excel.py 比较新旧数据,更新信号列

依赖关系

┌─────────────────────────────────────────────────────────────────┐
│                           main.py                               │
│                    (入口文件,演示 SDK 使用)                     │
└─────────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────┐
│                     src/core/__init__.py                         │
│              (公共 API 统一导出模块)                             │
└─────────────────────────────────────────────────────────────────┘
           │        │         │        │        │
           ▼        ▼         ▼        ▼        ▼
     ┌────────┐ ┌──────┐ ┌────────┐ ┌──────┐ ┌──────┐
     │ base   │ │excep │ │logging│ │ mil_ │ │ mil_ │
     │        │ │tions │ │_conf  │ │read_ │ │create│
     │        │ │      │ │       │ │data  │ │_data │
     └────────┘ └──────┘ └───────┘ └──────┘ └──────┘
                                       │         │
                                       ▼         ▼
                                  ┌────────┐ ┌────────┐
                                  │ mil_   │ │ mil_   │
                                  │read_   │ │update_ │
                                  │case    │ │excel   │
                                  └────────┘ └────────┘

快速开始

安装

pip install -e .

开发依赖安装

pip install -e ".[dev]"

基础使用

from src.core import setup_logging, read_excel_data, ExcelReaderConfig

# 初始化日志
setup_logging()

# 读取仿真数据
config = ExcelReaderConfig()
result = read_excel_data("simulation.xlsx", return_object=True, config=config)

# 获取信号数据
signal = result.get_signal("signal_name")
if signal:
    for log in signal.datalog:
        print(f"时间: {log.time}, 值: {log.value}")

数据模型

DataLog

仿真数据日志记录,表示单个时间点的数据。

@dataclass
class DataLog:
    time: float = 0.0      # 时间戳(秒)
    value: str = ""        # 信号值

属性说明

属性 类型 默认值 说明
time float 0.0 时间戳,单位为秒
value str "" 信号值,可以是任意类型

示例

log = DataLog(time=1.5, value="on")
print(f"时间: {log.time}, 值: {log.value}")

SignalData

信号数据封装,包含信号的元数据和数据日志。

@dataclass
class SignalData:
    column: int = 0                                    # 列索引
    attributes: dict[str, str] = field(default_factory=dict)  # 信号属性
    datalog: list[DataLog] = field(default_factory=list)      # 数据日志列表

属性说明

属性 类型 说明
column int Excel 列索引
attributes dict[str, str] 信号属性字典,键为行号,值为单元格值
datalog list[DataLog] 数据日志列表

方法说明

方法 返回值 说明
to_dict() dict[str, Any] 转换为字典格式

示例

signal = SignalData(column=2, attributes={"type": "boolean"}, datalog=[DataLog(0.0, "off")])
data = signal.to_dict()

ExcelDataResult

Excel 数据读取结果封装,提供对 Excel 数据的类型安全访问。

@dataclass
class ExcelDataResult:
    sheet_name: str = "Scenario1"                    # 工作表名称
    source_row: int = 0                              # Source: Input 所在行号
    signals: dict[str, SignalData] = field(default_factory=dict)  # 信号字典

属性说明

属性 类型 说明
sheet_name str 工作表名称,默认 "Scenario1"
source_row int "Source: Input" 标记所在行号
signals dict[str, SignalData] 信号名称到信号数据的映射

方法说明

方法 参数 返回值 说明
get_signal() name: str SignalData | None 获取指定信号的数据
get_signal_names() - list[str] 获取所有信号名称
to_dict() - dict[str, Any] 转换为字典格式

示例

result = ExcelDataResult(sheet_name="Scenario1", source_row=3, signals={})
signal = result.get_signal("signal_name")
names = result.get_signal_names()
data = result.to_dict()

ExcelReaderConfig

Excel 读取配置类,用于配置 Excel 文件的解析规则。

@dataclass
class ExcelReaderConfig:
    sheet_name: str = "Scenario1"              # 工作表名称
    source_header: str = "Source: Input"       # 数据源标记行文本
    time_column: int = 1                      # 时间列索引(A 列)
    header_row: int = 1                       # 信号名称所在行号
    type_row: int = 3                          # 信号类型所在行号
    interp_row: int = 6                        # 插值类型所在行号
    data_start_row_offset: int = 1             # 数据起始行偏移量
    output_header: str = "Source: Output"      # 输出标记行文本
    block_path_row: int = 4                    # 块路径所在行号

属性说明

属性 类型 默认值 说明
sheet_name str "Scenario1" 工作表名称
source_header str "Source: Input" 数据源标记行文本
time_column int 1 时间列索引(A 列)
header_row int 1 信号名称所在行号
type_row int 3 信号类型所在行号
interp_row int 6 插值类型所在行号
data_start_row_offset int 1 相对于 source_row 的数据起始行偏移量
output_header str "Source: Output" 输出标记行文本
block_path_row int 4 块路径所在行号

示例

config = ExcelReaderConfig(
    sheet_name="Scenario1",
    source_header="Source: Input",
    time_column=1
)

异常体系

异常模块位于 exceptions.py

异常层次

Exception
    └── MILSDKError (基础异常)
            ├── ExcelReadError (文件读取错误)
            ├── ExcelFormatError (格式错误)
            ├── CaseDataError (用例数据错误)
            └── ExcelWriteError (文件写入错误)

MILSDKError

基础异常类,所有 SDK 自定义异常的基类。

class MILSDKError(Exception):
    pass

ExcelReadError

Excel 文件读取错误,当文件不存在或读取失败时抛出。

class ExcelReadError(MILSDKError):
    """Excel 文件读取错误(文件不存在、权限问题等)"""
    pass

触发场景

  • 文件不存在
  • 文件权限问题
  • 文件格式损坏

ExcelFormatError

Excel 格式错误,当 Excel 格式不符合预期时抛出。

class ExcelFormatError(MILSDKError):
    """Excel 格式错误(缺少 Sheet、格式不匹配等)"""
    pass

触发场景

  • 缺少指定的 Sheet
  • 缺少 "Source: Input" 标记
  • 缺少模板版本号

CaseDataError

用例数据错误,当用例数据异常时抛出。

class CaseDataError(MILSDKError):
    """用例数据错误(信号不存在、类型错误等)"""
    pass

触发场景

  • 信号名称不存在
  • 时间格式错误
  • 缺少用例标题

ExcelWriteError

Excel 文件写入错误,当文件写入失败时抛出。

class ExcelWriteError(MILSDKError):
    """Excel 文件写入错误(权限问题、保存失败等)"""
    pass

触发场景

  • 文件权限问题
  • 磁盘空间不足
  • 保存失败

异常处理示例

from src.core import (
    MILSDKError,
    ExcelReadError,
    ExcelFormatError,
    CaseDataError,
    ExcelWriteError,
    read_excel_data,
)

try:
    result = read_excel_data("data.xlsx")
except ExcelReadError as e:
    print(f"文件读取失败: {e}")
except ExcelFormatError as e:
    print(f"格式错误: {e}")
except MILSDKError as e:
    print(f"SDK 错误: {e}")

日志模块

日志模块位于 logging_config.py

setup_logging

配置日志系统,设置日志级别、输出目标等。

def setup_logging(
    level: Literal["DEBUG", "INFO", "WARNING", "ERROR"] = "INFO",
    log_file: str | Path | None = "logs/mil_sdk.log",
    console_output: bool = True,
) -> logging.Logger

参数说明

参数 类型 默认值 说明
level str "INFO" 日志级别,可选 "DEBUG"、"INFO"、"WARNING"、"ERROR"
log_file str | Path | None "logs/mil_sdk.log" 日志文件路径,设为 None 则不写入文件
console_output bool True 是否输出到控制台

返回值:根日志记录器 (logging.Logger)

日志格式%(asctime)s [%(levelname)s] %(message)s

示例

from src.core import setup_logging

logger = setup_logging(level="DEBUG", log_file="logs/mil_sdk.log")

get_logger

获取指定名称的日志记录器。

def get_logger(name: str) -> logging.Logger

参数说明

参数 类型 说明
name str 日志记录器名称,通常使用 __name__

返回值:日志记录器实例

示例

from src.core import get_logger

logger = get_logger(__name__)
logger.info("This is an info message")

Excel 读取模块

read_excel_data

读取 MIL 仿真 Excel 文件并解析信号数据。

def read_excel_data(
    excel_path: str,
    return_object: bool = False,
    config: ExcelReaderConfig | None = None
) -> DataDict | ExcelDataResult

参数说明

参数 类型 默认值 说明
excel_path str - Excel 文件路径
return_object bool False 是否返回 ExcelDataResult 对象(推荐),False 保持向后兼容
config ExcelReaderConfig | None None Excel 读取配置,默认使用 ExcelReaderConfig()

返回值

return_object=False 时返回包含以下键的字典(向后兼容):

  • wb: Workbook 对象
  • sheet: Worksheet 对象
  • source_row: Source: Input 所在行号
  • <信号名>: 信号元数据(type, column, datalog

return_object=True 时返回 ExcelDataResult 对象(推荐):

  • sheet_name: 工作表名称
  • source_row: Source: Input 所在行号
  • signals: 信号名称到信号数据的映射

异常

异常类型 触发条件
ExcelReadError 文件不存在或读取失败
ExcelFormatError Excel 格式不符合预期

示例

from src.core import read_excel_data

# 使用推荐的方式
result = read_excel_data("simulation.xlsx", return_object=True)
for name in result.get_signal_names():
    signal = result.get_signal(name)
    if signal:
        print(f"信号: {name}, 数据点: {len(signal.datalog)}")

# 使用向后兼容的方式
data = read_excel_data("simulation.xlsx")
wb = data["wb"]
sheet = data["sheet"]

read_excel_case

读取 Excel 用例模板。

def read_excel_case(
    excel_path: str,
    data_dict: dict,
    addTimeEn: bool,
) -> CaseDict

参数说明

参数 类型 说明
excel_path str Excel 模板文件路径
data_dict dict 信号数据字典
addTimeEn bool 是否累加时间

返回值:用例字典

异常

异常类型 触发条件
ExcelReadError 文件读取失败
ExcelFormatError 格式错误
CaseDataError 用例数据错误

用例字典结构

{
    "Sheet名称": {
        "用例标题": {
            "enable": bool,      # 是否启用
            "step": {
                "step0": {
                    "name": str,           # 步骤名称
                    "time": float,         # 时间
                    "action": dict         # 操作字典
                },
                "step1": {...}
            }
        }
    }
}

示例

from src.core import read_excel_case, read_excel_data

data_dict = read_excel_data("simulation.xlsx", return_object=False)
cases = read_excel_case("template.xlsx", data_dict, addTimeEn=True)

for sheet_name, sheet_cases in cases.items():
    print(f"Sheet: {sheet_name}")
    for case_name, case_data in sheet_cases.items():
        print(f"  用例: {case_name}, 启用: {case_data['enable']}")

Excel 生成模块

create_excel_case

根据数据和用例模板生成测试用例 Excel 文件。

def create_excel_case(
    excel_path: str,
    data_dict: dict,
    sheets_dict: dict,
) -> None

参数说明

参数 类型 说明
excel_path str 输出目录路径
data_dict dict 数据字典
sheets_dict dict 工作表字典

异常

异常类型 触发条件
CaseDataError 数据异常
ExcelWriteError 文件写入错误

示例

from src.core import read_excel_data, read_excel_case, create_excel_case

data_dict = read_excel_data("simulation.xlsx", return_object=False)
sheets_dict = read_excel_case("template.xlsx", data_dict, addTimeEn=True)
create_excel_case("output/", data_dict, sheets_dict)

Excel 更新模块

update_case_excel

比较新旧数据差异,更新 Excel 文件中的信号列。

def update_case_excel(
    filename: str,
    old_data: dict,
    new_data: dict,
) -> None

参数说明

参数 类型 说明
filename str Excel 文件路径
old_data dict 旧数据字典
new_data dict 新数据字典

功能说明

  • 比较新旧数据中的信号差异
  • 在旧文件中添加新数据独有的信号列
  • 从旧文件中删除新数据不存在的信号列

异常

异常类型 触发条件
CaseDataError 数据类型错误

示例

from src.core import read_excel_data, update_case_excel

new_data = read_excel_data("new_data.xlsx", return_object=False)
old_data = read_excel_data("old_data.xlsx", return_object=False)
update_case_excel("updated.xlsx", old_data, new_data)

API 参考

公共导入

所有公共 API 均可通过以下方式导入:

from src.core import (
    # 数据模型
    DataLog,
    SignalData,
    ExcelDataResult,
    ExcelReaderConfig,
    # 核心函数
    read_excel_data,
    read_excel_case,
    create_excel_case,
    update_case_excel,
    # 异常
    MILSDKError,
    ExcelReadError,
    ExcelFormatError,
    CaseDataError,
    ExcelWriteError,
    # 日志
    setup_logging,
    get_logger,
)

函数签名汇总

函数 签名
DataLog DataLog(time: float = 0.0, value: str = "")
SignalData SignalData(column: int = 0, attributes: dict = {}, datalog: list = [])
ExcelDataResult ExcelDataResult(sheet_name: str = "Scenario1", source_row: int = 0, signals: dict = {})
ExcelReaderConfig ExcelReaderConfig(...)
read_excel_data read_excel_data(excel_path: str, return_object: bool = False, config: ExcelReaderConfig | None = None) -> DataDict | ExcelDataResult
read_excel_case read_excel_case(excel_path: str, data_dict: dict, addTimeEn: bool) -> CaseDict
create_excel_case create_excel_case(excel_path: str, data_dict: dict, sheets_dict: dict) -> None
update_case_excel update_case_excel(filename: str, old_data: dict, new_data: dict) -> None
setup_logging setup_logging(level: str = "INFO", log_file: str | Path | None = "logs/mil_sdk.log", console_output: bool = True) -> logging.Logger
get_logger get_logger(name: str) -> logging.Logger

使用示例

基础用法

from src.core import setup_logging, read_excel_data

setup_logging()
result = read_excel_data("simulation.xlsx", return_object=True)

signal_names = result.get_signal_names()
print(f"共解析 {len(signal_names)} 个信号")

for name in signal_names:
    signal = result.get_signal(name)
    if signal:
        print(f"  {name}: {len(signal.datalog)} 个数据点")

完整工作流

from pathlib import Path
from src.core import (
    setup_logging,
    read_excel_data,
    read_excel_case,
    create_excel_case,
    update_case_excel,
)

def main() -> None:
    setup_logging(level="INFO")

    excel_path = Path(__file__).parent

    data_dict = read_excel_data("new_data.xlsx", return_object=False)
    old_data_dict = read_excel_data("old_data.xlsx", return_object=False)

    update_case_excel("new_case.xlsx", old_data_dict, data_dict)


if __name__ == "__main__":
    main()

错误处理

from src.core import (
    ExcelReadError,
    ExcelFormatError,
    CaseDataError,
    MILSDKError,
    read_excel_data,
)

try:
    result = read_excel_data("simulation.xlsx", return_object=True)
except ExcelReadError as e:
    print(f"文件读取失败: {e}")
    raise
except ExcelFormatError as e:
    print(f"格式错误: {e}")
    raise
except MILSDKError as e:
    print(f"SDK 错误: {e}")
    raise

Excel 文件格式约定

仿真数据文件格式

Excel 仿真数据文件遵循以下格式约定:

行号 内容
1 信号名称(time, signal1, signal2...
2 预留行(通常为空)
3 "Source: Input" 标记行,标识数据列开始
4 信号类型信息
5 块路径信息
6 插值类型信息
7+ 时间-值数据对

示例结构

A B C D
time signal1 signal2 signal3
Source: Input Source: Input Source: Input Source: Input
type boolean float boolean
BlockPath /model/signal1 /model/signal2 /model/signal3
Interp const linear zero-order hold
off 0.0 true
1.0 on 1.5 false
2.0 off 2.0 true

要求

  • Sheet 名称:Scenario1
  • 时间列:必须存在且列名为 "time"
  • 跳过列:列名为 "Parameter:"、"Value"、"BlockPath" 的列会被跳过

用例模板文件格式

用例模板 Excel 文件遵循以下格式约定:

Sheet 名称 内容
Atech-Hefei 模板版本号
其他 Sheet 测试用例数据

用例数据格式

列号 列名 说明
1 TITLE 用例标题
2 STATUS 状态(完成测试/未完成)
3 ACTION 操作描述(signal1=value1; signal2=value2
4 NAME 步骤名称
5 TIME 时间

操作字符串格式

  • 多条操作使用分号 ; 分隔
  • 操作格式:信号名=值
  • 示例:signal1=on; signal2=0.5

版本历史

v0.1.0 (当前版本)

  • 初始版本发布
  • 支持 Excel 数据文件读取和解析
  • 支持测试用例模板读取和生成
  • 支持新旧数据对比和更新
S
Description
No description provided
Readme MIT
178 KiB
Languages
Python 100%