背景
部門需要開發(fā)一款App用于演示VDC或者VIU的功能镀脂,進一步控制汽車部件做出反應
方案1
作為一個對硬件,甚至對汽車沒一絲了解的Android開發(fā)纱新,第一時間并不想跳出舒適圈由捎,腦子里立馬浮現(xiàn)出一個場景商叹,同一局域網(wǎng)內(nèi)直接使用Socket通信燕刻。說干就干,啪啪啪剖笙,編碼完成卵洗,一個基于C/S架構(gòu)的Demo完成,領(lǐng)導看了看弥咪,有沒有一種可能过蹂,板子上不需要重新寫程序,直接使用板子已有的someIp協(xié)議進行通訊酪夷。what?
什么是someIp協(xié)議
SOME/IP全稱Scalableservice-Oriented Middleware over IP榴啸,基于IP的可擴展面向服務的中間件,是一種專用于汽車嵌入式的客戶端/服務器通信機制晚岭,訪問方式分別為事件通知event和RPC遠程調(diào)用
someip的數(shù)據(jù)結(jié)構(gòu)
Message ID(Server ID) :16bit,服務的ID勋功,標識出一個服務坦报;
Message ID(Method ID) :16bit库说,方法的ID,表示出一個方法片择;
Length:報文長度潜的,32bit,標識從request ID到報文結(jié)束的總長度字管;
Request ID(Client ID) :客戶端ID啰挪,16bit。區(qū)分不同的客戶端嘲叔;
Request ID(Session ID) :會話ID亡呵,區(qū)分同一個客戶端的多次調(diào)用;
Protocol Version :協(xié)議的版本號硫戈,固定值為x01;
Interface Version:服務接口版本锰什;
Message Type :報文類型,在AUTOSAR中丁逝,總共包含五種汁胆,包括REQUEST,REQUEST_NO_RETURN霜幼,NOTIFICATION嫩码,RESPONSE,ERROR罪既;
Return Code :返回碼谢谦,包括四種,REQUEST萝衩,REQUEST_NO_RETURN回挽,NOTIFICATION,RESPONSE猩谊;
Payload :數(shù)據(jù)段千劈,用于放置需要傳輸?shù)臄?shù)據(jù)
Android開發(fā)是否支持Someip協(xié)議
平時開發(fā)App,Android的主要的開發(fā)語言是java和kotlin,那如果我們能通過java直接構(gòu)建Someip消息體是否就能正常通信了牌捷?然后悲哀的發(fā)現(xiàn)光是按標準組建消息體 就是一個難度不小的挑戰(zhàn)墙牌,更別說還要能構(gòu)建通信通道,正常解析數(shù)據(jù)暗甥。又是一頓google search喜滨,發(fā)現(xiàn)BMW開源了一個實現(xiàn)someip的庫vSomeIp
https://github.com/COVESA/vsomeip
查看這個庫的過程中,我發(fā)現(xiàn)了一個名字非常數(shù)據(jù)的文件CMakeLists.txt撤防,Android開發(fā)中使用JNI的方式調(diào)用函數(shù)虽风,通過Cmake構(gòu)建so庫好像都跟這個文件有關(guān),那是不是意味著Android可以通過JNI 的方式調(diào)用vSomeIp庫,從而實現(xiàn)與硬件的通訊
開整
1.環(huán)境
- 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.項目結(jié)構(gòu)和配置
具體配置 可以參考 : (https://github.com/lixiaolia/ndk-someiplib)
4.延伸
基于上個git項目辜膝,其實已經(jīng)能夠完成單個應用內(nèi)的someip通訊了无牵,所以對于那個git項目已有的配置就不多做描述了。
但是要想與VDC或者VIU在一個局域網(wǎng)環(huán)境內(nèi)通訊還要另外做配置厂抖。
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后動態(tài)分配的IP地址
2.applications,這里定義你的clientId和你的項目名忱辅,注意routing和name和你調(diào)用jni函數(shù)傳遞的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.調(diào)用函數(shù)
public void requestService(String serviceId,String instanceId) 請求服務,會回調(diào)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) 發(fā)送RR請求
數(shù)據(jù)回調(diào),和服務端發(fā)起的Notification都會回調(diào)
public void onMessage(int service_id, int instance_id, int method_id, int client_id, byte[] bytes)
結(jié)束
以上是Android作為client端 基于someip協(xié)議與硬件通訊的一個大致配置踢关,本人已驗證伞鲫,可行,撒花~~