汽车电子
随着行业的 SOA 理念大火,带着一系列的解读和思考观点横行于世,笔者大多仔细研读过,虽然增加了很多碎片化知识和曾经的盲点,但也同样带来了更多的疑惑,本文撰写初衷是基于车厂的角度思考,如何在现有整车架构和软件资产下,进行 SOA 的设计开发,并从工具链和操作方法上给出案例。
1. 分布式 ECU-基于信号的架构设计
现在我们来看,在域控制器开发阶段,针对传统车厂,在分布式 ECU,或区域控制器集成,已经有了深厚的架构开发和经验积累的前提条件下,如何转型并进行中央域控的服务设计。
本章节描述现阶段,面对分布式 ECU,如何进行基于信号的整车电子电器架构开发。如下为介绍 MBSE 理论的较为经典文章。 服务设计相对软件架构设计影响较大,接下来着重分析 SOA 服务设计。
2. 中央域控架构-基于 SOA 服务设计
为了后续方便理解,我们大致把国内的整车电子电器架构分为三个阶段:
1.0 架构: 以逆向分析为主,整合现阶段市面上供应商的零部件,实现整车功能(典型代表 比亚迪 F3)
2.0 架构: 以正向开发为主,融合国内外先进经验技术,进行整车多车型的架构开发(典型代表 吉利 CMA 架构)
3.0 架构:硬件上从分布式 ECU 变为整车域控架构,软件上由基于 signal 变为 SOA ,能够大幅降低车辆上 ECU 数量,软件迭代速度也由质的飞跃(典型代表 特斯拉)
2.1. 服务设计依据
总体采用自上而下与自下而上想结合的形式进行服务定义。
针对网络上全拓补硬件,进行自下而上设计,将其分为传感器,执行器,与单功能 ECU 三类,分别针对硬件进行设备抽象 api 设定,与原子服务抽象。
针对整车功能定义 FR 与 FDR ,进行自上而下设计,首先使用面向对象的思维进行整车的类抽象,然后设计类的方法与属性,其中属性尽量与 原子服务进行映射,方法需要使用一个或多个原子服务进行实现
最终进行原子服务,与整车类的属性与方法进行映射,找出无法实现的类,以及没有使用到的原子服务,此部分进行服务代理设计,及使用传统 SWC 软件模块实现部分功能,然后代理成组合服务形式,参与整车架构设计。
2.2. SOA 设计区别
3.0 架构全面采用 SOA(service-oriented Architecture)设计思想进行构建,上一代 2.0 架构采用的是 POP(procedure oriented programming)面向过程的设计思想,需要注意的是,在 SOA 设计中,自上而下的设计方法中引入了 OOP(object oriented programming)面向对象的抽象与封装概念,这是为了在 2.0 现有架构基础上,能够继承原有 FR,FDR,LC,然后进行迭代开发。
也就是说通过 Class 的抽象,在原有 LC 一层新添加了类图的设计,通过抽象的类进行用例设计进行功能链路实现。后续进行服务化设计,只要满足类中的属性和方法能够实现即可,而不必去关心设计的服务如何支撑整个子系统及功能链路的实现。
POP 面向过程设计
在 2.0 架构中,首先要进行 Composition 功能组合的划分,然后在进行 Component 功能组件的划分,每个组件都有相应的输入和输出信号,依据这些来实现高内聚,低耦合的功能点实现,设计好组件/组合之后,然后进行模块间接口信号的设计,每个模块平均有 20 个左后的输入和输出信号
SOA 面向服务设计
在 3.0 架构中,首先要进行服务的划分和设计,每个服务是松耦合的,也就是分治的设计理念,及服务里包含的方法、event、field 具备高扇入,合理的扇出,而服务所抽象的数据要与处理的业务逻辑、流程、功能实现三方解耦,所设计的基础服务也要与操作系统解耦。
对比 2.0 中的设计,可以类似的理解为 2.0 先设计软件模块,然后设计信号接口,而 3.0 soa 后先设计服务接口(此接口不仅包含类似信号的 event,还包含类似函数的 method 方法),有了服务之后,在设计哪些模块提供这些服务,哪些模块订阅服务。
需要注意,SOA 后里的软件模块需要严格的区分 server 与 client,也就是一个模块要不就是提供某个或多个服务,要不就是订阅了一个或多个服务,不存在既订阅了一些服务又提供了一些服务的情况,这与 2.0 架构中的 swc 软件模块既有输入又有输出完全不一样。
OOP 面向对象设计
在 3.0 架构中,需要借助面向对象的设计方法来辅助支撑,也就是依托现有 2.0 架构,不进行大范围重构的前提条件下,使用类抽象出所有 LC 模块,并且通过设计不同类的用例图来实现原 LC 处理的功能。
2.0 架构上 MBSE 设计图上 LC 映射实现
3.0 架构上 MBSE 设计图上 LC 映射实现
2.3. 接口命名规范
在进行 SOA 服务设计时,需要遵循如下命名规范
2.3.1. 基础规范
尽量采用单词全称,名称过长后才使用缩写
名称不包含特殊字符 ,./~
名称如果过长,需要参考常用单词缩写表中内容进行缩写
考虑服务和参数的复用性,各类命名中不应带有拓补节点信息
不同层级的各类名称不能重复
命名中不能使用如下系统关键字:
skeleton
proxy
internal
resources
method
event
field
input/output
amsr
ara
com
someip
base
vac
std
serialization
2.3.2. Service Instance 服务实例
服务实例及指代定义的服务实现,后文若单独写服务,则意为服务实例
服务名采用首字母大写,全英文名称
服务名不能有下划线
服务名后缀需要以 Srv 结尾
eg. LedControlSrv
2.3.3. Service Interface 服务接口
SOA 平台上服务之间通信接口有 Event、Method 和 Field 三种形式, ServiceInterface 用于定义 Event/Method/Field 消息类型和具体的命名空间,与具体的通信协议无关。
服务接口名采用首字母大写,全英文名称
服务接口名不能有下划线
服务接口名需要以 SrvIf 结尾
eg. LedControlSrvIf
2.3.4. Event
Event 接口表示实际传输的数据,以数据为操作对象,只要能够清晰的表达数据的含义即可,命名规范遵循基础规范,后缀要以 Evt 结尾。 eg. CurrentVleEvt : 电流值 Event 消息传递方式如下图所示,为服务端主动向客户端发送,并且会触发客户端的 callback 函数进行数据处理。
2.3.5. Method
Mehtod 接口表示某种控制,通讯方式采用 RPC 远程调用,通常有动词行为,比如控制,状态查询,传输,注册,设置等。其中 Method 又分为 F&F,与 R&R 两类,FF 为单次调用,不需要反馈,RR 为 request resoponse,需要反馈。
请求-响应(request/response): R&R
请求-响应流(request/stream)
发后不管(fire-and-forget) : F&F
通道模式(channel)
整体设计依据遵循 CURD/REST 这类成熟的互联网通用接口概念 CURD
REST
接口名称需要表达清楚该方法的含义,推荐使用动名词进行命名,采用驼峰命名规范,基于如上 CURD/REST 参考,命名要以后缀 Mtd 作为结束,设计如下基础命名范式:
get 获取状态
set 设置状态
report 传递信息
judge 判断事件,返回 boolean
create 创建线程/进程/动态服务/文件/事件 等
delete 删除线程/进程/文件 等
eg. setSoundOnMtd : 鸣响喇叭
judgeVehMovingMtd : 判断车辆是否移动
getCarCfgMtd : 获取 car config 值
reportComponentStsMtd : 上报 component 状态信息
createXTaskUsrLightShowSrvMtd : 创建用户自定义的灯光 show 服务
deleteDebugClass0MsgMtd : 删除 debug 等级为 0 的所有 msg 调试文件 Method 消息传递方式如下图所示,为客户端主动向服务端请求,可以设定是否有服务端的返回值。
2.3.6. Field
Field 表示一种属性,通常指状态值或某种信息,名称应该清楚的表达该属性的含义。 Field 包含如下三类信息:
getter : 只读接口,原型为 method,获取服务端信息
notifier : 只读接口,原型为 event,接收服务端的数据
setter : 写入接口,原型为 method,设置/修改服务端相关信息
eg. VehMoveFld : 车辆移动控制 Field 消息传递方式如下图所示
3. 服务设计方法
针对大部分 OEM,需要在现有整车架构上进行服务设计升级,也就是已有 base 的 LC 需求,和工程级的软件 swc,需要依据这两类已有资产,使用 OOP 面向对象的抽象与封装手段,进行 SOA 服务化重构。 本章节以车身控制器中 危险报警 软件模块为例,说明如何在已有 LC 需求和软件模块前提下,使用 gitee,进行 SOA 设计。
需要注意的是,在设计服务接口时,要清楚的知道不同接口的实际特性和性能消耗。Event 为 服务端主动向客户端 发送请求,而 Method 恰好相反,为 客户端主动向服务端 进行请求调用。根据如上接口描述,整体服务设计遵循如下方案要点。
原 2.0 平台中信号尽量保留,作为 Event 接口类型进行预留
服务设计包括 类抽象-服务接口设计-服务实例设计-设计具体的进程/线程 运行此服务实例
每个抽象好的类,对应一个服务接口 : XX_Class -> XX_SrvIf
类中的属性对应 Event 接口类型 XX_Class::value -> XX_SrvIf-valueEvt
类中的方法对应 Method 接口类型 XX_Class::fuction -> XX_SrvIf-valueMtd
尽量不要设计 Field 接口
每个服务接口由一个具体的服务进行实例化 : XX_SrvIf -> XX_Srv
每个实例化的服务,都要设计哪个进程/线程/软件模块/ECU 来提供,哪些 App 会订阅
App 使用/设定服务里的数据/功能,需要通过 CM 通讯管理进行服务的订阅
实例化的服务之间,数据/功能 传递,则直接调用相互暴露的 api,可以不用 CM 参与(即组合服务和原子服务的关系)
App 之间不能直接进行数据传输,需要调用专有的服务进行数据传递(即服务代理模块)
3.1. 需求分析
示例软件模块主要实现危险警报功能,在整车条件允许的情况下,如果按下手机 app 上的警报按钮,则会触发车辆进行声光报警。
需要注意 针对 VFC/PNC 相关软件逻辑,虽然在 3.0 架构上已经不复存在,但是需要保留功能分区划域的思想,也就是针对不同场景需要触发不同的软件组件来执行完成,针对不同的 PNC,在 3.0 架构上可以作为 VLAN 划分的设计参考。 使用 gitee 进行需求编写,与原 LC 中需求进行追溯
3.2. 类的抽象和封装
使用 OOP 手段,根据需求和已有的软件模块,进行类的抽象,并将有可能被多次调用的软件逻辑进行类方法的封装,后续多次执行的代码逻辑,仅在实例化的类中执行一次即可。
类抽象,将整车从多个角度抽象成不同的类,每个类都可以映射成一个 service 的实体,不同的软件模块调用相同逻辑时,可以直接订阅此 service,使用 service 中的 method、event、field。
类方法封装,将接口进行泛化,封装成通用类的方法,达到松耦合的效果
类图:
时序图:
3.3. 服务设计
上一章推导出,所设计的类就是一个需要实现服务,根据此原则,在 gitee 中进行服务设计。 服务设计包含如下步骤和要素:
服务接口设计(method/event/field)
接口变量设计(method 的入参出参及数据类型,event 变量名及数据类型)
服务实例设计(发布者,服务 ID 等信息)
3.3.1. 数据类型设计
在 Gitee-数据类型 中进行数据类型设计
3.3.2. 服务接口设计
在 Gitee-Service 接口 中进行 service 接口设计
3.3.3. 服务实例设计
在 Gitee-Service 实例 中进行服务实例设计
3.3.4. 配置文件导出以及代码框架生成
通过 gitee 能够导出所有服务 list 的 csv 文件,基于此开发工具进行 arxml 文件和代码框架的转换生成,最终进行基于 SOA 服务化的软件开发。 代码框架可以参考笔者之前的回答。
4. 小结
针对已有历史软件资产的厂商,进行 SOA 架构升级的方法大致如上,此处借用了 gitee 企业版辅助进行架构设计,也希望大家意识到,软件定义汽车,不仅仅是软件产品,甚至整车研发的工具链和开发流程也都会慢慢靠向软件开发的生态工具链,抱残守缺,是注定要被时代淘汰的,此处诺基亚和柯达会深有感触。
审核编辑:刘清
全部0条评论
快来发表一下你的评论吧 !