徽州骆驼 · 11月19日

AP AUTOSAR 硬核技术(5):诊断管理

image.png

01.诊断管理

诊断管理是自适应平台基础中的一个功能集群。作为功能集群,诊断管理包括一个与自适应应用程序链接的库和一个实现诊断管理活动方面的守护进程。AP 工具链包含的诊断库提供了 C++ API,用于支持自适应应用程序在运行时访问诊断管理。

  • 实现 ISO 14229-5(UDSonIP),基于 ISO 14229-1(UDS)和 ISO 13400-2(DoIP)。
  • 是 AUTOSAR Adaptive平台(AP)Foundation 层上的功能集群(FC)。
  • 配置基于 AUTOSAR 的Diagnostic Extract Template(DEXT)诊断提取模板,已被多家 OEM 和供应商采用。

02.高层级架构

image.png

在自适应平台上,每个软件集群包含所有与更新、已安装功能或部署新功能/应用程序相关的部分。因此,自适应诊断管理器为每个已安装的软件集群提供一个独立的诊断地址,并支持整个机器或任何中间诊断部署的单一诊断地址。每个独立的诊断地址都有其自己的诊断服务器实例,为诊断提供了独立开发的可能性,例如独立的 ODX 文件。请注意,软件集群还与 UCM 的软件包耦合,可以进行更新或新安装。

软件簇 (SWCL)

  • 管理原子可升级/可扩展部分。
  • 部署/更新功能/应用:SWCL 包含与这些相关的所有部分。
  • 独立诊断服务器实例:每个已安装的软件集群支持自己的诊断服务器实例,具有独立的诊断地址。
  • 与 UCM 的软件包耦合:可以被更新或新安装。

image.png

如图所示:共享诊断地址 0x1234,包含 4 个软件集群,所有软件集群共享一个单一的诊断地址并引用相同的共享诊断贡献集(DiagnosticContributionSet),因为对于相同的诊断地址,一个 DID 只能存在一次。

共享诊断贡献集(DiagnosticContributionSet)诊断地址为 0x1234 包含两个 DID:DID 0x2314 和 DID 0x4321。

只有 DiagnosticContributionSet 指定了一个诊断地址。由于软件集群共享相同的诊断贡献集,诊断服务器会自动扩展到其他软件集群。

03.统一诊断服务

AUTOSAR 诊断管理的核心在于接收和处理来自网络层的统一诊断服务(UDS)请求。

UDS 通信由客户端请求和诊断服务的响应组成,例如客户端向 ECU 请求启动 UDS 服务,处理后,ECU 发送回正面或负面的响应(如果响应被抑制则不响应)。

UDS 并不是特定于 AUTOSAR 的,而是一种为 ECU 环境(汽车电子领域)设计的诊断通信协议,该协议在 ISO 14229-1:2013 标准中进行了标准化。

image.png
Diagnostic Session Handling

3.1 UDS 请求处理

接收UDS请求:当诊断管理收到UDS请求时,它会从传输层中提取与传输层无关的UDS信息,并尝试将其关联到现有的UDS会话中进行处理。

校验请求:诊断服务器会检查请求是否在当前会话中被允许,以及安全设置(如参数长度、服务标识符(SID)、子功能和条件等)是否有效。

分配请求:如果请求校验失败,会被拒绝;否则,诊断管理会生成内部响应或将请求传递给外部应用程序处理,并根据当前会话和安全级别分配给已有诊断对话、新建诊断对话或直接拒绝请求。

image.png

为了连接应用层和传输层,DM 实现了传输协议管理器(Transport Protocol Manager),其主要职责是对双向消息进行调度:将诊断客户端的请求转发到对应的诊断服务器实例,并将诊断服务器实例的响应传输到相应的 DoIP 传输协议处理程序,这个处理程序实际上连接了对应的客户端。

在配置诊断时,使用了 AUTOSAR 诊断提取模板(Diagnostic Extract Template, DEXT)。DEXT 是一种标准化的 AUTOSAR 交换格式,可以像系统描述(System Description)一样使用和通信。它定义了诊断通信的各个方面,包括 UDS 服务、会话配置、安全级别配置等,以及诊断事件管理,如诊断故障代码(DTCs)、诊断事件、操作周期等。DEXT 还支持从多个源合并信息,使诊断配置能够从最合适的源(无论是应用开发人员、集成商还是 OEM)构建。

3.2 诊断会话

UDS 定义了许多服务,可以用来访问 ECU 内部的信息,控制 ECU 内部的诊断功能,以及建立和控制诊断会话

通常,当客户端请求 UDS 服务时,服务器通常会在默认会话中启动。然后,根据 UDS 服务 DiagnosticSessionControl (0x10) 定义的会话请求类型,可以启动特定会话。这些会话在 ISO 14229-1:2013 中定义,包括:

  • 默认会话 Default Session
  • 编程会话 Programming Session
  • 诊断会话 Diagnostic Session
  • 扩展会话 Extended Session    

image.png

3.3 诊断故障代码

在诊断中诊断故障代码(DTC)代表车辆中检测到的某些问题(通常对生产或维修很重要)。DTC 由 3 个字节组成,并使用 DiagnosticEventToTroubleCodeUdsMappingARXML 元素映射到诊断事件,使得这些事件可以由 DTC 代码唯一标识。诊断管理使用 DTC 来唯一标识事件内存数据库中的数据。

DTC 有多种解释方式,AUTOSAR 诊断支持 ISO11992-4、ISO14229-1:2013 和 SAE-J2012-DA。这些不同的解释仅影响诊断报告,而不影响其他功能。多个 DTC 可以组合成一个诊断故障代码组(DiagnosticTroubleCodeGroup),允许诊断在一次操作中处理组内所有 DTC。DTC 组使用一个专用的 DTC 值来标识,这个值在有效 DTC 范围之外。为了方便,诊断提供了 DTC 组“GroupOfAllDTCs”(分配给 DTC 组标识符 0xFFFFFF),始终包含所有配置的 DTC。 

DiagnosticTroubleCodeGroup: 唯一地标识一组 DTC。DTC 组映射到有效DTC的范围。通过提供一组 DTC,表示对该组所有DTC请求某种操作。DTC 组的定义由 ISO 14229-1 和 OEM/ 供应商特定提供。

3.4 经典和自适应 AUTOSAR 中的诊断

自适应平台诊断管理的组织和责任与经典平台中的诊断栈有很多相似之处。概念上,诊断架构分为三个子组件:诊断通信管理器 (DCM)、诊断事件管理器 (DEM) 和传输协议 (DoIP)

诊断事件管理器 (DEM)

报告诊断事件:自适应应用使用 rd-diag 库中的 API 报告诊断事件。
功能:处理诊断事件(如应用错误或其他有趣的报告),将诊断故障代码(DTCs)与诊断事件绑定,并更新 DTC 状态。
特点:诊断事件在系统范围内是唯一的,一个事件只能由单个应用报告。在自适应平台中,应用通过一个由 DiagnosticMonitorInterface 分类的端口原型访问事件,该接口具有到服务依赖项的诊断映射。
数据存储:在自适应平台实现中,诊断管理器根据诊断事件更新故障代码(DTC)及其相关的环境信息,捕获和存储环境数据(SnapshotRecords和ExtendedDataRecords,称为Freeze Frame)这些数据使用持久性功能集群存储在文件系统中的文件中。与经典平台中在非易失性存储器中保留事件存储器的方式不同。 

诊断管理器的作用

核心元素:诊断管理器(即 rb-dm 进程)是处理诊断通信和支持 UDS 请求的核心元素。
UDS 请求处理: UDS 请求在诊断对话的上下文中处理,这代表了诊断客户端(测试仪)与诊断服务器之间的对话。
报告诊断事件:自适应应用程序使用 rd-diag 库中的 API 向诊断管理器报告诊断事件。诊断事件报告受监控实体的状态,并在系统中唯一标识该实体。
执行操作:当 rb-dm 从应用程序收到事件通知时,它会执行诊断配置中定义的操作,如更改诊断故障代码(DTC)状态,或捕获并存储与诊断事件相关的环境数据(扩展数据记录或快照记录)。这些事件是 rb-dm 事件内存管理的输入源。

诊断通信管理器 (DCM)

职责: DCM 负责执行 UDS(统一诊断服务)和(在经典平台中)SAE J1979 的通信路径和诊断服务。
功能: DCM 转发来自外部诊断扫描工具的诊断请求,并组装响应消息(如DTC、状态信息等),这些消息将传输到车辆内部测试仪或外部诊断工具。
实现: DCM 实现了诊断服务,类似于经典平台(CP)中的 DCM。
扩展:目前支持有限的诊断服务,后续将扩展更多的 UDS 服务。
并行处理: DCM 可以在默认会话下支持客户端的全并行处理,满足现代汽车架构的需求。

传输协议 (DoIP)

支持协议:自适应平台诊断管理支持“基于IP的诊断”(DoIP)通信协议,确保合适的诊断 UDS 客户端可以与诊断管理一起使用。 
分离处理:在经典平台中,传输协议与诊断事件管理器(DEM)和诊断通信管理器(DCM)是分开处理的。
组件:自适应平台的诊断组件包括诊断管理器(rb-dm)守护进程和一个库(rb-diag)。
管理职责:诊断管理器进程 rb-dm 负责管理诊断事件、控制 UDS 会话和处理 UDS 通信,必须在自适应平台启动时由执行管理启动。
API实现: rb-diag 库与自适应应用程序链接,提供 ara::diag 诊断接口的 API 实现。由于该库与应用程序链接,API 在应用程序的上下文中运行。

04.RTA-VRTE 工具中的诊断模块

RTA-VRTE 工具包中的诊断模块包括诊断管理器(DM)进程 rb-dm 和一个库 rb-diag。诊断管理器进程负责管理诊断事件、UDS 会话控制、UDS 通信等,必须由执行管理作为 RTA-VRTE 入门工具包平台启动的一部分启动。该库与交互 RTA-VRTE 入门工具包诊断的自适应应用程序链接,并提供 ara::diag 诊断接口的 API 实现。由于该库与应用程序链接,API 在应用程序的上下文中运行。

诊断管理器(即 rb-dm 进程)是处理诊断通信和支持 UDS 请求的核心元素。UDS 请求在诊断对话的上下文中处理,这代表了诊断客户端(测试仪)与诊断服务器之间的对话。

自适应应用程序使用 rd-diag 库中的 API 向诊断管理器报告诊断事件。诊断事件报告受监控实体的状态,并在系统中唯一标识该实体。当 rb-dm 从应用程序收到事件通知时,它会执行诊断配置中定义的任何操作,例如更改诊断故障代码(DTC)状态,或捕获和存储与该诊断事件相关的环境数据(扩展数据记录或快照记录)。因此,事件是 rb-dm 事件内存管理的输入源。

RTA-VRTE 入门工具包的诊断管理是完全动态配置的。RTA-VRTE 入门工具包使用基于 FlatCFG 数据的配置文件部署到目标 ECU,以配置诊断提取信息,即诊断信息可能会随着 ECU 上的软件更新而变化,以及用于主要 TCP/DoIP 设置的 bin 文件(dm_config.bin)。

4.1 初始化和依赖

诊断管理器(DM)守护进程 rb-dm 必须由执行管理作为 AP 平台启动的一部分启动。该进程还依赖于以下平台元素,这些元素必须在 rb-dm 启动前启动:

  • ara::com:用于与自适应应用程序通信。
  • ara::per:用于访问自适应应用程序文件并存储非易失性数据。
  • ara::log:用于日志记录和跟踪。

image.png
Platform Dependencies and Principal Interactions of Diagnostic Manager 

配置文件

RTA-VRTE 使用 FlatCFG 文件定义诊断配置,并使用一个 .bin 文件指定与诊断提取信息无关的设置,如主要 TCP/DoIP 设置。

诊断管理器dm_config.bin配置文件必须与诊断管理器可执行文件一起部署到目标 ECU。这些文件在启动时由rb-dm进程加载,因此必须部署到可以读取的位置。

配置文件的位置通过执行管理使用“-c”命令行选项传递给诊断管理器进程。

启动命令如下:

/opt/vrte/dia-diagnostic-manager/rb-dm -c dm_config.bin

依赖服务和目录

执行管理启动诊断管理器(rb-dm)进程,确保它作为 RTA-VRTE 平台启动的一部分被正确加载。

DM 进程在运行时依赖于 arapipcd,如果没有 arapipcd,rb-dm 将无法启动。

在执行编辑器中,rb-dm 的执行名称和进程名称必须设置为 rb_dm,否则将无法配置 ara::per。

配置示例

配置文件 dm_config.bin 是由 flatc 编译器根据dm_config.json 和 DmConfig.fbs 文件生成的,可以根据用户需求调整 DM 配置。

rb-dm 库的位置(LD_LIBRARY_PATH)必须进行配置,默认文件夹为 /opt/vrte/dia-diagnostic-manager/lib

启动时,运行时诊断管理进程 rb-dm 从部署的数据文件 aradiagd___flatcfg.bin 加载配置。

最终配置如下: 

LD_LIBRARY_PATH=/lib:/usr/lib:/opt/vrte/lib:/opt/vrte/dia-diagnostic-manager/lib

ECUCFG_ENV_VAR_ROOTFOLDER=/opt/vrte/dia-diagnostic-manager/etc/ecu-cfg/

加载配置文件: rb-dm 进程从指定位置加载 dm_config.bin 配置文件和其他必要的 FlatCFG 数据文件。

4.2 诊断应用程序

诊断应用程序通过分类为 ara::diag 诊断接口和 rb-diag 库的 PortPrototypes 与诊断管理进行交互。交互可以提供数据访问(用于外部测试工具和 DTC 存储)、报告检测到的故障监控、更新操作周期等功能。

ISOLAR-VRTE 应用设计编辑器包括 Harmony 应用设计语言(HADL)。

HADL 是一种为创建 ARXML 清单元素(包括自适应 SWCs、端口原型和诊断接口)而优化的领域特定语言(DSL)。

应用程序与诊断管理接口的核心概念处理类(Handler Classes)和实例指定符(InstanceSpecifiers)。

一个自适应应用程序使用诊断管理 API 类作为基类定义诊断处理类。基类提供实用方法(例如,启动和停止处理程序)、由诊断管理调用的通知回调方法,以及定义派生类中所需方法实现签名的纯虚方法。

定义诊断处理类:

自适应应用使用诊断管理 API 类作为基类定义诊断处理类。   

例如,以下代码片段使用诊断管理 API GenericDataIdentifier 类作为基类定义一个新的处理类:

#include

class MyDID : public ara::diag::GenericDataIdentifier {

public:

    MyDID( ara::core::InstanceSpecifier is );

    // 定义虚方法的实现

};

GenericDataIdentifier 构造函数(以及派生类的构造函数)将 InstanceSpecifier 作为参数。InstanceSpecifier 是从自适应 SWC 的 PortPrototype的shortName 构造的,因为这些信息是应用设计人员已知的。创建的 InstanceSpecifier 在构造派生处理类时传递给诊断管理 API。

扩展上述示例,以下代码片段实例化处理程序,并传递使用端口 shortName 创建的 InstanceSpecifier:

MyDID did( InstanceSpecifier( "portname" ) );

自适应应用程序创建由不同诊断接口分类的端口,以支持各种诊断功能,包括数据访问、监控、操作周期、数据传输和诊断例程控制。

例化处理类:通过传递 InstanceSpecifier 实例化处理类,并关联到特定的端口原型。

4.3 数据访问  

诊断管理通过在响应 UDS ReadDataByIdentifier(0x22) 和 WriteDataByIdentifier(0x2E) 请求时调用自适应应用程序中定义的处理程序来支持数据访问。

通过 GenericDataIdentifier 类的实例提供对数据标识符的访问。创建的对象在构造期间通过传递给构造函数的 InstanceSpecifier 关联到相关的端口原型。

端口原型必须由 ara::diag DiagnosticDataIdentifierGenericInterface 接口分类。

GenericDataIdentifier 类包含 Read 和 Write 纯虚方法,必须实现以提供所需的数据访问。因此,应用程序创建一个从 GenericDataIdentifier 类派生的类,在派生类中提供 ReadWrite 的实现:

class MyDID : public ara::diag::GenericDataIdentifier

{

    // 实现 Read 和 Write 方法

};

派生类的实例是数据标识符的处理程序,并在响应 ReadDataByIndentifier 或 WriteDataByIdentifier 请求时由诊断管理调用。

4.4 监控

诊断管理支持自适应应用程序报告诊断事件。以这种方式报告事件的应用程序是监控应用程序。监控应用程序识别特定故障(例如,故障传感器、短路等)并使用事件报告故障。诊断事件在系统范围内是唯一的,因此事件的监控只能由单个应用程序执行。   

自适应应用程序通过由 DiagnosticMonitorInterface 分类且具有诊断映射到服务依赖项的端口原型访问用于监控的事件。

监控自适应应用程序通常独立于诊断管理,并负责向诊断管理报告事件状态。诊断管理还可以使用 initMonitor 回调(在构建 Monitor 类时建立)控制监控自适应应用程序,例如暂停监控。

// 定义回调(由诊断管理调用)

auto cbk = [&]( ara::diag::InitMonitorReason ima ) { ... }

// 定义端口原型(用于诊断事件)

ara::core::InstanceSpecifier specifier( "port" );

// 定义去抖动(基于计数器,由诊断管理管理)

ara::diag::Monitor::CounterBased debounceParams = {

    .failedThreshold = 3,

    .passedThreshold = -3,

    .failedStepsize = 1U,

    .passedStepsize = 1U,

    .useJumpToFailed = true,

    .useJumpToPassed = true

};

// 创建用于事件报告的监控实例

ara::diag::Monitor m( specifier, cbk, debounceParams );

监控应用程序使用 ReportMonitorAction API 报告诊断事件。这需要一个 MonitorAction 参数,表示要报告的事件状态:

m.ReportMonitorAction( ara::diag::MonitorAction::kPassed );

监控应用程序执行自己的事件去抖动时,会报告一个合格状态(kPassed或kFailed),因为只有在达到应用程序自己的去抖动阈值后才会报告事件。相反,使用诊断管理内部去抖动的监控应用程序报告一个不合格状态(kPrepassed或kPrefailed),因为这些报告可能只是一次性的故障,不会持续存在。当诊断管理去抖动阈值被超过时,不合格状态报告可以变为合格。

诊断管理支持两种去抖动形式:

  • 基于时间:不合格报告启动一个诊断管理计时器,当计时器到期时结果变为合格。计时器到期前的后续矛盾不合格报告只会重置诊断管理计时器。
  • 基于计数器:诊断管理计数不合格报告,当达到一定阈值数量的相同报告后变为合格。达到阈值前收到的后续矛盾不合格报告会重置计数器。

诊断管理去抖动通过引用诊断事件和所需去抖动算法属性的 DiagnosticEventToDebounceAlgorithmMapping 元素启用。如果事件没有这样的映射,则诊断管理不应用任何去抖动算法。当事件未定义去抖动算法时,监控应用程序传递不合格状态(即 kPrepassed 或 kPrefailed)是一个错误。

4.5 基于计数器的去抖动机制  

事件报告: kPrefailed 时计数器增加,kPrepassed 时计数器减少。

事件状态:当计数器值等于失败阈值时,事件状态设为失败。当计数器值等于通过阈值时,事件状态设为通过。

可配置参数:

  • 失败阈值 (counterFailedThreshold): 达到该值后,计数器不再增加,事件状态设为失败。
  • 通过阈值 (counterPassedThreshold): 达到该值后,计数器不再减少,事件状态设为通过。
  • 增加步长 (counterIncrementStepSize): 计数器增加的步长。
  • 减少步长(counterDecrementStepSize): 计数器减少的步长。
  • 跳跃值 (counterJumpUpValue, counterJumpDownValue):
  • 如果在 kPrefailed 时计数器小于 counterJumpUpValue,则立即设为 counterJumpUpValue。
  • 如果在 kPrepassed 时计数器大于 counterJumpDownValue,则立即设为 counterJumpDownValue。
  • 跳跃标志 (counterJumpUp, counterJumpDown): 用于启用或禁用跳跃行为。

操作周期:

  • 定义: 根据 ISO 14229-1:2013 标准,操作周期定义了监控的开始和结束条件。一个 ECU(电子控制单元)可以有多个操作周期,典型的周期包括点火开/关、上电/断电等。
  • 通知: 自适应应用通过 SetNotifier API 通知诊断管理操作周期状态的变化。或者,可以配置为在初始化/终止时由诊断管理自动启动/结束操作周期。
  • 事件报告: 诊断事件只有在操作周期开始时才能报告,如果操作周期不活跃,则报告会被忽略。  

image.png

4.6 数据传输 

UDS服务中的RequestUpload(0x35)和 RequestDownload(0x34) 提供了诊断客户端和服务器之间的数据传输能力。   

image.png

注意:UDS数据传输服务是通过 GenericUDSService 诊断接口支持的。目前的实现不支持 DiagnosticUploadInterface 或 DiagnosticDownloadInterface,这些诊断接口在此处文档中详细记录以求完整。

工作机制:

  • 诊断应用被视为数据源,因此 UploadService 启动从应用到诊断客户端的数据传输。
  • DownloadService则相反,启动从诊断客户端到应用的数据传输。

交互过程:在自适应平台上,应用程序与诊断库(Diagnostic Library)进行数据传输的交互,是通过 UploadService 或 DownloadService 类的实例来实现的。具体过程如下:

实例化服务:应用程序通过实例化 UploadService 或 DownloadService 类来访问数据传输功能。   

关联端口原型:创建的上传/下载处理程序对象在构造过程中必须通过传递给构造函数的 InstanceSpecifier 关联相关端口原型。端口原型必须由 ara::diag::DiagnosticUploadInterface 或 ara::diag::DiagnosticDownloadInterface 接口进行分类。

数据传输:通过这些接口,应用程序能够进行诊断数据的上传或下载操作,实现与诊断库的有效交互。

class UploadApp : public ara::diag::UploadService

注意事项: UploadService 和 DownloadService 类不能直接实例化,因为它们包含纯虚方法,例如请求数据传输或传递传输的数据。子类必须实现这些方法后才能实例化。

4.7 例行控制 RoutineControl 

UDS 服务 RoutineControl(0x31)允许诊断客户端启动和停止服务器中的例行程序,并请求例行程序的结果。该服务通常用于相对复杂的控制序列,如启动自检功能、清除或擦除非易失性存储器、覆盖正常诊断服务器功能等。

关键点:启动和停止例行程序:通过例行控制接口中的 Start、Stop 和 RequestResults 方法。例行标识符(RID):每个方法都需要一个预定义的例行标识符。

实现: GenericRoutine 类包含纯虚方法,必须实现这些方法以提供所需的例行控制。类似于 GenericDataIdentifier 类的使用,应用程序使用GenericRoutine 类作为基类创建派生类,并在派生类中实现纯虚方法。

class MyRoutine : public ara::diag::GenericRoutine  

在 ISOLAR-VRTE 中,HADL 文件中应实例化 DiagnosticRoutineInterface 并提供一个端口。要在 DEXT 编辑器中将端口映射到例行程序,需要执行 DiagnosticServiceGenericMapping。

4.8 服务验证

服务验证在处理UDS请求期间提供了两个回调:

在 UDS 处理的早期阶段,在诊断管理器检查请求服务标识符(SID)是否受支持之前进行制造商特定检查。如果检查失败,请求会立即被合适的负响应代码(NRC)拒绝。

在诊断管理器验证 SID 并检查安全访问之后,进行供应商特定检查。如果检查失败,请求会被合适的 NRC 拒绝。

05.与诊断管理的合作

在 AP 工具中使用 DEXT 编辑器来配置诊断服务。DEXT 格式由 AUTOSAR 标准化,因此其他符合标准的 DEXT 编辑器也可以使用。

配置新诊断元素的步骤:

应用设计: 使用 ISOLAR-VRTE 应用设计编辑器和 Harmony 应用设计语言添加所需的软件组件、端口原型和诊断接口。

诊断配置: 使用 ISOLAR-VRTE DEXT 编辑器将所需元素添加到 DEXT 诊断配置中,并将其映射到 HADL 文件中定义的端口。

清单处理: 使用 ISOLAR-VRTE 清单处理将 AUTOSAR DEXT 配置转换为 RTA-VRTE 启动工具包 FlatCFG 数据配置,以便部署到目标 ECU。

应用处理程序: 实现诊断元素的自适应应用处理程序,例如通过子类化相关的 ara::diag API 类。   

END

作者:刘向
来源:汽车电子与软件

推荐阅读:

更多汽车电子干货请关注汽车电子与软件专栏。欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。)
推荐阅读
关注数
5726
内容数
470
汽车电子与软件行业的相关技术报道及解读。
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息