[Platinum UPnP] 101 :: (2) Device에 Service 올리기 + StateVariable & Event
[Platinum UPnP] 101 :: (1) 간단한 Device 만들기
[Platinum UPnP] 101 :: (2) Device에 Service 올리기 + StateVariable & Event
[Platinum UPnP] 101 :: (3) Service에 Action 추가하기
[Platinum UPnP] 101 :: (4) 간단한 Control-Point 만들기
※ 주의: 앞으로 나오는 이상한 문자열이나 XML에 대해서 친절하게 설명하지 않습니다. 눈치것 의미를 알아차리시거나, 스펙문서를 통해 의미를 파악해 주시기 바랍니다. 이 문서의 목적은 Platinum UPnP를 이용해 잘 동작하는 Tutorial 프로그램을 작성하는 것이니까요.
파일: tutorial02.cpp
앞에서 우리는 간단한 Device를 만들어서 확인해 보았습니다. 하지만, UPnP에서 Device만으로는 아무런 소용이 없습니다. UPnP는 Device가 소유하고 있는 Service를 통해서만 Device를 조작할 수 있습니다. 이러한 동작을 위해 Service는 다시 Action과 StateVariable, Event라는 개념을 갖고 있습니다. Action은 외부에서의 명령, StateVariable은 말 그대로 상태값(음량, 음소거 상태 등등)이며, Event는 StateVariable의 변경이 일어났을 때, 만약 그것을 외부에 알리도록 설정되어 있다면 변경을 공지합니다.
아마 Windows의 COM/DCOM/ActiveX 개념을 알고 있다면, 위의 내용을 이해하기 훨씬 쉬울 겁니다. 임의의 바이너리를 범용적으로 연결하여 동작하기 위한 기본 원리는 완벽하게 동일하다고 말할 수 있겠네요. 뭐, 모른다면 할 수 없구요 ㅋ
이번 작업은 앞서 했던 파일(tutorial01.cpp)을 바탕으로 수정하도록 하겠습니다.
먼저, 성의가 없었던 Device의 생성자를 살짝 수정합니다. 수정된 코드는 아래와 같습니다.
TutorialDevice() : PLT_DeviceHost("/", "", "urn:schemas-upnp-org:device:Tutorial:1", "Tutorial Device") { }
이제 Service를 생성해야 합니다. Service는 XML 형식의 Service Description에 의해 그 형태가 결정 됩니다. Service Description에는 그 Service가 갖는 Action과 StateVariable, Event에 대해 기술되어 있습니다.
일단 복잡함을 피하기 위하여, StateVariable과 Event에 대한 동작을 먼저 만들어 보겠습니다. 지금 구현할 동작을 명세하자면, 콘솔 입력을 통해 문자열을 입력 받은 후 그것을 StateVariable에 저장하여 Event가 발생하는지를 확인합니다.
우선 Service Description XML을 준비해야 합니다. 그래서 아래와 같은 변수를 정의합니다.
const char* SCPDXML_TUTORIAL = "<?xml version=\"1.0\" ?>" " <scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">" " <specversion>" " <major>1</major>" " <minor>0</minor>" " </specversion>" " <servicestatetable>" " <statevariable sendevents=\"yes\">" " <name>PrintBuffer</name>" " <datatype>string</datatype>" " <defaultvalue></defaultvalue>" " </statevariable>" " </servicestatetable>" " </scpd>";
위의 XML에서는 현재 Service 안에 문자열(string)로 취급되는 "PrintBuffer"라는 이름의 StateVariable이 하나 있으며, 변경시 Event가 발생한다는 것을 알려주고 있습니다. 이제 Device에 이 Service를 등록해야 합니다.
전 단계에서 PLT_DeviceHost 클래스의 자식 클래스를 만들기 위해 정의했던 함수가 있습니다. 바로 SetupServices() 함수 였습니다. 기억하시는지요? Device에 Service를 등록하기 위해서는 이 함수 안에서 등록해 주어야 합니다. 그래서 아래와 같이 SetupServices() 함수에 내용물을 넣습니다.
virtual NPT_Result SetupServices(void) { PLT_Service* service = new PLT_Service( this, "urn:schemas-upnp-org:service:Tutorial:1", "urn:upnp-org:serviceId:Tutorial.001", "Tutorial"); service->SetSCPDXML(SCPDXML_TUTORIAL); AddService(service); service->SetStateVariable("Status", "True"); return NPT_SUCCESS; }
이렇게 함으로써 "urn:upnp-org:serviceId:Tutorial.001"이라는 아이디와 "Tutorial"이라는 이름을 갖는 Service가 Device에 등록 되었습니다.
StateVariable과 Event가 정상 동작하는지 판별하기 위해서는 수동으로 값을 변경해 주어야 할 필요가 있습니다. 그래서 아래와 같이 외부에서 "PrintBuffer"를 조작하기 위한 함수를 하나 만들겠습니다.
void SetPrintBuffer(const char* str) { PLT_Service* service; if (FindServiceByName("Tutorial", service) == NPT_SUCCESS) { service->SetStateVariable("PrintBuffer", str); } }
FindServiceByName() 함수는 PLT_DeviceHost의 부모 클래스인 PLT_DeviceData의 멤버함수 입니다. 위 함수 이외에도 다양한 방법으로 Device가 소유한 Service를 찾을 수 있습니다. Service를 찾고나면 원하는 StateVariable에 입력값을 저장합니다.
마지막으로 콘솔에서 값을 입력 받아 위에서 정의한 SetPrintBuffer() 함수를 호출하도록 main() 함수의 while loop 구문을 아래와 같이 수정합니다.
bool stop = false; do { std::string input; std::cin >> input; if (input == "q" || input == "Q") { stop = true; } else { tutorial_device->SetPrintBuffer(input.c_str()); } } while(!stop);
이제 모든 작업이 완료 되었습니다. 코드를 빌드하여 실행시킨 후 StateVariable을 변경시켜 Event가 발생하는지 Device Spy로 확인할 차례입니다. 여기서 주의할 점은 Device Spy의 해당 서비스에서 마우스 오른쪽 버튼으로 컨텍스트 메뉴를 연 뒤 "Subscribe to Events" 항목을 클릭해야 해당 Service의 Event가 Device Spy에 나타나게 됩니다.
확인해 본 결과 저는 아주 잘 동작하고 있습니다. 여러분은 어떠신가요?
'오픈소스 > Network' 카테고리의 다른 글
[Platinum UPnP] 101 :: (5) Device 연결하고 Action 호출하기 (0) | 2013.03.02 |
---|---|
[Platinum UPnP] 101 :: (4) 간단한 Control-Point 만들기 (0) | 2013.03.02 |
[Platinum UPnP] 101 :: (3) Service에 Action 추가하기 (0) | 2013.03.01 |
[Platinum UPnP] 101 :: (1) 간단한 Device 만들기 (0) | 2013.03.01 |
[Platinum UPnP] 101 :: (0) 소개 (0) | 2013.03.01 |