카테고리 없음
sdbus예제 2
2025. 12. 26. 17:00반응형
# Adaptor (서버 측) 코드 생성
sdbus-c++-xml2cpp calculator.xml --adaptor=calculator-adaptor.h
# Proxy (클라이언트 측) 코드 생성
sdbus-c++-xml2cpp calculator.xml --proxy=calculator-proxy.h
xml
<?xml version="1.0" encoding="UTF-8"?>
<node name="/org/example/Calculator">
<interface name="org.example.Calculator">
<method name="Add">
<arg name="a" type="i" direction="in"/>
<arg name="b" type="i" direction="in"/>
<arg name="result" type="i" direction="out"/>
</method>
<method name="Sub">
<arg name="a" type="i" direction="in"/>
<arg name="b" type="i" direction="in"/>
<arg name="result" type="i" direction="out"/>
</method>
<method name="Multiply">
<arg name="a" type="i" direction="in"/>
<arg name="b" type="i" direction="in"/>
<arg name="result" type="i" direction="out"/>
</method>
<method name="Divide">
<arg name="a" type="i" direction="in"/>
<arg name="b" type="i" direction="in"/>
<arg name="result" type="i" direction="out"/>
</method>
<signal name="Calculated">
<arg name="operation" type="s"/>
<arg name="result" type="i"/>
</signal>
</interface>
</node>
cmake list
cmake_minimum_required(VERSION 3.12)
project(calculator_sdbus_cpp CXX)
# C++ 표준 설정
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 컴파일 옵션
add_compile_options(-Wall -Wextra)
# pkg-config 사용
find_package(PkgConfig REQUIRED)
# sdbus-c++ 찾기
pkg_check_modules(SDBUSCPP REQUIRED sdbus-c++)
# XML 파일 경로
set(CALCULATOR_XML ${CMAKE_CURRENT_SOURCE_DIR}/calculator.xml)
# 코드 생성 명령 (옵션)
# find_program(SDBUSCPP_XML2CPP sdbus-c++-xml2cpp)
# if(SDBUSCPP_XML2CPP)
# # Adaptor 생성
# add_custom_command(
# OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/calculator-adaptor.h
# COMMAND ${SDBUSCPP_XML2CPP} ${CALCULATOR_XML}
# --adaptor=${CMAKE_CURRENT_BINARY_DIR}/calculator-adaptor.h
# DEPENDS ${CALCULATOR_XML}
# COMMENT "Generating adaptor from XML"
# )
#
# # Proxy 생성
# add_custom_command(
# OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/calculator-proxy.h
# COMMAND ${SDBUSCPP_XML2CPP} ${CALCULATOR_XML}
# --proxy=${CMAKE_CURRENT_BINARY_DIR}/calculator-proxy.h
# DEPENDS ${CALCULATOR_XML}
# COMMENT "Generating proxy from XML"
# )
# endif()
# 서버 실행 파일
add_executable(calculator-server
calculator-server.cpp
# ${CMAKE_CURRENT_BINARY_DIR}/calculator-adaptor.h # 생성된 헤더
)
target_include_directories(calculator-server PRIVATE
${SDBUSCPP_INCLUDE_DIRS}
${CMAKE_CURRENT_BINARY_DIR}
)
target_link_libraries(calculator-server
${SDBUSCPP_LIBRARIES}
)
target_link_directories(calculator-server PRIVATE
${SDBUSCPP_LIBRARY_DIRS}
)
target_compile_options(calculator-server PRIVATE
${SDBUSCPP_CFLAGS_OTHER}
)
# 클라이언트 실행 파일
add_executable(calculator-client
calculator-client.cpp
# ${CMAKE_CURRENT_BINARY_DIR}/calculator-proxy.h # 생성된 헤더
)
target_include_directories(calculator-client PRIVATE
${SDBUSCPP_INCLUDE_DIRS}
${CMAKE_CURRENT_BINARY_DIR}
)
target_link_libraries(calculator-client
${SDBUSCPP_LIBRARIES}
)
target_link_directories(calculator-client PRIVATE
${SDBUSCPP_LIBRARY_DIRS}
)
target_compile_options(calculator-client PRIVATE
${SDBUSCPP_CFLAGS_OTHER}
)
# 설치 규칙
install(TARGETS calculator-server calculator-client
RUNTIME DESTINATION bin
)
install(FILES ${CALCULATOR_XML}
DESTINATION share/dbus-1/interfaces
)
# 빌드 정보 출력
message(STATUS "sdbus-c++ version: ${SDBUSCPP_VERSION}")
message(STATUS "sdbus-c++ include dirs: ${SDBUSCPP_INCLUDE_DIRS}")
message(STATUS "sdbus-c++ libraries: ${SDBUSCPP_LIBRARIES}")
server.cpp
#include <sdbus-c++/sdbus-c++.h>
#include <iostream>
#include <csignal>
// 생성된 adaptor 헤더를 포함해야 하지만, 여기서는 직접 구현
class Calculator : public sdbus::AdaptorInterfaces<>
{
public:
Calculator(sdbus::IConnection& connection, std::string objectPath)
: AdaptorInterfaces(connection, std::move(objectPath))
{
// 인터페이스 등록
registerAdaptor();
}
~Calculator()
{
unregisterAdaptor();
}
protected:
// 메서드 구현
int32_t Add(int32_t a, int32_t b)
{
int32_t result = a + b;
std::cout << "Add called: " << a << " + " << b << " = " << result << std::endl;
// 시그널 발송
emitCalculated("Add", result);
return result;
}
int32_t Sub(int32_t a, int32_t b)
{
int32_t result = a - b;
std::cout << "Sub called: " << a << " - " << b << " = " << result << std::endl;
emitCalculated("Sub", result);
return result;
}
int32_t Multiply(int32_t a, int32_t b)
{
int32_t result = a * b;
std::cout << "Multiply called: " << a << " * " << b << " = " << result << std::endl;
emitCalculated("Multiply", result);
return result;
}
int32_t Divide(int32_t a, int32_t b)
{
if (b == 0) {
throw sdbus::Error("org.example.Calculator.DivisionByZero", "Division by zero");
}
int32_t result = a / b;
std::cout << "Divide called: " << a << " / " << b << " = " << result << std::endl;
emitCalculated("Divide", result);
return result;
}
void emitCalculated(const std::string& operation, int32_t result)
{
auto signal = createSignal("org.example.Calculator", "Calculated");
signal << operation << result;
emitSignal(signal);
}
private:
void registerAdaptor()
{
// Add 메서드 등록
registerMethod("org.example.Calculator", "Add", "ii", "i",
[this](sdbus::MethodCall call) {
int32_t a, b;
call >> a >> b;
auto reply = call.createReply();
reply << Add(a, b);
reply.send();
});
// Sub 메서드 등록
registerMethod("org.example.Calculator", "Sub", "ii", "i",
[this](sdbus::MethodCall call) {
int32_t a, b;
call >> a >> b;
auto reply = call.createReply();
reply << Sub(a, b);
reply.send();
});
// Multiply 메서드 등록
registerMethod("org.example.Calculator", "Multiply", "ii", "i",
[this](sdbus::MethodCall call) {
int32_t a, b;
call >> a >> b;
auto reply = call.createReply();
reply << Multiply(a, b);
reply.send();
});
// Divide 메서드 등록
registerMethod("org.example.Calculator", "Divide", "ii", "i",
[this](sdbus::MethodCall call) {
int32_t a, b;
call >> a >> b;
auto reply = call.createReply();
reply << Divide(a, b);
reply.send();
});
// Calculated 시그널 등록
registerSignal("org.example.Calculator", "Calculated", "si");
}
};
static bool running = true;
void signalHandler(int signal)
{
std::cout << "\nReceived signal " << signal << ", shutting down..." << std::endl;
running = false;
}
int main()
{
// 시그널 핸들러 설정
std::signal(SIGINT, signalHandler);
std::signal(SIGTERM, signalHandler);
try
{
// D-Bus 연결 생성
auto connection = sdbus::createSessionBusConnection();
// 서비스 이름 요청
connection->requestName("org.example.Calculator");
// Calculator 객체 생성
Calculator calculator(*connection, "/org/example/Calculator");
std::cout << "Calculator server started at org.example.Calculator" << std::endl;
std::cout << "Object path: /org/example/Calculator" << std::endl;
std::cout << "Press Ctrl+C to exit." << std::endl;
// 이벤트 루프
while (running)
{
connection->processPendingEvent();
}
std::cout << "Server stopped." << std::endl;
}
catch (const sdbus::Error& e)
{
std::cerr << "D-Bus error: " << e.what() << std::endl;
return 1;
}
catch (const std::exception& e)
{
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
client.cpp
#include <sdbus-c++/sdbus-c++.h>
#include <iostream>
#include <string>
#include <thread>
#include <chrono>
class CalculatorProxy : public sdbus::ProxyInterfaces<>
{
public:
CalculatorProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath)
: ProxyInterfaces(connection, std::move(destination), std::move(objectPath))
{
registerProxy();
}
~CalculatorProxy()
{
unregisterProxy();
}
// 메서드 호출
int32_t Add(int32_t a, int32_t b)
{
int32_t result;
auto method = createMethodCall("org.example.Calculator", "Add");
method << a << b;
auto reply = method.call();
reply >> result;
return result;
}
int32_t Sub(int32_t a, int32_t b)
{
int32_t result;
auto method = createMethodCall("org.example.Calculator", "Sub");
method << a << b;
auto reply = method.call();
reply >> result;
return result;
}
int32_t Multiply(int32_t a, int32_t b)
{
int32_t result;
auto method = createMethodCall("org.example.Calculator", "Multiply");
method << a << b;
auto reply = method.call();
reply >> result;
return result;
}
int32_t Divide(int32_t a, int32_t b)
{
int32_t result;
auto method = createMethodCall("org.example.Calculator", "Divide");
method << a << b;
auto reply = method.call();
reply >> result;
return result;
}
// 시그널 핸들러 등록
void onCalculated(std::function<void(const std::string&, int32_t)> callback)
{
registerSignalHandler("org.example.Calculator", "Calculated",
[callback](sdbus::Signal signal) {
std::string operation;
int32_t result;
signal >> operation >> result;
callback(operation, result);
});
}
private:
void registerProxy()
{
// 프록시 등록 완료
finishRegistration();
}
};
int main(int argc, char* argv[])
{
try
{
// D-Bus 연결 생성
auto connection = sdbus::createSessionBusConnection();
// Calculator 프록시 생성
CalculatorProxy calculator(*connection, "org.example.Calculator", "/org/example/Calculator");
// 시그널 핸들러 등록
calculator.onCalculated([](const std::string& operation, int32_t result) {
std::cout << "Signal received - Operation: " << operation
<< ", Result: " << result << std::endl;
});
// 시그널 수신을 위한 스레드
std::thread eventLoop([&connection]() {
while (true) {
connection->processPendingEvent();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
});
eventLoop.detach();
std::cout << "Calculator Client started" << std::endl;
std::cout << "Commands: add, sub, multiply, divide, quit" << std::endl;
// 대화형 루프
std::string command;
while (true)
{
std::cout << "\nEnter command: ";
std::cin >> command;
if (command == "quit" || command == "exit")
{
break;
}
int32_t a, b, result;
std::cout << "Enter first number: ";
std::cin >> a;
std::cout << "Enter second number: ";
std::cin >> b;
try
{
if (command == "add")
{
result = calculator.Add(a, b);
std::cout << "Result: " << a << " + " << b << " = " << result << std::endl;
}
else if (command == "sub")
{
result = calculator.Sub(a, b);
std::cout << "Result: " << a << " - " << b << " = " << result << std::endl;
}
else if (command == "multiply")
{
result = calculator.Multiply(a, b);
std::cout << "Result: " << a << " * " << b << " = " << result << std::endl;
}
else if (command == "divide")
{
result = calculator.Divide(a, b);
std::cout << "Result: " << a << " / " << b << " = " << result << std::endl;
}
else
{
std::cout << "Unknown command: " << command << std::endl;
}
}
catch (const sdbus::Error& e)
{
std::cerr << "D-Bus error: " << e.getName() << " - " << e.getMessage() << std::endl;
}
}
std::cout << "Client stopped." << std::endl;
}
catch (const sdbus::Error& e)
{
std::cerr << "D-Bus error: " << e.what() << std::endl;
return 1;
}
catch (const std::exception& e)
{
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
빌드 및 실행
# 프로젝트 구조
project/
├── calculator.xml
├── calculator-server.cpp
├── calculator-client.cpp
└── CMakeLists.txt
# 코드 생성 (수동)
sdbus-c++-xml2cpp calculator.xml --adaptor=calculator-adaptor.h
sdbus-c++-xml2cpp calculator.xml --proxy=calculator-proxy.h
# 빌드
mkdir build && cd build
cmake ..
make
# 서버 실행 (터미널 1)
./calculator-server
# 클라이언트 실행 (터미널 2)
./calculator-client
반응형
