HardwareAbstract
在编写LoadLibrary前,我们需要完善HardwareAbstract类,否则TreeView显示什么?后续的内容应该如何操作?都将是一个坑。
由于本项目已完成,所以就不再一点一点的回顾HardwareAbstract类中添加的一堆一堆的代码了,先大体阐述一下类中的内容,具体代码展示
abstract or interface
由于要进行反射,所以必然要将所有硬件的公共部分提取出来,而抽象和接口都可以实现这个工作,使用接口更规范,如果不实现接口类,是会报错的;而使用抽象可以编写一些内部类,只要继承了该抽象类就可以调用内部类,虽然使用静态类也能实现,不过还是抽象类的封装更好一些,随着项目内容增多,公共方法不好找。不过最后还是使用了abstract抽象类,因为如果使用interface的话还是要在上面加一层,这样子就无法体现接口的好处了。
之前的项目使用的是abstract,导致了该类过于臃肿
该抽象类一共550多行代码,这还是注释不全的情况下,看到以后说实话都不太想动他了。
HardwareAbstract 应该有什么
何为HardwareAbstract,肯定是与硬件相关的抽象方法和抽象类(再加上interface部分),那对于硬件的交互信息和展示信息都应该在该类中,因此该类应该设计成一个硬件的集合抽象,其中包含了必须的接口和公共实现类。接口中必须要能返回硬件的所有信息,例如该硬件的功能,调用该功能必须要输入的信息以及该功能内部的执行接口。分别为GetHardwareInfo和Execute。
GetHardwareInfo
该方法主要是为了获取硬件的所有信息,包含硬件类型,名称,描述等基础信息,又包含了该硬件的功能信息,其中功能可分为初始化,定时和可调用三个维度。
- 初始化指的是系统利用反射加载完现有的硬件模块后,通过硬件模块获得现场的真实数据后需要对该硬件进行初始化,例如我们反射加载了一个海康门禁的模块,门禁模块提出,我需要ip地址,端口号,用户名和密码才能工作。而现场肯定要将一个真实存在的海康门禁进行添加,加上这些门禁所需的信息。通过对这个门禁实例化后,会调用初始化方法,初始化方法的内容可能是模拟登录,拿到Handle的句柄,则初始化维度中将有一个模拟登录的功能。
- 定时指的是定时触发的功能,例如获取温湿度如果需要定时触发,则定时这个维度中应该有一个功能叫获取温湿度。
- 可调用值得是该硬件中有哪些功能是上位机可以调用的。
Execute
真正执行调用维度的接口实现方法,通过GetHardwareInfo方法获得的信息中,只有可调用的维度是需要上位机主动调用的,其他的都是系统内部调用。Execute方法应该为async/wait还是同步的,之前的实现是同步的,在网关服务中统一调用。这样的不便之处就是对于某个功能或者某类功能要异步或者同步需要动态设定或者写死,而在Execute中可通过编写人员的判断来约定好这个功能,好处是服务不需要去管理这个。
公用方法
目前公用的方法很全,包括了log4j,配置文件(ini),异步execute方法的事件,心跳公用方法,测试类,http相关类等。
实现
HardwareAbstract
在公共包中创建一个HardwareLibrary项目,该项目的位置位于Public文件下。将AssemblyLibrary项目的HardwareAbstract类放到新建的项目中。此时HardwareGateWebApi、AssemblyLibrary和HardwareGatewayProductization**项目会报错,因此需要添加对新项目的引用。
在HardwareLibrary中创建HardwareAbstract类,用于存放抽象方法;在创建一个abstract文件夹,用于存放HardwareAbstract继承的抽象类,该类主要用于存放一些公共的方法,如果写在一起对于维护不好维护。
注意:这样子的namespace会报错,所以我直接将namespace改为了HardwareLibrary,没有加文件夹名称。
在HardwareAbstract.cs中添加代码
/// <summary>
/// 获得设备相关配置项
/// </summary>
/// <returns></returns>
public abstract HardwareProperties GetHardwareInfo();
/// <summary>
/// 执行方法
/// </summary>
/// <param name="function">功能码</param>
/// <param name="hardware">设备基础信息</param>
/// <param name="param">调用参数</param>
/// <returns>如果返回值是非空,则出现错误信息,记录或将日志放到服务器上</returns>
public abstract AjaxResult Execute(Function function, string hardware, string param);
AjaxResult
该类在之前提到过,此时我们需要将该类放到公共包中使用,重新创建一个类项目,命名为UtilsLibaray,将该类放到此项目中
之前添加后,忘记说使用Nuget下载Newtonsoft.Json了,在此补上,添加的依赖如下
HardwareProperties
该类为硬件信息参数,大体展现内容已在前文描述,代码如下
public class HardwareProperties
{
private string _type;
private string _model;
private string _folder;
private string _relyOnFolder;
private List<string> _relyOnFiles;
private string _version;
private string _describe;
private List<Function> _operationFun;
private List<Function> _initializationFun;
private List<Function> _timeingFun;
private List<String> _params;
/// <summary>
/// 类型
/// </summary>
public string Type { get => _type; set => _type = value; }
/// <summary>
/// 类型主键
/// </summary>
public string TypeId { get; set; }
/// <summary>
/// 型号
/// </summary>
public string Model { get => _model; set => _model = value; }
/// <summary>
/// 型号主键
/// </summary>
public string ModelId { get; set; }
/// <summary>
/// 依赖文件夹
/// 单一文件夹
/// </summary>
public string RelyOnFolder { get => _relyOnFolder; set => _relyOnFolder = value; }
/// <summary>
/// 版本号
/// </summary>
public string Version { get => _version; set => _version = value; }
/// <summary>
/// 模块描述
/// </summary>
public string Describe { get => _describe; set => _describe = value; }
/// <summary>
/// 所在文件夹
/// dll所在文件夹的路径
/// </summary>
public string Folder { get => _folder; set => _folder = value; }
/// <summary>
/// 依赖文件列表
/// runtime文件夹中个别dll
/// </summary>
public List<string> RelyOnFiles { get => _relyOnFiles; set => _relyOnFiles = value; }
/// <summary>
/// 可操作功能
/// </summary>
public List<Function> OperationFun { get => _operationFun; set => _operationFun = value; }
/// <summary>
/// 初始化时触发功能
/// </summary>
public List<Function> InitializationFun { get => _initializationFun; set => _initializationFun = value; }
/// <summary>
/// 定时功能
/// </summary>
public List<Function> TimeingFun { get => _timeingFun; set => _timeingFun = value; }
public List<Function> IndependentFun { get; set; }
public List<String> Params { get => _params; set => _params = value; }
/// <summary>
/// 构造函数
/// </summary>
/// <param name="typeId">类型ID</param>
/// <param name="modelId">型号ID</param>
/// <param name="type">类型</param>
/// <param name="model">型号</param>
/// <param name="relyOnFolder">依赖的文件夹</param>
/// <param name="relyOnFiles">依赖的文件列表</param>
/// <param name="version">版本号</param>
/// <param name="describe">描述</param>
/// <param name="typeClass">类型描述</param>
/// <param name="operationFun">操作功能</param>
/// <param name="initializationFun">初始功能</param>
/// <param name="timeingFun">定时功能</param>
/// <param name="independentFun">模块功能</param>
public HardwareProperties(string typeId, string modelId, string type, string model, string relyOnFolder, List<string> relyOnFiles, string version, string describe, Type typeClass, List<Function> operationFun, List<Function> initializationFun, List<Function> timeingFun, List<Function> independentFun)
{
TypeId = typeId;
ModelId = modelId;
_type = type;
_model = model;
_relyOnFolder = relyOnFolder;
_relyOnFiles = relyOnFiles;
_version = version;
_describe = describe;
_operationFun = operationFun;
_initializationFun = initializationFun;
_timeingFun = timeingFun;
if (typeClass == null)
{
_params = new List<string>();
}
else
{
_params = AssemblyControl.GetParams(typeClass, new List<string>() { "定时功能", "操作功能", "初始化", "模块功能" });
}
IndependentFun = independentFun;
}
}
该代码我放到了entity文件夹中,此时interface和abstract有些怪怪的,就新建了一个util将这两个文件夹放入其中。
AssemblyControl.GetParams
该方法为通过硬件的功能类型获得各自的功能值,主要用于展示。
我们将它作为HardwareProperties的扩展类,新建一个extends文件夹,创建HardwarePropertiesExt扩展类,将代码放入
/// <summary>
/// 根据类型获取展示信息
/// </summary>
/// <param name="typeClass"></param>
/// <returns></returns>
public static List<string> GetParams(this HardwareProperties properties, Type typeClass, List<string> ignoreFun)
{
List<string> retList = new List<string>();
foreach (PropertyInfo info in typeClass.GetProperties())
{
if (ignoreFun == null || !ignoreFun.Contains(info.Name))
{
retList.Add(info.Name);
}
}
return retList;
}
布局如图:
Function
此时生成解决方案时还会报错,报错为未识别Function类,该类为枚举类,主要是列出了所有的硬件功能。
一开始打算细分,例如空调的功能为一个枚举,灯控的为一个枚举,这样子更清晰,后来放弃了。虽然清晰了,但是会形成同一个功能重复,例如空调的关机和一体机的关机是一个功能,而空调的设置温度可能是一个特定的值,也有可能是设置温度范围。所以这里就没有分的那么清晰。而且有些值的确有歧义,例如空调的开机可能是风机工作(直接485控制),也有可能是接通电源(红外控制)。
public enum Function
{
/// <summary>
/// 文件下载
/// </summary>
[Description("文件下载")]
DownLoadFile = 03,
/// <summary>
/// 开机
/// </summary>
[Description("开机")]
OpenMachine = 05,
/// <summary>
/// 关机
/// </summary>
[Description("关机")]
CloseMachine = 06,
/// <summary>
/// 设置温度
/// </summary>
[Description("设置温度")]
SettingTemputre = 07,
/// <summary>
/// 自动
/// </summary>
[Description("自动")]
SettingAuto = 08,
}
AHardware
该方法用于存放抽象类的公共方法,例如模拟数据方法,心跳返回等,该部分在后续进行说明。
LoadLibrary方法
此时就可以在MainForm中添加LoadLibrary方法了,该方法主要就是利用放射类获得硬件属性来进行展示。
Dictionary<string, List<HardwareProperties>> valuePairs = new Dictionary<string, List<HardwareProperties>>();
_properties.Clear();
// 解析硬件反射类
_hardwares.ForEach(hardwareAbstract =>
{
// 获得硬件信息
HardwareProperties hardware = hardwareAbstract.GetHardwareInfo();
// 将引用的路径赋值给硬件信息
hardware.Folder = hardwareAbstract.IncludeDll;
_properties.Add(hardware);
// 形成 类型为key的字典,便于后面初始化TreeView控件
if (!valuePairs.ContainsKey(hardware.Type))
{
valuePairs.Add(hardware.Type, new List<HardwareProperties>());
}
valuePairs[hardware.Type].Add(hardware);
});
hardwareTV.Nodes.Clear();
foreach (string item in valuePairs.Keys)
{
// 创建一个硬件类型的Node
TreeNode typeNode = new TreeNode(item);
// 遍历硬件,获取硬件的型号(该型号必须唯一)
valuePairs[item].ForEach(propertie => {
TreeNode modelNode = typeNode.Nodes.Add(propertie.Model);
modelNode.Tag = propertie;
});
// 展示时树的顶级为硬件类型分类,下面为各型号分类
hardwareTV.Nodes.Add(typeNode);
}
编译成功,但是还无法查看效果,需要创建硬件项目。
标签:set,string,get,产品库,List,硬件,public,底层 From: https://www.cnblogs.com/wanghun315/p/17603986.html