930 likes | 1.19k Views
Microsoft Robotics Developer Studio 고급 프로그래밍 과정 [Part 5] CCR 및 DSS 서비스 프로그래밍. 2008 로보틱스 그룹 마이크로소프트. 김 영 준 수석 yjoonkim@microsoft.com. 목차. Part 1: CCR 프로그래밍 Part 2: 기본 DSS 서비스 개발 Part 3: 로봇 기본 서비스 개발 Part 4: 로봇 연결 서비스 개발 Part 5: UI 가 포함된 서비스 개발. CCR 프로그래밍.
E N D
Microsoft Robotics Developer Studio고급 프로그래밍 과정[Part 5] CCR 및 DSS 서비스프로그래밍 2008 로보틱스 그룹 마이크로소프트 김영 준 수석 yjoonkim@microsoft.com
목차 • Part 1: CCR 프로그래밍 • Part 2: 기본 DSS 서비스 개발 • Part 3: 로봇기본 서비스 개발 • Part 4: 로봇 연결 서비스 개발 • Part 5: UI가 포함된 서비스 개발
Dispatcher와 DispatcherQueue • CCR의 기본 처리 방식 • 뭔가 해야 할 일을 생성함 • 생성된 일을 작업 큐에 넣어 놓음 • 일을 처리하는 엔진이 큐에 있는 해야 할 일을 가져다가 처리함 • CCR에서는 뭔가 해야 할 일을 정의하기 위해 ITask 클래스 형태의 객체를 사용함 • 실제 일을 수행하는 엔진이라 할 수 있는 Dispacher 라는 것이 존재함 • Dispacher에 여러 개의 큐브를 만들어 놓을 수 있음 • 이 큐를 DispacherQueue라고 함 • 이러한 큐들은 모두 Dispacher가 관리를 함
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); } } }
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); } }
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; }
메시지를 전달하는 Port private 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); } }
멀티 형식의 메세지를 전달하는 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); } }
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); } } //수행해야할 작업들이 기술되어 있는 헨들러임 privateHandlermyHandler() { //현재의 시간을 리스트박스에 출력합니다. listBox1.Items.Add("Now is "+ System.DateTime.Now.ToString()); returnnull; }
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); } }
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; }
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); } }
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; }
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); } }
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; }
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();
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); } }
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; }
Coding pattern for asynchronous task handling … //Do Task A-1 yield return Arbiter.Choice( DoAsynchronousTask A-1, delegate(…) { do for successful result }, delegate(Exception e) { do exceptional case }); if (error) yield break; //Do Task A-2 yield return Arbiter.Choice( DoAsynchronousTask A-2, delegate(…) { do for successful result }, delegate(Exception e) { do exceptional case }); … //Do Task A-3 …
Demo: Sample code dispatcher = new Dispatcher(0, "CCRSample"); dq = new DispatcherQueue("DispatcherQueue", dispatcher); //Start time ticks1 = System.DateTime.Now.Ticks; //Summation between 1 ~ 500,000,000 Arbiter.Activate(dq, Arbiter.FromIteratorHandler(myHandler1_2)); //Summation between 500,000,0001 ~ 1000,000,000 Arbiter.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 result listBox1.Items.Add("Total Sum: " + (s1 + s2).ToString()); //Display elapsed result listBox1.Items.Add("Elapsed Time (ms): " + ((ticks2 - ticks1) / 10000).ToString()); }));
Massive calculation processing- Segmentation of calculation block Server 1 Manycore Server 2 Manycore Server 3 Manycore Server 4 Matrix calculation Calculation result Manycore CCR DSS
Massive calculation processing- Sequential processing for isolated data Data n … Data 2 Data 1 Server 1 Manycore Server 2 Manycore Server 3 Manycore Server 4 Manycore CCR DSS
Concurrent processing by utilizing manycore and multiprocessor Join Task 1 Task 3 Task 6 Task Task 2 Choice Choice Task 4 Task 7 Task 9 Task 8 Task 5 Core 1 Core 1 Core 1 Core 1 Core 2 Core 2 Core 2 Core 2 Core 3 Core 3 Core 3 Core 3 Core n Core n Core n Core n CCR CPU 1 CPU 2 CPU 3 CPU n
High performance calculation with distributed concurrency platform Image and signal processing Precise orbit calculation Fuel accounting Calculation for simulation Node Cluster for Distributed and Concurrent Processing Core 1 Core 1 Core 1 Core 1 Core 1 Core 1 Core 2 Core 2 Core 2 Core 2 Core 2 Core 2 … … … … … … … … … … Core n Core n Core n Core n Core n Core n CPU 1 CPU n CPU 1 CPU n CPU 1 CPU n Server 1 Server 2 Server n CCR DSS
DSS 서비스란? MSRS에서 사용되는 모든 프로그램은 DSS 서비스 형태로 만들어 짐 입력 출력 서비스 인터페이스
DSS 서비스의 구성 DSS 서비스 서비스에서사용되는 데이터를 보관하는 클래스 (State라고 부름) Get 핸들러 Update 핸들러 Replace 핸들러 Subscribe 핸들러 Get 인터페이스 Update 인터페이스 Replace 인터페이스 Subscribe 인터페이스
DSS 서비스 개발 과정 • 1 단계 • MSRS의 dssnewservice.exe를 통해서 기본 템플릿 생성 • 2 단계 • 주고 받을 데이터 클래스 정의 • 3 단계 • 주고 받을 인터페이스(포트) 정의 • 4단계 • 해당 데이터를 처리하는 핸들러 모듈 구현
DSS 서비스 개발의 특징 • 기존에 이미 정의되어 있는 클래스를 이용함 • Get • Update • Replace • Subscribe • 위의 4가지 패턴만 이용하면 모든 서비스들이 구현 가능 • 내부의 상태 값은 옵션 항목으로서 서비스 구현 시 반드시 사용되는 것은 아님
Get 유형의 서비스 • Get은 서비스 내부에 저장되어 있는 상태 값을 리턴함 Get 요청 현재 시간 리턴
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; }
Get 서비스 테스트 • VPL 다이어그램 • 과제 • 속도 값을 가지는 두 개의 변수를 선언해서 값 읽어 오기
Update 유형의 서비스 • 서비스 내부에 저장되어 있는 상태 값을 변경시킴 Update할 값 전달 처리 결과 리턴
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; } [ServicePort()] public class MyWheelsOperations : PortSet<DsspDefaultLookup, DsspDefaultDrop, Get, Update> { } public class Update : Update<MyWheelsState, PortSet<DefaultUpdateResponseType, Fault>> { } [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; }
Update 서비스 테스트 • 값을 저장하는 VPL 다이어그램 • 과제 • 저장된 값을 확인하는 다이어그램 코드의 추가
Replace 유형의 서비스 • 서비스 내부에 저장되어 있는 상태 값을 통째로 변경시킴 • 내부적으로는 Update 유형과 차이는 없음 • Update 사용시에는 코드 내부에서 일부 필드만 사용될 경우를 가정함 • Replace 사용시에는 내부 코드에서 통째로 State가 변경되는 경우는 가정함 • 일반적으로는 State 값의 일부만 변경하는 경우가 대부분임으로 주로 Update 유형을 많이 적용함 Replace 할 값 전달 처리 결과 리턴
Replace 서비스 생성 • 1 단계 • MSRS의 dssnewservice.exe를 통해서 기본 템플릿 생성 • 2 단계 • 주고 받을 데이터 클래스 정의 • 3 단계 • 주고 받을 인터페이스(포트) 정의 • 4단계 • 해당 데이터를 처리하는 핸들러 모듈 구현 • 5단계 • 컴파일 후 VPL에서 테스트 [ServicePort()] public class MyWheelsOperations : PortSet<DsspDefaultLookup, DsspDefaultDrop, Get, Update, Replace> { } public class Replace : Replace<MyWheelsState, PortSet<DefaultReplaceResponseType, Fault>> { } [ServiceHandler(ServiceHandlerBehavior.Exclusive)] public virtual IEnumerator<ITask> ReplaceHandler(Replace replace) { _state = replace.Body; replace.ResponsePort.Post(DefaultReplaceResponseType.Instance); yield break; }
Replace 서비스 테스트 • 값을 저장하는 VPL 다이어그램 • 과제 • 저장된 값을 확인하는 다이어그램 코드의 추가
Subscribe 유형의 서비스 • 일종의 구독 신청 후, 값을 전달 받는 방식으로서, 통상적으로 이벤트를 구독 신청해 놓고 관련 이벤트가 발생할 때 마다 값을 받는 방식임 이벤트 결과 리턴
Subscribe 서비스 생성 • 1 단계 • MSRS의 dssnewservice.exe를 통해서 기본 템플릿 생성 • 2 단계 • 주고 받을 데이터 클래스 정의 • 3 단계 • 주고 받을 인터페이스(포트) 정의 • 4단계 • 5단계 • 컴파일 후 VPL에서 테스트 [ServicePort()] public class MyWheelsOperations : PortSet<DsspDefaultLookup, DsspDefaultDrop, Subscribe, Get, Update, Replace> { } public class Subscribe : Subscribe<SubscribeRequestType, PortSet<SubscribeResponseType, Fault>> { } 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) { } ); }
Subscribe 서비스 테스트 • 변경된 값을 리턴하는 VPL 다이어그램
Microsoft Robotics Developer Studio고급 프로그래밍 과정[Part 6] 로봇서비스프로그래밍 2008 로보틱스 그룹 마이크로소프트 김영 준 수석 yjoonkim@microsoft.com
로봇 기본 서비스 • 로봇의 센서와 엑츄에이터 제어와 관련된 공통적인 인터페이스들을 MS에서 미리 정의해 놓았음 • Drive • Contact Sensor • Motor • Web Cam • 이미 관련 State와 인터페이스들이 정의되어 있기 때문에 기존 정의된 항목들을 그대로 사용하면 됨 • 개발하는 절차는 기본 DSS 서비스 방식과 동일함