批量发送设备升级指令信息

This commit is contained in:
ChenYi 2026-01-04 16:22:49 +08:00
parent 7f0563c88d
commit ecfc7d0b4f
12 changed files with 384 additions and 4278 deletions

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,40 @@
using JiShe.ServicePro.Enums;
using System.ComponentModel.DataAnnotations;
namespace JiShe.IoT.DeviceAggregation.Dto
{
/// <summary>
/// 设备批量升级信息
/// </summary>
public class DeviceBatchUpgradeForApiInput
{
/// <summary>
/// 设备地址列表
/// </summary>
[Required]
public List<string> AddressList { get; set; }
/// <summary>
/// 物联网平台类型,默认没有指定
/// </summary>
[Required]
public IoTPlatformTypeEnum IoTPlatform { get; set; }
/// <summary>
/// 物联网平台中对应的产品Id
/// </summary>
[Required]
public string IoTPlatformProductId { get; set; }
/// <summary>
/// 固件版本信息
/// </summary>
[Required]
public Guid NowFirmwareVersionDataId { get; set; }
/// <summary>
/// 升级描述
/// </summary>
public string UpgradeDescription { get; set; }
}
}

View File

@ -55,6 +55,13 @@ namespace JiShe.IoT.DeviceAggregation
/// <returns></returns>
Task<bool> DeviceUpgradeForApiAsync(DeviceUpgradeForApiInput input);
/// <summary>
/// 批量发送设备升级指令信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<bool> DeviceBatchUpgradeForApiAsync(DeviceBatchUpgradeForApiInput input);
/// <summary>
/// 下载设备固件文件
/// </summary>

View File

@ -1,4 +1,5 @@
using JiShe.ServicePro.Enums;
using JiShe.ServicePro.Core;
using JiShe.ServicePro.Enums;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
@ -19,6 +20,11 @@ namespace JiShe.IoT.IoTPlatformAggregation.Dto
[Required]
public IoTPlatformTypeEnum IoTPlatformType { get; set; }
/// <summary>
/// 物联网平台中对应产品物模型属性或者事件类型 <see cref="DataDictionaryTypeConst"/>
/// </summary>
public string FiledType { get; set; }
/// <summary>
/// 物联网平台中对应的产品Id
/// </summary>

View File

@ -20,6 +20,7 @@ using JiShe.ServicePro.OneNETManagement.OneNETProducts;
using Mapster;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StackExchange.Redis;
using Volo.Abp;
using Volo.Abp.Content;
@ -388,7 +389,7 @@ namespace JiShe.IoT.DeviceAggregation
//数据写入遥测任务数据存储通道
if (deviceInfo.IoTPlatform == IoTPlatformTypeEnum.OneNET)
{
return await DeviceUpgradeCommandToOneNET(deviceInfo, receiveCommandInfoDto, deviceFirmwareVersionInfo, fileInfo,input);
return await DeviceUpgradeCommandToOneNET(deviceInfo, receiveCommandInfoDto, deviceFirmwareVersionInfo, fileInfo, input);
}
else if (deviceInfo.IoTPlatform == IoTPlatformTypeEnum.CTWing)
{
@ -407,6 +408,80 @@ namespace JiShe.IoT.DeviceAggregation
}
}
/// <summary>
/// 批量发送设备升级指令信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<bool> DeviceBatchUpgradeForApiAsync(DeviceBatchUpgradeForApiInput input)
{
try
{
if(input.AddressList == null || input.AddressList.Count <= 0)
{
throw new UserFriendlyException($"{nameof(DeviceBatchUpgradeForApiAsync)} 设备批量升级时地址列表不能为空","-101");
}
//固件信息
var deviceFirmwareVersionInfo = await deviceFirmwareInfoService.FindByIdAsync(new IdInput() { Id = input.NowFirmwareVersionDataId });
if (deviceFirmwareVersionInfo == null)
{
throw new UserFriendlyException($"产品Id{input.IoTPlatformProductId}的新固件信息{input.NowFirmwareVersionDataId}不存在");
}
var fileInfo = await fileAppService.GetFileAsync(new IdInput() { Id = deviceFirmwareVersionInfo.FirmwareFileId });
if (fileInfo == null)
{
throw new UserFriendlyException($"产品Id{input.IoTPlatformProductId}的新固件信息{deviceFirmwareVersionInfo.FirmwareFileName}固件文件不存在");
}
foreach (var item in input.AddressList)
{
var deviceInfo = await deviceAppService.FindByDeviceAddressAsync(item);
if (deviceInfo == null)
{
throw new UserFriendlyException($"{nameof(DeviceBatchUpgradeForApiAsync)} 设备{item}不存在", "-102");
}
//将指令存储
var receiveCommandInfoDto = new ReceiveCommandInfoDto()
{
DeviceAddress = deviceInfo.DeviceAddress,
DeviceType = deviceInfo.DeviceType,
SourceType = DeviceTelemetrySourceTypeEnum.AdminSystem,
IoTPlatform = deviceInfo.IoTPlatform,
};
var deviceUpgradeRecordInput = input.Adapt<DeviceUpgradeForApiInput>();
deviceUpgradeRecordInput.Id = deviceInfo.Id;
//数据写入遥测任务数据存储通道
if (deviceInfo.IoTPlatform == IoTPlatformTypeEnum.OneNET)
{
return await DeviceUpgradeCommandToOneNET(deviceInfo, receiveCommandInfoDto, deviceFirmwareVersionInfo, fileInfo, deviceUpgradeRecordInput);
}
else if (deviceInfo.IoTPlatform == IoTPlatformTypeEnum.CTWing)
{
//await redisPubSubService.PublishReliableAsync(DistributedMessageCenterConst.CTWingAepCommandIssuedEventName,commandRequest);
//return true;
throw new UserFriendlyException($"发送设备升级指令信息失败CTWing暂未实现。");
}
else
{
throw new UserFriendlyException($"发送设备升级指令信息失败,未找到对应的产品配置信息。");
}
}
return true;
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// 下载设备固件文件
@ -414,7 +489,34 @@ namespace JiShe.IoT.DeviceAggregation
[AllowAnonymous]
public async Task<RemoteStreamContent> DownloadFirmwareInfoAsync(IdInput input)
{
return await fileAppService.AllowDownloadAsync(input);
try
{
// Lua 脚本:获取并删除键(原子操作)
string cacheKey = string.Format($"{RedisConst.CacheDeviceUpgradeRecordDataKey}", input.Id);
var redisResult = await FreeRedisProvider.Instance.GetDelAsync(cacheKey);
if (string.IsNullOrWhiteSpace(redisResult))
{
throw new UserFriendlyException($"{nameof(DownloadFirmwareInfoAsync)} 设备升级记录信息 {input.Id} 不存在");
}
var downLoadResult = await fileAppService.AllowDownloadAsync(input);
if (downLoadResult.ContentLength.HasValue && downLoadResult.ContentLength.Value > 0)
{
//更新状态为文件下载中
await deviceUpgradeRecordService.UpdateStatusAsync(new UpdateStatusInput()
{
Id = input.Id,
UpgradeStatus = DeviceUpgradeStatusTypeEnum.Downloading,
});
}
return downLoadResult;
}
catch (Exception ex)
{
logger.LogError($"{nameof(DownloadFirmwareInfoAsync)}{input.Id}下载设备固件文件发生异常,{ex.Message}");
throw;
}
}
@ -803,23 +905,23 @@ namespace JiShe.IoT.DeviceAggregation
await ioTDBDataChannelManageService.DeviceTelemetryTaskWriterAsync(DataChannelManage.DeviceTelemetryTaskDataChannel.Writer, (DistributedMessageCenterConst.OneNETCommandIssuedEventName, packetTaskInfo));
//检查下设备是否在线
var deviceOnlineStatus = await oneNETDeviceService.DeviceInfoDetailAsync(new DeviceInfoDetailInput()
{
DeviceName = deviceInfo.IoTPlatformDeviceOpenInfo,
OneNETAccountId = deviceInfo.IoTPlatformAccountId,
ProductId = deviceInfo.IoTPlatformProductId,
});
////检查下设备是否在线
//var deviceOnlineStatus = await oneNETDeviceService.DeviceInfoDetailAsync(new DeviceInfoDetailInput()
//{
// DeviceName = deviceInfo.IoTPlatformDeviceOpenInfo,
// OneNETAccountId = deviceInfo.IoTPlatformAccountId,
// ProductId = deviceInfo.IoTPlatformProductId,
//});
if (deviceOnlineStatus == null || deviceOnlineStatus.Code != ResponeResultEnum.Success)
{
throw new UserFriendlyException("获取平台设备信息失败");
}
//if (deviceOnlineStatus == null || deviceOnlineStatus.Code != ResponeResultEnum.Success)
//{
// throw new UserFriendlyException("获取平台设备信息失败");
//}
if (deviceOnlineStatus.Data.Status != 1)
{
throw new UserFriendlyException("设备不在线");
}
//if (deviceOnlineStatus.Data.Status != 1)
//{
// throw new UserFriendlyException("设备不在线");
//}
await redisPubSubService.PublishReliableAsync(DistributedMessageCenterConst.OneNETCommandIssuedEventName, packetTaskInfo);
return true;
@ -914,7 +1016,7 @@ namespace JiShe.IoT.DeviceAggregation
}
//发送OneNET平台设备升级指令HEX格式字符串
taskInput.Commands = new Dictionary<string, object>()
taskInput.Commands = new Dictionary<string, object>()
{
{ upgradeProperty.IoTPlatformRawFieldName, upgradeRecordInput.UpgradeMessage.ToHexString() }
};
@ -925,32 +1027,19 @@ namespace JiShe.IoT.DeviceAggregation
};
var packetTaskInfo = GetDeviceTelemetryPacketTaskInfo(ioTDBOptions, commandRequest, deviceInfo.Adapt<DeviceCacheInfos>(), taskInput.Commands.Serialize());
await ioTDBDataChannelManageService.DeviceTelemetryTaskWriterAsync(DataChannelManage.DeviceTelemetryTaskDataChannel.Writer, (DistributedMessageCenterConst.OneNETCommandIssuedEventName, packetTaskInfo));
await ioTDBDataChannelManageService.DeviceTelemetryTaskWriterAsync(DataChannelManage.DeviceTelemetryTaskDataChannel.Writer, (DistributedMessageCenterConst.OneNETUpgradeCommandIssuedEventName, packetTaskInfo));
//检查下设备是否在线
var deviceOnlineStatus = await oneNETDeviceService.DeviceInfoDetailAsync(new DeviceInfoDetailInput()
{
DeviceName = deviceInfo.IoTPlatformDeviceOpenInfo,
OneNETAccountId = deviceInfo.IoTPlatformAccountId,
ProductId = deviceInfo.IoTPlatformProductId,
});
if (deviceOnlineStatus == null || deviceOnlineStatus.Code != ResponeResultEnum.Success)
{
throw new UserFriendlyException("获取平台设备信息失败");
}
if (deviceOnlineStatus.Data.Status != 1)
{
throw new UserFriendlyException("设备不在线");
}
//将升级记录数据Id存入Redis缓存中。
string cacheKey = string.Format($"{RedisConst.CacheDeviceUpgradeRecordDataKey}", upgradeRecordInput.Id);
await FreeRedisProvider.Instance.SetAsync(cacheKey, upgradeRecordInput.Id);
await redisPubSubService.PublishReliableAsync(DistributedMessageCenterConst.OneNETCommandIssuedEventName, packetTaskInfo);
//更新状态为升级中
await deviceUpgradeRecordService.UpdateAsync(new UpdateDeviceUpgradeRecordInput()
await deviceUpgradeRecordService.UpdateStatusAsync(new UpdateStatusInput()
{
Id = deviceInfo.Id,
Id = upgradeRecordInput.Id.Value,
UpgradeStatus = DeviceUpgradeStatusTypeEnum.Upgrading,
});
return true;

View File

@ -217,6 +217,10 @@ namespace JiShe.IoT.IoTPlatformAggregation
var oneNETAllThingModel = FreeRedisProvider.Instance.HGet<OneNETAllThingModel>(key, input.IoTPlatformProductId);
if (oneNETAllThingModel != null)
{
if (!string.IsNullOrWhiteSpace(input.FiledType) && input.FiledType.ToLowerInvariant().Contains("event"))
{
return OneNETAllThingModel.GetAllEventsSelectResult(oneNETAllThingModel.Events);
}
return OneNETAllThingModel.GetAllPropertiesSelectResult(oneNETAllThingModel.Properties);
}
@ -237,6 +241,11 @@ namespace JiShe.IoT.IoTPlatformAggregation
await FreeRedisProvider.Instance.HSetAsync<OneNETAllThingModel>(key, input.IoTPlatformProductId, oneNETAllThingModel);
if (!string.IsNullOrWhiteSpace(input.FiledType) && input.FiledType.ToLowerInvariant().Contains("event"))
{
return OneNETAllThingModel.GetAllEventsSelectResult(oneNETAllThingModel.Events);
}
return OneNETAllThingModel.GetAllPropertiesSelectResult(oneNETAllThingModel.Properties);
}

View File

@ -1,32 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace JiShe.IoT.Migrations
{
/// <inheritdoc />
public partial class update20251231 : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "UpgradeDescription",
table: "ServiceProDeviceUpgradeRecord",
type: "varchar(512)",
maxLength: 512,
nullable: false,
defaultValue: "",
comment: "升级描述")
.Annotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "UpgradeDescription",
table: "ServiceProDeviceUpgradeRecord");
}
}
}

View File

@ -13,8 +13,8 @@ using Volo.Abp.EntityFrameworkCore;
namespace JiShe.IoT.Migrations
{
[DbContext(typeof(IoTDbContext))]
[Migration("20251231074453_update20251231")]
partial class update20251231
[Migration("20260104061132_InitialCreate")]
partial class InitialCreate
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -297,15 +297,6 @@ namespace JiShe.IoT.Migrations
b.Property<int>("DeviceCount")
.HasColumnType("int");
b.Property<Guid?>("DeviceThingModelFileId")
.HasColumnType("char(36)")
.HasComment("设备物模型文件Id");
b.Property<string>("DeviceThingModelFileName")
.HasMaxLength(256)
.HasColumnType("varchar(256)")
.HasComment("设备物模型文件名称");
b.Property<string>("ExtraProperties")
.HasColumnType("longtext")
.HasColumnName("ExtraProperties")
@ -399,6 +390,15 @@ namespace JiShe.IoT.Migrations
.HasColumnName("TenantId")
.HasComment("租户ID");
b.Property<Guid?>("ThingModelFileId")
.HasColumnType("char(36)")
.HasComment("物模型文件Id");
b.Property<string>("ThingModelFileName")
.HasMaxLength(256)
.HasColumnType("varchar(256)")
.HasComment("物模型文件名称");
b.Property<string>("ThirdType")
.HasMaxLength(128)
.HasColumnType("varchar(128)")
@ -2275,15 +2275,6 @@ namespace JiShe.IoT.Migrations
.HasColumnType("datetime(6)")
.HasColumnName("DeletionTime");
b.Property<Guid?>("DeviceThingModelFileId")
.HasColumnType("char(36)")
.HasComment("设备物模型文件Id");
b.Property<string>("DeviceThingModelFileName")
.HasMaxLength(256)
.HasColumnType("varchar(256)")
.HasComment("设备物模型文件名称");
b.Property<string>("ExtraProperties")
.HasColumnType("longtext")
.HasColumnName("ExtraProperties")
@ -2394,6 +2385,15 @@ namespace JiShe.IoT.Migrations
.HasColumnName("TenantId")
.HasComment("租户ID");
b.Property<Guid?>("ThingModelFileId")
.HasColumnType("char(36)")
.HasComment("物模型文件Id");
b.Property<string>("ThingModelFileName")
.HasMaxLength(256)
.HasColumnType("varchar(256)")
.HasComment("物模型文件名称");
b.Property<string>("ThingModelInfos")
.HasColumnType("longtext")
.HasComment("平台物模型信息");

View File

@ -847,8 +847,8 @@ namespace JiShe.IoT.Migrations
TupDeviceModel = table.Column<string>(type: "varchar(128)", maxLength: 128, nullable: true, comment: "设备型号")
.Annotation("MySql:CharSet", "utf8mb4"),
DeviceCount = table.Column<int>(type: "int", nullable: false),
DeviceThingModelFileId = table.Column<Guid>(type: "char(36)", nullable: true, comment: "设备物模型文件Id", collation: "ascii_general_ci"),
DeviceThingModelFileName = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true, comment: "设备物模型文件名称")
ThingModelFileId = table.Column<Guid>(type: "char(36)", nullable: true, comment: "物模型文件Id", collation: "ascii_general_ci"),
ThingModelFileName = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true, comment: "物模型文件名称")
.Annotation("MySql:CharSet", "utf8mb4"),
IsEnabled = table.Column<bool>(type: "bit(1)", nullable: false, comment: "是否启用"),
ConcurrencyStamp = table.Column<string>(type: "varchar(40)", maxLength: 40, nullable: false)
@ -1137,6 +1137,8 @@ namespace JiShe.IoT.Migrations
FirmwareSignature = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: false, comment: "签名校验值")
.Annotation("MySql:CharSet", "utf8mb4"),
UpgradeResult = table.Column<int>(type: "int", nullable: true, comment: "升级结果"),
UpgradeDescription = table.Column<string>(type: "varchar(512)", maxLength: 512, nullable: false, comment: "升级描述")
.Annotation("MySql:CharSet", "utf8mb4"),
ConcurrencyStamp = table.Column<string>(type: "varchar(40)", maxLength: 40, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
CreationTime = table.Column<DateTime>(type: "datetime(6)", nullable: false),
@ -1313,8 +1315,8 @@ namespace JiShe.IoT.Migrations
.Annotation("MySql:CharSet", "utf8mb4"),
ProductAccesskey = table.Column<string>(type: "varchar(1024)", maxLength: 1024, nullable: false, comment: "产品访问密钥")
.Annotation("MySql:CharSet", "utf8mb4"),
DeviceThingModelFileId = table.Column<Guid>(type: "char(36)", nullable: true, comment: "设备物模型文件Id", collation: "ascii_general_ci"),
DeviceThingModelFileName = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true, comment: "设备物模型文件名称")
ThingModelFileId = table.Column<Guid>(type: "char(36)", nullable: true, comment: "物模型文件Id", collation: "ascii_general_ci"),
ThingModelFileName = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true, comment: "物模型文件名称")
.Annotation("MySql:CharSet", "utf8mb4"),
IsEnabled = table.Column<bool>(type: "bit(1)", nullable: false),
AccessProtocol = table.Column<int>(type: "int", nullable: false, comment: "接入协议"),

View File

@ -294,15 +294,6 @@ namespace JiShe.IoT.Migrations
b.Property<int>("DeviceCount")
.HasColumnType("int");
b.Property<Guid?>("DeviceThingModelFileId")
.HasColumnType("char(36)")
.HasComment("设备物模型文件Id");
b.Property<string>("DeviceThingModelFileName")
.HasMaxLength(256)
.HasColumnType("varchar(256)")
.HasComment("设备物模型文件名称");
b.Property<string>("ExtraProperties")
.HasColumnType("longtext")
.HasColumnName("ExtraProperties")
@ -396,6 +387,15 @@ namespace JiShe.IoT.Migrations
.HasColumnName("TenantId")
.HasComment("租户ID");
b.Property<Guid?>("ThingModelFileId")
.HasColumnType("char(36)")
.HasComment("物模型文件Id");
b.Property<string>("ThingModelFileName")
.HasMaxLength(256)
.HasColumnType("varchar(256)")
.HasComment("物模型文件名称");
b.Property<string>("ThirdType")
.HasMaxLength(128)
.HasColumnType("varchar(128)")
@ -2272,15 +2272,6 @@ namespace JiShe.IoT.Migrations
.HasColumnType("datetime(6)")
.HasColumnName("DeletionTime");
b.Property<Guid?>("DeviceThingModelFileId")
.HasColumnType("char(36)")
.HasComment("设备物模型文件Id");
b.Property<string>("DeviceThingModelFileName")
.HasMaxLength(256)
.HasColumnType("varchar(256)")
.HasComment("设备物模型文件名称");
b.Property<string>("ExtraProperties")
.HasColumnType("longtext")
.HasColumnName("ExtraProperties")
@ -2391,6 +2382,15 @@ namespace JiShe.IoT.Migrations
.HasColumnName("TenantId")
.HasComment("租户ID");
b.Property<Guid?>("ThingModelFileId")
.HasColumnType("char(36)")
.HasComment("物模型文件Id");
b.Property<string>("ThingModelFileName")
.HasMaxLength(256)
.HasColumnType("varchar(256)")
.HasComment("物模型文件名称");
b.Property<string>("ThingModelInfos")
.HasColumnType("longtext")
.HasComment("平台物模型信息");

View File

@ -113,6 +113,18 @@ namespace JiShe.IoT.Controllers
return _deviceAggregationService.DeviceUpgradeForApiAsync(input);
}
/// <summary>
/// 批量发送设备升级指令信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost(nameof(DeviceBatchUpgradeForApiAsync))]
[SwaggerOperation(summary: "批量发送设备升级指令信息", Tags = new[] { "AggregationDevice" })]
public Task<bool> DeviceBatchUpgradeForApiAsync(DeviceBatchUpgradeForApiInput input)
{
return _deviceAggregationService.DeviceBatchUpgradeForApiAsync(input);
}
/// <summary>
/// 下载设备固件文件
/// </summary>