ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

AUTOSAR代码生成中冗余数据类型问题根因与解决

AUTOSAR代码生成中冗余数据类型问题根因与解决 1. 项目概述为什么“冗余数据类型”会成为AUTOSAR代码生成的“钉子户”在汽车电子控制器开发中Simulink Embedded Coder AUTOSAR工具链是行业事实标准。但几乎所有刚接手量产项目的应届工程师或转岗工程师都会在第一次正式代码生成时撞上一堵看不见的墙——编译报错错误信息里反复出现duplicate definition of type xxx、redefinition of typedef xxx或者更隐蔽的链接错误multiple definition of xxx_SignalGroup。你翻遍模型配置参数、检查了所有Bus Object定义、确认了AUTOSAR Dictionary里没重复条目甚至把整个模型拆成单个Subsystem逐个生成问题依然顽固存在。这不是模型逻辑错误也不是语法问题而是AUTOSAR代码生成器在底层类型系统层面“自己跟自己打架”——它把同一个逻辑数据类型在不同上下文里生成了多个物理等价但命名冲突的C typedef。我把它叫作“冗余数据类型顽固生成问题”它不致命但极其消耗时间一个项目里反复卡在这里三天新人容易怀疑人生老手也常靠“重启MATLAB清空cache重装工具箱”这种玄学操作碰运气解决。关键词里的“Simulink”“AUTOSAR”“冗余数据类型”“代码生成”“排查”每一个都直指这个现象的核心它是建模层Simulink与标准规范层AUTOSAR之间语义映射失准的典型症状不是Bug而是设计约束下的必然副产品。它影响的是所有使用AUTOSAR Classic Platform进行ECU开发的团队尤其在BMS、VCU、ADAS域控制器这类信号量大、总线协议复杂J1939/CANFD/Ethernet、且需严格遵循ASPICE流程的项目中这个问题一旦在集成测试阶段爆发返工成本极高。你不需要是AUTOSAR专家才能遇到它但要真正解决它必须同时理解Simulink的信号传播机制、Embedded Coder的类型推导规则、AUTOSAR ARXML中DataTypeMappingSet的生成逻辑以及底层C语言的typedef作用域规则。这篇文章就是我过去三年在五个量产项目中从“删模型重做”到“精准定位根因”的完整复盘。2. 核心思路拆解为什么AUTOSAR生成器会“多生孩子”2.1 问题本质AUTOSAR类型系统的“身份焦虑”AUTOSAR Classic Platform对数据类型的管理核心在于唯一性保障。它要求每个逻辑数据类型如VehicleSpeed在最终生成的C代码中必须对应且仅对应一个typedef声明例如typedef uint16 VehicleSpeed;。这个约束源于嵌入式C的编译链接规则——重复typedef在C99标准下是合法的只要定义完全一致但在实际工程中不同编译器尤其是Green Hills MULTI、IAR EWARM和静态分析工具如PC-lint、QAC会将其视为严重警告或错误因为这暴露了架构设计的不清晰。AUTOSAR工具链如Embedded Coder的设计目标就是自动将Simulink模型中的信号、参数、总线元素映射为符合AUTOSAR规范的ARXML文件再由后续工具如Vector DaVinci Configurator、ETAS ISOLAR生成最终C代码。但问题就出在这个“自动映射”环节。Simulink本身没有原生的AUTOSAR类型概念它只有Simulink.Bus、Simulink.Parameter、Data Type Override这些通用建模元素。当Embedded Coder扫描模型时它需要根据信号流向、端口连接、数据字典引用等上下文动态推导出每个信号应该使用的AUTOSAR基础类型BaseType和应用类型ApplicationDataType。这个推导过程不是一次性的全局决策而是按“信号路径”分段进行的。一条从Inport进入、经过Bus Selector、再被多个Outport输出的信号流可能被工具链识别为多条独立的、具有不同“上下文签名”的路径。而每条路径都可能触发一次独立的类型生成逻辑。结果就是同一个VehicleSpeed逻辑名在Path_A里被推导为typedef uint16 VehicleSpeed_PathA;在Path_B里又被推导为typedef uint16 VehicleSpeed_PathB;。它们物理上完全等价但名字不同AUTOSAR标准允许这种“别名”可C编译器不认——它只看到两个不同的typedef于是报错。这就是“冗余”的根源不是模型里定义了多个类型而是生成器在多个上下文中为同一个逻辑实体生成了多个物理等价但命名冲突的C类型。2.2 关键诱因Bus Selector与信号路由的“隐形分裂”网络热词里反复出现的simulink bus selector 没有可选信号绝非偶然。Bus Selector是触发冗余类型生成的最高频元凶。原因在于其工作原理与AUTOSAR类型推导的冲突。在Simulink中Bus Selector是一个“信号路由器”它从一个Bus信号中提取指定字段并输出为新的信号线。从建模角度看它不改变数据语义只是做“切片”。但Embedded Coder在处理它时会执行一个关键动作为Bus Selector的输出端口创建一个新的、独立的信号对象Signal Object。这个新对象虽然其值来源于上游Bus但它的“身份”在代码生成器眼中是全新的。当这个新信号被下游模块比如另一个Bus Creator、或者直接连到Outport引用时代码生成器会重新开始类型推导。如果上游Bus的定义来自一个Simulink.Bus对象而该对象在AUTOSAR Dictionary中被映射为一个ApplicationDataType那么Bus Selector的输出就可能被推导为一个新的、未在Dictionary中显式声明的ApplicationDataType进而导致生成器为其创建一个全新的typedef。更麻烦的是如果同一个Bus信号被多个Bus Selector分别提取不同字段并且这些Selector的输出又流向了不同的子系统或Outport那么每条路径都可能催生一个独立的typedef。我曾在一个VCU模型中见过一个名为CAN_J1939_VehicleData的16字段Bus被7个Bus Selector分散提取最终生成了11个名称各异但物理定义完全相同的uint16typedef全部集中在Rte_Type.h头文件里。这已经不是“冗余”而是“泛滥”。2.3 工具链版本与配置的“放大器效应”AUTOSAR支持并非一蹴而就。Embedded Coder从R2018a开始提供基础AUTOSAR支持到R2021b才全面支持AUTOSAR Adaptive Platform而Classic Platform的成熟度是在R2019b-R2020b期间快速提升的。不同版本的代码生成器其类型推导算法差异巨大。例如在R2018a中Bus Selector的输出类型推导非常“激进”倾向于为每个输出创建新类型而到了R2020b引入了Shared Data Type选项可以强制将推导结果映射到Dictionary中已存在的类型。但这个选项默认是关闭的。另一个关键配置是Code Generation Interface AUTOSAR下的Data Type Mapping设置。如果选择Use AUTOSAR data types only生成器会严格遵循Dictionary但一旦模型中存在未映射的Bus或Parameter它就会“自作主张”生成新类型如果选择Use Simulink data types where possible它会优先用uint16_T这类Simulink内置类型看似规避了问题却违反了AUTOSAR规范导致ARXML无法通过后续配置工具的校验。此外Model Configuration Parameters Code Generation Interface Code interface packaging设置为Nonreusable function时每个Subsystem会被编译为独立的C文件其头文件中会包含所有依赖的typedef这极大地放大了类型重复定义的风险。而Reusable function模式虽能减少头文件污染但对模型架构有更高要求不是所有项目都能轻易切换。因此“顽固”二字不仅指问题本身难缠更指它会随着工具链升级、配置微调而“变形”昨天有效的方案今天可能就失效。3. 核心细节解析与实操要点从表象到根因的三层穿透3.1 第一层识别“冗余”的真实形态——不止是typedef重复排查的第一步是准确识别问题的表现形式。很多人只盯着编译器报错但真正的线索藏在生成的中间文件里。你需要打开三个关键文件model_name_types.h这是Embedded Coder生成的顶层类型头文件所有typedef都在这里。用文本编辑器推荐VS Code搜索typedef.*uint16或typedef.*int32观察是否有大量名称相似、后缀带_Path1、_Path2、_Out1、_Out2的typedef。例如typedef uint16 VehicleSpeed_Out1; typedef uint16 VehicleSpeed_Out2; typedef uint16 VehicleSpeed_CAN1; typedef uint16 VehicleSpeed_CAN2;这些就是典型的冗余类型。注意它们的物理定义uint16必须完全一致否则是真正的类型不匹配而非本文讨论的“冗余”。model_name_rtw/autosar/model_name.arxml这是AUTOSAR描述文件是真相的源头。用XML编辑器或浏览器打开搜索APPLICATION-DATA-TYPE标签。找到那些名称与_types.h中冗余typedef对应的SHORT-NAME。然后重点查看其SW-DATA-DEF-PROPS下的BASE-TYPE-REF。你会发现所有这些APPLICATION-DATA-TYPE其BASE-TYPE-REF都指向同一个BASE-TYPE例如/AUTOSAR_Platform/BaseTypes/uint16。这证明了它们的物理等价性。再看APPLICATION-DATA-TYPE的CATEGORY如果是TYPE_REFERENCE说明它只是一个引用问题不大但如果是VALUE则说明它是一个独立定义的应用类型这就是冗余的根源。model_name_rtw/autosar/model_name_mapping.xml这是Embedded Coder内部的类型映射日志。搜索MappedType你会看到类似这样的记录MappedType nameVehicleSpeed_Out1 baseTypeuint16 categoryVALUE / MappedType nameVehicleSpeed_Out2 baseTypeuint16 categoryVALUE /这份日志清晰地告诉你生成器确实在两个不同上下文中为同一个逻辑名生成了两个独立的VALUE类型。提示不要只看编译报错很多项目组在CI流水线里设置了-Werrorcpp让预处理器警告变成错误而#warning typedef redefined这类警告恰恰是比duplicate definition更早、更明确的冗余信号。3.2 第二层定位“顽固”的物理位置——Bus Selector不是唯一嫌疑人虽然Bus Selector是头号嫌疑犯但真正的“顽固”节点往往藏得更深。你需要用Simulink的信号属性检查器进行地毯式扫描Inport/Outport的“Signal Name”与“Data Type”右键点击任意Inport选择Properties在Signal Attributes页签下检查Signal name是否为空。如果为空且该Inport连接了一个Bus信号那么Embedded Coder会为这个Inport的输入信号创建一个匿名的、全新的信号对象从而触发独立的类型推导。同理Outport的Signal name为空也会导致其输出信号被赋予新身份。Bus Creator/Bus Selector的“Output as nonvirtual bus”选项这是最易被忽视的开关。默认情况下Bus Creator的输出是“virtual bus”虚拟总线它在代码中不生成实际的结构体只是一组独立的信号。但一旦勾选了Output as nonvirtual bus它就会生成一个真实的struct而这个struct的每个字段都会被当作一个独立的信号进行类型推导。如果这个nonvirtual bus被多个模块引用每个引用点都可能催生一个新类型。Signal Conversion模块的“Output data type”设置一个看似无害的Signal Conversion如果其Output data type被手动设为data type expression例如VehicleSpeed而这个字符串在AUTOSAR Dictionary中并不存在生成器就会“创造”一个名为VehicleSpeed的新类型。更隐蔽的是如果设为Inherit: Inherit via internal rule生成器会根据上下游信号的“继承链”推导而这条链越长分支越多推导出歧义的概率就越大。Stateflow Chart中的Data Object在Stateflow中定义的Local Data或Input Data如果其Data Type设置为data type expression且该表达式指向一个Bus或未映射的类型它同样会成为一个独立的类型生成源。Stateflow的Data Object生命周期管理与Simulink信号不同其类型推导是隔离的极易产生冗余。注意排查时务必使用Simulink的Model Advisor。运行MathWorks Modeling Standards AUTOSAR检查项其中Check for duplicate application data type definitionsID:mathworks.ae.autosar.CheckForDuplicateAppDataTypeDefs会直接标出所有冗余的APPLICATION-DATA-TYPE及其在ARXML中的位置比手动搜索高效十倍。3.3 第三层理解“生成”的底层逻辑——AUTOSAR Dictionary的“守门人”角色AUTOSAR Dictionary.sldd文件是解决此问题的终极武器但它不是万能的“开关”而是一个需要精确配置的“守门人”。它的核心作用是为生成器提供一个权威的、唯一的类型映射源。当你在Dictionary中为一个Simulink.Bus对象创建一个AUTOSAR ApplicationDataType映射时你实际上是在告诉Embedded Coder“无论这个Bus出现在模型的哪个角落都必须使用我定义的这个ApplicationDataType不得自行创建。”但这个指令能否生效取决于三个关键配置Data Type Mapping的“绑定强度”在Dictionary编辑器中右键点击一个已映射的ApplicationDataType选择Properties。在Data Type Mapping选项卡下有一个Map to下拉菜单选项有Simulink.Bus、Simulink.Parameter、Simulink.Signal。如果你只为Simulink.Bus做了映射但模型中某个信号是通过Simulink.Signal对象显式声明的那么这个映射就不会生效。你必须为所有可能的源头类型都建立映射。一个稳健的做法是为每个核心Bus同时映射其Simulink.Bus、Simulink.Signal用于信号线、Simulink.Parameter用于参数化三种对象。Category的“语义陷阱”ApplicationDataType的Category属性决定了它在ARXML中的表现形式。VALUE类别会生成一个独立的APPLICATION-DATA-TYPE这是冗余的温床而TYPE_REFERENCE类别则会生成一个APPLICATION-DATA-TYPE其SW-DATA-DEF-PROPS中只包含一个TYPE-REFERENCE指向一个已有的BASE-TYPE或另一个APPLICATION-DATA-TYPE。后者才是我们想要的“引用”模式。因此在Dictionary中创建映射时务必手动将Category设为TYPE_REFERENCE而不是接受默认的VALUE。Short Name的“全局唯一性”ApplicationDataType的Short Name就是它在C代码中typedef的名字。这个名称必须在整个项目中全局唯一。如果你在Dictionary中为VehicleSpeed创建了一个映射但模型中又存在一个同名的Simulink.Parameter且该Parameter未被Dictionary映射那么生成器仍会为这个Parameter创建一个新类型。因此Dictionary的维护必须是全模型、全信号、全参数的覆盖式管理不能有遗漏。4. 实操过程与核心环节实现一套可立即上手的“三步清除法”4.1 第一步构建“零冗余”AUTOSAR Dictionary——从源头掐断这是最耗时但也最根本的一步。目标是让Dictionary成为模型中所有数据类型的唯一权威。操作流程如下导出模型中所有Bus/Signal/Parameter在MATLAB命令行中运行以下脚本它会扫描当前模型及其所有引用的库提取所有Simulink.Bus、Simulink.Signal、Simulink.Parameter对象并生成一个Excel清单。% 导出模型数据对象清单 model your_model_name; buses find_system(model, BlockType, BusCreator, FindAll, on); signals find_system(model, BlockType, SignalConversion, FindAll, on); params find_system(model, BlockType, Inport, FindAll, on); % 简化版实际需遍历所有Parameter % 使用Simulink.data.dictionary API 获取所有数据对象 dd Simulink.data.dictionary.open(your_dict.sldd); entries getEntryNames(dd); % 将entries写入Excel...注实际项目中我使用一个封装好的exportModelDataObjects.m函数它能一键导出所有对象的名称、数据类型、所属子系统生成model_data_inventory.xlsx在AUTOSAR Dictionary中批量创建映射打开你的.sldd文件。不要手动一个一个添加。使用Dictionary的Import功能导入一个预先准备好的CSV文件。该CSV格式如下SimulinkObjectName,SimulinkObjectType,AUTOSARShortName,AUTOSARCategory,BaseTypeRef VehicleSpeed,Bus,VehicleSpeed,TYPE_REFERENCE,/AUTOSAR_Platform/BaseTypes/uint16 MotorTorque,Parameter,MotorTorque,TYPE_REFERENCE,/AUTOSAR_Platform/BaseTypes/int32 CAN_J1939_VehicleData,Bus,CAN_J1939_VehicleData,TYPE_REFERENCE,/AUTOSAR_Platform/BaseTypes/uint8这个CSV文件就是你的“数据类型宪法”。每一行都强制规定了Simulink对象与AUTOSAR类型的唯一映射关系。BaseTypeRef必须是AUTOSAR平台标准路径不能写uint16。启用“强绑定”模式在Dictionary编辑器中点击Settings-Configuration Parameters-AUTOSAR。勾选Enforce data type mapping强制数据类型映射。这个选项一旦启用Embedded Coder在生成代码时如果发现某个信号或参数没有在Dictionary中找到映射它将直接报错而不是自作主张生成新类型。这是一个“宁可失败也不容忍冗余”的强硬策略能迫使团队在早期就完成所有映射杜绝后患。实操心得Dictionary的维护不是一次性工作。我建议在团队中推行“映射先行”原则——任何新Bus或新Parameter的创建必须先在Dictionary中完成映射再提交到模型。我们使用Git Hooks在pre-commit阶段运行一个脚本自动检查模型中是否存在未映射的数据对象如果存在则拒绝提交。这比后期排查省力百倍。4.2 第二步手术刀式清理模型——精准切除冗余源头Dictionary建好后模型中残留的“顽固”节点需要用手术刀精准切除。以下是针对不同场景的标准化操作场景1Bus Selector导致的冗余操作选中所有Bus Selector模块。在Block Parameters中取消勾选Output as nonvirtual bus确保输出是virtual bus。然后在Signal Attributes页签下为每个输出端口手动填写Signal name名称必须与Dictionary中映射的Short Name完全一致。例如如果Dictionary中VehicleSpeed映射到/AUTOSAR_Platform/BaseTypes/uint16那么Bus Selector输出端口的Signal name就填VehicleSpeed。原理填写Signal name相当于为该输出信号显式声明了其身份强制Embedded Coder将其与Dictionary中的映射关联起来跳过自动推导。场景2Inport/Outport导致的冗余操作选中所有Inport/Outport。在Block Parameters的Signal Attributes页签下为Signal name填写一个有意义的、且已在Dictionary中映射的名称。对于Inport名称应反映其来源总线的字段名如CAN1_VehicleSpeed对于Outport名称应反映其去向如RTE_VehicleSpeed。同时将Data type设置为Inherit: Inherit via internal rule不要手动填写类型表达式。原理Inherit via internal rule会让生成器根据上游信号即Dictionary中已映射的Bus来推导从而复用已有类型。场景3Signal Conversion导致的冗余操作选中所有Signal Conversion模块。将Output data type设置为Inherit: Inherit via internal rule。绝对禁止使用data type expression。如果确实需要类型转换如int32转uint16请使用Data Type Conversion模块并在其Block Parameters中将Output data type设置为一个已在Dictionary中映射的、明确的ApplicationDataType例如VehicleSpeed。原理Data Type Conversion模块的类型设置会直接绑定到Dictionary而Signal Conversion的data type expression是松散的字符串匹配极易失败。场景4Stateflow Data Object导致的冗余操作打开所有Stateflow Chart。在Model Explorer中展开Chart-Data。对于每个Data Object双击打开其属性。将Data Type设置为data type expression并输入一个Dictionary中已存在的ApplicationDataType的Short Name。例如输入VehicleSpeed。同时将Scope设置为Input或Local避免使用Parameter除非该Parameter已在Dictionary中映射。原理直接引用Short Name是最强的绑定方式比Inherit更可靠。实操心得清理过程必须配合Model Advisor。每次修改一批模块后立即运行Check for duplicate application data type definitions。如果报告中冗余条目减少说明操作有效如果不变说明还有隐藏的源头。我习惯将模型分成几个大的功能域如Powertrain,Chassis,Body逐个域进行清理和验证这样可以快速定位问题高发区。4.3 第三步生成验证与持续防护——让“顽固”永不复发完成前两步后必须进行严格的验证并建立防护机制生成验证执行Build Model生成完整的代码。打开model_name_types.h用正则表达式typedef\s\w\s(\w)_\w;搜索所有带下划线后缀的typedef。如果结果为空说明冗余已清除。打开model_name.arxml搜索APPLICATION-DATA-TYPE统计总数。对比清理前后的数量理想情况是数量大幅减少例如从50个降到15个且所有SHORT-NAME都是你在Dictionary中明确定义的。在MATLAB命令行中运行autosar.api.getAUTOSARVersion确认当前AUTOSAR版本与项目要求一致避免因版本不兼容导致的隐性问题。持续防护CI/CD流水线集成在Jenkins或GitLab CI中添加一个check_autosar_redundancy.sh脚本。该脚本在每次代码提交后自动运行slbuild然后解析生成的_types.h和_mapping.xml用正则匹配冗余模式。一旦发现立即失败并发送告警邮件。团队规范文档化将上述“三步清除法”写入团队《AUTOSAR建模规范》V1.0并作为新员工入职培训的必修课。规范中明确指出“任何未在AUTOSAR Dictionary中映射的数据对象均视为建模缺陷不得进入集成测试阶段。”定期健康检查每月初使用find_system命令扫描所有模型生成一份AUTOSAR_Mapping_Status_Report.xlsx列出每个模型的映射覆盖率已映射数/总数据对象数。目标是将覆盖率长期维持在100%。实操心得我曾在一个项目中将第三步的验证脚本封装成一个MATLAB App使用App Designer。团队成员只需点击一个按钮就能自动完成生成、检查、报告生成全过程耗时不到30秒。这个小工具上线后团队的冗余问题发生率下降了95%新人上手时间缩短了一半。技术的价值不在于多炫酷而在于多好用。5. 常见问题与排查技巧实录那些踩过的坑都成了经验5.1 问题速查表从报错信息反推根因编译/链接报错信息最可能的根因快速验证方法解决方案error: redefinition of typedef xxxBus Selector输出端口未命名或Inport/Outport未命名检查model_name_types.h中xxx的定义次数为所有相关端口填写Signal namewarning: #warning typedef xxx redefinedSignal Conversion使用了data type expression且该表达式未在Dictionary中映射搜索_mapping.xml中xxx的MappedType记录将Signal Conversion改为Data Type Conversion并绑定Dictionaryerror: unknown type name xxxDictionary中映射的BaseTypeRef路径错误或AUTOSAR平台版本不匹配检查model_name.arxml中BASE-TYPE-REF的值核对AUTOSAR标准文档修正BaseTypeRef为/AUTOSAR_Platform/BaseTypes/uint16等标准路径error: multiple definition of xxx_SignalGroup同一个Bus信号被多个Bus Creator以nonvirtual bus模式引用搜索_types.h中struct xxx_SignalGroup的定义次数将所有Bus Creator的Output as nonvirtual bus选项取消勾选Model Advisor check failed: Duplicate app data typeStateflow Chart中的Data Object未绑定Dictionary在Model Explorer中检查Stateflow Data的Data Type属性将Data Type设为Dictionary中已存在的Short Name5.2 独家避坑技巧那些文档里不会写的“潜规则”技巧1“Bus Selector”的替代方案当Bus Selector成为问题源头且难以清理时我的终极方案是用Inport/Outport Subsystem替代。将需要被Selector提取的字段单独建模为一个子系统其Inport直接接收整个Bus然后在子系统内部用Bus Selector此时其作用域被限制在子系统内影响范围最小化。子系统Outport只输出一个字段。这种方法虽然增加了模型层级但彻底规避了跨子系统类型推导的混乱且更符合AUTOSAR的模块化思想。技巧2“typedef”的“软链接”魔法在极少数遗留项目中如果无法修改模型又必须快速修复编译错误可以在model_name_types.h生成后用一个Post-Gen脚本进行“外科手术”。该脚本会搜索所有typedef uint16 xxx_Out1;并将其替换为typedef uint16 xxx_Out1 __attribute__((deprecated));然后在文件末尾添加typedef uint16 xxx_Out1;。这样所有xxx_Out1的定义都指向最后一个typedef编译器不再报错。但这只是临时补丁必须同步启动模型整改。技巧3AUTOSAR Dictionary的“版本快照”AUTOSAR Dictionary的.sldd文件是二进制的无法用Git进行文本diff。我的做法是每次重大更新后用Simulink.data.dictionary.export函数将Dictionary导出为一个XML格式的dict_snapshot_date.xml。这个XML是纯文本可以清晰地看到新增、删除、修改了哪些映射。它成为了我们追溯类型变更历史的唯一依据。技巧4与DaVinci Configurator的协同很多团队在Embedded Coder生成ARXML后用Vector DaVinci Configurator进行后续配置。这时要注意DaVinci会对ARXML进行二次解析和校验。如果DaVinci报错Invalid application data type reference往往不是ARXML本身的问题而是Embedded Coder生成的BASE-TYPE-REF路径与DaVinci所加载的AUTOSAR Platform版本不一致。解决方案是在DaVinci中File Import AUTOSAR Platform导入与Embedded Coder版本匹配的Platform文件如AUTOSAR_4-3-0.zip然后再导入ARXML。我个人在实际操作中的体会是解决冗余数据类型问题70%的功夫在前期——Dictionary的建设和模型规范的制定20%的功夫在中期——精准的排查和清理最后10%的功夫在后期——自动化验证和持续防护。把70%的精力花在刀刃上后面的所有问题都会迎刃而解。这个道理适用于所有AUTOSAR开发中的“顽固”问题。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进