<think>我们按照给定的规则来编写脚本。规则中提到的路径可能是XPath的简化表示,我们需要将其转换为正确的XPath表达式。同时,由于ARXML文件通常有命名空间,我们需要处理命名空间。
规则1:路径“EcuC/EcucConfigSet/EcucPduCollection/Pdus/(<+)<” 表示在Pdus下的所有容器,每个容器对应一个Frame(即PDU)。我们提取这些容器的SHORT-NAME作为Frame列。
规则2:在同一个Frame容器(父节点)中,查找ECUC-TEXTUAL-PARAM-VALUE节点,其VALUE为SEND或RECEIVE,作为Direction列。
规则3:在Frame的祖父节点(即Pdus的父节点是EcucPduCollection,再上一级是EcucConfigSet)下,查找路径“Com/ComConfig/ComSignalGroups/...”和“Com/ComConfig/ComSignals/...”,提取信号组和信号。注意规则3中的路径可能是从EcucConfigSet开始的相对路径。
但是,规则3中提到的路径是“Com/ComConfig/ComSignalGroups/(/+)<”和“Com/ComConfig/ComSignals/(/+)<”,这里可能表示在ComConfig容器下的ComSignalGroups和ComSignals容器下的所有容器(信号组和信号)。我们需要提取这些容器的SHORT-NAME,并标记类型(SignalGroup或Signal)。
规则4:将信号组和信号关联到对应的Frame和Direction。这里需要知道信号组和信号是如何与Frame关联的?根据规则,我们是在Frame的祖父节点(即EcucConfigSet)下查找信号组和信号,所以它们应该是同级的。但是,信号组和信号需要引用到PDU(Frame)上。通常,在信号组和信号的容器中会有一个引用指向PDU。因此,我们需要在信号组和信号的容器中查找对PDU的引用,从而确定它们属于哪个Frame。
然而,规则并没有明确说明如何关联,所以我们假设在信号组和信号的容器中有一个引用到PDU(即Frame)的节点。我们可以通过这个引用来匹配。
步骤:
1. 遍历所有Frame(PDU),记录Frame名称和方向。
2. 遍历所有信号组和信号,记录它们的名称和类型,并解析它们引用的PDU(Frame)名称。
3. 根据引用的PDU名称,将信号组和信号关联到Frame和Direction。
由于ARXML结构复杂,我们使用以下策略:
- 使用ElementTree解析XML,注意处理命名空间。
- 首先提取所有Frame(PDU)的信息,存储为字典:{pdu_name: direction}
- 然后提取所有信号组和信号,对于每个信号组/信号,找到它引用的PDU(通过查找REFERENCE-VALUE节点,且其DEFINITION-REF以"/ComIPdu"结尾?或者通过查找FRAME-REF?具体需要看ARXML结构)。然后根据引用的PDU名称去字典中查找方向。
但是,规则3的路径是“Com/ComConfig/ComSignalGroups/(/+)<”,这可能是从EcucConfigSet开始的路径。所以,我们可以在EcucConfigSet下查找ComConfig容器,然后在ComConfig容器下查找ComSignalGroups和ComSignals容器,再遍历它们的所有子容器。
注意:规则3中的路径可能是相对路径,所以我们需要从EcucConfigSet节点开始查找。
具体步骤:
1. 提取Frame(PDU):
- 路径:EcuC/EcucConfigSet/EcucPduCollection/Pdus/(下的所有容器)
- 每个容器就是一个PDU,其SHORT-NAME就是Frame名称。
- 在同一个容器中查找ECUC-TEXTUAL-PARAM-VALUE节点,且其DEFINITION-REF包含"ComDirection"(因为方向参数),然后取VALUE值。
2. 提取信号组和信号:
- 路径:从EcucConfigSet节点开始,查找Com/ComConfig/ComSignalGroups和Com/ComConfig/ComSignals下的所有容器。
- 对于每个容器,类型分别为SignalGroup和Signal。
- 在容器中查找对PDU的引用。通常,信号组和信号会通过一个引用(如REFERENCE-VALUE)指向一个PDU(即Frame)。我们需要解析这个引用,得到被引用的PDU的SHORT-NAME。
3. 关联:将信号组/信号与它们引用的PDU关联起来,从而得到对应的方向。
4. 输出到Excel:包含列:Signal(信号组或信号的名称),Type(SignalGroup或Signal),Frame(关联的Frame名称),Direction(方向)
注意:由于ARXML文件可能有多个,我们可以在当前目录遍历所有arxml文件。
考虑到命名空间,我们将动态获取命名空间。
由于规则中的路径描述可能不完全符合XPath,我们根据描述调整XPath表达式。
我们编写脚本如下:
步骤:
- 遍历当前目录下的所有arxml文件。
- 对于每个文件,解析并提取Frame(PDU)信息,存储为字典(Frame名称->方向)。
- 提取信号组和信号,并解析其引用的Frame名称,然后根据字典找到方向。
- 将结果收集到列表,然后输出到Excel。
注意:如果信号组或信号没有引用到任何Frame,则Frame列和Direction列为空。
我们假设引用节点是REFERENCE-VALUE,并且其DEFINITION-REF指示了引用类型(例如,指向PDU的引用)。为了得到引用的PDU名称,我们可以从引用值(VALUE节点)中提取,通常是PDU实例的路径,最后一部分是PDU的SHORT-NAME。
例如:REFERENCE-VALUE的VALUE为“/EcuC/EcucConfigSet/EcucPduCollection/Pdus/PDU_Engine”,则我们提取“PDU_Engine”作为Frame名称。
代码实现:
由于ARXML结构复杂,我们可能需要根据实际情况调整。以下是一个基础版本,我们将添加详细的日志来帮助调试。
我们将使用以下结构:
1. 提取所有PDU(Frame):
XPath: ".//*[SHORT-NAME='EcuC']/*[SHORT-NAME='EcucConfigSet']/*[SHORT-NAME='EcucPduCollection']/*[SHORT-NAME='Pdus']/*"
但更通用的方式可能是逐层查找,因为命名空间问题。
2. 对于每个PDU容器,获取其SHORT-NAME,然后查找其下的参数值(ECUC-TEXTUAL-PARAM-VALUE)中DEFINITION-REF包含"ComDirection"的,获取其VALUE。
3. 提取信号组和信号:
信号组路径:".//*[SHORT-NAME='Com']/*[SHORT-NAME='ComConfig']/*[SHORT-NAME='ComSignalGroups']/*"
信号路径: ".//*[SHORT-NAME='Com']/*[SHORT-NAME='ComConfig']/*[SHORT-NAME='ComSignals']/*"
4. 对于每个信号组/信号容器,查找其下的REFERENCE-VALUE节点(可能多个),其中DEFINITION-REF包含"ComIPdu"(表示引用到PDU)。然后从VALUE中提取引用的PDU名称(取最后一段)。
5. 关联方向。
注意:由于ARXML可能使用不同的命名空间,我们将动态处理命名空间。
由于没有具体的ARXML文件,我们只能根据常见结构编写,可能需要根据实际文件调整。
我们开始编写代码,并添加详细的日志输出。
</think>
以下是根据您的规则编写的Python脚本,用于解析ARXML文件并提取Frame、信号组、信号及其方向信息,并将结果输出到Excel文件:
```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:ECUC-CONTAINER-VALUE[ns:SHORT-NAME='EcucPduCollection']/ns:SUB-CONTAINERS/ns:ECUC-CONTAINER-VALUE/ns:SUB-CONTAINERS/ns:ECUC-CONTAINER-VALUE", namespaces=ns)
if not frame_nodes:
logger.warning("未找到Frame节点")
return frame_info
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:
# 检查参数名称是否为ComDirection
def_ref_node = param.find("./ns:DEFINITION-REF", namespaces=ns)
if def_ref_node is not None and def_ref_node.text and def_ref_node.text.endswith("/ComDirection"):
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("开始提取信号组信息...")
# 查找ComConfig容器
com_config = root.find(".//ns:ECUC-CONTAINER-VALUE[ns:SHORT-NAME='Com']/ns:SUB-CONTAINERS/ns:ECUC-CONTAINER-VALUE[ns:SHORT-NAME='ComConfig']", namespaces=ns)
if com_config is None:
logger.warning("未找到ComConfig容器")
return results
# 查找信号组容器
signal_groups_container = com_config.find("./ns:SUB-CONTAINERS/ns:ECUC-CONTAINER-VALUE[ns:SHORT-NAME='ComSignalGroups']", namespaces=ns)
if signal_groups_container:
signal_group_nodes = signal_groups_container.findall("./ns:SUB-CONTAINERS/ns:ECUC-CONTAINER-VALUE", 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_name = "N/A"
ref_nodes = group.findall(".//ns:REFERENCE-VALUE", namespaces=ns)
for ref in ref_nodes:
# 检查引用类型是否为IPDU
def_ref = ref.find("./ns:DEFINITION-REF", namespaces=ns)
if def_ref is not None and def_ref.text and def_ref.text.endswith("/ComIPdu"):
value_node = ref.find("./ns:VALUE", namespaces=ns)
if value_node is not None and value_node.text:
# 从引用路径中提取Frame名称
match = re.search(r'/([^/]+)$', value_node.text)
if match:
frame_name = match.group(1)
break
# 获取方向信息
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("开始提取信号信息...")
signals_container = com_config.find("./ns:SUB-CONTAINERS/ns:ECUC-CONTAINER-VALUE[ns:SHORT-NAME='ComSignals']", namespaces=ns)
if signals_container:
signal_nodes = signals_container.findall("./ns:SUB-CONTAINERS/ns:ECUC-CONTAINER-VALUE", 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_name = "N/A"
ref_nodes = signal.findall(".//ns:REFERENCE-VALUE", namespaces=ns)
for ref in ref_nodes:
# 检查引用类型是否为IPDU
def_ref = ref.find("./ns:DEFINITION-REF", namespaces=ns)
if def_ref is not None and def_ref.text and def_ref.text.endswith("/ComIPdu"):
value_node = ref.find("./ns:VALUE", namespaces=ns)
if value_node is not None and value_node.text:
# 从引用路径中提取Frame名称
match = re.search(r'/([^/]+)$', value_node.text)
if match:
frame_name = match.group(1)
break
# 获取方向信息
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)
logger.info(f"结果已保存到: {output_file}")
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}")
if __name__ == "__main__":
main()
```
### 脚本功能说明
#### 规则1: 提取Frame信息
```python
def extract_frame_info(root, ns):
# 规则1: 查找Frame路径
frame_nodes = root.findall(
".//ns:ECUC-CONTAINER-VALUE[ns:SHORT-NAME='EcucPduCollection']"
"/ns:SUB-CONTAINERS/ns:ECUC-CONTAINER-VALUE"
"/ns:SUB-CONTAINERS/ns:ECUC-CONTAINER-VALUE",
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"
# 规则2: 查找方向信息
direction = "N/A"
param_nodes = frame.findall(".//ns:ECUC-TEXTUAL-PARAM-VALUE", namespaces=ns)
for param in param_nodes:
# 检查参数名称是否为ComDirection
def_ref_node = param.find("./ns:DEFINITION-REF", namespaces=ns)
if def_ref_node and def_ref_node.text.endswith("/ComDirection"):
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
```
- **精确路径匹配**:
- `EcuC/EcucConfigSet/EcucPduCollection/Pdus/` → `EcucPduCollection`容器
- 查找所有`ECUC-CONTAINER-VALUE`作为Frame节点
- **方向参数检查**:
- 通过`DEFINITION-REF`检查参数名称是否为`ComDirection`
- 确保只提取有效的方向值("SEND"或"RECEIVE")
#### 规则3: 提取信号组和信号信息
```python
def extract_signal_info(root, ns, frame_info):
# 规则3: 查找ComConfig容器
com_config = root.find(
".//ns:ECUC-CONTAINER-VALUE[ns:SHORT-NAME='Com']"
"/ns:SUB-CONTAINERS/ns:ECUC-CONTAINER-VALUE[ns:SHORT-NAME='ComConfig']",
namespaces=ns
)
# 查找信号组容器
signal_groups_container = com_config.find(
"./ns:SUB-CONTAINERS/ns:ECUC-CONTAINER-VALUE[ns:SHORT-NAME='ComSignalGroups']",
namespaces=ns
)
# 查找信号容器
signals_container = com_config.find(
"./ns:SUB-CONTAINERS/ns:ECUC-CONTAINER-VALUE[ns:SHORT-NAME='ComSignals']",
namespaces=ns
)
# 提取信号组信息
for group in signal_group_nodes:
# 获取信号组名称
name_node = group.find("./ns:SHORT-NAME", namespaces=ns)
group_name = name_node.text
# 查找关联的Frame
for ref in ref_nodes:
# 检查引用类型是否为IPDU
if def_ref.text.endswith("/ComIPdu"):
# 从引用路径中提取Frame名称
match = re.search(r'/([^/]+)$', value_node.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")
})
```
1. **精确路径匹配**:
- `Com/ComConfig/ComSignalGroups/` → 信号组容器
- `Com/ComConfig/ComSignals/` → 信号容器
2. **类型标记**:
- SignalGroup:来自ComSignalGroups容器
- Signal:来自ComSignals容器
3. **Frame关联**:
- 查找`REFERENCE-VALUE`节点
- 检查`DEFINITION-REF`以`/ComIPdu`结尾
- 使用正则表达式`r'/([^/]+)$'`提取Frame名称
#### 规则4: 标记信号组/信号到Frame
```python
# 获取Frame的方向信息
direction = frame_info.get(frame_name, "N/A")
# 添加到结果
results.append({
"Signal": group_name, # 或signal_name
"Type": "SignalGroup", # 或"Signal"
"Frame": frame_name,
"Direction": direction
})
```
- 使用之前提取的frame_info字典获取方向
- 每个信号组/信号都关联到对应的Frame和方向
- 如果找不到Frame信息,使用"N/A"
#### Excel输出结构
```python
# DataFrame列定义
columns = ["Signal", "Type", "Frame", "Direction"]
# Excel输出包含三个工作表:
# 1. SignalList: 信号/信号组列表
# - Signal: 信号组或信号名称
# - Type: SignalGroup或Signal
# - Frame: 关联的Frame名称
# - Direction: 方向(SEND/RECEIVE/N/A)
# 2. FileStats: 文件处理统计
# 3. DirectionStats: 方向分布统计
```
### 脚本执行流程
1. **搜索ARXML文件**:
- 在当前目录查找所有.arxml文件
- 记录找到的文件数量
2. **处理每个文件**:
- 解析XML并处理命名空间
- 提取Frame信息(名称+方向)
- 提取信号组/信号信息并关联Frame
3. **生成报告**:
- SignalList:所有信号/信号组及其关联信息
- FileStats:每个文件处理的条目数
- DirectionStats:方向分布统计
4. **保存结果**:
- 输出到Excel文件`arxml_analysis_report.xlsx`
### 使用示例
**ARXML结构示例**:
```xml
<!-- Frame定义 -->
<ECUC-CONTAINER-VALUE>
<SHORT-NAME>EcucPduCollection</SHORT-NAME>
<SUB-CONTAINERS>
<ECUC-CONTAINER-VALUE>
<SHORT-NAME>Pdus</SHORT-NAME>
<SUB-CONTAINERS>
<ECUC-CONTAINER-VALUE>
<SHORT-NAME>PDU_Engine</SHORT-NAME>
<PARAMETER-VALUES>
<ECUC-TEXTUAL-PARAM-VALUE>
<DEFINITION-REF>/AUTOSAR/EcucDefs/Com/ComDirection</DEFINITION-REF>
<VALUE>SEND</VALUE>
</ECUC-TEXTUAL-PARAM-VALUE>
</PARAMETER-VALUES>
</ECUC-CONTAINER-VALUE>
</SUB-CONTAINERS>
</ECUC-C