硬件管理平台-硬件网关-插件模块

发布时间 2023-08-12 15:57:26作者: 摧残一生

硬件管理平台-硬件网关-插件模块

前言

硬件产品库最终导出了硬件及其相关的依赖项,接下来就需要将依赖项放入到网关项目中,放入后需要做两个动作,第一个是通过硬件配置软件将压缩包解压并放入指定位置,然后进行该硬件的具体操作,另一个是硬件网关服务启动后,将特定目录下的硬件添加到网关中,进而在进行下面的操作。本文将讲述的是后者,硬件网关如何将硬件添加到网关中,实际上添加方式与硬件产品库中的方式一致,都是通过反射来操作的。唯一的不同可能就是执行的环境不同,一个是在公司内部,出现错误可以马上解决,而硬件网关的插件模块是在客户现场操作的,调试和解决问题都需要必须快速精准,否则将将耗费大量的人力财力。

插件项目搭建

网关文件夹中创建一个名为HardwareGatewayPlugin的类库项目,并配置生成路径与HardwareGatewayService一致:..\bin\Debug\HardwareGateway\

准备工作

  1. 设置生成路径

    将该项目路径设置为与HardwareGatewayWebApi生成路径一致:..\bin\Debug\HardwareGateway\.

  2. 引用log4net

    无需AssemblyInfo.cs的配置,因为该类为类库,被引用时可配置项可通用。

    代码中可添加日志的引用代码

    readonly ILog _loginfo = LogManager.GetLogger("loginfo");
    readonly ILog _logerror = LogManager.GetLogger("logerror");
    
  3. 将引用的依赖包中属性“复制本地”设置为false。

Exception机制

因该项目客户现场使用,所以需要增加额外的日志和Exception机制,可以在报错时立马获取错误信息并解决。

  1. 在公共包的AssemblyLibrary项目AssemblyControl类中增加事件属性,在反射过程中如果出现错误可直接通过log文件获得错误信息

    public event AssemblyErrorEvent<AssemblyErrorEventArgs> ErrorEvent;
    

    AssemblyErrorEventArgs类型为

    public class AssemblyErrorEventArgs : EventArgs
    {
    
        /// <summary>
        /// 读取的dll文件路径
        /// </summary>
        public string DllFile { get; internal set; }
        /// <summary>
        /// 类加载失败的异常信息
        /// </summary>
        public Exception Exception { get; internal set; }
        /// <summary>
        /// 位置
        /// </summary>
        public string Seek { get; internal set; }
    }1
    
  2. 在插件项目中添加错误事件,包含组件的各流程中可能发生的错误,如组件加载前、组件加载后,组件加载错误时的相关异常

  3. 错误的相关描述信息,如dll未找到、构造函数问题-必须无参、未找到集成类等

反射模块

在插件项目中添加PluginManage.cs类,复用产品库的反射类代码即可,不过唯一的不同是需要将反射后的实例保存起来,以供后面的代码使用。

插件管理的流程是:

  1. 初始化反射类,并将Assembly_ErrorEvent赋值给事件委托ErrorEvent。
  2. 编写对外接口Load,主要作用是通过反射获得实例类,并保存到hardwareDic变量中。
  3. 添加异常机制,在主要代码处添加异常反馈,防止出现错误还不知的清空。
  4. 编写两个调用硬件项目中Execute的调用方法,分别为FunctionAsyncFunction
  5. 编写对外接口GetHardwareAbstracts,主要为获得当前反射的实例。
/// <summary>
/// 插件管理器
/// </summary>
/// <typeparam name="HardwareAbstract">插件方法的接口定义类,必须使用 abstract 修饰</typeparam>
public sealed class PluginManager : IDisposable
{
    readonly ILog _loginfo = LogManager.GetLogger("loginfo");
    readonly ILog _logerror = LogManager.GetLogger("logerror");
    #region 字段

    /// <summary>
    /// 设备类型的dll反射类,用于通过ke获得反射获得的硬件类
    /// </summary>
    readonly Dictionary<string, HardwareAbstract> hardwareDic = new Dictionary<string, HardwareAbstract>();
    /// <summary>
    /// 声明反射类
    /// </summary>
    readonly AssemblyControl assembly;
    /// <summary>
    /// 反射类的锁
    /// </summary>
    public Object DicLock = new object();
    #endregion

    #region 事件
    /// <summary>
    /// 插件实例创建前的事件
    /// </summary>
    public event PluginEvent<PluginInstanceCreatingArgs> OnInstanceCreating;
    /// <summary>
    /// 异步,没有返回值
    /// </summary>
    /// <param name="function"></param>
    /// <param name="hardwareInfo"></param>
    /// <param name="param"></param>
    private void FunctionAsync(String model, Function function, String hardwareInfo, string param = "")
    {
        Task<AjaxResult>.Run(() =>
        {
            AjaxResult result = AjaxResult.error("", "未找到");
            try
            {
                result = hardwareDic[model].Execute(function, hardwareInfo, param);
            }
            catch (Exception ex)
            {
                _logerror.Error($"FunctionAsync:Function:{function}, HardwareInfo:{hardwareInfo}, param:{param}", ex);
            }
            return result;
        }).ContinueWith(aj => {
            //后续的可以加这里 aj.Result
        });
    }

    public async Task<AjaxResult> AsyncResult(String model, Function function, String hardwareInfo, string param)
    {
        var task = await Task<AjaxResult>.Run(() =>
        {
            try
            {
                return hardwareDic[model].Execute(function, hardwareInfo, param);
            }
            catch (Exception ex)
            {
                _logerror.Error($"AsyncResult error:Function:{function}, HardwareInfo:{hardwareInfo}, param:{param}", ex);
                return AjaxResult.error("失败", "失败");
            }
        });
        return task;
    }

    public AjaxResult Function(Function function, String hardwareInfo, string param = "")
    {
        AjaxResult result = AjaxResult.error("", "未找到");
        try
        {
            HardwareInfo info = JSONUtils.DeserializeObject<HardwareInfo>(hardwareInfo);
            if (hardwareDic.ContainsKey(info.设备型号))
            {
                // 将获得状态修改为了异步
                FunctionAsync(info.设备型号, function, hardwareInfo, param);
                return AjaxResult.success("操作成功");
            }
            else
            {
                _logerror.Error($"运行的dll未找到 function{function} hardwareInfo{hardwareInfo}");
            }
        }
        catch (Exception ex)
        {
            _logerror.Error($"执行命令错误:Function:{function}, HardwareInfo:{hardwareInfo}, param:{param}", ex);
        }
        return result;
    }

    /// <summarydd
    /// 插件实例创建后的事件
    /// </summary>
    public event PluginEvent<PluginInstanceCreatedArgs<HardwareAbstract>> OnInstanceCreated;

    /// <summary>
    /// 加载插件失败的事件,具体的错误请通过参数<paramref name="e"/>的属性 ErrorType
    /// 错误类型参见 <see cref="PluginErrorTypes"/>
    /// </summary>
    public event PluginErrorEvent<PluginErrorEventArgs> OnError;

       
    #endregion

    /// <summary>
    /// 初始化插件管理器
    /// </summary>
    /// <param name="pluginFolder">插件所在目录,绝对路径或相对路径</param>
    public PluginManager()
    {
        assembly = new AssemblyControl();
        assembly.ErrorEvent += Assembly_ErrorEvent;
    }

    private void Assembly_ErrorEvent(AssemblyErrorEventArgs e)
    {
        EmitPluginErrorEvent(new PluginErrorEventArgs()
        {
            FileName = e.DllFile,
            Exception = e.Exception,
            ErrorType = PluginErrorTypes.InvalidDll,
            Seek = e.Seek
        });
        _logerror.Error($"ErrorEvent DllFile {e.DllFile} Seek {e.Seek}", e.Exception);
    }
      
    /// <summary>
    /// 加载插件目录下的所有插件
    /// </summary>
    /// <param name="filter">插件过滤器,返回false表示不加载这个插件</param>
    public void Load(Func<string, bool> filter = null)
    {
        string[] files = Directory.GetFiles(assembly.PluginsFolder, "*.dll", SearchOption.AllDirectories).Where(filename => filter == null || filter(filename)).ToArray();// 筛选dll文件
        //反射获得类
        List<HardwareAbstract> hardwares = LoadAssembly(files);
        bool acquired = false;
        try
        {
            Monitor.Enter(DicLock, ref acquired);
            hardwareDic.Clear();
            hardwares.ForEach(hardware =>
            {
                hardwareDic.Add(hardware.GetHardwareInfo().Model, hardware);
            });
        }
        catch (Exception ex)
        {
            _logerror.Error("Load dll error", ex);
        }
        finally
        {
            if (acquired) { Monitor.Exit(DicLock); }
        }
    }

    public List<HardwareAbstract> GetHardwareAbstracts()
    {
        List<HardwareAbstract> hardwares = new List<HardwareAbstract>();
        bool acquired = false;
        try
        {
            Monitor.Enter(DicLock, ref acquired);
            hardwares.AddRange(hardwareDic.Values);
        }
        finally
        {
            if (acquired) { Monitor.Exit(DicLock); }
        }
        return hardwares;
    }

    /// <summary>
    /// 加载程序集
    /// </summary>
    /// <param name="filenames"></param>
    /// <returns></returns>
    private List<HardwareAbstract> LoadAssembly(string[] filenames)
    {
        // 加载程序集
        try
        {
            // 创建实例前,发出事件
            EmitPluginEvent(OnInstanceCreating, new PluginInstanceCreatingArgs()
            {
                FileNames = filenames
            });

            // 加载
            List<HardwareAbstract> hardwareAbstracts = assembly.GetHardwareAbstracts();

            // 创建实例成功后,发出事件
            EmitPluginEvent(OnInstanceCreated, new PluginInstanceCreatedArgs<HardwareAbstract>()
            {
                FileNames = filenames,
                Instance = hardwareAbstracts.ToArray()
            });

            return hardwareAbstracts;
        }
        catch (Exception ex)
        {
            // 程序集加载失败
            EmitPluginErrorEvent(new PluginErrorEventArgs()
            {
                FileName = string.Join(",", filenames),
                Exception = ex,
                ErrorType = PluginErrorTypes.InvalidDll
            });
            _logerror.Error("DLL加载失败", ex);
            return null;
        }
    }

    /// <summary>
    /// 发出插件事件的公共函数
    /// </summary>
    /// <returns>返回true阻止插件继续加载</returns>
    private bool EmitPluginEvent<ArgType>(PluginEvent<ArgType> eventName, ArgType arg)
        where ArgType : PluginEventArgs
    {
        if (eventName == null)
        {
            return false;
        }

        eventName.Invoke(this, arg);

        return arg.Cancel;
    }

    /// <summary>
    /// 发出插件错误事件的公共函数
    /// </summary>
    private void EmitPluginErrorEvent(PluginErrorEventArgs arg)
    {
        if (OnError == null)
        {
            return;
        }

        OnError.Invoke(this, arg);
    }
    private ManualResetEvent mre;
    public void Dispose()
    {
        lock (this)
        {
            hardwareDic.Clear();
        }
    }
}

结束

自此一个很简单的 反射模块就写完了