最近在弄一个公司的设备管理框架,发现可以把设备抽象出来,对其他可能的设备也可能用这一套框架。出于保密原则,删除了公司业务相关的信息,就列出来如下。
DevTool模块
1 程序描述
DevManager简称DM,ViewManager简称VM。程序启动后,DevManager内注册对Controller的事件处理回调eventCallbackForDM,以便Dev事件到来时回调;ViewManager内注册对Controller的事件处理回调eventCallbackForVM,以便UI事件到来时回调。
eventCallbackForDM内调用dispatchRequestFromCfg解析是否有匹配的事件对应的需求需要处理,如果有而且需要界面,则调用getDataFromDMtoVMForView从DevManager中获取对应界面需要的相应数据,并调用ViewManager的回调requestCallback处理对应的UI请求,最终弹出UI。UI处理完之后,调用eventCallbackForVM,通知Controller对应UI的完成事件,并将UI的返回数据保存在Controller内,然后Controller内调用getDataFromVMtoDMForDev组织数据,调用DevManager的requestCallback请求,最终调用DevApi更新Dev内数据。如果需要提示操作结果,还需要经历一次DM-Controller->VM->Controller的操作。
上述描述是针对Dev事件处理的,也有来源于用户操作的处理。如果是类似修改名称的操作,处理顺序为VM->Controller->DM->Controller->VM-> Controller。
1) VM先启动主页面,响应用户按钮请求,修改名称是在VM内弹框;
2) 填完Dev名称,合法验证通过后,Controller获取数据形成Dev的Request;
3) DM内调用封装好的DevApi实现修改名称,成功之后,返回给Controller;
4) Controller发送UI的Request,说明修改名称成功;
5) VM弹出修改成功提示,用户点击成功;
6) VM通知Controller确认成功。
在5)用户点击成功之前,如果此时用户断开Dev,而且用户需求中要求断开Dev之后关闭相关的对话框,将从DM->Controller->VM完成一次关闭提示框的操作。
在DM和VM之间加一层Controller的好处是所有需要配置定制的部分全部在此部分完成,其他实现可以以库的形式提供。
抽象一层Controller,还有一个好处,是不用在VM内缓存任何数据。比如想实现一个下拉框选择Dev,仅仅有连接/断开事件时传Dev指针到VM内是不够的,需要VM自己维护已有的Dev列表。
UML静态图如下:
2 功能
DevManager、ViewManager 分别实现设备管理、视图管理。主程序内实现控制器的功能,连接各个模块,实现消息的传递、客户定制需求的处理。
2.1 设备管理
连接Dev则新建Dev对象存入devMap,所有针对Dev的数据操作都由DevManager进行。除了与Dev通信,还要负责回复Controller的请求数据、调用Controller的回调,通知Controller事件来了。
2.2 视图管理
响应Conroller的请求弹出UI,当UI操作结束时,调用Controller的回调返回UI操作的结果。
2.3 控制器
Conroller提供回调函数给DevManager、ViewManager,以响应事件,并实现对配置的解析,针对当时的事件,生成相应的请求,提交给DevManager和ViewManager。
2.4 性能
2.5 输入项
2.6 输出项
无。
2.7 算法
无。
2.8 流程逻辑
无。
2.9 接口
数据传输统一采用XML格式,如果某些元素是二进制流数据,且不是无符号整型,也先转成十六进制字符串,然后再传输。
2.9.1 ViewType定义格式
支持的视图类型:View、ListView、TabView、ImageView、Button、ComboBox、TextView、TableView、TrayView、BubbleView、AnimateView。
支持的视图内容布局类型:contentLayout(left/right/center/top/down) marginWidth/marginHeight
支持的布局类型:linearLayout(vertical/horizontal)
支持的宽高类型:height/width(30%/30) %方式定义与父窗口的相对位置,纯数字定义与MFC的rc文件中定义单位相同
例如定义一个竖型的管理工具mainView
<?xml version="1.0" encoding="utf-8"?> <view> <viewType>mainView</viewType> <width>500</width> <height>400</height> <IsCenterToDeskTop>true<isCenterToDeskTop> <linearLayout orientation = "vertical"> <linearName>outline</linearName> <marginWidth>2%</marginWidth> <width>96%</width> <height>96%</height> <!--headline--> <linearLayout orientation = "horizontal"> <linearName>headline</linearName> <marginWidth>2%</marginWidth> <width>96%</width> <height>5%</height> <backgroundColor>#0001</backgroundColor> <imageView> <imageName>logoIco</imageName> <imageUrl>$(res)/logoIco.png</imageUrl> <contentLayout>center</contentLayout> <marginWidth>2%</marginWidth> <width>5%</width> <height>96%</height> </imageView> <textView> <marginWidth>2%</marginWidth> <width>70%</width> <height>96%</height> <textValue>$(CustomerName)$(DevName)$(ToolName)</textValue> </textView> <button> <id>btn_min</id> <action>onMin</action> <marginWidth>2%</marginWidth> <width>5%</width> <height>96%</height> <imageUrl>$(res)/btn_min.png</imageUrl> </button> <button> <id>btn_max</id> <action>onMax</action> <marginWidth>2%</marginWidth> <width>5%</width> <height>96%</height> <imageUrl>$(res)/btn_max.png</imageUrl> <isEnable>false</isEnable> </button> <button> <id>btn_close</id> <action>onClose</action> <marginWidth>2%</marginWidth> <width>5%</width> <height>96%</height> <imageUrl>$(res)/btn_close.png</imageUrl> </button> </linearLayout> <!--logo long--> <imageView> <imageName>logoLong</imageName> <imageUrl>$(res)/logoLong.png</imageUrl> <contentLayout>center</contentLayout> <marginWidth>2%</marginWidth> <width>96%</width> <height>30%</height> </imageView> <textView> <marginWidth>2%</marginWidth> <width>70%</width> <height>50%</height> <textValue>$(CustomerName)$(DevName)$(ToolName)</textValue> </textView> <linearLayout orientation = "horizontal"> <linearName>headline</linearName> <marginWidth>2%</marginWidth> <width>96%</width> <height>56%</height> <!--logo--> <listView> <id>listView_mainView</id> </listView> <!--btnlist--> <linearLayout orientation = "vertical"> <linearName>btnList</linearName> <marginWidth>2%</marginWidth> <width>36%</width> <height>56%</height> <button> <id>btn_change_name</id> <action>onChangeName</action> <marginWidth>2%</marginWidth> <width>96%</width> <height>10%</height> <isEnable>false</isEnable> </button> <button> <id>btn_view_devinfo</id> <action>onView devinfo </action> <marginWidth>2%</marginWidth> <width>96%</width> <height>10%</height> <isEnable>false</isEnable> </button> <button> <id>btn_init</id> <action>onInit</action> <marginWidth>2%</marginWidth> <width>96%</width> <height>10%</height> <isEnable>false</isEnable> </button> </linearLayout> </linearLayout> </linearLayout> </view>
2.9.2 DM->Controller交互事件数据格式
<?xml version='1.0' encoding='utf-8'?> <event> <eventType>connect</eventType> <devIndex>3</devIndex> </event>
eventType可选择connect/disconnect/format/datachange
在Controller的eventCallbackForDM中调用。
然后Controller调用getDatatFromDMtoVMForView,组织下一小节中描述的Controller->VM交互请求数据。
mapToUIId标签实现从数据到UI填充的映射。
2.9.3 Controller->VM交互请求数据格式
<?xml version='1.0' encoding='utf-8'?> <request> <requestType>createUI</requestType> <view> <viewType>mainView</viewType> <viewId>view_1</viewId> <viewParentId>desktop</viewParentId> <devList> <!—此处的id与viewType定义中的控件对应的id相同,表示填充到对应的id里--> <id>devList</id> <mapToUIId>listView_mainView</mapToUIId> <selectedId>dev_1</selectedId> <dev> <id>dev_1</id> <name>DevStd</name> <sn>DS20150415</sn> <imageUrl>$(res)/dev.png</imageUrl> <otherParamList> <param1> <id>param_1</id> <imageUrl>$(res)/param1.png</imageUrl> </param1> <param2> <id>param_2</id> <imageUrl>$(res)/param2.png</imageUrl> </param2> </otherParamList > </dev> </devList> <button> <id>btn_change_name</id> <isEnable>true</isEnable> </button> <button> <id>btn_view_devinfo</id> <isEnable>true</isEnable> </button> <button> <id>btn_init</id> <isEnable>true</isEnable> </button> </view> </request>
requestType可以是一些已在概要设计里归纳需要实现的需求,比如createUI、closeUI、showUI、hideUI、updateUI。updateUI中对应的是viewId。当且仅当是createUI需要viewType、viewParent、devId。createUI的返回参数有一个是viewId。
该数据格式在VM的requestCallback中调用。
2.9.4 VM->Controller交互事件数据格式
<?xml version='1.0' encoding='utf-8'?> <request> <requestType>updateUI</requestType> <view> <viewType>mainView</viewType> <viewIndex>view_1</viewIndex > <event>click</event > <listView> <id>listView_mainView</id> <selectedId>param_2</selectedId> </listView> </view> </request>
用户点击了listView的证书节点。
该数据格式在DM的eventCallbackForVM中调用。
用户选择了证书,则提交请求使能证书操作相关按钮。Controller->VM的请求在VM的requestCallback中调用,格式如下:
<?xml version='1.0' encoding='utf-8'?> <request> <requestType>updateUI</requestType> <view> <viewId>1</viewId> <viewType>mainView</viewType> <button> <id>btn_view_param2</id> <isEnable>true</isEnable> </button> </view> </request>
用户点击了修改名称按钮。则调用一次VM->Controller的交互事件,声明按钮动作,Controller再提交一次到VM的交互请求:
<?xml version='1.0' encoding='utf-8'?> <request> <requestType>createUI</requestType> <view> <viewType>changeLabelView</viewType> <viewId>view_2</viewId> <devList> <dev> <name>dev1</name> <id>dev1_devStd</id> <value>devStd</value> <mapToUIId>oldName</mapToUIId> </label> <sn> <id>DS20150415<id> <mapToUIId>sn</mapToUIId> </sn> </dev> </devList> </view> </request>
用户点击了changeNameView的OK之后,调用一次VM->Controller的交互事件,正式调用修改名称操作。
2.9.5 Controller->DM的交互请求数据格式
<?xml version='1.0' encoding='utf-8'?> <request> <requestType>updateDev</requestType> <dev> <id>dev1_devStd</id> </dev> <action>changeName</action> <paramList> <oldName>devStd</oldName> <newName>devStdNew</newName> </paramList> </request>
该消息在DM的requestCallback中调用。
requestCallback处理完之后,如果执行成功,Controller再发一次到VM的更新消息,改变UI显示的Name,并创建一个提示框,提示成功。
3 补充说明:
1、同步异步调用的实现区别:
同步:调用者会等待回调结束之后做下一步操作。
异步:调用者请求之后,马上返回。如果有更新的结果,则以事件方式通知Controller。完全不需要在调用者和被调用者创建新的线程来检测。这样做的要求是要在同一进程里。
如果非得是不同进程,可以考虑实现一种RPC远程过程回调。但是目前来看,从之前讨论过的安全性问题来看,应该尽量做到同一进程,然后UI以库形式调用。
2、UI中定义的按钮响应函数处理方法:
所有的按钮功能在Controller中定义,比如修改名称,按照字符串匹配查找相应的函数指针,并填充相应的参数列表。可以统一所有的参数均为字符串。
3、UI中的隐藏:通过Width或者Height更新为0%即可。
至此,可以统一所有的UI请求,不论是不是管理工具发出的,或者即使是安装包的UI,都可以按照这一套来做,完全解耦。全部用XML定义。