3. 修复MODULE块中的重复定义

任务说明

A2L文件中,同一MEASUREMENT或CHARACTERISTIC对象在MODULE块内被重复定义时,会导致标定工具加载失败或对象访问歧义。本案例的目标是:检测MODULE块中重复的对象名称,并自动移除后出现的重复定义,确保每个对象在文件中唯一。

实现方案

使用Python脚本解析A2L文件,按MODULE块边界扫描,提取每个MEASUREMENT和CHARACTERISTIC的名称,记录首次出现位置,删除后续重复的完整定义块。脚本同时输出检测报告,便于人工确认。

完整代码

import re
import sys
from collections import OrderedDict

def parse_a2l(filepath):
    with open(filepath, 'r', encoding='latin-1') as f:
        content = f.read()
    return content

def find_module_blocks(content):
    """定位所有MODULE块的起止偏移"""
    blocks = []
    pattern = re.compile(r'^\s*MODULE\s+', re.MULTILINE)
    for match in pattern.finditer(content):
        start = match.start()
        # 找到对应的END_MODULE
        depth = 1
        pos = match.end()
        while depth > 0:
            end_match = re.search(r'^\s*(END_MODULE|MODULE)\b', content[pos:], re.MULTILINE)
            if not end_match:
                break
             = end_match.group(1)
            if  == 'MODULE':
                depth += 1
            else:
                depth -= 1
            pos += end_match.end()
        blocks.append((start, pos))
    return blocks

def extract_duplicates(module_text):
    """返回重复对象名列表及每个对象首次出现位置"""
    seen = OrderedDict()  # name -> (type, start_offset)
    duplicates = []
    # 匹配MEASUREMENT和CHARACTERISTIC块
    obj_pattern = re.compile(
        r'^\s*(?P<type>MEASUREMENT|CHARACTERISTIC)\s+(?P<name>\w+)\s*$',
        re.MULTILINE
    )
    for match in obj_pattern.finditer(module_text):
        obj_type = match.group('type')
        obj_name = match.group('name')
        if obj_name in seen:
            duplicates.append((obj_name, obj_type, match.start()))
        else:
            seen[obj_name] = (obj_type, match.start())
    return seen, duplicates

def remove_duplicate_blocks(content, module_blocks):
    """删除重复对象定义块,返回清理后的内容和报告"""
    # 收集所有需要删除的区间(按偏移排序)
    remove_intervals = []
    report_lines = []

    for mod_start, mod_end in module_blocks:
        module_text = content[mod_start:mod_end]
        seen, dups = extract_duplicates(module_text)
        for name, obj_type, dup_offset in dups:
            # 找到该重复定义的结束位置(下一个对象或END_MODULE)
            block_end = find_block_end(module_text, dup_offset)
            abs_start = mod_start + dup_offset
            abs_end = mod_start + block_end
            remove_intervals.append((abs_start, abs_end))
            report_lines.append(
                f"重复对象: {name} (类型: {obj_type}) @ 偏移 {abs_start}-{abs_end}"
            )

    # 按起始位置排序,从后往前删除
    remove_intervals.sort(key=lambda x: x[0])
    for start, end in reversed(remove_intervals):
        content = content[:start] + content[end:]

    return content, report_lines

def find_block_end(text, start_offset):
    """从对象定义起始处找到块结束(下一个对象或END_MODULE)"""
    # 从start_offset之后查找下一个对象定义或END_MODULE
    search_start = start_offset + 1
    next_obj = re.search(
        r'^\s*(?:MEASUREMENT|CHARACTERISTIC)\s+\w+\s*$|^\s*END_MODULE\b',
        text[search_start:],
        re.MULTILINE
    )
    if next_obj:
        return search_start + next_obj.start()
    return len(text)  # 到模块末尾

def main():
    if len(sys.argv) != 2:
        print("用法: python fix_duplicates.py <input.a2l>")
        sys.exit(1)

    input_file = sys.argv[1]
    content = parse_a2l(input_file)
    module_blocks = find_module_blocks(content)

    if not module_blocks:
        print("未找到MODULE块")
        sys.exit(1)

    cleaned_content, report = remove_duplicate_blocks(content, module_blocks)

    # 输出报告
    if report:
        print("检测到重复定义,已移除以下对象:")
        for line in report:
            print(f"  {line}")
    else:
        print("未检测到重复定义")

    # 写回文件
    output_file = input_file.replace('.a2l', '_fixed.a2l')
    with open(output_file, 'w', encoding='latin-1') as f:
        f.write(cleaned_content)
    print(f"清理完成,输出文件: {output_file}")

if __name__ == '__main__':
    main()

运行与验证

运行方式:

python fix_duplicates.py 你的文件.a2l

验证方法:

  1. 脚本执行后,终端会列出所有被移除的重复对象名称及位置;
  2. 检查输出文件(_fixed.a2l)中每个对象名称是否唯一:
grep -E "^(MEASUREMENT|CHARACTERISTIC)" 你的文件_fixed.a2l | awk '{print $2}' | sort | uniq -d

若该命令无输出,说明所有对象名称唯一,修复成功。

通过条件:脚本报告无重复对象,且上述grep验证命令不返回任何重复名称。

注意事项

  • 脚本按MODULE块独立检测,不同MODULE块中的同名对象不会被误删;
  • 删除逻辑保留首次出现的定义,移除后续重复块;
  • 若A2L文件包含if data等条件块,脚本按文本顺序处理,不会跨条件块判断。