diff --git a/docs/api_reference.md b/docs/api_reference.md new file mode 100644 index 0000000..27cee77 --- /dev/null +++ b/docs/api_reference.md @@ -0,0 +1,1312 @@ +# API 参考 + +## 目录 + +- [核心模块 (core)](#核心模块-core) + - [Base 类](#base-类) + - [公共函数](#公共函数) + - [枚举类型](#枚举类型) +- [数据类型模块 (types)](#数据类型模块-types) + - [BaseType](#basetype) + - [Unit](#unit) + - [DataConstraint](#dataconstraint) + - [CompuMethod](#compumethod) + - [应用数据类型](#应用数据类型) + - [实现数据类型](#实现数据类型) + - [SwAddrMethod](#swaddrmethod) + - [数据类型映射](#数据类型映射) + - [接口](#接口) + - [软件组件类型](#软件组件类型) + - [Package](#package) + - [SwcInternalBehavior](#swcinternalbehavior) +- [工具模块 (utils)](#工具模块-utils) + +--- + +## 核心模块 (core) + +### Base 类 + +所有类型的基类,提供通用的属性和方法。 + +**文件位置:** `src/core/base.py` + +```python +from src.core.base import Base +``` + +#### 属性 + +| 属性名 | 类型 | 说明 | +|--------|------|------| +| `name` | str | 元素名称 | +| `id` | Optional[int] | 元素标识符 | +| `parent` | Optional[Base] | 父元素引用 | +| `description` | Optional[str] | 元素描述 | + +#### 只读属性 + +| 属性名 | 类型 | 说明 | +|--------|------|------| +| `class_name` | str | 获取类型名称 (如 "ApplicationValueDataType") | +| `parent_id` | Optional[int] | 获取父类的 id | +| `package_path` | str | 获取完整包路径 (如 "/Package/SubPackage/Name") | + +#### 方法 + +##### `to_arxml(doc: Document) -> Element` + +将对象序列化为 ARXML DOM 元素。**必须被子类重写。** + +**参数:** +- `doc`: XML Document 对象 + +**返回:** +- ARXML DOM Element + +--- + +### 公共函数 + +#### `create_uuid() -> str` + +生成一个 UUID 字符串。 + +```python +from src.core.base import create_uuid + +uuid = create_uuid() # e.g., "a1b2c3d4-e5f6-7890-abcd-ef1234567890" +``` + +#### `create_text_element(doc: Document, tag: str, text: str) -> Element` + +创建带有文本内容的 XML 元素。 + +**参数:** +- `doc`: XML Document 对象 +- `tag`: XML 标签名 +- `text`: 元素文本内容 + +**返回:** +- XML Element + +**示例:** + +```python +from src.core.base import create_text_element +from src.utils import create_arxml_document + +doc = create_arxml_document() +element = create_text_element(doc, "SHORT-NAME", "MyElement") +``` + +--- + +### 枚举类型 + +#### Category + +数据类型分类枚举。 + +**文件位置:** `src/core/enums.py` + +```python +from src import Category +``` + +| 枚举值 | 说明 | +|--------|------| +| `FIXED_LENGTH` | 固定长度 | +| `IDENTICAL` | 相同 | +| `TEXT_TABLE` | 文本表 | +| `LINEAR` | 线性 | +| `BOOLEAN` | 布尔 | +| `VALUE` | 值类型 | +| `STRUCTURE` | 结构体 | +| `ARRAY` | 数组 | +| `SCALE_LINEAR` | 缩放线性 | +| `RECORD` | 记录 | +| `UNION` | 联合 | +| `DATA_REFERENCE` | 数据引用 | +| `TYPE_REFERENCE` | 类型引用 | + +#### Encoding + +数据编码格式枚举。 + +```python +from src import Encoding +``` + +| 枚举值 | 说明 | +|--------|------| +| `ONE_COMPONENT` | 单组件 | +| `TWO_COMPONENT` | 双组件 | +| `BCD_P` | BCD Plus | +| `BCD_UP` | BCD Up | +| `IEEE754` | IEEE754 浮点数 | +| `ISO_8859_1` | ISO-8859-1 编码 | +| `UTF_8` | UTF-8 编码 | +| `UCS_2` | UCS-2 编码 | +| `NONE` | 无编码 | +| `VOID` | Void | +| `BOOLEAN` | 布尔 | +| `UTF_16` | UTF-16 编码 | + +#### CalibrationAccess + +校准访问权限枚举。 + +```python +from src import CalibrationAccess +``` + +| 枚举值 | 说明 | +|--------|------| +| `READ_ONLY` | 只读 | +| `NOT_ACCESSIBLE` | 不可访问 | +| `READ_WRITE` | 读写 | +| `NOT_SPECIFIED` | 未指定 | + +#### SwCalibrationAccess + +校准访问权限枚举(向后兼容版本)。 + +```python +from src import SwCalibrationAccess +``` + +| 枚举值 | 说明 | +|--------|------| +| `ReadOnly` | 只读 | +| `NotAccessible` | 不可访问 | +| `ReadWrite` | 读写 | +| `NotSpecified` | 未指定 | + +#### UpperSts / LowerSts + +上下限区间状态枚举。 + +```python +from src import UpperSts, LowerSts +``` + +| 枚举值 | 说明 | +|--------|------| +| `CLOSED` | 闭区间 | +| `OPEN` | 开区间 | + +#### ConstraintSpecified + +约束指定类型枚举。 + +```python +from src import ConstraintSpecified +``` + +| 枚举值 | 说明 | +|--------|------| +| `PHYSICAL` | 物理值约束 | +| `INTERNAL` | 内部值约束 | + +#### CompuContents + +转换内容方向枚举。 + +```python +from src import CompuContents +``` + +| 枚举值 | 说明 | +|--------|------| +| `USE_INTERNAL_TO_PHYSICAL` | 使用内部到物理的转换 | +| `USE_PHYSICAL_TO_INTERNAL` | 使用物理到内部的转换 | + +#### ComponentType + +组件类型枚举。 + +```python +from src import ComponentType +``` + +| 枚举值 | 说明 | +|--------|------| +| `COMPOSITION` | 组合组件 | +| `ATOMIC` | 原子组件 | + +#### AtomicType + +原子组件类型枚举。 + +```python +from src import AtomicType +``` + +| 枚举值 | 说明 | +|--------|------| +| `APPLICATION` | 应用组件 | +| `SENSOR_ACTUATOR` | 传感器/执行器组件 | +| `HARDWARE_ABSTRACTION` | 硬件抽象组件 | +| `COMPLEX_DRIVER` | 复杂驱动组件 | +| `SERVICE_PROXY` | 服务代理组件 | + +#### ImplementationCodeType + +实现代码类型枚举。 + +```python +from src import ImplementationCodeType +``` + +| 枚举值 | 说明 | +|--------|------| +| `SOURCE_CODE` | 源代码 | +| `OBJECT_CODE` | 目标代码 | + +#### AccessPoint + +访问点枚举。 + +```python +from src import AccessPoint +``` + +| 枚举值 | 说明 | +|--------|------| +| `READ` | 读取 | +| `WRITE` | 写入 | + +#### PortDirection + +端口方向枚举。 + +```python +from src import PortDirection +``` + +| 枚举值 | 说明 | +|--------|------| +| `IN` | 输入端口 | +| `OUT` | 输出端口 | + +#### Semantic + +数组大小语义枚举。 + +```python +from src import Semantic +``` + +| 枚举值 | 说明 | +|--------|------| +| `FIXED` | 固定大小 | +| `VARIABLE` | 可变大小 | + +#### SectionType + +段类型枚举。 + +```python +from src import SectionType +``` + +| 枚举值 | 说明 | +|--------|------| +| `CODE` | 代码段 | +| `DATA` | 数据段 | +| `CONST` | 常量段 | + +#### Policy + +数据策略枚举。 + +```python +from src import Policy +``` + +| 枚举值 | 说明 | +|--------|------| +| `STANDARD` | 标准策略 | +| `EXTENDED` | 扩展策略 | + +--- + +## 数据类型模块 (types) + +### BaseType + +基础数据类型,表示最底层的原生类型定义。 + +**文件位置:** `src/types/base_type.py` + +```python +from src import BaseType, create_base_type +``` + +#### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 类型名称 | +| `size` | int | 是 | 位大小 (如 8, 16, 32) | +| `category` | Optional[Category] | 否 | 分类类型 | +| `encoding` | Optional[Encoding] | 否 | 编码格式 | +| `native_description` | Optional[str] | 否 | 原生描述 | + +#### 工厂函数 + +##### `create_base_type(name: str, size: int, category: Optional[Category] = None, encoding: Optional[Encoding] = None, native_description: Optional[str] = None) -> BaseType` + +创建 BaseType 实例的工厂函数。 + +**示例:** + +```python +# 创建 8 位无符号整数类型 +uint8 = create_base_type( + name="UInt8", + size=8, + category=Category.FIXED_LENGTH, + encoding=Encoding.TWO_COMPONENT +) +``` + +--- + +### Unit + +数据单位,用于物理量的表示。 + +**文件位置:** `src/types/unit.py` + +```python +from src import Unit, create_unit +``` + +#### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 单位名称 | +| `display` | Optional[str] | 否 | 显示名称 | +| `factor` | Optional[float] | 否 | 因子 | +| `offset` | Optional[float] | 否 | 偏移量 | + +#### 工厂函数 + +##### `create_unit(name: str, display: Optional[str] = None, factor: Optional[float] = None, offset: Optional[float] = None) -> Unit` + +创建 Unit 实例的工厂函数。 + +**示例:** + +```python +unit = create_unit( + name="km_per_hour", + display="km/h", + factor=1.0, + offset=0.0 +) +``` + +--- + +### DataConstraint + +数据约束,定义有效值范围。 + +**文件位置:** `src/types/data_constraint.py` + +```python +from src import DataConstraint, create_data_constraint +``` + +#### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 约束名称 | +| `unit` | Optional[Unit] | 否 | 关联的单位 | +| `lower` | Optional[float] | 否 | 下限值 | +| `upper` | Optional[float] | 否 | 上限值 | +| `lower_sts` | Optional[LowerSts] | 否 | 下限状态 (CLOSED/OPEN) | +| `upper_sts` | Optional[UpperSts] | 否 | 上限状态 (CLOSED/OPEN) | +| `constraint_specified` | Optional[ConstraintSpecified] | 否 | 约束类型 | + +#### 工厂函数 + +##### `create_data_constraint(name: str, lower: Optional[float] = None, upper: Optional[float] = None, unit: Optional[Unit] = None, lower_sts: Optional[LowerSts] = None, upper_sts: Optional[UpperSts] = None, constraint_specified: Optional[ConstraintSpecified] = None) -> DataConstraint` + +创建 DataConstraint 实例的工厂函数。 + +**示例:** + +```python +constraint = create_data_constraint( + name="SpeedConstraint", + lower=0, + upper=250, + lower_sts=LowerSts.CLOSED, + upper_sts=UpperSts.OPEN +) +``` + +--- + +### CompuMethod + +计算方法,定义内部值与物理值之间的转换关系。 + +**文件位置:** `src/types/compu_method.py` + +```python +from src import CompuMethod, Linear, TextTable, create_linear, create_text_table, create_compu_method +``` + +#### Linear 类 + +线性转换方法。 + +##### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `factor` | float | 是 | 缩放因子 | +| `offset` | float | 是 | 偏移量 | + +转换公式:`physical = factor * internal + offset` + +##### 工厂函数 + +`create_linear(factor: float, offset: float) -> Linear` + +#### TextTable 类 + +文本表转换方法。 + +##### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `vt` | str | 是 | 文本值 | +| `lower` | Optional[int] | 否 | 下限 | +| `upper` | Optional[int] | 否 | 上限 | +| `lower_sts` | Optional[LowerSts] | 否 | 下限状态 | +| `upper_sts` | Optional[UpperSts] | 否 | 上限状态 | + +##### 工厂函数 + +`create_text_table(vt: str, lower: Optional[int] = None, upper: Optional[int] = None, lower_sts: Optional[LowerSts] = None, upper_sts: Optional[UpperSts] = None) -> TextTable` + +#### CompuMethod 类 + +完整的计算方法。 + +##### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 方法名称 | +| `unit` | Optional[Unit] | 否 | 单位 | +| `linear` | Optional[Linear] | 否 | 线性转换 | +| `text_tables` | Optional[List[TextTable]] | 否 | 文本表列表 | +| `category` | Optional[Category] | 否 | 分类 | +| `compu_contents` | Optional[CompuContents] | 否 | 转换内容方向 | + +##### 方法 + +| 方法 | 说明 | +|------|------| +| `get_text_table(vt: str) -> Optional[TextTable]` | 根据文本值查找文本表 | +| `add_text_table(text_table: TextTable)` | 添加文本表 | + +##### 工厂函数 + +`create_compu_method(name: str, unit: Optional[Unit] = None, linear: Optional[Linear] = None, text_tables: Optional[List[TextTable]] = None, category: Optional[Category] = None, compu_contents: Optional[CompuContents] = None) -> CompuMethod` + +**示例:** + +```python +# 创建带线性转换的计算方法 +linear = create_linear(factor=0.01, offset=0) +compu = create_compu_method( + name="SpeedConversion", + linear=linear, + category=Category.LINEAR +) + +# 创建带文本表的计算方法 +table = create_text_table(vt="OK", lower=0, upper=0) +compu = create_compu_method( + name="StatusConversion", + text_tables=[table], + category=Category.TEXT_TABLE +) +``` + +--- + +### 应用数据类型 + +#### ApplicationBooleanDataType + +布尔类型应用数据。 + +**文件位置:** `src/types/application_types.py` + +```python +from src import ApplicationBooleanDataType, create_boolean_type +``` + +##### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 类型名称 | +| `compu_method` | Optional[CompuMethod] | 否 | 计算方法 | +| `data_constraint` | Optional[DataConstraint] | 否 | 数据约束 | + +##### 工厂函数 + +`create_boolean_type(name: str, compu_method: Optional[CompuMethod] = None, data_constraint: Optional[DataConstraint] = None) -> ApplicationBooleanDataType` + +--- + +#### ApplicationValueDataType + +标量值类型应用数据。 + +```python +from src import ApplicationValueDataType, create_value_type +``` + +##### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 类型名称 | +| `unit` | Optional[Unit] | 否 | 单位 | +| `compu_method` | Optional[CompuMethod] | 否 | 计算方法 | +| `data_constraint` | Optional[DataConstraint] | 否 | 数据约束 | + +##### 工厂函数 + +`create_value_type(name: str, unit: Optional[Unit] = None, compu_method: Optional[CompuMethod] = None, data_constraint: Optional[DataConstraint] = None) -> ApplicationValueDataType` + +--- + +#### ApplicationStructureDataType + +结构体类型应用数据。 + +```python +from src import ApplicationStructureDataType, create_structure_type +``` + +##### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 类型名称 | +| `structure_elements` | Optional[List[StructureElement]] | 否 | 结构体成员列表 | + +##### 嵌套类:StructureElement + +结构体成员元素。 + +**构造函数属性:** + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 成员名称 | +| `data_type` | Base | 是 | 数据类型 | +| `offset` | Optional[int] | 否 | 位偏移 | + +##### 方法 + +| 方法 | 说明 | +|------|------| +| `get_element(name: str) -> Optional[StructureElement]` | 查找成员 | +| `add_element(element: StructureElement)` | 添加成员 | + +##### 工厂函数 + +`create_structure_type(name: str, elements: Optional[List[ApplicationStructureDataType.StructureElement]] = None) -> ApplicationStructureDataType` + +--- + +#### ApplicationArrayDataType + +数组类型应用数据。 + +```python +from src import ApplicationArrayDataType, create_array_type +``` + +##### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 类型名称 | +| `element` | Optional[ArrayElement] | 否 | 数组元素 | + +##### 嵌套类:ArrayElement + +数组元素配置。 + +**构造函数属性:** + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `length` | int | 是 | 数组长度 | +| `data_type` | Base | 是 | 元素数据类型 | +| `array_size_semantics` | Optional[Semantic] | 否 | 大小语义 | + +##### 工厂函数 + +`create_array_type(name: str, element: Optional[ApplicationArrayDataType.ArrayElement] = None) -> ApplicationArrayDataType` + +--- + +### 实现数据类型 + +#### ImplementationValueDataType + +实现层标量值类型。 + +**文件位置:** `src/types/implementation_types.py` + +```python +from src import ImplementationValueDataType, create_impl_value_type +``` + +##### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 类型名称 | +| `base_type` | Optional[BaseType] | 否 | 基础类型 | +| `compu_method` | Optional[CompuMethod] | 否 | 计算方法 | +| `data_constraint` | Optional[DataConstraint] | 否 | 数据约束 | + +##### 工厂函数 + +`create_impl_value_type(name: str, base_type: Optional[BaseType] = None, compu_method: Optional[CompuMethod] = None, data_constraint: Optional[DataConstraint] = None) -> ImplementationValueDataType` + +--- + +#### ImplementationStructureDataType + +实现层结构体类型。 + +```python +from src import ImplementationStructureDataType, create_impl_structure_type +``` + +##### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 类型名称 | +| `structure_elements` | Optional[List[StructureElement]] | 否 | 结构体成员列表 | + +##### 嵌套类:StructureElement + +与 ApplicationStructureDataType.StructureElement 结构相同。 + +##### 工厂函数 + +`create_impl_structure_type(name: str, elements: Optional[List[ImplementationStructureDataType.StructureElement]] = None) -> ImplementationStructureDataType` + +--- + +#### ImplementationArrayDataType + +实现层数组类型。 + +```python +from src import ImplementationArrayDataType, create_impl_array_type +``` + +##### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 类型名称 | +| `element` | Optional[ArrayElement] | 否 | 数组元素 | + +##### 嵌套类:ArrayElement + +与 ApplicationArrayDataType.ArrayElement 结构相同。 + +##### 工厂函数 + +`create_impl_array_type(name: str, element: Optional[ImplementationArrayDataType.ArrayElement] = None) -> ImplementationArrayDataType` + +--- + +### SwAddrMethod + +软件地址方法,定义代码/数据的内存段。 + +**文件位置:** `src/types/sw_addr_method.py` + +```python +from src import SwAddrMethod, create_sw_addr_method, SectionType +``` + +#### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 方法名称 | +| `section_type` | Optional[SectionType] | 否 | 段类型 (CODE/DATA/CONST) | + +#### 工厂函数 + +`create_sw_addr_method(name: str, section_type: Optional[SectionType] = None) -> SwAddrMethod` + +**示例:** + +```python +# 代码段地址方法 +code_method = create_sw_addr_method("CODE", section_type=SectionType.CODE) + +# 数据段地址方法 +data_method = create_sw_addr_method("DATA", section_type=SectionType.DATA) +``` + +--- + +### 数据类型映射 + +#### DataTypeMapping + +应用类型到实现类型的映射。 + +**文件位置:** `src/types/data_mapping.py` + +```python +from src import DataTypeMapping, create_data_type_mapping +``` + +##### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 映射名称 | +| `application_data_type` | Base | 是 | 应用数据类型 | +| `implementation_data_type` | Base | 是 | 实现数据类型 | + +##### 工厂函数 + +`create_data_type_mapping(name: str, application_data_type: Base, implementation_data_type: Base) -> DataTypeMapping` + +--- + +#### DataTypeMappingSet + +数据类型映射集合。 + +```python +from src import DataTypeMappingSet, create_data_type_mapping_set +``` + +##### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 集合名称 | +| `data_type_mappings` | Optional[List[DataTypeMapping]] | 否 | 映射列表 | + +##### 方法 + +| 方法 | 说明 | +|------|------| +| `get_mapping(name: str) -> Optional[DataTypeMapping]` | 查找映射 | +| `add_mapping(mapping: DataTypeMapping)` | 添加映射 | + +##### 工厂函数 + +`create_data_type_mapping_set(name: str, mappings: Optional[List[DataTypeMapping]] = None) -> DataTypeMappingSet` + +--- + +### 接口 + +#### Interface + +接口基类。 + +**文件位置:** `src/types/interface.py` + +```python +from src import Interface +``` + +##### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 接口名称 | +| `is_service` | bool | 否 | 是否为服务接口 (默认 False) | + +--- + +#### SenderReceiverInterface + +发送-接收接口,用于数据信号传递。 + +```python +from src import SenderReceiverInterface, create_sender_receiver_interface, Policy, CalibrationAccess +``` + +##### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 接口名称 | +| `data_element` | Optional[SenderReceiverInterface.DataElement] | 否 | 数据元素 | +| `is_service` | bool | 否 | 是否为服务接口 | + +##### 嵌套类:DataElement + +数据元素定义。 + +**构造函数属性:** + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 元素名称 | +| `sw_addr_method` | Optional[SwAddrMethod] | 否 | 地址方法 | +| `data_constraint` | Optional[DataConstraint] | 否 | 数据约束 | +| `data_type` | Base | 是 | 数据类型 | +| `policy` | Optional[Policy] | 否 | 数据策略 | +| `calibration_access` | Optional[CalibrationAccess] | 否 | 校准访问权限 | + +##### 工厂函数 + +`create_sender_receiver_interface(name: str, data_element: Optional[SenderReceiverInterface.DataElement] = None, is_service: bool = False) -> SenderReceiverInterface` + +--- + +#### ClientServerInterface + +客户端-服务端接口,用于服务调用。 + +```python +from src import ClientServerInterface +``` + +##### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 接口名称 | +| `operations` | Optional[List[Operation]] | 否 | 操作列表 | +| `is_service` | bool | 否 | 是否为服务接口 | + +##### 嵌套类:Operation + +服务操作定义。 + +**构造函数属性:** + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 操作名称 | +| `is_server` | bool | 否 | 是否为服务端 | +| `arguments` | Optional[List[Argument]] | 否 | 参数列表 | + +##### 嵌套类:Argument + +操作参数定义。 + +**构造函数属性:** + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 参数名称 | +| `direction` | PortDirection | 是 | 参数方向 (IN/OUT) | +| `data_type` | Base | 是 | 参数数据类型 | + +##### 方法 + +| 方法 | 说明 | +|------|------| +| `get_operation(name: str) -> Optional[Operation]` | 查找操作 | +| `add_operation(operation: Operation)` | 添加操作 | + +--- + +### 软件组件类型 + +#### SwComponentType + +软件组件类型基类。 + +**文件位置:** `src/types/sw_component_type.py` + +```python +from src import SwComponentType, ComponentType +``` + +##### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 组件名称 | +| `component_type` | Optional[ComponentType] | 否 | 组件类型 | + +--- + +#### ApplicationSwComponentType + +应用软件组件类型。 + +```python +from src import ApplicationSwComponentType +``` + +##### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 组件名称 | +| `supports_multiple_instantiation` | bool | 否 | 是否支持多实例化 | +| `atomic_type` | Optional[AtomicType] | 否 | 原子类型 | +| `implementation_code_type` | Optional[ImplementationCodeType] | 否 | 实现代码类型 | +| `ports` | Optional[List[PortPrototype]] | 否 | 端口列表 | +| `internal_behavior` | Optional[SwcInternalBehavior] | 否 | 内部行为 | + +##### 方法 + +| 方法 | 说明 | +|------|------| +| `get_port(name: str) -> Optional[PortPrototype]` | 查找端口 | +| `add_port(port: PortPrototype)` | 添加端口 | + +--- + +#### PortPrototype + +端口原型基类。 + +```python +from src import PortPrototype, PortDirection +``` + +##### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 端口名称 | +| `interface` | Optional[Interface] | 否 | 关联接口 | + +--- + +#### RPortPrototype + +接收端口 (Required Port)。 + +```python +from src import RPortPrototype +``` + +继承自 PortPrototype,用于从其他组件接收数据。 + +--- + +#### PPortPrototype + +发送端口 (Provided Port)。 + +```python +from src import PPortPrototype +``` + +继承自 PortPrototype,用于向其他组件发送数据。 + +--- + +#### PRPortPrototype + +双向端口 (Provided Required Port)。 + +```python +from src import PRPortPrototype +``` + +##### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 端口名称 | +| `interface` | Optional[Interface] | 否 | 提供接口 | +| `required_interface` | Optional[Interface] | 否 | 需求接口 | + +--- + +### Package + +AUTOSAR 包,用于组织和管理元素。 + +**文件位置:** `src/types/package.py` + +```python +from src import Package, create_package +``` + +#### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 包名称 | +| `elements` | Optional[List[Base]] | 否 | 包内元素列表 | + +#### 方法 + +| 方法 | 说明 | +|------|------| +| `get_element(name: str) -> Optional[Base]` | 查找元素 | +| `add_element(element: Base)` | 添加元素 (支持覆盖同名元素) | + +#### 工厂函数 + +`create_package(name: str, elements: Optional[List[Base]] = None) -> Package` + +**示例:** + +```python +# 创建包并添加元素 +pkg = create_package("SensorPkg", elements=[ + base_type, + impl_type, + interface, + component +]) +``` + +--- + +### SwcInternalBehavior + +软件组件内部行为。 + +**文件位置:** `src/types/swc_internal_behavior.py` + +```python +from src import SwcInternalBehavior, RunnableEntity, create_runnable_entity, create_timing_event, create_init_event +``` + +#### VariableAccess + +变量访问定义。 + +##### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 访问名称 | +| `port_prototype_ref` | Optional[str] | 否 | 端口原型引用 | +| `target_data_prototype_ref` | Optional[str] | 否 | 目标数据原型引用 | + +--- + +#### RunnableEntity + +可运行实体 (Runnable)。 + +##### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | Runnable 名称 | +| `minimum_start_interval` | Optional[float] | 否 | 最小启动间隔 (秒) | +| `can_be_invoked_concurrently` | bool | 否 | 是否可并发调用 | +| `data_receive_points` | Optional[List[VariableAccess]] | 否 | 数据接收点 | +| `data_send_points` | Optional[List[VariableAccess]] | 否 | 数据发送点 | +| `variable_accesses` | Optional[List[VariableAccess]] | 否 | 变量访问列表 | +| `symbol` | Optional[str] | 否 | 符号 (函数名) | +| `sw_addr_method_ref` | Optional[SwAddrMethod] | 否 | 地址方法引用 | + +##### 方法 + +| 方法 | 说明 | +|------|------| +| `add_data_receive_point(access: VariableAccess)` | 添加数据接收点 | +| `add_data_send_point(access: VariableAccess)` | 添加数据发送点 | +| `add_variable_access(access: VariableAccess)` | 添加变量访问 | + +##### 工厂函数 + +`create_runnable_entity(name: str, symbol: Optional[str] = None, minimum_start_interval: Optional[float] = None) -> RunnableEntity` + +--- + +#### 事件类型 + +##### InitEvent + +初始化事件。 + +```python +from src import InitEvent + +event = InitEvent(name="Init", runnable_ref="MyRunnable") +``` + +##### TimingEvent + +定时事件。 + +```python +from src import TimingEvent, create_timing_event + +event = create_timing_event(name="Timing_10ms", period=0.01) +``` + +**构造函数属性:** + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 事件名称 | +| `period` | float | 是 | 周期 (秒) | + +##### OperationInvokedEvent + +操作调用事件。 + +```python +from src import OperationInvokedEvent, create_operation_invoked_event + +event = create_operation_invoked_event(name="OpInvoke", operation_ref="MyOperation") +``` + +##### DataReceivedEvent + +数据接收事件。 + +```python +from src import DataReceivedEvent, create_data_received_event + +event = create_data_received_event(name="DataRx", data_element_ref="MyDataElement", period=0.01) +``` + +--- + +#### SwcInternalBehavior + +软件组件内部行为容器。 + +##### 构造函数属性 + +| 属性名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| `name` | str | 是 | 行为名称 | +| `runnable_entities` | Optional[List[RunnableEntity]] | 否 | Runnable 列表 | +| `events` | Optional[List[Union[InitEvent, TimingEvent, OperationInvokedEvent, DataReceivedEvent]]] | 否 | 事件列表 | +| `symbol` | Optional[str] | 否 | 符号前缀 | + +##### 方法 + +| 方法 | 说明 | +|------|------| +| `get_runnable(name: str) -> Optional[RunnableEntity]` | 查找 Runnable | +| `add_runnable(runnable: RunnableEntity)` | 添加 Runnable | +| `get_event(name: str) -> Optional[Event]` | 查找事件 | +| `add_event(event: Event)` | 添加事件 | + +##### 工厂函数 + +`create_swc_internal_behavior(name: str, runnable_entities: Optional[List[RunnableEntity]] = None, events: Optional[List[Event]] = None) -> SwcInternalBehavior` + +--- + +## 工具模块 (utils) + +### create_arxml_document() -> Document + +创建标准 ARXML 文档,包含 AUTOSAR 命名空间。 + +**文件位置:** `src/utils/arxml_writer.py` + +```python +from src import create_arxml_document + +doc = create_arxml_document() +``` + +**返回:** +- xml.dom.minidom.Document 对象 + +--- + +### write_arxml(doc: Document, filepath: str, encoding: str = 'utf-8') -> None + +将 Document 写入文件 (紧凑格式)。 + +```python +from src import create_arxml_document, write_arxml + +doc = create_arxml_document() +# ... 添加内容 ... +write_arxml(doc, "output.arxml") +``` + +--- + +### write_arxml_pretty(doc: Document, filepath: str, encoding: str = 'utf-8') -> None + +将 Document 写入文件 (格式化输出)。 + +```python +from src import create_arxml_document, write_arxml_pretty + +doc = create_arxml_document() +# ... 添加内容 ... +write_arxml_pretty(doc, "output_formatted.arxml") +``` + +--- + +### get_ar_packages_element(doc: Document) -> Element + +获取 AR-PACKAGES 根元素。 + +```python +from src import get_ar_packages_element + +doc = create_arxml_document() +packages = get_ar_packages_element(doc) +``` + +--- + +### add_package_to_document(doc: Document, package_element: Element) -> None + +将 Package 元素添加到文档。 + +```python +from src import add_package_to_document + +pkg_element = package.to_arxml(doc) +add_package_to_document(doc, pkg_element) +``` + +--- + +## 索引 + +- [Base](#base-类) - 所有类型的基类 +- [Category](#category) - 数据分类枚举 +- [create_uuid](#create_uuid--str) - UUID 生成 +- [create_arxml_document](#create_arxml_document---document) - 文档创建 +- [write_arxml](#write_arxmldoc-document-filepath-str-encoding-str--utf-8---none) - 文件写入 +- [create_package](#package) - 包创建 +- [create_base_type](#basetype) - 基础类型创建 +- [create_value_type](#applicationvaluedatatype) - 值类型创建 +- [create_sender_receiver_interface](#senderreceiverinterface) - 接口创建 +- [ApplicationSwComponentType](#applicationswcomponenttype) - 组件类型 +- [create_swc_internal_behavior](#swcinternalbehavior) - 内部行为创建 \ No newline at end of file diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..b0d4c09 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,257 @@ +# 项目架构 + +## 整体架构图 + +``` +arxml_sdk/ +├── src/ # 源代码 +│ ├── __init__.py # 包入口,导出全部公共API +│ ├── core/ # 核心基础设施层 +│ │ ├── __init__.py +│ │ ├── base.py # Base 基类 +│ │ ├── constants.py # ARXML 标签常量 +│ │ └── enums.py # AUTOSAR 枚举类型 +│ ├── types/ # 数据类型层 +│ │ ├── __init__.py +│ │ ├── base_type.py # 基础数据类型 +│ │ ├── unit.py # 单位类型 +│ │ ├── data_constraint.py # 数据约束 +│ │ ├── compu_method.py # 计算方法 +│ │ ├── application_types.py # 应用数据类型 +│ │ ├── implementation_types.py # 实现数据类型 +│ │ ├── sw_addr_method.py # 软件地址方法 +│ │ ├── data_mapping.py # 数据类型映射 +│ │ ├── sw_component_type.py # 软件组件类型 +│ │ ├── interface.py # 接口定义 +│ │ ├── package.py # 包管理 +│ │ └── swc_internal_behavior.py # SWC内部行为 +│ └── utils/ # 工具层 +│ ├── __init__.py +│ └── arxml_writer.py # ARXML文档写入 +└── tests/ # 测试 +``` + +## 模块职责 + +### core 模块 - 核心基础设施层 + +提供 SDK 运行所需的基础组件,不依赖其他业务模块。 + +| 文件 | 职责 | 关键类/函数 | +|------|------|-------------| +| base.py | 定义所有类型的基础类 | `Base` (dataclass), `create_uuid()`, `create_text_element()` | +| constants.py | ARXML 标签常量统一管理 | 约140个XML标签常量定义 | +| enums.py | AUTOSAR 枚举类型定义 | `Category`, `Encoding`, `PortDirection` 等12个枚举 | + +**Base 类核心属性:** + +| 属性 | 类型 | 说明 | +|------|------|------| +| name | str | 元素名称 | +| id | Optional[int] | 元素标识符 | +| parent | Optional[Base] | 父元素引用 | +| description | Optional[str] | 元素描述 | +| package_path | str | 完整包路径 (如 `/Pkg1/Pkg2/Name`) | + +**Base 类核心方法:** + +| 方法 | 说明 | +|------|------| +| to_arxml(doc) | 抽象方法,将对象序列化为 ARXML DOM 元素 | +| class_name | 属性,获取类型名称 | +| parent_id | 属性,获取父类 ID | + +### types 模块 - 数据类型层 + +实现 AUTOSAR 数据模型的各个类型,依赖 core 模块。 + +| 文件 | 职责 | 主要类 | +|------|------|--------| +| base_type.py | 基础数据类型 (SW-BASE-TYPE) | `BaseType` | +| unit.py | 数据单位 | `Unit` | +| data_constraint.py | 数据约束范围 | `DataConstraint` | +| compu_method.py | 计算方法 (线性转换/文本表) | `Linear`, `TextTable`, `CompuMethod` | +| application_types.py | 应用层数据类型 | `ApplicationBooleanDataType`, `ApplicationValueDataType`, `ApplicationStructureDataType`, `ApplicationArrayDataType` | +| implementation_types.py | 实现层数据类型 | `ImplementationValueDataType`, `ImplementationStructureDataType`, `ImplementationArrayDataType` | +| sw_addr_method.py | 软件地址方法 | `SwAddrMethod` | +| data_mapping.py | 应用-实现类型映射 | `DataTypeMapping`, `DataTypeMappingSet` | +| sw_component_type.py | 软件组件类型和端口 | `ApplicationSwComponentType`, `RPortPrototype`, `PPortPrototype`, `PRPortPrototype` | +| interface.py | 接口定义 | `SenderReceiverInterface`, `ClientServerInterface`, `ModeSwitchInterface` | +| package.py | AUTOSAR 包 | `Package` | +| swc_internal_behavior.py | SWC 内部行为 | `RunnableEntity`, `TimingEvent`, `InitEvent`, `SwcInternalBehavior` | + +### utils 模块 - 工具层 + +提供 ARXML 文档创建和写入功能。 + +| 文件 | 职责 | 关键函数 | +|------|------|----------| +| arxml_writer.py | ARXML 文档创建和写入 | `create_arxml_document()`, `write_arxml()`, `write_arxml_pretty()` | + +## 类型继承体系 + +``` +Base (core.base) +├── BaseType (types.base_type) +├── Unit (types.unit) +├── DataConstraint (types.data_constraint) +├── CompuMethod (types.compu_method) +│ ├── Linear +│ └── TextTable +├── ApplicationDataType (types.application_types) +│ ├── ApplicationBooleanDataType +│ ├── ApplicationValueDataType +│ ├── ApplicationStructureDataType +│ │ └── StructureElement (内部类) +│ └── ApplicationArrayDataType +│ └── ArrayElement (内部类) +├── ImplementationDataType (types.implementation_types) +│ ├── ImplementationValueDataType +│ ├── ImplementationStructureDataType +│ │ └── StructureElement (内部类) +│ └── ImplementationArrayDataType +│ └── ArrayElement (内部类) +├── SwAddrMethod (types.sw_addr_method) +├── DataTypeMapping (types.data_mapping) +│ └── DataTypeMappingSet +├── SwComponentType (types.sw_component_type) +│ ├── AtomicComponentType +│ │ └── ApplicationSwComponentType +│ └── CompositionSwComponentType +├── PortPrototype (types.sw_component_type) +│ ├── RPortPrototype +│ ├── PPortPrototype +│ └── PRPortPrototype +├── Interface (types.interface) +│ ├── SenderReceiverInterface +│ │ └── DataElement (内部类) +│ ├── ClientServerInterface +│ │ ├── Operation (内部类) +│ │ └── Argument (内部类) +│ ├── ModeSwitchInterface +│ ├── ParameterInterface +│ ├── TriggerInterface +│ └── NvDataInterface +├── Package (types.package) +└── SwcInternalBehavior (types.swc_internal_behavior) + ├── RunnableEntity + └── EventType (Union) + ├── InitEvent + ├── TimingEvent + ├── OperationInvokedEvent + └── DataReceivedEvent +``` + +## 依赖关系图 + +``` + ┌─────────────────────────────────────┐ + │ arxml_sdk (src/__init__.py) │ + │ 导出全部 193 个公共 API 符号 │ + └─────────────────────────────────────┘ + │ + ┌───────────────────────────┼───────────────────────────┐ + │ │ │ + ▼ ▼ ▼ +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ core 模块 │ │ types 模块 │ │ utils 模块 │ +│ (基础设施层) │ │ (业务逻辑层) │ │ (工具层) │ +├─────────────────┤ ├─────────────────┤ ├─────────────────┤ +│ Base 基类 │ │ 数据类型类 │ │ ARXML文档创建 │ +│ 常量定义 │◄─────►│ 组件类型类 │ │ ARXML文件写入 │ +│ 枚举定义 │ │ 接口定义类 │ │ │ +│ UUID/文本工具 │ │ 包管理类 │ │ │ +└─────────────────┘ │ 内部行为类 │ └─────────────────┘ + └─────────────────┘ + │ + ▼ + ┌─────────────────┐ + │ tests 模块 │ + │ (测试层) │ + ├─────────────────┤ + │ 单元测试 │ + │ 集成测试 │ + └─────────────────┘ +``` + +**依赖约束:** +- `core` → 无依赖(最底层) +- `types` → 依赖 `core` +- `utils` → 依赖 `core` +- `tests` → 依赖 `src` 所有模块 + +## 设计模式 + +### 1. 工厂函数模式 + +所有类型都配有 `create_xxx()` 工厂函数,简化对象创建: + +```python +# 使用工厂函数 +uint8 = create_base_type("UInt8", size=8) + +# 替代直接实例化(如果存在构造函数) +uint8 = BaseType(name="UInt8", size=8) +``` + +### 2. 组合模式 + +通过 `add_xxx()` 方法支持动态添加子元素: + +```python +component = ApplicationSwComponentType(name="Sensor") +component.add_port(PPortPrototype(name="DataPort")) +``` + +### 3. 序列化模式 + +所有类型都实现 `to_arxml(doc)` 方法,将自身序列化为 ARXML DOM 元素: + +```python +element = component.to_arxml(doc) +``` + +### 4. 数据类模式 + +使用 `@dataclass` 简化数据类定义: + +```python +@dataclass +class BaseType: + name: str + size: int + category: Optional[Category] = None + encoding: Optional[Encoding] = None +``` + +## 数据流 + +``` +用户代码 + │ + ▼ +创建对象 (工厂函数) + │ + ├──► BaseType, ApplicationDataType, SwComponentType 等 + │ + ▼ +序列化 (to_arxml) + │ + ├──► XML DOM Element + │ + ▼ +写入文件 (arxml_writer) + │ + ▼ +.arxml 文件 +``` + +## 技术栈 + +| 方面 | 详情 | +|------|------| +| 编程语言 | Python 3 | +| 标准库 | `uuid`, `xml.dom.minidom`, `dataclasses`, `typing`, `enum` | +| 测试框架 | pytest | +| 目标格式 | AUTOSAR ARXML (R4.4 schema) | +| 版本 | 2.0.1 | \ No newline at end of file diff --git a/docs/guide.md b/docs/guide.md new file mode 100644 index 0000000..702ded3 --- /dev/null +++ b/docs/guide.md @@ -0,0 +1,548 @@ +# 使用指南 + +## 目录 + +- [快速开始](#快速开始) +- [基础类型创建](#基础类型创建) +- [应用数据类型](#应用数据类型) +- [接口定义](#接口定义) +- [软件组件](#软件组件) +- [内部行为](#内部行为) +- [打包与导出](#打包与导出) +- [完整示例](#完整示例) + +--- + +## 快速开始 + +### 环境要求 + +- Python 3.8+ + +### 安装 + +将 `src` 目录添加到 Python 路径即可使用: + +```python +import sys +sys.path.insert(0, '/path/to/arxml_sdk/src') +``` + +或直接使用相对导入: + +```python +from src import create_arxml_document, write_arxml +``` + +### 最小示例 + +```python +from src import ( + create_arxml_document, write_arxml, create_package, + create_base_type, Category, Encoding +) + +doc = create_arxml_document() +base_type = create_base_type("UInt8", size=8, encoding=Encoding.TWO_COMPONENT) +package = create_package("MyPackage", elements=[base_type]) +package_element = package.to_arxml(doc) +doc.documentElement.appendChild(package_element) +write_arxml(doc, "minimal.arxml") +``` + +--- + +## 基础类型创建 + +### 创建基本整数类型 + +```python +from src import create_base_type, Category, Encoding + +uint8 = create_base_type("UInt8", size=8, encoding=Encoding.TWO_COMPONENT) +uint16 = create_base_type("UInt16", size=16, encoding=Encoding.TWO_COMPONENT) +sint8 = create_base_type("Sint8", size=8, encoding=Encoding.TWO_COMPONENT) +float32 = create_base_type("Float32", size=32, encoding=Encoding.IEEE754) +``` + +### 创建带文本表的类型 + +```python +from src import ( + create_base_type, create_unit, create_data_constraint, + create_linear, create_text_table, create_compu_method, + Category, LowerSts, UpperSts +) + +unit = create_unit("km/h", display="km/h", factor=1.0, offset=0.0) + +constraint = create_data_constraint( + "SpeedConstraint", + lower=0, upper=250, + lower_sts=LowerSts.CLOSED, + upper_sts=UpperSts.OPEN +) + +linear = create_linear(factor=0.01, offset=0) + +compu = create_compu_method( + "SpeedConversion", + unit=unit, + linear=linear, + category=Category.LINEAR +) + +value_type = create_value_type( + "Speed", + unit=unit, + compu_method=compu, + data_constraint=constraint +) +``` + +--- + +## 应用数据类型 + +### 布尔类型 + +```python +from src import create_boolean_type, create_compu_method, create_linear, Category + +bool_compu = create_compu_method( + "BoolConversion", + linear=create_linear(factor=1, offset=0), + category=Category.BOOLEAN +) + +bool_type = create_boolean_type("ErrorFlag", compu_method=bool_compu) +``` + +### 值类型 + +```python +from src import create_value_type, create_unit, create_linear, create_compu_method, Category + +unit = create_unit("deg", display="°C") +linear = create_linear(factor=0.1, offset=-40) +compu = create_compu_method("Temperature", unit=unit, linear=linear, category=Category.LINEAR) + +temp_type = create_value_type("Temperature", unit=unit, compu_method=compu) +``` + +### 结构体类型 + +```python +from src import ( + ApplicationStructureDataType, create_value_type, + create_unit, create_linear, create_compu_method +) + +unit = create_unit("V") +linear = create_linear(factor=0.001, offset=0) +compu = create_compu_method("Voltage", unit=unit, linear=linear, category=Category.LINEAR) +voltage_type = create_value_type("Voltage", unit=unit, compu_method=compu) + +current_type = create_value_type("Current", unit=unit, compu_method=compu) + +structure = ApplicationStructureDataType(name="PowerData") +structure.add_element( + ApplicationStructureDataType.StructureElement(name="voltage", data_type=voltage_type, offset=0) +) +structure.add_element( + ApplicationStructureDataType.StructureElement(name="current", data_type=current_type, offset=32) +) +``` + +### 数组类型 + +```python +from src import ApplicationArrayDataType, create_value_type, create_unit, Semantic + +element_type = create_value_type("SensorValue", unit=create_unit("counts")) + +array_type = ApplicationArrayDataType(name="SensorData") +array_type.element = ApplicationArrayDataType.ArrayElement( + length=10, + data_type=element_type, + array_size_semantics=Semantic.FIXED +) +``` + +--- + +## 实现数据类型 + +### 值类型 + +```python +from src import create_impl_value_type, create_base_type, create_value_type, Encoding + +base_type = create_base_type("UInt16", size=16, encoding=Encoding.TWO_COMPONENT) +app_type = create_value_type("AppCounter") + +impl_type = create_impl_value_type("ImplCounter", base_type=base_type) +``` + +### 结构体类型 + +```python +from src import ImplementationStructureDataType, create_impl_value_type, create_base_type, Encoding + +base_u8 = create_base_type("UInt8", size=8, encoding=Encoding.TWO_COMPONENT) +base_u16 = create_base_type("UInt16", size=16, encoding=Encoding.TWO_COMPONENT) + +impl_u8 = create_impl_value_type("ImplUInt8", base_type=base_u8) +impl_u16 = create_impl_value_type("ImplUInt16", base_type=base_u16) + +struct_type = ImplementationStructureDataType(name="ImplStatus") +struct_type.add_element( + ImplementationStructureDataType.StructureElement(name="status", data_type=impl_u8, offset=0) +) +struct_type.add_element( + ImplementationStructureDataType.StructureElement(name="value", data_type=impl_u16, offset=8) +) +``` + +### 数组类型 + +```python +from src import ImplementationArrayDataType, create_impl_value_type, create_base_type, Semantic, Encoding + +base = create_base_type("UInt32", size=32, encoding=Encoding.TWO_COMPONENT) +element_type = create_impl_value_type("ImplUInt32", base_type=base) + +impl_array = ImplementationArrayDataType(name="ImplBuffer") +impl_array.element = ImplementationArrayDataType.ArrayElement( + length=16, + data_type=element_type, + array_size_semantics=Semantic.FIXED +) +``` + +--- + +## 数据类型映射 + +```python +from src import ( + DataTypeMapping, DataTypeMappingSet, + create_value_type, create_impl_value_type, create_base_type, Encoding +) + +base = create_base_type("UInt8", size=8, encoding=Encoding.TWO_COMPONENT) +app_type = create_value_type("AppStatus") +impl_type = create_impl_value_type("ImplStatus", base_type=base) + +mapping = DataTypeMapping( + name="StatusMapping", + application_data_type=app_type, + implementation_data_type=impl_type +) + +mapping_set = DataTypeMappingSet(name="DefaultMappings") +mapping_set.add_mapping(mapping) +``` + +--- + +## 接口定义 + +### 发送-接收接口 + +```python +from src import ( + SenderReceiverInterface, create_sender_receiver_interface, + create_value_type, create_unit, Policy, CalibrationAccess +) + +unit = create_unit("km/h") +data_element = SenderReceiverInterface.DataElement( + name="Speed", + data_type=create_value_type("Speed", unit=unit), + policy=Policy.STANDARD, + calibration_access=CalibrationAccess.READ_ONLY +) + +interface = create_sender_receiver_interface("SpeedInterface", data_element=data_element) +``` + +### 客户端-服务端接口 + +```python +from src import ( + ClientServerInterface, create_value_type, create_unit, + PortDirection +) + +unit = create_unit("counts") + +operation = ClientServerInterface.Operation( + name="Initialize", + is_server=True, + arguments=[ + ClientServerInterface.Argument( + name="config", + direction=PortDirection.IN, + data_type=create_value_type("Config", unit=unit) + ) + ] +) + +cs_interface = ClientServerInterface(name="ControlInterface") +cs_interface.add_operation(operation) +``` + +--- + +## 软件组件 + +### 创建组件 + +```python +from src import ( + ApplicationSwComponentType, PPortPrototype, RPortPrototype, + SenderReceiverInterface, create_sender_receiver_interface, + AtomicType, ComponentType +) + +interface = create_sender_receiver_interface("DataInterface") +port = PPortPrototype(name="DataPort", interface=interface) + +component = ApplicationSwComponentType( + name="SensorComponent", + supports_multiple_instantiation=False, + atomic_type=AtomicType.APPLICATION +) +component.add_port(port) +``` + +### 创建组合组件 + +```python +from src import SwComponentType, ComponentType, AtomicComponentType, CompositionSwComponentType + +child1 = ApplicationSwComponentType(name="Child1") +child2 = ApplicationSwComponentType(name="Child2") + +composition = CompositionSwComponentType(name="Composition") +composition._component_type = ComponentType.COMPOSITION +``` + +--- + +## 内部行为 + +### 创建 Runnable 和事件 + +```python +from src import ( + SwcInternalBehavior, RunnableEntity, TimingEvent, InitEvent, + VariableAccess, create_swc_internal_behavior, create_runnable_entity, + create_timing_event, create_init_event, create_data_received_event +) + +runnable = create_runnable_entity( + name="UpdateMeasurement", + symbol="UpdateMeasurement", + minimum_start_interval=0.01 +) +runnable.add_variable_access( + VariableAccess(name="SpeedAccess", port_prototype_ref="SpeedPort") +) + +timing_event = create_timing_event(name="Timing_10ms", period=0.01) +init_event = InitEvent(name="Init", runnable_ref="UpdateMeasurement") + +data_rx_event = create_data_received_event( + name="DataReady", + data_element_ref="ReadyFlag", + period=0.05 +) + +behavior = create_swc_internal_behavior( + name="SensorBehavior", + runnable_entities=[runnable], + events=[timing_event, init_event, data_rx_event] +) + +component = ApplicationSwComponentType(name="Sensor") +component.internal_behavior = behavior +``` + +--- + +## 打包与导出 + +### 基本导出流程 + +```python +from src import ( + create_arxml_document, write_arxml, create_package, + get_ar_packages_element, add_package_to_document, + create_base_type, create_value_type, create_unit +) + +doc = create_arxml_document() +packages_element = get_ar_packages_element(doc) + +base_type = create_base_type("UInt8", size=8) +app_type = create_value_type("AppData") +package = create_package("MyPkg", elements=[base_type, app_type]) + +package_element = package.to_arxml(doc) +add_package_to_document(doc, package_element) + +write_arxml(doc, "output.arxml") +``` + +### 格式化输出 + +```python +from src import write_arxml_pretty + +write_arxml_pretty(doc, "output_formatted.arxml") +``` + +--- + +## 完整示例 + +以下示例创建一个完整的 AUTOSAR 软件组件配置: + +```python +from src import ( + create_arxml_document, write_arxml_pretty, create_package, + get_ar_packages_element, add_package_to_document, + create_base_type, create_unit, create_linear, create_compu_method, + create_value_type, create_impl_value_type, + create_sender_receiver_interface, SenderReceiverInterface, + ApplicationSwComponentType, PPortPrototype, RPortPrototype, + SwcInternalBehavior, RunnableEntity, TimingEvent, InitEvent, + Category, Encoding, AtomicType, Policy, CalibrationAccess, + create_sw_addr_method, SectionType, DataTypeMappingSet, DataTypeMapping +) + +doc = create_arxml_document() + +base_uint8 = create_base_type("UInt8", size=8, encoding=Encoding.TWO_COMPONENT) +base_uint16 = create_base_type("UInt16", size=16, encoding=Encoding.TWO_COMPONENT) + +kmh_unit = create_unit("kmh", display="km/h", factor=1.0, offset=0.0) +speed_linear = create_linear(factor=0.01, offset=0) +speed_compu = create_compu_method("SpeedCompu", unit=kmh_unit, linear=speed_linear, category=Category.LINEAR) + +app_speed = create_value_type("AppSpeed", unit=kmh_unit, compu_method=speed_compu) +impl_speed = create_impl_value_type("ImplSpeed", base_type=base_uint16) + +mapping = DataTypeMapping( + name="SpeedMapping", + application_data_type=app_speed, + implementation_data_type=impl_speed +) +mapping_set = DataTypeMappingSet(name="SpeedMappingSet") +mapping_set.add_mapping(mapping) + +data_element = SenderReceiverInterface.DataElement( + name="SpeedData", + data_type=app_speed, + policy=Policy.STANDARD, + calibration_access=CalibrationAccess.READ_WRITE +) +speed_interface = create_sender_receiver_interface("SpeedInterface", data_element=data_element) + +code_method = create_sw_addr_method("CODE", section_type=SectionType.CODE) + +runnable = RunnableEntity( + name="UpdateSpeed", + symbol="UpdateSpeed", + minimum_start_interval=0.01, + can_be_invoked_concurrently=False +) +runnable.add_variable_access( + VariableAccess(name="SpeedAccess", port_prototype_ref="SpeedPort") +) + +timing = TimingEvent(name="Timing_10ms", period=0.01) +init = InitEvent(name="Init", runnable_ref="UpdateSpeed") + +behavior = SwcInternalBehavior( + name="SpeedSensorBehavior", + runnable_entities=[runnable], + events=[timing, init], + symbol="SpeedSensor" +) + +port = PPortPrototype(name="SpeedPort", interface=speed_interface) + +component = ApplicationSwComponentType( + name="SpeedSensorComponent", + supports_multiple_instantiation=False, + atomic_type=AtomicType.APPLICATION, + internal_behavior=behavior +) +component.add_port(port) + +package = create_package("SpeedSensorPkg", elements=[ + base_uint8, + base_uint16, + kmh_unit, + app_speed, + impl_speed, + mapping_set, + speed_interface, + code_method, + component +]) + +package_element = package.to_arxml(doc) +add_package_to_document(doc, package_element) + +write_arxml_pretty(doc, "SpeedSensor.arxml") +print("Generated: SpeedSensor.arxml") +``` + +--- + +## 常见问题 + +### Q: 如何创建多个端口? + +```python +interface1 = create_sender_receiver_interface("DataInterface") +interface2 = create_sender_receiver_interface("StatusInterface") + +component = ApplicationSwComponentType(name="MyComponent") +component.add_port(PPortPrototype(name="DataPort", interface=interface1)) +component.add_port(RPortPrototype(name="StatusPort", interface=interface2)) +``` + +### Q: 如何添加多个 Runnable? + +```python +behavior = SwcInternalBehavior(name="Behavior") + +runnable1 = create_runnable_entity(name="InitRunnable", symbol="Init") +runnable2 = create_runnable_entity(name="MainRunnable", symbol="Main", minimum_start_interval=0.01) + +behavior.add_runnable(runnable1) +behavior.add_runnable(runnable2) + +component = ApplicationSwComponentType(name="MyComponent") +component.internal_behavior = behavior +``` + +### Q: 如何创建嵌套结构体? + +```python +inner_type = create_value_type("InnerValue") +inner_struct = ApplicationStructureDataType(name="InnerStruct") +inner_struct.add_element( + ApplicationStructureDataType.StructureElement(name="value", data_type=inner_type) +) + +outer_struct = ApplicationStructureDataType(name="OuterStruct") +outer_struct.add_element( + ApplicationStructureDataType.StructureElement(name="inner", data_type=inner_struct) +) +``` \ No newline at end of file diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..b5c93c3 --- /dev/null +++ b/src/__init__.py @@ -0,0 +1,193 @@ +""" +arxml_sdk - AUTOSAR ARXML 配置生成 SDK +""" +from .core import ( + Base, + create_uuid, + create_text_element, + Category, + Encoding, + CalibrationAccess, + SwCalibrationAccess, + UpperSts, + LowerSts, + ConstraintSpecified, + CompuContents, + ComponentType, + AtomicType, + ImplementationCodeType, + AccessPoint, + PortDirection, + Semantic, + SectionType, + Policy, +) +from .types import ( + BaseType, + create_base_type, + Unit, + create_unit, + DataConstraint, + create_data_constraint, + Linear, + TextTable, + CompuMethod, + create_linear, + create_text_table, + create_compu_method, + ApplicationDataType, + ApplicationBooleanDataType, + ApplicationValueDataType, + ApplicationStructureDataType, + ApplicationArrayDataType, + create_boolean_type, + create_value_type, + create_structure_type, + create_array_type, + ImplementationDataType, + ImplementationValueDataType, + ImplementationStructureDataType, + ImplementationArrayDataType, + create_impl_value_type, + create_impl_structure_type, + create_impl_array_type, + SwAddrMethod, + create_sw_addr_method, + DataTypeMapping, + DataTypeMappingSet, + create_data_type_mapping, + create_data_type_mapping_set, + SwComponentType, + AtomicComponentType, + CompositionSwComponentType, + ApplicationSwComponentType, + PortPrototype, + RPortPrototype, + PPortPrototype, + PRPortPrototype, + Interface, + SenderReceiverInterface, + ClientServerInterface, + ModeSwitchInterface, + ParameterInterface, + TriggerInterface, + NvDataInterface, + create_sender_receiver_interface, + Package, + create_package, + SwcInternalBehavior, + RunnableEntity, + VariableAccess, + InitEvent, + TimingEvent, + OperationInvokedEvent, + DataReceivedEvent, + create_runnable_entity, + create_timing_event, + create_init_event, + create_operation_invoked_event, + create_data_received_event, + create_swc_internal_behavior, +) + +from .utils import ( + create_arxml_document, + write_arxml, + write_arxml_pretty, + get_ar_packages_element, + add_package_to_document, +) + +__version__ = "2.0.1" + +__all__ = [ + "Base", + "create_uuid", + "create_text_element", + "Category", + "Encoding", + "CalibrationAccess", + "SwCalibrationAccess", + "UpperSts", + "LowerSts", + "ConstraintSpecified", + "CompuContents", + "ComponentType", + "AtomicType", + "ImplementationCodeType", + "AccessPoint", + "PortDirection", + "Semantic", + "SectionType", + "Policy", + "BaseType", + "create_base_type", + "Unit", + "create_unit", + "DataConstraint", + "create_data_constraint", + "Linear", + "TextTable", + "CompuMethod", + "create_linear", + "create_text_table", + "create_compu_method", + "ApplicationDataType", + "ApplicationBooleanDataType", + "ApplicationValueDataType", + "ApplicationStructureDataType", + "ApplicationArrayDataType", + "create_boolean_type", + "create_value_type", + "create_structure_type", + "create_array_type", + "ImplementationDataType", + "ImplementationValueDataType", + "ImplementationStructureDataType", + "ImplementationArrayDataType", + "create_impl_value_type", + "create_impl_structure_type", + "create_impl_array_type", + "SwAddrMethod", + "create_sw_addr_method", + "DataTypeMapping", + "DataTypeMappingSet", + "create_data_type_mapping", + "create_data_type_mapping_set", + "SwComponentType", + "AtomicComponentType", + "CompositionSwComponentType", + "ApplicationSwComponentType", + "PortPrototype", + "RPortPrototype", + "PPortPrototype", + "PRPortPrototype", + "Interface", + "SenderReceiverInterface", + "ClientServerInterface", + "ModeSwitchInterface", + "ParameterInterface", + "TriggerInterface", + "NvDataInterface", + "create_sender_receiver_interface", + "Package", + "create_package", + "create_arxml_document", + "write_arxml", + "write_arxml_pretty", + "get_ar_packages_element", + "add_package_to_document", + "SwcInternalBehavior", + "RunnableEntity", + "VariableAccess", + "InitEvent", + "TimingEvent", + "OperationInvokedEvent", + "DataReceivedEvent", + "create_runnable_entity", + "create_timing_event", + "create_init_event", + "create_operation_invoked_event", + "create_data_received_event", + "create_swc_internal_behavior", +] \ No newline at end of file diff --git a/src/core/__init__.py b/src/core/__init__.py new file mode 100644 index 0000000..726d3cf --- /dev/null +++ b/src/core/__init__.py @@ -0,0 +1,45 @@ +""" +core 模块 +""" +from .base import Base, create_uuid, create_text_element +from .constants import * +from .enums import ( + Category, + Encoding, + CalibrationAccess, + SwCalibrationAccess, + UpperSts, + LowerSts, + ConstraintSpecified, + CompuContents, + ComponentType, + AtomicType, + ImplementationCodeType, + AccessPoint, + PortDirection, + Semantic, + SectionType, + Policy, +) + +__all__ = [ + "Base", + "create_uuid", + "create_text_element", + "Category", + "Encoding", + "CalibrationAccess", + "SwCalibrationAccess", + "UpperSts", + "LowerSts", + "ConstraintSpecified", + "CompuContents", + "ComponentType", + "AtomicType", + "ImplementationCodeType", + "AccessPoint", + "PortDirection", + "Semantic", + "SectionType", + "Policy", +] \ No newline at end of file diff --git a/src/core/base.py b/src/core/base.py new file mode 100644 index 0000000..068bee8 --- /dev/null +++ b/src/core/base.py @@ -0,0 +1,57 @@ +""" +核心基类和工具函数模块 +""" +import uuid +import re +import xml.dom.minidom as Dom +from dataclasses import dataclass, field +from typing import Optional, List + +from .constants import UUID, SHORT_NAME +from .enums import Category, Encoding, CalibrationAccess, UpperSts, LowerSts + + +def create_uuid() -> str: + """生成 UUID""" + return str(uuid.uuid4()) + + +def create_text_element(doc: Dom.Document, tag: str, text: str) -> Dom.Element: + """创建文本元素""" + element = doc.createElement(tag) + node = doc.createTextNode(text) + element.appendChild(node) + return element + + +@dataclass +class Base: + """所有类的基类,提供名称和层级结构支持""" + name: str + id: Optional[int] = field(default=None) + parent: Optional['Base'] = field(default=None, repr=False) + description: Optional[str] = field(default=None) + + @property + def class_name(self) -> str: + """获取类型名称""" + return self.__class__.__name__ + + @property + def parent_id(self) -> Optional[int]: + """获取父类ID""" + return self.parent.id if self.parent is not None else None + + @property + def package_path(self) -> str: + """获取完整路径(包路径+名称)""" + path = [] + current = self + while current is not None: + path.append(current.name) + current = current.parent + return '/' + '/'.join(reversed(path)) + + def to_arxml(self, doc: Dom.Document) -> Dom.Element: + """转换为 ARXML 元素""" + raise NotImplementedError("to_arxml method must be implemented by subclass") \ No newline at end of file diff --git a/src/core/constants.py b/src/core/constants.py new file mode 100644 index 0000000..79264b5 --- /dev/null +++ b/src/core/constants.py @@ -0,0 +1,138 @@ +""" +AUTOSAR ARXML 标签常量统一管理模块 +""" + +UUID = "UUID" +AR_PACKAGE = "AR-PACKAGE" +AR_PACKAGES = "AR-PACKAGES" +ELEMENT = "ELEMENT" +ELEMENTS = "ELEMENTS" +CATEGORY = "CATEGORY" +SHORT_NAME = "SHORT-NAME" +DEST = "DEST" +UNIT = "UNIT" +UNIT_REF = "UNIT-REF" +COMPU_METHOD = "COMPU-METHOD" +COMPU_METHOD_REF = "COMPU-METHOD-REF" +DATA_CONSTR = "DATA-CONSTR" +DATA_CONSTR_REF = "DATA-CONSTR-REF" +TYPE_TREF = "TYPE-TREF" +SW_BASE_TYPE = "SW-BASE-TYPE" +BASE_TYPE_REF = "BASE-TYPE-REF" +IMPLEMENTATION_DATA_TYPE_REF = "IMPLEMENTATION-DATA-TYPE-REF" +APPLICATION_DATA_TYPE_REF = "APPLICATION-DATA-TYPE-REF" +IS_SERVICE = "IS-SERVICE" +SENDER_RECEIVER_INTERFACE = "SENDER-RECEIVER-INTERFACE" +APPLICATION_SW_COMPONENT_TYPE = "APPLICATION-SW-COMPONENT-TYPE" +PORTS = "PORTS" +R_PORT_PROTOTYPE = "R-PORT-PROTOTYPE" +P_PORT_PROTOTYPE = "P-PORT-PROTOTYPE" +REQUIRED_COM_SPECS = "REQUIRED-COM-SPECS" +PROVIDED_COM_SPECS = "PROVIDED-COM-SPECS" +PROVIDED_INTERFACE_TREF = "PROVIDED-INTERFACE-TREF" +REQUIRED_INTERFACE_TREF = "REQUIRED-INTERFACE-TREF" +NONQUEUED_SENDER_COM_SPEC = "NONQUEUED-SENDER-COM-SPEC" +NONQUEUED_RECEIVER_COM_SPEC = "NONQUEUED-RECEIVER-COM-SPEC" +FIELDS = "FIELDS" +TYPE_REF = "TYPE-REF" +VARIABLE_DATA_PROTOTYPE = "VARIABLE-DATA-PROTOTYPE" +PORT_REF = "PORT-REF" +ENABLE_TAKE_ADDRESS = "ENABLE-TAKE-ADDRESS" +ERROR_HANDLING = "ERROR-HANDLING" +INDIRECT_API = "INDIRECT-API" +ARRAY_VALUE_SPECIFICATION = "ARRAY-VALUE-SPECIFICATION" +RECORD_VALUE_SPECIFICATION = "RECORD-VALUE-SPECIFICATION" +NUMERICAL_VALUE_SPECIFICATION = "NUMERICAL-VALUE-SPECIFICATION" +DATA_ELEMENT_REF = "DATA-ELEMENT-REF" +HANDLE_OUT_OF_RANGE = "HANDLE-OUT-OF-RANGE" +USES_END_TO_END_PROTECTION = "USES-END-TO-END-PROTECTION" +ALIVE_TIMEOUT = "ALIVE-TIMEOUT" +ENABLE_UPDATE = "ENABLE-UPDATE" +HANDLE_NEVER_RECEIVED = "HANDLE-NEVER-RECEIVED" +HANDLE_TIMEOUT_TYPE = "HANDLE-TIMEOUT-TYPE" +INIT_VALUE = "INIT-VALUE" +APPLICATION_VALUE_SPECIFICATION = "APPLICATION-VALUE-SPECIFICATION" +SHORT_LABEL = "SHORT-LABEL" +SW_VALUE_CONT = "SW-VALUE-CONT" +SW_VALUES_PHYS = "SW-VALUES-PHYS" +INTERNAL_BEHAVIORS = "INTERNAL-BEHAVIORS" +SWC_INTERNAL_BEHAVIOR = "SWC-INTERNAL-BEHAVIOR" +DATA_TYPE_MAPPING_REF = "DATA-TYPE-MAPPING-REF" +DATA_TYPE_MAPPING_REFS = "DATA-TYPE-MAPPING-REFS" +EVENTS = "EVENTS" +PERIOD = "PERIOD" +SW_ADDR_METHOD_REF = "SW-ADDR-METHOD-REF" +CLIENT_SERVER_OPERATION_REF = "CLIENT-SERVER-OPERATION-REF" +INIT_EVENT = "INIT-EVENT" +TIMING_EVENT = "TIMING-EVENT" +START_ON_EVENT_REF = "START-ON-EVENT-REF" +PORT_API_OPTIONS = "PORT-API-OPTIONS" +PORT_API_OPTION = "PORT-API-OPTION" +HANDLE_TERMINATION_AND_RESTART = "HANDLE-TERMINATION-AND-RESTART" +SYMBOL = "SYMBOL" +RUNNABLES = "RUNNABLES" +RUNNABLE_ENTITY = "RUNNABLE-ENTITY" +MINIMUM_START_INTERVAL = "MINIMUM-START-INTERVAL" +CAN_BE_INVOKED_CONCURRENTLY = "CAN-BE-INVOKED-CONCURRENTLY" +DATA_RECEIVE_POINT_BY_ARGUMENTS = "DATA-RECEIVE-POINT-BY-ARGUMENTS" +VARIABLE_ACCESS = "VARIABLE-ACCESS" +ACCESSED_VARIABLE = "ACCESSED-VARIABLE" +AUTOSAR_VARIABLE_IREF = "AUTOSAR-VARIABLE-IREF" +PORT_PROTOTYPE_REF = "PORT-PROTOTYPE-REF" +TARGET_DATA_PROTOTYPE_REF = "TARGET-DATA-PROTOTYPE-REF" +DATA_SEND_POINTS = "DATA-SEND-POINTS" +SW_DATA_DEF_PROPS = "SW-DATA-DEF-PROPS" +SW_DATA_DEF_PROPS_VARIANTS = "SW-DATA-DEF-PROPS-VARIANTS" +SW_DATA_DEF_PROPS_CONDITIONAL = "SW-DATA-DEF-PROPS-CONDITIONAL" +SW_CALIBRATION_ACCESS = "SW-CALIBRATION-ACCESS" +SW_IMPL_POLICY = "SW-IMPL-POLICY" +SWC_IMPLEMENTATION = "SWC-IMPLEMENTATION" +CODE = "CODE" +CODE_DESCRIPTORS = "CODE-DESCRIPTORS" +BEHAVIOR_REF = "BEHAVIOR-REF" +AUTOSAR_ENGINEERING_OBJECT = "AUTOSAR-ENGINEERING-OBJECT" +ARTIFACT_DESCRIPTORS = "ARTIFACT-DESCRIPTORS" +SW_ADDR_METHOD = "SW-ADDR-METHOD" +SECTION_TYPE = "SECTION-TYPE" +APPLICATION_PRIMITIVE_DATA_TYPE = "APPLICATION-PRIMITIVE-DATA-TYPE" +APPLICATION_RECORD_DATA_TYPE = "APPLICATION-RECORD-DATA-TYPE" +APPLICATION_ARRAY_DATA_TYPE = "APPLICATION-ARRAY-DATA-TYPE" +ARRAY_SIZE = "ARRAY-SIZE" +ARRAY_SIZE_SEMANTICS = "ARRAY-SIZE-SEMANTICS" +MAX_NUMBER_OF_ELEMENTS = "MAX-NUMBER-OF-ELEMENTS" +PHYS_CONSTRS = "PHYS-CONSTRS" +DATA_CONSTR_RULE = "DATA-CONSTR-RULE" +DATA_CONSTR_RULES = "DATA-CONSTR-RULES" +LOWER_LIMIT = "LOWER-LIMIT" +UPPER_LIMIT = "UPPER-LIMIT" +V = "V" +VT = "VT" +COMPU_INTERNAL_TO_PHYS = "COMPU-INTERNAL-TO-PHYS" +COMPU_SCALES = "COMPU-SCALES" +COMPU_SCALE = "COMPU-SCALE" +COMPU_CONST = "COMPU-CONST" +INTERVAL_TYPE = "INTERVAL-TYPE" +COMPU_NUMERATOR = "COMPU-NUMERATOR" +COMPU_DENOMINATOR = "COMPU-DENOMINATOR" +COMPU_RATIONAL_COEFFS = "COMPU-RATIONAL-COEFFS" +IMPLEMENTATION_DATA_TYPE = "IMPLEMENTATION-DATA-TYPE" +IMPLEMENTATION_DATA_TYPE_ELEMENT = "IMPLEMENTATION-DATA-TYPE-ELEMENT" +SUB_ELEMENTS = "SUB-ELEMENTS" +BASE_TYPE_SIZE = "BASE-TYPE-SIZE" +BASE_TYPE_ENCODING = "BASE-TYPE-ENCODING" +NATIVE_DECLARATION = "NATIVE-DECLARATION" +FACTOR_SI_TO_UNIT = "FACTOR-SI-TO-UNIT" +OFFSET_SI_TO_UNIT = "OFFSET-SI-TO-UNIT" +DATA_TYPE_MAPPING_SET = "DATA-TYPE-MAPPING-SET" +DATA_TYPE_MAPS = "DATA-TYPE-MAPS" +DATA_TYPE_MAP = "DATA-TYPE-MAP" +AUTOSAR = "AUTOSAR" +APPLICATION_RECORD_ELEMENT = "APPLICATION-RECORD-ELEMENT" +DATA_ELEMENTS = "DATA-ELEMENTS" +CLIENT_SERVER_INTERFACE = "CLIENT-SERVER-INTERFACE" +CLIENT_SERVER_OPERATION = "CLIENT-SERVER-OPERATION" +ARGUMENT = "ARGUMENT" +APPLICATION_PRIMITIVE_TYPE_REF = "APPLICATION-PRIMITIVE-TYPE-REF" +DIRECTION = "DIRECTION" +OPERATION_INVOKED_EVENT = "OPERATION-INVOKED-EVENT" +DATA_RECEIVED_EVENT = "DATA-RECEIVED-EVENT" \ No newline at end of file diff --git a/src/core/enums.py b/src/core/enums.py new file mode 100644 index 0000000..3fa7ea3 --- /dev/null +++ b/src/core/enums.py @@ -0,0 +1,135 @@ +""" +AUTOSAR 枚举类型统一管理模块 +""" +from enum import Enum, auto + + +class Category(Enum): + """数据分类类型""" + FIXED_LENGTH = auto() + IDENTICAL = auto() + TEXT_TABLE = auto() + LINEAR = auto() + BOOLEAN = auto() + VALUE = auto() + STRUCTURE = auto() + ARRAY = auto() + SCALE_LINEAR_AND_TEXT_TABLE = auto() + BITFIELD_TEXT_TABLE = auto() + SCALE_LINEAR = auto() + RECORD = auto() + UNION = auto() + DATA_REFERENCE = auto() + TYPE_REFERENCE = auto() + + +class Encoding(Enum): + """数据编码格式""" + ONE_COMPONENT = "1C - One's Complement" + TWO_COMPONENT = "2C - Two's Complement" + BCD_P = "BCD-p - Packed Binary Coded Decimals" + BCD_UP = "BCD-Up - Unpacked Binary Coded Decimals" + DSP_FRACTIONAL = "DSP-FRACTIONAL - Digital Signal Processor" + SM = "SM - Sign Magnitude" + IEEE754 = "IEEE754 - Floating Point" + ISO_8859_1 = "ISO-8859-1 - ASCII-Strings" + ISO_8859_2 = "ISO-8859-2 - ASCII-Strings" + WINDOWS_1252 = "Windows-1252 - ASCII Strings" + UTF_8 = "UTF-8 - UCS Transformation Format 8" + UCS_2 = "UCS-2 - Universal Character Set 2" + NONE = "NONE - Unsigned Integer" + VOID = "VOID - C Language" + BOOLEAN = "BOOLEAN - Logical Type" + UTF_16 = "UTF-16: Character encoding for Unicode code points" + + +class CalibrationAccess(Enum): + """校准访问权限""" + READ_ONLY = 'Read-Only' + NOT_ACCESSIBLE = 'Not Accessible' + READ_WRITE = 'Read-Write' + NOT_SPECIFIED = 'Not Specified' + + +class SwCalibrationAccess(Enum): + """校准访问权限(同 CalibrationAccess,保持向后兼容)""" + ReadOnly = 'Read-Only' + NotAccessible = 'Not Accessible' + ReadWrite = 'Read-Write' + NotSpecified = 'Not Specified' + + +class UpperSts(Enum): + """上限区间状态""" + CLOSED = '[' + OPEN = ']' + + +class LowerSts(Enum): + """下限区间状态""" + CLOSED = ']' + OPEN = '[' + + +class ConstraintSpecified(Enum): + """约束指定类型""" + PHYSICAL = auto() + INTERNAL = auto() + + +class CompuContents(Enum): + """转换内容方向""" + USE_INTERNAL_TO_PHYSICAL = auto() + USE_PHYSICAL_TO_INTERNAL = auto() + + +class ComponentType(Enum): + """组件类型""" + COMPOSITION = auto() + ATOMIC = auto() + + +class AtomicType(Enum): + """原子组件类型""" + APPLICATION = "Application" + SENSOR_ACTUATOR = "SensorActuator" + HARDWARE_ABSTRACTION = "I/O - Hardware Abstraction (application layer)" + COMPLEX_DRIVER = "Complex Driver (application layer)" + SERVICE_PROXY = "Service Proxy" + + +class ImplementationCodeType(Enum): + """实现代码类型""" + SOURCE_CODE = auto() + OBJECT_CODE = auto() + + +class AccessPoint(Enum): + """访问点""" + READ = auto() + WRITE = auto() + + +class PortDirection(Enum): + """端口方向""" + IN = auto() + OUT = auto() + + +class Semantic(Enum): + """语义/数组大小语义""" + FIXED = 'FIXED-SIZE' + VARIABLE = 'Variable' + + +class SectionType(Enum): + """段类型""" + CODE = auto() + DATA = auto() + CONST = auto() + + +class Policy(Enum): + """数据策略""" + STANDARD = auto() + EXTENDED = auto() \ No newline at end of file diff --git a/src/types/__init__.py b/src/types/__init__.py new file mode 100644 index 0000000..015116e --- /dev/null +++ b/src/types/__init__.py @@ -0,0 +1,141 @@ +""" +types 模块 - 数据类型统一管理 +""" +from .base_type import BaseType, create_base_type +from .unit import Unit, create_unit +from .data_constraint import DataConstraint, create_data_constraint +from .compu_method import ( + Linear, TextTable, CompuMethod, + create_linear, create_text_table, create_compu_method +) +from .application_types import ( + ApplicationDataType, + ApplicationBooleanDataType, + ApplicationValueDataType, + ApplicationStructureDataType, + ApplicationArrayDataType, + create_boolean_type, + create_value_type, + create_structure_type, + create_array_type, +) +from .implementation_types import ( + ImplementationDataType, + ImplementationValueDataType, + ImplementationStructureDataType, + ImplementationArrayDataType, + create_impl_value_type, + create_impl_structure_type, + create_impl_array_type, +) +from .sw_addr_method import SwAddrMethod, create_sw_addr_method +from .data_mapping import ( + DataTypeMapping, + DataTypeMappingSet, + create_data_type_mapping, + create_data_type_mapping_set, +) +from .sw_component_type import ( + SwComponentType, + AtomicComponentType, + CompositionSwComponentType, + ApplicationSwComponentType, + PortPrototype, + RPortPrototype, + PPortPrototype, + PRPortPrototype, +) +from .interface import ( + Interface, + SenderReceiverInterface, + ClientServerInterface, + ModeSwitchInterface, + ParameterInterface, + TriggerInterface, + NvDataInterface, + create_sender_receiver_interface, +) +from .package import Package, create_package +from .swc_internal_behavior import ( + SwcInternalBehavior, + RunnableEntity, + VariableAccess, + InitEvent, + TimingEvent, + OperationInvokedEvent, + DataReceivedEvent, + create_runnable_entity, + create_timing_event, + create_init_event, + create_operation_invoked_event, + create_data_received_event, + create_swc_internal_behavior, +) + +__all__ = [ + "BaseType", + "create_base_type", + "Unit", + "create_unit", + "DataConstraint", + "create_data_constraint", + "Linear", + "TextTable", + "CompuMethod", + "create_linear", + "create_text_table", + "create_compu_method", + "ApplicationDataType", + "ApplicationBooleanDataType", + "ApplicationValueDataType", + "ApplicationStructureDataType", + "ApplicationArrayDataType", + "create_boolean_type", + "create_value_type", + "create_structure_type", + "create_array_type", + "ImplementationDataType", + "ImplementationValueDataType", + "ImplementationStructureDataType", + "ImplementationArrayDataType", + "create_impl_value_type", + "create_impl_structure_type", + "create_impl_array_type", + "SwAddrMethod", + "create_sw_addr_method", + "DataTypeMapping", + "DataTypeMappingSet", + "create_data_type_mapping", + "create_data_type_mapping_set", + "SwComponentType", + "AtomicComponentType", + "CompositionSwComponentType", + "ApplicationSwComponentType", + "PortPrototype", + "RPortPrototype", + "PPortPrototype", + "PRPortPrototype", + "Interface", + "SenderReceiverInterface", + "ClientServerInterface", + "ModeSwitchInterface", + "ParameterInterface", + "TriggerInterface", + "NvDataInterface", + "create_sender_receiver_interface", + "Package", + "create_package", + "SwcInternalBehavior", + "RunnableEntity", + "VariableAccess", + "InitEvent", + "TimingEvent", + "OperationInvokedEvent", + "DataReceivedEvent", + "create_runnable_entity", + "create_timing_event", + "create_init_event", + "create_operation_invoked_event", + "create_data_received_event", + "create_swc_internal_behavior", +] \ No newline at end of file diff --git a/src/types/application_types.py b/src/types/application_types.py new file mode 100644 index 0000000..e8e5f4a --- /dev/null +++ b/src/types/application_types.py @@ -0,0 +1,273 @@ +""" +应用数据类型模块 +""" +from dataclasses import dataclass, field +from typing import Optional, List, TYPE_CHECKING, Union +from xml.dom.minidom import Document + +from ..core.base import Base, create_uuid, create_text_element +from ..core.constants import ( + APPLICATION_PRIMITIVE_DATA_TYPE, APPLICATION_RECORD_DATA_TYPE, APPLICATION_ARRAY_DATA_TYPE, + SHORT_NAME, CATEGORY, SW_DATA_DEF_PROPS, SW_DATA_DEF_PROPS_VARIANTS, + SW_DATA_DEF_PROPS_CONDITIONAL, SW_CALIBRATION_ACCESS, COMPU_METHOD_REF, DEST, + COMPU_METHOD, DATA_CONSTR_REF, DATA_CONSTR, UNIT_REF, UNIT, ELEMENTS, ELEMENT, + ARRAY_SIZE_SEMANTICS, MAX_NUMBER_OF_ELEMENTS, TYPE_TREF, APPLICATION_RECORD_ELEMENT +) +from ..core.enums import Category, CalibrationAccess, Semantic + +if TYPE_CHECKING: + from .unit import Unit + from .compu_method import CompuMethod + from .data_constraint import DataConstraint + + +@dataclass +class ApplicationDataType(Base): + """应用数据类型基类""" + category: Optional[Category] = None + calibration_access: CalibrationAccess = CalibrationAccess.READ_WRITE + + +@dataclass +class ApplicationBooleanDataType(ApplicationDataType): + """布尔类型""" + compu_method: Optional['CompuMethod'] = None + data_constraint: Optional['DataConstraint'] = None + category: Category = Category.BOOLEAN + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(APPLICATION_PRIMITIVE_DATA_TYPE) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + element.appendChild(create_text_element(doc, CATEGORY, self.category.name)) + + sw_data_def_props = doc.createElement(SW_DATA_DEF_PROPS) + sw_data_def_props_variants = doc.createElement(SW_DATA_DEF_PROPS_VARIANTS) + sw_data_def_props_conditional = doc.createElement(SW_DATA_DEF_PROPS_CONDITIONAL) + sw_data_def_props_conditional.appendChild( + create_text_element(doc, SW_CALIBRATION_ACCESS, self.calibration_access.value.upper()) + ) + + if self.compu_method is not None: + compu_method_ref = create_text_element(doc, COMPU_METHOD_REF, self.compu_method.package_path) + compu_method_ref.setAttribute(DEST, COMPU_METHOD) + sw_data_def_props_conditional.appendChild(compu_method_ref) + + if self.data_constraint is not None: + data_constr_ref = create_text_element(doc, DATA_CONSTR_REF, self.data_constraint.package_path) + data_constr_ref.setAttribute(DEST, DATA_CONSTR) + sw_data_def_props_conditional.appendChild(data_constr_ref) + + sw_data_def_props_variants.appendChild(sw_data_def_props_conditional) + sw_data_def_props.appendChild(sw_data_def_props_variants) + element.appendChild(sw_data_def_props) + + return element + + +@dataclass +class ApplicationValueDataType(ApplicationDataType): + """值类型(标量值)""" + unit: Optional['Unit'] = None + compu_method: Optional['CompuMethod'] = None + data_constraint: Optional['DataConstraint'] = None + category: Category = Category.VALUE + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(APPLICATION_PRIMITIVE_DATA_TYPE) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + element.appendChild(create_text_element(doc, CATEGORY, self.category.name)) + + sw_data_def_props = doc.createElement(SW_DATA_DEF_PROPS) + sw_data_def_props_variants = doc.createElement(SW_DATA_DEF_PROPS_VARIANTS) + sw_data_def_props_conditional = doc.createElement(SW_DATA_DEF_PROPS_CONDITIONAL) + sw_data_def_props_conditional.appendChild( + create_text_element(doc, SW_CALIBRATION_ACCESS, self.calibration_access.value.upper()) + ) + + if self.unit is not None: + unit_ref = create_text_element(doc, UNIT_REF, self.unit.package_path) + unit_ref.setAttribute(DEST, UNIT) + sw_data_def_props_conditional.appendChild(unit_ref) + + if self.compu_method is not None: + compu_method_ref = create_text_element(doc, COMPU_METHOD_REF, self.compu_method.package_path) + compu_method_ref.setAttribute(DEST, COMPU_METHOD) + sw_data_def_props_conditional.appendChild(compu_method_ref) + + if self.data_constraint is not None: + data_constr_ref = create_text_element(doc, DATA_CONSTR_REF, self.data_constraint.package_path) + data_constr_ref.setAttribute(DEST, DATA_CONSTR) + sw_data_def_props_conditional.appendChild(data_constr_ref) + + sw_data_def_props_variants.appendChild(sw_data_def_props_conditional) + sw_data_def_props.appendChild(sw_data_def_props_variants) + element.appendChild(sw_data_def_props) + + return element + + +@dataclass +class ApplicationStructureDataType(ApplicationDataType): + """结构体类型""" + category: Category = Category.STRUCTURE + structure_elements: List['ApplicationStructureDataType.StructureElement'] = field(default_factory=list) + + @dataclass + class StructureElement(Base): + """结构体成员""" + data_type: Optional[ApplicationDataType] = None + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(APPLICATION_RECORD_ELEMENT) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + if self.data_type: + element.appendChild(create_text_element(doc, CATEGORY, self.data_type.category.name)) + type_ref = create_text_element(doc, TYPE_TREF, self.data_type.package_path) + type_ref.setAttribute('DEST', APPLICATION_PRIMITIVE_DATA_TYPE) + element.appendChild(type_ref) + return element + + def get_element(self, name: str) -> Optional['StructureElement']: + """根据名称查找成员""" + for element in self.structure_elements: + if element.name == name: + return element + return None + + def add_element(self, element: 'StructureElement') -> None: + """添加结构成员""" + if self.get_element(element.name) is not None: + return + self.structure_elements.append(element) + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(APPLICATION_RECORD_DATA_TYPE) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + element.appendChild(create_text_element(doc, CATEGORY, self.category.name)) + + sw_data_def_props = doc.createElement(SW_DATA_DEF_PROPS) + sw_data_def_props_variants = doc.createElement(SW_DATA_DEF_PROPS_VARIANTS) + sw_data_def_props_conditional = doc.createElement(SW_DATA_DEF_PROPS_CONDITIONAL) + sw_data_def_props_conditional.appendChild( + create_text_element(doc, SW_CALIBRATION_ACCESS, self.calibration_access.value.upper()) + ) + sw_data_def_props_variants.appendChild(sw_data_def_props_conditional) + sw_data_def_props.appendChild(sw_data_def_props_variants) + element.appendChild(sw_data_def_props) + + elements = doc.createElement(ELEMENTS) + for structure_element in self.structure_elements: + elements.appendChild(structure_element.to_arxml(doc)) + element.appendChild(elements) + + return element + + +@dataclass +class ApplicationArrayDataType(ApplicationDataType): + """数组类型""" + category: Category = Category.ARRAY + + @dataclass + class ArrayElement(Base): + """数组成员""" + length: Optional[int] = None + data_type: Optional[ApplicationDataType] = None + array_size_semantics: Semantic = Semantic.FIXED + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(ELEMENT) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + if self.data_type: + element.appendChild(create_text_element(doc, CATEGORY, self.data_type.category.name)) + type_ref = create_text_element(doc, TYPE_TREF, self.data_type.package_path) + type_ref.setAttribute('DEST', APPLICATION_PRIMITIVE_DATA_TYPE) + element.appendChild(type_ref) + element.appendChild(create_text_element(doc, ARRAY_SIZE_SEMANTICS, self.array_size_semantics.value)) + element.appendChild(create_text_element(doc, MAX_NUMBER_OF_ELEMENTS, str(self.length or 0))) + return element + + element: Optional['ArrayElement'] = None + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(APPLICATION_ARRAY_DATA_TYPE) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + element.appendChild(create_text_element(doc, CATEGORY, self.category.name)) + + sw_data_def_props = doc.createElement(SW_DATA_DEF_PROPS) + sw_data_def_props_variants = doc.createElement(SW_DATA_DEF_PROPS_VARIANTS) + sw_data_def_props_conditional = doc.createElement(SW_DATA_DEF_PROPS_CONDITIONAL) + sw_data_def_props_conditional.appendChild( + create_text_element(doc, SW_CALIBRATION_ACCESS, self.calibration_access.value.upper()) + ) + sw_data_def_props_variants.appendChild(sw_data_def_props_conditional) + sw_data_def_props.appendChild(sw_data_def_props_variants) + element.appendChild(sw_data_def_props) + + if self.element: + array_element = doc.createElement(ELEMENT) + array_element.appendChild(self.element.to_arxml(doc)) + element.appendChild(array_element) + + return element + + +def create_boolean_type( + name: str, + compu_method: Optional['CompuMethod'] = None, + data_constraint: Optional['DataConstraint'] = None, +) -> ApplicationBooleanDataType: + """创建布尔类型的工厂函数""" + return ApplicationBooleanDataType( + name=name, + compu_method=compu_method, + data_constraint=data_constraint, + ) + + +def create_value_type( + name: str, + unit: Optional['Unit'] = None, + compu_method: Optional['CompuMethod'] = None, + data_constraint: Optional['DataConstraint'] = None, +) -> ApplicationValueDataType: + """创建值类型的工厂函数""" + return ApplicationValueDataType( + name=name, + unit=unit, + compu_method=compu_method, + data_constraint=data_constraint, + ) + + +def create_structure_type( + name: str, + elements: Optional[List['ApplicationStructureDataType.StructureElement']] = None, +) -> ApplicationStructureDataType: + """创建结构体类型的工厂函数""" + return ApplicationStructureDataType( + name=name, + structure_elements=elements or [], + ) + + +def create_array_type( + name: str, + element: Optional['ApplicationArrayDataType.ArrayElement'] = None, +) -> ApplicationArrayDataType: + """创建数组类型的工厂函数""" + return ApplicationArrayDataType( + name=name, + element=element, + ) \ No newline at end of file diff --git a/src/types/base_type.py b/src/types/base_type.py new file mode 100644 index 0000000..9ab6042 --- /dev/null +++ b/src/types/base_type.py @@ -0,0 +1,57 @@ +""" +基础数据类型模块 +""" +from dataclasses import dataclass, field +from typing import Optional +from xml.dom.minidom import Document + +from ..core.base import Base, create_uuid, create_text_element +from ..core.constants import SW_BASE_TYPE, SHORT_NAME, CATEGORY, BASE_TYPE_SIZE, BASE_TYPE_ENCODING, NATIVE_DECLARATION +from ..core.enums import Category, Encoding + + +@dataclass +class BaseType(Base): + """基础数据类型""" + size: Optional[int] = 8 + category: Category = Category.FIXED_LENGTH + encoding: Encoding = Encoding.ONE_COMPONENT + native_description: Optional[str] = None + + @property + def encoding_value(self) -> str: + """获取编码值""" + if self.encoding == Encoding.ONE_COMPONENT: + return "1C" + elif self.encoding == Encoding.TWO_COMPONENT: + return "2C" + return self.encoding.name.upper() + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(SW_BASE_TYPE) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + element.appendChild(create_text_element(doc, CATEGORY, self.category.name)) + element.appendChild(create_text_element(doc, BASE_TYPE_SIZE, str(self.size))) + element.appendChild(create_text_element(doc, BASE_TYPE_ENCODING, self.encoding_value)) + if self.native_description: + element.appendChild(create_text_element(doc, NATIVE_DECLARATION, self.native_description)) + return element + + +def create_base_type( + name: str, + size: int = 8, + category: Category = Category.FIXED_LENGTH, + encoding: Encoding = Encoding.ONE_COMPONENT, + native_description: Optional[str] = None, +) -> BaseType: + """创建基础数据类型的工厂函数""" + return BaseType( + name=name, + size=size, + category=category, + encoding=encoding, + native_description=native_description, + ) \ No newline at end of file diff --git a/src/types/compu_method.py b/src/types/compu_method.py new file mode 100644 index 0000000..b24e5b4 --- /dev/null +++ b/src/types/compu_method.py @@ -0,0 +1,143 @@ +""" +计算方法类型模块 +""" +from dataclasses import dataclass, field +from typing import Optional, List +from xml.dom.minidom import Document + +from ..core.base import Base, create_uuid, create_text_element +from ..core.constants import ( + COMPU_METHOD, SHORT_NAME, CATEGORY, UNIT_REF, DEST, UNIT, + COMPU_INTERNAL_TO_PHYS, COMPU_SCALES, COMPU_SCALE, COMPU_CONST, + LOWER_LIMIT, UPPER_LIMIT, INTERVAL_TYPE, VT, + COMPU_NUMERATOR, COMPU_DENOMINATOR, COMPU_RATIONAL_COEFFS, V +) +from ..core.enums import Category, CompuContents, LowerSts, UpperSts +from .unit import Unit + + +@dataclass +class Linear(Base): + """线性转换""" + factor: Optional[float] = None + offset: Optional[float] = None + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + compu_scale = doc.createElement(COMPU_SCALE) + compu_rational_coeffes = doc.createElement(COMPU_RATIONAL_COEFFS) + + compu_numerator = doc.createElement(COMPU_NUMERATOR) + compu_numerator.appendChild(create_text_element(doc, V, str(self.offset or 0))) + compu_numerator.appendChild(create_text_element(doc, V, str(self.factor or 0))) + compu_rational_coeffes.appendChild(compu_numerator) + + compu_denominator = doc.createElement(COMPU_DENOMINATOR) + compu_denominator.appendChild(create_text_element(doc, V, '1')) + compu_rational_coeffes.appendChild(compu_denominator) + + compu_scale.appendChild(compu_rational_coeffes) + return compu_scale + + +@dataclass +class TextTable(Base): + """文本表""" + vt: Optional[str] = None + lower: Optional[int] = None + upper: Optional[int] = None + lower_sts: LowerSts = LowerSts.CLOSED + upper_sts: UpperSts = UpperSts.CLOSED + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + compu_scale = doc.createElement(COMPU_SCALE) + lower_limit = create_text_element(doc, LOWER_LIMIT, str(self.lower or 0)) + lower_limit.setAttribute(INTERVAL_TYPE, self.lower_sts.value) + compu_scale.appendChild(lower_limit) + + upper_limit = create_text_element(doc, UPPER_LIMIT, str(self.upper or 0)) + upper_limit.setAttribute(INTERVAL_TYPE, self.upper_sts.value) + compu_scale.appendChild(upper_limit) + + compu_const = doc.createElement(COMPU_CONST) + compu_const.appendChild(create_text_element(doc, VT, self.vt or '')) + compu_scale.appendChild(compu_const) + + return compu_scale + + +@dataclass +class CompuMethod(Base): + """计算方法""" + unit: Optional[Unit] = None + linear: Optional[Linear] = None + text_tables: List[TextTable] = field(default_factory=list) + category: Category = Category.IDENTICAL + compu_contents: CompuContents = CompuContents.USE_INTERNAL_TO_PHYSICAL + + def get_text_table(self, vt: str) -> Optional[TextTable]: + """根据 vt 查找文本表""" + for text_table in self.text_tables: + if text_table.vt == vt: + return text_table + return None + + def add_text_table(self, text_table: TextTable) -> None: + """添加文本表""" + if self.get_text_table(text_table.vt) is not None: + return + self.text_tables.append(text_table) + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(COMPU_METHOD) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + element.appendChild(create_text_element(doc, CATEGORY, self.category.name.replace("_", ""))) + + if self.unit is not None: + unit_ref = create_text_element(doc, UNIT_REF, self.unit.package_path) + unit_ref.setAttribute(DEST, UNIT) + element.appendChild(unit_ref) + + compu_internal_to_phys = doc.createElement(COMPU_INTERNAL_TO_PHYS) + compu_scales = doc.createElement(COMPU_SCALES) + + for text_table in self.text_tables: + compu_scales.appendChild(text_table.to_arxml(doc)) + + if self.linear is not None: + compu_scales.appendChild(self.linear.to_arxml(doc)) + + compu_internal_to_phys.appendChild(compu_scales) + element.appendChild(compu_internal_to_phys) + + return element + + +def create_linear(factor: float, offset: float, name: str = "Linear") -> Linear: + """创建线性转换的工厂函数""" + return Linear(name=name, factor=factor, offset=offset) + + +def create_text_table(name: str, vt: str, lower: int = 0, upper: int = 0) -> TextTable: + """创建文本表的工厂函数""" + return TextTable(name=name, vt=vt, lower=lower, upper=upper) + + +def create_compu_method( + name: str, + unit: Optional[Unit] = None, + linear: Optional[Linear] = None, + text_tables: Optional[List[TextTable]] = None, + category: Category = Category.IDENTICAL, +) -> CompuMethod: + """创建计算方法的工厂函数""" + return CompuMethod( + name=name, + unit=unit, + linear=linear, + text_tables=text_tables or [], + category=category, + ) \ No newline at end of file diff --git a/src/types/data_constraint.py b/src/types/data_constraint.py new file mode 100644 index 0000000..5b20a7f --- /dev/null +++ b/src/types/data_constraint.py @@ -0,0 +1,73 @@ +""" +数据约束类型模块 +""" +from dataclasses import dataclass, field +from typing import Optional, Union +from xml.dom.minidom import Document + +from ..core.base import Base, create_uuid, create_text_element +from ..core.constants import ( + DATA_CONSTR, SHORT_NAME, DATA_CONSTR_RULES, DATA_CONSTR_RULE, + PHYS_CONSTRS, LOWER_LIMIT, UPPER_LIMIT, UNIT_REF, DEST, UNIT, INTERVAL_TYPE +) +from ..core.enums import UpperSts, LowerSts, ConstraintSpecified +from .unit import Unit + + +@dataclass +class DataConstraint(Base): + """数据约束""" + unit: Optional[Unit] = None + lower: Union[int, float] = 0 + upper: Union[int, float] = 1 + lower_sts: LowerSts = LowerSts.CLOSED + upper_sts: UpperSts = UpperSts.CLOSED + constraint_specified: ConstraintSpecified = ConstraintSpecified.PHYSICAL + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(DATA_CONSTR) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + + data_constr_rules = doc.createElement(DATA_CONSTR_RULES) + data_constr_rule = doc.createElement(DATA_CONSTR_RULE) + phys_constrs = doc.createElement(PHYS_CONSTRS) + + lower_limit = create_text_element(doc, LOWER_LIMIT, str(self.lower)) + lower_limit.setAttribute(INTERVAL_TYPE, self.lower_sts.name.upper()) + phys_constrs.appendChild(lower_limit) + + upper_limit = create_text_element(doc, UPPER_LIMIT, str(self.upper)) + upper_limit.setAttribute(INTERVAL_TYPE, self.upper_sts.name.upper()) + phys_constrs.appendChild(upper_limit) + + if self.unit is not None: + unit_ref = create_text_element(doc, UNIT_REF, self.unit.package_path) + unit_ref.setAttribute(DEST, UNIT) + phys_constrs.appendChild(unit_ref) + + data_constr_rule.appendChild(phys_constrs) + data_constr_rules.appendChild(data_constr_rule) + element.appendChild(data_constr_rules) + + return element + + +def create_data_constraint( + name: str, + lower: Union[int, float] = 0, + upper: Union[int, float] = 1, + lower_sts: LowerSts = LowerSts.CLOSED, + upper_sts: UpperSts = UpperSts.CLOSED, + unit: Optional[Unit] = None, +) -> DataConstraint: + """创建数据约束的工厂函数""" + return DataConstraint( + name=name, + lower=lower, + upper=upper, + lower_sts=lower_sts, + upper_sts=upper_sts, + unit=unit, + ) \ No newline at end of file diff --git a/src/types/data_mapping.py b/src/types/data_mapping.py new file mode 100644 index 0000000..7e4cff6 --- /dev/null +++ b/src/types/data_mapping.py @@ -0,0 +1,101 @@ +""" +数据类型映射集模块 +""" +from dataclasses import dataclass, field +from typing import Optional, List, TYPE_CHECKING +from xml.dom.minidom import Document + +from ..core.base import Base, create_uuid, create_text_element +from ..core.constants import ( + DATA_TYPE_MAPPING_SET, DATA_TYPE_MAPS, DATA_TYPE_MAP, + SHORT_NAME, APPLICATION_DATA_TYPE_REF, DEST, APPLICATION_PRIMITIVE_DATA_TYPE, + IMPLEMENTATION_DATA_TYPE_REF, DEST as REF_DEST, IMPLEMENTATION_DATA_TYPE +) +if TYPE_CHECKING: + from .application_types import ApplicationDataType + from .implementation_types import ImplementationDataType +else: + from .application_types import ApplicationDataType as ApplicationDataType + from .implementation_types import ImplementationDataType as ImplementationDataType + + +@dataclass +class DataTypeMapping(Base): + """数据类型映射""" + application_data_type: Optional['ApplicationDataType'] = None + implementation_data_type: Optional['ImplementationDataType'] = None + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(DATA_TYPE_MAP) + + if self.application_data_type: + app_type_ref = create_text_element( + doc, APPLICATION_DATA_TYPE_REF, self.application_data_type.package_path + ) + app_type_ref.setAttribute(DEST, APPLICATION_PRIMITIVE_DATA_TYPE) + element.appendChild(app_type_ref) + + if self.implementation_data_type: + impl_type_ref = create_text_element( + doc, IMPLEMENTATION_DATA_TYPE_REF, self.implementation_data_type.package_path + ) + impl_type_ref.setAttribute(DEST, IMPLEMENTATION_DATA_TYPE) + element.appendChild(impl_type_ref) + + return element + + +@dataclass +class DataTypeMappingSet(Base): + """数据类型映射集""" + data_type_mappings: List[DataTypeMapping] = field(default_factory=list) + + def get_mapping(self, name: str) -> Optional[DataTypeMapping]: + """根据名称查找映射""" + for mapping in self.data_type_mappings: + if mapping.name == name: + return mapping + return None + + def add_mapping(self, mapping: DataTypeMapping) -> None: + """添加映射""" + if self.get_mapping(mapping.name) is None: + self.data_type_mappings.append(mapping) + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(DATA_TYPE_MAPPING_SET) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + + data_type_maps = doc.createElement(DATA_TYPE_MAPS) + for mapping in self.data_type_mappings: + data_type_maps.appendChild(mapping.to_arxml(doc)) + element.appendChild(data_type_maps) + + return element + + +def create_data_type_mapping( + name: str, + application_data_type: Optional['ApplicationDataType'] = None, + implementation_data_type: Optional['ImplementationDataType'] = None, +) -> DataTypeMapping: + """创建数据类型映射的工厂函数""" + return DataTypeMapping( + name=name, + application_data_type=application_data_type, + implementation_data_type=implementation_data_type, + ) + + +def create_data_type_mapping_set( + name: str, + mappings: Optional[List[DataTypeMapping]] = None, +) -> DataTypeMappingSet: + """创建数据类型映射集的工厂函数""" + return DataTypeMappingSet( + name=name, + data_type_mappings=mappings or [], + ) \ No newline at end of file diff --git a/src/types/implementation_types.py b/src/types/implementation_types.py new file mode 100644 index 0000000..63d85c1 --- /dev/null +++ b/src/types/implementation_types.py @@ -0,0 +1,218 @@ +""" +实现数据类型模块 +""" +from dataclasses import dataclass, field +from typing import Optional, List, TYPE_CHECKING +from xml.dom.minidom import Document + +from ..core.base import Base, create_uuid, create_text_element +from ..core.constants import ( + IMPLEMENTATION_DATA_TYPE, IMPLEMENTATION_DATA_TYPE_ELEMENT, SHORT_NAME, CATEGORY, + SW_DATA_DEF_PROPS, SW_DATA_DEF_PROPS_VARIANTS, SW_DATA_DEF_PROPS_CONDITIONAL, + BASE_TYPE_REF, DEST, SW_BASE_TYPE, COMPU_METHOD_REF, COMPU_METHOD, + IMPLEMENTATION_DATA_TYPE_REF, SUB_ELEMENTS, ARRAY_SIZE, ARRAY_SIZE_SEMANTICS +) +from ..core.enums import Category, CalibrationAccess, Semantic + +if TYPE_CHECKING: + from .base_type import BaseType + from .compu_method import CompuMethod + from .data_constraint import DataConstraint + + +@dataclass +class ImplementationDataType(Base): + """实现数据类型基类""" + category: Optional[Category] = None + calibration_access: CalibrationAccess = CalibrationAccess.NOT_SPECIFIED + + +@dataclass +class ImplementationValueDataType(ImplementationDataType): + """值类型(标量值)""" + base_type: Optional['BaseType'] = None + compu_method: Optional['CompuMethod'] = None + data_constraint: Optional['DataConstraint'] = None + category: Category = Category.VALUE + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(IMPLEMENTATION_DATA_TYPE) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + element.appendChild(create_text_element(doc, CATEGORY, self.category.name)) + + sw_data_def_props = doc.createElement(SW_DATA_DEF_PROPS) + sw_data_def_props_variants = doc.createElement(SW_DATA_DEF_PROPS_VARIANTS) + sw_data_def_props_conditional = doc.createElement(SW_DATA_DEF_PROPS_CONDITIONAL) + + if self.base_type is not None: + base_type_ref = create_text_element(doc, BASE_TYPE_REF, self.base_type.package_path) + base_type_ref.setAttribute(DEST, SW_BASE_TYPE) + sw_data_def_props_conditional.appendChild(base_type_ref) + + if self.compu_method is not None: + compu_method_ref = create_text_element(doc, COMPU_METHOD_REF, self.compu_method.package_path) + compu_method_ref.setAttribute(DEST, COMPU_METHOD) + sw_data_def_props_conditional.appendChild(compu_method_ref) + + sw_data_def_props_variants.appendChild(sw_data_def_props_conditional) + sw_data_def_props.appendChild(sw_data_def_props_variants) + element.appendChild(sw_data_def_props) + + return element + + +@dataclass +class ImplementationStructureDataType(ImplementationDataType): + """结构体类型""" + category: Category = Category.STRUCTURE + structure_elements: List['ImplementationStructureDataType.StructureElement'] = field(default_factory=list) + + @dataclass + class StructureElement(Base): + """结构体成员""" + data_type: Optional[ImplementationDataType] = None + category: Category = Category.TYPE_REFERENCE + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(IMPLEMENTATION_DATA_TYPE_ELEMENT) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + element.appendChild(create_text_element(doc, CATEGORY, self.category.name)) + + sw_data_def_props = doc.createElement(SW_DATA_DEF_PROPS) + sw_data_def_props_variants = doc.createElement(SW_DATA_DEF_PROPS_VARIANTS) + sw_data_def_props_conditional = doc.createElement(SW_DATA_DEF_PROPS_CONDITIONAL) + + if self.data_type: + impl_type_ref = create_text_element( + doc, IMPLEMENTATION_DATA_TYPE_REF, self.data_type.package_path + ) + impl_type_ref.setAttribute(DEST, IMPLEMENTATION_DATA_TYPE) + sw_data_def_props_conditional.appendChild(impl_type_ref) + + sw_data_def_props_variants.appendChild(sw_data_def_props_conditional) + sw_data_def_props.appendChild(sw_data_def_props_variants) + element.appendChild(sw_data_def_props) + + return element + + def get_element(self, name: str) -> Optional['StructureElement']: + """根据名称查找成员""" + for element in self.structure_elements: + if element.name == name: + return element + return None + + def add_element(self, element: 'StructureElement') -> None: + """添加结构成员""" + if self.get_element(element.name) is not None: + return + self.structure_elements.append(element) + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(IMPLEMENTATION_DATA_TYPE) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + element.appendChild(create_text_element(doc, CATEGORY, self.category.name)) + + sub_elements = doc.createElement(SUB_ELEMENTS) + for structure_element in self.structure_elements: + sub_elements.appendChild(structure_element.to_arxml(doc)) + element.appendChild(sub_elements) + + return element + + +@dataclass +class ImplementationArrayDataType(ImplementationDataType): + """数组类型""" + category: Category = Category.ARRAY + + @dataclass + class ArrayElement(Base): + """数组成员""" + length: Optional[int] = None + data_type: Optional[ImplementationDataType] = None + category: Category = Category.TYPE_REFERENCE + array_size_semantics: Semantic = Semantic.FIXED + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(IMPLEMENTATION_DATA_TYPE_ELEMENT) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + element.appendChild(create_text_element(doc, CATEGORY, self.category.name)) + element.appendChild(create_text_element(doc, ARRAY_SIZE, str(self.length or 0))) + element.appendChild(create_text_element(doc, ARRAY_SIZE_SEMANTICS, self.array_size_semantics.value)) + + sw_data_def_props = doc.createElement(SW_DATA_DEF_PROPS) + sw_data_def_props_variants = doc.createElement(SW_DATA_DEF_PROPS_VARIANTS) + sw_data_def_props_conditional = doc.createElement(SW_DATA_DEF_PROPS_CONDITIONAL) + + if self.data_type: + impl_type_ref = create_text_element( + doc, IMPLEMENTATION_DATA_TYPE_REF, self.data_type.package_path + ) + impl_type_ref.setAttribute(DEST, IMPLEMENTATION_DATA_TYPE) + sw_data_def_props_conditional.appendChild(impl_type_ref) + + sw_data_def_props_variants.appendChild(sw_data_def_props_conditional) + sw_data_def_props.appendChild(sw_data_def_props_variants) + element.appendChild(sw_data_def_props) + + return element + + element: Optional['ArrayElement'] = None + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(IMPLEMENTATION_DATA_TYPE) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + element.appendChild(create_text_element(doc, CATEGORY, self.category.name)) + + sub_elements = doc.createElement(SUB_ELEMENTS) + if self.element: + sub_elements.appendChild(self.element.to_arxml(doc)) + element.appendChild(sub_elements) + + return element + + +def create_impl_value_type( + name: str, + base_type: Optional['BaseType'] = None, + compu_method: Optional['CompuMethod'] = None, +) -> ImplementationValueDataType: + """创建实现值类型的工厂函数""" + return ImplementationValueDataType( + name=name, + base_type=base_type, + compu_method=compu_method, + ) + + +def create_impl_structure_type( + name: str, + elements: Optional[List['ImplementationStructureDataType.StructureElement']] = None, +) -> ImplementationStructureDataType: + """创建实现结构体类型的工厂函数""" + return ImplementationStructureDataType( + name=name, + structure_elements=elements or [], + ) + + +def create_impl_array_type( + name: str, + element: Optional['ImplementationArrayDataType.ArrayElement'] = None, +) -> ImplementationArrayDataType: + """创建实现数组类型的工厂函数""" + return ImplementationArrayDataType( + name=name, + element=element, + ) \ No newline at end of file diff --git a/src/types/interface.py b/src/types/interface.py new file mode 100644 index 0000000..a0e768a --- /dev/null +++ b/src/types/interface.py @@ -0,0 +1,196 @@ +""" +接口定义模块 +""" +from dataclasses import dataclass, field +from typing import Optional, List, TYPE_CHECKING, Union +from xml.dom.minidom import Document + +from ..core.base import Base, create_uuid, create_text_element +from ..core.constants import ( + SENDER_RECEIVER_INTERFACE, SHORT_NAME, IS_SERVICE, DATA_ELEMENTS, + VARIABLE_DATA_PROTOTYPE, CATEGORY, SW_DATA_DEF_PROPS, SW_DATA_DEF_PROPS_VARIANTS, + SW_DATA_DEF_PROPS_CONDITIONAL, SW_CALIBRATION_ACCESS, SW_IMPL_POLICY, + TYPE_TREF, DEST, APPLICATION_PRIMITIVE_DATA_TYPE, APPLICATION_RECORD_DATA_TYPE, + APPLICATION_ARRAY_DATA_TYPE, CLIENT_SERVER_INTERFACE, CLIENT_SERVER_OPERATION, + ARGUMENT, APPLICATION_PRIMITIVE_TYPE_REF, DIRECTION +) +from ..core.enums import Policy, CalibrationAccess + +if TYPE_CHECKING: + from .base_type import BaseType + from .compu_method import CompuMethod + from .data_constraint import DataConstraint + from .sw_addr_method import SwAddrMethod + from .application_types import ApplicationDataType + from .implementation_types import ImplementationDataType + + +TYPE_DEST_MAPPING = { + "ApplicationStructureDataType": APPLICATION_RECORD_DATA_TYPE, + "ApplicationArrayDataType": APPLICATION_ARRAY_DATA_TYPE, +} + + +@dataclass +class Interface(Base): + """接口基类""" + is_service: bool = False + + +@dataclass +class SenderReceiverInterface(Interface): + """发送-接收接口""" + + @dataclass + class DataElement(Base): + """数据元素""" + sw_addr_method: Optional['SwAddrMethod'] = None + data_constraint: Optional['DataConstraint'] = None + data_type: Optional[Union['ApplicationDataType', 'ImplementationDataType']] = None + policy: Policy = Policy.STANDARD + calibration_access: CalibrationAccess = CalibrationAccess.READ_ONLY + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(VARIABLE_DATA_PROTOTYPE) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + + if self.data_type: + element.appendChild(create_text_element(doc, CATEGORY, self.data_type.category.name)) + + sw_data_def_props = doc.createElement(SW_DATA_DEF_PROPS) + sw_data_def_props_variants = doc.createElement(SW_DATA_DEF_PROPS_VARIANTS) + sw_data_def_props_conditional = doc.createElement(SW_DATA_DEF_PROPS_CONDITIONAL) + sw_data_def_props_conditional.appendChild( + create_text_element(doc, SW_CALIBRATION_ACCESS, self.calibration_access.value.upper()) + ) + sw_data_def_props_conditional.appendChild( + create_text_element(doc, SW_IMPL_POLICY, self.policy.name.upper()) + ) + sw_data_def_props_variants.appendChild(sw_data_def_props_conditional) + sw_data_def_props.appendChild(sw_data_def_props_variants) + element.appendChild(sw_data_def_props) + + type_dest = TYPE_DEST_MAPPING.get(type(self.data_type).__name__, APPLICATION_PRIMITIVE_DATA_TYPE) + type_tref = create_text_element(doc, TYPE_TREF, self.data_type.package_path) + type_tref.setAttribute(DEST, type_dest) + element.appendChild(type_tref) + + return element + + data_element: Optional[DataElement] = None + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(SENDER_RECEIVER_INTERFACE) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + element.appendChild(create_text_element(doc, IS_SERVICE, str(self.is_service).lower())) + + if self.data_element: + data_elements = doc.createElement(DATA_ELEMENTS) + data_elements.appendChild(self.data_element.to_arxml(doc)) + element.appendChild(data_elements) + + return element + + +@dataclass +class ClientServerInterface(Interface): + """客户端-服务端接口""" + + @dataclass + class Operation(Base): + """操作方法定义""" + is_server: bool = True + arguments: List['ClientServerInterface.Argument'] = field(default_factory=list) + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(CLIENT_SERVER_OPERATION) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + element.appendChild(create_text_element(doc, DIRECTION, "SERVER" if self.is_server else "CLIENT")) + return element + + @dataclass + class Argument(Base): + """操作参数定义""" + direction: str = "IN" + data_type: Optional['ApplicationDataType'] = None + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(ARGUMENT) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + element.appendChild(create_text_element(doc, DIRECTION, self.direction)) + if self.data_type: + type_ref = create_text_element(doc, APPLICATION_PRIMITIVE_TYPE_REF, self.data_type.package_path) + type_ref.setAttribute(DEST, APPLICATION_PRIMITIVE_DATA_TYPE) + element.appendChild(type_ref) + return element + + operations: List[Operation] = field(default_factory=list) + + def get_operation(self, name: str) -> Optional['Operation']: + """根据名称查找操作""" + for op in self.operations: + if op.name == name: + return op + return None + + def add_operation(self, operation: 'Operation') -> None: + """添加操作""" + if self.get_operation(operation.name) is None: + self.operations.append(operation) + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(CLIENT_SERVER_INTERFACE) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + element.appendChild(create_text_element(doc, IS_SERVICE, str(self.is_service).lower())) + + for op in self.operations: + element.appendChild(op.to_arxml(doc)) + + return element + + +@dataclass +class ModeSwitchInterface(Interface): + """模式切换接口""" + pass + + +@dataclass +class ParameterInterface(Interface): + """参数接口""" + pass + + +@dataclass +class TriggerInterface(Interface): + """触发接口""" + pass + + +@dataclass +class NvDataInterface(Interface): + """NvData接口""" + pass + + +def create_sender_receiver_interface( + name: str, + data_element: Optional[SenderReceiverInterface.DataElement] = None, + is_service: bool = False, +) -> SenderReceiverInterface: + """创建发送-接收接口的工厂函数""" + return SenderReceiverInterface( + name=name, + data_element=data_element, + is_service=is_service, + ) \ No newline at end of file diff --git a/src/types/package.py b/src/types/package.py new file mode 100644 index 0000000..13060b5 --- /dev/null +++ b/src/types/package.py @@ -0,0 +1,59 @@ +""" +Package 模块 +""" +from dataclasses import dataclass, field +from typing import List, Optional, TYPE_CHECKING +from xml.dom.minidom import Document + +from ..core.base import Base, create_uuid, create_text_element +from ..core.constants import AR_PACKAGE, SHORT_NAME, ELEMENTS + +if TYPE_CHECKING: + from .base_type import BaseType + from .application_types import ApplicationDataType + from .implementation_types import ImplementationDataType + from .compu_method import CompuMethod + from .data_constraint import DataConstraint + from .data_mapping import DataTypeMappingSet + + +@dataclass +class Package(Base): + """AUTOSAR 包""" + elements: List[Base] = field(default_factory=list) + + def get_element(self, name: str) -> Optional[Base]: + """根据名称查找元素""" + for element in self.elements: + if element.name == name: + return element + return None + + def add_element(self, element: Base) -> None: + """添加元素""" + existing = self.get_element(element.name) + if existing is None: + self.elements.append(element) + else: + element.id = existing.id + element.description = existing.description + self.elements.remove(existing) + self.elements.append(element) + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(AR_PACKAGE) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + + elements = doc.createElement(ELEMENTS) + for elem in self.elements: + elements.appendChild(elem.to_arxml(doc)) + element.appendChild(elements) + + return element + + +def create_package(name: str, elements: Optional[List[Base]] = None) -> Package: + """创建包的工厂函数""" + return Package(name=name, elements=elements or []) \ No newline at end of file diff --git a/src/types/sw_addr_method.py b/src/types/sw_addr_method.py new file mode 100644 index 0000000..e3772a8 --- /dev/null +++ b/src/types/sw_addr_method.py @@ -0,0 +1,30 @@ +""" +软件地址方法模块 +""" +from dataclasses import dataclass, field +from typing import Optional +from xml.dom.minidom import Document + +from ..core.base import Base, create_uuid, create_text_element +from ..core.constants import SW_ADDR_METHOD, SHORT_NAME, SECTION_TYPE +from ..core.enums import SectionType + + +@dataclass +class SwAddrMethod(Base): + """软件地址方法""" + section_type: Optional[SectionType] = None + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(SW_ADDR_METHOD) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + if self.section_type: + element.appendChild(create_text_element(doc, SECTION_TYPE, self.section_type.name)) + return element + + +def create_sw_addr_method(name: str, section_type: Optional[SectionType] = None) -> SwAddrMethod: + """创建软件地址方法的工厂函数""" + return SwAddrMethod(name=name, section_type=section_type) \ No newline at end of file diff --git a/src/types/sw_component_type.py b/src/types/sw_component_type.py new file mode 100644 index 0000000..5910d50 --- /dev/null +++ b/src/types/sw_component_type.py @@ -0,0 +1,287 @@ +""" +软件组件类型模块 +""" +from dataclasses import dataclass, field +from typing import Optional, List, TYPE_CHECKING +from xml.dom.minidom import Document + +from ..core.base import Base, create_uuid, create_text_element +from ..core.constants import ( + APPLICATION_SW_COMPONENT_TYPE, SHORT_NAME, PORTS, R_PORT_PROTOTYPE, P_PORT_PROTOTYPE, + REQUIRED_COM_SPECS, PROVIDED_COM_SPECS, PROVIDED_INTERFACE_TREF, REQUIRED_INTERFACE_TREF, + NONQUEUED_RECEIVER_COM_SPEC, NONQUEUED_SENDER_COM_SPEC, DATA_ELEMENT_REF, VARIABLE_DATA_PROTOTYPE, + DEST, SENDER_RECEIVER_INTERFACE, HANDLE_OUT_OF_RANGE, USES_END_TO_END_PROTECTION, + ALIVE_TIMEOUT, ENABLE_UPDATE, HANDLE_NEVER_RECEIVED, HANDLE_TIMEOUT_TYPE, SW_DATA_DEF_PROPS, + SW_DATA_DEF_PROPS_VARIANTS, SW_DATA_DEF_PROPS_CONDITIONAL, SW_CALIBRATION_ACCESS, SW_IMPL_POLICY, + TYPE_TREF, INTERNAL_BEHAVIORS, SWC_INTERNAL_BEHAVIOR, RUNNABLES, RUNNABLE_ENTITY, + MINIMUM_START_INTERVAL, CAN_BE_INVOKED_CONCURRENTLY, DATA_RECEIVE_POINT_BY_ARGUMENTS, + VARIABLE_ACCESS, ACCESSED_VARIABLE, AUTOSAR_VARIABLE_IREF, PORT_PROTOTYPE_REF, + TARGET_DATA_PROTOTYPE_REF, DATA_SEND_POINTS, EVENTS, INIT_EVENT, TIMING_EVENT, + OPERATION_INVOKED_EVENT, DATA_RECEIVED_EVENT, START_ON_EVENT_REF, PERIOD, CODE, + SW_ADDR_METHOD_REF, CLIENT_SERVER_INTERFACE, +) +from ..core.enums import ComponentType, AtomicType, ImplementationCodeType, CalibrationAccess, Policy + +if TYPE_CHECKING: + from .interface import SenderReceiverInterface, ClientServerInterface + from .data_mapping import SwAddrMethod + from .swc_internal_behavior import SwcInternalBehavior + + +@dataclass +class SwComponentType(Base): + """软件组件类型基类""" + component_type: ComponentType = ComponentType.ATOMIC + + +@dataclass +class AtomicComponentType(SwComponentType): + """原子组件类型""" + supports_multiple_instantiation: bool = True + atomic_type: AtomicType = AtomicType.APPLICATION + implementation_code_type: ImplementationCodeType = ImplementationCodeType.SOURCE_CODE + + +@dataclass +class CompositionSwComponentType(SwComponentType): + """组合组件类型""" + component_type: ComponentType = ComponentType.COMPOSITION + + +@dataclass +class ParameterSwComponentType(SwComponentType): + """参数组件类型""" + pass + + +@dataclass +class ApplicationSwComponentType(AtomicComponentType): + """应用软件组件类型""" + atomic_type: AtomicType = AtomicType.APPLICATION + ports: List['PortPrototype'] = field(default_factory=list) + internal_behavior: Optional['SwcInternalBehavior'] = None + + def get_port(self, name: str) -> Optional['PortPrototype']: + """根据名称查找端口""" + for port in self.ports: + if port.name == name: + return port + return None + + def add_port(self, port: 'PortPrototype') -> None: + """添加端口""" + if self.get_port(port.name) is None: + self.ports.append(port) + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(APPLICATION_SW_COMPONENT_TYPE) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + + if self.ports: + ports_elem = doc.createElement(PORTS) + for port in self.ports: + ports_elem.appendChild(port.to_arxml(doc)) + element.appendChild(ports_elem) + + if self.internal_behavior: + internal_behaviors = doc.createElement(INTERNAL_BEHAVIORS) + internal_behaviors.appendChild(self.internal_behavior.to_arxml(doc)) + element.appendChild(internal_behaviors) + + return element + + +@dataclass +class ServiceSwComponentType(AtomicComponentType): + """服务组件类型""" + pass + + +@dataclass +class SensorActuatorSwComponentType(AtomicComponentType): + """传感器/执行器组件类型""" + atomic_type: AtomicType = AtomicType.SENSOR_ACTUATOR + + +@dataclass +class EcuAbstractionSwComponentType(AtomicComponentType): + """ECU抽象组件类型""" + atomic_type: AtomicType = AtomicType.HARDWARE_ABSTRACTION + + +@dataclass +class ComplexDeviceDriverSwComponentType(AtomicComponentType): + """复杂驱动组件类型""" + atomic_type: AtomicType = AtomicType.COMPLEX_DRIVER + + +@dataclass +class ServiceProxySwComponentType(AtomicComponentType): + """服务代理组件类型""" + atomic_type: AtomicType = AtomicType.SERVICE_PROXY + + +@dataclass +class PortPrototype(Base): + """端口原型基类""" + interface: Optional['SenderReceiverInterface'] = None + + +@dataclass +class RPortPrototype(PortPrototype): + """接收端口(R-Port)""" + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(R_PORT_PROTOTYPE) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + + if self.interface: + required_com_specs = doc.createElement(REQUIRED_COM_SPECS) + nonqueued_receiver_com_spec = doc.createElement(NONQUEUED_RECEIVER_COM_SPEC) + + if hasattr(self.interface, 'data_element') and self.interface.data_element: + data_element_ref = create_text_element( + doc, DATA_ELEMENT_REF, self.interface.data_element.package_path + ) + data_element_ref.setAttribute(DEST, VARIABLE_DATA_PROTOTYPE) + nonqueued_receiver_com_spec.appendChild(data_element_ref) + + nonqueued_receiver_com_spec.appendChild( + create_text_element(doc, HANDLE_OUT_OF_RANGE, 'FALSE') + ) + nonqueued_receiver_com_spec.appendChild( + create_text_element(doc, USES_END_TO_END_PROTECTION, 'FALSE') + ) + nonqueued_receiver_com_spec.appendChild( + create_text_element(doc, ALIVE_TIMEOUT, '0') + ) + nonqueued_receiver_com_spec.appendChild( + create_text_element(doc, ENABLE_UPDATE, 'FALSE') + ) + nonqueued_receiver_com_spec.appendChild( + create_text_element(doc, HANDLE_NEVER_RECEIVED, 'FALSE') + ) + nonqueued_receiver_com_spec.appendChild( + create_text_element(doc, HANDLE_TIMEOUT_TYPE, 'NONE') + ) + + required_com_specs.appendChild(nonqueued_receiver_com_spec) + element.appendChild(required_com_specs) + + required_interface_tref = create_text_element( + doc, REQUIRED_INTERFACE_TREF, self.interface.package_path + ) + required_interface_tref.setAttribute(DEST, SENDER_RECEIVER_INTERFACE) + element.appendChild(required_interface_tref) + + return element + + +@dataclass +class PPortPrototype(PortPrototype): + """发送端口(P-Port)""" + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(P_PORT_PROTOTYPE) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + + if self.interface: + provided_com_specs = doc.createElement(PROVIDED_COM_SPECS) + nonqueued_sender_com_spec = doc.createElement(NONQUEUED_SENDER_COM_SPEC) + + if hasattr(self.interface, 'data_element') and self.interface.data_element: + data_element_ref = create_text_element( + doc, DATA_ELEMENT_REF, self.interface.data_element.package_path + ) + data_element_ref.setAttribute(DEST, VARIABLE_DATA_PROTOTYPE) + nonqueued_sender_com_spec.appendChild(data_element_ref) + + provided_com_specs.appendChild(nonqueued_sender_com_spec) + element.appendChild(provided_com_specs) + + provided_interface_tref = create_text_element( + doc, PROVIDED_INTERFACE_TREF, self.interface.package_path + ) + provided_interface_tref.setAttribute(DEST, SENDER_RECEIVER_INTERFACE) + element.appendChild(provided_interface_tref) + + return element + + +@dataclass +class PRPortPrototype(PortPrototype): + """双向端口(PR-Port)""" + required_interface: Optional['SenderReceiverInterface'] = None + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(P_PORT_PROTOTYPE) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + + if self.interface: + provided_com_specs = doc.createElement(PROVIDED_COM_SPECS) + nonqueued_sender_com_spec = doc.createElement(NONQUEUED_SENDER_COM_SPEC) + + if hasattr(self.interface, 'data_element') and self.interface.data_element: + data_element_ref = create_text_element( + doc, DATA_ELEMENT_REF, self.interface.data_element.package_path + ) + data_element_ref.setAttribute(DEST, VARIABLE_DATA_PROTOTYPE) + nonqueued_sender_com_spec.appendChild(data_element_ref) + + provided_com_specs.appendChild(nonqueued_sender_com_spec) + element.appendChild(provided_com_specs) + + provided_interface_tref = create_text_element( + doc, PROVIDED_INTERFACE_TREF, self.interface.package_path + ) + provided_interface_tref.setAttribute(DEST, SENDER_RECEIVER_INTERFACE) + element.appendChild(provided_interface_tref) + + if self.required_interface: + required_com_specs = doc.createElement(REQUIRED_COM_SPECS) + nonqueued_receiver_com_spec = doc.createElement(NONQUEUED_RECEIVER_COM_SPEC) + + if hasattr(self.required_interface, 'data_element') and self.required_interface.data_element: + data_element_ref = create_text_element( + doc, DATA_ELEMENT_REF, self.required_interface.data_element.package_path + ) + data_element_ref.setAttribute(DEST, VARIABLE_DATA_PROTOTYPE) + nonqueued_receiver_com_spec.appendChild(data_element_ref) + + nonqueued_receiver_com_spec.appendChild( + create_text_element(doc, HANDLE_OUT_OF_RANGE, 'FALSE') + ) + nonqueued_receiver_com_spec.appendChild( + create_text_element(doc, USES_END_TO_END_PROTECTION, 'FALSE') + ) + nonqueued_receiver_com_spec.appendChild( + create_text_element(doc, ALIVE_TIMEOUT, '0') + ) + nonqueued_receiver_com_spec.appendChild( + create_text_element(doc, ENABLE_UPDATE, 'FALSE') + ) + nonqueued_receiver_com_spec.appendChild( + create_text_element(doc, HANDLE_NEVER_RECEIVED, 'FALSE') + ) + nonqueued_receiver_com_spec.appendChild( + create_text_element(doc, HANDLE_TIMEOUT_TYPE, 'NONE') + ) + + required_com_specs.appendChild(nonqueued_receiver_com_spec) + element.appendChild(required_com_specs) + + required_interface_tref = create_text_element( + doc, REQUIRED_INTERFACE_TREF, self.required_interface.package_path + ) + required_interface_tref.setAttribute(DEST, SENDER_RECEIVER_INTERFACE) + element.appendChild(required_interface_tref) + + return element \ No newline at end of file diff --git a/src/types/swc_internal_behavior.py b/src/types/swc_internal_behavior.py new file mode 100644 index 0000000..14cd673 --- /dev/null +++ b/src/types/swc_internal_behavior.py @@ -0,0 +1,276 @@ +""" +SWC 内部行为模块 +""" +from dataclasses import dataclass, field +from typing import Optional, List, Union, TYPE_CHECKING +from xml.dom.minidom import Document + +from ..core.base import Base, create_uuid, create_text_element +from ..core.constants import ( + RUNNABLE_ENTITY, SHORT_NAME, MINIMUM_START_INTERVAL, CAN_BE_INVOKED_CONCURRENTLY, + DATA_RECEIVE_POINT_BY_ARGUMENTS, VARIABLE_ACCESS, ACCESSED_VARIABLE, AUTOSAR_VARIABLE_IREF, + PORT_PROTOTYPE_REF, TARGET_DATA_PROTOTYPE_REF, DATA_SEND_POINTS, SYMBOL, + SW_ADDR_METHOD_REF, DATA_ELEMENT_REF, RUNNABLES, EVENTS, INIT_EVENT, TIMING_EVENT, + OPERATION_INVOKED_EVENT, DATA_RECEIVED_EVENT, START_ON_EVENT_REF, PERIOD, + CLIENT_SERVER_OPERATION_REF, DEST, VARIABLE_DATA_PROTOTYPE, SWC_INTERNAL_BEHAVIOR +) + +if TYPE_CHECKING: + from .interface import ClientServerInterface + from .sw_addr_method import SwAddrMethod + + +@dataclass +class VariableAccess(Base): + """变量访问""" + port_prototype_ref: Optional[str] = None + target_data_prototype_ref: Optional[str] = None + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(ACCESSED_VARIABLE) + autosar_var_iref = doc.createElement(AUTOSAR_VARIABLE_IREF) + + if self.port_prototype_ref: + port_ref = create_text_element(doc, PORT_PROTOTYPE_REF, self.port_prototype_ref) + port_ref.setAttribute(DEST, "PORT-PROTOTYPE") + autosar_var_iref.appendChild(port_ref) + + if self.target_data_prototype_ref: + target_ref = create_text_element(doc, TARGET_DATA_PROTOTYPE_REF, self.target_data_prototype_ref) + target_ref.setAttribute(DEST, VARIABLE_DATA_PROTOTYPE) + autosar_var_iref.appendChild(target_ref) + + element.appendChild(autosar_var_iref) + return element + + +@dataclass +class RunnableEntity(Base): + """Runnable 实体""" + minimum_start_interval: int = 0 + can_be_invoked_concurrently: bool = False + data_receive_points: List[VariableAccess] = field(default_factory=list) + data_send_points: List[VariableAccess] = field(default_factory=list) + variable_accesses: List[VariableAccess] = field(default_factory=list) + symbol: Optional[str] = None + sw_addr_method_ref: Optional[str] = None + + def add_data_receive_point(self, access: VariableAccess) -> None: + """添加数据接收点""" + self.data_receive_points.append(access) + + def add_data_send_point(self, access: VariableAccess) -> None: + """添加数据发送点""" + self.data_send_points.append(access) + + def add_variable_access(self, access: VariableAccess) -> None: + """添加变量访问""" + self.variable_accesses.append(access) + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(RUNNABLE_ENTITY) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + element.appendChild(create_text_element(doc, MINIMUM_START_INTERVAL, str(self.minimum_start_interval))) + element.appendChild(create_text_element(doc, CAN_BE_INVOKED_CONCURRENTLY, str(self.can_be_invoked_concurrently).lower())) + + if self.symbol: + element.appendChild(create_text_element(doc, SYMBOL, self.symbol)) + + if self.sw_addr_method_ref: + method_ref = create_text_element(doc, SW_ADDR_METHOD_REF, self.sw_addr_method_ref) + method_ref.setAttribute(DEST, "SW-ADDR-METHOD") + element.appendChild(method_ref) + + if self.data_receive_points: + data_receive_elem = doc.createElement(DATA_RECEIVE_POINT_BY_ARGUMENTS) + for access in self.data_receive_points: + data_receive_elem.appendChild(access.to_arxml(doc)) + element.appendChild(data_receive_elem) + + if self.data_send_points: + data_send_elem = doc.createElement(DATA_SEND_POINTS) + for access in self.data_send_points: + data_send_elem.appendChild(access.to_arxml(doc)) + element.appendChild(data_send_elem) + + if self.variable_accesses: + var_access_elem = doc.createElement(VARIABLE_ACCESS) + for access in self.variable_accesses: + var_access_elem.appendChild(access.to_arxml(doc)) + element.appendChild(var_access_elem) + + return element + + +@dataclass +class InitEvent(Base): + """初始化事件""" + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(INIT_EVENT) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + return element + + +@dataclass +class TimingEvent(Base): + """定时事件""" + period: float = 0.01 + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(TIMING_EVENT) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + element.appendChild(create_text_element(doc, PERIOD, str(self.period))) + return element + + +@dataclass +class OperationInvokedEvent(Base): + """操作调用事件""" + operation_ref: Optional[str] = None + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(OPERATION_INVOKED_EVENT) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + if self.operation_ref: + op_ref = create_text_element(doc, START_ON_EVENT_REF, self.operation_ref) + op_ref.setAttribute(DEST, CLIENT_SERVER_OPERATION_REF) + element.appendChild(op_ref) + return element + + +@dataclass +class DataReceivedEvent(Base): + """数据接收事件""" + data_element_ref: Optional[str] = None + period: float = 0.01 + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(DATA_RECEIVED_EVENT) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + if self.data_element_ref: + data_ref = create_text_element(doc, START_ON_EVENT_REF, self.data_element_ref) + data_ref.setAttribute(DEST, DATA_ELEMENT_REF) + element.appendChild(data_ref) + element.appendChild(create_text_element(doc, PERIOD, str(self.period))) + return element + + +EventType = Union[InitEvent, TimingEvent, OperationInvokedEvent, DataReceivedEvent] + + +@dataclass +class SwcInternalBehavior(Base): + """SWC 内部行为""" + runnable_entities: List[RunnableEntity] = field(default_factory=list) + events: List[EventType] = field(default_factory=list) + symbol: Optional[str] = None + + def get_runnable(self, name: str) -> Optional[RunnableEntity]: + """根据名称查找 Runnable""" + for runnable in self.runnable_entities: + if runnable.name == name: + return runnable + return None + + def add_runnable(self, runnable: RunnableEntity) -> None: + """添加 Runnable""" + if self.get_runnable(runnable.name) is None: + self.runnable_entities.append(runnable) + + def get_event(self, name: str) -> Optional[EventType]: + """根据名称查找事件""" + for event in self.events: + if event.name == name: + return event + return None + + def add_event(self, event: EventType) -> None: + """添加事件""" + if self.get_event(event.name) is None: + self.events.append(event) + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(SWC_INTERNAL_BEHAVIOR) + element.setAttribute("UUID", create_uuid()) + + if self.symbol: + element.appendChild(create_text_element(doc, SYMBOL, self.symbol)) + + if self.runnable_entities: + runnables = doc.createElement(RUNNABLES) + for runnable in self.runnable_entities: + runnables.appendChild(runnable.to_arxml(doc)) + element.appendChild(runnables) + + if self.events: + events_elem = doc.createElement(EVENTS) + for event in self.events: + events_elem.appendChild(event.to_arxml(doc)) + element.appendChild(events_elem) + + return element + + +def create_runnable_entity( + name: str, + minimum_start_interval: int = 0, + can_be_invoked_concurrently: bool = False, + symbol: Optional[str] = None, + sw_addr_method_ref: Optional[str] = None, +) -> RunnableEntity: + """创建 Runnable 实体的工厂函数""" + return RunnableEntity( + name=name, + minimum_start_interval=minimum_start_interval, + can_be_invoked_concurrently=can_be_invoked_concurrently, + symbol=symbol, + sw_addr_method_ref=sw_addr_method_ref, + ) + + +def create_timing_event(name: str, period: float = 0.01) -> TimingEvent: + """创建定时事件的工厂函数""" + return TimingEvent(name=name, period=period) + + +def create_init_event(name: str) -> InitEvent: + """创建初始化事件的工厂函数""" + return InitEvent(name=name) + + +def create_operation_invoked_event(name: str, operation_ref: Optional[str] = None) -> OperationInvokedEvent: + """创建操作调用事件的工厂函数""" + return OperationInvokedEvent(name=name, operation_ref=operation_ref) + + +def create_data_received_event(name: str, data_element_ref: Optional[str] = None, period: float = 0.01) -> DataReceivedEvent: + """创建数据接收事件的工厂函数""" + return DataReceivedEvent(name=name, data_element_ref=data_element_ref, period=period) + + +def create_swc_internal_behavior( + name: str, + runnable_entities: Optional[List[RunnableEntity]] = None, + events: Optional[List[EventType]] = None, + symbol: Optional[str] = None, +) -> SwcInternalBehavior: + """创建 SWC 内部行为的工厂函数""" + return SwcInternalBehavior( + name=name, + runnable_entities=runnable_entities or [], + events=events or [], + symbol=symbol, + ) \ No newline at end of file diff --git a/src/types/unit.py b/src/types/unit.py new file mode 100644 index 0000000..692a5ba --- /dev/null +++ b/src/types/unit.py @@ -0,0 +1,43 @@ +""" +单位数据类型模块 +""" +from dataclasses import dataclass, field +from typing import Optional +from xml.dom.minidom import Document + +from ..core.base import Base, create_uuid, create_text_element +from ..core.constants import UNIT, SHORT_NAME, FACTOR_SI_TO_UNIT, OFFSET_SI_TO_UNIT + + +@dataclass +class Unit(Base): + """数据单位""" + display: Optional[str] = '' + factor: int = 1 + offset: int = 0 + + def to_arxml(self, doc: Document) -> Document: + """转换为 ARXML 元素""" + element = doc.createElement(UNIT) + element.setAttribute("UUID", create_uuid()) + element.appendChild(create_text_element(doc, SHORT_NAME, self.name)) + element.appendChild(create_text_element(doc, FACTOR_SI_TO_UNIT, str(self.factor))) + element.appendChild(create_text_element(doc, OFFSET_SI_TO_UNIT, str(self.offset))) + if self.display: + element.appendChild(create_text_element(doc, "DISPLAY", self.display)) + return element + + +def create_unit( + name: str, + display: Optional[str] = None, + factor: int = 1, + offset: int = 0, +) -> Unit: + """创建单位的工厂函数""" + return Unit( + name=name, + display=display or '', + factor=factor, + offset=offset, + ) \ No newline at end of file diff --git a/src/utils/__init__.py b/src/utils/__init__.py new file mode 100644 index 0000000..63dce84 --- /dev/null +++ b/src/utils/__init__.py @@ -0,0 +1,18 @@ +""" +utils 模块 - ARXML 工具函数 +""" +from .arxml_writer import ( + create_arxml_document, + write_arxml, + write_arxml_pretty, + get_ar_packages_element, + add_package_to_document, +) + +__all__ = [ + "create_arxml_document", + "write_arxml", + "write_arxml_pretty", + "get_ar_packages_element", + "add_package_to_document", +] \ No newline at end of file diff --git a/src/utils/arxml_writer.py b/src/utils/arxml_writer.py new file mode 100644 index 0000000..cd14fce --- /dev/null +++ b/src/utils/arxml_writer.py @@ -0,0 +1,62 @@ +""" +ARXML 文件写入工具模块 +""" +from xml.dom.minidom import Document +from xml.dom.minidom import Element +from typing import Optional + +from ..core.constants import AUTOSAR, AR_PACKAGES + + +def create_arxml_document() -> Document: + """创建 ARXML 文档""" + doc = Document() + autosar = doc.createElement(AUTOSAR) + autosar.setAttribute("xmlns", "http://autosar.org/schema/r4.4") + autosar.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance") + autosar.setAttribute("xsi:schemaLocation", "http://autosar.org/schema/r4.4 AUTOSAR_4-4.xsd") + ar_packages = doc.createElement(AR_PACKAGES) + autosar.appendChild(ar_packages) + doc.appendChild(autosar) + return doc + + +def write_arxml(doc: Document, filepath: str, encoding: str = 'utf-8') -> None: + """将 ARXML Document 写入文件 + + Args: + doc: ARXML Document 对象 + filepath: 输出文件路径 + encoding: 文件编码(默认 utf-8) + """ + with open(filepath, 'w', encoding=encoding) as f: + xml_string = doc.toprettyxml(indent=' ', encoding=encoding) + f.write(xml_string.decode(encoding)) + + +def write_arxml_pretty(doc: Document, filepath: str, encoding: str = 'utf-8') -> None: + """将 ARXML Document 写入文件(格式化输出) + + Args: + doc: ARXML Document 对象 + filepath: 输出文件路径 + encoding: 文件编码(默认 utf-8) + """ + with open(filepath, 'w', encoding=encoding) as f: + f.write(doc.toprettyxml(indent=' ')) + + +def get_ar_packages_element(doc: Document) -> Optional[Element]: + """获取 AR-PACKAGES 元素""" + if doc.documentElement and doc.documentElement.tagName == AUTOSAR: + packages = doc.documentElement.getElementsByTagName(AR_PACKAGES) + if packages: + return packages[0] + return None + + +def add_package_to_document(doc: Document, package_element: Element) -> None: + """添加 Package 元素到文档""" + packages = get_ar_packages_element(doc) + if packages is not None: + packages.appendChild(package_element) \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..721d53f --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +"""测试包初始化""" \ No newline at end of file diff --git a/tests/test_core.py b/tests/test_core.py new file mode 100644 index 0000000..e1b03f6 --- /dev/null +++ b/tests/test_core.py @@ -0,0 +1,82 @@ +""" +核心模块测试 +""" +import pytest +from src.core.base import Base, create_uuid, create_text_element +from src.core.enums import Category, Encoding, CalibrationAccess + + +class TestBase: + """Base 基类测试""" + + def test_base_creation(self): + """测试基本创建""" + base = Base(name="TestElement") + assert base.name == "TestElement" + assert base.id is None + assert base.parent is None + assert base.description is None + + def test_base_with_parent(self): + """测试带父元素的创建""" + parent = Base(name="Parent") + child = Base(name="Child", parent=parent) + assert child.parent == parent + assert child.parent_id == parent.id + + def test_package_path(self): + """测试包路径计算""" + parent = Base(name="Parent") + child = Base(name="Child", parent=parent) + assert child.package_path == "/Parent/Child" + + def test_package_path_deep(self): + """测试深层包路径""" + level1 = Base(name="Level1") + level2 = Base(name="Level2", parent=level1) + level3 = Base(name="Level3", parent=level2) + level4 = Base(name="Level4", parent=level3) + assert level4.package_path == "/Level1/Level2/Level3/Level4" + + +class TestUtilityFunctions: + """工具函数测试""" + + def test_create_uuid(self): + """测试 UUID 生成""" + uuid1 = create_uuid() + uuid2 = create_uuid() + assert uuid1 != uuid2 + assert len(uuid1) == 36 + + def test_create_text_element(self): + """测试文本元素创建""" + import xml.dom.minidom as Dom + + doc = Dom.Document() + element = create_text_element(doc, "TEST_TAG", "test_value") + + assert element.tagName == "TEST_TAG" + assert element.firstChild.nodeValue == "test_value" + + +class TestEnums: + """枚举类型测试""" + + def test_category_enum(self): + """测试 Category 枚举""" + assert Category.FIXED_LENGTH is not None + assert Category.VALUE is not None + assert Category.STRUCTURE is not None + assert Category.ARRAY is not None + + def test_calibration_access_enum(self): + """测试 CalibrationAccess 枚举""" + assert CalibrationAccess.READ_ONLY is not None + assert CalibrationAccess.READ_WRITE is not None + assert CalibrationAccess.NOT_ACCESSIBLE is not None + assert CalibrationAccess.NOT_SPECIFIED is not None + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) \ No newline at end of file diff --git a/tests/test_integration.py b/tests/test_integration.py new file mode 100644 index 0000000..ed6b176 --- /dev/null +++ b/tests/test_integration.py @@ -0,0 +1,136 @@ +""" +集成测试 - 完整 ARXML 文件生成 +""" +import pytest +from xml.dom.minidom import Document +from src import ( + create_base_type, + create_unit, + create_data_constraint, + create_compu_method, + create_linear, + create_boolean_type, + create_value_type, + create_structure_type, + create_impl_value_type, + create_sender_receiver_interface, + SenderReceiverInterface, + ApplicationStructureDataType, + Category, + CalibrationAccess, +) + + +class TestARXMLGeneration: + """ARXML 生成测试""" + + def test_full_type_chain(self): + """测试完整的类型链(应用类型 -> 实现类型 -> 基础类型)""" + base_type = create_base_type("UInt8", size=8) + assert base_type.name == "UInt8" + + impl_type = create_impl_value_type("ImplUInt8", base_type=base_type) + assert impl_type.name == "ImplUInt8" + assert impl_type.base_type == base_type + + app_type = create_value_type("AppUInt8") + assert app_type.name == "AppUInt8" + + doc = Document() + base_element = base_type.to_arxml(doc) + assert base_element.tagName == "SW-BASE-TYPE" + + impl_element = impl_type.to_arxml(doc) + assert impl_element.tagName == "IMPLEMENTATION-DATA-TYPE" + + app_element = app_type.to_arxml(doc) + assert app_element.tagName == "APPLICATION-PRIMITIVE-DATA-TYPE" + + def test_unit_with_compu_method(self): + """测试单位与计算方法关联""" + unit = create_unit("kmh", display="km/h") + + linear = create_linear(factor=0.01, offset=0) + compu = create_compu_method("SpeedConversion", unit=unit, linear=linear) + assert compu.unit == unit + assert compu.linear == linear + + def test_complex_structure(self): + """测试复杂结构体""" + member1 = ApplicationStructureDataType.StructureElement(name="temperature") + member2 = ApplicationStructureDataType.StructureElement(name="humidity") + + struct = create_structure_type("EnvironmentData", elements=[member1, member2]) + assert len(struct.structure_elements) == 2 + + doc = Document() + element = struct.to_arxml(doc) + assert element.tagName == "APPLICATION-RECORD-DATA-TYPE" + + def test_interface_creation(self): + """测试接口创建""" + data_element = SenderReceiverInterface.DataElement(name="SpeedData") + interface = create_sender_receiver_interface( + "SpeedInterface", + data_element=data_element, + is_service=False + ) + + assert interface.name == "SpeedInterface" + assert interface.data_element == data_element + + doc = Document() + element = interface.to_arxml(doc) + assert element.tagName == "SENDER-RECEIVER-INTERFACE" + + def test_data_constraint_chain(self): + """测试数据约束链""" + unit = create_unit("percent", display="%") + constraint = create_data_constraint( + "PercentageRange", + lower=0, + upper=100, + unit=unit + ) + + assert constraint.unit == unit + assert constraint.lower == 0 + assert constraint.upper == 100 + + doc = Document() + element = constraint.to_arxml(doc) + assert element.tagName == "DATA-CONSTR" + + def test_package_creation(self): + """测试包创建""" + from src import create_package, create_base_type + + base_type = create_base_type("TestType") + pkg = create_package("TestPackage", elements=[base_type]) + + assert pkg.name == "TestPackage" + assert len(pkg.elements) == 1 + assert pkg.get_element("TestType") == base_type + + def test_package_to_arxml(self): + """测试包 ARXML 转换""" + from src import create_package, create_base_type + + base_type = create_base_type("PackageType") + pkg = create_package("MyPackage", elements=[base_type]) + + doc = Document() + element = pkg.to_arxml(doc) + + assert element.tagName == "AR-PACKAGE" + assert element.hasAttribute("UUID") + + short_name = element.getElementsByTagName("SHORT-NAME")[0] + assert short_name.firstChild.nodeValue == "MyPackage" + + elements = element.getElementsByTagName("ELEMENTS") + assert len(elements) == 1 + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) \ No newline at end of file diff --git a/tests/test_swc.py b/tests/test_swc.py new file mode 100644 index 0000000..15bfbcf --- /dev/null +++ b/tests/test_swc.py @@ -0,0 +1,294 @@ +""" +SWC 组件和内部行为测试 +""" +import pytest +from xml.dom.minidom import Document + +from src import ( + ApplicationSwComponentType, + RPortPrototype, + PPortPrototype, + PRPortPrototype, + SenderReceiverInterface, + create_sender_receiver_interface, + create_runnable_entity, + create_timing_event, + create_init_event, + create_operation_invoked_event, + create_data_received_event, + create_swc_internal_behavior, + SwcInternalBehavior, + VariableAccess, +) + + +class TestRunnableEntity: + """RunnableEntity 测试""" + + def test_create_runnable_entity(self): + """测试创建 Runnable 实体""" + runnable = create_runnable_entity( + name="Runnable_Init", + minimum_start_interval=10, + can_be_invoked_concurrently=False, + symbol="Runnable_Init_Function" + ) + assert runnable.name == "Runnable_Init" + assert runnable.minimum_start_interval == 10 + assert runnable.can_be_invoked_concurrently is False + assert runnable.symbol == "Runnable_Init_Function" + + def test_runnable_entity_add_data_access(self): + """测试 Runnable 添加数据访问""" + runnable = create_runnable_entity(name="TestRunnable") + access = VariableAccess(name="DataAccess", port_prototype_ref="Port1") + runnable.add_variable_access(access) + + assert len(runnable.variable_accesses) == 1 + + def test_runnable_entity_to_arxml(self): + """测试 Runnable 实体 ARXML 转换""" + runnable = create_runnable_entity( + name="TestRunnable", + symbol="TestRunnable_Func" + ) + + doc = Document() + element = runnable.to_arxml(doc) + + assert element.tagName == "RUNNABLE-ENTITY" + assert element.hasAttribute("UUID") + + short_name = element.getElementsByTagName("SHORT-NAME")[0] + assert short_name.firstChild.nodeValue == "TestRunnable" + + +class TestEvents: + """事件测试""" + + def test_create_timing_event(self): + """测试创建定时事件""" + event = create_timing_event("TimingEvent_10ms", period=0.01) + assert event.name == "TimingEvent_10ms" + assert event.period == 0.01 + + def test_timing_event_to_arxml(self): + """测试定时事件 ARXML 转换""" + event = create_timing_event("PeriodicTask", period=0.05) + + doc = Document() + element = event.to_arxml(doc) + + assert element.tagName == "TIMING-EVENT" + assert element.hasAttribute("UUID") + + short_name = element.getElementsByTagName("SHORT-NAME")[0] + assert short_name.firstChild.nodeValue == "PeriodicTask" + + def test_create_init_event(self): + """测试创建初始化事件""" + event = create_init_event("InitEvent") + assert event.name == "InitEvent" + + def test_init_event_to_arxml(self): + """测试初始化事件 ARXML 转换""" + event = create_init_event("InitEvent") + + doc = Document() + element = event.to_arxml(doc) + + assert element.tagName == "INIT-EVENT" + assert element.hasAttribute("UUID") + + def test_create_operation_invoked_event(self): + """测试创建操作调用事件""" + event = create_operation_invoked_event( + "OpInvokeEvent", + operation_ref="/Package/Interface/Operation" + ) + assert event.name == "OpInvokeEvent" + assert event.operation_ref == "/Package/Interface/Operation" + + def test_operation_invoked_event_to_arxml(self): + """测试操作调用事件 ARXML 转换""" + event = create_operation_invoked_event("OpInvokeEvent") + + doc = Document() + element = event.to_arxml(doc) + + assert element.tagName == "OPERATION-INVOKED-EVENT" + assert element.hasAttribute("UUID") + + def test_create_data_received_event(self): + """测试创建数据接收事件""" + event = create_data_received_event( + "DataReceived", + data_element_ref="/Package/Interface/Port", + period=0.02 + ) + assert event.name == "DataReceived" + assert event.data_element_ref == "/Package/Interface/Port" + assert event.period == 0.02 + + def test_data_received_event_to_arxml(self): + """测试数据接收事件 ARXML 转换""" + event = create_data_received_event("DataReceived", period=0.01) + + doc = Document() + element = event.to_arxml(doc) + + assert element.tagName == "DATA-RECEIVED-EVENT" + assert element.hasAttribute("UUID") + + +class TestSwcInternalBehavior: + """SWC 内部行为测试""" + + def test_create_swc_internal_behavior(self): + """测试创建 SWC 内部行为""" + runnable = create_runnable_entity(name="MainRunnable") + timing = create_timing_event("MainTiming", period=0.01) + + behavior = create_swc_internal_behavior( + name="ComponentBehavior", + runnable_entities=[runnable], + events=[timing], + symbol="Component_Symbol" + ) + assert behavior.name == "ComponentBehavior" + assert len(behavior.runnable_entities) == 1 + assert len(behavior.events) == 1 + assert behavior.symbol == "Component_Symbol" + + def test_swc_internal_behavior_add_runnable(self): + """测试 SWC 内部行为添加 Runnable""" + behavior = create_swc_internal_behavior(name="Behavior") + runnable = create_runnable_entity(name="NewRunnable") + + behavior.add_runnable(runnable) + assert behavior.get_runnable("NewRunnable") == runnable + + def test_swc_internal_behavior_add_event(self): + """测试 SWC 内部行为添加事件""" + behavior = create_swc_internal_behavior(name="Behavior") + event = create_timing_event("NewTiming", period=0.02) + + behavior.add_event(event) + assert behavior.get_event("NewTiming") == event + + def test_swc_internal_behavior_to_arxml(self): + """测试 SWC 内部行为 ARXML 转换""" + runnable = create_runnable_entity(name="MainRunnable", symbol="MainRunnable_Func") + timing = create_timing_event("Periodic", period=0.01) + + behavior = create_swc_internal_behavior( + name="ComponentBehavior", + runnable_entities=[runnable], + events=[timing] + ) + + doc = Document() + element = behavior.to_arxml(doc) + + assert element.tagName == "SWC-INTERNAL-BEHAVIOR" + assert element.hasAttribute("UUID") + + +class TestSwComponent: + """软件组件测试""" + + def test_create_application_swc(self): + """测试创建应用组件""" + swc = ApplicationSwComponentType(name="TestComponent") + assert swc.name == "TestComponent" + assert swc.atomic_type.value == "Application" + + def test_application_swc_add_port(self): + """测试应用组件添加端口""" + swc = ApplicationSwComponentType(name="TestComponent") + + data_element = SenderReceiverInterface.DataElement(name="TestData") + interface = create_sender_receiver_interface("TestInterface", data_element=data_element) + port = PPortPrototype(name="TestPort", interface=interface) + + swc.add_port(port) + assert swc.get_port("TestPort") == port + + def test_application_swc_to_arxml(self): + """测试应用组件 ARXML 转换""" + swc = ApplicationSwComponentType(name="TestComponent") + + doc = Document() + element = swc.to_arxml(doc) + + assert element.tagName == "APPLICATION-SW-COMPONENT-TYPE" + assert element.hasAttribute("UUID") + + short_name = element.getElementsByTagName("SHORT-NAME")[0] + assert short_name.firstChild.nodeValue == "TestComponent" + + def test_application_swc_with_ports_to_arxml(self): + """测试带端口的应用组件 ARXML 转换""" + swc = ApplicationSwComponentType(name="ComponentWithPorts") + + data_element = SenderReceiverInterface.DataElement(name="StatusData") + interface = create_sender_receiver_interface("StatusInterface", data_element=data_element) + port = PPortPrototype(name="StatusPort", interface=interface) + swc.add_port(port) + + doc = Document() + element = swc.to_arxml(doc) + + assert element.tagName == "APPLICATION-SW-COMPONENT-TYPE" + ports = element.getElementsByTagName("PORTS") + assert len(ports) == 1 + + +class TestPortPrototypes: + """端口原型测试""" + + def test_r_port_prototype_to_arxml(self): + """测试 R-Port ARXML 转换""" + data_element = SenderReceiverInterface.DataElement(name="RxData") + interface = create_sender_receiver_interface("RxInterface", data_element=data_element) + port = RPortPrototype(name="RPort1", interface=interface) + + doc = Document() + element = port.to_arxml(doc) + + assert element.tagName == "R-PORT-PROTOTYPE" + assert element.hasAttribute("UUID") + + short_name = element.getElementsByTagName("SHORT-NAME")[0] + assert short_name.firstChild.nodeValue == "RPort1" + + def test_p_port_prototype_to_arxml(self): + """测试 P-Port ARXML 转换""" + data_element = SenderReceiverInterface.DataElement(name="TxData") + interface = create_sender_receiver_interface("TxInterface", data_element=data_element) + port = PPortPrototype(name="PPort1", interface=interface) + + doc = Document() + element = port.to_arxml(doc) + + assert element.tagName == "P-PORT-PROTOTYPE" + assert element.hasAttribute("UUID") + + short_name = element.getElementsByTagName("SHORT-NAME")[0] + assert short_name.firstChild.nodeValue == "PPort1" + + def test_pr_port_prototype_to_arxml(self): + """测试 PR-Port ARXML 转换""" + data_element = SenderReceiverInterface.DataElement(name="BidirectionalData") + interface = create_sender_receiver_interface("BiInterface", data_element=data_element) + port = PRPortPrototype(name="PRPort1", interface=interface) + + doc = Document() + element = port.to_arxml(doc) + + assert element.tagName == "P-PORT-PROTOTYPE" + assert element.hasAttribute("UUID") + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) \ No newline at end of file diff --git a/tests/test_types.py b/tests/test_types.py new file mode 100644 index 0000000..8714219 --- /dev/null +++ b/tests/test_types.py @@ -0,0 +1,423 @@ +""" +数据类型测试 +""" +import pytest +from xml.dom.minidom import Document + +from src import ( + BaseType, + create_base_type, + Unit, + create_unit, + Category, + Encoding, +) +from src.core.base import create_uuid, create_text_element + + +class TestBaseType: + """BaseType 测试""" + + def test_create_base_type(self): + """测试创建基础类型""" + base_type = create_base_type("UInt8", size=8) + assert base_type.name == "UInt8" + assert base_type.size == 8 + assert base_type.category == Category.FIXED_LENGTH + + def test_base_type_encoding(self): + """测试编码属性""" + base_type = create_base_type( + "SInt8", + encoding=Encoding.TWO_COMPONENT + ) + assert base_type.encoding_value == "2C" + + def test_base_type_to_arxml(self): + """测试 ARXML 转换""" + base_type = create_base_type("UInt16", size=16) + doc = Document() + element = base_type.to_arxml(doc) + + assert element.tagName == "SW-BASE-TYPE" + assert element.hasAttribute("UUID") + + short_name = element.getElementsByTagName("SHORT-NAME")[0] + assert short_name.firstChild.nodeValue == "UInt16" + + +class TestUnit: + """Unit 测试""" + + def test_create_unit(self): + """测试创建单位""" + unit = create_unit("kmh", display="km/h", factor=1, offset=0) + assert unit.name == "kmh" + assert unit.display == "km/h" + assert unit.factor == 1 + assert unit.offset == 0 + + def test_unit_to_arxml(self): + """测试单位 ARXML 转换""" + unit = create_unit("m/s", display="meters per second") + doc = Document() + element = unit.to_arxml(doc) + + assert element.tagName == "UNIT" + assert element.hasAttribute("UUID") + + short_name = element.getElementsByTagName("SHORT-NAME")[0] + assert short_name.firstChild.nodeValue == "m/s" + + +class TestDataConstraint: + """DataConstraint 测试""" + + def test_create_data_constraint(self): + """测试创建数据约束""" + from src import create_data_constraint + + constraint = create_data_constraint( + "SpeedConstraint", + lower=0, + upper=250 + ) + assert constraint.name == "SpeedConstraint" + assert constraint.lower == 0 + assert constraint.upper == 250 + + def test_data_constraint_to_arxml(self): + """测试数据约束 ARXML 转换""" + from src import create_data_constraint + + constraint = create_data_constraint( + "RangeConstraint", + lower=0, + upper=100 + ) + doc = Document() + element = constraint.to_arxml(doc) + + assert element.tagName == "DATA-CONSTR" + assert element.hasAttribute("UUID") + + +class TestCompuMethod: + """CompuMethod 测试""" + + def test_create_compu_method(self): + """测试创建计算方法""" + from src import create_compu_method, create_linear + + linear = create_linear(factor=0.1, offset=10) + compu = create_compu_method( + "LinearConversion", + linear=linear + ) + assert compu.name == "LinearConversion" + assert compu.linear == linear + assert compu.category == Category.IDENTICAL + + def test_compu_method_with_text_table(self): + """测试带文本表的计算方法""" + from src import create_compu_method, create_text_table + + text_table = create_text_table("OnOff", "ON", 0, 1) + compu = create_compu_method( + "OnOffTable", + text_tables=[text_table] + ) + assert len(compu.text_tables) == 1 + assert compu.get_text_table("ON") == text_table + + def test_compu_method_to_arxml(self): + """测试计算方法 ARXML 转换""" + from src import create_compu_method + + compu = create_compu_method("TestMethod") + doc = Document() + element = compu.to_arxml(doc) + + assert element.tagName == "COMPU-METHOD" + assert element.hasAttribute("UUID") + + +class TestApplicationTypes: + """应用数据类型测试""" + + def test_create_boolean_type(self): + """测试创建布尔类型""" + from src import create_boolean_type + + bool_type = create_boolean_type("BooleanSignal") + assert bool_type.name == "BooleanSignal" + assert bool_type.category == Category.BOOLEAN + + def test_create_value_type(self): + """测试创建值类型""" + from src import create_value_type + + value_type = create_value_type("SpeedValue") + assert value_type.name == "SpeedValue" + assert value_type.category == Category.VALUE + + def test_create_structure_type(self): + """测试创建结构体类型""" + from src import create_structure_type, ApplicationStructureDataType + + element = ApplicationStructureDataType.StructureElement(name="member1") + struct_type = create_structure_type("MyStruct", elements=[element]) + assert struct_type.name == "MyStruct" + assert len(struct_type.structure_elements) == 1 + assert struct_type.get_element("member1") == element + + +class TestImplementationTypes: + """实现数据类型测试""" + + def test_create_impl_value_type(self): + """测试创建实现值类型""" + from src import create_impl_value_type, create_base_type + + base = create_base_type("UInt8") + impl_type = create_impl_value_type("ImplUInt8", base_type=base) + assert impl_type.name == "ImplUInt8" + assert impl_type.base_type == base + assert impl_type.category == Category.VALUE + + def test_create_impl_structure_type(self): + """测试创建实现结构体类型""" + from src import create_impl_structure_type, ImplementationStructureDataType + + element = ImplementationStructureDataType.StructureElement(name="field1") + struct = create_impl_structure_type("ImplStruct", elements=[element]) + assert struct.name == "ImplStruct" + assert len(struct.structure_elements) == 1 + + def test_impl_value_type_to_arxml(self): + """测试实现值类型 ARXML 转换""" + from src import create_impl_value_type, create_base_type + + base = create_base_type("UInt8") + impl_type = create_impl_value_type("ImplUInt8", base_type=base) + + doc = Document() + element = impl_type.to_arxml(doc) + + assert element.tagName == "IMPLEMENTATION-DATA-TYPE" + assert element.hasAttribute("UUID") + + short_name = element.getElementsByTagName("SHORT-NAME")[0] + assert short_name.firstChild.nodeValue == "ImplUInt8" + + def test_impl_structure_type_to_arxml(self): + """测试实现结构体类型 ARXML 转换""" + from src import create_impl_structure_type, ImplementationStructureDataType + + element = ImplementationStructureDataType.StructureElement(name="field1") + struct = create_impl_structure_type("ImplStruct", elements=[element]) + + doc = Document() + element = struct.to_arxml(doc) + + assert element.tagName == "IMPLEMENTATION-DATA-TYPE" + assert element.hasAttribute("UUID") + + +class TestLinear: + """Linear 转换测试""" + + def test_linear_to_arxml(self): + """测试线性转换 ARXML 转换""" + from src import create_linear + + linear = create_linear(factor=0.1, offset=10) + + doc = Document() + element = linear.to_arxml(doc) + + assert element.tagName == "COMPU-SCALE" + assert element.hasChildNodes() + + +class TestTextTable: + """TextTable 文本表测试""" + + def test_create_text_table(self): + """测试创建文本表""" + from src import create_text_table + + text_table = create_text_table("OnOff", "ON", 0, 1) + assert text_table.name == "OnOff" + assert text_table.vt == "ON" + assert text_table.lower == 0 + assert text_table.upper == 1 + + def test_text_table_to_arxml(self): + """测试文本表 ARXML 转换""" + from src import create_text_table + + text_table = create_text_table("Status", "ACTIVE", 1, 2) + + doc = Document() + element = text_table.to_arxml(doc) + + assert element.tagName == "COMPU-SCALE" + assert element.hasChildNodes() + + vt_elem = element.getElementsByTagName("VT") + assert len(vt_elem) > 0 + + +class TestInterfaces: + """接口测试""" + + def test_client_server_interface_creation(self): + """测试创建客户端-服务端接口""" + from src import ClientServerInterface + + cs_interface = ClientServerInterface(name="TestService") + assert cs_interface.name == "TestService" + assert cs_interface.is_service is False + + operation = ClientServerInterface.Operation(name="StartOperation") + cs_interface.add_operation(operation) + assert cs_interface.get_operation("StartOperation") == operation + + def test_client_server_interface_to_arxml(self): + """测试客户端-服务端接口 ARXML 转换""" + from src import ClientServerInterface + + cs_interface = ClientServerInterface(name="TestService") + operation = ClientServerInterface.Operation(name="StartOperation") + cs_interface.add_operation(operation) + + doc = Document() + element = cs_interface.to_arxml(doc) + + assert element.tagName == "CLIENT-SERVER-INTERFACE" + assert element.hasAttribute("UUID") + + def test_client_server_operation_to_arxml(self): + """测试操作方法 ARXML 转换""" + from src import ClientServerInterface + + operation = ClientServerInterface.Operation(name="TestOp", is_server=True) + + doc = Document() + element = operation.to_arxml(doc) + + assert element.tagName == "CLIENT-SERVER-OPERATION" + assert element.hasAttribute("UUID") + + +class TestDataMapping: + """数据类型映射测试""" + + def test_data_type_mapping_creation(self): + """测试创建数据类型映射""" + from src import create_data_type_mapping + + mapping = create_data_type_mapping("TestMapping") + assert mapping.name == "TestMapping" + + def test_data_type_mapping_to_arxml(self): + """测试数据类型映射 ARXML 转换""" + from src import create_data_type_mapping + + mapping = create_data_type_mapping("TestMapping") + + doc = Document() + element = mapping.to_arxml(doc) + + assert element.tagName == "DATA-TYPE-MAP" + + def test_data_type_mapping_set_creation(self): + """测试创建数据类型映射集""" + from src import create_data_type_mapping_set, create_data_type_mapping + + mapping1 = create_data_type_mapping("Mapping1") + mapping_set = create_data_type_mapping_set("TestSet", mappings=[mapping1]) + + assert mapping_set.name == "TestSet" + assert len(mapping_set.data_type_mappings) == 1 + assert mapping_set.get_mapping("Mapping1") == mapping1 + + def test_data_type_mapping_set_to_arxml(self): + """测试数据类型映射集 ARXML 转换""" + from src import create_data_type_mapping_set + + mapping_set = create_data_type_mapping_set("TestSet") + + doc = Document() + element = mapping_set.to_arxml(doc) + + assert element.tagName == "DATA-TYPE-MAPPING-SET" + assert element.hasAttribute("UUID") + + +class TestApplicationTypesToArxml: + """应用数据类型 ARXML 测试""" + + def test_value_type_to_arxml(self): + """测试应用值类型 ARXML 转换""" + from src import create_value_type + + value_type = create_value_type("SpeedValue") + + doc = Document() + element = value_type.to_arxml(doc) + + assert element.tagName == "APPLICATION-PRIMITIVE-DATA-TYPE" + assert element.hasAttribute("UUID") + + short_name = element.getElementsByTagName("SHORT-NAME")[0] + assert short_name.firstChild.nodeValue == "SpeedValue" + + def test_boolean_type_to_arxml(self): + """测试布尔类型 ARXML 转换""" + from src import create_boolean_type + + bool_type = create_boolean_type("IsActive") + + doc = Document() + element = bool_type.to_arxml(doc) + + assert element.tagName == "APPLICATION-PRIMITIVE-DATA-TYPE" + assert element.hasAttribute("UUID") + + def test_structure_type_to_arxml(self): + """测试应用结构体类型 ARXML 转换""" + from src import create_structure_type, ApplicationStructureDataType + + element = ApplicationStructureDataType.StructureElement(name="member1") + struct = create_structure_type("MyStruct", elements=[element]) + + doc = Document() + arxml_element = struct.to_arxml(doc) + + assert arxml_element.tagName == "APPLICATION-RECORD-DATA-TYPE" + assert arxml_element.hasAttribute("UUID") + + def test_array_type_to_arxml(self): + """测试应用数组类型 ARXML 转换""" + from src import create_array_type, create_value_type, ApplicationArrayDataType, Semantic + + element_type = create_value_type("ArrayElement") + array_element = ApplicationArrayDataType.ArrayElement( + name="element", + data_type=element_type, + array_size_semantics=Semantic.FIXED, + length=10 + ) + array_type = create_array_type("MyArray", element=array_element) + + doc = Document() + arxml_element = array_type.to_arxml(doc) + + assert arxml_element.tagName == "APPLICATION-ARRAY-DATA-TYPE" + assert arxml_element.hasAttribute("UUID") + + +if __name__ == "__main__": + pytest.main([__file__, "-v"]) \ No newline at end of file