08_apollo_scripts_scripts子模块软件架构分析

08_apollo_scripts_scripts子模块软件架构分析

1. 概述

  Apollo scripts子模块是Apollo自动驾驶平台的核心脚本集合,提供完整的开发、构建、测试和运行支持。采用模块化架构设计,将脚本按照功能划分为多个类别,包括基础环境配置、构建工具、模块管理、测试验证、数据处理等,实现开发流程自动化与标准化,简化平台使用复杂度,为开发者提供高效便捷的开发体验。

2. 软件架构图

  Apollo scripts子模块采用功能导向的架构设计,将脚本按照功能和使用场景划分为多个模块,各模块之间通过清晰的接口和依赖关系进行交互。整个架构遵循高内聚、低耦合的设计原则,确保了脚本系统的稳定性和可维护性。

graph TB subgraph "scripts子模块" subgraph "基础环境模块" B1[apollo_base.sh]:::key B2[apollo.bashrc]:::key B3[env.sh]:::key B4[common.bashrc]:::key end subgraph "构建工具模块" C1[apollo_buildify.sh]:::key C2[apollo_clean.sh]:::key C3[apollo_format.sh]:::key C4[apollo_lint.sh]:::key C5[apollo_release.sh]:::key end subgraph "模块管理模块" M1[apollo_action.sh]:::key M2[cyber_launch.sh]:::key M3[dreamview.sh]:::key M4[perception.sh]:::key M5[planning.sh]:::key end subgraph "测试验证模块" T1[apollo_ci.sh]:::key T2[apollo_coverage.sh]:::key T3[apollo_rule_check.sh]:::key T4[msf_local_evaluation.sh]:::key end subgraph "数据处理模块" D1[data_record.py]:::key D2[dump_record.sh]:::key D3[filter_planning.sh]:::key D4[record_message.py]:::key D5[record_bag.py]:::key end subgraph "工具脚本模块" U1[apollo_docs.sh]:::key U2[generate_proto_build_file.sh]:::key U3[install_gcc.sh]:::key U4[yapf.sh]:::key U5[buildifier.sh]:::key end subgraph "传感器模块" S1[camera.sh]:::key S2[canbus.sh]:::key S3[velodyne.sh]:::key S4[gps.sh]:::key S5[radar.sh]:::key end subgraph "扩展脚本模块" E1[bootstrap.sh]:::key E2[docker_start_user.sh]:::key E3[ota.sh]:::key E4[switch_vehicle.sh]:::key end end subgraph "外部依赖" X1[Shell解释器]:::key X2[Python解释器]:::key X3[Bazel构建系统]:::key X4[CyberRT框架]:::key X5[Docker引擎]:::key end subgraph "用户接口" Y1[命令行接口]:::key Y2[自动化脚本]:::key Y3[IDE集成]:::key end B1 --> X1 B2 --> X1 B3 --> X1 B4 --> X1 C1 --> X1 C1 --> X3 C2 --> X1 C2 --> X3 C3 --> X1 C3 --> X3 C4 --> X1 C4 --> X3 C5 --> X1 C5 --> X3 M1 --> X1 M1 --> X4 M2 --> X1 M2 --> X4 M3 --> X1 M3 --> X4 M4 --> X1 M4 --> X4 M5 --> X1 M5 --> X4 T1 --> X1 T1 --> X3 T2 --> X1 T2 --> X3 T3 --> X1 T3 --> X3 T4 --> X1 T4 --> X4 D1 --> X2 D2 --> X1 D3 --> X1 D4 --> X2 D5 --> X2 U1 --> X1 U1 --> X3 U2 --> X1 U3 --> X1 U4 --> X1 U4 --> X2 U5 --> X1 U5 --> X3 S1 --> X1 S1 --> X4 S2 --> X1 S2 --> X4 S3 --> X1 S3 --> X4 S4 --> X1 S4 --> X4 S5 --> X1 S5 --> X4 E1 --> X1 E1 --> X3 E2 --> X1 E2 --> X5 E3 --> X1 E3 --> X4 E4 --> X1 E4 --> X4 Y1 --> B1 Y1 --> C1 Y1 --> M1 Y1 --> T1 Y1 --> D1 Y1 --> U1 Y1 --> S1 Y1 --> E1 Y2 --> B1 Y2 --> C1 Y2 --> M1 Y2 --> T1 Y2 --> D1 Y2 --> U1 Y2 --> S1 Y2 --> E1 Y3 --> B1 Y3 --> C1 Y3 --> M1 Y3 --> T1 Y3 --> D1 Y3 --> U1 Y3 --> S1 Y3 --> E1 classDef key fill:#e1f5fe

3. 调用流程图

  Apollo scripts子模块的调用流程涵盖了从环境准备到系统运行的完整开发周期,包括环境配置、项目构建、模块启动和数据处理等多个阶段。整个流程设计清晰,步骤明确,便于开发者理解和使用。

3.1. 开发环境配置流程

sequenceDiagram participant User as 开发者 participant Scripts as scripts子模块 participant Shell as Shell解释器 participant Env as 开发环境 User->>Scripts: 执行apollo.bashrc Scripts->>Shell: 加载基础配置 Shell->>Env: 设置环境变量 Env-->>Shell: 环境变量设置完成 Shell->>Shell: 配置命令自动补全 Shell->>Shell: 设置别名 Shell-->>User: 环境配置完成

3.2. 项目构建流程

sequenceDiagram participant User as 开发者 participant Scripts as scripts子模块 participant Bazel as Bazel构建系统 participant Project as Apollo项目 User->>Scripts: 执行apollo_buildify.sh Scripts->>Bazel: 生成构建文件 Bazel-->>Scripts: 构建文件生成完成 User->>Scripts: 执行apollo_format.sh Scripts->>Project: 格式化代码 Project-->>Scripts: 代码格式化完成 User->>Scripts: 执行apollo_lint.sh Scripts->>Project: 检查代码质量 Project-->>Scripts: 代码检查完成 User->>Scripts: 执行apollo_build Scripts->>Bazel: 启动构建 Bazel->>Project: 编译源代码 Project-->>Bazel: 编译完成 Bazel-->>Scripts: 构建完成 Scripts-->>User: 构建结果返回

3.3. 模块启动流程

sequenceDiagram participant User as 开发者 participant Scripts as scripts子模块 participant Cyber as CyberRT框架 participant Module as Apollo模块 participant Dreamview as Dreamview User->>Scripts: 执行dreamview.sh Scripts->>Dreamview: 启动Dreamview Dreamview-->>Scripts: Dreamview启动完成 User->>Scripts: 执行apollo_action.sh start Scripts->>Cyber: 启动CyberRT框架 Cyber-->>Scripts: CyberRT启动完成 Scripts->>Module: 启动感知模块 Module-->>Scripts: 感知模块启动完成 Scripts->>Module: 启动定位模块 Module-->>Scripts: 定位模块启动完成 Scripts->>Module: 启动规划模块 Module-->>Scripts: 规划模块启动完成 Scripts->>Module: 启动控制模块 Module-->>Scripts: 控制模块启动完成 Scripts-->>User: 所有模块启动完成

3.4. 数据处理流程

sequenceDiagram participant User as 开发者 participant Scripts as scripts子模块 participant Record as 数据记录 participant Process as 数据处理 participant Analyze as 数据分析 User->>Scripts: 执行data_record.py Scripts->>Record: 开始录制数据 Record-->>Scripts: 数据录制开始 User->>Scripts: 执行record_message.py Scripts->>Record: 开始录制消息 Record-->>Scripts: 消息录制开始 User->>Scripts: 停止录制 Scripts->>Record: 停止录制 Record-->>Scripts: 数据保存完成 User->>Scripts: 执行dump_record.sh Scripts->>Process: 回放录制数据 Process-->>Scripts: 数据回放完成 User->>Scripts: 执行filter_planning.sh Scripts->>Process: 过滤规划数据 Process-->>Scripts: 数据过滤完成 User->>Scripts: 执行performance_parse.py Scripts->>Analyze: 分析性能数据 Analyze-->>Scripts: 分析完成 Scripts-->>User: 分析结果返回

3.5. 测试验证流程

sequenceDiagram participant User as 开发者 participant Scripts as scripts子模块 participant Test as 测试系统 participant Coverage as 覆盖率分析 participant CI as CI系统 User->>Scripts: 执行apollo_rule_check.sh Scripts->>Test: 执行规则检查 Test-->>Scripts: 规则检查完成 User->>Scripts: 执行apollo_ci.sh Scripts->>CI: 执行CI测试 CI->>Test: 执行单元测试 Test-->>CI: 单元测试完成 CI->>Test: 执行集成测试 Test-->>CI: 集成测试完成 CI-->>Scripts: CI测试完成 User->>Scripts: 执行apollo_coverage.sh Scripts->>Coverage: 执行覆盖率分析 Coverage-->>Scripts: 覆盖率分析完成 Scripts-->>User: 测试结果返回

4. 详细UML类图

  Apollo scripts子模块的UML类图展示了脚本系统的核心组件、它们之间的关系以及各自的职责。类图设计遵循面向对象的设计原则,将脚本的功能抽象为类和方法,便于理解和维护。

4.1. 核心脚本类图

classDiagram class Script { <<abstract>> +execute() +get_name(): string +get_description(): string +get_usage(): string -name: string -description: string -usage: string } class BaseScript { +set_environment() +load_config() +validate_environment(): bool -environment: Environment -config: Config } class BuildScript { +build() +clean() +format() +lint() -bazel: Bazel -project: Project } class ModuleScript { +start() +stop() +restart() +status(): ModuleStatus -module: Module -cyber: CyberRT } class DataScript { +record() +playback() +filter() +analyze() -data_manager: DataManager -data_processor: DataProcessor } class TestScript { +run_tests() +coverage() +check_rules() -test_manager: TestManager -coverage_analyzer: CoverageAnalyzer } class Environment { +set_variable(string, string) +get_variable(string): string +load_profile(string) -variables: map<string, string> } class Config { +load_config(string): Config +get_value(string): string +set_value(string, string) -config_file: string -values: map<string, string> } class Bazel { +build(string) +clean() +test(string) +coverage(string) -workspace: string } Script <|-- BaseScript BaseScript <|-- BuildScript BaseScript <|-- ModuleScript BaseScript <|-- DataScript BaseScript <|-- TestScript BaseScript --> Environment BaseScript --> Config BuildScript --> Bazel ModuleScript --> Module ModuleScript --> CyberRT DataScript --> DataManager DataScript --> DataProcessor TestScript --> TestManager TestScript --> CoverageAnalyzer

4.2. 脚本执行类图

classDiagram class ScriptExecutor { +execute_script(Script): ExecutionResult +parse_args(list<string>): Args +validate_args(Args, Script): bool -script_factory: ScriptFactory -arg_parser: ArgParser } class ScriptFactory { +create_script(string): Script +get_all_scripts(): list<Script> -scripts: map<string, Script> } class ArgParser { +parse(list<string>): Args +add_option(string, string, string) +get_option(string): string -options: map<string, Option> } class Args { +get(string): string +get_list(string): list<string> +has(string): bool -args: map<string, string> -list_args: map<string, list<string>> } class ExecutionResult { +is_success(): bool +get_output(): string +get_error(): string +get_exit_code(): int -success: bool -output: string -error: string -exit_code: int } class Option { +get_name(): string +get_short_name(): string +get_description(): string -name: string -short_name: string -description: string -required: bool } ScriptExecutor --> Script ScriptExecutor --> ScriptFactory ScriptExecutor --> ArgParser ScriptFactory --> Script ArgParser --> Args ArgParser --> Option ScriptExecutor --> ExecutionResult

4.3. 数据处理类图

classDiagram class DataManager { +record_data(string, Data) +playback_data(string): DataStream +save_data(string, Data) +load_data(string): Data -data_store: DataStore -data_formatter: DataFormatter } class DataProcessor { +filter_data(DataStream, Filter): DataStream +transform_data(DataStream, Transformer): DataStream +analyze_data(DataStream, Analyzer): AnalysisResult -filters: map<string, Filter> -transformers: map<string, Transformer> -analyzers: map<string, Analyzer> } class DataStream { +read(): Data +write(Data) +close() -stream: Stream -format: string } class DataStore { +save(string, Data) +load(string): Data +delete(string) +list(): list<string> -store_path: string } class DataFormatter { +serialize(Data): string +deserialize(string): Data +get_format(): string -format: string } class Filter { <<abstract>> +apply(Data): bool } class Transformer { <<abstract>> +transform(Data): Data } class Analyzer { <<abstract>> +analyze(DataStream): AnalysisResult } DataManager --> DataStore DataManager --> DataFormatter DataManager --> DataStream DataProcessor --> DataStream DataProcessor --> Filter DataProcessor --> Transformer DataProcessor --> Analyzer DataProcessor --> AnalysisResult Filter <|-- DataFilter Transformer <|-- DataTransformer Analyzer <|-- DataAnalyzer

5. 状态机

  Apollo scripts子模块的状态机展示了脚本系统在不同阶段的状态转换和处理流程,包括脚本执行状态机、模块管理状态机和数据处理状态机等。这些状态机设计清晰,状态转换明确,便于开发者理解系统的运行机制和行为。

5.1. 脚本执行状态机

stateDiagram-v2 [*] --> Idle: 空闲状态 Idle --> Parsing: 解析命令行参数 Parsing --> Validating: 参数解析完成 Validating --> Executing: 参数验证通过 Executing --> Running: 执行脚本 Running --> Processing: 脚本运行中 Processing --> Finished: 脚本执行完成 Finished --> Idle: 返回空闲状态 Validating --> Error: 参数验证失败 Executing --> Error: 执行失败 Running --> Error: 运行错误 Processing --> Error: 处理错误 Error --> Idle: 返回空闲状态 state Parsing { [*] --> Tokenizing: 命令分词 Tokenizing --> Identifying: 分词完成 Identifying --> [*]: 命令识别完成 } state Validating { [*] --> CheckingArgs: 检查参数 CheckingArgs --> CheckingEnvironment: 参数检查完成 CheckingEnvironment --> [*]: 环境检查完成 } state Executing { [*] --> LoadingScript: 加载脚本 LoadingScript --> Preparing: 脚本加载完成 Preparing --> [*]: 准备完成 } state Processing { [*] --> ExecutingCommands: 执行命令 ExecutingCommands --> HandlingOutput: 命令执行完成 HandlingOutput --> [*]: 输出处理完成 } state Error { [*] --> LoggingError: 记录错误 LoggingError --> NotifyingUser: 错误记录完成 NotifyingUser --> [*]: 用户通知完成 }

5.2. 模块管理状态机

stateDiagram-v2 [*] --> Stopped: 模块停止 Stopped --> Starting: 启动模块 Starting --> Running: 模块启动完成 Running --> Paused: 暂停模块 Paused --> Running: 恢复模块 Running --> Stopping: 停止模块 Stopping --> Stopped: 模块停止完成 Running --> Restarting: 重启模块 Restarting --> Running: 模块重启完成 state Starting { [*] --> Initializing: 初始化模块 Initializing --> LoadingConfig: 模块初始化完成 LoadingConfig --> Connecting: 配置加载完成 Connecting --> [*]: 连接完成 } state Running { [*] --> Processing: 处理数据 Processing --> Communicating: 数据处理完成 Communicating --> Processing: 通信完成 } state Paused { [*] --> SavingState: 保存状态 SavingState --> [*]: 状态保存完成 } state Stopping { [*] --> ClosingConnections: 关闭连接 ClosingConnections --> CleaningUp: 连接关闭完成 CleaningUp --> [*]: 清理完成 } state Restarting { [*] --> StoppingModule: 停止模块 StoppingModule --> StartingModule: 模块停止完成 StartingModule --> [*]: 模块启动完成 }

5.3. 数据处理状态机

stateDiagram-v2 [*] --> Ready: 准备就绪 Ready --> Recording: 开始录制 Recording --> Processing: 录制完成 Processing --> Filtering: 数据处理完成 Filtering --> Analyzing: 数据过滤完成 Analyzing --> Ready: 数据分析完成 Ready --> Playing: 开始回放 Playing --> Processing: 回放完成 state Recording { [*] --> Initializing: 初始化录制 Initializing --> Capturing: 初始化完成 Capturing --> Saving: 数据捕获完成 Saving --> [*]: 数据保存完成 } state Processing { [*] --> Loading: 加载数据 Loading --> Parsing: 数据加载完成 Parsing --> Converting: 数据解析完成 Converting --> [*]: 数据转换完成 } state Filtering { [*] --> ApplyingFilters: 应用过滤器 ApplyingFilters --> Validating: 过滤完成 Validating --> [*]: 验证完成 } state Analyzing { [*] --> Calculating: 计算统计信息 Calculating --> Generating: 计算完成 Generating --> [*]: 报告生成完成 } state Playing { [*] --> LoadingPlayback: 加载回放数据 LoadingPlayback --> Replaying: 数据加载完成 Replaying --> [*]: 回放完成 }

6. 源码分析

6.1. 基础环境脚本分析

6.1.1. apollo_base.sh 核心逻辑
bash 复制代码
#!/usr/bin/env bash

# apollo_base.sh - Apollo基础脚本

# 设置Apollo根目录
export APOLLO_ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"

# 设置CyberRT路径
export CYBER_PATH="${APOLLO_ROOT_DIR}/cyber"
export LD_LIBRARY_PATH="${CYBER_PATH}/lib:${LD_LIBRARY_PATH}"
export PATH="${CYBER_PATH}/bin:${APOLLO_ROOT_DIR}/bin:${PATH}"

# 设置Bazel配置
export BAZEL_CONFIG="--config=cuda"
export BAZEL_OUTPUT_BASE="${APOLLO_ROOT_DIR}/.cache/bazel"

# 设置Python路径
export PYTHONPATH="${APOLLO_ROOT_DIR}:${PYTHONPATH}"

# 定义通用函数

# 检查命令是否存在
function check_command() {
    local cmd="$1"
    if ! command -v "${cmd}" > /dev/null 2>&1; then
        echo "错误: 命令 ${cmd} 不存在"
        return 1
    fi
    return 0
}

# 检查目录是否存在
function check_dir() {
    local dir="$1"
    if [ ! -d "${dir}" ]; then
        echo "错误: 目录 ${dir} 不存在"
        return 1
    fi
    return 0
}

# 检查文件是否存在
function check_file() {
    local file="$1"
    if [ ! -f "${file}" ]; then
        echo "错误: 文件 ${file} 不存在"
        return 1
    fi
    return 0
}

# 日志函数
function log() {
    local level="$1"
    local message="$2"
    local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
    echo "[${timestamp}] [${level}] ${message}"
}

# 错误处理函数
function error_exit() {
    local message="$1"
    local exit_code="${2:-1}"
    log "ERROR" "${message}"
    exit "${exit_code}"
}

# 加载其他配置
if [ -f "${APOLLO_ROOT_DIR}/scripts/env.sh" ]; then
    source "${APOLLO_ROOT_DIR}/scripts/env.sh"
fi

# 加载用户配置
if [ -f "${HOME}/.apollo/config" ]; then
    source "${HOME}/.apollo/config"
fi
6.1.2. apollo.bashrc 核心逻辑
bash 复制代码
#!/usr/bin/env bash

# apollo.bashrc - Apollo Shell环境配置

# 加载Apollo基础脚本
if [ -f "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/apollo_base.sh" ]; then
    source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/apollo_base.sh"
fi

# 设置命令别名
alias ab="apollo_build"
alias ac="apollo_clean"
alias af="apollo_format"
alias al="apollo_lint"
alias ad="dreamview.sh"
alias ap="perception.sh"
alias apl="planning.sh"

# 设置命令自动补全
if [ -f "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/apollo_auto_complete.bash" ]; then
    source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/apollo_auto_complete.bash"
fi

# 显示欢迎信息
echo ""
echo "Apollo开发环境已配置完成"
echo "使用 'ab' 构建项目,'ac' 清理,'ad' 启动Dreamview"
echo ""

6.2. 构建工具脚本分析

6.2.1. apollo_format.sh 核心逻辑
bash 复制代码
#!/usr/bin/env bash

# apollo_format.sh - 代码格式化脚本

# 加载基础脚本
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/apollo_base.sh"

# 定义格式化命令
function format_cpp() {
    echo "格式化C++代码..."
    find . -name "*.cc" -o -name "*.cpp" -o -name "*.h" -o -name "*.hpp" | \
        grep -v "third_party" | \
        grep -v "bazel-*" | \
        xargs clang-format -i
}

function format_python() {
    echo "格式化Python代码..."
    find . -name "*.py" | \
        grep -v "third_party" | \
        grep -v "bazel-*" | \
        xargs yapf -i
}

function format_bazel() {
    echo "格式化Bazel构建文件..."
    find . -name "BUILD" -o -name "*.BUILD" -o -name "WORKSPACE" | \
        grep -v "third_party" | \
        grep -v "bazel-*" | \
        xargs buildifier
}

# 主函数
function main() {
    local format_all=true
    local format_cpp_only=false
    local format_python_only=false
    local format_bazel_only=false
    
    # 解析命令行参数
    while [[ $# -gt 0 ]]; do
        case "$1" in
            --cpp)
                format_cpp_only=true
                format_all=false
                ;;
            --python)
                format_python_only=true
                format_all=false
                ;;
            --bazel)
                format_bazel_only=true
                format_all=false
                ;;
            -h|--help)
                echo "用法: apollo_format.sh [选项]"
                echo "选项:"
                echo "  --cpp      只格式化C++代码"
                echo "  --python   只格式化Python代码"
                echo "  --bazel    只格式化Bazel构建文件"
                echo "  -h, --help  显示帮助信息"
                return 0
                ;;
            *)
                error_exit "未知选项: $1"
                ;;
        esac
        shift
    done
    
    # 切换到Apollo根目录
    cd "${APOLLO_ROOT_DIR}" || error_exit "无法切换到Apollo根目录"
    
    # 执行格式化
    if [[ "${format_all}" == true || "${format_cpp_only}" == true ]]; then
        format_cpp
    fi
    
    if [[ "${format_all}" == true || "${format_python_only}" == true ]]; then
        format_python
    fi
    
    if [[ "${format_all}" == true || "${format_bazel_only}" == true ]]; then
        format_bazel
    fi
    
    echo "代码格式化完成"
}

# 调用主函数
main "$@"
6.2.2. apollo_lint.sh 核心逻辑
bash 复制代码
#!/usr/bin/env bash

# apollo_lint.sh - 代码检查脚本

# 加载基础脚本
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/apollo_base.sh"

# 定义检查命令
function lint_cpp() {
    echo "检查C++代码..."
    local cpplint_config="${APOLLO_ROOT_DIR}/.cpplint"
    
    if [ ! -f "${cpplint_config}" ]; then
        echo "警告: C++代码检查配置文件不存在,使用默认配置"
        cpplint_config=""
    else
        cpplint_config="--filter=-whitespace,-readability/casting,-build/include_subdir,-runtime/references"
    fi
    
    find . -name "*.cc" -o -name "*.cpp" -o -name "*.h" -o -name "*.hpp" | \
        grep -v "third_party" | \
        grep -v "bazel-*" | \
        xargs cpplint ${cpplint_config}
}

function lint_python() {
    echo "检查Python代码..."
    find . -name "*.py" | \
        grep -v "third_party" | \
        grep -v "bazel-*" | \
        xargs pylint --disable=all --enable=unused-import,unused-variable,unused-argument,missing-docstring
}

# 主函数
function main() {
    local lint_all=true
    local lint_cpp_only=false
    local lint_python_only=false
    
    # 解析命令行参数
    while [[ $# -gt 0 ]]; do
        case "$1" in
            --cpp)
                lint_cpp_only=true
                lint_all=false
                ;;
            --python)
                lint_python_only=true
                lint_all=false
                ;;
            -h|--help)
                echo "用法: apollo_lint.sh [选项]"
                echo "选项:"
                echo "  --cpp      只检查C++代码"
                echo "  --python   只检查Python代码"
                echo "  -h, --help  显示帮助信息"
                return 0
                ;;
            *)
                error_exit "未知选项: $1"
                ;;
        esac
        shift
    done
    
    # 切换到Apollo根目录
    cd "${APOLLO_ROOT_DIR}" || error_exit "无法切换到Apollo根目录"
    
    # 执行代码检查
    if [[ "${lint_all}" == true || "${lint_cpp_only}" == true ]]; then
        lint_cpp
    fi
    
    if [[ "${lint_all}" == true || "${lint_python_only}" == true ]]; then
        lint_python
    fi
    
    echo "代码检查完成"
}

# 调用主函数
main "$@"

6.3. 模块管理脚本分析

6.3.1. apollo_action.sh 核心逻辑
bash 复制代码
#!/usr/bin/env bash

# apollo_action.sh - 模块管理核心脚本

# 加载基础脚本
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/apollo_base.sh"

# 定义模块管理命令
function start_module() {
    local module="$1"
    echo "启动模块: ${module}..."
    
    # 检查模块是否存在
    if [ ! -f "${APOLLO_ROOT_DIR}/modules/${module}/BUILD" ]; then
        error_exit "模块 ${module} 不存在"
    fi
    
    # 启动模块
    cyber_launch start "${APOLLO_ROOT_DIR}/modules/${module}/launch/${module}.launch"
    
    if [ $? -eq 0 ]; then
        echo "模块 ${module} 启动成功"
    else
        error_exit "模块 ${module} 启动失败"
    fi
}

function stop_module() {
    local module="$1"
    echo "停止模块: ${module}..."
    
    # 停止模块
    cyber_launch stop "${APOLLO_ROOT_DIR}/modules/${module}/launch/${module}.launch"
    
    if [ $? -eq 0 ]; then
        echo "模块 ${module} 停止成功"
    else
        error_exit "模块 ${module} 停止失败"
    fi
}

function restart_module() {
    local module="$1"
    echo "重启模块: ${module}..."
    stop_module "${module}"
    start_module "${module}"
}

function status_module() {
    local module="$1"
    echo "检查模块 ${module} 状态..."
    
    # 检查模块状态
    cyber_launch status "${APOLLO_ROOT_DIR}/modules/${module}/launch/${module}.launch"
}

function list_modules() {
    echo "列出所有可用模块..."
    ls -d "${APOLLO_ROOT_DIR}/modules/*" | grep -v "common" | grep -v "common_msgs" | sort
}

# 主函数
function main() {
    local action=""
    local module=""
    
    # 解析命令行参数
    while [[ $# -gt 0 ]]; do
        case "$1" in
            start)
                action="start"
                module="$2"
                shift 2
                ;;
            stop)
                action="stop"
                module="$2"
                shift 2
                ;;
            restart)
                action="restart"
                module="$2"
                shift 2
                ;;
            status)
                action="status"
                module="$2"
                shift 2
                ;;
            list)
                action="list"
                shift
                ;;
            -h|--help)
                echo "用法: apollo_action.sh <命令> [模块]"
                echo "命令:"
                echo "  start    启动模块"
                echo "  stop     停止模块"
                echo "  restart  重启模块"
                echo "  status   检查模块状态"
                echo "  list     列出所有模块"
                echo "模块: 模块名称 (如: perception, planning, control)"
                return 0
                ;;
            *)
                error_exit "未知命令: $1"
                ;;
        esac
    done
    
    # 执行命令
    case "${action}" in
        start)
            start_module "${module}"
            ;;
        stop)
            stop_module "${module}"
            ;;
        restart)
            restart_module "${module}"
            ;;
        status)
            status_module "${module}"
            ;;
        list)
            list_modules
            ;;
        *)
            error_exit "请指定命令"
            ;;
    esac
}

# 调用主函数
main "$@"
6.3.2. dreamview.sh 核心逻辑
bash 复制代码
#!/usr/bin/env bash

# dreamview.sh - Dreamview启动脚本

# 加载基础脚本
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/apollo_base.sh"

# 定义Dreamview启动命令
function start_dreamview() {
    echo "启动Dreamview..."
    
    # 检查Dreamview是否已启动
    if pgrep -f "dreamview" > /dev/null; then
        echo "Dreamview已经在运行"
        return 0
    fi
    
    # 启动Dreamview
    cd "${APOLLO_ROOT_DIR}" && \
        ./bazel-bin/modules/dreamview/dreamview --web_host=0.0.0.0 --web_port=8888 &
    
    if [ $? -eq 0 ]; then
        echo "Dreamview启动成功,访问 http://localhost:8888"
    else
        error_exit "Dreamview启动失败"
    fi
}

function stop_dreamview() {
    echo "停止Dreamview..."
    
    # 停止Dreamview
    pkill -f "dreamview"
    
    if [ $? -eq 0 ]; then
        echo "Dreamview停止成功"
    else
        echo "Dreamview未运行"
    fi
}

function restart_dreamview() {
    echo "重启Dreamview..."
    stop_dreamview
    start_dreamview
}

# 主函数
function main() {
    local action="start"
    
    # 解析命令行参数
    while [[ $# -gt 0 ]]; do
        case "$1" in
            start)
                action="start"
                shift
                ;;
            stop)
                action="stop"
                shift
                ;;
            restart)
                action="restart"
                shift
                ;;
            -h|--help)
                echo "用法: dreamview.sh [命令]"
                echo "命令:"
                echo "  start    启动Dreamview (默认)"
                echo "  stop     停止Dreamview"
                echo "  restart  重启Dreamview"
                echo "  -h, --help  显示帮助信息"
                return 0
                ;;
            *)
                error_exit "未知命令: $1"
                ;;
        esac
    done
    
    # 执行命令
    case "${action}" in
        start)
            start_dreamview
            ;;
        stop)
            stop_dreamview
            ;;
        restart)
            restart_dreamview
            ;;
        *)
            error_exit "请指定命令"
            ;;
    esac
}

# 调用主函数
main "$@"

6.4. 数据处理脚本分析

6.4.1. data_record.py 核心逻辑
python 复制代码
#!/usr/bin/env python3

# data_record.py - 数据记录脚本

import os
import sys
import time
import argparse
import subprocess

# 加载基础配置
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..'))

# 定义数据记录类
class DataRecorder:
    def __init__(self, output_dir, duration=0, topics=None):
        self.output_dir = output_dir
        self.duration = duration
        self.topics = topics or []
        self.process = None
        self.record_file = None
    
    def start(self):
        """开始录制"""
        print(f"开始录制数据,输出目录: {self.output_dir}")
        
        # 创建输出目录
        os.makedirs(self.output_dir, exist_ok=True)
        
        # 生成记录文件名
        timestamp = time.strftime("%Y%m%d_%H%M%S")
        self.record_file = os.path.join(self.output_dir, f"record_{timestamp}.record")
        
        # 构建录制命令
        cmd = ["cyber_recorder", "record", "-o", self.record_file]
        
        if self.topics:
            cmd.extend(self.topics)
        
        # 执行录制命令
        print(f"执行命令: {' '.join(cmd)}")
        self.process = subprocess.Popen(cmd)
        
        print(f"数据录制已开始,记录文件: {self.record_file}")
    
    def stop(self):
        """停止录制"""
        if self.process:
            print("停止录制数据...")
            self.process.terminate()
            self.process.wait()
            self.process = None
            print(f"数据录制已停止,记录文件: {self.record_file}")
        else:
            print("没有正在进行的录制")
    
    def wait(self):
        """等待录制完成"""
        if self.process and self.duration > 0:
            print(f"等待 {self.duration} 秒...")
            time.sleep(self.duration)
            self.stop()

# 主函数
def main():
    parser = argparse.ArgumentParser(description="Apollo数据记录脚本")
    parser.add_argument("-o", "--output", required=True, help="输出目录")
    parser.add_argument("-d", "--duration", type=int, default=0, help="录制时长(秒),0表示无限录制")
    parser.add_argument("-t", "--topic", action="append", help="要录制的话题,可以多次指定")
    
    args = parser.parse_args()
    
    recorder = DataRecorder(args.output, args.duration, args.topic)
    
    try:
        recorder.start()
        recorder.wait()
        
        if args.duration == 0:
            print("按Ctrl+C停止录制...")
            while True:
                time.sleep(1)
    
    except KeyboardInterrupt:
        print("\n接收到中断信号")
    finally:
        recorder.stop()
        print("程序退出")

if __name__ == "__main__":
    main()

7. 设计模式

7.1. 命令模式

  Apollo scripts子模块采用命令模式来实现脚本的解析和执行,将命令封装为函数或对象,提高了命令的可扩展性和可维护性。

bash 复制代码
# 命令模式的实现

# 命令注册
COMMANDS=()

# 注册命令
function register_command() {
    local name="$1"
    local handler="$2"
    local description="$3"
    
    COMMANDS["${name}"]="${handler}"
    COMMAND_DESCRIPTIONS["${name}"]="${description}"
}

# 执行命令
function execute_command() {
    local command="$1"
    local args="${@:2}"
    
    if [ -n "${COMMANDS[${command}]}" ]; then
        ${COMMANDS[${command}]} ${args}
    else
        echo "未知命令: ${command}"
        show_help
        return 1
    fi
}

# 示例命令注册
register_command "start" start_command "启动模块"
register_command "stop" stop_command "停止模块"
register_command "restart" restart_command "重启模块"
register_command "status" status_command "检查状态"

7.2. 模板方法模式

  模板方法模式用于定义脚本执行的骨架,具体步骤由子类或具体实现来完成,提高了代码的复用性和可维护性。

bash 复制代码
# 模板方法模式的实现

# 基础脚本模板
function script_template() {
    # 步骤1: 加载基础配置
    load_config
    
    # 步骤2: 解析命令行参数
    parse_args "$@"
    
    # 步骤3: 验证环境
    validate_environment
    
    # 步骤4: 执行具体逻辑
    execute_logic
    
    # 步骤5: 清理资源
    cleanup
}

# 具体脚本实现
function build_script() {
    function execute_logic() {
        # 构建逻辑
        echo "执行构建..."
    }
    
    script_template "$@"
}

function test_script() {
    function execute_logic() {
        # 测试逻辑
        echo "执行测试..."
    }
    
    script_template "$@"
}

7.3. 策略模式

  策略模式用于实现不同的算法或策略,根据条件选择合适的策略执行,提高了代码的灵活性和可扩展性。

bash 复制代码
# 策略模式的实现

# 定义策略
STRATEGIES=()

# 注册策略
function register_strategy() {
    local name="$1"
    local implementation="$2"
    
    STRATEGIES["${name}"]="${implementation}"
}

# 执行策略
function execute_strategy() {
    local strategy="$1"
    local args="${@:2}"
    
    if [ -n "${STRATEGIES[${strategy}]}" ]; then
        ${STRATEGIES[${strategy}]} ${args}
    else
        echo "未知策略: ${strategy}"
        return 1
    fi
}

# 示例策略注册
register_strategy "clang_format" format_with_clang
register_strategy "google_format" format_with_google
register_strategy "custom_format" format_with_custom

7.4. 观察者模式

  观察者模式用于实现事件的发布和订阅机制,当事件发生时,通知所有订阅者,提高了代码的解耦性和可扩展性。

bash 复制代码
# 观察者模式的实现

# 观察者注册
OBSERVERS=()

# 注册观察者
function register_observer() {
    local event="$1"
    local observer="$2"
    
    if [ -z "${OBSERVERS[${event}]}" ]; then
        OBSERVERS[${event}]=()
    fi
    
    OBSERVERS[${event}]+=("${observer}")
}

# 通知观察者
function notify_observers() {
    local event="$1"
    local data="$2"
    
    if [ -n "${OBSERVERS[${event}]}" ]; then
        for observer in "${OBSERVERS[${event}][@]}"; do
            ${observer} "${data}"
        done
    fi
}

# 示例观察者注册
register_observer "build_start" log_build_start
register_observer "build_complete" log_build_complete
register_observer "build_failed" log_build_failed

7.5. 单例模式

  单例模式用于确保某个类或对象在系统中只有一个实例,提高了资源的利用效率和系统的一致性。

bash 复制代码
# 单例模式的实现

# 单例变量
SINGLETON_INSTANCE=""

# 获取单例实例
function get_singleton() {
    if [ -z "${SINGLETON_INSTANCE}" ]; then
        # 创建实例
        SINGLETON_INSTANCE=$(create_instance)
    fi
    
    echo "${SINGLETON_INSTANCE}"
}

# 创建实例
function create_instance() {
    # 创建实例的逻辑
    echo "singleton_instance_$(date +%s)"
}

# 示例使用
singleton=$(get_singleton)
echo "获取单例: ${singleton}"

# 再次获取
singleton2=$(get_singleton)
echo "再次获取: ${singleton2}"

echo "是否相同: $([ "${singleton}" == "${singleton2}" ] && echo "是" || echo "否")"

8. 总结

  Apollo scripts子模块是Apollo自动驾驶平台的核心脚本集合,提供完整的开发、构建、测试和运行支持。采用模块化架构设计,将脚本按照功能划分为多个类别,包括基础环境配置、构建工具、模块管理、测试验证、数据处理等,实现开发流程自动化与标准化,简化平台使用复杂度。通过命令模式、模板方法、策略模式、观察者模式和单例模式等多种设计模式,提高了代码的可维护性和可扩展性。scripts子模块为开发者提供了高效便捷的开发体验,是Apollo平台的重要组成部分。

相关推荐
故渊at10 小时前
系列三:组件化与模块化进阶 | 第11篇 组件化项目规范与问题根治:依赖、资源、Manifest 与混淆的全链路管控
android·架构·mvvm·模块化·组件化
goodluckyaa12 小时前
NVIDIAGPU 架构中的不变常量(宏观 → 微观)
架构·gpu算力
wenzhangli713 小时前
AI-IDE 关键技术解析:从自然语言到企业级智能开发平台的架构演进
ide·人工智能·架构
m0_7471245313 小时前
ARM架构基础知识扫盲
arm开发·架构
pe7er14 小时前
软件设计不要“既要又要”
前端·后端·架构
X54先生(人文科技)14 小时前
《元创力》纪实录·卷宗2.1P上去的安全带:当“表演性合规”成为文明的遮羞布
人工智能·架构·开源·ai写作·开源协议
IPHWT 零软网络15 小时前
信创场景下大容量语音网关的架构设计与实践——以 MX120G-A 为例
架构·信创·国产化·语音网关
柒和远方16 小时前
每日一学V017:用 Prompt 做 NLP:解构赋值与 AI 全栈的第一次实战
javascript·架构·代码规范
原来是猿16 小时前
Docker 【 技术架构(2)】
docker·架构
湘-枫叶情缘16 小时前
论 AGI 时代个体“神通化能力”的生成机理、工程架构与主权协同
架构·agi