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

目       录

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

10.1        配置文件设计… 3

10.2        加载设备驱动… 4

10.3        加载界面视图… 8

10.4        加载数据导出… 12

10.5        加载服务组件… 14

10.6        全局十二分监测… 17

10.7        小结… 19

 

第捌章     宿主程序详细规划

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

图片 1 

   
宿主程序接受来自人士的动作,通过布署文件完毕加载插件恐怕把曾经加载的插件与总控器举行互动。当然,宿主程序也说不定与其他赞助工作举行相互。

10.1     配置文件设计

      
加载插件的法子有众多样,可以经过遍历指定目录下的程序集,找到呼应的插件接口类型,并且加载到框架平台,以往有不胜枚举编辑软件皆以行使的这么格局。可是本身倍感那种方法有个别有个别暴力,不恐怕任哪个人来到你家门前就允许她进门的。所以,SuperIO框架平台运用一种更友善的不二法门,通过布置文件加载插件。把二回开发好的插件消息配置到对应的文本中,只有插件新闻“合法”的事态下才会基于气象加载插件到框架平长沙。

依据那样考虑,就要求对配置文件进行规划,以怎么样的文件格式保存消息,以及都封存什么样的音讯。

      配置文件的格式拔取XML格局,对.NET
Framework的System.Configuration.Configuration工具类举行1次封装。先定义1个接口,对操作配置文件举行科班,接口定义如下图:

图片 2 

    
配置文件保留什么样的音讯,取决于应用进度中所须要的音讯,差其他插件或许用到的安顿音讯不一样。那么先定义2个基础的布局音信,包涵:插件文件路径、实例类消息(命令空间和类名)、标题和标注等信息,以便可以通过反射工具类加载插件。配置音讯类定义如下图:

图片 3 

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

图片 4

10.2     加载设备驱动

    
设备驱动的安顿文件与功底配置文件不均等,紧要涉嫌到两有的:可挂载的配备驱动消息和早已挂载到框架平台的驱动新闻。

    
可挂载的装备驱动音讯在AssemblyDeviceSectionGroup配置组中展开布署,当挂载新的设施驱动的时候,如增添设备,就会从那几个配置组中加载新闻,以便操作人士开展抉择。配置组下的配备驱动配置音讯定义如下图:

图片 5 

    当调用增添设备窗体的时候会从布局文件读取程序集音讯,如下图:

图片 6 

    当触发增添设备事件的时候,会成立新的设施驱动,代码完毕如下:

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配置组中进行配置,也等于对已经挂载的配备驱动,在下次发轫框架平台的时候要自行把这几个设施驱动挂载到阳台下运转,当发生删除设备驱动事件时从该配置组中删除相关音讯。可以安装通信类型变更设备驱动的报道方式。配置组下的配制消息定义如下图:

图片 7 

  
当下次起动框架平台时会从那一个配置组加载并实例化设备驱动,挂载到框架平台下运作设备驱动实例,在宿主程序中浮现设备驱动实例实时运营情状。加载设备驱动代码如下:

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;
}

     设备驱动全部计划文件如下图:

图片 8

10.3     加载界面视图

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

   
考虑到开发费用和人工成本,SuperIO没有像组态软件的法门完结图形化显示,可是自定义图形化UI突显部分是必须贯彻的,满意差异用户、差距采纳场景的须求。

   
SuperIO是由此事件和接口的点子来促成自定义图形彰显。设备驱动对数码开展打包,打包后的数量或然是:字符串(数组)、类对象、字节数组等,通过调用事件(OnDeviceObjectChangedHandler)把包装后的数码以目的的款式传递给图形突显接口(IGraphicsShow),再反向解析数据消息体今后差距的UI组件上,支持四种展现风格。

    
那么,那样就资助一回开发者继承图形彰显接口(IGraphicsShow),独立开发一个零部件(DLL),并且挂载到陈设文件中,当鼠标单事菜单的图纸突显项时自动以插件的样式加载DLL,并以FormTab的格局浮现图形界面。

   
针对加载界面视图的整整进程涉及到:配制文件、加载视图菜单、单击事件显得视图等。

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

 图片 9

    
当框架平台运转时,会自行加载配置文件,并出示在界面视图的食谱中,加载配置文件的代码如下:

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;
       }
}

10.4     加载数据导出

   
一般情形下用不到数量导出插件接口功用,不过在做项目标时候的确会发出三种数额格式举办相互的场馆,例如:N个厂家要合并自个儿的多少音信,不过规定的数据格式又不均等,又迫于用户的威吓,不得不同盟工作。那么就足以用多少导出插件来成功。

    
有人会狐疑:那样的成效无法在设施驱动和出示视图中成功吗?当然可以那样操作。不过,小编不想把开发稳定的装备驱动和出示视图效率模块随意扩大代码,更不情愿为了本来不相干的事务恐怕影响到自然应该干好的事情,对于有“洁癖”的开发者来讲更不乐意那样干。本来设备驱动在框架平奥兰多就是可变的因子,然则对于数据导出又是相对稳定的一对,所以把多少导出功用重新解耦出来,单独设计成插件组件的主意。

   
那有的效用设计的相比简单,也是因而布署文件的方法挂载插件,每便运营框架平台都会把布署文件中的数据导出插件加载进来,直到框架平台退出。约等于说挂载相应的插件就有照应的导出数据作用,不加载插件就不会有其余操作。

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

 图片 10

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

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.5     加载服务组件

    
设备驱动的效率只承担与硬件设施开展互动,无法把事务性的劳动加到设备驱动中,否则恐怕会潜移默化多少不奇怪的互动;界面视图只负责对征集上来的多寡举行实时呈现,无法把事务性的劳动加到界面视图中,否则会影响人机交互的体验感;数据导出只负责对数据开展格式化并导出到相应的介质中,不可以把事务性的服务加到数据导出中,否则数据导出不负有作用界面的互动能力。

    
服务组件针对特出的事务性服务场合,请参见《7.外部接口的统筹》。服务组件的加载进程与界面视图的加载进程看似。

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

图片 11

   
当框架平台运营时,会活动加载配置文件,并突显在劳务的菜系中,加载配置文件的代码如下:

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.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.7     小结

   
至此,框架平台的雏形就已经到位了,一次开发设备驱动、数据展现、数据导出和劳务组件,举办零部件挂载,加载、运维的100%流程都可以走通了。

    可是,对于三次开发还相应有着调节成效,下一章节中牵线《第叁1章  
调试器设计》

 

笔者:唯笑志在

Email:504547114@qq.com

QQ:504547114

.NET开发技术联盟:54256083

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

法定网址:http://www.bmpj.net

相关文章