硬件管理平台-硬件网关-网关配置项目
硬件管理平台-硬件网关-网关配置项目
简介
在本章开始前,我们需要做一下说明,为例更好的服务于网关项目,更好的让前面章节中的xml服务于网关,需要创建一个网关配置页面来新增硬件信息,并进行保存,保存后的xml才能被网关识别,进而让网关进行后续的任务(例如:硬件管理平台-硬件网关-插件模块-集成的下半部分)。
目的
为了让配置项与网关服务分离,这样子的好处是只有在硬件添加或修改时才会使用网关配置项,而这个操作对于客户来说是比较少见的,就算前期客户无休止的添加硬件时,我们也需要通过硬件项目平台来获取这个硬件的依赖及操作类,也需要有人去给他进行配置。而分离出来的网关服务可以专一的执行他的功能,操作功能和与上位机交互即可,无需关心其他的事情。
正文
网关配置项目创建
-
在4.配置程序文件夹中创建一个windows窗体项目,名称设置为HardwareGatewayConfig。
-
设置其生成路径,与HardwareGatewayService一致,或者可单独创建一个文件夹,专门存放网关配置,推荐前者。
-
修改App.config文件,添加dlls文件的引用
<runtime> <!--xmlns是必需的特性。指定程序集绑定所需的 XML 命名空间。 使用字符串“urn: 架构-microsoft-com:asm.v1”作为值。--> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <publisherPolicy apply="yes"/> <!--指定运行时是否使用发布者策略--> <!--指定加载程序集时公共语言运行时搜索的子目录, 其中privatePath是相对于*.exe.config文件的相对路径,多个文件夹以分号分隔。--> <probing privatePath="dlls"/> </assemblyBinding> </runtime>
注意:此时网关配置项目引用的dlls与产品库的dlls文件夹相同,因此在初始化时必须判断完是否有更新后再进行后续操作,否则将无法替换dlls的相关dll,因为会被该项目占用。
Zip解压
在网关配置项目的Program.cs的Main方法中添加解压方法
/// <summary>
/// 完成更新后将压缩包进行改名,并存放在本路径下,为了方便后期查找
/// </summary>
/// <param name="zipFile">zip文件</param>
/// <param name="folder">要存放的历史压缩包路径</param>
private static void UpdateFinish(string zipFile, string folder)
{
if (MessageBox.Show("更新完成,是否删除压缩包", "提示", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
// 将压缩包以当前时间戳来命名
File.Move(zipFile, string.Format("{0}\\target-{1}.zip", folder, System.DateTime.Now.ToString("yyyyMMddhhmmss")));
}
}
/// <summary>
/// 解压zip包
/// </summary>
private static void UnZipFile()
{
string zipFile = string.Format("{0}\\target.zip", Application.StartupPath);
// 同一路径下存在该压缩包
string folder = Application.StartupPath;
bool isExists = false;
if (File.Exists(zipFile))
{
isExists = true;
}
// 判断更新包存在
if (MessageBox.Show(isExists ? "发现更新包,目前服务还未安装,是否直接解压缩zip并启动服务" : "网关未安装,是否直接进行安装", "提示", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
if (isExists)
{
// 解压
ZipUtil.UnZipFile(zipFile, folder, true);
UpdateFinish(zipFile, folder);
}
}
}
在该项目中创建util文件夹,然后创建ZipUtil操作类,并将解压代码放到该处。
/// <summary>
/// 解压文件
/// </summary>
/// <param name="zipFile">被解压的压缩文件</param>
/// <param name="dirAim">解压到目标路径</param>
/// <param name="emptyFolder">是否解压空文件夹</param>
public static void UnZipFile(string zipFile, string dirAim, bool emptyFolder)
{
FastZip fastzip = new FastZip();
//// Create Empty Directory
fastzip.CreateEmptyDirectories = emptyFolder;
fastzip.ExtractZip(zipFile, dirAim, string.Empty);
}
为什么要单独创建一个类,而不是把他放到公共包中使用?由于公共包中的dll可能会被更新,如果使用的话会导致相关依赖包无法被替换,因此该类需要独立存在于util文件夹中。
其中FastZip是引用了ICSharpCode.SharpZipLib控件,该控件也需要独立存放,防止被更新。
注意:该处未添加服务的相关代码,例如当网关服务在运行时需将网关关闭然后再进行替换,否则也是无法更新的(又给自己挖一坑)。
网关配置项目xml配置
-
页面布局
在编写前我们将页面进行布局,左侧为硬件树,展示内容与产品库项目的左侧树一致,唯一的不同是不需要添加checkbox框;右侧的上方为动态显示的属性值与对应的输入框;中部是”添加“、”删除“按钮。
右上侧输入框的作用,目前动态输入框的逻辑是在少于8个属性的时候,将多余的label和TextBox都进行隐藏,而保存到xml时,判断是否被隐藏,如果显示且label的值不是以label开头的,则被存入xml中的HardwareInfo属性中。
其他功能或非xml逻辑后期再添加
-
在窗体中添加两类方法,一类是获取硬件项目的类;一类是读取xml的相关展示类
-
获取硬件类
/// <summary> /// 初始化网关 /// 1. 尝试获取网关服务的返回值 /// 2. 获取到成功信息后获取目前网关服务中的硬件类,并将该硬件类初始化到左侧的硬件树中 /// </summary> private void InitGateway() { // 判断硬件网关是否连通;之前是重置硬件 AjaxResult result = _hardwareControl.RefreshHardware(); if (result == null) { MessageBox.Show("返回数据失败"); } else { if (result.code == AjaxResult.OK) { // 获取所有的硬件类 List<HardwareProperties> properties = _hardwareControl.GetHardwares(); // 获取自定义的硬件类,该类的特点是不需要控制 DeviceTypeList.Clear(); _funClient.GetDeviceTypes().ForEach(dic => { DeviceTypeList.Add(dic["deviceTypeName"], dic["deviceTypeId"]); }); // 初始化所有的硬件类,包括非自定义和自定义的 LoadLibrary(properties); } } } /// <summary> /// 获得属性进行设备展示 /// </summary> /// <param name="properties">可控制的硬件</param> private void LoadLibrary(List<HardwareProperties> properties) { DeviceTypeDic.Clear(); independentDic.Clear(); Dictionary<string, List<HardwareProperties>> valuePairs = new Dictionary<string, List<HardwareProperties>>(); if (properties != null) { properties.ForEach(item => { // 按照类型进行分组,形成先是类型树,对应硬件类中的详细硬件型号 if (!valuePairs.ContainsKey(item.Type)) { valuePairs.Add(item.Type, new List<HardwareProperties>()); } // 模块自有功能 if (item.IndependentFun != null) { String funs = AssemblyUtil.GetDescriptionByEnumLis(item.IndependentFun); if (!String.IsNullOrEmpty(funs)) { independentDic.Add(item.Model, funs.Split(',')); } } // 硬件类型对应的类型主键 if (!DeviceTypeDic.ContainsKey(item.Type)) { DeviceTypeDic.Add(item.Type, item.TypeId); } valuePairs[item.Type].Add(item); }); } // 添加类型树 hardwareTV.Nodes.Clear(); foreach (string item in valuePairs.Keys) { TreeNode typeNode = new TreeNode(item); List<HardwareProperties> hardwares = valuePairs[item]; hardwares.ForEach(propertie => { TreeNode modelNode = typeNode.Nodes.Add(propertie.Model); modelNode.Tag = propertie; }); hardwareTV.Nodes.Add(typeNode); } // 在类型中添加非控制类硬件 foreach (String key in DeviceTypeList.Keys) { TreeNode typeNode = new TreeNode(key) { Tag = DeviceTypeList[key] }; hardwareTV.Nodes.Add(typeNode); } }
-
读取xml信息
/// <summary> /// 读取xml中的列表 /// </summary> /// <param name="model"></param> private void LoadXml(string model) { _hardwareXmlEntitys = xmlClient.GetHardwareInfos(); List<HardwareXmlEntity> hardwares = new List<HardwareXmlEntity>(); if (string.IsNullOrEmpty(model)) { hardwares.AddRange(_hardwareXmlEntitys); } else { hardwares = _hardwareXmlEntitys.Where(dic => dic.Param["设备型号"].Equals(model)).ToList<HardwareXmlEntity>(); } hardwareLV.Items.Clear(); hardwares.ForEach(hardware => { hardwareLV.Items.Add(AddListViewItem(hardware)); }); } /// <summary> /// 添加节点 /// </summary> /// <param name="hardware"></param> /// <returns></returns> private ListViewItem AddListViewItem(HardwareXmlEntity hardware) { ListViewItem item = new ListViewItem { Text = string.Format("{0}", (hardwareLV.Items.Count + 1)), Tag = hardware }; item.SubItems.Add(hardware.Param["设备主键"]); item.SubItems.Add(hardware.Param["设备型号"]); item.SubItems.Add(hardware.Param["Ip地址"]); item.SubItems.Add(hardware.Param["端口号"]); item.SubItems.Add(hardware.OperationTxt); item.SubItems.Add(hardware.SettingsTxt); return item; } }
-
-
增加选择左侧树的点击事件
业务场景是当选择了硬件树后,右侧上半部分显示该硬件需要输入的属性信息,右侧下半部分显示目前已经添加的硬件信息。
-
左侧硬件树添加AfterSelect事件
// 判断是选择的硬件还是硬件的型号 if (e.Node.Tag != null) { // 当选择的为硬件型号 if (e.Node.Tag is HardwareProperties properties) { // 将该硬件型号的内容进行赋值 modifyField.DeviceType = properties.Type; modifyField.DeviceTypeId = properties.TypeId; modifyField.ModelType = properties.Model; modifyField.ModelTypeId = properties.ModelId; SettingFunction(properties.Model, properties.InitializationFun, properties.OperationFun); // 读取该型号的硬件信息 LoadXml(properties.Model); // 动态显示硬件属性 SelectTreeView(properties.Params); //修改按钮属性 SettingAttribute(this.modifyBtn, "新增", true); SettingAttribute(this.delBtn, "删除", false); // 将该次操作定义为新增 _operation = Operation.Add; } // 选择的为硬件 else if (e.Node.Tag is String) { // 读取该类型种的硬件列表 LoadXmlByType(e.Node.Tag as String); //修改按钮属性 SettingAttribute(this.modifyBtn, "新增", false); SettingAttribute(this.delBtn, "删除", false); _operation = Operation.Showing; } }
此方法为点击了硬件树后触发的方法,主要目的是动态显示硬件属性;展示硬件信息;将选择的数据记录下来;动态修改按钮属性。
-
右上侧的动态框SelectTreeView方法
/// <summary> /// 动态加载硬件属性 /// </summary> /// <param name="items">当前选中的硬件型号所拥有的属性</param> private void SelectTreeView(List<string> items) { // 先判断属性个数是否大于了控件区的个数(现在是8个) if (items.Count > this.bodyPal.Controls.Count) { // 大于了,则提示 MessageBox.Show(String.Format("目前仅支持{0}个参数", items.Count)); } // 去掉无需人工添加的硬件属性 int nCount = items.Count - autoValue.Count; int nSeek = 1; for (int i = 0; i < bodyPal.Controls.Count; i++) { // 判断是否超过了硬件设置属性 if (nSeek <= nCount) { // 如果是无需人工添加的硬件属性则跳过 if (autoValue.Contains(items[i])) { continue; } // 改为硬件属性名称,将label或textBox进行修改,修改显示情况和显示内容 // label修改 SettingAttribute(bodyPal.Controls[String.Format("label{0}", nSeek)], items[i], true); // textBox修改 SettingAttribute(bodyPal.Controls[String.Format("textBox{0}", nSeek)], "", true); // textBox设置为可编辑 bodyPal.Controls[String.Format("textBox{0}", nSeek)].Enabled = true; nSeek += 1; } else { // 如果没有该控件则跳过 if (bodyPal.Controls[String.Format("label{0}", nSeek)] == null) { break; } // 设置回原来样式 SettingAttribute(bodyPal.Controls[String.Format("label{0}", nSeek)], bodyPal.Controls[String.Format("label{0}", nSeek)].Name, false); SettingAttribute(bodyPal.Controls[String.Format("textBox{0}", nSeek)], bodyPal.Controls[String.Format("textBox{0}", nSeek)].Name, false); bodyPal.Controls[String.Format("textBox{0}", nSeek)].Enabled = true; nSeek += 1; } } }
SettingAttribute方法为通用设置属性参数值
/// <summary> /// 设置控件属性信息 /// </summary> /// <param name="control">控件</param> /// <param name="text">显示信息</param> /// <param name="isVisible">是否显示属性</param> private void SettingAttribute(Control control, string text, bool isVisible) { control.Text = text; control.Visible = isVisible; }
-
显示硬件信息列表,通过读取xml即可获得
// 读取该类型种的硬件列表 LoadXmlByType(e.Node.Tag as String); // 读取该型号的硬件信息 LoadXml(properties.Model);
-
-
双击添加按钮,增加添加事件
该事件的作用是添加/修改硬件属性信息
private void modifyBtn_Click(object sender, EventArgs e) { // 获得硬件属性信息 HardwareXmlEntity hardwareDic = GetNewHardwareDic(); // 判断如果是修改,则将原来的设备主键赋值给该硬件属性实例 if (_operation == Operation.Edit) { xmlClient.RemoveHardwareInfo(hardwareDic.Param["设备主键"]); } // 向xml中添加硬件属性实例 xmlClient.AddHardwareInfo(hardwareDic); // 因变更,因此设置一个标识,该标识主要作用是判断是否有过修改 SetDeviceSetting(true); // 重新读取该设备信息,类似于刷新设备信息列表 LoadXml(_selectHardwareInfo.设备型号); }
我们着重说明获取硬件信息,获取硬件信息主要是将非人工添加的硬件信息和人工添加的硬件信息整合的过程,非人工添加的部分属性我们已经将其存储起来了,只需要对应赋值即可;人工添加的部分只需要遍历bodyPal中的控件,通过label控件的value和textBox对应的value即可获得硬件信息的数值
/// <summary> /// 点击新增/修改按钮后,获得一个新的设备信息,根据_operation来判断是新增还是修改 /// 新增:创建新的主键,添加到xml中,刷新ListView /// 修改:将原有主键赋值给新的设备信息,然后从xml中删除并新增 /// 新的设备中InitializationFun和OperationFun的获得方式 /// 1.点击了treeView后获得的 /// 2.点击了ListView(双击/单击)后获得 /// Param 调用GetNewHardwareByDic获得 /// SettingFun可支持多种方式 /// 1.按照定时功能分类,不涉及设备类型和型号 /// 2.按照同类型号的功能分类,不细化每个设备信息 /// 3.细化到设备信息,每一条都进行设置 /// </summary> /// <returns></returns> private HardwareXmlEntity GetNewHardwareDic() { HardwareXmlEntity hardwareDic = new HardwareXmlEntity { InitializationFun = _selectHardwareInfo.初始化, OperationFun = _selectHardwareInfo.操作功能, Param = GetNewHardwareByDic() }; if (_operation != Operation.Edit) { hardwareDic.Param.Add("设备主键", System.Guid.NewGuid().ToString("N")); } else { hardwareDic.Param.Add("设备主键", _selectHardwareInfo.设备主键); } return hardwareDic; }
GetNewHardwareByDic方法中为添加硬件属性
/// <summary> /// 根据BodyPal获得所有的组件 /// </summary> /// <returns></returns> private Dictionary<string, string> GetNewHardwareByDic() { Dictionary<string, string> valuePairs = new Dictionary<string, string>(); foreach (Control control in this.bodyPal.Controls) { if (control.Visible && control is Label) { valuePairs.Add(control.Text, this.bodyPal.Controls["textBox" + control.Name.Replace("label", "")].Text); } } if (!string.IsNullOrEmpty(modifyField.DeviceType)) { valuePairs.Add("设备类型", modifyField.DeviceType); } if (!string.IsNullOrEmpty(modifyField.DeviceTypeId)) { valuePairs.Add("类型主键", modifyField.DeviceTypeId); } if (!string.IsNullOrEmpty(modifyField.ModelType)) { valuePairs.Add("设备型号", modifyField.ModelType); } if (!string.IsNullOrEmpty(modifyField.ModelTypeId)) { valuePairs.Add("型号主键", modifyField.ModelTypeId); } return valuePairs; }
小结
此时,在硬件网关服务启动的情况下,运行网关配置程序可进行硬件信息的增删改,为硬件网关添加硬件数据并进行操作做准备。