AutoTest是一個基於.NET平臺實現的自動化/壓力測試的系統,可獨立運行於windows平臺下,支持分佈式部署,不須要其餘配置或編譯器的支持。(本質是一個基於協議的測試工具),前面還有一篇對其功能的簡單介紹【AutoTest簡介】html
AutoTest用於發佈的部分有2個部分,主程序【AutoTest.exe】及分佈式部署程序【RemoteService.exe】(用於將將測試業務分佈式部署到遠程主機)web
而在內部又被設計爲多個組成部分,最終完成對自定義腳本文件的解析並按腳本要求的模式去執行。算法
如上圖,簡單介紹下json
執行邏輯層主要由3部分組成windows
表示層暫時是由2部分組成安全
最下面的2個模塊是用於系統內部模塊的單元測試,自己與系統運行無關,因此就不贅述了數據結構
此外還有一個重要的組成部分-腳本文件,這裏使用的腳本依託於XML文件,規則上基本是獨立定義的。腳本文件控制全部Case的執行方式,內容,甚至是執行路徑,還包括對斷言的額外處理。腳本文件被解釋器(CaseExecutiveActuator的一部分)轉換爲系統/工具直接可使用的數據結構,並最終被執行。(腳本規則相對獨立,將不會在此篇中涉及)框架
CaseExecutiveActuator模塊主要由2部分組成,腳本處理部分(算法)及腳本內容的存儲部分(數據)。dom
腳本對外的存在形式是XML文件,這個XML也是最終的腳本文件。系統解析這個XML文件(對CASE內容,或測試業務的修改包括新建也只是對XML腳本文件的修改而),而後對其進行進一步處理(固然使用合適的數據結構對其進行存儲是第一步)。腳本的存儲比較簡單,先看這部分,其實就是實現了一個相似Tree的數據結構來存儲整個腳本邏輯,而後每一個單獨的Case都存儲在單獨的Cell裏(Cell爲該自定義數據結構的基礎單元,於單個Case對應)。tcp
Cell的結構以下(簡單的實現了形如右邊圖片的的數據結構)
其實很容易看出來這個結構跟TreeView的結構十分相似,其實最初腳本數據的存儲是直接藉助於TreeView的,不過爲了將業務跟UI徹底分離以便將來向其餘平臺移植時不受UI框架的影響,仍是自行實現了這樣的數據結構。Cell結構自己十分簡單,有3個主要的結構指針
childCellList 指向子Cell
nextCell 指向下一個Cell
parentCell 指向父Cell
還有2個數據源
caseXmlNode 指向元素的腳本數據,保留原始數據的地址是爲了方便AutoTest對腳本的直接修改(這裏使用的XML,若是但願相關腳本文件類型須要修改此處及腳本解析部分)
caseRunData 已經解析完成的單Case數據(實際加載腳步時對全部腳本解析一次,把結果存儲在這裏,後面的執行即直接取這裏的數據以提升性能)
另外包含2個輔助元素
caseType 指示該Cell即Case的實類型,能夠輔助處理異常Cell的數據。
uiTag 可選的指針,用於數據綁定,名字也提示了主要用於綁定UI(實際可用綁定任何類型的數據)
該機構還提供一些其餘功能如Cell的容器及索引器等有興趣的能夠看下下面的Code
1 /******************************************************************************* 2 * Copyright (c) 2015 lijie 3 * All rights reserved. 4 * 5 * 文件名稱: 6 * 內容摘要: mycllq@hotmail.com 7 * 8 * 歷史記錄: 9 * 日 期: 201505016 建立人: lijie8118054@126.com 10 * 描 述: 建立 11 *******************************************************************************/ 12 13 namespace CaseExecutiveActuator.Cell 14 { 15 //using CaseCell = TreeNode;//可以讓2類徹底等價 16 public class CaseCell 17 { 18 List<CaseCell> childCellList; 19 20 private CaseType caseType; 21 private XmlNode caseXmlNode; 22 private myRunCaseData<ICaseExecutionContent> caseRunData; 23 private object uiTag; 24 25 private CaseCell nextCell; 26 private CaseCell parentCell; 27 28 29 public CaseCell() 30 { 31 32 } 33 34 /// <summary> 35 /// CaseCell構造函數 36 /// </summary> 37 /// <param name="yourCaseType">CaseType</param> 38 /// <param name="yourXmlNode">CaseCell腳本原始信息</param> 39 /// <param name="yourCaseRunData">CaseCell腳本解析後的信息</param> 40 public CaseCell(CaseType yourCaseType, XmlNode yourXmlNode ,myRunCaseData<ICaseExecutionContent> yourCaseRunData) 41 { 42 caseType = yourCaseType; 43 caseXmlNode = yourXmlNode; 44 caseRunData = yourCaseRunData; 45 } 46 47 /// <summary> 48 /// 獲取或設置CaseCell腳本解析後的信息 49 /// </summary> 50 public myRunCaseData<ICaseExecutionContent> CaseRunData 51 { 52 get { return caseRunData; } 53 set { caseRunData = value; } 54 } 55 56 /// <summary> 57 /// 獲取或設置CaseCell腳本原始信息 58 /// </summary> 59 public XmlNode CaseXmlNode 60 { 61 get { return caseXmlNode; } 62 set { caseXmlNode = value; } 63 } 64 65 /// <summary> 66 /// 獲取或設置UiTag,能夠用於UI控件與cell的綁定 67 /// </summary> 68 public object UiTag 69 { 70 get { return uiTag; } 71 set { uiTag = value; } 72 } 73 74 /// <summary> 75 /// 獲取當前Cell類型 76 /// </summary> 77 public CaseType CaseType 78 { 79 get { return caseType; } 80 } 81 82 /// <summary> 83 /// 獲取下一個Cell,若是沒有返回null 84 /// </summary> 85 public CaseCell NextCell 86 { 87 get { return nextCell; } 88 } 89 90 /// <summary> 91 /// 獲取當前Cell的父Cell,若是沒有返回null 92 /// </summary> 93 public CaseCell ParentCell 94 { 95 get { return parentCell; } 96 } 97 98 /// <summary> 99 /// 獲取當前Cell的ChildCells列表 100 /// </summary> 101 public List<CaseCell> ChildCells 102 { 103 get { return childCellList; } 104 } 105 106 /// <summary> 107 /// 獲取一個值標識當前Cell是否有NextCell 108 /// </summary> 109 public bool IsHasNextCell 110 { 111 get { return nextCell != null; } 112 } 113 114 /// <summary> 115 /// 獲取一個值標識當前Cell是否有parentCell 116 /// </summary> 117 public bool IsHasParent 118 { 119 get 120 { 121 if (parentCell != null) 122 { 123 return true; 124 } 125 return false; 126 } 127 } 128 129 /// <summary> 130 /// 獲取一個值標識當前Cell是否有ChildCell 131 /// </summary> 132 public bool IsHasChild 133 { 134 get 135 { 136 if (childCellList != null) 137 { 138 if (childCellList.Count != 0) 139 { 140 return true; 141 } 142 } 143 return false; 144 } 145 } 146 147 /// <summary> 148 /// 設置下一個Cell 149 /// </summary> 150 /// <param name="yourCaseCell">下一個Cell</param> 151 public void SetNextCell(CaseCell yourCaseCell) 152 { 153 nextCell = yourCaseCell; 154 } 155 156 /// <summary> 157 /// 設置ParentCell 158 /// </summary> 159 /// <param name="yourCaseCell">ParentCell</param> 160 public void SetParentCell(CaseCell yourCaseCell) 161 { 162 parentCell = yourCaseCell; 163 } 164 165 /// <summary> 166 /// 向當前Cell中插入子Cell 167 /// </summary> 168 /// <param name="yourCaseCell">子Cell</param> 169 public void Add(CaseCell yourCaseCell) 170 { 171 if (childCellList == null) 172 { 173 childCellList = new List<CaseCell>(); 174 } 175 yourCaseCell.SetParentCell(this); 176 childCellList.Add(yourCaseCell); 177 if(childCellList.Count>1) 178 { 179 childCellList[childCellList.Count-2].SetNextCell(yourCaseCell); 180 } 181 } 182 183 //一個tag存放ui指針/引用 184 //實現一個Nodes.Count計算每層數目,或返回是否有子結構 185 //Nodes[0]索引或實現NodeStart,返回層中第一個CaseCell 186 //實現一個NextNode返回層中的下一個CaseCell 187 } 188 189 public class ProjctCollection 190 { 191 List<CaseCell> myProjectChilds; 192 193 public List<CaseCell> ProjectCells 194 { 195 get { return myProjectChilds; } 196 } 197 198 public void Add(CaseCell yourCaseCell) 199 { 200 if (myProjectChilds == null) 201 { 202 myProjectChilds = new List<CaseCell>(); 203 } 204 myProjectChilds.Add(yourCaseCell); 205 } 206 207 public CaseCell this[int indexP, int indexC] 208 { 209 get 210 { 211 if(myProjectChilds.Count>indexP) 212 { 213 if (myProjectChilds[indexP].IsHasChild) 214 { 215 if (myProjectChilds[indexP].ChildCells.Count > indexC) 216 { 217 return myProjectChilds[indexP].ChildCells[indexC]; 218 } 219 } 220 } 221 return null; 222 } 223 } 224 225 226 } 227 }
執行的實體CaseExecutiveActuator自己會較多點,介紹的也會粗略些,不過大致也是能夠簡單的分紅2個部分。能夠很容易的想到假如咱們得了腳本文件,那麼有2個問題:第一就是怎麼知道選擇哪個case,當前case執行完成後執行哪個,第二個問題就是case中包含的業務如何執行。CaseExecutiveActuator也正是分紅了這2部分
先來看比較簡單的CaseRunTime
實際上於CaseRunTime 緊密相關的還有另外2個組件myCaseLoop,myCsaeQueue(他們實際上也僅被CaseRunTime 使用)
經過名字其實大概能夠猜到他們的功能
單獨看看myCaseRunTime
如上圖能夠看到myCaseRunTime其實是包含了一個myCsaeQueue列表的,不過邏輯的核心是nextCase。有興趣的能夠看看下面的實現(貼出的只包含關鍵部分)
1 class RunCaseCount 2 { 3 /// <summary> 4 /// for count strut 5 /// </summary> 6 struct CaseLoopCountInfo 7 { 8 CaseCell loopNode; 9 int caseRate; 10 11 /// <summary> 12 /// Initialization the CaseLoopCountInfo 13 /// </summary> 14 /// <param name="yourLoopNode">your LoopNode</param> 15 /// <param name="yourCaseRate">your CaseRate</param> 16 public CaseLoopCountInfo(CaseCell yourLoopNode, int yourCaseRate) 17 { 18 loopNode = yourLoopNode; 19 caseRate = yourCaseRate; 20 } 21 22 /// <summary> 23 /// get the LoopNode 24 /// </summary> 25 public CaseCell LoopNode 26 { 27 get { return loopNode; } 28 } 29 30 /// <summary> 31 /// get the CaseRate 32 /// </summary> 33 public int CaseRate 34 { 35 get { return caseRate; } 36 } 37 } 38 39 40 /// <summary> 41 /// get main task case count(just main but not Include the goto case) 42 /// </summary> 43 /// <param name="startNode">start Node</param> 44 /// <returns>count</returns> 45 public static int GetCount(CaseCell startNode) 46 { 47 int nowCount = 0; 48 List<CaseLoopCountInfo> nowLoops = new List<CaseLoopCountInfo>(); 49 while (startNode!=null) 50 { 51 if (startNode.CaseType == CaseType.Case) 52 { 53 nowCount++; 54 } 55 else if (startNode.CaseType == CaseType.Repeat) 56 { 57 if (startNode.IsHasChild) 58 { 59 myCaseLaodInfo tempProjectLoadInfo = myCaseScriptAnalysisEngine.getCaseLoadInfo(startNode.CaseXmlNode); 60 nowLoops.Add(new CaseLoopCountInfo(startNode.ChildCells[0], tempProjectLoadInfo.times)); 61 } 62 } 63 else if (startNode.CaseType == CaseType.Project) 64 { 65 if(startNode.IsHasChild) 66 { 67 startNode = startNode.ChildCells[0]; 68 } 69 continue; 70 } 71 startNode = startNode.NextCell; 72 } 73 while (nowLoops.Count!=0) 74 { 75 startNode = nowLoops[nowLoops.Count - 1].LoopNode; 76 int tempRate = nowLoops[nowLoops.Count - 1].CaseRate; 77 nowLoops.Remove(nowLoops[nowLoops.Count - 1]); 78 while (startNode != null) 79 { 80 if (startNode.CaseType == CaseType.Case) 81 { 82 nowCount += tempRate; 83 } 84 else if (startNode.CaseType == CaseType.Repeat) 85 { 86 if (startNode.IsHasChild) 87 { 88 myCaseLaodInfo tempProjectLoadInfo = myCaseScriptAnalysisEngine.getCaseLoadInfo(startNode.CaseXmlNode); 89 nowLoops.Add(new CaseLoopCountInfo(startNode.ChildCells[0], tempProjectLoadInfo.times * tempRate)); 90 } 91 } 92 startNode = startNode.NextCell; 93 } 94 } 95 return nowCount; 96 } 97 98 99 } 100 101 102 /// <summary> 103 /// CsaeQueue it will only used in myCaseRunTime 104 /// </summary> 105 class myCsaeQueue 106 { 107 private CaseCell startCaseNode; 108 private CaseCell nowCaseNode; 109 List<myCaseLoop> myCaseLoopList; 110 111 private int queueTotalCount; 112 private int queueNowCount; 113 114 public event delegateLoopChangeEventHandler OnLoopChangeEvent; 115 116 /// <summary> 117 /// myCsaeQueue initialize 118 /// </summary> 119 /// <param name="yourStartCase">your StartCase and make sure it is not null</param> 120 public myCsaeQueue(CaseCell yourStartCase) 121 { 122 queueTotalCount = RunCaseCount.GetCount(yourStartCase); 123 startCaseNode = yourStartCase; 124 nowCaseNode = null; 125 myCaseLoopList = new List<myCaseLoop>(); 126 } 127 128 /// <summary> 129 /// get now CaseCell 130 /// </summary> 131 public CaseCell NowCaseNode 132 { 133 get 134 { 135 if (nowCaseNode != null) 136 { 137 if (myCaseLoopList.Count > 0) 138 { 139 return myCaseLoopList[myCaseLoopList.Count - 1].NowCaseNode; 140 } 141 else 142 { 143 return nowCaseNode; 144 } 145 } 146 else 147 { 148 return startCaseNode; 149 } 150 } 151 } 152 153 /// <summary> 154 /// get the Queue Count Progress(queueTotalCount and queueNowCount) 155 /// </summary> 156 public KeyValuePair<int,int> GetCountProgress 157 { 158 get 159 { 160 return new KeyValuePair<int, int>(queueTotalCount, queueNowCount); 161 } 162 } 163 164 165 /// <summary> 166 /// i will add new CaseLoop and Subscribe 【OnLoopChangeEvent】 167 /// </summary> 168 /// <param name="yourStartCase">your StartCase</param> 169 /// <param name="yourTimes">your Times</param> 170 private void AddCaseLoop(CaseCell yourStartCase, int yourTimes) 171 { 172 myCaseLoopList.Add(new myCaseLoop(yourStartCase, yourTimes)); 173 myCaseLoopList[myCaseLoopList.Count - 1].OnLoopChangeEvent += OnLoopChangeEvent; 174 } 175 176 /// <summary> 177 /// i will remove your CaseLoop and unSubscribe 【OnLoopChangeEvent】 178 /// </summary> 179 /// <param name="yourCaseLoop">yourCaseLoop</param> 180 private void DelCaseLoop(myCaseLoop yourCaseLoop) 181 { 182 yourCaseLoop.OnLoopChangeEvent -= OnLoopChangeEvent; 183 myCaseLoopList.Remove(yourCaseLoop); 184 } 185 186 187 /// <summary> 188 /// i will get the next myTreeTagInfo in my queue 189 /// </summary> 190 /// <returns>the CaseCell you want</returns> 191 public CaseCell nextCase() 192 { 193 194 if(nowCaseNode==null) //起始節點 195 { 196 nowCaseNode = startCaseNode; 197 if (nowCaseNode.CaseType == CaseType.Repeat) 198 { 199 if (nowCaseNode.IsHasChild) 200 { 201 myCaseLaodInfo tempProjectLoadInfo = myCaseScriptAnalysisEngine.getCaseLoadInfo(nowCaseNode.CaseXmlNode); 202 AddCaseLoop(nowCaseNode.ChildCells[0], tempProjectLoadInfo.times); 203 } 204 return nextCase(); 205 } 206 else if (nowCaseNode.CaseType == CaseType.Case) 207 { 208 queueNowCount++; 209 return nowCaseNode; 210 } 211 else if (nowCaseNode.CaseType == CaseType.Project) 212 { 213 if (nowCaseNode.IsHasChild) 214 { 215 startCaseNode = nowCaseNode.ChildCells[0]; 216 nowCaseNode = null; 217 return nextCase(); 218 } 219 return null; //空Project 220 } 221 else 222 { 223 return null; //當前設計不會有這種狀況 224 } 225 } 226 else 227 { 228 if (myCaseLoopList.Count > 0) 229 { 230 int tempNowListIndex = myCaseLoopList.Count - 1; 231 CaseCell tempNextLoopTreeNode = myCaseLoopList[tempNowListIndex].nextCase(); 232 if (tempNextLoopTreeNode == null) 233 { 234 DelCaseLoop(myCaseLoopList[tempNowListIndex]); 235 return nextCase(); 236 } 237 else 238 { 239 if (tempNextLoopTreeNode.CaseType == CaseType.Repeat) 240 { 241 if (tempNextLoopTreeNode.IsHasChild) 242 { 243 myCaseLaodInfo tempProjectLoadInfo = myCaseScriptAnalysisEngine.getCaseLoadInfo(tempNextLoopTreeNode.CaseXmlNode); 244 AddCaseLoop(tempNextLoopTreeNode.ChildCells[0], tempProjectLoadInfo.times); 245 } 246 247 return nextCase(); 248 } 249 else if (tempNextLoopTreeNode.CaseType == CaseType.Case) 250 { 251 queueNowCount++; 252 return tempNextLoopTreeNode; 253 } 254 else 255 { 256 return null; //當前設計不會有這種狀況 257 } 258 } 259 } 260 else 261 { 262 if(nowCaseNode.NextCell == null) 263 { 264 return null; //當前 【Queue】 結束 265 } 266 else 267 { 268 nowCaseNode = nowCaseNode.NextCell; 269 if (nowCaseNode.CaseType == CaseType.Repeat) 270 { 271 if (nowCaseNode.IsHasChild) 272 { 273 myCaseLaodInfo tempProjectLoadInfo = myCaseScriptAnalysisEngine.getCaseLoadInfo(nowCaseNode.CaseXmlNode); 274 AddCaseLoop(nowCaseNode.ChildCells[0], tempProjectLoadInfo.times); 275 } 276 277 return nextCase(); 278 } 279 else if (nowCaseNode.CaseType == CaseType.Case) 280 { 281 queueNowCount++; 282 return nowCaseNode; 283 } 284 else 285 { 286 return null; //當前設計不會有這種狀況 287 } 288 } 289 } 290 } 291 } 292 293 } 294 295 /// <summary> 296 /// CaseLoop it will only used in myCsaeQueue 297 /// </summary> 298 class myCaseLoop 299 { 300 private CaseCell startCaseNode; 301 private CaseCell nowCaseNode; 302 private int totalTimes; 303 private int myTimes; 304 305 public event delegateLoopChangeEventHandler OnLoopChangeEvent; 306 307 /// <summary> 308 /// myCaseLoop initialize 309 /// </summary> 310 /// <param name="yourStartCase">your StartCase and make sure it is not null</param> 311 /// <param name="yourTimes">your Times </param> 312 public myCaseLoop(CaseCell yourStartCase, int yourTimes) 313 { 314 totalTimes = myTimes = yourTimes; 315 startCaseNode = yourStartCase; 316 nowCaseNode = null; 317 } 318 319 /// <summary> 320 /// get now CaseCell 321 /// </summary> 322 public CaseCell NowCaseNode 323 { 324 get 325 { 326 if (nowCaseNode != null) 327 { 328 return nowCaseNode; 329 } 330 else 331 { 332 return startCaseNode; 333 } 334 } 335 } 336 337 /// <summary> 338 /// i will trigger 【OnLoopChangeEvent】 339 /// </summary> 340 /// <param name="yourTarget"></param> 341 private void ReportLoopProgress(CaseCell yourTarget) 342 { 343 if (OnLoopChangeEvent != null) 344 { 345 OnLoopChangeEvent(yourTarget.ParentCell, string.Format("{0}/{1}", totalTimes, totalTimes - myTimes + 1)); 346 } 347 } 348 349 /// <summary> 350 /// i will trigger 【OnLoopChangeEvent】 and this lood is end 351 /// </summary> 352 /// <param name="yourTarget"></param> 353 private void ReportLoopEnd(CaseCell yourTarget) 354 { 355 if (OnLoopChangeEvent != null) 356 { 357 this.OnLoopChangeEvent(yourTarget.ParentCell, ""); 358 } 359 } 360 361 /// <summary> 362 /// i will get the next myTreeTagInfo in my loop 363 /// </summary> 364 /// <returns>the CaseCell you want</returns> 365 public CaseCell nextCase() 366 { 367 if (myTimes > 0) 368 { 369 if (nowCaseNode == null) //起始節點 370 { 371 nowCaseNode = startCaseNode; 372 //report position 373 ReportLoopProgress(nowCaseNode); 374 return nowCaseNode; 375 } 376 else 377 { 378 if (nowCaseNode.NextCell == null) 379 { 380 myTimes--; 381 if (myTimes > 0) 382 { 383 nowCaseNode = startCaseNode; 384 ReportLoopProgress(nowCaseNode); 385 return nowCaseNode; 386 } 387 else 388 { 389 ReportLoopEnd(nowCaseNode); 390 return null; //此處爲null,指示當前【Loop】結束 391 } 392 393 } 394 else 395 { 396 nowCaseNode = nowCaseNode.NextCell; 397 return nowCaseNode; //此處caseType可能爲case或repeat,該類的擁有者將會分別處理 398 } 399 } 400 } 401 else 402 { 403 return null; 404 } 405 } 406 } 407 408 /// <summary> 409 /// myCaseRunTime - you can get next case here 410 /// </summary> 411 public sealed class myCaseRunTime 412 { 413 414 private List<myCsaeQueue> myCsaeQueueList; 415 private bool isThroughAllCase; 416 417 /// <summary> 418 /// show loop track 419 /// </summary> 420 public event delegateLoopChangeEventHandler OnLoopChangeEvent; 421 /// <summary> 422 /// show Queue track (the frist and last Queue will nor trigger) 423 /// </summary> 424 public event delegateQueueChangeEventHandler OnQueueChangeEvent; 425 426 /// <summary> 427 /// myCaseRunTime initialize 428 /// </summary> 429 public myCaseRunTime() 430 { 431 myCsaeQueueList = new List<myCsaeQueue>(); 432 } 433 434 /// <summary> 435 /// get now CaseRunTime all Progress 436 /// </summary> 437 public List<KeyValuePair<int ,int >> GetNowCountProgress 438 { 439 get 440 { 441 List<KeyValuePair<int, int>> nowCountProgress = new List<KeyValuePair<int, int>>(); 442 foreach (var tempCsaeQueue in myCsaeQueueList) 443 { 444 nowCountProgress.Add(tempCsaeQueue.GetCountProgress); 445 } 446 return nowCountProgress; 447 } 448 } 449 450 /// <summary> 451 /// i will add new CsaeQueue and Subscribe 【OnLoopChangeEvent】 452 /// </summary> 453 /// <param name="yourCsaeQueue">your CsaeQueue that will add</param> 454 private void AddCsaeQueue(myCsaeQueue yourCsaeQueue) 455 { 456 myCsaeQueueList.Add(yourCsaeQueue); 457 yourCsaeQueue.OnLoopChangeEvent += OnLoopChangeEvent; 458 } 459 460 //// <summary> 461 /// i will add new CsaeQueue and Subscribe 【OnLoopChangeEvent】(and will trigger【OnQueueChangeEvent】) 462 /// </summary> 463 /// <param name="yourCsaeQueue">your CsaeQueue that will add</param> 464 /// <param name="yourProjectId">Project Id to OnQueueChangeEvent</param> 465 /// <param name="yourCaseId">Case Id to OnQueueChangeEvent</param> 466 private void AddCsaeQueue(myCsaeQueue yourCsaeQueue, int yourProjectId, int yourCaseId) 467 { 468 ReportQueueAction(myCsaeQueueList[myCsaeQueueList.Count - 1].NowCaseNode, string.Format("▼GoTo Project:{0} Case:{1}", yourProjectId, yourCaseId)); 469 AddCsaeQueue(yourCsaeQueue); 470 ReportQueueAction(myCsaeQueueList[myCsaeQueueList.Count - 1].NowCaseNode, "▲"); 471 } 472 473 /// <summary> 474 /// i will remove the CaseQueue and unSubscribe 【OnLoopChangeEvent】 475 /// </summary> 476 /// <param name="yourCsaeQueue">your CsaeQueue that will rwmove</param> 477 private void DelCsaeQueue(myCsaeQueue yourCsaeQueue) 478 { 479 if (myCsaeQueueList.Count>1) 480 { 481 ReportQueueAction(yourCsaeQueue.NowCaseNode, "▼"); 482 ReportQueueAction(myCsaeQueueList[myCsaeQueueList.Count - 2].NowCaseNode, "▼▲"); 483 } 484 yourCsaeQueue.OnLoopChangeEvent -= OnLoopChangeEvent; 485 myCsaeQueueList.Remove(yourCsaeQueue); 486 } 487 488 489 /// <summary> 490 /// i will report the QueueAction to his user 491 /// </summary> 492 /// <param name="yourTarget">your CaseCell Target</param> 493 /// <param name="yourMessage">your Message</param> 494 private void ReportQueueAction(CaseCell yourTarget, string yourMessage) 495 { 496 if (OnQueueChangeEvent != null) 497 { 498 OnQueueChangeEvent(yourTarget, yourMessage); 499 } 500 } 501 502 503 /// <summary> 504 /// you must readyStart before get nextCase (and here also can reset the StartCase) 505 /// </summary> 506 /// <param name="yourStartCase">your StartCase</param> 507 public void readyStart(CaseCell yourStartCase) 508 { 509 myCsaeQueueList.Clear(); 510 AddCsaeQueue(new myCsaeQueue(yourStartCase)); 511 ReportQueueAction(yourStartCase, "◆"); 512 } 513 514 515 /// <summary> 516 /// you must readyStart before get nextCase (and here also can reset the StartCase) 517 /// </summary> 518 /// <param name="yourStartCase">your StartCase</param> 519 /// <param name="yourIsThrough">it will change the behaviour that is it will go through all case(now it is replaced by [goto])</param> 520 public void readyStart(CaseCell yourStartCase, bool yourIsThrough) 521 { 522 readyStart(yourStartCase); 523 isThroughAllCase = yourIsThrough; 524 } 525 526 /// <summary> 527 /// i will get the next myTreeTagInfo in myCaseRunTime 528 /// </summary> 529 /// <returns>the CaseCell you want</returns> 530 public CaseCell nextCase() 531 { 532 if (myCsaeQueueList.Count > 0) 533 { 534 CaseCell tempTreeNodeCase = myCsaeQueueList[myCsaeQueueList.Count - 1].nextCase(); 535 if(tempTreeNodeCase==null) 536 { 537 DelCsaeQueue(myCsaeQueueList[myCsaeQueueList.Count - 1]); 538 return nextCase(); 539 } 540 else 541 { 542 return tempTreeNodeCase; 543 } 544 } 545 else 546 { 547 return null; 548 } 549 } 550 551 /// <summary> 552 /// here i will jump into other case in myCaseRunTime 553 /// </summary> 554 /// <param name="yourProjectId">your Project Id</param> 555 /// <param name="yourCaseId">your Case Id</param> 556 /// <returns>is success</returns> 557 public bool gotoMyCase(int yourProjectId, int yourCaseId, Dictionary<int, Dictionary<int, CaseCell>> myRunTimeCaseDictionary) 558 { 559 if (myRunTimeCaseDictionary.ContainsKey(yourProjectId)) 560 { 561 if (myRunTimeCaseDictionary[yourProjectId].ContainsKey(yourCaseId)) 562 { 563 AddCsaeQueue(new myCsaeQueue(myRunTimeCaseDictionary[yourProjectId][yourCaseId]), yourProjectId, yourCaseId); 564 return true; 565 } 566 else 567 { 568 ReportQueueAction(myCsaeQueueList[myCsaeQueueList.Count - 1].NowCaseNode, "▼GoTo error"); 569 return false; 570 } 571 } 572 else 573 { 574 return false; 575 } 576 } 577 }
而最終myCaseRunTime也是爲CaseActionActuator 服務的,如今來看下CaseActionActuator。
CaseActionActuator相對比較多一點,由於要完成的功能會多一些,跟其餘模塊的聯繫也會大一些
這個可能看起來就很亂了,上圖的模塊主要就是一個Case文件的在系統中的表現,能夠理解爲一個User,這個User經過Case腳本文件能夠執行一套業務,執行過程也是獨立的,環境,線程,數據也都是獨立的。因此能夠建立任意多個這種模塊以模擬大量的用戶同時操做,固然腳本可使用不一樣的腳本文件,也可使用相同腳本文件(若使用相同腳本文件系統會對當前模塊進行深度克隆,克隆的用戶共享部分不會影響運行的數據)。該模塊還能夠選擇以Cell對UI控件進行綁定,以達到執行過程當中用戶界面的友好反饋,固然不一樣的UI控件的動態效果須要單獨的處理(處理由另外一個輔助模塊myActionActuator完成)
這個模塊的圖起來亂點,不過code相對清晰,有興趣能夠看下面代碼(貼出的是關鍵部分)
1 /// <summary> 2 /// CASE執行器 3 /// </summary> 4 public class CaseActionActuator:IDisposable,ICloneable 5 { 6 #region Private Class 7 /// <summary> 8 /// 描述執行可能所須要的附加信息(可擴展),能夠爲null 9 /// </summary> 10 private class ExecutiveAdditionalInfo 11 { 12 private bool isRetry; 13 private int tryTimes; 14 private bool isStoping; 15 private bool isTryCase; 16 17 public ExecutiveAdditionalInfo(bool yourIstry,int yourTryTimes) 18 { 19 isRetry = yourIstry; 20 tryTimes = yourTryTimes; 21 isStoping = false; 22 isTryCase = false; 23 } 24 25 public ExecutiveAdditionalInfo(bool yourStoping) 26 { 27 isRetry = false; 28 isTryCase = false; 29 tryTimes = -98; 30 isStoping = yourStoping; 31 } 32 33 public ExecutiveAdditionalInfo(bool yourStoping, bool yourTryCase) 34 { 35 isRetry = false; 36 isTryCase = yourTryCase; 37 tryTimes = -98; 38 isStoping = yourStoping; 39 } 40 41 public bool IsReTry 42 { 43 get 44 { 45 return isRetry; 46 } 47 set 48 { 49 isRetry = value; 50 } 51 } 52 53 public bool IsTryCase 54 { 55 get 56 { 57 return isTryCase; 58 } 59 } 60 61 public int TryTimes 62 { 63 get 64 { 65 return tryTimes; 66 } 67 set 68 { 69 tryTimes = value; 70 } 71 } 72 73 public bool IsStoping 74 { 75 get 76 { 77 return isStoping; 78 } 79 } 80 } 81 82 /// <summary> 83 /// 描述單次執行所需的基本數據集 84 /// </summary> 85 private class ExecutivebasicData 86 { 87 myRunCaseData<ICaseExecutionContent> runCaseData; 88 TreeNode executiveNode; 89 90 public ExecutivebasicData(myRunCaseData<ICaseExecutionContent> yourCaseData,TreeNode yourExecutiveNode) 91 { 92 runCaseData = yourCaseData; 93 executiveNode = yourExecutiveNode; 94 } 95 } 96 97 #endregion 98 99 /// <summary> 100 /// 克隆Actuator的根 101 /// </summary> 102 private CaseActionActuator rootActuator; 103 104 /// <summary> 105 /// 執行線程同步器 106 /// </summary> 107 private ManualResetEvent myManualResetEvent = new ManualResetEvent(true); 108 109 /// <summary> 110 /// 執行器名稱 111 /// </summary> 112 private string myName; 113 114 /// <summary> 115 /// Actuator State 116 /// </summary> 117 private CaseActuatorState runState; 118 119 120 /// <summary> 121 /// case guide diver 122 /// </summary> 123 private myCaseRunTime caseRunTime; 124 125 /// <summary> 126 /// ExecutionDevice List with his name【執行驅動器映射表】 127 /// </summary> 128 private Dictionary<string, ICaseExecutionDevice> myExecutionDeviceList; 129 130 /// <summary> 131 /// Parameter List 132 /// </summary> 133 private Dictionary<string, string> runActuatorParameterList; 134 135 /// <summary> 136 /// StaticData List 137 /// </summary> 138 private Dictionary<string, IRunTimeStaticData> runActuatorStaticDataList; 139 140 /// <summary> 141 /// Execution Result List 142 /// </summary> 143 private List<myExecutionDeviceResult> runExecutionResultList; 144 145 /// <summary> 146 /// RunTimeCaseDictionary 147 /// </summary> 148 private Dictionary<int, Dictionary<int, CaseCell>> runTimeCaseDictionary; 149 150 /// <summary> 151 /// ProjctCollection 152 /// </summary> 153 private ProjctCollection runCellProjctCollection; 154 155 /// <summary> 156 /// Actuator Task Thread 157 /// </summary> 158 private Thread myActuatorTaskThread; 159 private Thread myActuatorTryThread; 160 161 /// <summary> 162 /// the thread not used and do not make it out of control 163 /// </summary> 164 private List<Thread> invalidThreadList; 165 166 private string nowExecutiveData; 167 private string myErrorInfo; 168 169 private int executiveThinkTime; 170 private int caseThinkTime; 171 172 public delegate void delegateGetExecutiveDataEventHandler(string yourTitle, string yourContent); 173 public delegate void delegateGetActionErrorEventHandler(string yourContent); 174 public delegate void delegateGetExecutiveResultEventHandler(string sender, myExecutionDeviceResult yourResult); 175 public delegate void delegateGetActuatorStateEventHandler(string sender, CaseActuatorState yourState); 176 public delegate void delegateActuatorParameterListEventHandler(); 177 178 179 public event delegateGetExecutiveData OnGetExecutiveData; 180 public event delegateGetExecutiveData OnGetActionError; 181 public event delegateGetExecutiveResultEventHandler OnExecutiveResult; 182 public event delegateGetActuatorStateEventHandler OnActuatorStateChanged; 183 public delegateActuatorParameterListEventHandler OnActuatorParameterListChanged; //外部須要訪問 event修飾後,會禁止非建立類服務 184 185 186 /// <summary> 187 /// 構造函數 188 /// </summary> 189 public CaseActionActuator() 190 { 191 rootActuator = null; 192 myExecutionDeviceList = new Dictionary<string, ICaseExecutionDevice>(); 193 runActuatorParameterList = new Dictionary<string, string>(); 194 runActuatorStaticDataList = new Dictionary<string, IRunTimeStaticData>(); 195 runExecutionResultList = new List<myExecutionDeviceResult>(); 196 invalidThreadList = new List<Thread>(); 197 myErrorInfo = ""; 198 myName = "Main Actuator"; 199 runState = CaseActuatorState.Stop; 200 executiveThinkTime = 0; 201 } 202 203 /// <summary> 204 /// 構造函數 205 /// </summary> 206 /// <param name="yourName">當前執行器的名稱</param> 207 public CaseActionActuator(string yourName) 208 { 209 rootActuator = null; 210 myExecutionDeviceList = new Dictionary<string, ICaseExecutionDevice>(); 211 runActuatorParameterList = new Dictionary<string, string>(); 212 runActuatorStaticDataList = new Dictionary<string, IRunTimeStaticData>(); 213 runExecutionResultList = new List<myExecutionDeviceResult>(); 214 invalidThreadList = new List<Thread>(); 215 myErrorInfo = ""; 216 myName = yourName; 217 runState = CaseActuatorState.Stop; ; 218 } 219 220 /// <summary> 221 /// 克隆 222 /// </summary> 223 /// <returns>克隆對象</returns> 224 public object Clone() 225 { 226 CaseActionActuator cloneActuator = new CaseActionActuator(); 227 cloneActuator.rootActuator = null; 228 cloneActuator.myExecutionDeviceList = myExecutionDeviceList.MyClone(); 229 cloneActuator.runActuatorParameterList = runActuatorParameterList.MyClone<string,string>(); 230 cloneActuator.runActuatorStaticDataList = runActuatorStaticDataList.MyClone(); 231 //cloneActuator.runExecutionResultList = new List<myExecutionDeviceResult>(); 232 cloneActuator.SetCaseRunTime(this.runTimeCaseDictionary, this.runCellProjctCollection); 233 cloneActuator.caseThinkTime = this.caseThinkTime; 234 return cloneActuator; 235 } 236 237 238 239 /// <summary> 240 /// 獲取或設置執行器標識名 241 /// </summary> 242 public string MyName 243 { 244 get 245 { 246 return myName; 247 } 248 set 249 { 250 myName = value; 251 } 252 } 253 254 /// <summary> 255 /// 獲取或設置執行器的全局思考/等待時間 256 /// </summary> 257 public int ExecutiveThinkTime 258 { 259 get 260 { 261 return executiveThinkTime; 262 } 263 set 264 { 265 executiveThinkTime = value; 266 } 267 } 268 269 /// <summary> 270 /// 獲取【CaseActionActuator】運行狀態 271 /// </summary> 272 public CaseActuatorState Runstate 273 { 274 get 275 { 276 return runState; 277 } 278 } 279 280 /// <summary> 281 /// 獲取當前任務執行進度 282 /// </summary> 283 public List<KeyValuePair<int ,int >> RunProgress 284 { 285 get 286 { 287 if (caseRunTime != null) 288 { 289 return caseRunTime.GetNowCountProgress; 290 } 291 else 292 { 293 return null; 294 } 295 } 296 } 297 298 /// <summary> 299 /// 獲取ErrorInfo屬性 300 /// </summary> 301 public string ErrorInfo 302 { 303 get 304 { 305 return myErrorInfo; 306 } 307 } 308 309 /// <summary> 310 /// 獲取執行過程 311 /// </summary> 312 public string NowExecutiveData 313 { 314 get 315 { 316 return nowExecutiveData; 317 318 } 319 } 320 321 /// <summary> 322 /// 獲取當前任務執行結果列表 323 /// </summary> 324 public List<myExecutionDeviceResult> NowExecutionResultList 325 { 326 get 327 { 328 return runExecutionResultList; 329 } 330 } 331 332 /// <summary> 333 /// 獲取當前參數化數據列表 334 /// </summary> 335 public Dictionary<string, string> NowParameterList 336 { 337 get 338 { 339 return runActuatorParameterList; 340 } 341 } 342 343 /// <summary> 344 /// 獲取當前靜態參數化數據列表 345 /// </summary> 346 public Dictionary<string, IRunTimeStaticData> NowStaticDataList 347 { 348 get 349 { 350 return runActuatorStaticDataList; 351 } 352 } 353 354 /// <summary> 355 /// 獲取當前執行器列表 356 /// </summary> 357 public Dictionary<string, ICaseExecutionDevice> NowExecutionDeviceList 358 { 359 get 360 { 361 return myExecutionDeviceList; 362 } 363 } 364 365 /// <summary> 366 /// 獲取當前CASE列表 367 /// </summary> 368 public Dictionary<int, Dictionary<int, CaseCell>> RunTimeCaseDictionary 369 { 370 get 371 { 372 return runTimeCaseDictionary; 373 } 374 } 375 376 /// <summary> 377 /// 獲取當前ProjctCollection 378 /// </summary> 379 public ProjctCollection RunCellProjctCollection 380 { 381 get 382 { 383 return runCellProjctCollection; 384 } 385 } 386 387 /// <summary> 388 /// 獲取當前執行器是否填充過數據 389 /// </summary> 390 public bool IsActuatorDataFill 391 { 392 get 393 { 394 return ((runCellProjctCollection != null) && (runCellProjctCollection != null)); 395 } 396 } 397 398 /// <summary> 399 /// i can updata you myRunTimeCaseDictionary() 400 /// </summary> 401 /// <param name="yourCaseDictionary">you myRunTimeCaseDictionary</param> 402 public void UpdataRunTimeCaseDictionary(Dictionary<int, Dictionary<int, CaseCell>> yourCaseDictionary) 403 { 404 runTimeCaseDictionary = yourCaseDictionary; 405 } 406 407 //RunTime Queue隊列變化時通知 408 void caseRunTime_OnQueueChangeEvent(CaseCell yourTarget, string yourMessage) 409 { 410 if (yourMessage != "") 411 { 412 if (yourMessage.StartsWith("▲")) 413 { 414 //附加任務起始節點 415 myActionActuator.SetCaseNodeExpand(yourTarget); 416 } 417 else if (yourMessage.StartsWith("◆")) 418 { 419 //主任務起始節點 420 while(yourTarget.CaseType!=CaseType.Case) 421 { 422 if(yourTarget.IsHasChild) 423 { 424 yourTarget=yourTarget.ChildCells[0]; 425 } 426 else 427 { 428 break; 429 } 430 } 431 myActionActuator.SetCaseNodeExpand(yourTarget); 432 } 433 yourMessage = "【" + yourMessage + "】"; 434 } 435 436 myActionActuator.SetCaseNodeLoopChange(yourTarget, yourMessage); 437 } 438 439 //RunTime Queue 中Loop變化時通知 440 void caseRunTime_OnLoopChangeEvent(CaseCell yourTarget, string yourMessage) 441 { 442 if (yourMessage!="") 443 { 444 myActionActuator.SetCaseNodeExpand(yourTarget); 445 myActionActuator.SetCaseNodeLoopRefresh(yourTarget); 446 yourMessage = "【" + yourMessage + "】"; 447 } 448 myActionActuator.SetCaseNodeLoopChange(yourTarget, yourMessage); 449 } 450 451 452 /// <summary> 453 /// i will load your ActionActuator (if your have another rule file ,please override or add a new realize) 454 /// </summary> 455 /// <param name="sourceNode">source Node</param> 456 public void LoadScriptRunTime(XmlNode sourceNode) 457 { 458 if (sourceNode != null) 459 { 460 if (sourceNode.HasChildNodes) 461 { 462 foreach (XmlNode tempNode in sourceNode.ChildNodes) 463 { 464 switch (tempNode.Name) 465 { 466 #region RunTimeParameter 467 case "RunTimeParameter": 468 if (tempNode.HasChildNodes) 469 { 470 foreach (XmlNode tempNodeChild in tempNode.ChildNodes) 471 { 472 if (tempNodeChild.Name == "NewParameter") 473 { 474 if (tempNodeChild.Attributes["name"] != null) 475 { 476 AddRunActuatorParameter(tempNodeChild.Attributes["name"].Value, tempNodeChild.InnerText); 477 } 478 else 479 { 480 SetNowActionError("can not find name in ScriptRunTime - RunTimeParameter"); 481 } 482 } 483 else 484 { 485 SetNowActionError("find unkonw data in ScriptRunTime - RunTimeParameter"); 486 } 487 } 488 } 489 break; 490 #endregion 491 492 #region RunTimeActuator 493 case "RunTimeActuator": 494 if (tempNode.HasChildNodes) 495 { 496 string tempActuatorName = ""; 497 CaseProtocol tempActuatorProtocol = CaseProtocol.unknownProtocol; 498 foreach (XmlNode tempNodeChild in tempNode.ChildNodes) 499 { 500 if (tempNodeChild.Name == "NewActuator") 501 { 502 if (tempNodeChild.Attributes["name"] != null && tempNodeChild.Attributes["protocol"] != null) 503 { 504 tempActuatorName = tempNodeChild.Attributes["name"].Value; 505 try 506 { 507 tempActuatorProtocol = (CaseProtocol)Enum.Parse(typeof(CaseProtocol), tempNodeChild.Attributes["protocol"].Value); 508 } 509 catch 510 { 511 tempActuatorProtocol = CaseProtocol.unknownProtocol; 512 SetNowActionError("find unknown Protocol in ScriptRunTime - RunTimeActuator"); 513 } 514 switch (tempActuatorProtocol) 515 { 516 case CaseProtocol.vanelife_http: 517 myConnectForVanelife_http ConnectInfo = new myConnectForVanelife_http(tempActuatorProtocol, CaseTool.getXmlInnerVaule(tempNodeChild, "dev_key"), CaseTool.getXmlInnerVaule(tempNodeChild, "dev_secret"), CaseTool.getXmlInnerVaule(tempNodeChild, "default_url")); 518 AddExecutionDevice(tempActuatorName, ConnectInfo); 519 break; 520 case CaseProtocol.http: 521 myConnectForHttp ConnectInfo_http = new myConnectForHttp(tempActuatorProtocol, CaseTool.getXmlInnerVaule(tempNodeChild, "default_url")); 522 AddExecutionDevice(tempActuatorName, ConnectInfo_http); 523 break; 524 default: 525 SetNowActionError("find nonsupport Protocol in ScriptRunTime "); 526 break; 527 } 528 } 529 else 530 { 531 SetNowActionError("can not find name or protocol in ScriptRunTime - RunTimeActuator"); 532 } 533 } 534 else 535 { 536 SetNowActionError("find unkonw data in ScriptRunTime - RunTimeActuator"); 537 } 538 } 539 } 540 break; 541 #endregion 542 543 #region RunTimeStaticData 544 case "RunTimeStaticData": 545 if (tempNode.HasChildNodes) 546 { 547 foreach (XmlNode tempNodeChild in tempNode.ChildNodes) 548 { 549 if (tempNodeChild.Name == "NewStaticData") 550 { 551 if (tempNodeChild.Attributes["name"] != null && tempNodeChild.Attributes["type"] != null) 552 { 553 CaseStaticDataType tempType; 554 string tempName = tempNodeChild.Attributes["name"].Value; 555 string tempTypeStr = tempNodeChild.Attributes["type"].Value; 556 string tempVaule = tempNodeChild.InnerText; 557 try 558 { 559 tempType = (CaseStaticDataType)Enum.Parse(typeof(CaseStaticDataType), "staticData_"+tempTypeStr); 560 } 561 catch 562 { 563 SetNowActionError("find unknown type in RunTimeStaticData - ScriptRunTime"); 564 continue; 565 } 566 switch (tempType) 567 { 568 case CaseStaticDataType.staticData_index: 569 myStaticDataIndex tempStaticDataIndex; 570 string tempTypeError; 571 if (myCaseDataTypeEngine.getIndexStaticData(out tempStaticDataIndex, out tempTypeError, tempVaule)) 572 { 573 runActuatorStaticDataList.myAdd(tempName, tempStaticDataIndex); 574 } 575 else 576 { 577 runActuatorStaticDataList.myAdd(tempName, tempStaticDataIndex); 578 SetNowActionError(tempVaule); 579 } 580 break; 581 case CaseStaticDataType.staticData_random: 582 myStaticDataRandomStr tempStaticDataRandomStr; 583 if(myCaseDataTypeEngine.getRandomStaticData(out tempStaticDataRandomStr,out tempTypeError,tempVaule)) 584 { 585 runActuatorStaticDataList.myAdd(tempName, tempStaticDataRandomStr); 586 } 587 else 588 { 589 runActuatorStaticDataList.myAdd(tempName, tempStaticDataRandomStr); 590 SetNowActionError(tempVaule); 591 } 592 break; 593 case CaseStaticDataType.staticData_time: 594 myStaticDataNowTime tempStaticDataNowTime; 595 myCaseDataTypeEngine.getTimeStaticData(out tempStaticDataNowTime, tempVaule); 596 runActuatorStaticDataList.myAdd(tempName, tempStaticDataNowTime); 597 break; 598 default: 599 SetNowActionError("find nonsupport Protocol in RunTimeStaticData - ScriptRunTime "); 600 break; 601 } 602 603 } 604 else 605 { 606 SetNowActionError("can not find name or type in RunTimeStaticData - ScriptRunTime"); 607 } 608 } 609 else 610 { 611 SetNowActionError("find unkonw data in RunTimeStaticData - ScriptRunTime"); 612 } 613 } 614 } 615 break; 616 #endregion 617 618 default: 619 SetNowActionError("find unkonw data in ScriptRunTime"); 620 break; 621 } 622 } 623 } 624 else 625 { 626 SetNowActionError("Error Source Node"); 627 } 628 } 629 } 630 631 /// <summary> 632 /// 鏈接這些器 633 /// </summary> 634 public void ConnectExecutionDevice() 635 { 636 SetNowExecutiveData("CaseExecutionDevice connecting ......"); 637 foreach (KeyValuePair<string, ICaseExecutionDevice> tempKvp in myExecutionDeviceList) 638 { 639 if (tempKvp.Value.executionDeviceConnect()) 640 { 641 SetNowExecutiveData(string.Format("【RunTimeActuator】:{0} 鏈接成功", tempKvp.Key)); 642 } 643 else 644 { 645 SetNowActionError(tempKvp.Key + "connect fail"); 646 } 647 } 648 SetNowExecutiveData("Connect complete"); 649 } 650 651 /// <summary> 652 /// 爲執行器斷開鏈接 653 /// </summary> 654 public void DisconnectExecutionDevice() 655 { 656 foreach (KeyValuePair<string, ICaseExecutionDevice> tempKvp in myExecutionDeviceList) 657 { 658 tempKvp.Value.executionDeviceClose(); 659 } 660 } 661 662 /// <summary> 663 /// 建立任務 664 /// </summary> 665 private void CreateNewActuatorTask() 666 { 667 //Thread myThreadTest = new Thread(new ThreadStart(ExecutiveActuatorTask),10240); 668 if (myActuatorTaskThread!=null) 669 { 670 if(myActuatorTaskThread.IsAlive) 671 { 672 invalidThreadList.Add(myActuatorTaskThread); 673 ClearInvalidThreadList(); 674 SetNowActionError("Forced to terminate the residual task"); 675 } 676 } 677 myActuatorTaskThread = new Thread(new ThreadStart(ExecutiveActuatorTask)); 678 myActuatorTaskThread.Name = myName + "_ExecutiveActuatorTask"; 679 myActuatorTaskThread.Priority = ThreadPriority.Normal; 680 myActuatorTaskThread.IsBackground = true; 681 myActuatorTaskThread.Start(); 682 } 683 684 /// <summary> 685 /// 執行任務 686 /// </summary> 687 private void ExecutiveActuatorTask() 688 { 689 ConnectExecutionDevice(); 690 CaseCell nowExecutiveNode = null; 691 myRunCaseData<ICaseExecutionContent> nowRunCaseData = null; 692 ExecutiveAdditionalInfo nowAdditionalInfo; 693 694 while((nowExecutiveNode=caseRunTime.nextCase())!=null) 695 { 696 if ((nowRunCaseData = nowExecutiveNode.CaseRunData) != null) 697 { 698 nowAdditionalInfo = null; 699 ExecutiveAnCases(nowRunCaseData, nowExecutiveNode,ref nowAdditionalInfo); 700 701 while (nowAdditionalInfo != null) 702 { 703 //Stoping 704 if (nowAdditionalInfo.IsStoping) 705 { 706 SetNowExecutiveData("操做者主動終止任務"); 707 goto EndTask; 708 } 709 //ReTry 710 if(nowAdditionalInfo.IsReTry) 711 { 712 nowAdditionalInfo.IsReTry = false; 713 ExecutiveAnCases(nowRunCaseData, nowExecutiveNode,ref nowAdditionalInfo); 714 } 715 else 716 { 717 break; 718 } 719 } 720 } 721 else 722 { 723 //沒有執行數據請處理 724 SetNowActionError("嚴重異常,未找到合法執行數據"); 725 726 } 727 } 728 EndTask: 729 DisconnectExecutionDevice(); 730 SetRunState(CaseActuatorState.Stop); 731 SetNowExecutiveData("任務已結束"); 732 } 733 734 /// <summary> 735 /// 建立一個定項執行任務 736 /// </summary> 737 /// <param name="yourTryNode"></param> 738 private void CreateNewActuatorTry(CaseCell yourTryNode) 739 { 740 if (myActuatorTryThread != null) 741 { 742 if (myActuatorTryThread.IsAlive) 743 { 744 invalidThreadList.Add(myActuatorTryThread); 745 ClearInvalidThreadList(); 746 SetNowActionError("Forced to terminate the residual task"); 747 } 748 } 749 myActuatorTryThread = new Thread(new ParameterizedThreadStart(ExecutiveActuatorTry)); 750 myActuatorTryThread.Name = myName + "_ExecutiveActuatorTry"; 751 myActuatorTryThread.Priority = ThreadPriority.Normal; 752 myActuatorTryThread.IsBackground = true; 753 myActuatorTryThread.Start(yourTryNode); 754 } 755 756 /// <summary> 757 /// 執行一次定項測試 758 /// </summary> 759 private void ExecutiveActuatorTry(object yourTryNode) 760 { 761 ConnectExecutionDevice(); 762 CaseCell nowExecutiveNode = (CaseCell)yourTryNode; 763 myRunCaseData<ICaseExecutionContent> nowRunCaseData = nowExecutiveNode.CaseRunData; 764 ExecutiveAdditionalInfo nowAdditionalInfo; 765 766 if (nowRunCaseData != null) 767 { 768 nowAdditionalInfo = new ExecutiveAdditionalInfo(false, true); 769 ExecutiveAnCases(nowRunCaseData, nowExecutiveNode, ref nowAdditionalInfo); 770 } 771 else 772 { 773 //沒有執行數據請處理 774 SetNowActionError("嚴重異常,未找到合法執行數據"); 775 } 776 777 DisconnectExecutionDevice(); 778 SetRunState(CaseActuatorState.Stop); 779 SetNowExecutiveData("定項執行完成"); 780 } 781 782 /// <summary> 783 /// 執行指定的Case 784 /// </summary> 785 /// <param name="nowRunCaseData">myRunCaseData</param> 786 /// <param name="nowExecutiveNode">now CaseCell</param> 787 private void ExecutiveAnCases(myRunCaseData<ICaseExecutionContent> nowRunCaseData, CaseCell nowExecutiveNode, ref ExecutiveAdditionalInfo nowAdditionalInfo) 788 { 789 bool tempIsBreakError = false; 790 myExecutionDeviceResult executionResult; 791 792 if(runState==CaseActuatorState.Pause) 793 { 794 myActionActuator.SetCaseNodePause(nowExecutiveNode); 795 } 796 myManualResetEvent.WaitOne(); 797 if (runState == CaseActuatorState.Stoping) 798 { 799 nowAdditionalInfo = new ExecutiveAdditionalInfo(true); 800 myActionActuator.SetCaseNodeStop(nowExecutiveNode); 801 return; 802 } 803 804 if (nowRunCaseData.errorMessages == null) 805 { 806 if (myExecutionDeviceList.ContainsKey(nowRunCaseData.testContent.myCaseActuator)) 807 { 808 var nowDevice = myExecutionDeviceList[nowRunCaseData.testContent.myCaseActuator]; 809 ExecutionDeviceRunLink: 810 if (nowDevice.isDeviceConnect) 811 { 812 //nowDevice.executionDeviceRun() 813 myActionActuator.SetCaseNodeRunning(nowExecutiveNode); 814 executionResult = nowDevice.executionDeviceRun(nowRunCaseData.testContent, OnGetExecutiveData, myName,runActuatorParameterList, runActuatorStaticDataList, nowRunCaseData.id); 815 HandleCaseExecutiveResul(nowRunCaseData, nowExecutiveNode, executionResult,ref nowAdditionalInfo); 816 817 } 818 else 819 { 820 //Device沒有鏈接 821 SetNowExecutiveData(string.Format("【ID:{0}】 {1}鏈接中斷,嘗試鏈接中···", nowRunCaseData.id, nowRunCaseData.testContent.myCaseActuator)); 822 if (nowDevice.executionDeviceConnect()) 823 { 824 //nowDevice.executionDeviceRun() 825 goto ExecutionDeviceRunLink; 826 } 827 else 828 { 829 SetNowExecutiveData(string.Format("【ID:{0}】 {1}鏈接失敗", nowRunCaseData.id, nowRunCaseData.testContent.myCaseActuator)); 830 myActionActuator.SetCaseNodeConnectInterrupt(nowExecutiveNode); 831 832 tempIsBreakError = true; 833 executionResult = new myExecutionDeviceResult(nowRunCaseData.id, "CaseActuator鏈接失敗"); 834 } 835 } 836 } 837 else 838 { 839 //testContent沒有找到合適的myCaseActuator 840 SetNowExecutiveData(string.Format("【ID:{0}】 未找到指定CaseActuator", nowRunCaseData.id)); 841 myActionActuator.SetCaseNodeNoActuator(nowExecutiveNode); 842 myActionActuator.SetCaseNodeContentWarning(nowExecutiveNode); 843 844 tempIsBreakError = true; 845 executionResult = new myExecutionDeviceResult(nowRunCaseData.id, "未找到指定CaseActuator"); 846 } 847 } 848 else 849 { 850 //nowRunCaseData有錯誤 851 SetNowActionError(string.Format("【ID:{0}】 執行數據腳本存在錯誤", nowRunCaseData.id)); 852 myActionActuator.SetCaseNodeAbnormal(nowExecutiveNode); 853 854 tempIsBreakError = true; 855 executionResult = new myExecutionDeviceResult(nowRunCaseData.id, "執行數據腳本存在錯誤" + nowRunCaseData.errorMessages); 856 } 857 858 //AddExecutionResult 859 AddExecutionResult(executionResult); 860 861 //Sleep 862 if (!tempIsBreakError) 863 { 864 int tempSleepTime = executiveThinkTime + caseThinkTime; 865 if(tempSleepTime>0) 866 { 867 SetNowExecutiveData(string.Format("sleep {0} ms···", tempSleepTime)); 868 Thread.Sleep(tempSleepTime); 869 } 870 } 871 872 //nowExecutiveNode.TreeView.Invoke(new delegateBasicAnonymous(() => { })); 873 //nowExecutiveNode.TreeView.Invoke(new delegateBasicAnonymous(() => nowExecutiveNode.BackColor = System.Drawing.Color.LightSkyBlue)); 874 875 } 876 877 878 /// <summary> 879 /// 處理斷言,及結果封裝.用於【ExecutiveActuatorTask】 880 /// </summary> 881 /// <param name="yourRunData">確保其不爲null</param> 882 /// <param name="yourExecutionResult">確保其不爲null</param> 883 /// <param name="nowAdditionalInfo"></param> 884 private void HandleCaseExecutiveResul(myRunCaseData<ICaseExecutionContent> yourRunData, CaseCell nowExecutiveNode, myExecutionDeviceResult yourExecutionResult, ref ExecutiveAdditionalInfo nowAdditionalInfo) 885 { 886 string tempError; 887 yourExecutionResult.caseId = yourRunData.id; 888 if (yourExecutionResult.additionalEroor != null) 889 { 890 myActionActuator.SetCaseNodeContentWarning(nowExecutiveNode); 891 } 892 yourExecutionResult.expectMethod = yourRunData.caseExpectInfo.myExpectType; 893 yourExecutionResult.expectContent = yourRunData.caseExpectInfo.myExpectContent.getTargetContentData(runActuatorParameterList, runActuatorStaticDataList, out tempError); 894 if (tempError != null) 895 { 896 myActionActuator.SetCaseNodeContentWarning(nowExecutiveNode); 897 yourExecutionResult.additionalEroor = yourExecutionResult.additionalEroor.myAddValue(tempError); 898 } 899 if (CaseTool.CheckBackData(yourExecutionResult.backContent, yourExecutionResult.expectContent, yourRunData.caseExpectInfo.myExpectType)) 900 { 901 yourExecutionResult.result = CaseResult.Pass; 902 myActionActuator.SetCaseNodePass(nowExecutiveNode); 903 } 904 else 905 { 906 yourExecutionResult.result = CaseResult.Fail; 907 myActionActuator.SetCaseNodeFial(nowExecutiveNode); 908 } 909 910 #region ParameterSaves 911 if(yourRunData.caseAttribute.myParameterSaves!=null) 912 { 913 foreach (ParameterSave tempParameterSave in yourRunData.caseAttribute.myParameterSaves) 914 { 915 string tempPickVaule = null; 916 switch (tempParameterSave.parameterFunction) 917 { 918 case PickOutFunction.pick_json: 919 tempPickVaule = CaseTool.PickJsonParameter(tempParameterSave.parameterFindVaule, yourExecutionResult.backContent); 920 break; 921 case PickOutFunction.pick_str: 922 string tempFindVaule; 923 int tempLen; 924 CaseTool.GetStrPickData(tempParameterSave.parameterFindVaule,out tempFindVaule,out tempLen); 925 if (tempFindVaule!=null) 926 { 927 tempPickVaule = CaseTool.PickStrParameter(tempFindVaule, tempLen , yourExecutionResult.backContent); 928 } 929 else 930 { 931 tempError = string.Format("【ID:{0}】ParameterSave 腳本數據不合法", yourRunData.id); 932 yourExecutionResult.additionalRemark = yourExecutionResult.additionalRemark.myAddValue(tempError); 933 SetNowActionError(tempError); 934 myActionActuator.SetCaseNodeContentWarning(nowExecutiveNode); 935 } 936 937 break; 938 case PickOutFunction.pick_xml: 939 tempPickVaule = CaseTool.PickXmlParameter(tempParameterSave.parameterFindVaule, yourExecutionResult.backContent); 940 break; 941 default: 942 tempError = string.Format("【ID:{0}】 ParameterSave 暫不支持該數據提取方式", yourRunData.id); 943 yourExecutionResult.additionalRemark = yourExecutionResult.additionalRemark.myAddValue(tempError); 944 SetNowActionError(tempError); 945 myActionActuator.SetCaseNodeContentWarning(nowExecutiveNode); 946 break; 947 } 948 if(tempPickVaule!=null) 949 { 950 SetNowActionError(string.Format("【ID:{0}】 ParameterSave 在執行結果中未找到指定參數", yourRunData.id)); 951 } 952 else 953 { 954 AddRunActuatorParameter(tempParameterSave.parameterName, tempPickVaule); 955 } 956 } 957 } 958 #endregion 959 960 #region actions 961 if (yourRunData.actions != null) 962 { 963 if (yourRunData.actions.Keys.Contains(yourExecutionResult.result)) 964 { 965 switch (yourRunData.actions[yourExecutionResult.result].caseAction) 966 { 967 case CaseAction.action_alarm: 968 if (yourRunData.actions[yourExecutionResult.result].addInfo != null) 969 { 970 VoiceService.Speak(yourRunData.actions[yourExecutionResult.result].addInfo); 971 SetNowExecutiveData("【action_alarm】"); 972 } 973 else 974 { 975 VoiceService.Beep(); 976 } 977 break; 978 case CaseAction.action_continue: 979 //do nothing 980 break; 981 case CaseAction.action_goto: 982 if (nowAdditionalInfo != null) 983 { 984 //定項不執行goto 985 if(nowAdditionalInfo.IsTryCase) 986 { 987 break; 988 } 989 } 990 if (yourRunData.actions[yourExecutionResult.result].addInfo == null) 991 { 992 tempError = string.Format("【ID:{0}】 CaseAction Case數據中部沒有發現目的ID", yourRunData.id); 993 yourExecutionResult.additionalRemark = yourExecutionResult.additionalRemark.myAddValue(tempError); 994 SetNowActionError(tempError); 995 myActionActuator.SetCaseNodeContentWarning(nowExecutiveNode); 996 } 997 else 998 { 999 int tempCaseID; 1000 int tempProjectID; 1001 if (CaseTool.getTargetCaseID(yourRunData.actions[yourExecutionResult.result].addInfo, out tempProjectID, out tempCaseID)) 1002 { 1003 if (caseRunTime.gotoMyCase(tempProjectID, tempCaseID, runTimeCaseDictionary )) 1004 { 1005 SetNowExecutiveData("【action_goto】"); 1006 yourExecutionResult.additionalRemark = yourExecutionResult.additionalRemark.myAddValue(string.Format("【action_goto】觸發,已經跳轉到Project:{0} Case:{1}", tempProjectID, tempCaseID)); 1007 } 1008 else 1009 { 1010 tempError = string.Format("【ID:{0}】action_goto跳轉任務未成功", yourRunData.id); 1011 yourExecutionResult.additionalRemark = yourExecutionResult.additionalRemark.myAddValue(tempError); 1012 SetNowActionError(tempError); 1013 } 1014 } 1015 else 1016 { 1017 tempError = string.Format("【ID:{0}】 CaseAction 目標跳轉Case不合法", yourRunData.id); 1018 yourExecutionResult.additionalRemark = yourExecutionResult.additionalRemark.myAddValue(tempError); 1019 SetNowActionError(tempError); 1020 myActionActuator.SetCaseNodeContentWarning(nowExecutiveNode); 1021 } 1022 } 1023 break; 1024 case CaseAction.action_retry: 1025 if (nowAdditionalInfo != null) 1026 { 1027 //定項不執行goto 1028 if (nowAdditionalInfo.IsTryCase) 1029 { 1030 break; 1031 } 1032 } 1033 if (yourRunData.actions[yourExecutionResult.result].addInfo != null) 1034 { 1035 try 1036 { 1037 int tempTryTimes = int.Parse(yourRunData.actions[yourExecutionResult.result].addInfo); 1038 if (tempTryTimes > 0) 1039 { 1040 if (nowAdditionalInfo == null) 1041 { 1042 nowAdditionalInfo = new ExecutiveAdditionalInfo(true, tempTryTimes); 1043 if (nowAdditionalInfo.TryTimes > 0) 1044 { 1045 SetNowExecutiveData("【action_retry】將被觸發"); 1046 } 1047 else 1048 { 1049 nowAdditionalInfo.IsReTry = false; 1050 } 1051 } 1052 else 1053 { 1054 nowAdditionalInfo.TryTimes--; 1055 yourExecutionResult.additionalRemark += string.Format("retry: {0}/{1}", tempTryTimes, tempTryTimes - nowAdditionalInfo.TryTimes); 1056 if( nowAdditionalInfo.TryTimes > 0) 1057 { 1058 nowAdditionalInfo.IsReTry = true; 1059 } 1060 } 1061 } 1062 1063 } 1064 catch 1065 { 1066 tempError = string.Format("【ID:{0}】 retry 解析錯誤", yourRunData.id); 1067 yourExecutionResult.additionalRemark = yourExecutionResult.additionalRemark.myAddValue(tempError); 1068 SetNowActionError(tempError); 1069 myActionActuator.SetCaseNodeContentWarning(nowExecutiveNode); 1070 } 1071 } 1072 else 1073 { 1074 if (nowAdditionalInfo == null) 1075 { 1076 nowAdditionalInfo = new ExecutiveAdditionalInfo(true, -99); 1077 } 1078 else 1079 { 1080 yourExecutionResult.additionalRemark += "【action_retry】 always"; 1081 nowAdditionalInfo.IsReTry = true; 1082 } 1083 } 1084 break; 1085 case CaseAction.action_stop: 1086 PauseCaseScript(); 1087 break; 1088 case CaseAction.action_unknow: 1089 tempError = string.Format("【ID:{0}】 CaseAction 未能解析", yourRunData.id); 1090 yourExecutionResult.additionalRemark = yourExecutionResult.additionalRemark.myAddValue(tempError); 1091 SetNowActionError(tempError); 1092 myActionActuator.SetCaseNodeContentWarning(nowExecutiveNode); 1093 break; 1094 default: 1095 //do nothing 1096 break; 1097 1098 } 1099 } 1100 } 1101 #endregion 1102 1103 #region Sleep 1104 if(yourRunData.caseAttribute.attributeDelay>0) 1105 { 1106 caseThinkTime = yourRunData.caseAttribute.attributeDelay; 1107 } 1108 else 1109 { 1110 if(caseThinkTime!=0) 1111 { 1112 caseThinkTime = 0; 1113 } 1114 } 1115 #endregion 1116 1117 } 1118 1119 /// <summary> 1120 /// 重置ErrorInfo 1121 /// </summary> 1122 public void ResetErrorInfo() 1123 { 1124 myErrorInfo = ""; 1125 } 1126 1127 /// <summary> 1128 /// 重置NowExecutiveData 1129 /// </summary> 1130 public void ResetNowExecutiveData() 1131 { 1132 nowExecutiveData = ""; 1133 } 1134 1135 /// <summary> 1136 /// 觸發【OnGetExecutiveData】 1137 /// </summary> 1138 /// <param name="yourContent"></param> 1139 private void SetNowExecutiveData(string yourContent) 1140 { 1141 if (OnGetExecutiveData != null) 1142 { 1143 this.OnGetExecutiveData(myName, yourContent); 1144 } 1145 } 1146 1147 /// <summary> 1148 /// 設置nowExecutiveData 及觸發【OnGetExecutiveData】 1149 /// </summary> 1150 /// <param name="yourContent"></param> 1151 private void SetAndSaveNowExecutiveData(string yourContent) 1152 { 1153 nowExecutiveData = yourContent; 1154 if (OnGetExecutiveData != null) 1155 { 1156 this.OnGetExecutiveData(myName, yourContent); 1157 } 1158 } 1159 1160 /// <summary> 1161 /// 觸發【OnGetActionError】 1162 /// </summary> 1163 /// <param name="yourContent">Action Error Content</param> 1164 private void SetNowActionError(string yourContent) 1165 { 1166 if (OnGetActionError != null) 1167 { 1168 this.OnGetActionError(myName, yourContent); 1169 } 1170 } 1171 1172 /// <summary> 1173 /// 設置 myErrorInfo 並 觸發【OnGetActionError】(若不想觸發OnGetActionError請直接操做myErrorInfo) 1174 /// </summary> 1175 /// <param name="yourContent">Action Error Content</param> 1176 private void SetAndSaveNowActionError(string yourContent) 1177 { 1178 myErrorInfo = yourContent; 1179 if (OnGetActionError != null) 1180 { 1181 this.OnGetActionError(myName, myErrorInfo); 1182 } 1183 } 1184 1185 /// <summary> 1186 /// 設置 runState 並 觸發【OnActuatorStateChanged】 1187 /// </summary> 1188 /// <param name="yourStae"></param> 1189 private void SetRunState(CaseActuatorState yourStae) 1190 { 1191 runState = yourStae; 1192 if(OnActuatorStateChanged!=null) 1193 { 1194 this.OnActuatorStateChanged(myName, yourStae); 1195 } 1196 } 1197 1198 /// <summary> 1199 /// 添加執行結果到結果集並觸發【OnExecutiveResult】 1200 /// </summary> 1201 /// <param name="yourExecutionResult">your ExecutionResult</param> 1202 private void AddExecutionResult(myExecutionDeviceResult yourExecutionResult) 1203 { 1204 runExecutionResultList.Add(yourExecutionResult); 1205 if (OnExecutiveResult != null) 1206 { 1207 this.OnExecutiveResult(myName, yourExecutionResult); 1208 } 1209 } 1210 1211 /// <summary> 1212 /// 添加ExecutionDevice 1213 /// </summary> 1214 /// <param name="yourDeviceConnectInfo"></param> 1215 /// <returns></returns> 1216 private bool AddExecutionDevice(string yourDeviceName, IConnectExecutiveData yourDeviceConnectInfo) 1217 { 1218 switch (yourDeviceConnectInfo.myCaseProtocol) 1219 { 1220 case CaseProtocol.vanelife_http: 1221 myExecutionDeviceList.myAdd(yourDeviceName, new CaseProtocolExecutionForVanelife_http((myConnectForVanelife_http)yourDeviceConnectInfo)); 1222 break; 1223 case CaseProtocol.http: 1224 myExecutionDeviceList.myAdd(yourDeviceName, new CaseProtocolExecutionForHttp((myConnectForHttp)yourDeviceConnectInfo)); 1225 break; 1226 default: 1227 SetNowActionError(yourDeviceName + " is an nonsupport Protocol"); 1228 break; 1229 } 1230 return true; 1231 } 1232 1233 /// <summary> 1234 /// 添加或修改【runActuatorParameterList】 1235 /// </summary> 1236 /// <param name="yourParameterName"> Parameter Name</param> 1237 /// <param name="yourParameterVaule">Parameter Vaule</param> 1238 public void AddRunActuatorParameter(string yourParameterName, string yourParameterVaule) 1239 { 1240 runActuatorParameterList.myAdd(yourParameterName, yourParameterVaule); 1241 if (OnActuatorParameterListChanged!=null) 1242 { 1243 this.OnActuatorParameterListChanged(); 1244 } 1245 } 1246 1247 /// <summary> 1248 /// 設置 【case guide diver】 1249 /// </summary> 1250 /// <param name="yourCaseDictionary">your Case ID list</param> 1251 public void SetCaseRunTime(Dictionary<int, Dictionary<int, CaseCell>> yourCaseDictionary, ProjctCollection yourProjctCollection) 1252 { 1253 if (yourCaseDictionary != null && yourProjctCollection!=null) 1254 { 1255 runTimeCaseDictionary = yourCaseDictionary; 1256 runCellProjctCollection = yourProjctCollection; 1257 caseRunTime = new myCaseRunTime(); 1258 caseRunTime.OnLoopChangeEvent += caseRunTime_OnLoopChangeEvent; 1259 caseRunTime.OnQueueChangeEvent += caseRunTime_OnQueueChangeEvent; 1260 } 1261 else 1262 { 1263 SetNowActionError("your CaseDictionary or ProjctCollection is null"); 1264 } 1265 } 1266 1267 /// <summary> 1268 /// 獲取執行器是否能夠執行 1269 /// </summary> 1270 /// <returns>is ok</returns> 1271 private bool IsActionActuatorCanRun(CaseCell yourStartNode) 1272 { 1273 if (runCellProjctCollection == null) 1274 { 1275 SetAndSaveNowActionError("your CellProjctCollection is null"); 1276 return false; 1277 } 1278 if (runTimeCaseDictionary==null) 1279 { 1280 SetAndSaveNowActionError("your RunTimeCaseDictionary is null"); 1281 return false; 1282 } 1283 if (caseRunTime == null) 1284 { 1285 SetAndSaveNowActionError("your CaseRuntime is null"); 1286 return false; 1287 } 1288 if (myExecutionDeviceList.Count == 0) 1289 { 1290 SetAndSaveNowActionError("can not find any ExecutionDevice"); 1291 return false; 1292 } 1293 if (yourStartNode==null) 1294 { 1295 SetAndSaveNowActionError("your StartNode is null"); 1296 return false; 1297 } 1298 return true; 1299 } 1300 1301 /// <summary> 1302 /// 執行項目任務 1303 /// </summary> 1304 /// <param name="yourStartNode">起始節點</param> 1305 /// <returns>Success</returns> 1306 public bool RunCaseScript(CaseCell yourStartNode) 1307 { 1308 switch (runState) 1309 { 1310 case CaseActuatorState.Running: 1311 SetAndSaveNowActionError("當前任務還未結束"); 1312 return false; 1313 case CaseActuatorState.Stoping: 1314 SetAndSaveNowActionError("當前任務正在終止中"); 1315 return false; 1316 case CaseActuatorState.Pause: 1317 SetRunState(CaseActuatorState.Running); 1318 myManualResetEvent.Set(); 1319 SetNowExecutiveData("任務恢復"); 1320 return true; 1321 case CaseActuatorState.Stop: 1322 if (yourStartNode == null) 1323 { 1324 SetAndSaveNowActionError("未發現任何可用節點"); 1325 return false; 1326 } 1327 else if (!IsActionActuatorCanRun(yourStartNode)) 1328 { 1329 return false; 1330 } 1331 caseRunTime.readyStart(yourStartNode); 1332 runExecutionResultList.Clear(); 1333 SetRunState(CaseActuatorState.Running); 1334 myManualResetEvent.Set(); 1335 CreateNewActuatorTask(); 1336 SetNowExecutiveData("任務開始"); 1337 return true; 1338 case CaseActuatorState.Trying: 1339 SetAndSaveNowActionError("存在未還未結束指定項任務"); 1340 return false; 1341 default: 1342 return false; 1343 } 1344 1345 } 1346 1347 /// <summary> 1348 /// 暫停當前項目任務(可恢復) 1349 /// </summary> 1350 /// <returns>Success</returns> 1351 public bool PauseCaseScript() 1352 { 1353 if (runState == CaseActuatorState.Running) 1354 { 1355 myManualResetEvent.Reset(); 1356 SetRunState(CaseActuatorState.Pause); 1357 SetNowExecutiveData("任務已暫停"); 1358 return true; 1359 } 1360 else 1361 { 1362 SetAndSaveNowActionError("未發現處於運行狀態中的任務"); 1363 return false; 1364 } 1365 } 1366 1367 /// <summary> 1368 /// 中止項目(不可恢復) 1369 /// </summary> 1370 /// <returns></returns> 1371 public bool StopCaseScript() 1372 { 1373 if (runState == CaseActuatorState.Running) 1374 { 1375 SetRunState(CaseActuatorState.Stoping); 1376 SetNowExecutiveData("正在終止任務"); 1377 return true; 1378 } 1379 else if (runState == CaseActuatorState.Pause) 1380 { 1381 myManualResetEvent.Set(); 1382 SetRunState(CaseActuatorState.Stoping); 1383 SetNowExecutiveData("正在終止任務"); 1384 return true; 1385 } 1386 else if (runState == CaseActuatorState.Stoping) 1387 { 1388 SetAndSaveNowActionError("正在終止任務"); 1389 return false; 1390 } 1391 else 1392 { 1393 SetAndSaveNowActionError("當前項目已經中止"); 1394 return false; 1395 } 1396 } 1397 1398 /// <summary> 1399 /// 單步執行項目成員(必須在Pause狀態,即時不在Pause狀態,也會先進行Pause) 1400 /// </summary> 1401 /// <param name="yourNode">當前項目(若是項目已經開始能夠爲null)</param> 1402 /// <returns>Success</returns> 1403 public bool TryNextCaseScript(CaseCell yourNode) 1404 { 1405 if (runState == CaseActuatorState.Running) 1406 { 1407 PauseCaseScript(); 1408 myManualResetEvent.Set(); 1409 SetNowExecutiveData("單步執行>"); 1410 myManualResetEvent.Reset(); 1411 return true; 1412 1413 } 1414 else if (runState == CaseActuatorState.Stop) 1415 { 1416 if (RunCaseScript(yourNode)) 1417 { 1418 PauseCaseScript(); 1419 SetNowExecutiveData("單步執行>"); 1420 myManualResetEvent.Set(); 1421 myManualResetEvent.Reset(); 1422 return true; 1423 } 1424 else 1425 { 1426 SetAndSaveNowActionError("沒法進行單步執行"); 1427 return false; 1428 } 1429 } 1430 else if (runState == CaseActuatorState.Pause) 1431 { 1432 SetNowExecutiveData("單步執行>"); 1433 myManualResetEvent.Set(); 1434 myManualResetEvent.Reset(); 1435 return true; 1436 } 1437 else if (runState == CaseActuatorState.Running) 1438 { 1439 SetAndSaveNowActionError("正在結束項目,沒法進行單步執行"); 1440 return false; 1441 } 1442 else if (runState == CaseActuatorState.Trying) 1443 { 1444 SetAndSaveNowActionError("存在正在執行指定項任務"); 1445 return false; 1446 } 1447 return false; 1448 } 1449 1450 /// <summary> 1451 /// 定項執行指定項(一直執行同一條CASE,僅在項目中止後可使用,且goto及retry在這種任務中無效) 1452 /// </summary> 1453 /// <param name="yourNode">定項 數據</param> 1454 /// <returns>is Success</returns> 1455 public bool TryNowCaseScript(CaseCell yourNode) 1456 { 1457 if (runState == CaseActuatorState.Stop) 1458 { 1459 SetRunState(CaseActuatorState.Trying); 1460 myManualResetEvent.Set(); 1461 CreateNewActuatorTry(yourNode); 1462 return true; 1463 } 1464 else if (runState == CaseActuatorState.Trying) 1465 { 1466 SetAndSaveNowActionError("上一個指定項任務還未結束"); 1467 return false; 1468 } 1469 else 1470 { 1471 SetAndSaveNowActionError("要進行定向執行前,必須先中止任務"); 1472 return false; 1473 } 1474 } 1475 1476 /// <summary> 1477 /// 強制關閉全部正在執行的任務(謹慎調用) 1478 /// </summary> 1479 public void KillAll() 1480 { 1481 if (myActuatorTaskThread != null) 1482 { 1483 if (myActuatorTaskThread.IsAlive) 1484 { 1485 myActuatorTaskThread.Abort(); 1486 DisconnectExecutionDevice(); 1487 SetRunState(CaseActuatorState.Stop); 1488 } 1489 } 1490 if (myActuatorTryThread != null) 1491 { 1492 if (myActuatorTryThread.IsAlive) 1493 { 1494 myActuatorTryThread.Abort(); 1495 DisconnectExecutionDevice(); 1496 SetRunState(CaseActuatorState.Stop); 1497 } 1498 } 1499 1500 ClearInvalidThreadList(); 1501 } 1502 1503 /// <summary> 1504 /// 清理無效線程列表 1505 /// </summary> 1506 private void ClearInvalidThreadList() 1507 { 1508 for (int i = invalidThreadList.Count - 1; i >= 0; i--) 1509 { 1510 ErrorLog.PutInLog(string.Format("fing InvalidThread Name:{0} State:{1}" , invalidThreadList[i].Name ,invalidThreadList[i].ThreadState.ToString())); 1511 if (invalidThreadList[i].IsAlive) 1512 { 1513 invalidThreadList[i].Abort(); 1514 } 1515 else 1516 { 1517 invalidThreadList.Remove(invalidThreadList[i]); 1518 } 1519 } 1520 for (int i = invalidThreadList.Count - 1; i >= 0; i--) 1521 { 1522 if (!invalidThreadList[i].IsAlive) 1523 { 1524 invalidThreadList.Remove(invalidThreadList[i]); 1525 } 1526 } 1527 } 1528 1529 /// <summary> 1530 /// 實現【IDisposable】強烈建議結束前調用(即時當前可用使用disconnectExecutionDevice替代) 1531 /// </summary> 1532 public void Dispose() 1533 { 1534 KillAll(); 1535 DisconnectExecutionDevice(); 1536 myExecutionDeviceList.Clear(); 1537 runActuatorParameterList.Clear(); 1538 runActuatorStaticDataList.Clear(); 1539 runTimeCaseDictionary = null; 1540 runCellProjctCollection = null; 1541 if (caseRunTime!=null) 1542 { 1543 caseRunTime.OnLoopChangeEvent -= caseRunTime_OnLoopChangeEvent; 1544 caseRunTime.OnQueueChangeEvent -= caseRunTime_OnQueueChangeEvent; 1545 caseRunTime = null; 1546 } 1547 1548 } 1549 1550 }
同時CaseActionActuator還包含一個比較重要的部分myCaseProtocolEngine,該部分是協議引擎,實際上只要PC上可使用的協議經過簡單的修改均可以加入到系統
對於協議引擎必須繼承規定好的接口實現,而後就能將協議加入系統,具體方法見【http://www.cnblogs.com/lulianqi/p/4773268.html】
這個模塊還要許多其餘部分好比Case文件的解析,及其餘能夠自定義的內容這裏就不繼續講下去了,有興趣的能夠下載完整代碼。
MyCommonTool其實包含的東西也比較多不過功能都相對獨立,見下圖
能夠看到這個模塊徹底是由一個個獨立的類組成,相互之間的聯繫很是少。有不少子模塊提供不一樣的服務經過命名也大概能夠看出來VoiceService提供語音服務,myWebTool提供web服務,myEncryption提供加密服務等等,它被系統的不少模塊使用。因爲類比較多而又沒有什麼邏輯上的聯繫,這裏一樣不繼續講下去了。須要瞭解的能夠下載完整的代碼
這個部分主要與UI的形式與呈現有關,固然包括了不少定製的數據的banding,在AutoTest及RemoteService都會用到裏面的東西。不過針對不一樣的界面框架這部分是不能重用的。因此也不用怎麼看,稍微貼張圖吧。
對於AutoTest自己已經沒有太多執行相關的邏輯處理了(都由CaseExecutiveActuator來處理),其主要處理case的呈現,及執行過程當中的動態修改,還包括報告的格式化輸出,同時還要2個重要功能組織多個User(執行體)跟鏈接遠程測試主機。
先稍微過下界面相關的內容,以下圖
如今來看剛剛提到的一個重要的功能組織多個User,AutoTest能夠建立任意多個虛擬執行體User,他們存儲在CaseRunner中,同時CaseRunner還提供一些對User的基本的控制功能,以下圖
而這個部分其實放在邏輯層更加合適,不過以前設計經驗不足。把CaseRunner於UI控件十分深的banding在一塊兒了。綁定效果以下圖
這個就是大量CaseRunner綁定的效果(這個不是主界面,總體的顯示效果能夠看下【http://www.cnblogs.com/lulianqi/p/4773146.html】)
固然還有前面提到的另一個重要功能鏈接遠程測試主機,主要負責鏈接分佈式部署在其餘PC上的執行模塊(就是後面要講的RemoteService),同時能夠控制及配置遠程主機上的User(還包括鏈接的維持)。鏈接功能由RemoteClient組件來完成,它在前面提到的AutoTest.myTool 下,簡單看下它的實現
基本結構以下圖
這個其實實現起來比較簡單,由於藉助WCF框架,不少工做框架已經作好了,不過一些通道的選擇配置以及是否適合雙工通訊可能不是最優的,後面還須要調整,能夠看下下面的code
1 class ExecuteServiceCallback : IExecuteServiceCallback 2 { 3 4 public ExecuteServiceCallback() 5 { 6 7 } 8 9 public delegate void RunnerStateChangeEventHandler(ExecuteServiceCallback sender, RemoteRunnerInfo remoteRunnerInfo); 10 11 public event RunnerStateChangeEventHandler OnRunnerStateChange; 12 13 public void ReportState(RemoteRunnerInfo remoteRunnerInfo) 14 { 15 if (OnRunnerStateChange!=null) 16 { 17 this.OnRunnerStateChange(this, remoteRunnerInfo); 18 } 19 } 20 } 21 22 public class RemoteClient:IDisposable 23 { 24 25 /// <summary> 26 /// 描述當前Client鏈接狀態 27 /// </summary> 28 public enum RemoteClientState 29 { 30 Connecting=0, //正在鏈接 31 Connected=1, //鏈接成功 32 Break = 2, //鏈接中斷,而且正在進行重連 33 Lost = 3 //鏈接中斷,且不會從新鏈接(必須觸發重連) 34 } 35 36 ExecuteServiceClient executeServiceClient = null; 37 InstanceContext instanceContext = null; 38 //EndpointAddress myEp = new EndpointAddress("http://localhost:8087/SelService"); 39 EndpointAddress myEp = null; 40 41 private RemoteRunner showWindow; 42 private bool isLive = false; 43 private RemoteClientState clientState = RemoteClientState.Lost; 44 private int reCounectTime=5; 45 46 private Thread clientLife; 47 48 49 public delegate void RunnerStateChangeEventHandler(RemoteClient sender, RemoteRunnerInfo remoteRunnerInfo); 50 public delegate void AllRunnerInforEventHandler(RemoteClient sender, RemoteRunnerInfo remoteRunnerInfo); 51 public delegate void ClientStateChangeEventHandler(RemoteClient sender, RemoteClientState nowSate); 52 public delegate void ClientErrorEventHandler(RemoteClient sender, string errorInfo); 53 54 public event RunnerStateChangeEventHandler OnRunnerStateChange; 55 public event AllRunnerInforEventHandler OnPutAllRunnerInfor; 56 public event ClientStateChangeEventHandler OnClientStateChange; 57 public event ClientErrorEventHandler OnClientErrorInfor; 58 59 60 61 public RemoteClient(EndpointAddress yourEp, RemoteRunner yourWindow ) 62 { 63 myEp = yourEp; 64 showWindow = yourWindow; 65 } 66 67 public override string ToString() 68 { 69 if (myEp != null) 70 { 71 return myEp.Uri.Host + ":" + myEp.Uri.Port; 72 } 73 else 74 { 75 return "Null Ep"; 76 } 77 } 78 79 /// <summary> 80 /// 獲取或設置RemoteClient 的基礎地址 81 /// </summary> 82 public EndpointAddress ClientEp 83 { 84 get { return myEp;} 85 set { myEp = value; } 86 } 87 88 /// <summary> 89 /// 獲取或設置ShowWindow 90 /// </summary> 91 public RemoteRunner ShowWindow 92 { 93 get { return showWindow; } 94 set { showWindow = value; } 95 } 96 97 /// <summary> 98 /// 獲取當前Client鏈接狀態(自維護狀態,該狀態同時提示生命線程運行狀況) 99 /// </summary> 100 public RemoteClientState ClientState 101 { 102 get { return clientState; } 103 } 104 105 /// <summary> 106 /// 獲取當前executeServiceClient通道狀態 107 /// </summary> 108 public CommunicationState ExecuteServiceClientState 109 { 110 get 111 { 112 if (executeServiceClient==null) 113 { 114 return CommunicationState.Closed; 115 } 116 return executeServiceClient.State; 117 } 118 } 119 120 /// <summary> 121 /// 獲取或設置斷線重連次數限制 122 /// </summary> 123 public int ReCounectTime 124 { 125 get { return reCounectTime; } 126 set { reCounectTime = value; } 127 } 128 129 /// <summary> 130 /// 報告當前Client全部Runner狀態 131 /// </summary> 132 /// <param name="remoteRunnerInfo"></param> 133 private void PutAllRunnerInfor(RemoteRunnerInfo remoteRunnerInfo) 134 { 135 if (OnPutAllRunnerInfor != null) 136 { 137 this.OnPutAllRunnerInfor(this, remoteRunnerInfo); 138 } 139 if (showWindow != null) 140 { 141 showWindow.RefreshRemoteRunnerView(remoteRunnerInfo); 142 } 143 } 144 145 /// <summary> 146 /// 改變鏈接狀態 147 /// </summary> 148 private void SetClientState(RemoteClientState nowState) 149 { 150 clientState = nowState; 151 if (OnClientStateChange != null) 152 { 153 this.OnClientStateChange(this, nowState); 154 } 155 } 156 157 /// <summary> 158 /// 向訂閱者報告錯誤信息 159 /// </summary> 160 /// <param name="errorInfo">錯誤信息</param> 161 private void SetClientErrorInfo(string errorInfo) 162 { 163 if(errorInfo!=null && OnClientErrorInfor!=null) 164 { 165 this.OnClientErrorInfor(this, errorInfo); 166 } 167 } 168 169 /// <summary> 170 /// 建立一個【ExecuteServiceClient】實例 171 /// </summary> 172 /// <returns>【ExecuteServiceClient】實例</returns> 173 private ExecuteServiceClient RestartClient() 174 { 175 if (instanceContext==null) 176 { 177 //InstanceContext 178 ExecuteServiceCallback executeServiceCallback = new ExecuteServiceCallback(); 179 executeServiceCallback.OnRunnerStateChange += executeServiceCallback_OnRunnerStateChange; 180 instanceContext = new InstanceContext(executeServiceCallback); 181 //Binding 182 System.ServiceModel.Channels.Binding binding = new WSDualHttpBinding(); 183 ((System.ServiceModel.WSDualHttpBinding)(binding)).Security.Mode = WSDualHttpSecurityMode.None; 184 ((System.ServiceModel.WSDualHttpBinding)(binding)).Security.Message.ClientCredentialType = MessageCredentialType.UserName; 185 binding.SendTimeout = new TimeSpan(0, 0, 10); 186 binding.OpenTimeout = new TimeSpan(0, 0, 10); 187 binding.ReceiveTimeout = new TimeSpan(0, 0, 10); 188 189 System.ServiceModel.Channels.Binding tcpBinding = new NetTcpBinding(); 190 ((System.ServiceModel.NetTcpBinding)(tcpBinding)).Security.Mode = SecurityMode.None; 191 ((System.ServiceModel.NetTcpBinding)(tcpBinding)).Security.Message.ClientCredentialType = MessageCredentialType.UserName; 192 tcpBinding.SendTimeout = new TimeSpan(0, 0, 10); 193 tcpBinding.OpenTimeout = new TimeSpan(0, 0, 10); 194 tcpBinding.ReceiveTimeout = new TimeSpan(0, 0, 10); 195 196 executeServiceClient = new ExecuteServiceClient(instanceContext, binding, myEp); 197 //executeServiceClient = new ExecuteServiceClient(instanceContext, new WSDualHttpBinding(), myEp); 198 199 instanceContext.Closed += instanceContext_Closed; 200 instanceContext.Opened += instanceContext_Opened; 201 return executeServiceClient; 202 } 203 else 204 { 205 instanceContext.Closed -= instanceContext_Closed; 206 instanceContext.Opened -= instanceContext_Opened; 207 instanceContext = null; 208 return RestartClient(); 209 } 210 } 211 212 #region RestartClient通訊 213 214 /// <summary> 215 /// 處理收到的雙工回調(僅報告有變化的Runner) 216 /// </summary> 217 void executeServiceCallback_OnRunnerStateChange(ExecuteServiceCallback sender, RemoteRunnerInfo remoteRunnerInfo) 218 { 219 if(OnRunnerStateChange!=null) 220 { 221 this.OnRunnerStateChange(this, remoteRunnerInfo); 222 } 223 if (remoteRunnerInfo == null) 224 { 225 if (showWindow != null) 226 { 227 showWindow.ShowError("Null Data" + "\r\n"); 228 } 229 return; 230 } 231 if (remoteRunnerInfo.RunnerStateList != null) 232 { 233 if (showWindow != null) 234 { 235 showWindow.UpdataRemoteRunnerView(remoteRunnerInfo); 236 } 237 } 238 } 239 240 /// <summary> 241 /// 獲取當前Client全部Runner狀態 242 /// </summary> 243 public void GetAllRunnerInfor() 244 { 245 if (ExecuteServiceClientState == CommunicationState.Opened) 246 { 247 try 248 { 249 RemoteRunnerInfo nowRemoteRunnerInfo = executeServiceClient.GetAllRunnerSate(); 250 if (nowRemoteRunnerInfo != null) 251 { 252 PutAllRunnerInfor(nowRemoteRunnerInfo); 253 } 254 } 255 catch (Exception ex) 256 { 257 SetClientErrorInfo(ex.Message); 258 } 259 } 260 else 261 { 262 SetClientErrorInfo("鏈接未打開"); 263 } 264 } 265 266 /// <summary> 267 /// 獲取當前Client全部Runner狀態(提供內部使用,不會捕捉錯誤) 268 /// </summary> 269 private void GetAllRunnerInforEx() 270 { 271 RemoteRunnerInfo nowRemoteRunnerInfo = executeServiceClient.GetAllRunnerSate(); 272 if (nowRemoteRunnerInfo != null) 273 { 274 PutAllRunnerInfor(nowRemoteRunnerInfo); 275 } 276 } 277 278 /// <summary> 279 /// 啓動指定執行器 280 /// </summary> 281 /// <param name="runnerList">執行器列表</param> 282 public void StartRunner(List<int> runnerList) 283 { 284 if (ExecuteServiceClientState == CommunicationState.Opened) 285 { 286 try 287 { 288 executeServiceClient.StartRunner(runnerList.ToArray()); 289 } 290 catch (Exception ex) 291 { 292 SetClientErrorInfo(ex.Message); 293 } 294 } 295 else 296 { 297 SetClientErrorInfo("鏈接未打開"); 298 } 299 } 300 301 /// <summary> 302 /// 暫停指定執行器 303 /// </summary> 304 /// <param name="runnerList">執行器列表</param> 305 public void PauseRunner(List<int> runnerList) 306 { 307 if (ExecuteServiceClientState == CommunicationState.Opened) 308 { 309 try 310 { 311 executeServiceClient.PauseRunner(runnerList.ToArray()); 312 } 313 catch (Exception ex) 314 { 315 SetClientErrorInfo(ex.Message); 316 } 317 } 318 else 319 { 320 SetClientErrorInfo("鏈接未打開"); 321 } 322 } 323 324 /// <summary> 325 /// 中止指定執行器 326 /// </summary> 327 /// <param name="runnerList">執行器列表</param> 328 public void StopRunner(List<int> runnerList) 329 { 330 if (ExecuteServiceClientState == CommunicationState.Opened) 331 { 332 try 333 { 334 executeServiceClient.StopRunner(runnerList.ToArray()); 335 } 336 catch(Exception ex) 337 { 338 SetClientErrorInfo(ex.Message); 339 } 340 } 341 else 342 { 343 SetClientErrorInfo("鏈接未打開"); 344 } 345 } 346 347 #endregion 348 349 350 private void instanceContext_Closed(object sender, EventArgs e) 351 { 352 353 } 354 355 void instanceContext_Opened(object sender, EventArgs e) 356 { 357 358 } 359 360 /// <summary> 361 /// 啓動當前Client 362 /// </summary> 363 public void StartClient() 364 { 365 //Thread myThreadTest = new Thread(new ThreadStart(ExecutiveActuatorTask),10240); 366 if (clientLife != null) 367 { 368 if (clientLife.IsAlive) 369 { 370 if (showWindow != null) 371 { 372 showWindow.ShowError("IsAlive"); 373 } 374 } 375 else 376 { 377 clientLife = null; 378 StartClient(); 379 } 380 } 381 else 382 { 383 clientLife = new Thread(new ParameterizedThreadStart(ClientAliveTask)); 384 clientLife.Name = "ClientLife" + myEp.ToString(); 385 clientLife.Priority = ThreadPriority.Normal; 386 clientLife.IsBackground = true; 387 isLive = true; 388 clientLife.Start(executeServiceClient); 389 } 390 } 391 392 /// <summary> 393 /// 中止當前Client 394 /// </summary> 395 public void StopClient() 396 { 397 isLive = false; 398 } 399 400 401 /// <summary> 402 /// 保持鏈接的心跳及從新鏈接的線程任務 403 /// </summary> 404 private void ClientAliveTask(object yourExecuteClient) 405 { 406 ExecuteServiceClient executeClient = (ExecuteServiceClient)yourExecuteClient; 407 int counectTime = reCounectTime; 408 409 ReConnect: 410 executeClient = RestartClient(); 411 try 412 { 413 SetClientState(RemoteClientState.Connecting); 414 GetAllRunnerInforEx(); 415 SetClientState(RemoteClientState.Connected); 416 } 417 catch (Exception ex) 418 { 419 MyCommonTool.ErrorLog.PutInLogEx(ex); 420 if (counectTime > 0 && isLive) 421 { 422 counectTime--; 423 SetClientState(RemoteClientState.Break); 424 Thread.Sleep(2000); 425 goto ReConnect; 426 } 427 else 428 { 429 StopClient(); 430 } 431 } 432 433 while (isLive) 434 { 435 try 436 { 437 executeClient.ExecuteServiceBeat(); 438 } 439 catch (Exception ex) 440 { 441 SetClientState(RemoteClientState.Break); 442 Thread.Sleep(2000); 443 MyCommonTool.ErrorLog.PutInLogEx(ex); 444 counectTime = reCounectTime; 445 goto ReConnect; 446 } 447 Thread.Sleep(10000); 448 } 449 450 SetClientState(RemoteClientState.Lost); 451 } 452 453 public void Dispose() 454 { 455 if (clientLife != null) 456 { 457 if (clientLife.IsAlive) 458 { 459 clientLife.Abort(); 460 } 461 } 462 executeServiceClient = null; 463 instanceContext = null; 464 } 465 } 466 }
這個部分與AutoTest總體結構是類似的,在執行邏輯上也是一樣依靠邏輯層的3個主要模塊。而內部基於WCF單獨實現了服務部分,服務內容主要包括對狀態及業務返回數據進行過濾重組而後上報給鏈接的控制機,還有接收及處理控制機的控制或配置等命令請求。
下面看一下自身的結構,以下圖
能夠看到結構上與AutoTest確實是大致一致的,主要看下跟分佈式部署相關的2個組件
其餘部分與遠程服務沒有太大關係的就不介紹了
ExecuteService 按要求實現IExecuteService接口,結構跟邏輯都比較清晰(如今這部分的設計十分簡單,後期對服務內容的控制也只須要改動這個地方就能夠了),直接看下下面的結構圖
ServerHost 其實就是對ExecuteService再一層的封裝,加入了許多與執行數據相關的邏輯,以方便應用層面的直接調用,以下圖
如上圖,訂閱了多個回調委託來獲取數據,在內部進行處理(MessageTransferChannel_RunnerCommandCallback,MessageTransferChannel_GetAllRemoteRunnerInfoCallback),同時也定義了許多委託拋出數據給應用使用,同時還須要介紹應用的命令處理後轉發給ExecuteService,固然還有一個重要的功能,服務的啓動跟維持也在這裏完成。下面是這個類code的具體實現
1 class ServerHost 2 { 3 Uri baseAddress = new Uri("http://localhost:8087/SelService");//初始默認值在運行時由設置值來決定 4 ServiceHost baseHost = null; 5 6 public delegate void ServerHostMessageEventHandler(string sender, string message); 7 public event ServerHostMessageEventHandler OnServerHostMessage; 8 9 public delegate List<CaseRunner> BackNowRunnerListEventHandler(); 10 public event BackNowRunnerListEventHandler OnBackNowRunnerList; 11 12 //public delegate void ServerHostCommandEventHandler(RunnerCommand command, List<int> runners); 13 //public event ServerHostCommandEventHandler OnServerHostCommand; 14 15 /// <summary> 16 /// 獲取或設置當前BaseAddress 17 /// </summary> 18 public Uri BaseAddress 19 { 20 get { return baseAddress; } 21 set { baseAddress = value; } 22 } 23 24 /// <summary> 25 /// 獲取當前BaseHost 26 /// </summary> 27 public ServiceHost BaseHost 28 { 29 get { return baseHost; } 30 } 31 32 /// <summary> 33 /// 獲取BaseHost當前鏈接狀態 34 /// </summary> 35 public CommunicationState BaseHostState 36 { 37 get 38 { 39 if (baseHost == null) 40 { 41 return CommunicationState.Closed; 42 } 43 else 44 { 45 return baseHost.State; 46 } 47 } 48 } 49 50 /// <summary> 51 /// 初始化ServerHost 52 /// </summary> 53 /// <param name="yourBaseAddress"></param> 54 public ServerHost(Uri yourBaseAddress) 55 { 56 baseAddress = yourBaseAddress; 57 MessageTransferChannel.MessageCallback += new Action<object, string>((a, b) => AddInfo(b)); 58 59 MessageTransferChannel.OnRunnerCommand += MessageTransferChannel_RunnerCommandCallback; 60 MessageTransferChannel.OnGetAllRemoteRunnerInfo += MessageTransferChannel_GetAllRemoteRunnerInfoCallback; 61 } 62 63 /// <summary> 64 /// 處理ExecuteService的遠程指令【runner狀態獲取】 65 /// </summary> 66 RemoteRunnerInfo MessageTransferChannel_GetAllRemoteRunnerInfoCallback() 67 { 68 List<CaseRunner> caseRunnerList = CaseRunnerList; 69 if (caseRunnerList == null) 70 { 71 return new RemoteRunnerInfo(); 72 } 73 return GetRunnerInfo(caseRunnerList, true); 74 } 75 76 /// <summary> 77 /// 處理ExecuteService的遠程指令【runner控制】 78 /// </summary> 79 void MessageTransferChannel_RunnerCommandCallback(ExecuteService sender, RunnerCommand command, List<int> runners) 80 { 81 List<CaseRunner> caseRunnerList=CaseRunnerList; 82 if(caseRunnerList==null) 83 { 84 return; 85 } 86 RunnerCommandExecute(caseRunnerList, command, runners); 87 } 88 89 /// <summary> 90 /// 觸發更新CaseRunner狀態雙工回調(局部更新) 91 /// </summary> 92 /// <param name="caseRunnerList">CaseRunner 列表</param> 93 public void SendStateCallBack(List<CaseRunner> caseRunnerList) 94 { 95 SendStateInfo(caseRunnerList, false); 96 } 97 98 /// <summary> 99 /// 觸發更新CaseRunner狀態雙工回調(所有更新) 100 /// </summary> 101 /// <param name="caseRunnerList">CaseRunner 列表</param> 102 public void SendAllStateCallBack(List<CaseRunner> caseRunnerList) 103 { 104 SendStateInfo(caseRunnerList, true); 105 } 106 107 /// <summary> 108 /// 像執行行體請求CaseRunner 最新列表 109 /// </summary> 110 private List<CaseRunner> CaseRunnerList 111 { 112 get 113 { 114 if (OnBackNowRunnerList() != null) 115 { 116 return OnBackNowRunnerList(); 117 } 118 return null; 119 } 120 } 121 122 /// <summary> 123 /// 輸出提示信息 124 /// </summary> 125 /// <param name="message"></param> 126 private void AddInfo(string message) 127 { 128 if(OnServerHostMessage!=null) 129 { 130 this.OnServerHostMessage("baseHost", message); 131 } 132 } 133 134 /// <summary> 135 /// 啓動BaseHost 136 /// </summary> 137 public void OpenBaseHost() 138 { 139 if (baseHost == null) 140 { 141 baseHost = new ServiceHost(typeof(ExecuteService), baseAddress); 142 143 ServiceMetadataBehavior smb = new ServiceMetadataBehavior(); 144 smb.HttpGetEnabled = true; 145 smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15; 146 147 baseHost.Description.Behaviors.Add(smb); 148 //baseHost.AddServiceEndpoint(typeof(IExecuteService), new BasicHttpBinding(), ""); 149 //baseHost.AddServiceEndpoint(typeof(IExecuteService), new WSDualHttpBinding(), ""); 150 151 System.ServiceModel.Channels.Binding binding = new WSDualHttpBinding(); 152 ((System.ServiceModel.WSDualHttpBinding)(binding)).Security.Mode = WSDualHttpSecurityMode.None; 153 ((System.ServiceModel.WSDualHttpBinding)(binding)).Security.Message.ClientCredentialType = MessageCredentialType.UserName; 154 155 System.ServiceModel.Channels.Binding tcpBinding = new NetTcpBinding(); 156 ((System.ServiceModel.NetTcpBinding)(tcpBinding)).Security.Mode = SecurityMode.None; 157 ((System.ServiceModel.NetTcpBinding)(tcpBinding)).Security.Message.ClientCredentialType = MessageCredentialType.UserName; 158 159 //測試開安全雙工只能在本機使用 160 baseHost.AddServiceEndpoint(typeof(IExecuteService), binding, ""); 161 162 baseHost.Opening += new EventHandler((yourObject, yourEventAgrs) => AddInfo("Opening")); 163 baseHost.Opened += new EventHandler((yourObject, yourEventAgrs) => AddInfo("Opened")); 164 baseHost.Closed += new EventHandler((yourObject, yourEventAgrs) => AddInfo("Closed")); 165 baseHost.Closing += new EventHandler((yourObject, yourEventAgrs) => AddInfo("Closing")); 166 baseHost.Faulted += new EventHandler((yourObject, yourEventAgrs) => AddInfo("Faulted")); 167 168 169 Thread openBaseThread = new Thread(new ThreadStart(BaseHostOpen)); 170 openBaseThread.IsBackground = true; 171 openBaseThread.Start(); 172 173 } 174 else 175 { 176 if (baseHost.State == CommunicationState.Opened) 177 { 178 AddInfo("服務已經開啓"); 179 } 180 else if (baseHost.State == CommunicationState.Opening) 181 { 182 AddInfo("服務正在開啓"); 183 } 184 else 185 { 186 baseHost.Abort(); 187 baseHost = null; 188 OpenBaseHost(); 189 } 190 } 191 } 192 193 private void BaseHostOpen() 194 { 195 try 196 { 197 baseHost.Open(); 198 } 199 catch (Exception ex) 200 { 201 AddInfo(ex.Message); 202 } 203 } 204 205 /// <summary> 206 /// 關閉BaseHost 207 /// </summary> 208 public void CloseBaseHost() 209 { 210 if (baseHost == null) 211 { 212 AddInfo("未發現服務"); 213 } 214 else 215 { 216 if (baseHost.State != CommunicationState.Closed) 217 { 218 AddInfo(baseAddress.ToString() + "服務關閉"); 219 baseHost.Close(); 220 } 221 else 222 { 223 AddInfo(baseAddress.ToString() + "服務已經關閉"); 224 } 225 } 226 } 227 228 229 /// <summary> 230 /// 執行遠程命令 231 /// </summary> 232 /// <param name="caseRunnerList">CaseRunner 列表</param> 233 /// <param name="command">命令類型</param> 234 /// <param name="Runners">受影響Runner</param> 235 private void RunnerCommandExecute(List<CaseRunner> caseRunnerList, RunnerCommand command, List<int> Runners) 236 { 237 switch (command) 238 { 239 case RunnerCommand.Start: 240 if (Runners != null) 241 { 242 if (Runners.Count > 0) 243 { 244 foreach (int tempRunnerIndex in Runners) 245 { 246 if (caseRunnerList.Count >= tempRunnerIndex) 247 { 248 caseRunnerList[tempRunnerIndex].RunQuiet(); 249 } 250 else 251 { 252 AddInfo("tempRunnerIndex error"); 253 } 254 } 255 } 256 } 257 else 258 { 259 AddInfo("Runners is null"); 260 } 261 break; 262 case RunnerCommand.Stop: 263 if (Runners != null) 264 { 265 if (Runners.Count > 0) 266 { 267 foreach (int tempRunnerIndex in Runners) 268 { 269 if (caseRunnerList.Count >= tempRunnerIndex) 270 { 271 caseRunnerList[tempRunnerIndex].StopQuiet(); 272 } 273 else 274 { 275 AddInfo("tempRunnerIndex error"); 276 } 277 } 278 } 279 } 280 else 281 { 282 AddInfo("Runners is null"); 283 } 284 break; 285 case RunnerCommand.Pause: 286 if (Runners != null) 287 { 288 if (Runners.Count > 0) 289 { 290 foreach (int tempRunnerIndex in Runners) 291 { 292 if (caseRunnerList.Count >= tempRunnerIndex) 293 { 294 caseRunnerList[tempRunnerIndex].PauseQuiet(); 295 } 296 else 297 { 298 AddInfo("tempRunnerIndex error"); 299 } 300 } 301 } 302 } 303 else 304 { 305 AddInfo("Runners is null"); 306 } 307 break; 308 default: 309 break; 310 } 311 } 312 313 /// <summary> 314 /// 觸發更新CaseRunner狀態雙工回調 315 /// </summary> 316 /// <param name="caseRunnerList">CaseRunner 列表</param> 317 /// <param name="isAll">是否所有更新</param> 318 private void SendStateInfo(List<CaseRunner> caseRunnerList,bool isAll) 319 { 320 if (BaseHostState != CommunicationState.Opened) 321 { 322 return; 323 } 324 if (caseRunnerList.Count > 0) 325 { 326 RemoteRunnerInfo remoteRunnerInfo = GetRunnerInfo(caseRunnerList,isAll); 327 if (remoteRunnerInfo.RunnerStateList != null) 328 { 329 if (remoteRunnerInfo.RunnerStateList.Count > 0) 330 { 331 if (MessageTransferChannel.OnRunnerInfoCallback != null) 332 { 333 MessageTransferChannel.OnRunnerInfoCallback(remoteRunnerInfo); 334 } 335 } 336 } 337 } 338 } 339 340 /// <summary> 341 /// 獲取List<CaseRunner>中的更新信息 342 /// </summary> 343 /// <param name="runnerList">CaseRunner 列表</param> 344 /// <param name="isUpdataAll">T表示所有更新,F標識局部更新</param> 345 /// <returns>更新信息</returns> 346 private RemoteRunnerInfo GetRunnerInfo(List<CaseRunner> runnerList, bool isUpdataAll) 347 { 348 RemoteRunnerInfo remoteRunnerInfo = new RemoteRunnerInfo(); 349 if (runnerList == null) 350 { 351 return null; 352 } 353 foreach (CaseRunner tempRunner in runnerList) 354 { 355 if (tempRunner.IsNeedUpdata || isUpdataAll) 356 { 357 tempRunner.IsNeedUpdata = false; 358 359 RunnerState tempRunnerState = new RunnerState(); 360 if (tempRunner.RunerActuator.NowExecutionResultList != null) 361 { 362 if (tempRunner.RunerActuator.NowExecutionResultList.Count > 0) 363 { 364 myExecutionDeviceResult tempResult = tempRunner.RunerActuator.NowExecutionResultList[tempRunner.RunerActuator.NowExecutionResultList.Count - 1]; 365 tempRunnerState.RunnerID = runnerList.IndexOf(tempRunner); 366 tempRunnerState.RunnerName = tempRunner.RunnerName; 367 tempRunnerState.NowCell = tempResult.caseId.ToString(); 368 tempRunnerState.CellResult = tempResult.result.ToString(); 369 tempRunnerState.State = tempRunner.RunnerState.ToString(); 370 tempRunnerState.Time = tempResult.spanTime; 371 tempRunnerState.RunDetails = tempResult.backContent; 372 tempRunnerState.RunnerProgress = tempRunner.RunerActuator.RunProgress; 373 remoteRunnerInfo.AddRunnerState(tempRunnerState); 374 } 375 else if (isUpdataAll) 376 { 377 tempRunnerState.RunnerID = runnerList.IndexOf(tempRunner); 378 tempRunnerState.RunnerName = tempRunner.RunnerName; 379 tempRunnerState.NowCell = ""; 380 tempRunnerState.CellResult = ""; 381 tempRunnerState.State = tempRunner.RunnerState.ToString(); 382 tempRunnerState.Time = ""; 383 tempRunnerState.RunDetails = ""; 384 tempRunnerState.RunnerProgress = tempRunner.RunerActuator.RunProgress; 385 remoteRunnerInfo.AddRunnerState(tempRunnerState); 386 } 387 else 388 { 389 tempRunnerState.RunnerID = runnerList.IndexOf(tempRunner); 390 tempRunnerState.RunnerName = tempRunner.RunnerName; 391 tempRunnerState.State = tempRunner.RunnerState.ToString(); 392 tempRunnerState.RunnerProgress = tempRunner.RunerActuator.RunProgress; 393 remoteRunnerInfo.AddRunnerState(tempRunnerState); 394 } 395 } 396 } 397 } 398 return remoteRunnerInfo; 399 } 400 401 /// <summary> 402 /// 獲取List<CaseRunner>中的更新信息(局部更新) 403 /// </summary> 404 /// <param name="runnerList">CaseRunner 列表</param> 405 /// <returns>更新信息</returns> 406 private RemoteRunnerInfo GetRunnerInfo(List<CaseRunner> runnerList) 407 { 408 return GetRunnerInfo(runnerList, false); 409 } 410 411 }
最後放幾個動畫,簡單演示下Auto組件的部分功能
系統代碼打包下載 AutoTest_V5.3_CodeOnly.rar