[Platinum UPnP] 101 :: (3) Service에 Action 추가하기




※ 주의: 앞으로 나오는 이상한 문자열이나 XML에 대해서 친절하게 설명하지 않습니다. 눈치것 의미를 알아차리시거나, 스펙문서를 통해 의미를 파악해 주시기 바랍니다. 이 문서의 목적은 Platinum UPnP를 이용해 잘 동작하는 Tutorial 프로그램을 작성하는 것이니까요.


파일: tutorial03.cpp


우리는 이제까지 Device를 만들어서 Service를 등록하고 Event가 발생하도록 StateVariable을 조작하는 것까지 해보았습니다.  이번에는 Device에 Action을 추가해 보겠습니다. Action의 기능은 입력 값을 문자열로 받아서 앞서 사용했던 "PrintBuffer" StateVariable의 값을 변경하고 콘솔에 변경된 값을 출력하는 것으로 하겠습니다. 이렇게 하면 Action이 정상적으로 동작 했는지에 대해 콘솔과 Event 양쪽에서 모두 확인할 수 있을 것입니다.


    우선 Service Description XML에 Action에 대한 내용을 추가합니다. 수정된 내용은 아래와 같습니다.


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>"
	"    <actionList>"
	"      <action>"
	"        <name>Print</name>"
	"        <argumentList>"
	"          <argument>"
	"            <name>String</name>"
	"            <direction>in</direction>"
	"            <relatedStateVariable>PrintBuffer</relatedStateVariable>"
	"          </argument>"
	"        </argumentList>"
	"      </action>"
	"    </actionList>"
	"    <serviceStateTable>"
	"      <stateVariable sendEvents=\"yes\">"
	"        <name>PrintBuffer</name>"
	"        <dataType>string</dataType>"
	"        <defaultValue></defaultValue>"
	"      </stateVariable>"
	"    </serviceStateTable>"
	"  </scpd>";


    변경 내용을 간략히 살펴보자면, "ActionList" Tag가 추가되고 그 안에 "Print"라는 이름의 Action이 하나 있습니다. 이 Action은 "String"이라는 입력값을 갖으며, 값의 형태는 "PrintBuffer" StateVariable의 정의를 따라갑니다. 스펙을 보면 아시겠지만, UPnP에서 Action의 입출력 값들의 타입은 무조건 StateVariable 중 하나와 연결되어야 하는 것으로 보입니다.

    이 상태로 컴파일 후 실행하면, Device Spy에서 Action이 추가된 것을 확인할 수 있습니다. 하지만, 현재로써는 실제로 Action이 동작하지 않습니다. 그래서 이제 Action의 구현 코드를 넣어야 합니다.

    PLT_DeviceHost 클래스에는 OnAction()이라는 virtual 함수가 있습니다. 이 함수의 내용을 오버라이딩(Overriding)하여 원하는 Action이 동작하도록 하여야 합니다. Tutorial Device 클래스에 추가된 코드는 다음과 같습니다.


virtual NPT_Result OnAction(PLT_ActionReference& action, const PLT_HttpRequestContext& context)
{
	PLT_ActionDesc& desc = action->GetActionDesc();
	const NPT_String name = desc.GetName();
	if (name == "Print") {
		NPT_String value;
		action->GetArgumentValue("String", value);
		PLT_Service* service = desc.GetService();
		service->SetStateVariable("PrintBuffer", value);
		printf("[Action:%s] (String, %s)\n", name, value);
		return NPT_SUCCESS;
	}
	return PLT_DeviceHost::OnAction(action, context);
}


    위 코드의 내용은 다음과 같습니다. OnAction() 함수가 호출되면 Action의 이름이 "Print"가 맞는지 확인합니다. 맞지 않다면 부모 클래스의 기본 OnAction() 함수를 수행합니다. 맞다면 다음과 같이 "Print" Action을 수행합니다. 호출된 Action에서 "String"의 값을 읽어 옵니다. 그리고 읽어온 값을 Service의 "PrintBuffer" StateVariable에 적용합니다. 마지막으로 수행한 Action과 입력값을 콘솔에 출력합니다.


    이제 UPnP에서 정의하는 기본적인 동작을 모두 갖춘 하나의 Device가 완성되었습니다. 다음에는 간단한 Control-Point를 하나 만들어 보겠습니다.