diff --git a/ZR.Admin.WebApi/Controllers/mes/dc/DeviceUploadDataController.cs b/ZR.Admin.WebApi/Controllers/mes/dc/DeviceUploadDataController.cs
new file mode 100644
index 00000000..a3808d96
--- /dev/null
+++ b/ZR.Admin.WebApi/Controllers/mes/dc/DeviceUploadDataController.cs
@@ -0,0 +1,105 @@
+using Microsoft.AspNetCore.Mvc;
+using ZR.Model.Dto;
+using ZR.Model.Business;
+using ZR.Service.Business.IBusinessService;
+using ZR.Admin.WebApi.Extensions;
+using ZR.Admin.WebApi.Filters;
+using ZR.Service.MES.dc.IService;
+using ZR.Model.dc;
+
+//创建时间:2025-09-21
+namespace ZR.Admin.WebApi.Controllers
+{
+ ///
+ /// 设备数据上传
+ ///
+ [Route("business/DeviceUploadData")]
+ public class DeviceUploadDataController : BaseController
+ {
+ ///
+ /// 接口
+ ///
+ private readonly IDeviceUploadDataService _DeviceUploadDataService;
+
+ public DeviceUploadDataController(IDeviceUploadDataService DeviceUploadDataService)
+ {
+ _DeviceUploadDataService = DeviceUploadDataService;
+ }
+
+ ///
+ /// 查询列表
+ ///
+ ///
+ ///
+ [HttpGet("list")]
+ public IActionResult QueryDeviceUploadData([FromQuery] DeviceUploadDataQueryDto parm)
+ {
+ var response = _DeviceUploadDataService.GetList(parm);
+ return SUCCESS(response);
+ }
+
+
+ ///
+ /// 查询详情
+ ///
+ ///
+ ///
+ [HttpGet("{Id}")]
+ public IActionResult GetDeviceUploadData(long Id)
+ {
+ var response = _DeviceUploadDataService.GetInfo(Id);
+
+ var info = response.Adapt();
+ return SUCCESS(info);
+ }
+
+ ///
+ /// 添加
+ ///
+ ///
+ [HttpPost]
+ [Log(Title = "", BusinessType = BusinessType.INSERT)]
+ public IActionResult AddDeviceUploadData([FromBody] DeviceUploadDataDto parm)
+ {
+ var modal = parm.Adapt().ToCreate(HttpContext);
+
+ var response = _DeviceUploadDataService.AddDeviceUploadData(modal);
+
+ return SUCCESS(response);
+ }
+
+ ///
+ /// 更新
+ ///
+ ///
+ [HttpPut]
+ [Log(Title = "", BusinessType = BusinessType.UPDATE)]
+ public IActionResult UpdateDeviceUploadData([FromBody] DeviceUploadDataDto parm)
+ {
+ var modal = parm.Adapt().ToUpdate(HttpContext);
+ var response = _DeviceUploadDataService.UpdateDeviceUploadData(modal);
+
+ return ToResponse(response);
+ }
+
+ ///
+ /// 删除
+ ///
+ ///
+ [HttpDelete("{ids}")]
+ [Log(Title = "", BusinessType = BusinessType.DELETE)]
+ public IActionResult DeleteDeviceUploadData(string ids)
+ {
+ int[] idsArr = Tools.SpitIntArrary(ids);
+ if (idsArr.Length <= 0) { return ToResponse(ApiResult.Error($"删除失败Id 不能为空")); }
+
+ var response = _DeviceUploadDataService.Delete(idsArr);
+
+ return ToResponse(response);
+ }
+
+
+
+
+ }
+}
\ No newline at end of file
diff --git a/ZR.Admin.WebApi/Controllers/mqtt/MqttController.cs b/ZR.Admin.WebApi/Controllers/mqtt/MqttController.cs
index f829864c..6b882511 100644
--- a/ZR.Admin.WebApi/Controllers/mqtt/MqttController.cs
+++ b/ZR.Admin.WebApi/Controllers/mqtt/MqttController.cs
@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using MQTTnet.Protocol;
using ZR.Common.MqttHelper;
+using ZR.Service.mqtt;
namespace ZR.Admin.WebApi.Controllers
{
diff --git a/ZR.Admin.WebApi/Program.cs b/ZR.Admin.WebApi/Program.cs
index 53f6ea0e..13cdaf64 100644
--- a/ZR.Admin.WebApi/Program.cs
+++ b/ZR.Admin.WebApi/Program.cs
@@ -12,6 +12,7 @@ using ZR.Admin.WebApi.Hubs;
using ZR.Admin.WebApi.Middleware;
using ZR.Common.Cache;
using ZR.Common.MqttHelper;
+using ZR.Service.mqtt;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
diff --git a/ZR.Admin.WebApi/wwwroot/Generatecode/ZrAdmin.NET--0921123904.zip b/ZR.Admin.WebApi/wwwroot/Generatecode/ZrAdmin.NET--0921123904.zip
new file mode 100644
index 00000000..090d4c4f
Binary files /dev/null and b/ZR.Admin.WebApi/wwwroot/Generatecode/ZrAdmin.NET--0921123904.zip differ
diff --git a/ZR.Common/ZR.Common.csproj b/ZR.Common/ZR.Common.csproj
index 2d152dd6..0f42505c 100644
--- a/ZR.Common/ZR.Common.csproj
+++ b/ZR.Common/ZR.Common.csproj
@@ -18,5 +18,6 @@
+
diff --git a/ZR.Model/MES/dc/DeviceUploadData.cs b/ZR.Model/MES/dc/DeviceUploadData.cs
new file mode 100644
index 00000000..e50e6149
--- /dev/null
+++ b/ZR.Model/MES/dc/DeviceUploadData.cs
@@ -0,0 +1,114 @@
+
+namespace ZR.Model.dc
+{
+ ///
+ ///
+ ///
+ [SugarTable("device_upload_data")]
+ public class DeviceUploadData
+ {
+ ///
+ /// 主键
+ ///
+ [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
+ public long Id { get; set; }
+
+ ///
+ /// 工厂
+ ///
+ [SugarColumn(ColumnName = "factory_code")]
+ public string FactoryCode { get; set; }
+
+ ///
+ /// 车间
+ ///
+ [SugarColumn(ColumnName = "workshop_code")]
+ public string WorkshopCode { get; set; }
+
+ ///
+ /// 线别
+ ///
+ [SugarColumn(ColumnName = "line_code")]
+ public string LineCode { get; set; }
+
+ ///
+ /// 设备
+ ///
+ [SugarColumn(ColumnName = "device_code")]
+ public string DeviceCode { get; set; }
+
+ ///
+ /// 字典编号
+ ///
+ [SugarColumn(ColumnName = "dict_code")]
+ public string DictCode { get; set; }
+
+ ///
+ /// 备注
+ ///
+ public string Remark { get; set; }
+
+ ///
+ /// 底漆循环温度DB1014.444
+ ///
+ public string Value01 { get; set; }
+
+ ///
+ /// 底漆循环湿度DB1014.498
+ ///
+ public string Value02 { get; set; }
+
+ ///
+ /// 色漆循环温度DB1014.552
+ ///
+ public string Value03 { get; set; }
+
+ ///
+ /// 色漆循环湿度DB1014.610
+ ///
+ public string Value04 { get; set; }
+
+ ///
+ /// 清漆循环温度DB1014.664
+ ///
+ public string Value05 { get; set; }
+
+ ///
+ /// 清漆循环湿度DB1014.722
+ ///
+ public string Value06 { get; set; }
+
+ ///
+ /// 纯水电导率DB1012.220
+ ///
+ public string Value07 { get; set; }
+
+ ///
+ /// 水份烘干温度DB1014.776
+ ///
+ public string Value08 { get; set; }
+
+ ///
+ /// 清漆烘干温度DB1014.892
+ ///
+ public string Value09 { get; set; }
+
+ ///
+ /// Value10
+ ///
+ public string Value10 { get; set; }
+
+ ///
+ /// 上传时间
+ ///
+ [SugarColumn(ColumnName = "upload_time")]
+ public DateTime UploadTime { get; set; } = DateTime.Now;
+
+ ///
+ /// 采集时间
+ ///
+ [SugarColumn(ColumnName = "collection_time")]
+ public DateTime CollectionTime { get; set; } = DateTime.Now;
+
+ }
+}
\ No newline at end of file
diff --git a/ZR.Model/MES/dc/Dto/DeviceUploadDataDto.cs b/ZR.Model/MES/dc/Dto/DeviceUploadDataDto.cs
new file mode 100644
index 00000000..18be1f49
--- /dev/null
+++ b/ZR.Model/MES/dc/Dto/DeviceUploadDataDto.cs
@@ -0,0 +1,129 @@
+using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
+
+namespace ZR.Model.Dto
+{
+ ///
+ /// 查询对象
+ ///
+ public class DeviceUploadDataQueryDto : PagerInfo
+ {
+ }
+ // 网关发来数据
+ public class DeviceUploadDataGatWayDto
+ {
+ [JsonPropertyName("time")]
+ public long Time { get; set; }
+ [JsonPropertyName("params")]
+ public DeviceUploadDataParamsDto DeviceParams { get; set; }
+ }
+ ///
+ /// 传来参数对象
+ ///
+ public class DeviceUploadDataParamsDto
+ {
+ ///
+ /// 底漆循环温度DB1014.444
+ ///
+ [JsonPropertyName("Value01")]
+ public decimal Value01 { get; set; }
+
+ ///
+ /// 底漆循环湿度DB1014.498
+ ///
+ [JsonPropertyName("Value02")]
+ public decimal Value02 { get; set; }
+
+ ///
+ /// 色漆循环温度DB1014.552
+ ///
+ [JsonPropertyName("Value03")]
+ public decimal Value03 { get; set; }
+
+ ///
+ /// 色漆循环湿度DB1014.610
+ ///
+ [JsonPropertyName("Value04")]
+ public decimal Value04 { get; set; }
+
+ ///
+ /// 清漆循环温度DB1014.664
+ ///
+ [JsonPropertyName("Value05")]
+ public decimal Value05 { get; set; }
+
+ ///
+ /// 清漆循环湿度DB1014.722
+ ///
+ [JsonPropertyName("Value06")]
+ public decimal Value06 { get; set; }
+
+ ///
+ /// 纯水电导率DB1012.220
+ ///
+ [JsonPropertyName("Value07")]
+ public decimal Value07 { get; set; }
+
+ ///
+ /// 水份烘干温度DB1014.776
+ ///
+ [JsonPropertyName("Value08")]
+ public decimal Value08 { get; set; }
+
+ ///
+ /// 清漆烘干温度DB1014.892
+ ///
+ [JsonPropertyName("Value09")]
+ public decimal Value09 { get; set; }
+
+ ///
+ /// Value10
+ ///
+ [JsonPropertyName("Value10")]
+ public decimal Value10 { get; set; }
+ }
+
+ ///
+ /// 输入输出对象
+ ///
+ public class DeviceUploadDataDto
+ {
+ [Required(ErrorMessage = "主键不能为空")]
+ public long Id { get; set; }
+
+ public string FactoryCode { get; set; }
+
+ public string WorkshopCode { get; set; }
+
+ public string LineCode { get; set; }
+
+ public string DeviceCode { get; set; }
+
+ public string DictCode { get; set; }
+
+ public string Remark { get; set; }
+
+ public string Value01 { get; set; }
+
+ public string Value02 { get; set; }
+
+ public string Value03 { get; set; }
+
+ public string Value04 { get; set; }
+
+ public string Value05 { get; set; }
+
+ public string Value06 { get; set; }
+
+ public string Value07 { get; set; }
+
+ public string Value08 { get; set; }
+
+ public string Value09 { get; set; }
+
+ public string Value10 { get; set; }
+
+
+
+ }
+}
\ No newline at end of file
diff --git a/ZR.Service/ZR.Service.csproj b/ZR.Service/ZR.Service.csproj
index 5d766791..dcda7a1c 100644
--- a/ZR.Service/ZR.Service.csproj
+++ b/ZR.Service/ZR.Service.csproj
@@ -23,7 +23,6 @@
-
diff --git a/ZR.Service/mes/dc/DeviceUploadDataService.cs b/ZR.Service/mes/dc/DeviceUploadDataService.cs
new file mode 100644
index 00000000..830229f5
--- /dev/null
+++ b/ZR.Service/mes/dc/DeviceUploadDataService.cs
@@ -0,0 +1,89 @@
+using Infrastructure.Attribute;
+using SqlSugar;
+using ZR.Model;
+using ZR.Model.dc;
+using ZR.Model.Dto;
+using ZR.Repository;
+using ZR.Service.MES.dc.IService;
+
+namespace ZR.Service.Business
+{
+ ///
+ /// Service业务层处理
+ ///
+ [AppService(ServiceType = typeof(IDeviceUploadDataService), ServiceLifetime = LifeTime.Transient)]
+ public class DeviceUploadDataService : BaseService, IDeviceUploadDataService
+ {
+ ///
+ /// 查询列表
+ ///
+ ///
+ ///
+ public PagedInfo GetList(DeviceUploadDataQueryDto parm)
+ {
+ var predicate = Expressionable.Create();
+
+ var response = Queryable()
+ .Where(predicate.ToExpression())
+ .ToPage(parm);
+
+ return response;
+ }
+
+
+ ///
+ /// 获取详情
+ ///
+ ///
+ ///
+ public DeviceUploadData GetInfo(long Id)
+ {
+ var response = Queryable()
+ .Where(x => x.Id == Id)
+ .First();
+
+ return response;
+ }
+
+ ///
+ /// 添加
+ ///
+ ///
+ ///
+ public DeviceUploadData AddDeviceUploadData(DeviceUploadData model)
+ {
+ return Context.Insertable(model).ExecuteReturnEntity();
+ }
+
+ ///
+ /// 修改
+ ///
+ ///
+ ///
+ public int UpdateDeviceUploadData(DeviceUploadData model)
+ {
+ //var response = Update(w => w.Id == model.Id, it => new DeviceUploadData()
+ //{
+ // FactoryCode = model.FactoryCode,
+ // WorkshopCode = model.WorkshopCode,
+ // LineCode = model.LineCode,
+ // DeviceCode = model.DeviceCode,
+ // DictCode = model.DictCode,
+ // Remark = model.Remark,
+ // Value01 = model.Value01,
+ // Value02 = model.Value02,
+ // Value03 = model.Value03,
+ // Value04 = model.Value04,
+ // Value05 = model.Value05,
+ // Value06 = model.Value06,
+ // Value07 = model.Value07,
+ // Value08 = model.Value08,
+ // Value09 = model.Value09,
+ // Value10 = model.Value10,
+ //});
+ //return response;
+ return Update(model, true);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/ZR.Service/mes/dc/IService/IDeviceUploadDataService.cs b/ZR.Service/mes/dc/IService/IDeviceUploadDataService.cs
new file mode 100644
index 00000000..353df758
--- /dev/null
+++ b/ZR.Service/mes/dc/IService/IDeviceUploadDataService.cs
@@ -0,0 +1,21 @@
+using ZR.Model;
+using ZR.Model.dc;
+using ZR.Model.Dto;
+
+namespace ZR.Service.MES.dc.IService
+{
+ ///
+ /// service接口
+ ///
+ public interface IDeviceUploadDataService : IBaseService
+ {
+ PagedInfo GetList(DeviceUploadDataQueryDto parm);
+
+ DeviceUploadData GetInfo(long Id);
+
+ DeviceUploadData AddDeviceUploadData(DeviceUploadData parm);
+
+ int UpdateDeviceUploadData(DeviceUploadData parm);
+
+ }
+}
diff --git a/ZR.Service/mes/qc/backend/QcBackEndService.cs b/ZR.Service/mes/qc/backend/QcBackEndService.cs
index 881aa94a..802dab2f 100644
--- a/ZR.Service/mes/qc/backend/QcBackEndService.cs
+++ b/ZR.Service/mes/qc/backend/QcBackEndService.cs
@@ -11,6 +11,7 @@ using ZR.Model.Business;
using ZR.Model.Dto;
using ZR.Model.MES.wms;
using ZR.Service.Business.IBusinessService;
+using ZR.Service.mqtt;
namespace ZR.Service.Business
{
diff --git a/ZR.Common/MqttHelper/MqttService.cs b/ZR.Service/mqtt/MqttService.cs
similarity index 74%
rename from ZR.Common/MqttHelper/MqttService.cs
rename to ZR.Service/mqtt/MqttService.cs
index da71e4e1..310a558d 100644
--- a/ZR.Common/MqttHelper/MqttService.cs
+++ b/ZR.Service/mqtt/MqttService.cs
@@ -1,19 +1,24 @@
-using Microsoft.Extensions.Configuration;
+using Infrastructure.Extensions;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Protocol;
using System;
-using System.Collections.Generic;
using System.Linq;
using System.Text;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
+using ZR.Common.MqttHelper;
+using ZR.Model.dc;
+using ZR.Model.Dto;
+using ZR.Model.mes.md;
-namespace ZR.Common.MqttHelper
+namespace ZR.Service.mqtt
{
- public class MqttService : IHostedService, IDisposable
+ public class MqttService :BaseService, IHostedService, IDisposable
{
private readonly ILogger _logger;
private readonly IConfiguration _configuration;
@@ -168,15 +173,9 @@ namespace ZR.Common.MqttHelper
{
_logger.LogInformation($"MQTT连接已建立,会话是否存在: {e.ConnectResult.IsSessionPresent}");
- // 仅在会话不存在时订阅(首次连接或会话失效)
- if (!e.ConnectResult.IsSessionPresent)
- {
- await SubscribeToTopicsAsync();
- }
- else
- {
- _logger.LogInformation("会话已存在,服务器保留订阅状态,跳过订阅");
- }
+ // 无论会话是否存在,都检查并确保订阅配置的主题
+ // 这解决了配置文件修改后重启服务不订阅新主题的问题
+ await SubscribeToTopicsAsync();
}
private async Task OnDisconnectedAsync(MqttClientDisconnectedEventArgs e)
@@ -220,7 +219,7 @@ namespace ZR.Common.MqttHelper
// 这里可以根据主题路由消息到不同的处理程序
switch (topic)
{
- case string t when t.StartsWith("devices/"):
+ case string t when t.StartsWith("shgx_tz/device/"):
await HandleDeviceMessage(topic, payload);
break;
case "system/alert":
@@ -256,17 +255,71 @@ namespace ZR.Common.MqttHelper
// 提取需要订阅的主题
var topicsToSubscribe = subscribeOptions.TopicFilters.Select(f => f.Topic).ToList();
+ var topicFilters = subscribeOptions.TopicFilters.ToList();
- // 检查是否已订阅所有主题,避免重复
- if (_subscribedTopics.SetEquals(topicsToSubscribe))
+ // 获取当前已订阅的主题
+ HashSet currentlySubscribedTopics;
+ lock (_subscribedTopics)
{
- _logger.LogInformation("所有主题已订阅,跳过订阅操作");
+ currentlySubscribedTopics = new HashSet(_subscribedTopics);
+ }
+
+ // 检查是否有新的主题需要订阅
+ var newTopics = topicsToSubscribe.Except(currentlySubscribedTopics).ToList();
+ var removedTopics = currentlySubscribedTopics.Except(topicsToSubscribe).ToList();
+
+ if (!newTopics.Any() && !removedTopics.Any())
+ {
+ _logger.LogInformation("所有主题已正确订阅,无需变更");
return;
}
- // 执行订阅并更新本地状态
- _logger.LogInformation($"订阅主题: {string.Join(", ", topicsToSubscribe)}");
- var result = await _mqttClient.SubscribeAsync(subscribeOptions);
+ // 处理新增的主题
+ if (newTopics.Any())
+ {
+ var newTopicFilters = topicFilters.Where(f => newTopics.Contains(f.Topic)).ToList();
+ if (newTopicFilters.Any())
+ {
+ var newSubscribeOptionsBuilder = new MqttClientSubscribeOptionsBuilder();
+
+ foreach (var topicFilter in newTopicFilters)
+ {
+ newSubscribeOptionsBuilder.WithTopicFilter(topicFilter);
+ }
+
+ var newSubscribeOptions = newSubscribeOptionsBuilder.Build();
+
+ _logger.LogInformation($"订阅新主题: {string.Join(", ", newTopics)}");
+ var result = await _mqttClient.SubscribeAsync(newSubscribeOptions);
+
+ foreach (var item in result.Items)
+ {
+ _logger.LogInformation($"订阅结果:{item.TopicFilter.Topic} -> {item.ResultCode}");
+ }
+ }
+ }
+
+ // 处理移除的主题(可选,根据业务需求决定是否取消订阅)
+ if (removedTopics.Any())
+ {
+ _logger.LogInformation($"配置中移除的主题: {string.Join(", ", removedTopics)}");
+ // 注意:这里没有执行取消订阅操作,因为有些场景下可能希望保留历史订阅
+ // 如果需要取消订阅,请取消注释下面的代码
+
+ // 使用构建器模式创建取消订阅选项
+ var unsubscribeOptionsBuilder = new MqttClientUnsubscribeOptionsBuilder();
+
+ // 为每个要取消订阅的主题调用WithTopic方法
+ foreach (var topic in removedTopics)
+ {
+ unsubscribeOptionsBuilder.WithTopicFilter(topic);
+ }
+
+ var unsubscribeOptions = unsubscribeOptionsBuilder.Build();
+ await _mqttClient.UnsubscribeAsync(unsubscribeOptions);
+ _logger.LogInformation($"已取消订阅: {string.Join(", ", removedTopics)}");
+
+ }
// 更新本地已订阅主题列表
lock (_subscribedTopics)
@@ -274,11 +327,6 @@ namespace ZR.Common.MqttHelper
_subscribedTopics.Clear();
_subscribedTopics.UnionWith(topicsToSubscribe);
}
-
- foreach (var item in result.Items)
- {
- _logger.LogInformation($"订阅结果:{item.TopicFilter.Topic} -> {item.ResultCode}");
- }
}
private void ScheduleReconnect()
@@ -320,7 +368,32 @@ namespace ZR.Common.MqttHelper
private Task HandleDeviceMessage(string topic, string payload)
{
_logger.LogInformation($"处理设备消息: {topic} - {payload}");
+
+ DeviceUploadDataGatWayDto deviceUploadDataGatWayDto = JsonSerializer.Deserialize(payload);
// 这里添加设备消息处理逻辑
+ string deviceCode = topic.Split("/")[2];
+ DeviceUploadData deviceUploadData = new()
+ {
+ FactoryCode = "上海干巷",
+ WorkshopCode = "涂装车间",
+ LineCode = "涂装生产线",
+ DeviceCode = deviceCode,
+ DictCode = "device_dict_plc_001",
+ Remark = "网关采集设备数据",
+ UploadTime = DateTime.Now,
+ CollectionTime = DateTimeOffset.FromUnixTimeMilliseconds(deviceUploadDataGatWayDto.Time).LocalDateTime,
+ Value01 = deviceUploadDataGatWayDto.DeviceParams.Value01.ToString(),
+ Value02 = deviceUploadDataGatWayDto.DeviceParams.Value02.ToString(),
+ Value03 = deviceUploadDataGatWayDto.DeviceParams.Value03.ToString(),
+ Value04 = deviceUploadDataGatWayDto.DeviceParams.Value04.ToString(),
+ Value05 = deviceUploadDataGatWayDto.DeviceParams.Value05.ToString(),
+ Value06 = deviceUploadDataGatWayDto.DeviceParams.Value06.ToString(),
+ Value07 = deviceUploadDataGatWayDto.DeviceParams.Value07.ToString(),
+ Value08 = deviceUploadDataGatWayDto.DeviceParams.Value08.ToString(),
+ Value09 = deviceUploadDataGatWayDto.DeviceParams.Value09.ToString(),
+ Value10 = deviceUploadDataGatWayDto.DeviceParams.Value10.ToString()
+ };
+ Context.Insertable(deviceUploadData).ExecuteCommand();
return Task.CompletedTask;
}