mqtt初版

This commit is contained in:
gcw_MV9p2JJN 2025-12-08 15:01:07 +08:00
parent cfdcd5d6bf
commit 5712801344
6 changed files with 317 additions and 45 deletions

View File

@ -19,25 +19,70 @@ namespace DOAN.Model.MES.recipe.Dto
/// </summary>
public class PfRecipeIssueLogDto
{
public string Receiver { get; set; }
/// <summary>
/// 下达记录ID主键
/// </summary>
public int Id { get; set; }
/// <summary>
/// 配方码
/// </summary>
public string RecipeCode { get; set; }
/// <summary>
/// 版本编号
/// </summary>
public string Version { get; set; }
/// <summary>
/// 下达时间
/// </summary>
public DateTime? IssueTime { get; set; }
/// <summary>
/// 工单号
/// </summary>
public string Workorder { get; set; }
/// <summary>
/// 产品码
/// </summary>
public string ProductCode { get; set; }
/// <summary>
/// 产品名称
/// </summary>
public string ProductName { get; set; }
/// <summary>
/// 创建人
/// </summary>
public string CreatedBy { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime? CreatedTime { get; set; }
/// <summary>
/// 更新人
/// </summary>
public string UpdatedBy { get; set; }
/// <summary>
/// 更新时间
/// </summary>
public DateTime? UpdatedTime { get; set; }
public int Id { get; set; }
public string RecipeCode { get; set; }
public string Version { get; set; }
public DateTime? IssueTime { get; set; }
public string IssuedBy { get; set; }
}
}

View File

@ -7,10 +7,50 @@ namespace DOAN.Model.MES.recipe
[SugarTable("pf_recipe_issue_log")]
public class PfRecipeIssueLog
{
/// <summary>
/// 接收方
/// 下达记录ID主键
/// </summary>
public string Receiver { get; set; }
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
public int Id { get; set; }
/// <summary>
/// 配方码
/// </summary>
[SugarColumn(ColumnName = "recipe_code")]
public string RecipeCode { get; set; }
/// <summary>
/// 版本编号
/// </summary>
public string Version { get; set; }
/// <summary>
/// 下达时间
/// </summary>
[SugarColumn(ColumnName = "issue_time")]
public DateTime? IssueTime { get; set; }
/// <summary>
/// 工单号
/// </summary>
[SugarColumn(ColumnName = "workorder")]
public string Workorder { get; set; }
/// <summary>
/// 产品码
/// </summary>
[SugarColumn(ColumnName = "productcode")]
public string ProductCode { get; set; }
/// <summary>
/// 产品名称
/// </summary>
[SugarColumn(ColumnName = "productname")]
public string ProductName { get; set; }
/// <summary>
/// 创建人
@ -36,34 +76,5 @@ namespace DOAN.Model.MES.recipe
[SugarColumn(ColumnName = "updated_time")]
public DateTime? UpdatedTime { get; set; }
/// <summary>
/// 下达记录ID主键
/// </summary>
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
public int Id { get; set; }
/// <summary>
/// 配方码
/// </summary>
[SugarColumn(ColumnName = "recipe_code")]
public string RecipeCode { get; set; }
/// <summary>
/// 版本编号
/// </summary>
public string Version { get; set; }
/// <summary>
/// 下达时间
/// </summary>
[SugarColumn(ColumnName = "issue_time")]
public DateTime? IssueTime { get; set; }
/// <summary>
/// 下达人
/// </summary>
[SugarColumn(ColumnName = "issued_by")]
public string IssuedBy { get; set; }
}
}

View File

@ -20,6 +20,6 @@ public interface IPADReportWorkService
int UpdateProReportwork(ProReportwork parm);
int StartWorkOrder(string workorder);
Task<int> StartWorkOrder(string workorder);
int FinishWorkOrder(string workorder);
}

View File

@ -1,8 +1,10 @@
using DOAN.Infrastructure.Helper;
using DOAN.Model;
using DOAN.Model.MES.base_;
using DOAN.Model.MES.mm;
using DOAN.Model.MES.product;
using DOAN.Model.MES.product.Dto;
using DOAN.Model.MES.recipe;
using DOAN.Repository;
using DOAN.Service.MES.mm.line;
using DOAN.Service.Mobile.IService;
@ -162,7 +164,7 @@ namespace DOAN.Service.Mobile
return Update(model, true);
}
public int StartWorkOrder(string workorder)
public async Task<int> StartWorkOrder(string workorder)
{
var result = 0;
// 获取同一天 同一组 同一线 的所有工单 把状态2 设为init 1
@ -184,9 +186,53 @@ namespace DOAN.Service.Mobile
});
//TODO 拼接MQTT消息发送给设备工单信息和配方信息
Context.Queryable<Pfref>
var RecipeMesg= Context.Queryable<PfRefProductRecipe>().LeftJoin<PfRecipeVersion>((rpr, r) => rpr.RecipeCode == r.RecipeCode)
.Where((rpr, r) => rpr.Productcode == handleWorkorder.ProductionCode)
.Where((rpr, r) => r.Status == 1)
.Select((rpr, r) => new
{
RecipeCode = r.RecipeCode,
Version = r.Version,
ParamList = Context.Queryable<PfRecipeParameters>().Where(it => it.RecipeCode == r.RecipeCode && it.Version == r.Version).ToList()
}).First();
if( RecipeMesg==null)
{
throw new Exception("未找到对应的配方信息,请检查产品与配方的关联关系,或配方状态是否有效");
}
// 合并两个对象为匿名对象
var combinedObject = new
{
WorkorderInfo = handleWorkorder,
RecipeInfo = RecipeMesg
};
string jsonString = Newtonsoft.Json.JsonConvert.SerializeObject(combinedObject, Newtonsoft.Json.Formatting.Indented);
//插入配方派发日志
PfRecipeIssueLog pfRecipeIssueLog = new PfRecipeIssueLog();
pfRecipeIssueLog.RecipeCode = RecipeMesg.RecipeCode;
pfRecipeIssueLog.Version = RecipeMesg.Version;
pfRecipeIssueLog.IssueTime = DateTime.Now;
pfRecipeIssueLog.Workorder = handleWorkorder.Workorder;
pfRecipeIssueLog.ProductCode = handleWorkorder.ProductionCode;
pfRecipeIssueLog.ProductName = handleWorkorder.ProductionName;
pfRecipeIssueLog.CreatedBy = "PDA";
pfRecipeIssueLog.CreatedTime = DateTime.Now;
Context.Insertable(pfRecipeIssueLog).ExecuteCommand();
MqttHelper _mqttHelper=new MqttHelper("mqtt://192.168.50.163", 8883);
// 连接
await _mqttHelper.ConnectAsync();
// 发送多条消息
await _mqttHelper.PublishMessageAsync("MES/Workorder/Start", jsonString);
// 断开连接
await _mqttHelper.DisconnectAsync();
return result;

View File

@ -18,6 +18,7 @@
<PackageReference Include="AspectCore.Abstractions" Version="2.4.0" />
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="8.0.0" />
<PackageReference Include="MQTTnet" Version="5.0.1.1416" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="UAParser" Version="3.1.47" />
<PackageReference Include="IPTools.China" Version="1.6.0" />

View File

@ -0,0 +1,169 @@
using MQTTnet;
using MQTTnet.Formatter;
using MQTTnet.Protocol;
using System;
using System.Threading.Tasks;
namespace DOAN.Infrastructure.Helper
{
/// <summary>
/// MQTT 消息发送工具类
/// </summary>
public class MqttHelper : IDisposable
{
// MQTT Broker 配置(可外部设置或配置文件读取)
public string MqttBrokerUrl { get; set; } = "broker.hivemq.com";
public int MqttBrokerPort { get; set; } = 1883;
public string MqttUsername { get; set; } = null;
public string MqttPassword { get; set; } = null;
private IMqttClient _mqttClient;
private bool _disposed = false;
/// <summary>
/// 构造函数 - 使用默认配置
/// </summary>
public MqttHelper() { }
/// <summary>
/// 构造函数 - 自定义配置
/// </summary>
public MqttHelper(string brokerUrl, int brokerPort, string username = null, string password = null)
{
MqttBrokerUrl = brokerUrl;
MqttBrokerPort = brokerPort;
MqttUsername = username;
MqttPassword = password;
}
/// <summary>
/// 连接到 MQTT Broker
/// </summary>
public async Task<bool> ConnectAsync()
{
try
{
if (_mqttClient?.IsConnected == true)
{
Console.WriteLine("MQTT 客户端已经连接");
return true;
}
var factory = new MqttClientFactory();
_mqttClient = factory.CreateMqttClient();
var options = new MqttClientOptionsBuilder()
.WithTcpServer(MqttBrokerUrl, MqttBrokerPort)
.WithCredentials(MqttUsername, MqttPassword)
.WithCleanSession()
.Build();
Console.WriteLine($"正在连接到 MQTT Broker: {MqttBrokerUrl}:{MqttBrokerPort}...");
await _mqttClient.ConnectAsync(options);
Console.WriteLine("✅ MQTT 连接成功!");
return true;
}
catch (Exception ex)
{
Console.WriteLine($"❌ MQTT 连接失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 发送消息到指定主题
/// </summary>
/// <param name="topic">目标主题</param>
/// <param name="messageContent">消息内容</param>
/// <param name="qosLevel">QoS 级别,默认为 AtLeastOnce(1)</param>
/// <param name="retain">是否保留消息,默认为 false</param>
public async Task<bool> PublishMessageAsync(string topic, string messageContent,
MqttQualityOfServiceLevel qosLevel = MqttQualityOfServiceLevel.AtLeastOnce,
bool retain = false)
{
try
{
if (_mqttClient?.IsConnected != true)
{
Console.WriteLine("MQTT 客户端未连接,尝试重新连接...");
if (!await ConnectAsync())
{
Console.WriteLine("❌ 重连失败,无法发送消息");
return false;
}
}
var message = new MqttApplicationMessageBuilder()
.WithTopic(topic)
.WithPayload(messageContent)
.WithQualityOfServiceLevel(qosLevel)
.WithRetainFlag(retain)
.Build();
Console.WriteLine($"正在向主题 '{topic}' 发送消息: {messageContent}");
await _mqttClient.PublishAsync(message);
Console.WriteLine("✅ 消息发送成功!");
return true;
}
catch (Exception ex)
{
Console.WriteLine($"❌ 消息发送失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 发送消息(使用默认主题配置)
/// </summary>
/// <param name="messageContent">消息内容</param>
/// <param name="qosLevel">QoS 级别</param>
/// <param name="retain">是否保留消息</param>
public async Task<bool> PublishMessageAsync(string messageContent,
MqttQualityOfServiceLevel qosLevel = MqttQualityOfServiceLevel.AtLeastOnce,
bool retain = false)
{
return await PublishMessageAsync("product/topic", messageContent, qosLevel, retain);
}
/// <summary>
/// 断开 MQTT 连接
/// </summary>
public async Task DisconnectAsync()
{
try
{
if (_mqttClient?.IsConnected == true)
{
await _mqttClient.DisconnectAsync();
Console.WriteLine("🔌 MQTT 已断开连接");
}
}
catch (Exception ex)
{
Console.WriteLine($"❌ 断开连接时发生错误: {ex.Message}");
}
}
/// <summary>
/// 检查是否已连接
/// </summary>
public bool IsConnected => _mqttClient?.IsConnected == true;
/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
if (!_disposed)
{
DisconnectAsync().Wait();
_mqttClient?.Dispose();
_disposed = true;
}
}
}
}