背景
部门需要开发一款App用于演示VDC或者VIU的功能,进一步控制汽车部件做出反应
方案1
作为一个对硬件,甚至对汽车没一丝了解的Android开发,第一时间并不想跳出舒适圈,脑子里立马浮现出一个场景,同一局域网内直接使用Socket通信。说干就干,啪啪啪,编码完成,一个基于C/S架构的Demo完成,领导看了看,有没有一种可能,板子上不需要重新写程序,直接使用板子已有的someIp协议进行通讯。what?
什么是someIp协议
SOME/IP全称Scalableservice-Oriented Middleware over IP,基于IP的可扩展面向服务的中间件,是一种专用于汽车嵌入式的客户端/服务器通信机制,访问方式分别为事件通知event和RPC远程调用
someip的数据结构
Message ID(Server ID) :16bit,服务的ID,标识出一个服务;
Message ID(Method ID) :16bit,方法的ID,表示出一个方法;
Length:报文长度,32bit,标识从request ID到报文结束的总长度;
Request ID(Client ID) :客户端ID,16bit。区分不同的客户端;
Request ID(Session ID) :会话ID,区分同一个客户端的多次调用;
Protocol Version :协议的版本号,固定值为x01;
Interface Version:服务接口版本;
Message Type :报文类型,在AUTOSAR中,总共包含五种,包括REQUEST,REQUEST_NO_RETURN,NOTIFICATION,RESPONSE,ERROR;
Return Code :返回码,包括四种,REQUEST,REQUEST_NO_RETURN,NOTIFICATION,RESPONSE;
Payload :数据段,用于放置需要传输的数据
Android开发是否支持Someip协议
平时开发App,Android的主要的开发语言是java和kotlin,那如果我们能通过java直接构建Someip消息体是否就能正常通信了?然后悲哀的发现光是按标准组建消息体 就是一个难度不小的挑战,更别说还要能构建通信通道,正常解析数据。又是一顿google search,发现BMW开源了一个实现someip的库vSomeIp
https://github.com/COVESA/vsomeip
查看这个库的过程中,我发现了一个名字非常数据的文件CMakeLists.txt,Android开发中使用JNI的方式调用函数,通过Cmake构建so库好像都跟这个文件有关,那是不是意味着Android可以通过JNI 的方式调用vSomeIp库,从而实现与硬件的通讯
开整
1.环境
- Android Studio 4.1.2 (Gradle 6.5, JDK 1.8)
- CMake 3.18.1 (for boost-cmake)
2.依赖
- boost 1.71.0 (https://boostorg.jfrog.io/artifactory/main/release/1.71.0/source/)
- vsomeip3 : (https://github.com/GENIVI/vsomeip.git).
- boost-cmake: Used CMake adapted boost (https://github.com/Orphis/boost-cmake).
3.项目结构和配置
具体配置 可以参考 : (https://github.com/lixiaolia/ndk-someiplib)
4.延伸
基于上个git项目,其实已经能够完成单个应用内的someip通讯了,所以对于那个git项目已有的配置就不多做描述了。
但是要想与VDC或者VIU在一个局域网环境内通讯还要另外做配置。
4.1配置json文件
{
"unicast" : "192.168.56.101",
"netmask" : "255.255.255.0",
"logging" :
{
"level" : "debug",
"console" : "true",
"file" : { "enable" : "true", "path" : "/var/log/vsomeip.log" },
"dlt" : "true"
},
"applications" :
[
{
"name" : "World",
"id" : "0x1314"
}
],
"clients" :
[
{
"service" : "0x1234",
"instance" : "0x5678",
"unreliable" : [ 40000, 40002 ]
}
],
"routing" : "World",
"service-discovery" :
{
"enable" : "true",
"multicast" : "239.255.0.255",
"port" : "30490",
"protocol" : "udp",
"initial_delay_min" : "10",
"initial_delay_max" : "100",
"repetitions_base_delay" : "200",
"repetitions_max" : "3",
"ttl" : "3",
"cyclic_offer_delay" : "2000",
"request_response_delay" : "1500"
}
}
需要注意的几个点
1.unicast 主机地址,这个是你连上wifi后动态分配的IP地址
2.applications,这里定义你的clientId和你的项目名,注意routing和name和你调用jni函数传递的name需要保持一致,不然会找不到路由
3.clients 这里定义板子提供的serviceId,instance,和定义的端口,Client端和Service端必须对齐。否则无法订阅事件,也无法接收到单播事件
4.multicast 定义组播地址和端口号,以及一些其他配置
4.2定义配置文件的路径
File someipConfig=new File(getCacheDir(),"demosomeip.json");
try {
if (someipConfig.exists()){
someipConfig.delete();
}
someipConfig.createNewFile();
writeConfigToFile(someipConfig);
}catch (Exception e){
sendLog(e.toString());
}
try {
Os.setenv("VSOMEIP_CONFIGURATION", someipConfig.getAbsolutePath(), true);
} catch (ErrnoException e) {
e.printStackTrace();
}
private void writeConfigToFile(File file) throws Exception {
FileOutputStream fileOutputStream=new FileOutputStream(file);
String value=getJson("vsomeip-udp-client.json");
JSONObject jsonObject=new JSONObject(value);
jsonObject.put("unicast",getIP());
value=jsonObject.toString();
sendLog("配置文件信息:"+value);
byte[] bytes=value.getBytes();
fileOutputStream.write(bytes);
fileOutputStream.close();
}
private String getJson(String fileName){
StringBuffer stringBuffer=new StringBuffer();
AssetManager assetManager=getAssets();
try {
BufferedReader reader=new BufferedReader(new InputStreamReader(assetManager.open(fileName)));
String line;
while ((line=reader.readLine())!=null){
stringBuffer.append(line);
}
}catch (Exception e){
}
return stringBuffer.toString();
}
5.调用函数
public void requestService(String serviceId,String instanceId) 请求服务,会回调onAvailability
public void requestEvent(String serviceId,String instanceId,String groupId,String eventId) 请求事件
public void subscribeEventGroup(String serviceId,String instanceId,String groupId) 订阅事件组,注意订阅事件组之前务必先请求事件,不然可能会存在找不到事件组
public void sendSampleRequest(String serviceId,String instanceId,String methodId,byte[] payload) 发送RR请求
数据回调,和服务端发起的Notification都会回调
public void onMessage(int service_id, int instance_id, int method_id, int client_id, byte[] bytes)
结束
以上是Android作为client端 基于someip协议与硬件通讯的一个大致配置,本人已验证,可行,撒花~~