AutoTest是一个基于.NET平台实现的自动化/压力测试的系统,可独立运行于windows平台下,支持分布式部署,不需要其他配置或编译器的支持。(本质是一个基于协议的测试工具),前面还有一篇对其功能的简单介绍【AutoTest简介】
AutoTest用于发布的部分有2个部分,主程序【AutoTest.exe】及分布式部署程序【RemoteService.exe】(用于将将测试业务分布式部署到远程主机)
而在内部又被设计为多个组成部分,最终完成对自定义脚本文件的解析并按脚本要求的模式去执行。
如上图,简单介绍下
执行逻辑层主要由3部分组成
表示层暂时是由2部分组成
最下面的2个模块是用于系统内部模块的单元测试,本身与系统运行无关,所以就不赘述了
此外还有一个重要的组成部分-脚本文件,这里使用的脚本依托于XML文件,规则上基本是独立定义的。脚本文件控制所有Case的执行方式,内容,甚至是执行路径,还包括对断言的额外处理。脚本文件被解释器(CaseExecutiveActuator的一部分)转换为系统/工具直接可以使用的数据结构,并最终被执行。(脚本规则相对独立,将不会在此篇中涉及)
CaseExecutiveActuator模块主要由2部分组成,脚本处理部分(算法)及脚本内容的存储部分(数据)。
脚本对外的存在形式是XML文件,这个XML也是最终的脚本文件。系统解析这个XML文件(对CASE内容,或测试业务的修改包括新建也只是对XML脚本文件的修改而),然后对其进行进一步处理(当然使用合适的数据结构对其进行存储是第一步)。脚本的存储比较简单,先看这部分,其实就是实现了一个类似Tree的数据结构来存储整个脚本逻辑,然后每个单独的Case都存储在单独的Cell里(Cell为该自定义数据结构的基础单元,于单个Case对应)。
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