前 17 篇都在 Classic 信号世界里打转:接口是端口,连线是 RTE,部署是 ECU Extract。本篇换世界观------把"服务"当一等公民 。真跑之后有三条硬结论:① System Composer 25.2 里确实有
systemcomposer.interface.ServiceInterface类,服务接口的"函数+参数+异步位"可建可查;但它只能挂在 client/server 端口上 ,数据端口挂它会被当场拒绝(报错原文附后)。② Adaptive 侧真能出东西:一个函数端口 + 作用域绑定端口的 Simulink Function,slbuild之后落盘 17 个产物 ,含stub/aragen/server_skeleton.h(3001 B,ara::com::ServiceFactory×2、OfferService×3、ara::core::Future×1)和SERVICE-INTERFACE(1 个 method / 2 个参数 / IN 1 + OUT 1)的 ARXML,外加 ExecutionManifest、ServiceInstanceManifest、MachineManifest 与 CMakeLists。③ 客户端 proxy 本机没打通 ,如实记录:Function Element Call块探不到端口句柄(in=0 out=0),函数连接器建不出来,构建报does not resolve to a Function Caller or port scoped Simulink Function block------"服务可提供"通了,"服务被调用"没通,这就是本篇的边界。
1. 先跑许可与目标文件,再谈 SOA
本专栏铁律"真跑为准"。SOA/Adaptive 这题比经典链更容易被"文档说可以"带偏,所以第一步是把工具在不在问清楚:
matlab
license('test','System_Composer') % -> 1
license('test','AUTOSAR_BLOCKSET') % -> 1
ver('systemcomposer') % -> System Composer 25.2
which('autosar_adaptive.tlc')
% -> E:\MATLAB\R2025b\toolbox\rtw\targets\AUTOSAR\AUTOSAR\autosar_adaptive.tlc
which('autosar.tlc') % -> ...\AUTOSAR\autosar.tlc (Classic)
顺带一条如实记录:license('test','SIMULINK_CODER') 与 license('test','EMBEDDED_CODER') 在本机都返回 0 ,但 autosar_adaptive.tlc 是真实存在的、且能构建出 C++ 与 ARXML。许可探测的 feature 名与"能不能干活"不是一回事,本专栏的判据永远是"产物有没有落盘",不是某个返回值。

2. 两种世界观的差异,一句话:信号是布线,服务是契约
| Classic AUTOSAR | Adaptive AUTOSAR | |
|---|---|---|
| 通信原语 | Sender-Receiver / Client-Server 接口 | ServiceInterface = Methods + Events + Fields |
| 绑定时机 | 编译期静态布线,RTE 生成时落定 | 运行期服务发现(SOME/IP、InstanceIdentifier) |
| 端点 | SWC 端口 ↔ RTE ↔ 信号/PDU/CAN | 进程 ↔ ara::com ↔ 网络端点 |
| 架构载体 | AutosarArchitecture 模型 |
softwarearchitecture 模型(client/server 端口) |
| MATLAB 产物 | ARXML 组件/装配/ECU Extract(架构12) | C++ ara::com 骨架 + 服务/执行/实例清单 |
| "发布/发现" | 无------谁连谁在生成时就写死了 | OfferService/FindService 是运行时动作 |
这张表里最容易被忽略的是最后一行:Classic 里"谁能调我"是编译期答案,Adaptive 里它是运行时答案。架构文档写"动态发现"很容易,写清"发现失败时怎么降级"才是架构工作------那属于架构15 的安全机制落点。
3. 架构侧:服务接口在字典里长什么样
System Composer 的接口字典(.sldd)里,服务接口是独立的一类对象:
matlab
d = systemcomposer.createDictionary('a18_sc_dict.sldd');
si = d.addServiceInterface('IF_Dyno'); % 服务接口
f1 = si.addElement('GetState'); % 函数元素(方法)
f1.setFunctionPrototype('float64 GetState(float64 rpm)');
f1.setAsynchronous(false); % 同步方法
f2 = si.addElement('StateChanged');
f2.setFunctionPrototype('void StateChanged()');
f2.setAsynchronous(true); % 异步 = 事件/通知语义
d.save;
实测回读(a18_gen.log):
text
S2 服务接口 OK class=systemcomposer.interface.ServiceInterface
fn1=float64GetState(float64,rpm) async=0
fn2=voidStateChanged() async=1
两个细节值得注意:① 原型字符串写进去会被规范化(float64 GetState(float64 rpm) → float64GetState(float64,rpm)),回读要和写入口径区分 ;② Asynchronous 是服务接口层的属性,这正是 ara::com 里"方法调用"与"事件通知"的分界------同步方法等结果,异步只发不等。
然后把它放进一个软件架构模型:
matlab
m = systemcomposer.createModel('a18_soa_sc','softwarearchitecture'); % 注意模型类型
m.linkDictionary('a18_sc_dict.sldd');
prov = m.Architecture.addComponent('DynoProvider');
cons = m.Architecture.addComponent('TelemetryConsumer');
arch.addPort('TOP_server','server'); arch.addPort('TOP_client','client');
pP = prov.OwnedArchitecture.addPort('P_server','server');
pC = cons.OwnedArchitecture.addPort('C_client','client');
pP.setInterface(si); pC.setInterface(si); % 成功

上图是真实模型截图(R2025b 无头 print):两个组件、两类端口------P_server(Server 方向)/C_client(Client 方向) ,以及一个数据端口 P_data(Input)。重载复核的端口清单:
text
S3 reload: comps=2 topPorts=2 connectors=0
comp DynoProvider port P_data dir=Input iface=(none)
comp DynoProvider port P_server dir=Server iface=IF_Dyno
comp TelemetryConsumer port C_client dir=Client iface=IF_Dyno
4. 端口方向的语义强约束:服务接口不许挂在数据端口上
这是本篇最"省事"的一条实测------不用背规则,M文件会告诉你:
matlab
pPi = prov.OwnedArchitecture.addPort('P_data','in');
pPi.setInterface(si);
text
Unable to associate service interface 'IF_Dyno' with a port that is not a
client-server port 'P_data'
同一套约束在模型类型层面还有一层。AutosarArchitecture(Classic 用的那个类型)里建 client/server 端口会被拦住:
text
Unable to create client-server ports for models that are not software architecture models.
两条报错合起来说明一件事:"服务"这件事在 MATLAB 里不是接口对象的属性,而是"模型类型 + 端口方向 + 接口类型"三者的一致性约束。想画一张"SOA 架构图"很容易,想让工具接受它,得三样都对上。
顺带把导出边界也测了------软件架构模型不能直接走 Classic 那条导出链:
text
Unable to export 'a18_soa_sc' because the root model is not an AUTOSAR architecture model.
5. 实现侧:Adaptive 服务真的能生成出来
架构图落不到实现,还是 PPT。Adaptive 侧的配方由报错原文一步步逼出来,全脚本在 a18_gen.m:
| 步 | 动作 | 关键点 |
|---|---|---|
| 1 | 目标是 Adaptive 平台 | set_param(mdl,'SystemTargetFile','autosar_adaptive.tlc') |
| 2 | 端口必须是函数端口 | Function Element 块 = Client-Server 端口(PortName=server) |
| 3 | 函数必须作用域绑定到端口 | Trigger:FunctionVisibility=port + ScopeName=端口名 |
| 4 | 模型必须是导出函数模型 | IsExportFunctionModel=on |
| 5 | 先映射再构建 | autosar.api.create(mdl) → slbuild(mdl) |
第 3 步是真正的门槛。第一次只把函数放在根级,构建直接给判据:
text
Model 'a18_adp' is configured for AUTOSAR Adaptive Platform but contains
scoped Simulink Function(s) 'a18_adp/GetState' at the root level. These are
not supported for AUTOSAR Adaptive Platform. Use port scoped Simulink
Functions and Client-Server ports to model Method communication.
改法就是 set_param(trg,'ScopeName','server') 再 set_param(trg,'FunctionVisibility','port')(顺序不能反 :先设可见性为 port 而作用域名还是空串,报 作用域名称 '' 为空)。

构建落盘 17 个产物 (a18_adaptive_server_autosar_adaptive/):
text
main.cpp (4.3 KB) ------ AP 进程入口
a18_adaptive_server.cpp (1.8 KB) ------ 算法实现
stub/aragen/server_skeleton.h (3.0 KB) ------ ara::com 服务骨架
stub/aragen/server_skeleton_impl.h (1.7 KB)
stub/aragen/server_araDynamicMethodDDS.h (1.4 KB)
stub/aragen/server_common.h (1.1 KB)
a18_adaptive_server_interface.arxml (3.5 KB) ------ SERVICE-INTERFACE
a18_adaptive_server_ExecutionManifest.arxml (10.9 KB) ------ 进程/可执行
a18_adaptive_server_ServiceInstanceManifest.arxml (7.1 KB)
stub/MachineManifest.arxml (6.7 KB) ------ 机器/网络端点
CMakeLists.txt (8.4 KB) ------ POSIX 目标构建
骨架里是真的 ara::com (节选自 stub/aragen/server_skeleton.h):
cpp
class ServerSkeleton {
public:
ServerSkeleton(ara::com::InstanceIdentifier instance, ara::com::
MethodCallProcessingMode mode = ara::com::
MethodCallProcessingMode::kEvent);
inline ara::core::Result<void> OfferService() {
ara::com::ServiceFactory::CreateService(mHndl);
...
}
virtual ara::core::Future<methods::GetState::Output> GetState(const double u) = 0;
};
脚本对骨架做的符号计数(可复核):ara::com::ServiceFactory 2 处、OfferService 3 处、StopOfferService 2 处、ara::core::Future 1 处、MethodCallProcessingMode 1 处、ResolveInstanceIDs 1 处。
ARXML 里的服务契约 (节选自 a18_adaptive_server_interface.arxml):
xml
<SERVICE-INTERFACE UUID="...">
<SHORT-NAME>server</SHORT-NAME>
<METHODS>
<CLIENT-SERVER-OPERATION UUID="...">
<SHORT-NAME>GetState</SHORT-NAME>
<ARGUMENTS>
<ARGUMENT-DATA-PROTOTYPE ...><SHORT-NAME>u</SHORT-NAME>
<TYPE-TREF DEST="STD-CPP-IMPLEMENTATION-DATA-TYPE">/AUTOSAR/StdTypes/double</TYPE-TREF>
<DIRECTION>IN</DIRECTION></ARGUMENT-DATA-PROTOTYPE>
<ARGUMENT-DATA-PROTOTYPE ...><SHORT-NAME>y</SHORT-NAME>
<DIRECTION>OUT</DIRECTION></ARGUMENT-DATA-PROTOTYPE>
</ARGUMENTS>
<FIRE-AND-FORGET>false</FIRE-AND-FORGET>
</CLIENT-SERVER-OPERATION>
</METHODS>
</SERVICE-INTERFACE>
脚本对该文件的解析计数:SERVICE-INTERFACE=1 / CLIENT-SERVER-OPERATION=1 / ARGUMENT-DATA-PROTOTYPE=2(IN 1 + OUT 1) / FIRE-AND-FORGET=false=1。三个命名规律一目了然:接口名 = 函数端口名(server) ,方法名 = Simulink Function 名(GetState) ,参数 = 函数原型里的入参/出参。
还有一条如实记录:slbuild 在生成完成后尾部抛了 要串联的数组的维度不一致(无头会话下 slbuild 返回值路径上的报错),但产物已完整落盘 ------判据仍以文件为准,不以返回值为准。这条与架构12"connect() 返回成功却不落盘"是同一种教训的镜像:工具说了什么不重要,落盘了什么才重要。
6. 真跑边界:9 项里 3 项 PASS、6 项被真实拦住

| # | 项目 | 结果 | 依据(报错原文/实测) |
|---|---|---|---|
| A | 服务接口建模(架构侧) | PASS | ServiceInterface + FunctionElement,原型/异步位可回读 |
| B | client/server 端口挂服务接口 | PASS | 仅 softwarearchitecture 模型可建,挂载成功 |
| C | 数据端口挂服务接口 | FAIL | ... not a client-server port 'P_data' |
| D | AutosarArchitecture 建 client/server |
FAIL | ... models that are not software architecture models |
| E | 软件架构模型导出 ARXML | FAIL | ... root model is not an AUTOSAR architecture model |
| F | archdata 平台映射(Adaptive) | FAIL | AUTOSARAdaptive is not a valid platform mapping(内置平台只有 AUTOSARClassic) |
| G | APM 读服务接口平台属性 | FAIL | Expected feature flag InterfaceDictionaryAUTOSARAdaptive to be featured on |
| H | Adaptive 服务代码生成(server) | PASS | 17 个产物 + SERVICE-INTERFACE + ExecutionManifest |
| I | Adaptive 客户端 proxy | FAIL | Client-Server port 'a18_adaptive_client/client' does not resolve to a Function Caller or port scoped Simulink Function block |
两个 FAIL 值得单独说:
- F/G 是"字典侧平台属性"这条路没开 :
Simulink.dictionary.archdata.create建出的ArchitecturalData对象可以建服务接口、可以挂AUTOSARClassic平台映射,但AUTOSARAdaptive不在允许值里;ARAdaptivePlatformMapping对象能构造(class正确),可一读平台属性就撞上未开启的 feature flag。工具链在位、开关未开------这是"展望"与"可用"之间的真实距离,不能含糊过去。 - I 是客户端 proxy 没通 :
Function Element Call块用get_param(...,'PortHandles')探到in=0 out=0,没有端口句柄就意味着那条"函数连接器"在无头脚本里建不出来,函数调用者自然"解析不到端口"。文档给的配方是Function Element Call↔Function-Call Subsystem之间要有函数连接器------手工在界面上拖是可行的,脚本化这一步本机没打通。
7. 与 Classic 链的关系:两条链,同一个判据

把架构12 的 Classic 链摆在一起看:
text
Classic: 架构模型 -> autosar.api.export -> 9 个 ARXML(含 ECU Extract) -> DaVinci/RTE [架构12: 47572 B]
Adaptive: 服务端口 + 作用域函数 -> slbuild(autosar_adaptive.tlc)
-> ara::com 骨架 + 服务/执行/实例清单 + CMakeLists + main.cpp [本篇: 17 个产物]
两条链的载体不同、产物语言不同(一个给 RTE,一个给 POSIX 进程),但收官判据完全一样:架构决策必须落成"可被下游工具读取的文件" 。这也解释了为什么本篇把大半篇幅花在"数文件、贴报错"上------SOA 在 MATLAB 里的"模样",不是一张架构图,而是四样东西:接口在字典、端点在架构、骨架在 C++、部署在清单。
工具链在位证据(本篇探针实测,文件/类确实存在):
text
autosar.mm.mm2ara.ARAGenerator / ServiceInterfaceWriter / ARABuilder
autosar.mm.mm2ara.+com.SomeipSerializerWriter ------ SOME/IP 序列化代码写入器
autosar.dictionary.ARAdaptivePlatformMapping ------ 字典侧 Adaptive 平台属性
autosar.internal.adaptive.soa.configureAdaptiveModelForSOA ------ SOA 一键配置入口
toolbox/rtw/targets/AUTOSAR/AUTOSAR/autosar_adaptive.tlc ------ Adaptive 系统目标文件
autosarlib "Adaptive Platform" / autosarlibara(ara::log) / autosarlibaprouting(Event Send/Receive)
其中 configureAdaptiveModelForSOA 可直接调用,本机对一个一进一出的最小模型实测报 Did not expect to get here------入口在、前置条件不明,这是一条留给后续版本的线头,不是可以写进交付文档的"已支持"。
8. 踩坑速查(全部真实报错)
| 症状(实测原文) | 根因 | 解法 |
|---|---|---|
... not a client-server port 'P_data' |
数据端口挂服务接口 | 服务接口只挂 client/server 端口 |
... models that are not software architecture models |
用 AutosarArchitecture 建 client/server 端口 |
换成 createModel(name,'softwarearchitecture') |
... root model is not an AUTOSAR architecture model |
拿软件架构模型走 Classic 导出 | Classic 导出只认 AUTOSAR 架构模型 |
AUTOSARAdaptive is not a valid platform mapping |
内置平台名只有 AUTOSARClassic |
Adaptive 平台属性走 feature flag 未开这条路,本篇如实记录 |
Expected feature flag InterfaceDictionaryAUTOSARAdaptive to be featured on |
字典侧 Adaptive 平台属性被闸门拦住 | 当前版本不可用;不要据此写"已支持" |
Use port scoped Simulink Functions and Client-Server ports ... |
根级作用域函数不被 Adaptive 支持 | Trigger 设 FunctionVisibility=port + ScopeName=端口名 |
作用域名称 '' 为空或不是有效的 ANSI C 标识符 |
先设 FunctionVisibility=port,作用域名还是空 |
先设 ScopeName,再设 FunctionVisibility |
模型 'a18_server' 和模型 'a18_adaptive_server' 中都定义了 Simulink 函数 'GetState' |
旧探针模型残留在模型目录,Simulink 函数跨模型重名 | 脚本开头清理同名旧模型 + 清 slprj 缓存 |
does not resolve to a Function Caller or port scoped Simulink Function block |
客户端函数端口没有函数连接器(PortHandles 为空) |
客户端 proxy 本机未打通,如实记录 |
要串联的数组的维度不一致(构建尾部) |
无头 slbuild 收尾阶段的报错 |
产物已落盘------判据以文件为准 |
9. 方法论:三条可迁移的结论
- "服务"是三元一致性,不是一个接口对象 :模型类型(
softwarearchitecture)+ 端口方向(client/server)+ 接口类型(ServiceInterface)必须同时对上;对不上的地方,工具会用报错原文告诉你哪一元错了------把报错当文档读,比翻手册快,这也是本专栏"真跑为准"最直接的收益; - 架构侧与实现侧是两条链,桥不由工具自动搭 :本篇 A/B 通了、H 通了,但 E/F/G/I 全断------"接口可查"不等于"服务可跑"。谁要在项目里承诺 SOA 落地,必须逐段给出"哪一段已通、哪一段靠人工、哪一段受闸门限制",否则就是把风险写成展望;
- 命名规律即追溯链 :接口名 = 函数端口名、方法名 = Simulink Function 名、参数 = 函数原型入参出参------这条一一对应关系一旦成立,架构16 要的"架构模型 ↔ 实现模型追溯"就不再是文档工作,而是可脚本化的字符串映射,可以进 CI 做一致性检查。
10. 本篇的可复现性
- 模型/字典:
a18_soa_sc.slx(软件架构模型)、a18_sc_dict.sldd(服务接口字典)、a18_ad_dict.sldd(archdata 平台映射试验)、a18_adaptive_server.slx(Adaptive 服务模型)、a18_adaptive_client.slx(客户端,未打通) - 脚本:
a18_gen.m(许可/目标文件 → 服务接口 → 软件架构模型 → archdata 平台映射 → Adaptive 代码生成 → 客户端边界 → 出图,一步一打点);探针a18_probe1..37.m(全部失败尝试与报错原文均留在日志里) - 日志:
a18_gen.log(每步 OK/FAIL、端口与接口回读、ARXML 标签计数、骨架符号计数、全部报错原文) - 产物:
models/a18_adaptive_server_autosar_adaptive/(17 个文件,C++ 骨架 + 4 个 ARXML + CMakeLists + main.cpp,可直接打开比对) - 统计口径:ARXML 计数 = 正则匹配标签出现次数;骨架符号计数 = 关键字
strfind次数;产物字节数取文件实际大小;Classic 对照数据取自架构12 的 9 个落盘 ARXML(47572 B) - 边界如实声明:客户端 proxy 未通 (函数连接器脚本化未成);字典侧 Adaptive 平台属性被 feature flag 拦 (
InterfaceDictionaryAUTOSARAdaptive);configureAdaptiveModelForSOA未跑通 (报Did not expect to get here);构建尾部有slbuild维度报错但产物完整
11. 收官:架构专栏走到"服务"这一站
「软件架构设计实战」从架构01 的"架构不是画框框"走到本篇,线索其实只有一条:让架构从"能被看懂"变成"能被工具验证" 。架构11 把框图变成可查询的模型,架构12 把模型变成可集成的 ARXML,架构13 把评审变成证据折算,本篇把"服务"落成 ara::com 骨架与清单------每一篇的收尾判据都是同一句话:产物在哪里。
Adaptive/SOA 这条线本机只走通了"提供方 + 服务描述 + 部署清单",代理一方和字典平台属性仍是开放的线头;mm2ara 的 SomeipSerializerWriter、configureAdaptiveModelForSOA 都还在工具箱里等着------下一次当 MATLAB 新版本把这些开关打开,本篇的脚本就是最短的回归测试 :跑一遍 a18_gen.m,数一数产物从 17 个变成了几个。
本篇全部命令真实执行,全部数字来自落盘文件与真实报错,模型、脚本、探针日志与生成产物随文开源。「软件架构设计实战」专栏与「基于MATLAB的AUTOSAR软件开发指南」互为前后篇------架构定结构,后者定实现,合起来才是完整的车载软件工程闭环。