MQTT CS架构设计

目录

1.项目背景

2.项目下载

3.架构介绍

4.细节讲解

4.1 消息接收处理

4.2 网关处理

4.3 接口方法定义

4.4 IOC容器加载 服务组件/客户端组件

4.5 数据库操作


1.项目背景

项目是针对线体项目,进行设计线体流程管理系统,包含人工工位(20个工位以上)和串联的单机设备(5台以上)
管理系统负责工位数据的收集,以及流程的控制,人员的操作响应等

拓扑图

2.项目下载

下载链接:https://download.csdn.net/download/rotion135/93011359

3.架构介绍

设计模式以 MQTT为主要通讯协议,同时支持与PLC的ModbusTCP,测试设备的RS232通讯等
采用CS的设计框架,客户端与服务端是多对一的模式。 架构设计上也支持多对多的模式,就是数据同步上需要重新设计一下


接口调用参考了MVC的模式实现,以定义控制器的方式,采用反射将接口与主题绑定,决定请求路径的执行与返回

4.细节讲解

解决方案目录

4.1 消息接收处理

MQTT服务,接收到客户端的请求后,在下面的事件中响应处理


        /// <summary>
        /// 消息接收
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        private Task MqttServer_InterceptingPublishAsync(InterceptingPublishEventArgs arg)

获取ClientId,知道来源客户端

再获取主题与携带的参数

把主题和参数都传入到 网关API 中处理,再等待网关回复数据,再通过主题返回给客户端

4.2 网关处理

网关收到消息后,根据主题路径,获取需要调用的目标方法,通过反射的机制实现方法调用,再返回结果。参数都是Json的数据结构,目标方法是空参数的话,传入的参数就需要时空Json对象,即"{}"

  /// <summary>
  /// 请求操作
  /// </summary>
  /// <param name="toptic">主题内容</param>
  /// <param name="param">参数</param>
  /// <returns></returns>
  public static async Task<string> RequestOperation(string clientId, string toptic, object param)
  {
      string msgModel = "{}";
      try
      {
          //根据主题获取请求路径
          // 1.根据主题获取请求路径/反射所需信息
          var item = TopticCache.TopticCaches.Find(x => x.RequestToptic == toptic);
          if (item == null)
          {
              return msgModel;
          }

          // 2.将入参JSON字符串转为JObject(核心:方便根据参数名取值+自动类型转换)                
          string paramJson = param?.ToString() ?? "{}";
          JObject paramJObj = JObject.Parse(paramJson);

          // 3.反射:拼接完整类名【命名空间.类名】,获取类的Type对象
          string fullClassName = $"{item.NameSpace}.{item.Controller}";
          Type targetClassType = AppDomain.CurrentDomain.GetAssemblies()
              .Select(asm => asm.GetType(fullClassName)) // 从每个程序集中获取类型
              .FirstOrDefault(type => type != null);     // 取第一个非空的匹配类型
          //Type targetClassType = Type.GetType(fullClassName);                
          if (targetClassType == null)
          {
              return msgModel;
          }

          // 4.创建类的实例(如果是单例/注入的服务,这里可以替换为IOC容器获取实例)
          object classInstance = Activator.CreateInstance(targetClassType);

          // 5.反射:获取类中指定的异步方法对象
          MethodInfo targetMethod = targetClassType.GetMethod(
              item.Method,
              BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
          if (targetMethod == null)
          {
              return msgModel;
          }

          // 6.核心:解析JSON参数,匹配目标方法的入参,生成参数数组
          ParameterInfo[] methodParams = targetMethod.GetParameters();
          object[] invokeParams = new object[methodParams.Length];
          for (int i = 0; i < methodParams.Length; i++)
          {
              var paramInfo = methodParams[i];
              // 从JSON中根据【参数名】取值,兼容参数名大小写不一致问题
              JToken paramValue = paramJObj.GetValue(paramInfo.Name, StringComparison.OrdinalIgnoreCase);

              if (paramValue == null || paramValue.Type == JTokenType.Null)
              {
                  // 无传参时,赋值默认值
                  invokeParams[i] = paramInfo.HasDefaultValue ? paramInfo.DefaultValue : null;
              }
              else
              {
                  // 自动类型转换:JSON值 转为 方法入参的实际类型
                  invokeParams[i] = paramValue.ToObject(paramInfo.ParameterType);
              }
          }

          //赋值ClientId
          (classInstance as BaseAPIController).ClientId = clientId;

          // 7.核心:反射调用【异步方法】并await执行,获取返回结果
          var methodResult = targetMethod.Invoke(classInstance, invokeParams);
          // 异步方法必须await,获取Task的实际返回值
          if (methodResult is Task realTask)
          {
              await realTask;
              // 通过反射获取Task<T>的Result属性,拿到真实返回数据
              var resultProperty = realTask.GetType().GetProperty("Result");
              if (resultProperty != null)
              {
                  var realData = resultProperty.GetValue(realTask);
                  // 8.将方法返回值 赋值给泛型返回对象
                  if (realData != null)
                  {
                      msgModel = JsonConvert.SerializeObject(realData);
                  }
              }
          }
          return msgModel;
      }
      catch (Exception ex)
      {
          MsgModel err = new MsgModel(false, MsgErrorType.Internal_Error, ex.Message);
          return JsonConvert.SerializeObject(err);
      }
  }

4.3 接口方法定义

接口类都需要继承基类 BaseAPIController

特性APIRoute 定义主题的前部分

方法,再用特性定义主题的后半部分

加起来,访问目标方法的主题就是:

后面再填充各个客户端的ClientId,就能请求方法了

4.4 IOC容器加载 服务组件/客户端组件

服务主程序 和 客户端主程序,运行起来时,通过配置参数来决定IOC容器加载的引用项目

服务端:

客户端:

4.5 数据库操作

此处定义数据库的操作方法,定义实体,定义数据模型

数据库ORM使用 SqlSugar,

我的这篇文章有详细介绍:

SqlSugar 数据库操作通用类设计_c# sqlsugar通用类-CSDN博客

Domain里边定义访问数据库的方法,增删查改等

如果有实现缓存的,先从缓存中获取。

DBControl  定义了数据库初始化的代码,包含生成数据库,生成表,添加索引,以及上线后新增字段等功能

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Rotion_深

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值