一个基于.NET平台的自动化/压力测试系统设计简述

AutoTest是一个基于.NET的自动化/压力测试系统,支持分布式部署,使用XML脚本文件定义测试逻辑。系统由执行逻辑层(包括MyControl.dll、myCommonTool.dll和CaseExecutiveActuator.dll)、表示层(AutoTest.exe和RemoteService.exe)以及脚本解析和执行模块组成。CaseExecutiveActuator是核心执行模块,负责脚本解析和执行,包括CaseCell数据结构用于存储和管理脚本逻辑。分布式服务RemoteService基于WCF实现,允许跨平台的远程测试任务部署。MyCommonTool模块提供通用工具服务,如日志和加密。整个系统设计强调模块化和灵活性,以适应不同平台和未来的扩展需求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

AutoTest系统设计概述#

AutoTest是一个基于.NET平台实现的自动化/压力测试的系统,可独立运行于windows平台下,支持分布式部署,不需要其他配置或编译器的支持。(本质是一个基于协议的测试工具),前面还有一篇对其功能的简单介绍【AutoTest简介

AutoTest用于发布的部分有2个部分,主程序【AutoTest.exe】及分布式部署程序【RemoteService.exe】(用于将将测试业务分布式部署到远程主机)

而在内部又被设计为多个组成部分,最终完成对自定义脚本文件的解析并按脚本要求的模式去执行。

如上图,简单介绍下

执行逻辑层主要由3部分组成

  • MyControl.dll                       该部分主要为表示层提供专门为业务定制的UI控件等用于显示数据的组件
  • myCommonTool.dll                  该部分为这个解决方案即这个系统提供通用的工具方法及组件如日志系统(指系统本身的日子系统,测试过程中的日志或记录由专门的记录采集模块完成)
  • CaseExecutiveActuator.dll       该部分为这个测试平台提供最核心的逻辑处理部分,如脚本的解析,脚本的选择,脚本的执行等(对于系统来说添加任意的其他的协议支持也仅需要修改这个库中的部分内容)

表示层暂时是由2部分组成

  • AutoTest.exe                            测试平台显示界面,运行于windows环境下,赋值数据及结果的呈现,同时负责与操作者者进行交换
  • RemoteService.exe                  分布式部署程序,负责在远程主机开启服务实现测试任务的分布式部署,分布式远程服务基于WCF框架理论上支持跨internet的分布式部署

最下面的2个模块是用于系统内部模块的单元测试,本身与系统运行无关,所以就不赘述了

此外还有一个重要的组成部分-脚本文件,这里使用的脚本依托于XML文件,规则上基本是独立定义的。脚本文件控制所有Case的执行方式,内容,甚至是执行路径,还包括对断言的额外处理。脚本文件被解释器(CaseExecutiveActuator的一部分)转换为系统/工具直接可以使用的数据结构,并最终被执行。(脚本规则相对独立,将不会在此篇中涉及)

核心执行模块【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

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部分

  • myCaseRunTime                                  负责指引Case路径,Cell的寻找,跳转及序列的指示 都是由它完成的
  • CaseActionActuator                             负责Case的具体执行,包括对各种协议的执行,及对结果的分析及储存,断言的处理,附加动作如语音提示,重试等执行相关的部分就由它处理

先来看比较简单的CaseRunTime   

实际上于CaseRunTime  紧密相关的还有另外2个组件myCaseLoop,myCsaeQueue(他们实际上也仅被CaseRunTime 使用)

通过名字其实大概可以猜到他们的功能

  • myCaseLoop                                 控制一组Case(可以是由多个Case组成的业务)的循环执行,当然支持循环里无限嵌套其他的循环
  • myCsaeQueue                          控制一个逻辑项目的Case坐标(当前执行的Case),基础的Case会放在一个个逻辑项目中,每个逻辑下项目可以标识一类业务的集合,而当前执行Case是可以在这些逻辑项目中来回跳转的,所以它负责多个逻辑项目的寻址
  • myCaseRunTime                           借助前面的CaseLoop 和  CsaeQueue,完成整个Case的移动轨迹(这个轨迹可能因执行结果不同而有不同的变化,取决于脚本文件如何写)
  • RunCaseCount                              一个辅助类,帮助统计脚本中实际将要执行的Case数量(会考虑起始点及循环等因素)                      

单独看看myCaseRunTime

如上图可以看到myCaseRunTime实际上是包含了一个myCsaeQueue列表的,不过逻辑的核心是nextCase。有兴趣的可以看看下面的实现(贴出的只包含关键部分)

View Code

而最终myCaseRunTime也是为CaseActionActuator 服务的,现在来看下CaseActionActuator。

CaseActionActuator相对比较多一点,因为要完成的功能会多一些,跟其他模块的联系也会大一些

这个可能看起来就很乱了,上图的模块主要就是一个Case文件的在系统中的表现,可以理解为一个User,这个User通过Case脚本文件可以执行一套业务,执行过程也是独立的,环境,线程,数据也都是独立的。所以可以创建任意多个这种模块以模拟大量的用户同时操作,当然脚本可以使用不同的脚本文件,也可以使用相同脚本文件(若使用相同脚本文件系统会对当前模块进行深度克隆,克隆的用户共享部分不会影响运行的数据)。该模块还可以选择以Cell对UI控件进行绑定,以达到执行过程中用户界面的友好反馈,当然不同的UI控件的动态效果需要单独的处理(处理由另一个辅助模块myActionActuator完成)

这个模块的图起来乱点,不过code相对清晰,有兴趣可以看下面代码(贴出的是关键部分)

View Code

同时CaseActionActuator还包含一个比较重要的部分myCaseProtocolEngine,该部分是协议引擎,实际上只要PC上可以使用的协议通过简单的修改都可以加入到系统

对于协议引擎必须继承规定好的接口实现,然后就能将协议加入系统。

这个模块还要许多其他部分比如Case文件的解析,及其他可以自定义的内容这里就不继续讲下去了,有兴趣的可以下载完整代码。

辅助工具模块MyCommonTool#

MyCommonTool其实包含的东西也比较多不过功能都相对独立,见下图

可以看到这个模块完全是由一个个独立的类组成,相互之间的联系非常少。有很多子模块提供不同的服务通过命名也大概可以看出来VoiceService提供语音服务,myWebTool提供web服务,myEncryption提供加密服务等等,它被系统的很多模块使用。由于类比较多而又没有什么逻辑上的联系,这里同样不继续讲下去了。需要了解的可以下载完整的代码

辅助UI显示模块MyControl#

这个部分主要与UI的形式与呈现有关,当然包括了很多定制的数据的banding,在AutoTest及RemoteService都会用到里面的东西。不过针对不同的界面框架这部分是不能重用的。所以也不用怎么看,稍微贴张图吧。

主程序界面AutoTest#

对于AutoTest本身已经没有太多执行相关的逻辑处理了(都由CaseExecutiveActuator来处理),其主要处理case的呈现,及执行过程中的动态修改,还包括报告的格式化输出,同时还要2个重要功能组织多个User(执行体)跟连接远程测试主机。

先稍微过下界面相关的内容,如下图

                               

  • AutoTest                                                              这当然是主体
  • AutoTest‎.myDialogWindow                                  这里包含UI系统下的除主体外的其他UI窗口(右边的图展开了该项,可以看下)
  • AutoTest‎.myControl                                             同样是个UI控件的定制集合,不过这里的控件仅在AutoTest适合使用
  • AutoTest‎.myTool                                                  工具类,为AutoTest 的行为提供便利
  • AutoTest‎.RemoteServiceReference                     分布式远程访问模块,这个模块是引用WCF自动生成的,用于连接远程主机(这里WCF使用的自承载方式,程序运行时并不需要去配置什么其他的内容)

现在来看刚刚提到的一个重要的功能组织多个User,AutoTest可以创建任意多个虚拟执行体User,他们存储在CaseRunner中,同时CaseRunner还提供一些对User的基本的控制功能,如下图

而这个部分其实放在逻辑层更加合适,不过之前设计经验不足。把CaseRunner于UI控件十分深的banding在一起了。绑定效果如下图

这个就是大量CaseRunner绑定的效果(这个不是主界面,整体的显示效果可以看下

当然还有前面提到的另外一个重要功能连接远程测试主机,主要负责连接分布式部署在其他PC上的执行模块(就是后面要讲的RemoteService),同时可以控制及配置远程主机上的User(还包括连接的维持)。连接功能由RemoteClient组件来完成,它在前面提到的AutoTest‎.myTool 下,简单看下它的实现

基本结构如下图

这个其实实现起来比较简单,因为借助WCF框架,很多工作框架已经做好了,不过一些通道的选择配置以及是否适合双工通信可能不是最优的,后面还需要调整,可以看下下面的code

View Code

分布式部署服务模块RemoteService#

这个部分与AutoTest整体结构是相似的,在执行逻辑上也是同样依靠逻辑层的3个主要模块。而内部基于WCF单独实现了服务部分,服务内容主要包括对状态及业务返回数据进行过滤重组然后上报给连接的控制机,还有接收及处理控制机的控制或配置等命令请求。

下面看一下自身的结构,如下图

可以看到结构上与AutoTest确实是大体一致的,主要看下跟分布式部署相关的2个组件

  • ExecuteService             直接提供服务说需要的部分(是WCF的必要部分)
  • ServerHost                    对服务连接的抽象再封装,所有对服务的操作,包括获取服务数据都通过这个组件,下层应用不直接使用ExecuteService服务(从结构关系图中也可以看出来ExecuteService只被ServerHost调用)

 其他部分与远程服务没有太大关系的就不介绍了

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组件的部分功能

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值