记一次智能水电抄表系统如何兼容多种协议及设备

#改造思路#

1.业务系统尽量保持纯粹,不直接处理设备协议及通信。

2.抽象出常用设备操作功能(抄表、拉合闸、清零、校时等),定义接口

3.以插件的形式融入系统,绑定对应的品牌型号,利用反射调用不同的插件。

接口定义如下

public interface IAggrementPlugin
{
    /// <summary>
    /// 启动
    /// </summary>
    public (bool success, string msg) Start(string @params);
    /// <summary>
    /// 停止
    /// </summary>
    public (bool success, string msg) Stop();

    /// <summary>
    /// 是否正在运行
    /// </summary>
    /// <returns></returns>
    public bool IsRunning();

    /// <summary>
    /// 透传
    /// </summary>
    /// <param name="imei">序列号</param>
    /// <param name="valueCmd">自定义指令</param>
    /// <returns></returns>
    public Task<string> Transform(string imei, string valueCmd);

    /// <summary>
    /// 打开继电器,断闸
    /// </summary>
    /// <returns></returns>
    public bool PowerOff(string sn, string addr, string code, string baseCmd);

    /// <summary>
    /// 关闭继电器,断闸
    /// </summary>
    /// <param name="sn"></param>
    /// <param name="addr"></param>
    /// <returns></returns>
    public bool PowerOn(string sn, string addr, string code, string baseCmd);

    /// <summary>
    /// 读取组合有功数据块电能
    /// </summary>
    /// <param name="sn"></param>
    /// <param name="addr"></param>
    /// <returns></returns>
    public string ReadCombinedAllTotal(string sn, string addr, string code);

    /// <summary>
    /// 读取组合有功总电能
    /// </summary>
    /// <param name="sn"></param>
    /// <param name="addr"></param>
    /// <returns></returns>
    public string ReadCombinedTotalE(string sn, string addr, string code);


    /// <summary>
    /// 读取正向有功数据块电能
    /// </summary>
    /// <param name="sn"></param>
    /// <param name="addr"></param>
    /// <returns></returns>
    public string ReadPositiveAllTotal(string sn, string addr, string code);


    /// <summary>
    /// 读取正向有功总电能
    /// </summary>
    /// <param name="sn"></param>
    /// <param name="addr"></param>
    /// <returns></returns>
    public string ReadPositiveTotalE(string sn, string addr, string code);

    /// <summary>
    /// 读取反向有功数据块电能
    /// </summary>
    /// <param name="sn"></param>
    /// <param name="addr"></param>
    /// <returns></returns>
    public string ReadReverseAllTotal(string sn, string addr, string code);

    /// <summary>
    /// 读取反向有功总电能
    /// </summary>
    /// <param name="sn"></param>
    /// <param name="addr"></param>
    /// <returns></returns>
    public string ReadReverseTotalE(string sn, string addr, string code);

    /// <summary>
    /// 电表清零
    /// </summary>
    /// <param name="sn"></param>
    /// <param name="addr"></param>
    /// <param name="code"></param>
    /// <returns></returns>
    public string ClearMeter(string sn, string addr, string code);

    /// <summary>
    /// 校时
    /// </summary>
    /// <param name="sn"></param>
    /// <param name="addr"></param>
    /// <param name="code"></param>
    /// <returns></returns>
    public string Timing(string sn, string addr, string code);

    /// <summary>
    /// 配置参数
    /// </summary>
    /// <param name="sn">序列号</param>
    /// <param name="addrs">地址列表</param>
    /// <param name="code">编码</param>
    /// <returns></returns>
    public (bool success, string body) Configuration(string sn, List<string> addrs);
}

接口插件相关工具类

/// <summary>
/// 协议插件--工具类
/// </summary>
public static class AggrementPluginUtil
{
    public static ConcurrentDictionary<string, IAggrementPlugin> _pluginDic = new ConcurrentDictionary<string, IAggrementPlugin>();

    // 启动(卸载原dll)、停止、重新加载(卸载全部,重新加载)、清理dll(卸载不在系统中的dll、例如删除等)

    public static IAggrementPlugin? GetPluginService(string assemblyName, string filePath, string className = "Aggrement", bool fg = false)
    {
        IAggrementPlugin? docking = null;

        if (fg == false && _pluginDic.ContainsKey(assemblyName))
        {
            return _pluginDic[assemblyName];
        }

        filePath = Path.Combine(App.WebHostEnvironment.WebRootPath, filePath);
        if (File.Exists(filePath))
        {
            //Assembly assembly = Assembly.LoadFile(filePath);
            //docking = assembly.CreateInstance($"{assemblyName}.{className}") as IDocking;
            if (!_pluginDic.ContainsKey(assemblyName))
            {
                byte[] assemblyBuf = File.ReadAllBytes(filePath);
                Assembly assembly = Assembly.Load(assemblyBuf);
                //Assembly assembly = Assembly.LoadFrom(assemblyName);
                docking = assembly.CreateInstance($"{assemblyName}.{className}") as IAggrementPlugin;
                if (docking != null)
                {

                    _pluginDic.TryAdd(assemblyName, docking);
                }
                return docking;

            }
            else
            {
                if (fg == true)
                {
                    _pluginDic[assemblyName] = docking;
                }
                return _pluginDic[assemblyName];
            }
        }
        else
        {
            throw new Exception("当前程序集不存在");
        }
    }

    public static void Init(Dictionary<string, string> plugins, out string msg)
    {
        msg = "";
        foreach (var plugin in plugins)
        {
            if (_pluginDic.ContainsKey(plugin.Key))
            {
                _pluginDic.Remove(plugin.Key, out IAggrementPlugin _p);
                if (_p != null)
                {
                    _p.Stop();
                    _p = null;
                }
            }
            var _plugin = GetPluginService(plugin.Key, plugin.Value);
            if (_plugin == null)
            {
                msg += $"当前插件初始化失败{plugin.Key}请检查插件是否合理/r/n";
            }
        }
    }


    /// <summary>
    /// 根据电表信息获取插件
    /// </summary>
    /// <param name="device"></param>
    /// <param name="isDTU">判断是否是dtu</param>
    /// <returns></returns>
    public static (IAggrementPlugin, BrandModel) GetPlugin(ElectricMeter device, bool isDTU = false)
    {
        // 获取品牌型号缓存
        // 获取插件缓存
        var _cache = App.GetService<SysCacheService>();
        var brandModels = _cache.Get<List<BrandModel>>("BrandModels");
        var plugins = _cache.Get<List<AggrementPluginManage>>("AggrementPlugins");
        if (brandModels.Count == 0 || plugins.Count == 0)
        {
            throw Oops.Bah("插件服务缓存失败,请稍后重试");
        }
        BrandModel currentModel = null;
        AggrementPluginManage currentPlugin=null;
        if (isDTU == false)
        {
            currentModel = brandModels.Where(w => w.Id == device.BrandId).FirstOrDefault();
            if (currentModel == null)
            {
                throw Oops.Bah("当前插件服务缓存失败,请稍后重试");
            }
            currentPlugin = plugins
                .Where(w => w.Id == currentModel.AggrementPluginId)
                .FirstOrDefault();
            if (currentPlugin == null)
            {
                throw Oops.Bah("当前插件服务缓存失败,请稍后重试");
            }
        }
        else
        {
            //Admin.NET.Plugin.Dtu
            
            currentPlugin = plugins
                .Where(w => w.AssemblyName == "Admin.NET.Plugin.Dtu")
                .FirstOrDefault();
            if (currentPlugin == null)
            {
                throw Oops.Bah("当前插件服务缓存失败,请稍后重试");
            }
        }
        
        var pluginService = GetPluginService(currentPlugin.AssemblyName, currentPlugin.Path);
        if (pluginService == null)
        {
            throw Oops.Bah("当前插件获取失败");
        }
        if (pluginService.IsRunning() == false)
        {
            throw Oops.Bah("当前插件未运行");
        }
        return (pluginService, currentModel);
    }

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值