优化代码

This commit is contained in:
2026-06-08 10:17:44 +08:00
parent a05ecab7b2
commit dcf9fb590f
18 changed files with 1838 additions and 89 deletions
+394
View File
@@ -0,0 +1,394 @@
# arxml_sdk 项目优化与改进计划
## 一、当前状态分析
### 1.1 项目概述
**arxml_sdk** 是一个用于生成 AUTOSAR ARXML 配置文件的 Python SDK(版本 2.0.1),采用纯 Python 实现,零外部依赖,符合 AUTOSAR R4.4 标准。
### 1.2 技术架构
```
arxml_sdk/
├── src/
│ ├── __init__.py # 包入口,导出全部 193 个公共 API
│ ├── core/ # 核心基础设施层
│ │ ├── base.py # Base 基类
│ │ ├── constants.py # 138 个 ARXML 标签常量
│ │ └── enums.py # 13 个枚举类型定义
│ ├── types/ # 数据类型层 (14 个模块)
│ │ ├── 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
│ └── utils/
│ └── arxml_writer.py
└── tests/ # 测试模块
```
### 1.3 设计模式
- **工厂函数模式**:所有类型配有 `create_xxx()` 工厂函数
- **组合模式**:通过 `add_xxx()` 方法支持动态添加子元素
- **序列化模式**:所有类型实现 `to_arxml(doc)` 方法
- **数据类模式**:使用 `@dataclass` 简化定义
### 1.4 代码质量问题
| 类别 | 问题 | 严重程度 |
|------|------|----------|
| 类型安全 | `to_arxml()` 返回类型声明为 `Document` 实际返回 `Element` | 高 |
| 命名规范 | `enums.py` 中枚举值命名不一致(大写驼峰混用) | 中 |
| 错误处理 | 缺少参数验证和异常处理机制 | 高 |
| 类型引用 | 大量使用 `TYPE_CHECKING` 和字符串类型提示 | 中 |
| 一致性 | 部分类缺少 `factory default` 默认值 | 低 |
| 性能 | 每次序列化都调用 `create_uuid()`,无缓存机制 | 中 |
---
## 二、改进计划
### 2.1 优先级 1:类型安全和 API 修复
#### 问题描述
`to_arxml()` 方法的返回类型声明为 `Document`,但实际返回 `Element`,这会导致类型检查工具报错。
#### 修改文件
- [src/core/base.py](file:///f:/MyProject/arxml_sdk/src/core/base.py#L55-L56) - 修复 `Base.to_arxml()` 返回类型
- 所有继承类(共 20+ 个文件)- 更新返回类型声明
#### 具体修改
```python
# 修改前
def to_arxml(self, doc: Document) -> Document:
# 修改后
def to_arxml(self, doc: Document) -> Element:
```
### 2.2 优先级 2:枚举值规范化
#### 问题描述
`enums.py` 中枚举值命名混乱:
- `Encoding.ONE_COMPONENT = "1C - One's Complement"` - 使用值包含特殊字符
- `UpperSts.CLOSED = '['` / `LowerSts.CLOSED = ']'` - 区间符号不正确
#### 修改文件
- [src/core/enums.py](file:///f:/MyProject/arxml_sdk/src/core/enums.py) - 重构枚举定义
#### 具体修改
1. **Encoding 枚举**:简化枚举值,移除描述性文本
```python
class Encoding(Enum):
"""数据编码格式"""
ONE_COMPONENT = "1C"
TWO_COMPONENT = "2C"
IEEE754 = "IEEE754"
NONE = "NONE"
# ...
```
2. **LowerSts/UpperSts 枚举**:使用正确的区间符号
```python
class IntervalType(Enum):
CLOSED_LEFT = "[" # 左闭
OPEN_LEFT = "(" # 左开
CLOSED_RIGHT = "]" # 右闭
OPEN_RIGHT = ")" # 右开
```
### 2.3 优先级 3:输入验证和错误处理
#### 问题描述
当前代码缺少参数验证,传入无效参数时会产生不明确的错误。
#### 修改文件
- [src/types/base_type.py](file:///f:/MyProject/arxml_sdk/src/types/base_type.py) - 添加验证
- [src/types/application_types.py](file:///f:/MyProject/arxml_sdk/src/types/application_types.py)
- [src/core/base.py](file:///f:/MyProject/arxml_sdk/src/core/base.py)
#### 具体修改
```python
from dataclasses import dataclass, field
from typing import Optional
import re
@dataclass
class BaseType(Base):
size: Optional[int] = 8
def __post_init__(self):
if self.size is not None and self.size <= 0:
raise ValueError(f"size must be positive, got {self.size}")
if self.size is not None and self.size > 65536:
raise ValueError(f"size exceeds maximum allowed value 65536, got {self.size}")
@property
def encoding_value(self) -> str:
if self.encoding == Encoding.TWO_COMPONENT:
return "2C"
elif self.encoding == Encoding.ONE_COMPONENT:
return "1C"
return self.encoding.value if hasattr(self.encoding, 'value') else str(self.encoding)
```
### 2.4 优先级 4UUID 缓存机制优化
#### 问题描述
每次调用 `to_arxml()` 都会生成新的 UUID,在大量序列化时效率较低。
#### 修改文件
- [src/core/base.py](file:///f:/MyProject/arxml_sdk/src/core/base.py)
#### 具体修改
```python
@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)
_uuid: Optional[str] = field(default=None, repr=False)
@property
def uuid(self) -> str:
"""延迟生成 UUID,仅在首次序列化时生成"""
if self._uuid is None:
self._uuid = create_uuid()
return self._uuid
```
### 2.5 优先级 5:支持 AUTOSAR R4.4 完整功能
#### 问题描述
对照 `spec.md` 中的 AUTOSAR 标准,部分功能尚未实现:
- 缺少 `PHYSICAL-DIMENSION` 支持
- 缺少 `SW-RECORD-LAYOUT` 支持
- 缺少 `BSW-MODULE-ENTRY` 支持
- 接口缺少 `POSSIBLE-ERRORS` 定义
#### 新增文件
| 文件 | 功能 | 优先级 |
|------|------|--------|
| `src/types/physical_dimension.py` | 物理维度定义 | 中 |
| `src/types/sw_record_layout.py` | 记录布局定义 | 低 |
| `src/types/bsw_module_entry.py` | BSW 模块条目 | 低 |
| `src/types/interpolation_routine.py` | 插值算法映射 | 低 |
#### 具体修改
```python
# 新增 physical_dimension.py
@dataclass
class PhysicalDimension(Base):
"""物理维度定义"""
length_exp: int = 0
mass_exp: int = 0
time_exp: int = 0
current_exp: int = 0
temperature_exp: int = 0
molar_amount_exp: int = 0
luminous_intensity_exp: int = 0
def to_arxml(self, doc: Document) -> Element:
element = doc.createElement("PHYSICAL-DIMENSION")
element.setAttribute("UUID", self.uuid)
element.appendChild(create_text_element(doc, SHORT_NAME, self.name))
element.appendChild(create_text_element(doc, "LENGTH-EXP", str(self.length_exp)))
# ... 其他维度
return element
# 更新 interface.py - 添加 POSSIBLE_ERRORS 支持
@dataclass
class ClientServerInterface(Interface):
"""客户端-服务端接口"""
possible_errors: List[ApplicationError] = field(default_factory=list)
@dataclass
class ApplicationError(Base):
error_code: int = 0
def to_arxml(self, doc: Document) -> Element:
element = doc.createElement(CLIENT_SERVER_INTERFACE)
# ... existing code ...
if self.possible_errors:
possible_errors_elem = doc.createElement("POSSIBLE-ERRORS")
for error in self.possible_errors:
possible_errors_elem.appendChild(error.to_arxml(doc))
element.appendChild(possible_errors_elem)
return element
```
### 2.6 优先级 6ARXML 解析器
#### 问题描述
当前 SDK 只能生成 ARXML 文件,缺少读取/解析现有 ARXML 文件的能力。
#### 新增文件
- `src/parser/arxml_parser.py` - ARXML 解析器
#### 具体实现
```python
class ARXMLParser:
"""ARXML 文件解析器"""
def __init__(self, filepath: str):
self.doc = minidom.parse(filepath)
def parse_base_type(self, element: Element) -> BaseType:
"""解析基础数据类型"""
name = self._get_text(element, SHORT_NAME)
category = Category[self._get_text(element, CATEGORY)]
size = int(self._get_text(element, BASE_TYPE_SIZE))
return BaseType(name=name, category=category, size=size)
def parse_package(self, element: Element) -> Package:
"""解析包"""
name = self._get_text(element, SHORT_NAME)
elements = []
for child in element.getElementsByTagName(ELEMENTS):
for elem in child.childNodes:
if elem.nodeType == Element.ELEMENT_NODE:
elements.append(self._parse_element(elem))
return Package(name=name, elements=elements)
```
### 2.7 优先级 7:测试覆盖率提升
#### 问题描述
当前测试文件较少,缺少对关键路径的完整测试。
#### 新增测试
- [tests/test_parser.py](file:///f:/MyProject/arxml_sdk/tests/test_parser.py) - 解析器测试
- [tests/test_validation.py](file:///f:/MyProject/arxml_sdk/tests/test_validation.py) - 验证测试
- [tests/test_blueprint_compliance.py](file:///f:/MyProject/arxml_sdk/tests/test_blueprint_compliance.py) - 标准合规测试
#### 具体修改
```python
# tests/test_validation.py
class TestValidation:
"""输入验证测试"""
def test_base_type_size_validation(self):
"""测试基础类型大小验证"""
with pytest.raises(ValueError, match="size must be positive"):
BaseType(name="Test", size=0)
with pytest.raises(ValueError, match="size exceeds maximum"):
BaseType(name="Test", size=100000)
def test_package_name_validation(self):
"""测试包名称验证"""
with pytest.raises(ValueError):
Package(name="Invalid/Name")
```
---
## 三、实施顺序
```
阶段 1:类型安全和 API 修复 (1-2 天)
├── 修复 to_arxml 返回类型
├── 更新所有继承类的返回类型声明
└── 运行现有测试确保无回归
阶段 2:枚举规范化 (1 天)
├── 重构 Encoding 枚举
├── 修复区间符号枚举
└── 更新所有引用
阶段 3:输入验证 (2 天)
├── 添加 __post_init__ 验证
├── 统一错误消息格式
└── 添加边界条件测试
阶段 4UUID 缓存优化 (1 天)
├── 实现延迟 UUID 生成
└── 性能基准测试
阶段 5:功能扩展 (3-5 天)
├── 添加 PhysicalDimension 支持
├── 添加 ClientServerInterface.POSSIBLE_ERRORS
└── 添加 SW-RECORD-LAYOUT 支持
阶段 6:解析器开发 (3-4 天)
├── 实现基础解析器
├── 添加类型映射
└── 集成测试
阶段 7:测试覆盖 (2 天)
├── 添加验证测试
├── 添加解析器测试
└── 添加合规性测试
```
---
## 四、验证步骤
### 4.1 类型检查
```bash
# 安装 mypy 并检查类型
pip install mypy
mypy src/ --ignore-missing-imports
```
### 4.2 单元测试
```bash
# 运行所有测试
pytest tests/ -v --cov=src --cov-report=html
# 检查覆盖率
coverage report --fail-under=80
```
### 4.3 ARXML 合规性验证
```bash
# 使用 xmllint 验证生成的 ARXML 文件
xmllint --noout output.arxml
# 使用 AUTOSAR Schema 验证(需要下载 XSD
xmllint --schema AUTOSAR_4-4.xsd output.arxml
```
### 4.4 性能基准测试
```python
# benchmark_uuid.py
import timeit
setup = "from src.core.base import create_uuid"
stmt = "create_uuid()"
result = timeit.timeit(stmt, setup, number=10000)
print(f"UUID 生成 10000 次耗时: {result:.3f}s")
```
---
## 五、风险和限制
| 风险 | 影响 | 缓解措施 |
|------|------|----------|
| API 变更导致现有用户代码不兼容 | 高 | 提供向后兼容的弃用警告 |
| UUID 缓存影响幂等性 | 中 | 提供禁用选项 |
| 解析器复杂度和维护成本 | 中 | 使用现有 XML 解析库 |
---
## 六、决策点
在开始实施之前,需要确认以下问题:
1. **向后兼容性**:是否需要保持与现有 API 的完全向后兼容?还是可以接受破坏性变更?
2. **优先级排序**:上述改进项的优先级是否合适?是否有其他更紧急的需求?
3. **测试覆盖率目标**:代码覆盖率目标应该设为多少?(建议 80%)
4. **新功能范围**:ARXML 解析器是否必须包含在本次改进中?
+1151
View File
File diff suppressed because it is too large Load Diff
+23 -1
View File
@@ -97,8 +97,20 @@ from .utils import (
get_ar_packages_element,
add_package_to_document,
)
from .types import (
PhysicalDimension,
create_physical_dimension,
create_length_dimension,
create_time_dimension,
create_velocity_dimension,
create_acceleration_dimension,
create_force_dimension,
create_voltage_dimension,
create_power_dimension,
create_frequency_dimension,
)
__version__ = "2.0.1"
__version__ = "2.1.0"
__all__ = [
"Base",
@@ -190,4 +202,14 @@ __all__ = [
"create_operation_invoked_event",
"create_data_received_event",
"create_swc_internal_behavior",
"PhysicalDimension",
"create_physical_dimension",
"create_length_dimension",
"create_time_dimension",
"create_velocity_dimension",
"create_acceleration_dimension",
"create_force_dimension",
"create_voltage_dimension",
"create_power_dimension",
"create_frequency_dimension",
]
+8
View File
@@ -31,6 +31,14 @@ class Base:
id: Optional[int] = field(default=None)
parent: Optional['Base'] = field(default=None, repr=False)
description: Optional[str] = field(default=None)
_uuid: Optional[str] = field(default=None, repr=False)
@property
def uuid(self) -> str:
"""获取 UUID,延迟生成,仅在首次访问时生成"""
if self._uuid is None:
self._uuid = create_uuid()
return self._uuid
@property
def class_name(self) -> str:
+22
View File
@@ -71,6 +71,18 @@ from .swc_internal_behavior import (
create_data_received_event,
create_swc_internal_behavior,
)
from .physical_dimension import (
PhysicalDimension,
create_physical_dimension,
create_length_dimension,
create_time_dimension,
create_velocity_dimension,
create_acceleration_dimension,
create_force_dimension,
create_voltage_dimension,
create_power_dimension,
create_frequency_dimension,
)
__all__ = [
"BaseType",
@@ -138,4 +150,14 @@ __all__ = [
"create_operation_invoked_event",
"create_data_received_event",
"create_swc_internal_behavior",
"PhysicalDimension",
"create_physical_dimension",
"create_length_dimension",
"create_time_dimension",
"create_velocity_dimension",
"create_acceleration_dimension",
"create_force_dimension",
"create_voltage_dimension",
"create_power_dimension",
"create_frequency_dimension",
]
+12 -12
View File
@@ -3,7 +3,7 @@
"""
from dataclasses import dataclass, field
from typing import Optional, List, TYPE_CHECKING, Union
from xml.dom.minidom import Document
from xml.dom.minidom import Document, Element
from ..core.base import Base, create_uuid, create_text_element
from ..core.constants import (
@@ -35,10 +35,10 @@ class ApplicationBooleanDataType(ApplicationDataType):
data_constraint: Optional['DataConstraint'] = None
category: Category = Category.BOOLEAN
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(APPLICATION_PRIMITIVE_DATA_TYPE)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.uuid)
element.appendChild(create_text_element(doc, SHORT_NAME, self.name))
element.appendChild(create_text_element(doc, CATEGORY, self.category.name))
@@ -74,10 +74,10 @@ class ApplicationValueDataType(ApplicationDataType):
data_constraint: Optional['DataConstraint'] = None
category: Category = Category.VALUE
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(APPLICATION_PRIMITIVE_DATA_TYPE)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.uuid)
element.appendChild(create_text_element(doc, SHORT_NAME, self.name))
element.appendChild(create_text_element(doc, CATEGORY, self.category.name))
@@ -121,10 +121,10 @@ class ApplicationStructureDataType(ApplicationDataType):
"""结构体成员"""
data_type: Optional[ApplicationDataType] = None
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(APPLICATION_RECORD_ELEMENT)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.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))
@@ -146,10 +146,10 @@ class ApplicationStructureDataType(ApplicationDataType):
return
self.structure_elements.append(element)
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(APPLICATION_RECORD_DATA_TYPE)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.uuid)
element.appendChild(create_text_element(doc, SHORT_NAME, self.name))
element.appendChild(create_text_element(doc, CATEGORY, self.category.name))
@@ -183,7 +183,7 @@ class ApplicationArrayDataType(ApplicationDataType):
data_type: Optional[ApplicationDataType] = None
array_size_semantics: Semantic = Semantic.FIXED
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(ELEMENT)
element.appendChild(create_text_element(doc, SHORT_NAME, self.name))
@@ -198,10 +198,10 @@ class ApplicationArrayDataType(ApplicationDataType):
element: Optional['ArrayElement'] = None
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(APPLICATION_ARRAY_DATA_TYPE)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.uuid)
element.appendChild(create_text_element(doc, SHORT_NAME, self.name))
element.appendChild(create_text_element(doc, CATEGORY, self.category.name))
+13 -8
View File
@@ -3,7 +3,7 @@
"""
from dataclasses import dataclass, field
from typing import Optional
from xml.dom.minidom import Document
from xml.dom.minidom import Document, Element
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
@@ -18,19 +18,24 @@ class BaseType(Base):
encoding: Encoding = Encoding.ONE_COMPONENT
native_description: Optional[str] = None
def __post_init__(self):
if self.size is not None and self.size <= 0:
raise ValueError(f"size must be positive, got {self.size}")
if self.size is not None and self.size > 65536:
raise ValueError(f"size exceeds maximum allowed value 65536, got {self.size}")
@property
def encoding_value(self) -> str:
"""获取编码值"""
if self.encoding == Encoding.ONE_COMPONENT:
return "1C"
elif self.encoding == Encoding.TWO_COMPONENT:
if self.encoding == Encoding.TWO_COMPONENT:
return "2C"
return self.encoding.name.upper()
elif self.encoding == Encoding.ONE_COMPONENT:
return "1C"
return self.encoding.value if hasattr(self.encoding, 'value') else str(self.encoding)
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(SW_BASE_TYPE)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.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)))
+5 -5
View File
@@ -3,7 +3,7 @@
"""
from dataclasses import dataclass, field
from typing import Optional, List
from xml.dom.minidom import Document
from xml.dom.minidom import Document, Element
from ..core.base import Base, create_uuid, create_text_element
from ..core.constants import (
@@ -22,7 +22,7 @@ class Linear(Base):
factor: Optional[float] = None
offset: Optional[float] = None
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
compu_scale = doc.createElement(COMPU_SCALE)
compu_rational_coeffes = doc.createElement(COMPU_RATIONAL_COEFFS)
@@ -49,7 +49,7 @@ class TextTable(Base):
lower_sts: LowerSts = LowerSts.CLOSED
upper_sts: UpperSts = UpperSts.CLOSED
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
compu_scale = doc.createElement(COMPU_SCALE)
lower_limit = create_text_element(doc, LOWER_LIMIT, str(self.lower or 0))
@@ -89,10 +89,10 @@ class CompuMethod(Base):
return
self.text_tables.append(text_table)
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(COMPU_METHOD)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.uuid)
element.appendChild(create_text_element(doc, SHORT_NAME, self.name))
element.appendChild(create_text_element(doc, CATEGORY, self.category.name.replace("_", "")))
+7 -3
View File
@@ -3,7 +3,7 @@
"""
from dataclasses import dataclass, field
from typing import Optional, Union
from xml.dom.minidom import Document
from xml.dom.minidom import Document, Element
from ..core.base import Base, create_uuid, create_text_element
from ..core.constants import (
@@ -24,10 +24,14 @@ class DataConstraint(Base):
upper_sts: UpperSts = UpperSts.CLOSED
constraint_specified: ConstraintSpecified = ConstraintSpecified.PHYSICAL
def to_arxml(self, doc: Document) -> Document:
def __post_init__(self):
if self.lower > self.upper:
raise ValueError(f"lower bound ({self.lower}) cannot be greater than upper bound ({self.upper})")
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(DATA_CONSTR)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.uuid)
element.appendChild(create_text_element(doc, SHORT_NAME, self.name))
data_constr_rules = doc.createElement(DATA_CONSTR_RULES)
+4 -4
View File
@@ -3,7 +3,7 @@
"""
from dataclasses import dataclass, field
from typing import Optional, List, TYPE_CHECKING
from xml.dom.minidom import Document
from xml.dom.minidom import Document, Element
from ..core.base import Base, create_uuid, create_text_element
from ..core.constants import (
@@ -25,7 +25,7 @@ class DataTypeMapping(Base):
application_data_type: Optional['ApplicationDataType'] = None
implementation_data_type: Optional['ImplementationDataType'] = None
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(DATA_TYPE_MAP)
@@ -63,10 +63,10 @@ class DataTypeMappingSet(Base):
if self.get_mapping(mapping.name) is None:
self.data_type_mappings.append(mapping)
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(DATA_TYPE_MAPPING_SET)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.uuid)
element.appendChild(create_text_element(doc, SHORT_NAME, self.name))
data_type_maps = doc.createElement(DATA_TYPE_MAPS)
+11 -11
View File
@@ -3,7 +3,7 @@
"""
from dataclasses import dataclass, field
from typing import Optional, List, TYPE_CHECKING
from xml.dom.minidom import Document
from xml.dom.minidom import Document, Element
from ..core.base import Base, create_uuid, create_text_element
from ..core.constants import (
@@ -35,10 +35,10 @@ class ImplementationValueDataType(ImplementationDataType):
data_constraint: Optional['DataConstraint'] = None
category: Category = Category.VALUE
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(IMPLEMENTATION_DATA_TYPE)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.uuid)
element.appendChild(create_text_element(doc, SHORT_NAME, self.name))
element.appendChild(create_text_element(doc, CATEGORY, self.category.name))
@@ -75,10 +75,10 @@ class ImplementationStructureDataType(ImplementationDataType):
data_type: Optional[ImplementationDataType] = None
category: Category = Category.TYPE_REFERENCE
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(IMPLEMENTATION_DATA_TYPE_ELEMENT)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.uuid)
element.appendChild(create_text_element(doc, SHORT_NAME, self.name))
element.appendChild(create_text_element(doc, CATEGORY, self.category.name))
@@ -112,10 +112,10 @@ class ImplementationStructureDataType(ImplementationDataType):
return
self.structure_elements.append(element)
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(IMPLEMENTATION_DATA_TYPE)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.uuid)
element.appendChild(create_text_element(doc, SHORT_NAME, self.name))
element.appendChild(create_text_element(doc, CATEGORY, self.category.name))
@@ -140,10 +140,10 @@ class ImplementationArrayDataType(ImplementationDataType):
category: Category = Category.TYPE_REFERENCE
array_size_semantics: Semantic = Semantic.FIXED
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(IMPLEMENTATION_DATA_TYPE_ELEMENT)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.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)))
@@ -168,10 +168,10 @@ class ImplementationArrayDataType(ImplementationDataType):
element: Optional['ArrayElement'] = None
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(IMPLEMENTATION_DATA_TYPE)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.uuid)
element.appendChild(create_text_element(doc, SHORT_NAME, self.name))
element.appendChild(create_text_element(doc, CATEGORY, self.category.name))
+31 -11
View File
@@ -3,7 +3,7 @@
"""
from dataclasses import dataclass, field
from typing import Optional, List, TYPE_CHECKING, Union
from xml.dom.minidom import Document
from xml.dom.minidom import Document, Element
from ..core.base import Base, create_uuid, create_text_element
from ..core.constants import (
@@ -50,10 +50,10 @@ class SenderReceiverInterface(Interface):
policy: Policy = Policy.STANDARD
calibration_access: CalibrationAccess = CalibrationAccess.READ_ONLY
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(VARIABLE_DATA_PROTOTYPE)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.uuid)
element.appendChild(create_text_element(doc, SHORT_NAME, self.name))
if self.data_type:
@@ -81,10 +81,10 @@ class SenderReceiverInterface(Interface):
data_element: Optional[DataElement] = None
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(SENDER_RECEIVER_INTERFACE)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.uuid)
element.appendChild(create_text_element(doc, SHORT_NAME, self.name))
element.appendChild(create_text_element(doc, IS_SERVICE, str(self.is_service).lower()))
@@ -100,16 +100,29 @@ class SenderReceiverInterface(Interface):
class ClientServerInterface(Interface):
"""客户端-服务端接口"""
@dataclass
class ApplicationError(Base):
"""应用错误定义"""
error_code: int = 0
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement("APPLICATION-ERROR")
element.setAttribute("UUID", self.uuid)
element.appendChild(create_text_element(doc, SHORT_NAME, self.name))
element.appendChild(create_text_element(doc, "ERROR-CODE", str(self.error_code)))
return element
@dataclass
class Operation(Base):
"""操作方法定义"""
is_server: bool = True
arguments: List['ClientServerInterface.Argument'] = field(default_factory=list)
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(CLIENT_SERVER_OPERATION)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.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
@@ -120,10 +133,10 @@ class ClientServerInterface(Interface):
direction: str = "IN"
data_type: Optional['ApplicationDataType'] = None
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(ARGUMENT)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.uuid)
element.appendChild(create_text_element(doc, SHORT_NAME, self.name))
element.appendChild(create_text_element(doc, DIRECTION, self.direction))
if self.data_type:
@@ -133,6 +146,7 @@ class ClientServerInterface(Interface):
return element
operations: List[Operation] = field(default_factory=list)
possible_errors: List[ApplicationError] = field(default_factory=list)
def get_operation(self, name: str) -> Optional['Operation']:
"""根据名称查找操作"""
@@ -146,13 +160,19 @@ class ClientServerInterface(Interface):
if self.get_operation(operation.name) is None:
self.operations.append(operation)
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(CLIENT_SERVER_INTERFACE)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.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.possible_errors:
possible_errors_elem = doc.createElement("POSSIBLE-ERRORS")
for error in self.possible_errors:
possible_errors_elem.appendChild(error.to_arxml(doc))
element.appendChild(possible_errors_elem)
for op in self.operations:
element.appendChild(op.to_arxml(doc))
+3 -3
View File
@@ -3,7 +3,7 @@ Package 模块
"""
from dataclasses import dataclass, field
from typing import List, Optional, TYPE_CHECKING
from xml.dom.minidom import Document
from xml.dom.minidom import Document, Element
from ..core.base import Base, create_uuid, create_text_element
from ..core.constants import AR_PACKAGE, SHORT_NAME, ELEMENTS
@@ -40,10 +40,10 @@ class Package(Base):
self.elements.remove(existing)
self.elements.append(element)
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(AR_PACKAGE)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.uuid)
element.appendChild(create_text_element(doc, SHORT_NAME, self.name))
elements = doc.createElement(ELEMENTS)
+111
View File
@@ -0,0 +1,111 @@
"""
物理维度类型模块
"""
from dataclasses import dataclass
from typing import Optional
from xml.dom.minidom import Document, Element
from ..core.base import Base, create_text_element
from ..core.constants import SHORT_NAME
@dataclass
class PhysicalDimension(Base):
"""物理维度定义
用于定义物理量的维度,如长度、时间、电流等。
基于 AUTOSAR 标准中的 PHYSICAL-DIMENSION 定义。
维度指数范围:
- LENGTH-EXP: 长度指数
- MASS-EXP: 质量指数
- TIME-EXP: 时间指数
- CURRENT-EXP: 电流指数
- TEMPERATURE-EXP: 温度指数
- MOLAR-AMOUNT-EXP: 物质的量指数
- LUMINOUS-INTENSITY-EXP: 发光强度指数
"""
length_exp: int = 0
mass_exp: int = 0
time_exp: int = 0
current_exp: int = 0
temperature_exp: int = 0
molar_amount_exp: int = 0
luminous_intensity_exp: int = 0
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement("PHYSICAL-DIMENSION")
element.setAttribute("UUID", self.uuid)
element.appendChild(create_text_element(doc, SHORT_NAME, self.name))
element.appendChild(create_text_element(doc, "LENGTH-EXP", str(self.length_exp)))
element.appendChild(create_text_element(doc, "MASS-EXP", str(self.mass_exp)))
element.appendChild(create_text_element(doc, "TIME-EXP", str(self.time_exp)))
element.appendChild(create_text_element(doc, "CURRENT-EXP", str(self.current_exp)))
element.appendChild(create_text_element(doc, "TEMPERATURE-EXP", str(self.temperature_exp)))
element.appendChild(create_text_element(doc, "MOLAR-AMOUNT-EXP", str(self.molar_amount_exp)))
element.appendChild(create_text_element(doc, "LUMINOUS-INTENSITY-EXP", str(self.luminous_intensity_exp)))
return element
def create_physical_dimension(
name: str,
length_exp: int = 0,
mass_exp: int = 0,
time_exp: int = 0,
current_exp: int = 0,
temperature_exp: int = 0,
molar_amount_exp: int = 0,
luminous_intensity_exp: int = 0,
) -> PhysicalDimension:
"""创建物理维度的工厂函数"""
return PhysicalDimension(
name=name,
length_exp=length_exp,
mass_exp=mass_exp,
time_exp=time_exp,
current_exp=current_exp,
temperature_exp=temperature_exp,
molar_amount_exp=molar_amount_exp,
luminous_intensity_exp=luminous_intensity_exp,
)
def create_length_dimension(name: str) -> PhysicalDimension:
"""创建长度维度 (L=1)"""
return create_physical_dimension(name, length_exp=1)
def create_time_dimension(name: str) -> PhysicalDimension:
"""创建时间维度 (T=1)"""
return create_physical_dimension(name, time_exp=1)
def create_velocity_dimension(name: str) -> PhysicalDimension:
"""创建速度维度 (L=1, T=-1)"""
return create_physical_dimension(name, length_exp=1, time_exp=-1)
def create_acceleration_dimension(name: str) -> PhysicalDimension:
"""创建加速度维度 (L=1, T=-2)"""
return create_physical_dimension(name, length_exp=1, time_exp=-2)
def create_force_dimension(name: str) -> PhysicalDimension:
"""创建力维度 (L=1, M=1, T=-2)"""
return create_physical_dimension(name, length_exp=1, mass_exp=1, time_exp=-2)
def create_voltage_dimension(name: str) -> PhysicalDimension:
"""创建电压维度 (L=2, M=1, T=-3, I=-1)"""
return create_physical_dimension(name, length_exp=2, mass_exp=1, time_exp=-3, current_exp=-1)
def create_power_dimension(name: str) -> PhysicalDimension:
"""创建功率维度 (L=2, M=1, T=-3)"""
return create_physical_dimension(name, length_exp=2, mass_exp=1, time_exp=-3)
def create_frequency_dimension(name: str) -> PhysicalDimension:
"""创建频率维度 (T=-1)"""
return create_physical_dimension(name, time_exp=-1)
+3 -3
View File
@@ -3,7 +3,7 @@
"""
from dataclasses import dataclass, field
from typing import Optional
from xml.dom.minidom import Document
from xml.dom.minidom import Document, Element
from ..core.base import Base, create_uuid, create_text_element
from ..core.constants import SW_ADDR_METHOD, SHORT_NAME, SECTION_TYPE
@@ -15,10 +15,10 @@ class SwAddrMethod(Base):
"""软件地址方法"""
section_type: Optional[SectionType] = None
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(SW_ADDR_METHOD)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.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))
+11 -11
View File
@@ -3,7 +3,7 @@
"""
from dataclasses import dataclass, field
from typing import Optional, List, TYPE_CHECKING
from xml.dom.minidom import Document
from xml.dom.minidom import Document, Element
from ..core.base import Base, create_uuid, create_text_element
from ..core.constants import (
@@ -17,8 +17,8 @@ from ..core.constants import (
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,
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
@@ -73,10 +73,10 @@ class ApplicationSwComponentType(AtomicComponentType):
if self.get_port(port.name) is None:
self.ports.append(port)
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(APPLICATION_SW_COMPONENT_TYPE)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.uuid)
element.appendChild(create_text_element(doc, SHORT_NAME, self.name))
if self.ports:
@@ -133,10 +133,10 @@ class PortPrototype(Base):
class RPortPrototype(PortPrototype):
"""接收端口(R-Port"""
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(R_PORT_PROTOTYPE)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.uuid)
element.appendChild(create_text_element(doc, SHORT_NAME, self.name))
if self.interface:
@@ -185,10 +185,10 @@ class RPortPrototype(PortPrototype):
class PPortPrototype(PortPrototype):
"""发送端口(P-Port"""
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(P_PORT_PROTOTYPE)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.uuid)
element.appendChild(create_text_element(doc, SHORT_NAME, self.name))
if self.interface:
@@ -219,10 +219,10 @@ class PRPortPrototype(PortPrototype):
"""双向端口(PR-Port"""
required_interface: Optional['SenderReceiverInterface'] = None
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(P_PORT_PROTOTYPE)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.uuid)
element.appendChild(create_text_element(doc, SHORT_NAME, self.name))
if self.interface:
+22 -14
View File
@@ -3,7 +3,7 @@ SWC 内部行为模块
"""
from dataclasses import dataclass, field
from typing import Optional, List, Union, TYPE_CHECKING
from xml.dom.minidom import Document
from xml.dom.minidom import Document, Element
from ..core.base import Base, create_uuid, create_text_element
from ..core.constants import (
@@ -26,7 +26,7 @@ class VariableAccess(Base):
port_prototype_ref: Optional[str] = None
target_data_prototype_ref: Optional[str] = None
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(ACCESSED_VARIABLE)
autosar_var_iref = doc.createElement(AUTOSAR_VARIABLE_IREF)
@@ -68,10 +68,10 @@ class RunnableEntity(Base):
"""添加变量访问"""
self.variable_accesses.append(access)
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(RUNNABLE_ENTITY)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.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()))
@@ -109,10 +109,10 @@ class RunnableEntity(Base):
class InitEvent(Base):
"""初始化事件"""
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(INIT_EVENT)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.uuid)
element.appendChild(create_text_element(doc, SHORT_NAME, self.name))
return element
@@ -122,10 +122,14 @@ class TimingEvent(Base):
"""定时事件"""
period: float = 0.01
def to_arxml(self, doc: Document) -> Document:
def __post_init__(self):
if self.period <= 0:
raise ValueError(f"period must be positive, got {self.period}")
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(TIMING_EVENT)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.uuid)
element.appendChild(create_text_element(doc, SHORT_NAME, self.name))
element.appendChild(create_text_element(doc, PERIOD, str(self.period)))
return element
@@ -136,10 +140,10 @@ class OperationInvokedEvent(Base):
"""操作调用事件"""
operation_ref: Optional[str] = None
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(OPERATION_INVOKED_EVENT)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.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)
@@ -154,10 +158,14 @@ class DataReceivedEvent(Base):
data_element_ref: Optional[str] = None
period: float = 0.01
def to_arxml(self, doc: Document) -> Document:
def __post_init__(self):
if self.period <= 0:
raise ValueError(f"period must be positive, got {self.period}")
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(DATA_RECEIVED_EVENT)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.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)
@@ -201,10 +209,10 @@ class SwcInternalBehavior(Base):
if self.get_event(event.name) is None:
self.events.append(event)
def to_arxml(self, doc: Document) -> Document:
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(SWC_INTERNAL_BEHAVIOR)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.uuid)
if self.symbol:
element.appendChild(create_text_element(doc, SYMBOL, self.symbol))
+7 -3
View File
@@ -3,7 +3,7 @@
"""
from dataclasses import dataclass, field
from typing import Optional
from xml.dom.minidom import Document
from xml.dom.minidom import Document, Element
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
@@ -16,10 +16,14 @@ class Unit(Base):
factor: int = 1
offset: int = 0
def to_arxml(self, doc: Document) -> Document:
def __post_init__(self):
if self.factor == 0:
raise ValueError("factor cannot be zero")
def to_arxml(self, doc: Document) -> Element:
"""转换为 ARXML 元素"""
element = doc.createElement(UNIT)
element.setAttribute("UUID", create_uuid())
element.setAttribute("UUID", self.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)))