<think>根据规则,我们需要:
1. 从固定路径找到Frame(即PDU)信息,路径为:EcuC/EcucConfigSet/EcucPduCollection/Pdus/下的容器(每个容器代表一个Frame)
2. 在每个Frame容器中,查找方向参数(ECUC-TEXTUAL-PARAM-VALUE的值是SEND或RECEIVE)
3. 然后,我们要在Frame的祖父节点(即EcucPduCollection的兄弟节点)中查找ComConfig容器,并在ComConfig容器下查找信号组(ComSignalGroups)和信号(ComSignals)
4. 信号组和信号会通过引用指向Frame,我们需要根据这个引用关系将信号组/信号与Frame及方向关联
但是注意,规则3中描述的路径是“Com/ComConfig/ComSignalGroups/([^/]+)<”和"Com/ComConfig/ComSignals/([^/]+)<",这里我们理解为在同一个ARXML文件中,ComConfig容器下分别有ComSignalGroups和ComSignals容器,它们包含了信号组和信号。
然而,在ARXML中,信号组和信号通常是通过引用与PDU(Frame)关联的。因此,我们需要:
- 首先提取所有PDU(Frame)信息,包括名称和方向。
- 然后提取所有信号组和信号,并记录它们引用的PDU。
- 最后将信号组/信号与PDU信息关联起来。
由于规则要求利用Frame位置在祖父节点中查找信号组和信号,这里我的理解是:Frame的祖父节点是EcucPduCollection,而ComConfig容器可能是EcucPduCollection的兄弟节点。因此,我们可以先定位到EcucPduCollection,然后找到其父节点(即EcuC/EcucConfigSet),再在父节点下查找ComConfig容器。
具体步骤:
步骤1:解析ARXML文件,定位到EcucPduCollection容器(即Pdus的父容器)。
步骤2:遍历Pdus下的每个PDU容器,提取PDU名称和方向。
步骤3:定位到ComConfig容器(在EcucPduCollection的兄弟节点中)。
步骤4:在ComConfig容器下遍历ComSignalGroups和ComSignals,提取信号组和信号,并记录它们引用的PDU。
步骤5:将信号组/信号与对应的PDU信息(包括方向)关联。
注意:由于ARXML结构复杂,我们使用递归或XPath来定位节点。同时,由于ARXML可能有命名空间,我们需要处理命名空间。
以下是根据上述规则编写的脚本:
</think>
以下是根据您描述的规则编写的Python脚本,用于解析ARXML文件并提取Frame信息、信号组和信号信息:
```python
import os
import xml.etree.ElementTree as ET
import pandas as pd
import re
import logging
from collections import defaultdict
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.StreamHandler(),
logging.FileHandler('arxml_processor.log')
]
)
logger = logging.getLogger(__name__)
def extract_frame_info(root, ns):
"""
提取Frame信息(规则1和规则2)
:param root: XML根节点
:param ns: 命名空间
:return: 字典 {frame_name: direction}
"""
frame_info = {}
logger.info("开始提取Frame信息...")
# 规则1: 查找Frame路径
frame_nodes = root.findall(".//ns:PDUS/ns:*", namespaces=ns)
logger.info(f"找到 {len(frame_nodes)} 个Frame节点")
for frame in frame_nodes:
# 获取Frame名称
name_node = frame.find("./ns:SHORT-NAME", namespaces=ns)
if name_node is None or not name_node.text:
continue
frame_name = name_node.text
# 规则2: 查找方向信息
direction = "N/A"
param_nodes = frame.findall(".//ns:ECUC-TEXTUAL-PARAM-VALUE", namespaces=ns)
for param in param_nodes:
value_node = param.find("./ns:VALUE", namespaces=ns)
if value_node is not None and value_node.text:
value_text = value_node.text.strip().upper()
if value_text in ["SEND", "RECEIVE"]:
direction = value_text
break
frame_info[frame_name] = direction
logger.info(f"找到Frame: {frame_name} ({direction})")
return frame_info
def extract_signal_info(root, ns, frame_info):
"""
提取信号组和信号信息(规则3和规则4)
:param root: XML根节点
:param ns: 命名空间
:param frame_info: Frame信息字典
:return: 结果列表
"""
results = []
# 规则3: 查找信号组信息
logger.info("开始提取信号组信息...")
signal_group_nodes = root.findall(".//ns:COM-SIGNAL-GROUPS/ns:*", namespaces=ns)
logger.info(f"找到 {len(signal_group_nodes)} 个信号组节点")
for group in signal_group_nodes:
# 获取信号组名称
name_node = group.find("./ns:SHORT-NAME", namespaces=ns)
if name_node is None or not name_node.text:
continue
group_name = name_node.text
# 查找关联的Frame
frame_ref = group.find(".//ns:FRAME-REF", namespaces=ns)
frame_name = "N/A"
if frame_ref is not None:
# 从引用中提取Frame名称
ref_text = frame_ref.text.strip() if frame_ref.text else ""
if ref_text:
# 使用正则提取Frame名称
match = re.search(r'/([^/]+)$', ref_text)
if match:
frame_name = match.group(1)
# 获取方向信息
direction = frame_info.get(frame_name, "N/A")
results.append({
"Signal": group_name,
"Type": "SignalGroup",
"Frame": frame_name,
"Direction": direction
})
logger.info(f"信号组: {group_name} -> Frame: {frame_name} ({direction})")
# 规则3: 查找信号信息
logger.info("开始提取信号信息...")
signal_nodes = root.findall(".//ns:COM-SIGNALS/ns:*", namespaces=ns)
logger.info(f"找到 {len(signal_nodes)} 个信号节点")
for signal in signal_nodes:
# 获取信号名称
name_node = signal.find("./ns:SHORT-NAME", namespaces=ns)
if name_node is None or not name_node.text:
continue
signal_name = name_node.text
# 查找关联的Frame
frame_ref = signal.find(".//ns:FRAME-REF", namespaces=ns)
frame_name = "N/A"
if frame_ref is not None:
# 从引用中提取Frame名称
ref_text = frame_ref.text.strip() if frame_ref.text else ""
if ref_text:
# 使用正则提取Frame名称
match = re.search(r'/([^/]+)$', ref_text)
if match:
frame_name = match.group(1)
# 获取方向信息
direction = frame_info.get(frame_name, "N/A")
results.append({
"Signal": signal_name,
"Type": "Signal",
"Frame": frame_name,
"Direction": direction
})
logger.info(f"信号: {signal_name} -> Frame: {frame_name} ({direction})")
return results
def process_arxml_file(file_path):
"""
处理单个ARXML文件
:param file_path: 文件路径
:return: 提取的结果列表
"""
logger.info(f"开始处理文件: {file_path}")
results = []
try:
# 解析XML文件
tree = ET.parse(file_path)
root = tree.getroot()
# 获取命名空间
ns = {}
if '}' in root.tag:
ns_uri = root.tag.split('}')[0].split('{')[1]
ns = {'ns': ns_uri}
# 提取Frame信息
frame_info = extract_frame_info(root, ns)
# 提取信号组和信号信息
signal_info = extract_signal_info(root, ns, frame_info)
results.extend(signal_info)
logger.info(f"处理完成: 提取到 {len(signal_info)} 个条目")
except Exception as e:
logger.error(f"处理文件时出错: {str(e)}", exc_info=True)
return results
def main():
logger.info("开始ARXML处理任务")
# 在当前目录搜索所有ARXML文件
arxml_files = [f for f in os.listdir('.') if f.lower().endswith('.arxml')]
if not arxml_files:
logger.warning("未找到任何ARXML文件")
return
logger.info(f"找到 {len(arxml_files)} 个ARXML文件")
# 收集所有结果
all_results = []
file_stats = {}
for file in arxml_files:
file_results = process_arxml_file(file)
all_results.extend(file_results)
file_stats[file] = len(file_results)
if not all_results:
logger.warning("未提取到任何有效数据")
return
# 创建DataFrame
df = pd.DataFrame(all_results, columns=["Signal", "Type", "Frame", "Direction"])
# 添加文件统计
stats_df = pd.DataFrame({
"FileName": list(file_stats.keys()),
"SignalCount": list(file_stats.values())
})
# 添加方向统计
direction_stats = pd.DataFrame({
"Direction": ["SEND", "RECEIVE", "N/A"],
"Count": [
sum(1 for r in all_results if r["Direction"] == "SEND"),
sum(1 for r in all_results if r["Direction"] == "RECEIVE"),
sum(1 for r in all_results if r["Direction"] not in ["SEND", "RECEIVE"])
]
})
# 保存到Excel
output_file = "arxml_analysis_report.xlsx"
logger.info(f"正在保存结果到: {output_file}")
try:
with pd.ExcelWriter(output_file) as writer:
df.to_excel(writer, sheet_name='SignalList', index=False)
stats_df.to_excel(writer, sheet_name='FileStats', index=False)
direction_stats.to_excel(writer, sheet_name='DirectionStats', index=False)
except Exception as e:
logger.error(f"保存Excel时出错: {str(e)}")
return
# 最终统计
signal_groups = sum(1 for r in all_results if r["Type"] == "SignalGroup")
signals = sum(1 for r in all_results if r["Type"] == "Signal")
frames = len(set(r["Frame"] for r in all_results))
logger.info(f"\n处理完成! 共处理 {len(arxml_files)} 个文件")
logger.info(f"提取到 {len(all_results)} 个条目:")
logger.info(f" 信号组: {signal_groups}")
logger.info(f" 信号: {signals}")
logger.info(f" 唯一Frame: {frames}")
logger.info(f"结果已保存到: {output_file}")
if __name__ == "__main__":
main()
```
### 脚本功能说明
#### 规则1: 提取Frame信息
```python
def extract_frame_info(root, ns):
# 查找Frame路径
frame_nodes = root.findall(".//ns:PDUS/ns:*", namespaces=ns)
for frame in frame_nodes:
# 获取Frame名称
name_node = frame.find("./ns:SHORT-NAME", namespaces=ns)
frame_name = name_node.text if name_node else "N/A"
# 查找方向信息
direction = "N/A"
param_nodes = frame.findall(".//ns:ECUC-TEXTUAL-PARAM-VALUE", namespaces=ns)
for param in param_nodes:
value_node = param.find("./ns:VALUE", namespaces=ns)
if value_node and value_node.text:
value_text = value_node.text.strip().upper()
if value_text in ["SEND", "RECEIVE"]:
direction = value_text
```
- **路径匹配**:使用XPath `".//ns:PDUS/ns:*"` 查找所有Frame节点
- **Frame名称**:从`SHORT-NAME`节点获取
- **方向信息**:遍历`ECUC-TEXTUAL-PARAM-VALUE`节点,查找"SEND"或"RECEIVE"
#### 规则2: 提取方向信息
```python
# 在Frame节点中查找方向参数
param_nodes = frame.findall(".//ns:ECUC-TEXTUAL-PARAM-VALUE", namespaces=ns)
for param in param_nodes:
value_node = param.find("./ns:VALUE", namespaces=ns)
if value_node and value_node.text:
value_text = value_node.text.strip().upper()
if value_text in ["SEND", "RECEIVE"]:
direction = value_text
```
- 在Frame节点的子节点中查找方向参数
- 只接受"SEND"或"RECEIVE"作为有效方向
#### 规则3: 提取信号组和信号信息
```python
# 信号组提取
signal_group_nodes = root.findall(".//ns:COM-SIGNAL-GROUPS/ns:*", namespaces=ns)
for group in signal_group_nodes:
name_node = group.find("./ns:SHORT-NAME", namespaces=ns)
group_name = name_node.text if name_node else "N/A"
# 查找关联的Frame
frame_ref = group.find(".//ns:FRAME-REF", namespaces=ns)
frame_name = "N/A"
if frame_ref and frame_ref.text:
match = re.search(r'/([^/]+)$', frame_ref.text)
if match:
frame_name = match.group(1)
# 设置类型为SignalGroup
results.append({
"Signal": group_name,
"Type": "SignalGroup",
"Frame": frame_name,
"Direction": frame_info.get(frame_name, "N/A")
})
# 信号提取(类似处理)
signal_nodes = root.findall(".//ns:COM-SIGNALS/ns:*", namespaces=ns)
# ...相同逻辑处理信号...
```
- **信号组路径**:`".//ns:COM-SIGNAL-GROUPS/ns:*"`
- **信号路径**:`".//ns:COM-SIGNALS/ns:*"`
- **Frame关联**:从`FRAME-REF`节点提取Frame名称
- **类型标记**:
- SignalGroup:来自COM-SIGNAL-GROUPS
- Signal:来自COM-SIGNALS
#### 规则4: 标记信号组/信号到Frame
```python
# 获取Frame的方向信息
direction = frame_info.get(frame_name, "N/A")
# 添加到结果
results.append({
"Signal": group_name,
"Type": "SignalGroup", # 或"Signal"
"Frame": frame_name,
"Direction": direction
})
```
- 使用之前提取的frame_info字典获取方向
- 每个信号组/信号都关联到对应的Frame和方向
#### 输出结构
```python
# DataFrame列定义
columns = ["Signal", "Type", "Frame", "Direction"]
# Excel输出包含三个工作表:
# 1. SignalList: 信号/信号组列表
# 2. FileStats: 文件处理统计
# 3. DirectionStats: 方向分布统计
```
### 脚本执行流程
1. **搜索ARXML文件**:在当前目录查找所有.arxml文件
2. **处理每个文件**:
- 解析XML并处理命名空间
- 提取Frame信息(名称+方向)
- 提取信号组/信号信息并关联Frame
3. **生成报告**:
- SignalList:所有信号/信号组及其关联信息
- FileStats:每个文件处理的条目数
- DirectionStats:方向分布统计
4. **保存结果**:输出到Excel文件
### 日志输出示例
```
2023-10-05 14:30:22,123 - INFO - 开始处理文件: vehicle_network.arxml
2023-10-05 14:30:22,456 - INFO - 找到 15 个Frame节点
2023-10-05 14:30:22,789 - INFO - 找到Frame: EngineFrame (SEND)
2023-10-05 14:30:23,012 - INFO - 找到Frame: BrakeFrame (RECEIVE)
2023-10-05 14:30:23,456 - INFO - 找到 8 个信号组节点
2023-10-05 14:30:23,789 - INFO - 信号组: EngineSignals -> Frame: EngineFrame (SEND)
2023-10-05 14:30:24,123 - INFO - 找到 32 个信号节点
2023-10-05 14:30:24,456 - INFO - 信号: EngineRPM -> Frame: EngineFrame (SEND)
2023-10-05 14:30:24,789 - INFO - 处理完成: 提取到 40 个条目
```
### 注意事项
1. 脚本假设ARXML文件符合AUTOSAR标准结构
2. 处理了XML命名空间问题
3. 使用正则表达式提取Frame名称
4. 包含详细的错误处理和日志记录
5. 输出结果包含完整的统计信息