[连载]《C#通信(串口和互连网)框架的部署与贯彻》- 10.宿主先后详细规划

10.5        加载服务组件… 14

10.5     加载服务组件

    
设备驱动的职能只负责与硬件装置开展相互,无法把事务性的劳动加到设备驱动中,否则恐怕会影响多少正常的并行;界面视图只负责对采集上来的数量进行实时突显,不可以把事务性的服务加到界面视图中,否则会影响人机交互的体验感;数据导出只担负对数据开展格式化并导出到相应的介质中,不大概把事务性的服务加到数据导出中,否则数据导出不持有成效界面的并行能力。

    
服务组件针对优秀的事务性服务场地,请参见《7.外部接口的宏图》。服务组件的加载进程与界面视图的加载进程看似。

配置文件与基础配置文件一律,配置文件定义如下图:

图片 1

   
当框架平台启动时,会自行加载配置文件,并出示在劳务的食谱中,加载配置文件的代码如下:

private void LoadServices()
{
       IConfigurationSource source = new SuperIO.ServicesConfiguration.ServicesConfigurationSource();
       source.Load();
       List<IAppService> serviceList = new List<IAppService>();
       for (int i = 0; i < source.Configuration.SectionGroups.Count; i++)
       {
              if (source.Configuration.SectionGroups[i].GetType() == typeof(SuperIO.ServicesConfiguration.ServicesSectionGroup))
              {
                     IObjectBuilder builder = new TypeCreator();
                     SuperIO.ServicesConfiguration.ServicesSectionGroup group = (SuperIO.ServicesConfiguration.ServicesSectionGroup)source.Configuration.SectionGroups[i];
                     Font font = new Font("Tahoma", 12);
                     SuperIO.ServicesConfiguration.ServicesSection section=null;
                     for (int j = 0; j < group.Sections.Count; j++)
                     {
                            section = (SuperIO.ServicesConfiguration.ServicesSection)group.Sections[j];
                            IAppService appService = builder.BuildUp<IAppService>(Application.StartupPath + "\\SuperIO\\ServicesConfig\\" + section.Name, section.Instance);
                            appService.ServiceType = section.ServiceType;
                            appService.IsAutoStart = section.IsAutoStart;
                            serviceList.Add (appService);
                            if (section.ServiceType == ServiceType.Show)
                            {
                                   BarButtonItem bt = new BarButtonItem(this.barManager1, section.Caption);
                                   bt.ItemAppearance.SetFont(font);
                                   bt.Tag = appService.ThisKey;
                                   bt.ItemClick += new ItemClickEventHandler(ServiceItem_ItemClick);
                                   barServices.AddItem(bt);
                            }
                     }
                     break;
              }
       }
       _DeviceController.AddAppService(serviceList);
}

    
_DeviceController.AddAppService(serviceList)代码会把劳动插件的实例伸张到控制器中,代码如下:

public void AddAppService(List<IAppService> serviceList)
{
       foreach (IAppService service in serviceList)
       {
              if (!_appServiceManager.Contain(service.ThisKey))
              {
                     service.WriteLogHandler += new WriteLogHandler(Service_WriteLogHandler);
                     if (service.IsAutoStart)
                     {
                            service.StartService();
                     }
                     _appServiceManager.Add(service.ThisKey, service);
                     DeviceMonitorLog.WriteLog(String.Format("<{0}>应用服务已经打开", service.ThisName));

              }
              else
              {
                     DeviceMonitorLog.WriteLog(String.Format("<{0}>应用服务已经存在", service.ThisName));
              }
       }
}

  
当单击菜单服务项时会调用ServiceItem_ItemClick函数,会调用服务组件的单击事件函数,代码如下:

public void OnClickAppService(string key)
{
       IAppService service = _appServiceManager.Get(key);
       if (service != null)
       {
           service.OnClick();
        }
}

10.7     小结

   
至此,框架平台的雏形就曾经形成了,二次开发设备驱动、数据呈现、数据导出和劳务组件,举办零部件挂载,加载、运行的全体流程都得以走通了。

    可是,对于二次开发还应该有所调节功用,下一章节中牵线《第11章  
调试器设计》

 

作者:唯笑志在

Email:504547114@qq.com

QQ:504547114

.NET开发技术联盟:54256083

文档下载:http://pan.baidu.com/s/1pJ7lZWf

官方网址:http://www.bmpj.net

10.7        小结… 19

10.4        加载数据导出… 12

目       录

10.6     全局至极监测

    
框架平台的安定团结始终是重中之重,在运转进程中大概爆发任何未知十分新闻,针对这一个尤其是不只怕用曾经存在的try…catch…来捕捉的。

    
在开行框架平台的时候扩张了ThreadException和UnhandledException事件对未知非凡举办捕捉,并把事件中对丰硕消息举行详尽的笔录。

     ThreadException事件,此事件允许 Windows 窗体应用程序处理 Windows
窗体线程中所暴发的其他未经处理的不得了。 请将事件处理程序附加到
ThreadException
事件以拍卖那些极度,因为这个尤其将使您的应用程序处于未知状态。
应尽量使用结构化非凡处理块来处理非常。详情请参见MSDN。

     UnhandledException事件,此事件提供未捕获到的那么些的打招呼。
此事件使应用程序能在系统暗中同意处理程序向用户告知极度并终止应用程序从前记录有关相当的新闻。
如若有丰硕的有关应用程序状态的消息,则足以运用其他艺术,如保存程序数据以有益未来进行还原。
提出谨慎行事,因为未经处理的尤其时或者会损坏程序数据。详情请参见MSDN。

     应用的代码如下:

public class MonitorException
{
       [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlAppDomain)]
       public static void Monitor()
       {
              Application.ThreadException += new ThreadExceptionEventHandler(MainThreadException);
              AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
       }

       public static void UnMonitor()
       {
              Application.ThreadException -= new ThreadExceptionEventHandler(MainThreadException);
              AppDomain.CurrentDomain.UnhandledException -= new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

       }

       private static void MainThreadException(object sender, ThreadExceptionEventArgs e)
       {
              try
              {
                     ShowException(e.Exception);
              }
              catch(Exception ex)
              {
                     GeneralLog.WriteLog(ex);
              }
       }

       private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)

       {
              ShowException((Exception)e.ExceptionObject);
       }

       private static void ShowException(Exception ex)
       {
              GeneralLog.WriteLog(ex);
       }
}

 
  因为那是一个静态事件,所以释放应用程序时必须分离事件处理程序,否则会促成内存泄漏。

10.1        配置文件设计… 3

10.6        全局极度监测… 17

10.4     加载数据导出

   
一般景色下用不到数量导出插件接口功效,不过在做项目标时候的确会爆发各种多少格式进行互动的光景,例如:N个厂家要合并本身的数额音信,不过规定的多少格式又不一样等,又迫于用户的威慑,不得不合营工作。那么就足以用数码导出插件来完结。

    
有人会猜忌:那样的机能否够在装备驱动和出示视图中做到吗?当然可以这样操作。但是,我不想把开发稳定的设备驱动和出示视图功用模块随意增添代码,更不乐意为了本来不相干的事情或者影响到自然应该干好的政工,对于有“洁癖”的开发者来讲更不甘于那样干。本来设备驱动在框架平纽伦堡就是可变的因子,然而对于数据导出又是相对安静的部分,所以把数量导出功用重新解耦出来,单独设计成插件组件的法门。

   
那有些效能设计的比较不难,也是透过安顿文件的办法挂载插件,每回启动框架平台都会把布署文件中的数据导出插件加载进来,直到框架平台退出。也就是说挂载相应的插件就有相应的导出数据功能,不加载插件就不会有任何操作。

    配置文件与基础配置文件一律,配置文件定义如下图:

 图片 2

    加载插件的代码定义如下:

public static List<IExportData> GetExportInstances()
{
       List<IExportData> exportlist = new List<IExportData>();
       ExportConfigurationSource source = new ExportConfigurationSource();
       source.Load();
       ExportSectionGroup group = (ExportSectionGroup)source.Configuration.GetSectionGroup("Export");
       if (group == null)
       {
              throw new NullReferenceException("获得导出程序集信息为空");
       }

       IObjectBuilder builder = new TypeCreator();
       foreach (ExportSection section in group.Sections)
       {
              IExportData export = builder.BuildUp<IExportData>(Application.StartupPath+ "\\SuperIO\\ExportConfig\\" + section.Name, section.Instance);
              exportlist.Add(export);
       }
       return exportlist;
}

10.2        加载设备驱动… 4

第十章           宿主程序详细设计… 2

10.1     配置文件设计

      
加载插件的点子有众多种,可以透过遍历指定目录下的程序集,找到相应的插件接口类型,并且加载到框架平台,以往有很多编制软件都以选择的那样方式。可是本人感觉到那种方法有点有些暴力,不可能任何人来到你家门前就同意他进门的。所以,SuperIO框架平台应用一种更和谐的方法,通过配备文件加载插件。把二次开发好的插件音讯配置到相应的文本中,唯有插件音信“合法”的情事下才会按照事态加载插件到框架平奥兰多。

依照那样考虑,就须要对配置文件进行统筹,以什么的文件格式保存音讯,以及都保留什么样的信息。

      配置文件的格式拔取XML方式,对.NET
Framework的System.Configuration.Configuration工具类举行二次封装。先定义一个接口,对操作配置文件举办标准,接口定义如下图:

图片 3 

    
配置文件保留什么样的音讯,取决于应用进度中所须要的音讯,不一致的插件大概用到的布局消息不雷同。那么先定义一个基础的布署新闻,蕴含:插件文件路径、实例类音讯(命令空间和类名)、题目和标注等音信,以便可以经过反射工具类加载插件。配置消息类定义如下图:

图片 4 

    使用安顿文件操作基类生成的文件格式如下图:

图片 5

10.3        加载界面视图… 8

10.2     加载设备驱动

    
设备驱动的配备文件与基础配置文件不雷同,主要涉嫌到两局地:可挂载的设施驱动音讯和已经挂载到框架平台的驱动新闻。

    
可挂载的设施驱动消息在AssemblyDeviceSectionGroup配置组中举行布署,当挂载新的设备驱动的时候,如增添设备,就会从那么些配置组中加载音讯,以便操作人士展开分选。配置组下的装备驱动配置消息定义如下图:

图片 6 

    当调用扩充设备窗体的时候会从布局文件读取程序集新闻,如下图:

图片 7 

    当触发扩张设备事件的时候,会创建新的装置驱动,代码完结如下:

public static IRunDevice CreateDeviceInstance(int devid, int devaddr, string devname, int assemblyid, string assemblyname, string instance, CommunicationType type, DeviceType devType, object iopara1, object iopara2)
{
       IObjectBuilder builder = new TypeCreator();
       IRunDevice dev = builder.BuildUp<IRunDevice>(Application.StartupPath + "\\SuperIO\\DeviceConfig\\" + assemblyname, instance);
       dev.DeviceParameter.DeviceAddr = devaddr;
       dev.DeviceParameter.DeviceName = devname;
       dev.DeviceRealTimeData.DeviceName = devname;
       if (type == CommunicationType.COM)
       {
              dev.DeviceParameter.COM.Port = (int)iopara1;
              dev.DeviceParameter.COM.Baud = (int)iopara2;
       }
       else if (type == CommunicationType.NET)
       {
              dev.DeviceParameter.NET.RemoteIP = (string)iopara1;
              dev.DeviceParameter.NET.RemotePort = (int)iopara2;
       }
       dev.IsRegLicense = true;
       dev.CommunicationType = type;
       dev.UserLevel = UserLevel.High;
       dev.InitDevice(devid);
       if (!Device.DebugDevice.IsDebug)
       {
              //--------------------把设备信息配制到文件中------------------------//
              CurrentDeviceSection section = new CurrentDeviceSection();
              section.DeviceID = dev.DeviceParameter.DeviceID;
              section.AssemblyID = assemblyid;
              section.X = 0;
              section.Y = 0;
              section.Note = String.Empty;
              section.CommunicateType = dev.CommunicationType;
              DeviceAssembly.AddDeviceToXml(section);
              //---------------------------------------------------------------//
       }
       return dev;
}

     
已经挂载到框架平台的驱动音讯在CurrentDeviceSectionGroup配置组中举行布置,约等于对已经挂载的设施驱动,在下次启动框架平台的时候要活动把这个设施驱动挂载到阳台下运作,当暴发删除设备驱动事件时从该配置组中去除相关音信。可以设置通信类型变更设备驱动的报纸发布方式。配置组下的配制音信定义如下图:

图片 8 

  
当下次启动框架平台时会从这几个配置组加载并实例化设备驱动,挂载到框架平台下运行设备驱动实例,在宿主程序中显得设备驱动实例实时运行状态。加载设备驱动代码如下:

public static List<IRunDevice> LoadDevicesFromXml()
{
       List<IRunDevice> list = new List<IRunDevice>();
       CurrentDeviceSectionGroup curgroup = (CurrentDeviceSectionGroup)_Source.Configuration.GetSectionGroup("CurrentDeviceSectionGroup");
       if (curgroup == null)
       {
              throw new NullReferenceException("获得当前设备配置信息为空");
       }
       AssemblyDeviceSectionGroup asmgroup = (AssemblyDeviceSectionGroup)_Source.Configuration.GetSectionGroup("AssemblyDeviceSectionGroup");
       if (asmgroup == null)
       {
              throw new NullReferenceException("获得设备程序集信息为空");
       }
       IObjectBuilder creator = new TypeCreator();
       for (int i = 0; i < curgroup.Sections.Count; i++)
       {
              CurrentDeviceSection cursect = (CurrentDeviceSection)curgroup.Sections[i];
              if (cursect.AssemblyID >= 0)
              {
                     for (int j = 0; j < asmgroup.Sections.Count; j++)
                     {
                            AssemblyDeviceSection asmsect = (AssemblyDeviceSection)asmgroup.Sections[j];
                            if (cursect.AssemblyID == asmsect.AssemblyID)
                            {
                                   string assemblypath = Application.StartupPath + "\\SuperIO\\DeviceConfig\\" + asmsect.AssemblyName;
                                   IRunDevice dev = creator.BuildUp<IRunDevice>(assemblypath, asmsect.Instance);
                                   dev.InitDevice(cursect.DeviceID);
                                   dev.CommunicationType = cursect.CommunicateType;
                                   list.Add(dev);
                                   break;
                            }
                     }
              }
       }
       return list;
}

     设备驱动全部布署文件如下图:

图片 9

 

10.3     加载界面视图

   
组态软件会有一个图片和UI引擎来支撑图形数字化突显,允许二次开发者通过拖拽UI组件举行图形化设计,并设置UI组件的性子与IO变量进行关联来体现数据新闻。

   
考虑到开发开销和人工开支,SuperIO没有像组态软件的措施已毕图形化展现,可是自定义图形化UI显示部分是必须兑现的,满意不一样用户、不一致应用场景的急需。

   
SuperIO是因而事件和接口的主意来兑现自定义图形突显。设备驱动对数据开展打包,打包后的数目可能是:字符串(数组)、类对象、字节数组等,通过调用事件(OnDeviceObjectChangedHandler)把包裹后的数额以目的的样式传递给图形呈现接口(IGraphicsShow),再反向解析数据消息突显在区其余UI组件上,援救各类显示风格。

    
那么,那样就帮衬二次开发者继承图形彰显接口(IGraphicsShow),独立开发一个零件(DLL),并且挂载到布署文件中,当鼠标单事菜单的图纸彰显项时自动以插件的情势加载DLL,并以FormTab的花样显得图形界面。

   
针对加载界面视图的满贯经过涉及到:配制文件、加载视图菜单、单击事件显得视图等。

    配置文件与功底配置文件一律,配置文件定义如下图:

 图片 10

    
当框架平台启动时,会自行加载配置文件,并突显在界面视图的食谱中,加载配置文件的代码如下:

private void LoadShowView()
{
       IConfigurationSource source = new SuperIO.ShowConfiguration.ShowConfigurationSource();
       source.Load();
       for (int i = 0; i < source.Configuration.SectionGroups.Count; i++)
       {
              if (source.Configuration.SectionGroups[i].GetType() == typeof(SuperIO.ShowConfiguration.ShowSectionGroup))
              {
                     SuperIO.ShowConfiguration.ShowSectionGroup group = (SuperIO.ShowConfiguration.ShowSectionGroup)source.Configuration.SectionGroups[i]
                     Font font = new Font("Tahoma", 12);
                     SuperIO.ShowConfiguration.ShowSection section=null;
                     for (int j = 0; j < group.Sections.Count; j++)
                     {
                            section = (SuperIO.ShowConfiguration.ShowSection)group.Sections[j];
                            BarButtonItem bt = new BarButtonItem(this.barManager1, section.Caption);
                            bt.ItemAppearance.SetFont(font);
                            bt.Tag = section.Name + "," + section.Instance + "," + section.Caption;
                            bt.ItemClick += new ItemClickEventHandler(ViewItem_ItemClick);
                            barGraphicsView.AddItem(bt);
                     }
                     break;
              }
       }
}

    
当鼠标单击菜单项时会触发ViewItem_ItemClick函数,并加载、展现视图界面,定义的代码如下:

private void ViewItem_ItemClick(object sender, ItemClickEventArgs e)
{
       try
       {
              string[] arr = e.Item.Tag.ToString().Split(',');
              SuperIO.ShowConfiguration.ShowConfigurationSource source = new SuperIO.ShowConfiguration.ShowConfigurationSource();
              IObjectBuilder builder = new TypeCreator();
              Form form = builder.BuildUp<Form>(Application.StartupPath + "\\SuperIO\\ShowConfig\\" + arr[0], arr[1]);
              if (this._DeviceController.AddGraphicsShow((IGraphicsShow)form))
              {
                     form.Text = arr[2].ToString();
                     form.MdiParent = this;
                     form.Show();
              }
              else
              {
                     form.Dispose();
              }
       }
       catch (System.Exception ex)
       {
              MessageBox.Show(ex.Message);
       }
}

  
在这么些进度中,有一个标题考虑到,就是屡屡单击同一个菜系视图项时,无法反复呈现同一个界面视图窗体,主要考虑到规划的创设和实施的效能。_DeviceController.AddGraphicsShow((IGraphicsShow)form)函数的代码定义如下:

public bool AddGraphicsShow(IGraphicsShow graphicsShow)
{
       if (!_dataShowController.Contain(graphicsShow.ThisKey))
       {
              _dataShowController.Add(graphicsShow.ThisKey, graphicsShow);
              graphicsShow.MouseRightContextMenuHandler += new MouseRightContextMenuHandler(RunContainer_MouseRightContextMenuHandler);
              graphicsShow.GraphicsShowClosedHandler+=new GraphicsShowClosedHandler (GraphicsShow_GraphicsShowClosedHandler);
              DeviceMonitorLog.WriteLog(String.Format("<{0}>显示视图已经打开", graphicsShow.ThisName));
              return true;
       }
       else
       {
              DeviceMonitorLog.WriteLog(String.Format("<{0}>显示视图已经存在", graphicsShow.ThisName));
              return false;
       }
}

第十章     宿主程序详细规划

    
前几章对配备驱动、IO实例、外部接口和完整控制器等举行了详细介绍,那一个都以框架平台的有机组成部分,这部分约等于后台服务的襄助组件,通过模块化落成框架平台的搭建;宿主程序也是框架平台的一局部,作为承载插件的一个软件平台,是人机交互的绝无仅有接口,通过鼠标点击落成各类指令,是插件式框架平台最后要兑现的有些。在《第2章
框架总体的规划》的“2.1
宿主程序设计”中对宿主程序的全部效应和界面举行了安插和设计,但是并从未涉及到细节层面,要兑现这几个布署的法力,包罗3上面工作:界面的落到实处,也等于UI布局,涉及到少量的代码控制;与插件(设备驱动、图形展现、数据导出和劳动组件)举办交互,把须求的插件加载到宿主程序中,最终传递给后台服务;与《第8章
总体控制器的宏图》中的IDeviceController总控制器接口举办交互,可以知晓为与后台服务的支持组件进行互动,接收宿主程序的输入,一般为插件音讯、操作响应等。交互的结构示意图如下:

图片 11 

   
宿主程序接受来自人士的动作,通过安顿文件完毕加载插件可能把已经加载的插件与总控器举行交互。当然,宿主程序也恐怕与其他赞助工作举行互动。

admin

网站地图xml地图