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; }