Microsoft Robotics Developer Studio 고고 고고고고고 고고 [Part 5] CCR 고 DSS 고고고 고고고고고 2008 로로로로 로로 로로로로로로로 로 로 로 로로 [email protected]
Jan 03, 2016
Microsoft Robotics Developer Studio
고급 프로그래밍 과정
[Part 5] CCR 및 DSS 서비스 프로그래밍
2008로보틱스 그룹
마이크로소프트
김 영 준 수석[email protected]
2Microsoft Robotics Studio
목차
Part 1: CCR 프로그래밍 Part 2: 기본 DSS 서비스 개발 Part 3: 로봇 기본 서비스 개발 Part 4: 로봇 연결 서비스 개발 Part 5: UI 가 포함된 서비스 개발
3Microsoft Robotics Studio
CCR 프로그래밍
4Microsoft Robotics Studio
Dispatcher 와 DispatcherQueue
CCR 의 기본 처리 방식 뭔가 해야 할 일을 생성함 생성된 일을 작업 큐에 넣어 놓음 일을 처리하는 엔진이 큐에 있는 해야 할 일을 가져다가 처리함
CCR 에서는 뭔가 해야 할 일을 정의하기 위해 ITask 클래스 형태의 객체를 사용함
실제 일을 수행하는 엔진이라 할 수 있는 Dispacher 라는 것이 존재함 Dispacher 에 여러 개의 큐브를 만들어 놓을 수 있음 이 큐를 DispacherQueue 라고 함 이러한 큐들은 모두 Dispacher 가 관리를 함
5Microsoft Robotics Studio
Dispatcher 와 DispatcherQueue
6Microsoft Robotics Studio
Dispatcher 와 DispatcherQueue
private void button1_Click(object sender, EventArgs e) { //Dispather 를 생성한다 using (Dispatcher dispatcher = new Dispatcher(0, " 첫번째 예제 ")) { //Dispatcher 로 부터 DispatcherQueue 를 생성한다 . // 나중에 작업해야할 태스크들을 생성한 후 이 큐에 넣어 놓으면 CCR 엔진이 처리함 DispatcherQueue dq1 = new DispatcherQueue("DispatcherQueue1", dispatcher); DispatcherQueue dq2 = new DispatcherQueue("DispatcherQueue2", dispatcher); // 간단하게 Dispacher 와 DispacherQueues 의 정보들을 표시함 listBox1.Items.Clear(); listBox1.Items.Add("Dispacher 이름 : " + dispatcher.Name); listBox1.Items.Add("PendingTaskCount : " + dispatcher.PendingTaskCount.ToString()); listBox1.Items.Add("ProcessedTaskCount : " + dispatcher.ProcessedTaskCount.ToString()); listBox1.Items.Add("WorkerThreadCount : " + dispatcher.WorkerThreadCount.ToString()); listBox1.Items.Add("DispatcherQueue Count : " + dispatcher.DispatcherQueues.Count.ToString()); //Dispacher 에 있는 DispacherQueses 들을 나열함 foreach (DispatcherQueue dq in dispatcher.DispatcherQueues) { listBox1.Items.Add("DispatcherQueue in Dispacher : " + dq.Name); } } }
7Microsoft Robotics Studio
CCR 의 ITask
8Microsoft Robotics Studio
CCR 의 ITask
private void button1_Click(object sender, EventArgs e) { using (Dispatcher dispatcher = new Dispatcher(0, " 두번째 예제 ")) { DispatcherQueue dq = new DispatcherQueue("DispatcherQueue", dispatcher); //Define handler of Anonymous method Handler myTaskHandler = delegate { System.Windows.Forms.MessageBox.Show("Hello"); }; //Define ITask ITask myTask = Arbiter.FromHandler(myTaskHandler); //Insert into DispacherQueue dq.Enqueue(myTask); //You can Also execute ITask as follow //Arbiter.Activate(dq, myTask); } }
9Microsoft Robotics Studio
CCR 의 ITask
private void button2_Click(object sender, EventArgs e) { using (Dispatcher dispatcher = new Dispatcher(0, " 두번째 예제 ")) { DispatcherQueue dq = new DispatcherQueue("DispatcherQueue", dispatcher); //Define Itask using seperate handler procedure ITask myTask = Arbiter.FromHandler(myTimeHandler()); dq.Enqueue(myTask); // Or Arbiter.Activate(dq, myTask); } } //This handler is converted into the ITask private Handler myTimeHandler() { //Insert current Time into the ListBox listBox1.Items.Add(System.DateTime.Now.ToString()); return null; }
10Microsoft Robotics Studio
메시지를 전달하는 Portprivate void button1_Click(object sender, EventArgs e) { //Define Port with String Type //You also can use any type of class Port<String> myPort = new Port<string>(); myPort.Post("Test String 1"); myPort.Post("Test String 2"); myPort.Post("Test String 3"); myPort.Post("Test String 4"); myPort.Post("Test String 5"); //Pick out the Item from Port // 여러가지 방법으로 데이터를 꺼낼 수 있음 //case 1 <- "Test String 1" will be picked out String myStr1 = (String)myPort.Test(); listBox1.Items.Add(myStr1); //case 2 <- "Test String 2" will be picked out listBox1.Items.Add((String)myPort.Test()); //case 3 <- "Test String 3" will be picked out String myStr2 = null; myPort.Test(out myStr2); listBox1.Items.Add(myStr2); //case 4 <- "Test String 4, 5" will be picked out while (myPort.Test(out myStr2)) { listBox1.Items.Add(myStr2); } }
11Microsoft Robotics Studio
멀티 형식의 메세지를 전달하는 PortSet
private void button1_Click(object sender, EventArgs e) { PortSet<int, String> myPortSet = new PortSet<int, String>(); myPortSet.Post(1); //or myPortSet.P0.Post(2); myPortSet.Post("Hi"); //or myPortSet.P1.Post("Hello"); //Pick out from the first Port int retInt = 0; while (myPortSet.P0.Test(out retInt)) { listBox1.Items.Add("Int:" + retInt.ToString()); } //Pick out from the second Port String retStr = null; while (myPortSet.P1.Test(out retStr)) { listBox1.Items.Add("String:" + retStr); } }
12Microsoft Robotics Studio
Arbiter 를 이용해 Task 만들기
private void button1_Click(object sender, EventArgs e) { using (Dispatcher dispatcher = new Dispatcher(0, " 다섯번째 예제 ")) { DispatcherQueue dq = new DispatcherQueue("DispatcherQueue", dispatcher); // 헨들러 타입으로 선언되어 있는 것을 ITask 형식으로 변환시킴 ITask myTask = Arbiter.FromHandler(myHandler()); Arbiter.Activate(dq, myTask); // 또는 dq,Enqueue(myTask); } }
// 수행해야할 작업들이 기술되어 있는 헨들러임 private Handler myHandler() { // 현재의 시간을 리스트박스에 출력합니다 . listBox1.Items.Add("Now is " + System.DateTime.Now.ToString()); return null; }
13Microsoft Robotics Studio
Arbiter.Receive 를 이용해 메시지와 헨들러 연결하기
private void button1_Click(object sender, EventArgs e) { using (Dispatcher dispatcher = new Dispatcher(0, " 여섯번 째 예제 ")) { DispatcherQueue dq = new DispatcherQueue("DispatcherQueue", dispatcher); // 메세지를 받을 포트를 정의함 Port<myStatus> mySatusPort = new Port<myStatus>(); myStatus myStatus1 = new myStatus(); myStatus myStatus2 = new myStatus(); myStatus1.inputStr = "Test String 1"; // 포트에 추가함 mySatusPort.Post(myStatus1); // 해당 포트에 메시지가 수신되면 해당 헨들러가 수행되도록 하는 Task 를 생성함 ITask myTask = Arbiter.Receive(true, mySatusPort, myHandler); myStatus2.inputStr = "Test String 2"; //Insert the item into the Port mySatusPort.Post(myStatus2); Arbiter.Activate(dq, myTask); // Or dq,Enqueue(myTask); // 잠시 기다렸다가 실행함 System.Threading.Thread.Sleep(300); // 결과를 출력함 listBox1.Items.Add("[1] " + myStatus1.outputStr); listBox1.Items.Add("[2] " + myStatus2.outputStr); } }
14Microsoft Robotics Studio
Arbiter.Receive 를 이용해 메시지와 헨들러 연결하기 #2
// 수신받을 메세지 타입을 클래스로 정의해야 함 public class myStatus { public String inputStr = null; public String outputStr = null; }
public void myHandler(myStatus myStatus) { // 입력받은 값에 현재 날짜와 시간을 더해서 outputStr 변수에 저장함 myStatus.outputStr = System.DateTime.Now.ToString() + " : " + myStatus.inputStr; }
15Microsoft Robotics Studio
Arbiter.JoinedReceive 를 이용해 여러 개의 메시지와 헨들러 연결하기
private void button1_Click(object sender, EventArgs e) { using (Dispatcher dispatcher = new Dispatcher(0, " 일곱번째 예제 ")) { DispatcherQueue dq = new DispatcherQueue("DispatcherQueue", dispatcher); //2 가지 타입의 변수들을 받아들이기 위한 포트 선언 Port<myStatus1> myPort1 = new Port<myStatus1>(); Port<myStatus2> myPort2 = new Port<myStatus2>(); myStatus1 myClass1 = new myStatus1(); myStatus2 myClass2 = new myStatus2(); myClass1.inputStr = " 첫번째 클래스 변수의 값입니다 ."; myPort1.Post(myClass1); myClass2.inputStr = " 두번째 클래스 변수의 값입니다 ."; myPort2.Post(myClass2); // 해야할 일을 ITask 형식으로 생성함 ITask myTask = Arbiter.JoinedReceive(false, myPort1, myPort2, myHandler); Arbiter.Activate(dq, myTask); // Or dq,Enqueue(myTask); //Waiting for the message processing System.Threading.Thread.Sleep(300); listBox1.Items.Add("[ 결과 ] " + myClass1.outputStr); } }
16Microsoft Robotics Studio
Arbiter.JoinedReceive 를 이용해 여러 개의 메시지와 헨들러 연결하기 #2
// 수신받을 메세지 타입을 클래스로 정의해야 함 public class myStatus1 { public String inputStr = null; public String outputStr = null; } public class myStatus2 { public String inputStr = null; public String outputStr = null; }
public void myHandler(myStatus1 myClass1, myStatus2 myClass2) { // 두 클래스 변수의 입력값을 연결하여 첫번째 클래스 변수의 outputStr 에 저장함 myClass1.outputStr = myClass1.inputStr + "+" + myClass2.inputStr; }
17Microsoft Robotics Studio
Arbiter.Choice 를 이용해 여러 개의 메시지중 하나에 반응하기
private void button1_Click(object sender, EventArgs e) { using (Dispatcher dispatcher = new Dispatcher(0, " 여덟 번째 예제 ")) { DispatcherQueue dq = new DispatcherQueue("DispatcherQueue", dispatcher); //PortSet 을 이용하여 두 가지 타입의 메세지를 받아들일 수 있도록 함 PortSet<myStatus1, myStatus2> myPortSet = new PortSet<myStatus1, myStatus2>(); // 클래스 초기화 myStatus1 myClass1 = new myStatus1(); myStatus2 myClass2 = new myStatus2(); // 두 개의 메시지 타입 중 한가지만 등록되도록 함 if (!checkBox1.Checked) { myClass1.inputStr = " 첫번째 값 "; myPortSet.P0.Post(myClass1); } if (checkBox1.Checked) { myClass2.inputStr = " 두 번째 값 "; myPortSet.P1.Post(myClass2); } // 해야 할 일을 ITask 로 변환함 ITask myTask = Arbiter.Choice(myPortSet, myHandler1, myHandler2); Arbiter.Activate(dq, myTask); // Or dq,Enqueue(myTask); //Waiting for the message processing System.Threading.Thread.Sleep(300); listBox1.Items.Add("[Class1] " + myClass1.outputStr); listBox1.Items.Add("[Class2] " + myClass2.outputStr); } }
18Microsoft Robotics Studio
Arbiter.Choice 를 이용해 여러 개의 메시지중 하나에 반응하기 #2
// 수신받을 메세지 타입을 클래스로 정의해야 함 public class myStatus1 { public String inputStr = null; public String outputStr = null; } public class myStatus2 { public String inputStr = null; public String outputStr = null; }
public void myHandler1(myStatus1 myClass1) { myClass1.outputStr = "Handler1 실행됨 : " + myClass1.inputStr; } public void myHandler2(myStatus2 myClass2) { myClass2.outputStr = "Handler2 실행됨 : " + myClass2.inputStr; }
19Microsoft Robotics Studio
Arbiter.Interleave 를 이용한 동시 작업 제어하기
private void button1_Click(object sender, EventArgs e) { using (Dispatcher dispatcher = new Dispatcher(0, " 아홉번째 예제 ")) { DispatcherQueue dq = new DispatcherQueue("DispatcherQueue", dispatcher); //Define three Ports Port<myStatus1> myPort1 = new Port<myStatus1>(); Port<myStatus2> myPort2 = new Port<myStatus2>(); Port<myStatus3> myPort3 = new Port<myStatus3>(); //Initialize the class objects myStatus1 myClass1 = new myStatus1(); myStatus2 myClass2 = new myStatus2(); myStatus3 myClass3 = new myStatus3();
20Microsoft Robotics Studio
Arbiter.Interleave 를 이용한 동시 작업 제어하기 #2
//Choose one between the bellow three classes if (checkBox1.Checked) { myClass1.inputStr = " 첫 번째 "; myPort1.Post(myClass1); } myClass2.inputStr = " 두 번째 "; myPort2.Post(myClass2); myClass3.inputStr = " 세 번째 "; myPort3.Post(myClass3); //Define ITask ITask myTask = Arbiter.Interleave( new TeardownReceiverGroup( Arbiter.Receive(false, myPort1, myHandler1) ), new ExclusiveReceiverGroup( Arbiter.Receive(true, myPort2, myHandler2) ), new ConcurrentReceiverGroup( Arbiter.Receive(true, myPort3, myHandler3) ) ); Arbiter.Activate(dq, myTask); //Waiting for the message processing System.Threading.Thread.Sleep(300); listBox1.Items.Add("[1 번 결과 ] " + myClass1.outputStr); listBox1.Items.Add("[2 번 결과 ] " + myClass2.outputStr); listBox1.Items.Add("[3 번 결과 ] " + myClass3.outputStr); } }
21Microsoft Robotics Studio
Arbiter.Interleave 를 이용한 동시 작업 제어하기 #3
public class myStatus1 { public String inputStr = null; public String outputStr = null; } public class myStatus2 { public String inputStr = null; public String outputStr = null; } public class myStatus3 { public String inputStr = null; public String outputStr = null; } public void myHandler1(myStatus1 myClass1) { myClass1.outputStr = "Handler1 실행 : " + myClass1.inputStr; } public void myHandler2(myStatus2 myClass2) { myClass2.outputStr = "Handler2 실행 : " + myClass2.inputStr; } public void myHandler3(myStatus3 myClass3) { myClass3.outputStr = "Handler3 실행 : " + myClass3.inputStr; }
22Microsoft Robotics Studio
Demo: Concurrency processing
23Microsoft Robotics Studio
Coding pattern for asynchronous task handling
…
//Do Task A-1yield return Arbiter.Choice( DoAsynchronousTask A-1, delegate(…) { do for successful result }, delegate(Exception e) { do exceptional case });
if (error) yield break;
//Do Task A-2yield return Arbiter.Choice( DoAsynchronousTask A-2, delegate(…) { do for successful result }, delegate(Exception e) { do exceptional case });…//Do Task A-3…
24Microsoft Robotics Studio
Demo: Summation from 1 to 1000,000,000
- Use one thread
25Microsoft Robotics Studio
Demo: Summation from 1 to 1000,000,000
- Use multi-core
26Microsoft Robotics Studio
Demo: Sample code
dispatcher = new Dispatcher(0, "CCRSample");dq = new DispatcherQueue("DispatcherQueue", dispatcher);
//Start timeticks1 = System.DateTime.Now.Ticks;
//Summation between 1 ~ 500,000,000Arbiter.Activate(dq, Arbiter.FromIteratorHandler(myHandler1_2));
//Summation between 500,000,0001 ~ 1000,000,000Arbiter.Activate(dq, Arbiter.FromIteratorHandler(myHandler2_2));
//Merge result Arbiter.Activate(dq, Arbiter.JoinedReceive(false, p1, p2, delegate(long s1, long s2){
ticks2 = System.DateTime.Now.Ticks;
//Display final resultlistBox1.Items.Add("Total Sum: " + (s1 + s2).ToString());
//Display elapsed resultlistBox1.Items.Add("Elapsed Time (ms): " + ((ticks2 - ticks1) / 10000).ToString());
}));
27Microsoft Robotics Studio
Massive calculation processing- Segmentation of calculation block
Matrix calculation
Server 1
Server 2
Server 3
Server 4
Calculation result
Manycore
Manycore
Manycore
Manycore
28Microsoft Robotics Studio
Massive calculation processing- Sequential processing for isolated
data
Server 1
Manycore
Server 2
Manycore
Server 3
Manycore
Server 4
Manycore
Data n … Data 2 Data 1
CCR DSS
29Microsoft Robotics Studio
Concurrent processing by utilizing manycore and multiprocessor
Core 1
Core 2
Core 3
Core n
Core 1
Core 2
Core 3
Core n
Core 1
Core 2
Core 3
Core n
Core 1
Core 2
Core 3
Core n
CPU 1 CPU 2 CPU 3 CPU n
Task 1
Task 2 Choice
Task 6
Task 4
Task 5
Choice
Task 8 Task 9
Join
Task 7
Task 3
Task
CCR
30Microsoft Robotics Studio
High performance calculation with distributed concurrency platform
Image and signal processingPrecise orbit calculationFuel accountingCalculation for simulation
Core 1
Core 2
Core n
Core 1
Core 2
Core n
CPU 1 CPU n
… ……
Core 1
Core 2
Core n
Core 1
Core 2
Core n
CPU 1 CPU n
… ……
Core 1
Core 2
Core n
Core 1
Core 2
Core n
CPU 1 CPU n
… ………
Server 1 Server 2 Server n
Node Clusterfor Distributed and
Concurrent Processing
31Microsoft Robotics Studio
기본 DSS 서비스 개발
32Microsoft Robotics Studio
DSS 서비스란 ?
MSRS 에서 사용되는 모든 프로그램은 DSS 서비스 형태로 만들어 짐
입력 출력
서비스 인터페이스
33Microsoft Robotics Studio
DSS 서비스의 구성
DSS 서비스
서비스에서 사용되는데이터를 보관하는
클래스 (State 라고 부름 )
Get인터페이스
Update인터페이스
Replace인터페이스
Subscribe인터페이스
Get핸들러
Update핸들러
Replace핸들러
Subscribe핸들러
34Microsoft Robotics Studio
DSS 서비스 개발 과정
1 단계 MSRS 의 dssnewservice.exe 를 통해서 기본 템플릿 생성
2 단계 주고 받을 데이터 클래스 정의
3 단계 주고 받을 인터페이스 ( 포트 ) 정의
4 단계 해당 데이터를 처리하는 핸들러 모듈 구현
35Microsoft Robotics Studio
DSS 서비스 개발의 특징
기존에 이미 정의되어 있는 클래스를 이용함 Get Update Replace Subscribe
위의 4 가지 패턴만 이용하면 모든 서비스들이 구현 가능
내부의 상태 값은 옵션 항목으로서 서비스 구현 시 반드시 사용되는 것은 아님
36Microsoft Robotics Studio
Get 유형의 서비스
Get 은 서비스 내부에 저장되어 있는 상태 값을 리턴함
Get 요청 현재 시간 리턴
37Microsoft Robotics Studio
Get 서비스 생성
1 단계 MSRS 의 dssnewservice.exe 를 통해서 기본 템플릿 생성 dssnewservice /namespace:MSRS.Lecture.DSS /service:MyClock
2 단계 주고 받을 데이터 클래스 정의 MyClockState 항목 안에 아래 코드 추가
3 단계 주고 받을 인터페이스 ( 포트 ) 정의 ( 이미 템플릿으로 만들어져 있기 때문에 생략 )
4 단계 해당 데이터를 처리하는 핸들러 모듈 구현 이미 만들어져 있는 Get 핸들러 안에 아래 코드 추가
5 단계 컴파일 후 VPL 에서 테스트
[DataContract()] public class MyClockState { [DataMember] public string CurrentDateTime = ""; }
[ServiceHandler(ServiceHandlerBehavior.Concurrent)] public virtual IEnumerator<ITask> GetHandler(Get get) { _state.CurrentDateTime = System.DateTime.Now.ToString();
get.ResponsePort.Post(_state); yield break; }
38Microsoft Robotics Studio
Get 서비스 테스트
VPL 다이어그램
과제 속도 값을 가지는 두 개의 변수를 선언해서 값 읽어 오기
39Microsoft Robotics Studio
Update 유형의 서비스
서비스 내부에 저장되어 있는 상태 값을 변경시킴
Update 할 값 전달 처리 결과 리턴
40Microsoft Robotics Studio
Update 서비스 생성
1 단계 MSRS 의 dssnewservice.exe 를 통해서 기본 템플릿 생성 dssnewservice /namespace:MSRS.Lecture.DSS /service:MyWheels
2 단계 주고 받을 데이터 클래스 정의
3 단계 주고 받을 인터페이스 ( 포트 ) 정의
4 단계 해당 데이터를 처리하는 핸들러 모듈 구현
5 단계 컴파일 후 VPL 에서 테스트
[DataContract()] public class MyWheelsState { [DataMember] public double LeftWheel = 0;
[DataMember] public double RightWheel = 0; }
[ServiceHandler(ServiceHandlerBehavior.Exclusive)] public virtual IEnumerator<ITask> UpdateHandler(Update update) { _state.LeftWheel = update.Body.LeftWheel; _state.RightWheel = update.Body.RightWheel;
update.ResponsePort.Post(DefaultUpdateResponseType.Instance); yield break; }
public class Update : Update<MyWheelsState, PortSet<DefaultUpdateResponseType, Fault>> { }
[ServicePort()] public class MyWheelsOperations : PortSet<DsspDefaultLookup, DsspDefaultDrop, Get, Update> { }
41Microsoft Robotics Studio
Update 서비스 테스트
값을 저장하는 VPL 다이어그램
과제 저장된 값을 확인하는 다이어그램 코드의 추가
42Microsoft Robotics Studio
Replace 유형의 서비스
서비스 내부에 저장되어 있는 상태 값을 통째로 변경시킴
내부적으로는 Update 유형과 차이는 없음 Update 사용시에는 코드 내부에서 일부 필드만 사용될 경우를 가정함 Replace 사용시에는 내부 코드에서 통째로 State 가 변경되는 경우는
가정함
일반적으로는 State 값의 일부만 변경하는 경우가 대부분임으로 주로 Update 유형을 많이 적용함
Replace 할 값 전달 처리 결과 리턴
43Microsoft Robotics Studio
Replace 서비스 생성
1 단계 MSRS 의 dssnewservice.exe 를 통해서 기본 템플릿 생성
2 단계 주고 받을 데이터 클래스 정의
3 단계 주고 받을 인터페이스 ( 포트 ) 정의
4 단계 해당 데이터를 처리하는 핸들러 모듈 구현
5 단계 컴파일 후 VPL 에서 테스트
[ServiceHandler(ServiceHandlerBehavior.Exclusive)] public virtual IEnumerator<ITask> ReplaceHandler(Replace replace) { _state = replace.Body;
replace.ResponsePort.Post(DefaultReplaceResponseType.Instance); yield break; }
public class Replace : Replace<MyWheelsState, PortSet<DefaultReplaceResponseType, Fault>> { }
[ServicePort()] public class MyWheelsOperations : PortSet<DsspDefaultLookup, DsspDefaultDrop, Get, Update, Replace> { }
44Microsoft Robotics Studio
Replace 서비스 테스트
값을 저장하는 VPL 다이어그램
과제 저장된 값을 확인하는 다이어그램 코드의 추가
45Microsoft Robotics Studio
Subscribe 유형의 서비스
일종의 구독 신청 후 , 값을 전달 받는 방식으로서 , 통상적으로 이벤트를 구독 신청해 놓고 관련 이벤트가 발생할 때 마다 값을 받는 방식임
이벤트 결과 리턴
46Microsoft Robotics Studio
Subscribe 서비스 생성
1 단계 MSRS 의 dssnewservice.exe 를 통해서 기본 템플릿 생성
2 단계 주고 받을 데이터 클래스 정의
3 단계 주고 받을 인터페이스 ( 포트 ) 정의
4 단계
5 단계 컴파일 후 VPL 에서 테스트
using submgr = Microsoft.Dss.Services.SubscriptionManager;
[Partner("SubMgr", Contract = submgr.Contract.Identifier, CreationPolicy = PartnerCreationPolicy.CreateAlways)] private submgr.SubscriptionManagerPort _submgrPort = new submgr.SubscriptionManagerPort();
public virtual IEnumerator<ITask> UpdateHandler(Update update){…SendNotification<Update>(_submgrPort, _state);…}
[ServiceHandler(ServiceHandlerBehavior.Exclusive)] public IEnumerator<ITask> SubscribeHandler(Subscribe subscribe) { yield return Arbiter.Choice( SubscribeHelper(_submgrPort, subscribe.Body, subscribe.ResponsePort), delegate(SuccessResult success) { }, delegate(Exception fault) { } ); }
public class Subscribe : Subscribe<SubscribeRequestType, PortSet<SubscribeResponseType, Fault>> { }
[ServicePort()] public class MyWheelsOperations : PortSet<DsspDefaultLookup, DsspDefaultDrop, Subscribe, Get, Update, Replace> { }
47Microsoft Robotics Studio
Subscribe 서비스 테스트
변경된 값을 리턴하는 VPL 다이어그램
Microsoft Robotics Developer Studio
고급 프로그래밍 과정
[Part 6] 로봇 서비스 프로그래밍
2008로보틱스 그룹
마이크로소프트
김 영 준 수석[email protected]
49Microsoft Robotics Studio
로봇 기본 서비스 개발
50Microsoft Robotics Studio
로봇 기본 서비스
로봇의 센서와 엑츄에이터 제어와 관련된 공통적인 인터페이스들을 MS 에서 미리 정의해 놓았음 Drive Contact Sensor Motor Web Cam
이미 관련 State 와 인터페이스들이 정의되어 있기 때문에 기존 정의된 항목들을 그대로 사용하면 됨
개발하는 절차는 기본 DSS 서비스 방식과 동일함
51Microsoft Robotics Studio
Bumper 서비스 구현하기 #1
using Microsoft.Ccr.Core;
using Microsoft.Dss.Core;
using Microsoft.Dss.Core.Attributes;
using Microsoft.Dss.ServiceModel.Dssp;
using Microsoft.Dss.ServiceModel.DsspServiceBase;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Xml;
using myfirstbrick = MyFirstBrick;
using drive = Microsoft.Robotics.Services.Drive.Proxy;
using bumper = Microsoft.Robotics.Services.ContactSensor.Proxy;
using motor = Microsoft.Robotics.Services.Motor.Proxy;
using submgr = Microsoft.Dss.Services.SubscriptionManager;
52Microsoft Robotics Studio
Bumper 서비스 구현하기 #2
namespace MyFirstBrickBumper
{
public sealed class Contract
{ public const String Identifier = "http://schemas.tempuri.org/2007/10/myfirstbrickbumper.html";
}
[DisplayName("MyFirstBrickBumper")]
[Description("The MyFirstBrickBumper Service")]
[Contract(Contract.Identifier)]
[AlternateContract(bumper.Contract.Identifier)]
public class MyFirstBumperService : DsspServiceBase
{
private bumper.ContactSensor _state = new bumper.ContactSensor();
[ServicePort("/myfirstbrickbumper", AllowMultipleInstances = false)]
private bumper.ContactSensorArrayOperations _mainPort = new bumper.ContactSensorArrayOperations();
// 구독기능을위해항상추가되어하는코드임 [Partner("SubMgr", Contract = submgr.Contract.Identifier, CreationPolicy = PartnerCreationPolicy.CreateAlways)]
private submgr.SubscriptionManagerPort _submgr = new submgr.SubscriptionManagerPort();
// 새로추가한타이머라인 private Port<DateTime> _timerPort = new Port<DateTime>();
public MyFirstBumperService(DsspServiceCreationPort creationPort)
: base(creationPort)
{ }
53Microsoft Robotics Studio
Bumper 서비스 구현하기 #3
protected override void Start()
{ base.Start();
// 타이머최초구동명령 _timerPort.Post(DateTime.Now);
Activate(Arbiter.Receive(true, _timerPort, TimerHandler));
}
// 주기적으로호출되는타이머모듈 void TimerHandler(DateTime signal)
{
//Bumper 신호를가상으로생성함 bumper.ContactSensor _newSensor = new bumper.ContactSensor();
_newSensor.HardwareIdentifier = 1;
_newSensor.Name = "front";
_newSensor.Pressed = true;
// 구독관리자에값을전달함 SendNotification<bumper.Update>(_submgr, _newSensor);
Activate(
Arbiter.Receive(false, TimeoutPort(1000), //1 초간격으로다시호출함 delegate(DateTime time)
{
_timerPort.Post(time);
}
)
);
}
54Microsoft Robotics Studio
Bumper 서비스 구현하기 #4
//Update 헨들러를위해추가된코드 //bumper.ContactSensor 는 Update 헨들러를통해전달됨 // 전달받은센서의상태값을 state 변수에저장함 [ServiceHandler(ServiceHandlerBehavior.Exclusive)]
public virtual IEnumerator<ITask> UpdateHandler(bumper.Update update)
{ _state = update.Body;
// 구독관리자에값을전달함 SendNotification<bumper.Update>(_submgr, _state);
update.ResponsePort.Post(DefaultUpdateResponseType.Instance);
yield break;
}
// 구독기능을위해기본적으로항상추가되어야하는모듈임 [ServiceHandler(ServiceHandlerBehavior.Concurrent)]
public virtual IEnumerator<ITask> SubscribeHandler(bumper.Subscribe subscribe)
{ SubscribeHelper(_submgr, subscribe.Body, subscribe.ResponsePort);
yield break;
}
}}
55Microsoft Robotics Studio
Bumper 서비스 테스트
과제 VPL 에서 Bumper 서비스가 넘겨 주는 값을 확인해 보기 바람
56Microsoft Robotics Studio
Motor 서비스 구현하기 #1
using Microsoft.Ccr.Core;using Microsoft.Dss.Core;using Microsoft.Dss.Core.Attributes;using Microsoft.Dss.ServiceModel.Dssp;using Microsoft.Dss.ServiceModel.DsspServiceBase;using System;using System.Collections.Generic;using System.ComponentModel;using System.Xml;using myfirstbrick = MyFirstBrick;
using drive = Microsoft.Robotics.Services.Drive.Proxy;using bumper = Microsoft.Robotics.Services.ContactSensor.Proxy;using motor = Microsoft.Robotics.Services.Motor.Proxy;using submgr = Microsoft.Dss.Services.SubscriptionManager;
57Microsoft Robotics Studio
Motor 서비스 구현하기 #2
namespace MyFirstBrickMotor{ public sealed class Contract { public const String Identifier = "http://schemas.tempuri.org/2007/10/myfirstbrickmotor.html"; }
[DisplayName("MyFirstBrickMotor")] [Description("The MyFirstBrickMotor Service")] [Contract(Contract.Identifier)] [AlternateContract(motor.Contract.Identifier)] public class MyFirstMotorService : DsspServiceBase { private motor.SetMotorPower _state = new motor.SetMotorPower();
[ServicePort("/myfirstbrickmotor", AllowMultipleInstances = false)] private motor.MotorOperations _mainPort = new motor.MotorOperations();
//Brick 서비스에데이터를전달하기위해 Brick 서비스의 mainport 를사용함 //Brick 서비스에데이터를전달하기위해서는단순히아해포트에데이터를 Post 하기만하면됨
//[Partner("MyFirstBrick", Contract = myfirstbrick.Contract.Identifier, CreationPolicy = PartnerCreationPolicy.UseExistingOrCreate)]
//private myfirstbrick.MyFirstBrickOperations _brickPort = new myfirstbrick.MyFirstBrickOperations();
58Microsoft Robotics Studio
Motor 서비스 구현하기 #3
public MyFirstMotorService(DsspServiceCreationPort creationPort) : base(creationPort) { }
protected override void Start() { base.Start(); }
[ServiceHandler(ServiceHandlerBehavior.Exclusive)] public virtual IEnumerator<ITask> UpdateHandler(motor.SetMotorPower update) { LogInfo(LogGroups.Console, "[[[Motor 서비스 ]]] " + update.Body.TargetPower.ToString());
//Motor 값을수신하였으면 , 해당값을 Brick 서비스로전달함 //_brickPort.Post(new MyFirstBrick.UpdateMotor(update.Body));
update.ResponsePort.Post(DefaultUpdateResponseType.Instance); yield break; } }}
59Microsoft Robotics Studio
Motor 서비스 테스트
과제 VPL 에서 모터 서비스에 값을 넘겨 주고 결과를 확인 바람
60Microsoft Robotics Studio
Drive 서비스 구현하기 #1
using Microsoft.Ccr.Core;using Microsoft.Dss.Core;using Microsoft.Dss.Core.Attributes;using Microsoft.Dss.ServiceModel.Dssp;using Microsoft.Dss.ServiceModel.DsspServiceBase;using System;using System.Collections.Generic;using System.ComponentModel;using System.Xml;using myfirstbrick = MyFirstBrick;
using drive = Microsoft.Robotics.Services.Drive.Proxy;using bumper = Microsoft.Robotics.Services.ContactSensor.Proxy;using motor = Microsoft.Robotics.Services.Motor.Proxy;using submgr = Microsoft.Dss.Services.SubscriptionManager;
61Microsoft Robotics Studio
Drive 서비스 구현하기 #2
namespace MyFirstBrickDrive{ public sealed class Contract { public const String Identifier = "http://schemas.tempuri.org/2007/10/myfirstbrickdrive.html"; }
[DisplayName("MyFirstBrickDrive")] [Description("The MyFirstBrickDrive Service")] [Contract(Contract.Identifier)] [AlternateContract(drive.Contract.Identifier)] public class MyFirstBrickDriveService : DsspServiceBase { [ServicePort("/myfirstbrickdrive", AllowMultipleInstances = false)] private drive.DriveOperations _mainPort = new drive.DriveOperations();
//Brick 서비스에데이터를전달하기위해 Brick 서비스의 mainport 를사용함 //Brick 서비스에데이터를전달하기위해서는단순히아해포트에데이터를 Post 하기만하면됨
//[Partner("MyFirstBrick", Contract = myfirstbrick.Contract.Identifier, CreationPolicy = PartnerCreationPolicy.UseExistingOrCreate)]
//private myfirstbrick.MyFirstBrickOperations _brickPort = new myfirstbrick.MyFirstBrickOperations();
62Microsoft Robotics Studio
Drive 서비스 구현하기 #3
public MyFirstBrickDriveService(DsspServiceCreationPort creationPort) : base(creationPort) { }
protected override void Start() { base.Start(); }
[ServiceHandler(ServiceHandlerBehavior.Exclusive)] public virtual IEnumerator<ITask> UpdateHandler(drive.SetDrivePower update) { LogInfo(LogGroups.Console, "[[[Drive 서비스 ]]] " + update.Body.LeftWheelPower.ToString() + " " +
update.Body.RightWheelPower.ToString());
//Drive 값을수신하였으면 , 해당값을 Brick 서비스로전달함 //_brickPort.Post(new MyFirstBrick.UpdateDrive(update.Body));
update.ResponsePort.Post(DefaultUpdateResponseType.Instance); yield break; } }}
63Microsoft Robotics Studio
Drive 서비스 테스트
과제 VPL 에서 Drive 서비스에 값을 넘겨 주고 결과를 확인 바람
64Microsoft Robotics Studio
로봇 연결 서비스 개발
65Microsoft Robotics Studio
로봇 연결 서비스
유형 1 유형 2
66Microsoft Robotics Studio
로봇 연결 서비스 #1
using Microsoft.Ccr.Core;using Microsoft.Dss.Core;using Microsoft.Dss.Core.Attributes;using Microsoft.Dss.ServiceModel.Dssp;using Microsoft.Dss.ServiceModel.DsspServiceBase;using System;using System.Collections.Generic;using System.ComponentModel;using System.Xml;using myfirstbrick = MyFirstBrick;
using drive = Microsoft.Robotics.Services.Drive.Proxy;using bumper = Microsoft.Robotics.Services.ContactSensor.Proxy;using motor = Microsoft.Robotics.Services.Motor.Proxy;using submgr = Microsoft.Dss.Services.SubscriptionManager;
67Microsoft Robotics Studio
로봇 연결 서비스 #2
namespace MyFirstBrick
{
[DisplayName("MyFirstBrick")]
[Description("The MyFirstBrick Service")]
[Contract(Contract.Identifier)]
public class MyFirstBrickService : DsspServiceBase
{ private MyFirstBrickState _state = new MyFirstBrickState();
[ServicePort("/myfirstbrick", AllowMultipleInstances = false)]
private MyFirstBrickOperations _mainPort = new MyFirstBrickOperations();
//Bumber 서비스에데이터를전달하기위해서는대상서비스를사전에파트너로지정해서오픈시켜야한다 .
[Partner("MyFirstBrickBumper", Contract = MyFirstBrickBumper.Contract.Identifier, CreationPolicy = PartnerCreationPolicy.UseExistingOrCreate)]
private bumper.ContactSensorArrayOperations _brickBumperPort = new bumper.ContactSensorArrayOperations();
//Bumber 데이터를자동으로생성하기위해타이머포트정의 ( 임시로정의한것임 )
private Port<DateTime> _timerPort = new Port<DateTime>();
public MyFirstBrickService(DsspServiceCreationPort creationPort)
: base(creationPort)
{ }
protected override void Start()
{ base.Start();
//Bumper 데이터를 3 초간격으로자동으로생성하기위해추가한코드임 _timerPort.Post(DateTime.Now);
Activate(Arbiter.Receive(true, _timerPort, TimerHandler));
}
68Microsoft Robotics Studio
로봇 연결 서비스 #3
// 이곳에서 Bumper 데이터를생성함 void TimerHandler(DateTime signal)
{ //Bumper 신호를가상으로생성함 bumper.ContactSensor _newSensor = new bumper.ContactSensor();
_newSensor.HardwareIdentifier = 1;
_newSensor.Name = "front";
_newSensor.Pressed = true;
_brickBumperPort.Post(new bumper.Update(_newSensor));
// 타이머를다시호출함 Activate(
Arbiter.Receive(false, TimeoutPort(3000), //3 초간격 delegate(DateTime time)
{ _timerPort.Post(time);
} ) ); }
[ServiceHandler(ServiceHandlerBehavior.Exclusive)]
public virtual IEnumerator<ITask> UpdateHandler(UpdateDrive update)
{ LogInfo(LogGroups.Console, "[[[Brick Drive 서비스 ]]] " + update.Body.LeftWheelPower.ToString() + " " + update.Body.RightWheelPower.ToString());
update.ResponsePort.Post(DefaultUpdateResponseType.Instance);
yield break;
}
69Microsoft Robotics Studio
로봇 연결 서비스 #4
[ServiceHandler(ServiceHandlerBehavior.Exclusive)]
public virtual IEnumerator<ITask> UpdateHandler(UpdateMotor update)
{ LogInfo(LogGroups.Console, "[[[Brick Motor 서비스 ]]] " + update.Body.TargetPower.ToString());
update.ResponsePort.Post(DefaultUpdateResponseType.Instance);
yield break;
} }}
70Microsoft Robotics Studio
Brick 서비스 테스트
과제 VPL 에서 Brick 서비스를 활용하는 예를 구현 바람
71Microsoft Robotics Studio
UI 를 추가하는 방법
UI 와 서비스 간의 통신 유형 서비스 -> UI UI -> 서비스
실습 목적 화면을 생성하고 범퍼 데이터를 생성하는 UI 설계 모터서비스로 부터 모터 값을 받은 후 화면에 표시 드라이브 서비스로 부터 드라이브 값을 받은 후 화면에 표시
UI 추가 방법 C# 에서 Add-> New Item 선택 후 WinForm 추가
72Microsoft Robotics Studio
UI 추가 코드 #1
using Microsoft.Ccr.Core;using Microsoft.Dss.Core;using Microsoft.Dss.Core.Attributes;using Microsoft.Dss.ServiceModel.Dssp;using Microsoft.Dss.ServiceModel.DsspServiceBase;using System;using System.Collections.Generic;using System.ComponentModel;using System.Xml;using W3C.Soap;
using System.Windows.Forms;using Microsoft.Ccr.Adapters.WinForms;using submgr = Microsoft.Dss.Services.SubscriptionManager;
73Microsoft Robotics Studio
UI 추가 코드 #2
namespace MyApp1
{
[DisplayName("MyApp1")]
[Description("The MyApp1 Service")]
[Contract(Contract.Identifier)]
public class MyApp1Service : DsspServiceBase
{
private MyApp1State _state = new MyApp1State();
[ServicePort("/myapp1", AllowMultipleInstances=false)]
private MyApp1Operations _mainPort = new MyApp1Operations();
[Partner("SubMgr", Contract = submgr.Contract.Identifier, CreationPolicy = PartnerCreationPolicy.CreateAlways)]
private submgr.SubscriptionManagerPort _subMgrPort = new submgr.SubscriptionManagerPort();
public MyApp1Service(DsspServiceCreationPort creationPort) :
base(creationPort)
{ }
Form1 App1Form = null;
protected override void Start()
{ base.Start();
WinFormsServicePort.Post(new RunForm(CreateAppForm));
}
74Microsoft Robotics Studio
UI 추가 코드 #3
Form1 _form;
Form CreateAppForm()
{ // 만약값을공유하는클래스가있다면 , 해당클래스도같이넘겨줄수있음 //_form = new Form1(_mainPort, _state);
_form = new Form1(_mainPort);
return _form;
}
[ServiceHandler(ServiceHandlerBehavior.Concurrent)]
public virtual IEnumerator<ITask> GetHandler(Get get)
{ get.ResponsePort.Post(_state);
yield break;
}
[ServiceHandler(ServiceHandlerBehavior.Exclusive)]
public virtual IEnumerator<ITask> UpdateHandler(Update update)
{ LogInfo(LogGroups.Console, update.Body.CustName);
SendNotification<Update>(_subMgrPort, update.Body);
//update.ResponsePort.Post(DefaultUpdateResponseType.Instance);
yield break;
}
[ServiceHandler(ServiceHandlerBehavior.Concurrent)]
public IEnumerator<ITask> SubscribeHandler(Subscribe subscribe)
{ SubscribeHelper(_subMgrPort, subscribe.Body, subscribe.ResponsePort);
yield break;
} }}
75Microsoft Robotics Studio
UI 추가 코드 #4
namespace MyApp1
{
public partial class Form1 : Form
{ private MyApp1Operations _mainPort;
public Form1(MyApp1Operations mainPort)
{ InitializeComponent();
_mainPort = mainPort;
}
private void button1_Click(object sender, EventArgs e)
{ MyApp1State _state = new MyApp1State();
_state.CustNo = textBox1.Text;
_state.CustName = textBox2.Text;
_state.OrderQty = textBox3.Text;
_state.Cost = textBox4.Text;
_mainPort.Post(new Update(_state));
} }}
76Microsoft Robotics Studio
UI 추가 코드 테스트
과제 이전의 UI 추가 패턴을 Brick 서비스의 범퍼 데이터 생성에 적용
77Microsoft Robotics Studio
값을 표시하는 UI 작업 #1
서비스에 아래 코드 추가
[ServiceHandler(ServiceHandlerBehavior.Exclusive)] public virtual IEnumerator<ITask> UpdateHandler(Update update) { WinFormsServicePort.FormInvoke( delegate() { _form.ShowData(update.Body); } );
update.ResponsePort.Post(DefaultUpdateResponseType.Instance); yield break; }
78Microsoft Robotics Studio
값을 표시하는 UI 작업 #2
UI 에 아래 코드 추가
namespace MyApp2{ public partial class Form1 : Form { public Form1() { InitializeComponent(); }
public void ShowData(MyApp2State _state) { textBox1.Text = _state.CustNo; textBox2.Text = _state.CustName; } }}
79Microsoft Robotics Studio
UI 추가 코드 테스트
과제 이전의 UI 추가 패턴을 Brick 서비스의 모터와 드라이브 서비스 데이터
표시에 적용
Microsoft Robotics Developer Studio
고급 프로그래밍 과정
[Part 7] 지능형 서비스 프로그래밍
2008로보틱스 그룹
마이크로소프트
김 영 준 수석[email protected]
81Microsoft Robotics Studio
지능형 서비스 개발
82Microsoft Robotics Studio
TTS 기능 이용하기
VPL 에서 TTS 기능 이용하기
83Microsoft Robotics Studio
음성인식 이용하기
VPL 에서 음성인식 기능 이용하기
84Microsoft Robotics Studio
간단한 비전처리 구현하기
VPL 에서 제스쳐 인식 결과 이용하기
85Microsoft Robotics Studio
오늘의 날씨정보 표시하기 #1
using Microsoft.Ccr.Core;
using Microsoft.Dss.Core;
using Microsoft.Dss.Core.Attributes;
using Microsoft.Dss.ServiceModel.Dssp;
using Microsoft.Dss.ServiceModel.DsspServiceBase;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Xml;
using System.Net;
using System.IO;
using System.Text;
using W3C.Soap;
using googleweather = GoogleWeather;
86Microsoft Robotics Studio
오늘의 날씨정보 표시하기 #2
namespace GoogleWeather.Today
{
[DisplayName("Google Today Weather")]
[Description("Google Today Weather")]
[Contract(Contract.Identifier)]
public class TodayWeatherService : DsspServiceBase
{ private TodayWeatherState _state = new TodayWeatherState();
[ServicePort("/googletodayweather", AllowMultipleInstances=false)]
private TodayWeatherOperations _mainPort = new TodayWeatherOperations();
public TodayWeatherService(DsspServiceCreationPort creationPort)
: base(creationPort)
{ }
protected override void Start()
{base.Start();
}
private const string _googleUrl = "http://www.google.co.kr";
87Microsoft Robotics Studio
오늘의 날씨정보 표시하기 #3
[ServiceHandler(ServiceHandlerBehavior.Concurrent)]
public virtual IEnumerator<ITask> GetHandler(Get get)
{ if (get.Body.CityName != "")
{ GetTodayWeather(_googleUrl + "/ig/api?weather=" + get.Body.CityName, get.Body.CityName);
} else
GetTodayWeather("", "");
get.ResponsePort.Post(_state);
yield break;
}
88Microsoft Robotics Studio
오늘의 날씨정보 표시하기 #4
private void GetTodayWeather(string strCityUrl, string cityName)
{ try
{ XmlDocument xmlDoc = new XmlDocument();
if (strCityUrl != "")
{ WebClient myWebClient = new WebClient();
Stream myStream = myWebClient.OpenRead(strCityUrl);
StreamReader sr = new StreamReader(myStream, Encoding.Default);
xmlDoc.Load(sr);
sr.Close();
sr.Dispose();
myStream.Close();
myStream.Dispose();
} else
{ xmlDoc.Load(@"C:\Microsoft Robotics Studio (1.5)\googleweather.xml");
}
XmlNodeList xWeatherNodeList = xmlDoc.SelectNodes("xml_api_reply/weather/forecast_conditions");
89Microsoft Robotics Studio
오늘의 날씨정보 표시하기 #5
//for (int i = 0; i < xWeatherNodeList.Count; i++) for (int i = 0; i < 1; i++) //1: Today { _state.DayOfWeek = xWeatherNodeList.Item(i).SelectSingleNode("day_of_week").Attributes["data"].Value; _state.Low = xWeatherNodeList.Item(i).SelectSingleNode("low").Attributes["data"].Value; _state.High = xWeatherNodeList.Item(i).SelectSingleNode("high").Attributes["data"].Value; _state.IconPath = xWeatherNodeList.Item(i).SelectSingleNode("icon").Attributes["data"].Value; _state.TodayCondition = xWeatherNodeList.Item(i).SelectSingleNode("condition").Attributes["data"].Value;
_state.WeatherText = _state.DayOfWeek+" " + cityName+" 의날씨는 " + _state.TodayCondition+" 입니다 . "; _state.WeatherText = _state.WeatherText + " 낮최저기온은 " + _state.Low + " 도이고 , "; _state.WeatherText = _state.WeatherText + " 낮최고기온은 " + _state.High + " 도입니다 . "; }
XmlNode xWeatherNode = xmlDoc.SelectSingleNode("xml_api_reply/weather/current_conditions");
if (xWeatherNode != null) { _state.CurrentCondition = xWeatherNode.SelectSingleNode("condition").Attributes["data"].Value; _state.CurrentTempF = xWeatherNode.SelectSingleNode("temp_f").Attributes["data"].Value; _state.CurrentTempC = xWeatherNode.SelectSingleNode("temp_c").Attributes["data"].Value; _state.CurrentHumidity = xWeatherNode.SelectSingleNode("humidity").Attributes["data"].Value; _state.CurrentWind = xWeatherNode.SelectSingleNode("wind_condition").Attributes["data"].Value;
_state.WeatherText = _state.WeatherText + " 현재시각의날씨는 " + _state.CurrentCondition + " 이며 , "; _state.WeatherText = _state.WeatherText + " 기온은 " + _state.CurrentTempC + " 도이고 , "; _state.WeatherText = _state.WeatherText + " 그리고 " + _state.CurrentHumidity + " 이며 , "; _state.WeatherText = _state.WeatherText + _state.CurrentWind + " 입니다 . "; }
xmlDoc = null; } catch { // }
90Microsoft Robotics Studio
오늘의 날씨정보 표시하기 #6
finally
{ // } } }
public sealed class Contract
{ public const String Identifier = "http://schemas.tempuri.org/2007/11/googletodayweather.html";
}
[DataContract()]
public class TodayWeatherState
{ [DataMember(IsRequired = false)]
public String DayOfWeek;
[DataMember(IsRequired = false)]
public String Low;
[DataMember(IsRequired = false)]
public String High;
[DataMember(IsRequired = false)]
public String IconPath;
[DataMember(IsRequired = false)]
public String TodayCondition;
[DataMember(IsRequired = false)]
public String CurrentCondition;
91Microsoft Robotics Studio
오늘의 날씨정보 표시하기 #7
[DataMember(IsRequired = false)]
public String CurrentTempF;
[DataMember(IsRequired = false)]
public String CurrentTempC;
[DataMember(IsRequired = false)]
public String CurrentHumidity;
[DataMember(IsRequired = false)]
public String CurrentWind;
[DataMember(IsRequired = false)]
public String WeatherText;
}
[ServicePort()]
public class TodayWeatherOperations : PortSet<DsspDefaultLookup, DsspDefaultDrop, Get>
{ }
[DataContract]
public class GetWeatherRequest
{ [DataMember]
public String CityName;
}
92Microsoft Robotics Studio
오늘의 날씨정보 표시하기 #8
public class Get : Get<GetWeatherRequest, PortSet<TodayWeatherState, Fault>>
{ public Get()
{ }
public Get(GetWeatherRequest body)
: base(body)
{ }
public Get(GetWeatherRequest body, Microsoft.Ccr.Core.PortSet<TodayWeatherState, W3C.Soap.Fault> responsePort)
: base(body, responsePort)
{ } }}
93Microsoft Robotics Studio
VPL 을 통한 오늘의 날씨 이용하기
VPL 에서 서울의 날씨 정보를 읽어 오기
과제 날씨 정보를 TTS 에 연결하기