[C++,QT/Qml]37.Qt 프로세스간의 통신(IPC) 구현하기6(commonapi some/ip)
안녕하세요 고급 개발자가 꿈인 코린이 입니다.
오늘은 commonapi dbus 가 아닌 commonapi someip를 이용한 프로세스간 통신에 대한 내용을 다루어 보겠습니다.
some ip통신은 dbus와 같이 ipc통신 입니다.
ip 주소를 통해서 ipc통신이 가능하다는 내용인데 그렇기 때문에 두개의 디바이스에 인터넷만 연결되어 있다면
프로세스간 통신이 가능하게 됩니다.
vsomeip에 관한 내용은 아래 블로그에 아주 자세하게 설명되어 있어서 설명 부분은 따로 언급드리지 않고
실제 프로그래밍 적으로 통신하는 방법이랑 환경 설정을 위주로 다루어 보겠습니다.
https://jjeongil.tistory.com/177
아래는 제가 some/ip 예제 소스코드를 빌드한 버전 정보 입니다.
이것도 commonapi -dbus 와 마찬가지로 버전을 꼭 맞춰주어야 빌드 에러가 안나옵니다.
이전에 commonapi-dbus의 버전을 맞춰주지 않아서 빌드에러 났던 경험이 있었고 이 에러내용을 해결하기 위해서
여러가지 방법을 찾아보았는데 에러가나는 이유는 버전이 안맞아서 나오는 경우 였습니다.
그렇기 때문에 버전정보를 꼭 맞춰주시기 바랍니다.
아래는 제가 지금 사용하는 버전정보로 추후에 알수없는 에러가 나거나 그러면 버전정보를 업데이트 하도록 하겠습니다.
예제 소스코드는 아래의 버전으로도 빌드 에러가 안나고 잘작동하는 것을 알수 있어서 아래버전으로 맞추었습니다.
1. some/ip 라이브러리 버전 정보
아래의 사이트에 가서 1번부터 차례대로 실행을 해줍니다.
https://at.projects.genivi.org/wiki/pages/viewpage.action?pageId=5472320
some pi 통신을 하기위해서는 commonapi-core-runtime ,commonapi-someip-runtime, vSomeip 등의 라이브 러리가
필요합니다.
그래서 위의 사이트에서 이러한 라이브러리등을 다운 받을때 아래의 버전을 맞추어서 다운 받아주시면 됩니다.
버전 정보는 아래와 같습니다.
capicxx-someip-runtime 3.1.12.17 (2020년 1월 19일 기준 최신버전)
capicxx-core-runtime 3.1.12.6 (2020년 1월 19일 기준 최신버전)
vsomeip 2.14.16 (2020년 1월 19일 기준 최신버전)
그리고 someip 통신이 가능하도록 자동으로 소스코드를 만들어주는 generator도 버전을 맞추어 줍니다.
commonapi-generator 3.1.12.4
someip-generator 3.1.12.2
위의 버전을 다운 받을때 git을 이용해서 clone을 한경우에는 거의 최신버전이 받아 집니다.
그런데 최신버전이 위의 버전과 다를 때는 git checkout 3.1.12.17 명령어로 버전을 바꾸어 줄수가 있습니다.
그리고 각 generator를 받는 곳은 아래 url에서 버전에 맞는 generator를 받으시면 됩니다.
commonapi-generator 받는곳
https://github.com/GENIVI/capicxx-core-tools/releases
someip-generator 받는곳
https://github.com/GENIVI/capicxx-someip-tools/releases
이렇게 버전정보를 맞춰주시면 someip 통신을 하기위한 기본적인 환경세팅은 완료된 것입니다.
2. 소스코드 설명
someip통신을 위한 예제 소스는 commonapi-dbus소스와 같습니다.
someip 예제 소스코드를 제공하는 사이트에서도 dbus소스를 그대로 사용하라고 나와있습니다.
그래서 dbus소스를 그대로 사용하시면 되는데 여기서 dbus와 차이점은 fdepl파일이 하나 추가가 되고
json파일이 추가가 된점이 다른 내용입니다.
이 fdepl은 fidl과 같이 someip 통신을 하기위해서 코드를 자동으로 만들어 주기위해서 인터페이스등을
지정하고 값을 설정하는 내용으로 이값을 설정해서 코드 generator 라이브러리를 돌려주면 someip통신이
가능한 소스코드가 자동으로 만들어 집니다.
json파일은 someip통신을 할 어떠한 앱이 실행될때 vsomeipd 데몬에 이 앱에 관련된 설정을 하기위해서
만들어주는 파일입니다. 그래서 앱을 실행 시킬때 이 json파일은 파싱하도록 하는 명령어를 실행후 앱을 실행하게 됩니다.
일단 fidl부터 설명을 드리겠습니다.
HelloWorld.fidl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package commonapi
interface HelloWorld {
version {major 1 minor 0}
method sayHello {//사용할 함수 이름
in {
String name //sayHello라는 함수의 인자
}
out {
String message//sayHello라는 함수를 호출했을때의 리턴값
}
}
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
|
HelloWorld.fdepl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
import "platform:/plugin/org.genivi.commonapi.someip/deployment/CommonAPI-SOMEIP_deployment_spec.fdepl"
import "HelloWorld.fidl"
define org.genivi.commonapi.someip.deployment for interface commonapi.HelloWorld {
SomeIpServiceID = 4660 // 각서비스의 고유 id
method sayHello {
SomeIpMethodID = 32000 //0-32767 사이값으로 설정해야 합니다. 다른 값으로 설정하면 소스코드 generator 에러가 발생 합니다.
}
}
define org.genivi.commonapi.someip.deployment for provider MyService {
instance commonapi.HelloWorld {
InstanceId = "test" //각 서비스의 instance id // 이 내용과 HelloWorldService.cpp의 registerservice 할때의 인자 값이 맞아야 합니다.
SomeIpInstanceID = 22136 //각 서비스의 someip instance id
}
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
|
HelloWorldClient.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
#include <iostream>
#include <string>
#include <unistd.h>
#include <CommonAPI/CommonAPI.hpp>
#include <v1/commonapi/HelloWorldProxy.hpp> // generator 최신 버전에는 경로가 v1입니다.
using namespace v1::commonapi; //generator 최신 버전에는 namesapce가 v1으로 설정되어 있음
int main() {
std::shared_ptr < CommonAPI::Runtime > runtime = CommonAPI::Runtime::get();
std::shared_ptr<HelloWorldProxy<>> myProxy = runtime->buildProxy<HelloWorldProxy>("local", "test");//서버와 연결할 프록시를 만들어 줍니다.
// 위의 코드에서 test와 local은 도메인과 인스턴스를 넣은것으로 서버에서 등록한 서비스와 동일해야 통신이 가능합니다.
// 여기서 test는 인턴스 id 임의로 fdepl 에서 지정한 instance id를 넣어주어야 합니다.
std::cout << "Checking availability!" << std::endl;
while (!myProxy->isAvailable())//만들어준 프록시가 사용 가능한지 가능하지 않다면 대기
usleep(10);
std::cout << "Available..." << std::endl;
CommonAPI::CallStatus callStatus;
std::string returnMessage;
myProxy->sayHello("Bob", callStatus, returnMessage);//fidl에 등록한 함수를 호출해준다. fidl에서 string형식을 인자로 넣어주고
//string형식으로 리턴받기 때문에 bob이라는 문자열을 넣어주고 리턴받을 변수를 넣어준다.
std::cout << "Got message: '" << returnMessage << "'\n";//리턴받은 변수를 출력한다.
return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
|
HelloWorldServer.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
#include <iostream>
#include <thread>
#include <CommonAPI/CommonAPI.hpp>
#include "HelloWorldStubImpl.hpp"
using namespace std;
int main() {
std::shared_ptr<CommonAPI::Runtime> runtime = CommonAPI::Runtime::get();
std::shared_ptr<HelloWorldStubImpl> myService = std::make_shared<HelloWorldStubImpl>();
if(runtime->registerService("local", "test", myService)){//서비스를 등록하고 클라이언트에서 호출할 함수를 재정의할 impl클래스를 등록한다.
std::cout << "Service registered." << std::endl;//서비스가 등록되면 출력할 로그
}else{
std::cout << "Service not registered." << std::endl;
}
std::cout << "Successfully Registered Service!" << std::endl;
while (true) {//클라이언트에서 신호를 보낼때까지 무한대기
std::cout << "Waiting for calls... (Abort with CTRL+C)" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(30));
}
return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
|
HelloWorldStubImpl.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#ifndef HELLOWORLDSTUBIMPL_H_
#define HELLOWORLDSTUBIMPL_H_
#include <CommonAPI/CommonAPI.hpp>
#include <v1/commonapi/HelloWorldStubDefault.hpp>
class HelloWorldStubImpl: public v1::commonapi::HelloWorldStubDefault {//HelloWorldStubDefault클래스를 상속 받습니다.
// HelloWorldStubDefault 클래스에 fidl로 설정한 함수들이 있어서 함수를 상속받으면 클라이언트에서 호출시 현재 클래스의 함수가 출력이 되도록 연결이 됩니다.
public:
HelloWorldStubImpl();
virtual ~HelloWorldStubImpl();
virtual void sayHello(const std::shared_ptr<CommonAPI::ClientId> _client, std::string _name, sayHelloReply_t _return);//sayHello라는 함수를 상속 받습니다.
//함수 재정의를 할수 있도록 상속 받습니다.
};
#endif /* HELLOWORLDSTUBIMPL_H_ */
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
|
HelloWorldStubImpl.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#include "HelloWorldStubImpl.hpp"
HelloWorldStubImpl::HelloWorldStubImpl() { }
HelloWorldStubImpl::~HelloWorldStubImpl() { }
void HelloWorldStubImpl::sayHello(const std::shared_ptr<CommonAPI::ClientId> _client, std::string _name, sayHelloReply_t _reply) {//상속받은 함수를 재정의 한다.
std::stringstream messageStream;
messageStream << "Hello " << _name << "!";
std::cout << "sayHello('" << _name << "'): '" << messageStream.str() << "'\n";
_reply(messageStream.str());//fidl에 정의한대로 문자열을 린턴하기 위해 매개변수에 넣어줌
};
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
|
이 코드에서는 함수 재정의를 통해서 클라이언트에서 특정한 함수를 호출했을때 동작 로직을 구현하는 부분 입니다.
코드 generator를 통해서 나온 코드를 상속 받으면 클라이언트에서 함수를 호출했을때 상속받은 함수가 출력이 되도록
연결이 되어있습니다.
이렇게해서 실행 결과는 genivi에서 제공한 결과와 같이 나옵니다.
첫번째 사진은 서버 실행 로그 입니다.
두번째사진은 클라이언트 실행 로그입니다.
두번째 이미지를 보시면 Hello bob!이 출력되어 있는것을 볼수가 있습니다.
이 파란색으로 표시된 부분은
이미지가 연속으로 두개가 출력이되면
웹에서 에러가 발생되어서
따로 추가하였습니다.
본문의 내용과 무관한 내용 입니다.
참고해주시면 감사하겠습니다.
여기까지 기본 someip 예제코드를 실행 시켜보았는데요
다음 시간에는 someip json파일에 대해서 깊히 살펴볼 예정이고 앞으로의 포스팅에서는 두개의 디바이스에서
서로 통신하는 내용을 다루어 보겠습니다.
여기까지 읽어주셔서 감사합니다.!