JiShe.IOT.Admin/src/JiShe.IoT.Application/IoTDeviceBasicAppService.cs

850 lines
42 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using JiShe.IoT.DeviceAggregation;
using JiShe.IoT.DeviceAggregation.Dto;
using JiShe.ServicePro;
using JiShe.ServicePro.ApacheIoTDB.Provider.Options;
using JiShe.ServicePro.Core;
using JiShe.ServicePro.DataChannelManages;
using JiShe.ServicePro.DeviceManagement.DeviceInfos;
using JiShe.ServicePro.DeviceManagement.DeviceInfos.Dto;
using JiShe.ServicePro.DeviceManagement.ThingModels;
using JiShe.ServicePro.Dto;
using JiShe.ServicePro.Encrypt;
using JiShe.ServicePro.Enums;
using JiShe.ServicePro.FileManagement.Files;
using JiShe.ServicePro.FreeRedisProvider;
using JiShe.ServicePro.FreeSqlProvider;
using JiShe.ServicePro.IoTDBManagement.DataChannels;
using JiShe.ServicePro.IoTDBManagement.TableModels;
using JiShe.ServicePro.IoTDBManagement.TreeModels;
using JiShe.ServicePro.OneNET.Provider.OpenApiModels.Commands;
using JiShe.ServicePro.OneNETManagement.OneNETDevices;
using JiShe.ServicePro.OneNETManagement.OneNETProducts;
using Mapster;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp;
namespace JiShe.IoT
{
/// <summary>
/// 设备基础服务
/// </summary>
public abstract class IoTDeviceBasicAppService : IoTAppService
{
protected readonly ILogger<IoTDeviceBasicAppService> logger;
protected readonly IDeviceAppService deviceAppService;
protected readonly IOneNETDeviceService oneNETDeviceService;
protected readonly IReliableRedisPubSubService redisPubSubService;
protected readonly IIoTDBDataChannelManageService ioTDBDataChannelManageService;
protected readonly IoTDBOptions ioTDBOptions;
protected readonly ServerApplicationOptions serverApplicationOptions;
protected readonly IOneNETProductService oneNETProductService;
protected readonly IDeviceThingModelManagementAppService deviceThingModelService;
protected readonly IIoTPlatformThingModelInfoAppService platformThingModelInfoAppService;
protected readonly IDeviceUpgradeRecordService deviceUpgradeRecordService;
protected readonly ITreeModelService treeModelService;
/// <param name="logger"></param>
/// <param name="deviceAppService">设备服务</param>
/// <param name="oneNETDeviceService">OneNET设备服务</param>
/// <param name="redisPubSubService">Redis发布订阅服务</param>
/// <param name="ioTDBDataChannelManageService">数据通道</param>
/// <param name="_ioTDBOptions">IoTDBOptions</param>
/// <param name="oneNETProductService">OneNET产品服务</param>
/// <param name="deviceThingModelService">设备端物模型服务</param>
/// <param name="platformThingModelInfoAppService">平台端端物模型服务</param>
/// <param name="deviceUpgradeRecordService">设备升级记录服务</param>
/// <param name="_serverOptions">应用服务配置</param>
/// <param name="treeModelService">IoTDB树模型服务</param>
protected IoTDeviceBasicAppService(
ILogger<IoTDeviceBasicAppService> logger,
IDeviceAppService deviceAppService,
IOneNETDeviceService oneNETDeviceService,
IReliableRedisPubSubService redisPubSubService,
IIoTDBDataChannelManageService ioTDBDataChannelManageService,
IOptions<IoTDBOptions> _ioTDBOptions,
IOptions<ServerApplicationOptions> _serverOptions,
IOneNETProductService oneNETProductService,
IDeviceThingModelManagementAppService deviceThingModelService,
IIoTPlatformThingModelInfoAppService platformThingModelInfoAppService,
IDeviceUpgradeRecordService deviceUpgradeRecordService,
ITreeModelService treeModelService)
{
this.logger = logger;
this.deviceAppService = deviceAppService;
this.oneNETDeviceService = oneNETDeviceService;
this.redisPubSubService = redisPubSubService;
this.ioTDBDataChannelManageService = ioTDBDataChannelManageService;
this.ioTDBOptions = _ioTDBOptions.Value;
this.serverApplicationOptions = _serverOptions.Value;
this.oneNETProductService = oneNETProductService;
this.deviceThingModelService = deviceThingModelService;
this.platformThingModelInfoAppService = platformThingModelInfoAppService;
this.deviceUpgradeRecordService = deviceUpgradeRecordService;
this.treeModelService = treeModelService;
}
#region OneNET
/// <summary>
/// OneNET设备创建
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
protected async Task<bool> OneNETDeviceCreateAsync(CreateDeviceAggregationInput input)
{
try
{
CreateDeviceInput createDeviceInput = input.Adapt<CreateDeviceInput>();
var productInfo = await oneNETProductService.GetProductInfoAsync(new IdInput<string>() { Id = input.IoTPlatformProductId });
if (productInfo == null)
{
throw new UserFriendlyException($"OneNET创建设备失败未找到对应的产品配置信息。");
}
if (input.DeviceSourceType == DeviceSourceTypeEnum.Workshop && !productInfo.IsEnabled) //车间生产推送,必须是已经启用的产品才可以
{
throw new UserFriendlyException($"车间生产推送OneNET创建设备失败产品未启用。");
}
createDeviceInput.DeviceName = input.DeviceAddress;
createDeviceInput.IoTPlatformAccountId = productInfo.OneNETAccountId;
createDeviceInput.IoTPlatformDeviceOpenInfo = $"{input.IoTPlatformProductId}{input.DeviceAddress}";
createDeviceInput.PlatformPassword = productInfo.ProductAccesskey;
createDeviceInput.IoTPlatformProductName = productInfo.ProductName;
createDeviceInput.AccountPhoneNumber = productInfo.AccountPhoneNumber;
if (input.DeviceType.HasValue)
{
createDeviceInput.DeviceType = input.DeviceType.Value;
}
var insertResult = await deviceAppService.CreateAsync(createDeviceInput);
if (insertResult == null)
{
logger.LogError($"{nameof(OneNETDeviceCreateAsync)} 添加设备信息失败:{input.Serialize()}");
return false;
}
//推送至OneNET平台
var pushResult = await oneNETDeviceService.CreateDeviceInfoAsync(new CreateDeviceInfoInput()
{
DeviceName = createDeviceInput.IoTPlatformDeviceOpenInfo,
ProductId = productInfo.IoTPlatformProductId,
OneNETAccountId = productInfo.OneNETAccountId,
Description = input.DeviceAddress,
});
if (pushResult == null || pushResult.Code != ServicePro.Enums.ResponeResultEnum.Success || pushResult.Data == null)
{
logger.LogError($"{nameof(OneNETDeviceCreateAsync)} 推送设备信息失败:{pushResult.Serialize()}");
return false;
}
//更新OneNET平台推送结果
await DeviceUpdateHandler(insertResult, pushResult, pushResult.Data.SecurityKey);
return true;
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// OneNET设备批量创建
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
protected async Task<bool> OneNETDeviceBatchCreateAsync(BatchCreateDeviceAggregationInput input)
{
try
{
var productInfo = await FreeSqlDbContext.Instance.Select<OneNETProductInfos>()
.Where(e => e.IoTPlatformProductId == input.IoTPlatformProductId)//此处不需要过滤产品状态,方便测试产品配置信息是否准确,避免跟车间生产搞混
.WhereIf(input.DeviceSourceType == DeviceSourceTypeEnum.Workshop, e => e.IsEnabled == true)
.FirstAsync();
if (productInfo == null)
{
throw new UserFriendlyException($"批量创建设备失败,未找到对应的产品配置信息。");
}
BatchCreateDeviceInput batchCreateDeviceInput = new BatchCreateDeviceInput()
{
IoTPlatform = input.IoTPlatform,
AddressList = input.AddressList,
DeviceInputs = new List<CreateDeviceInput>()
};
//检查网关或者直连设备信息是否已经存在
var checkDevicesInfos = await deviceAppService.FindByDeviceAddressAsync(new FindByDeviceAddressInput()
{
AddressList = input.AddressList,
IoTPlatform = input.IoTPlatform,
IoTPlatformProductId = input.IoTPlatformProductId,
});
foreach (var item in input.DeviceInfos)
{
if (checkDevicesInfos != null)
{
var checkDevices = checkDevicesInfos.Where(e => e.DeviceAddress == item.DeviceAddress).FirstOrDefault();
if (checkDevices != null && checkDevices.IsPlatformPushSuccess)
{
logger.LogError($"{nameof(OneNETDeviceBatchCreateAsync)} 平台{input.IoTPlatform} 产品 {input.IoTPlatformProductId} 下设备信息已存在:{item.DeviceAddress}");
continue;
}
}
CreateDeviceInput createDeviceInput = input.Adapt<CreateDeviceInput>();
createDeviceInput.DeviceName = item.DeviceAddress;
createDeviceInput.DeviceAddress = item.DeviceAddress;
createDeviceInput.IoTPlatformAccountId = productInfo.OneNETAccountId;
createDeviceInput.IoTPlatformDeviceOpenInfo = $"{input.IoTPlatformProductId}{item.DeviceAddress}";
createDeviceInput.PlatformPassword = productInfo.ProductAccesskey;
createDeviceInput.IoTPlatformProductName = productInfo.ProductName;
createDeviceInput.AccountPhoneNumber = productInfo.AccountPhoneNumber;
if (input.DeviceSourceType.HasValue)
{
createDeviceInput.DeviceSourceType = input.DeviceSourceType.Value;
}
if (input.DeviceType.HasValue)
{
createDeviceInput.DeviceType = input.DeviceType.Value;
}
if (item.BusinessSystemDeviceDataId.HasValue)
{
createDeviceInput.BusinessSystemDeviceDataId = item.BusinessSystemDeviceDataId.Value;
}
batchCreateDeviceInput.DeviceInputs.Add(createDeviceInput);
}
if (batchCreateDeviceInput.DeviceInputs != null || batchCreateDeviceInput.DeviceInputs.Count > 0)
{
var insertResult = await deviceAppService.BatchCreateAsync(batchCreateDeviceInput);
if (insertResult == null)
{
logger.LogError($"{nameof(OneNETDeviceBatchCreateAsync)} OneNET设备批量创建添加设备信息失败{input.Serialize()}");
return false;
}
//网关或者直连设备 推送至OneNET平台
var oneNETBatchCreateDeviceInfoInput = new BatchCreateDeviceInfoInput()
{
ProductId = productInfo.IoTPlatformProductId,
OneNETAccountId = productInfo.OneNETAccountId,
DeviceList = new List<string>()
};
oneNETBatchCreateDeviceInfoInput.DeviceList = batchCreateDeviceInput.DeviceInputs.Select(d => d.IoTPlatformDeviceOpenInfo).ToList();
var pushResult = await oneNETDeviceService.BatchCreateDeviceInfoAsync(oneNETBatchCreateDeviceInfoInput);
if (pushResult == null || pushResult.Code != ServicePro.Enums.ResponeResultEnum.Success)
{
logger.LogError($"{nameof(OneNETDeviceBatchCreateAsync)} 推送设备信息失败:{pushResult.Serialize()}");
return false;
}
//更新OneNET平台推送结果
foreach (var item in insertResult)
{
var successEntity = pushResult.Data.Successlist.Where(d => d.DeviceName == item.IoTPlatformDeviceOpenInfo).FirstOrDefault();
if (successEntity != null)
{
await DeviceUpdateHandler(item, HttpDataResultExtensions.Success(successEntity), successEntity.SecurityKey);
}
}
}
//暂不考虑子设备地址信息,由网关设备主动上报
return true;
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// 重新推送设备信息到OneNET物联网平台
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
public async Task<DeviceManagementInfoDto> RepushDeviceInfoToOneNET(DeviceManagementInfoDto input)
{
try
{
//检查OneNET平台设备是否已经存在
var oneNETDeviceInfoResult = await oneNETDeviceService.DeviceInfoDetailAsync(new DeviceInfoDetailInput()
{
DeviceName = input.IoTPlatformDeviceOpenInfo,
ProductId = input.IoTPlatformProductId,
OneNETAccountId = input.IoTPlatformAccountId,
});
if (oneNETDeviceInfoResult != null && oneNETDeviceInfoResult.Code == ServicePro.Enums.ResponeResultEnum.Success)
{
throw new UserFriendlyException($"OneNET推送失败账号产品下{input.IoTPlatformProductId}已经存在该设备{input.DeviceAddress}。");
}
//推送至OneNET平台
var pushResult = await oneNETDeviceService.CreateDeviceInfoAsync(new CreateDeviceInfoInput()
{
DeviceName = input.IoTPlatformDeviceOpenInfo,
ProductId = input.IoTPlatformProductId,
OneNETAccountId = input.IoTPlatformAccountId,
Description = input.DeviceAddress,
});
if (pushResult == null || pushResult.Code != ServicePro.Enums.ResponeResultEnum.Success || pushResult.Data == null)
{
logger.LogError($"{nameof(RepushDeviceInfoToOneNET)} 推送设备信息失败:{pushResult.Serialize()}");
throw new UserFriendlyException($"平台请求失败。");
}
return await DeviceUpdateHandler(input, pushResult, pushResult.Data.SecurityKey);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// 删除OneNET平台设备
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
public async Task<bool> DeleteDeviceInfoToOneNET(DeviceManagementInfoDto input)
{
try
{
//删除OneNET平台设备信息
var deleteResult = await oneNETDeviceService.DeleteDeviceInfoAsync(new DeleteDeviceInfoInput()
{
DeviceName = input.IoTPlatformDeviceOpenInfo,
ProductId = input.IoTPlatformProductId,
OneNETAccountId = input.IoTPlatformAccountId,
});
if (deleteResult == null || deleteResult.Code != ServicePro.Enums.ResponeResultEnum.Success)
{
logger.LogError($"{nameof(DeleteDeviceInfoToOneNET)} 删除设备信息失败:{deleteResult.Serialize()}");
throw new UserFriendlyException($"删除设备平台请求失败。");
}
var localDeleteReult = await deviceAppService.DeleteAsync(new IdInput() { Id = input.Id });
if (localDeleteReult == false)
{
throw new UserFriendlyException($"平台设备信息删除成功,系统删除设备失败。");
}
return localDeleteReult;
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// 发送OneNET平台设备指令
/// </summary>
/// <param name="deviceInfo">设备信息</param>
/// <param name="input">指令信息</param>
/// <param name="platformThingModelInfos">平台端物模型信息</param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
protected async Task<bool> DeviceCommandInfoToOneNET(DeviceManagementInfoDto deviceInfo, ReceiveCommandInfoDto input, List<IoTPlatformThingModelInfoDto> platformThingModelInfos = null)
{
try
{
//获取设备对应的平台端物模型信息,校验前端传入的属性标识集合是否存在不合法的属性标识符
if (platformThingModelInfos == null || platformThingModelInfos.Count <= 0)
{
platformThingModelInfos = await platformThingModelInfoAppService.FindByPlatformProductIdAsync(new IdInput<string>() { Id = deviceInfo.IoTPlatformProductId });
}
if (platformThingModelInfos == null || platformThingModelInfos.Count <= 0)
{
throw new UserFriendlyException($"设备{deviceInfo.DeviceAddress}的平台端物模型信息不存在。");
}
foreach (var item in input.Commands)
{
var tempPlatformThingModelInfo = platformThingModelInfos.Where(d => d.IoTPlatformRawFieldName == item.Key).FirstOrDefault();
if (tempPlatformThingModelInfo == null)
{
throw new UserFriendlyException($"设备{deviceInfo.DeviceAddress}平台端物模型信息不存在属性标识符{item.Key}。");
}
//排除升级指令
if (tempPlatformThingModelInfo.StandardFieldName.ToLowerInvariant() == ThingModelFixedTypeConst.FIRMWARE_UPGRADE.ToLowerInvariant())
{
throw new UserFriendlyException($"设备{deviceInfo.DeviceAddress}平台端物模型属性标识符{item.Key}是升级指令,此处不允许下发。");
}
}
//检查设备是否有配置设备端物模型信息
//如果有配置就检查指令字典中是否有SpecialCommand标识符
//如果有就需要构建 SpecialCommand 的特别指令
if (deviceInfo.IsNeedConfigDeviceModel && deviceInfo.DeviceThingModelDataId.HasValue && input.Commands.ContainsKey(ThingModelFixedTypeConst.SpecialCommand))
{
var propertyInfo = await oneNETProductService.GetProductThingModelSpecialCommandDataTypeListAsync(new IdInput<string>() { Id = deviceInfo.IoTPlatformProductId });
if (propertyInfo == null)
{
throw new UserFriendlyException($"{nameof(DeviceCommandInfoToOneNET)} OneNET设备属性设置失败产品Id{deviceInfo.IoTPlatformProductId}未找到对应的属性信息。");
}
Dictionary<string, string> tempSpecialCommand = await deviceThingModelService.BuildThingModelSpecialCommandAsync(propertyInfo, deviceInfo.DeviceThingModelDataId.Value);
input.Commands[ThingModelFixedTypeConst.SpecialCommand] = tempSpecialCommand;
}
else if (input.Commands.ContainsKey(ThingModelFixedTypeConst.SpecialCommand))
{
//设备端物模型解除绑定情况下SpecialCommand 还原默认值,设备也会恢复默认采集方式
//给SpecialCommand第一个参数设置默认值
var propertyInfo = await oneNETProductService.GetProductThingModelSpecialCommandDataTypeListAsync(new IdInput<string>() { Id = deviceInfo.IoTPlatformProductId });
if (propertyInfo == null)
{
throw new UserFriendlyException($"{nameof(DeviceCommandInfoToOneNET)} OneNET设备属性设置失败产品Id{deviceInfo.IoTPlatformProductId}未找到对应的属性信息。");
}
string paName = propertyInfo.FirstOrDefault()?.Identifier;
input.Commands[ThingModelFixedTypeConst.SpecialCommand] = new Dictionary<string, object>() {
{ paName, CommonConst.SpecialCommandDefaultValue }
};
logger.LogWarning($"{nameof(DeviceCommandInfoToOneNET)} OneNET平台设备{deviceInfo.DeviceAddress}特殊抄读指令还原为{CommonConst.SpecialCommandDefaultValue}");
}
var commandRequest = new OpenApiRequest()
{
Message = input.Serialize(),
};
var packetTaskInfo = GetDeviceTelemetryPacketTaskInfo(ioTDBOptions, commandRequest, deviceInfo.Adapt<DeviceCacheInfos>(), input.Commands.Serialize());
await ioTDBDataChannelManageService.DeviceTelemetryTaskWriterAsync(DataChannelManage.DeviceTelemetryTaskDataChannel.Writer, (DistributedMessageCenterConst.OneNETCommandIssuedEventName, packetTaskInfo));
await redisPubSubService.PublishReliableAsync(DistributedMessageCenterConst.OneNETCommandIssuedEventName, packetTaskInfo);
return true;
}
catch (Exception ex)
{
logger.LogError($"{nameof(DeviceCommandInfoToOneNET)} 发送OneNET设备指令信息发生异常设备地址{deviceInfo.DeviceAddress},异常信息:{ex.Message}");
throw;
}
}
/// <summary>
/// 发送OneNET平台设备升级指令
/// </summary>
/// <param name="deviceInfo"></param>
/// <param name="taskInput">设置属性请求内容相关</param>
/// <param name="deviceFirmwareInfo">固件数据Id</param>
/// <param name="fileObject">固件文件信息</param>
/// <param name="input">入参</param>
/// <param name="productInfo">产品信息(如果已获取则传入以避免重复调用)</param>
/// <param name="platformThingModelInfo">平台端物模型信息(如果已获取则传入以避免重复调用)</param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
protected async Task<bool> DeviceUpgradeCommandToOneNET(DeviceManagementInfoDto deviceInfo, ReceiveCommandInfoDto taskInput, DeviceFirmwareInfoDto deviceFirmwareInfo, FileObjectDto fileObject, DeviceUpgradeForApiInput input, dynamic productInfo = null, dynamic platformThingModelInfo = null)
{
try
{
if (deviceInfo == null || deviceFirmwareInfo == null || fileObject == null)
{
throw new UserFriendlyException($"{nameof(DeviceUpgradeCommandToOneNET)}设备或固件信息不能为空");
}
if ((deviceInfo.IoTPlatformProductId != deviceFirmwareInfo.IoTPlatformProductId) || (deviceInfo.IoTPlatform != deviceFirmwareInfo.IoTPlatform))
{
throw new UserFriendlyException($"设备{deviceInfo.DeviceAddress}平台产品信息固件中不一致");
}
//如果未传入产品信息和物模型信息,则获取
if (productInfo == null || platformThingModelInfo == null)
{
var (productInfoResult, platformThingModelInfoResult) = await GetOneNETProductAndThingModelInfoAsync(deviceInfo.IoTPlatformProductId, deviceInfo.DeviceAddress);
productInfo = productInfoResult;
platformThingModelInfo = platformThingModelInfoResult;
}
var upgradeProperty = ((List<IoTPlatformThingModelInfoDto>)platformThingModelInfo).Where(d => d.StandardFieldName == ThingModelFixedTypeConst.FIRMWARE_UPGRADE).FirstOrDefault();
if (upgradeProperty == null)
{
throw new UserFriendlyException($"设备{deviceInfo.DeviceAddress}平台端物模型信息不存在升级属性标识符{ThingModelFixedTypeConst.FIRMWARE_UPGRADE}。");
}
//构建升级指令《文件MD5值+OneNET产品KEY+升级标识符+文件大小》=>MD5计算获得签名值
var upgradeRecordInput = new CreateDeviceUpgradeRecordInput()
{
DeviceDataId = deviceInfo.Id,
DeviceName = deviceInfo.DeviceName,
DeviceAddress = deviceInfo.DeviceAddress,
OldFirmwareVersion = deviceInfo.FirmwareVersion,
NowFirmwareVersion = deviceFirmwareInfo.FirmwareVersion,
UpgradeSource = DeviceUpgradeSourceTypeEnum.AdminSystem,
UpgradeIdentifier = Yitter.IdGenerator.YitIdHelper.NextId(),
UpgradeDescription = input.UpgradeDescription,
UpgradeStatus = DeviceUpgradeStatusTypeEnum.NotUpgrade,
Id = GuidGenerator.Create()
};
var md5HashRaw = $"{fileObject.Md5Hash}{productInfo.ProductAccesskey}{upgradeRecordInput.UpgradeIdentifier}{fileObject.FileSize}";
var upgradeRequest = new DeviceUpgradeCommandRequest()
{
Length = fileObject.FileSize,
UpgradeIdentifier = upgradeRecordInput.UpgradeIdentifier,
SignatureValue = md5HashRaw.Md5Fun(),
FirmwareUrl = $"{serverApplicationOptions.DownloadDeviceFirmwareBasicUrl}{upgradeRecordInput.Id}",
TimeOut = serverApplicationOptions.DownloadDeviceFirmwareTimeOut,
};
upgradeRecordInput.UpgradeMessage = upgradeRequest.Serialize();
upgradeRecordInput.FirmwareSignature = upgradeRequest.SignatureValue;
string upgradeMessageHexString = upgradeRecordInput.UpgradeMessage.ToHexString();
//检查长度是否超过了OneNET平台升级属性标识符的长度限制
var thingModelInfoStr = ((OneNETProductInfoDto)productInfo).ThingModelInfos;
if (string.IsNullOrWhiteSpace(thingModelInfoStr))
{
logger.LogError($"{nameof(DeviceUpgradeCommandToOneNET)} 通过OneNET产品ID和属性标识符获取属性配置信息失败属性信息为空");
return false;
}
var thingModelInfo = thingModelInfoStr.Deserialize<OneNETAllThingModel>();
if (thingModelInfo.Properties == null)
{
logger.LogError($"{nameof(DeviceUpgradeCommandToOneNET)} 通过OneNET产品ID和属性标识符获取属性配置信息失败属性信息为空");
return false;
}
var property = thingModelInfo.Properties.Where(d => d.Identifier == upgradeProperty.IoTPlatformRawFieldName).FirstOrDefault(); //精准匹配
if (property == null)
{
logger.LogError($"{nameof(DeviceUpgradeCommandToOneNET)} 通过OneNET产品ID和属性标识符获取属性配置信息失败没有找到属性标识升级对应的属性平台信息");
return false;
}
var length = ((OneNETModelStringSpecs)property.DataType.Specs).Length;
if (upgradeMessageHexString.Length > length)
{
throw new UserFriendlyException($"设备{deviceInfo.DeviceAddress}平台端物模型信息属性标识符{upgradeProperty.IoTPlatformRawFieldName}长度超过OneNET平台限制请检查物模型属性标识符长度是否超过了{length}");
}
var insertResult = await deviceUpgradeRecordService.CreateAsync(upgradeRecordInput);
if (insertResult == null)
{
throw new UserFriendlyException($"设备{deviceInfo.DeviceAddress}升级记录失败");
}
//发送OneNET平台设备升级指令HEX格式字符串
taskInput.Commands = new Dictionary<string, object>()
{
{ upgradeProperty.IoTPlatformRawFieldName, upgradeMessageHexString }
};
var commandRequest = new OpenApiRequest()
{
Message = taskInput.Serialize(),
};
var packetTaskInfo = GetDeviceTelemetryPacketTaskInfo(ioTDBOptions, commandRequest, deviceInfo.Adapt<DeviceCacheInfos>(), taskInput.Commands.Serialize());
await ioTDBDataChannelManageService.DeviceTelemetryTaskWriterAsync(DataChannelManage.DeviceTelemetryTaskDataChannel.Writer, (DistributedMessageCenterConst.OneNETUpgradeCommandIssuedEventName, packetTaskInfo));
//将升级记录数据Id作为key文件数据ID作为值存入Redis缓存中。
string cacheKey = string.Format($"{RedisConst.CacheDeviceUpgradeRecordDataKey}", upgradeRecordInput.Id);
await FreeRedisProvider.Instance.SetAsync(cacheKey, fileObject.Id);
await redisPubSubService.PublishReliableAsync(DistributedMessageCenterConst.OneNETCommandIssuedEventName, packetTaskInfo);
//更新状态为升级中
await deviceUpgradeRecordService.UpdateStatusAsync(new UpdateStatusInput()
{
Id = upgradeRecordInput.Id.Value,
UpgradeStatus = DeviceUpgradeStatusTypeEnum.Upgrading,
});
return true;
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// 抄读OneNET平台设备属性数据
/// </summary>
/// <param name="deviceInfo"></param>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
protected async Task<Dictionary<string, object>> DevicePropertyValueToOneNET(DeviceManagementInfoDto deviceInfo, DevicePropertyValueForApiInput input)
{
try
{
//检查下设备是否在线
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("设备不在线");
}
var deviceDataResult = await oneNETDeviceService.QueryDevicePropertyDetail(new QueryDevicePropertyDetailInput()
{
DeviceName = deviceInfo.IoTPlatformDeviceOpenInfo,
OneNETAccountId = deviceInfo.IoTPlatformAccountId,
ProductId = deviceInfo.IoTPlatformProductId,
PropertyInfos = input.PropertyList
});
if (deviceDataResult == null || deviceDataResult.Success == false || deviceDataResult.Data == null)
{
throw new UserFriendlyException($"设备{deviceInfo.DeviceName}获取数据失败");
}
//获取设备产品平台端物模型信息
var platformThingModelInfo = await platformThingModelInfoAppService.FindByPlatformProductIdAsync(new IdInput<string>() { Id = deviceInfo.IoTPlatformProductId });
if (platformThingModelInfo == null || platformThingModelInfo.Count <= 0)
{
return deviceDataResult.Data;
}
List<string> updateKeys = new List<string>()
{
ThingModelFixedTypeConst.FIRMWARE_VERSION.ToLowerInvariant(),
ThingModelFixedTypeConst.ReadingMode.ToLowerInvariant()
};
var platformUpdatePropertyInfos = platformThingModelInfo.Where(d => updateKeys.Contains(d.StandardFieldName.ToLowerInvariant())).ToList();
if (platformUpdatePropertyInfos == null || platformUpdatePropertyInfos.Count <= 0)
{
return deviceDataResult.Data;
}
//根据抄读结果(单个或多个属性)更新设备信息,统一收集后只调用一次 UpdateDeviceInfos
bool needUpdate = false;
//抄读结果如果有固件版本号,则更新固件版本号到设备信息中
var firmwareVersionKey = platformUpdatePropertyInfos.FirstOrDefault(d => d.StandardFieldName?.ToLowerInvariant() == ThingModelFixedTypeConst.FIRMWARE_VERSION.ToLowerInvariant())?.IoTPlatformRawFieldName;
if (!string.IsNullOrWhiteSpace(firmwareVersionKey) && deviceDataResult.Data.ContainsKey(firmwareVersionKey))
{
deviceInfo.FirmwareVersion = deviceDataResult.Data[firmwareVersionKey].ToString();
needUpdate = true;
}
//抄读结果如果有抄读模式,则更新抄读模式到设备信息中
var readModeKey = platformUpdatePropertyInfos.FirstOrDefault(d => d.StandardFieldName?.ToLowerInvariant() == ThingModelFixedTypeConst.ReadingMode.ToLowerInvariant())?.IoTPlatformRawFieldName;
if (!string.IsNullOrWhiteSpace(readModeKey) && deviceDataResult.Data.ContainsKey(readModeKey))
{
var readModeValue = deviceDataResult.Data[readModeKey];
// 兼容 JsonElement / 字符串 / 数值 三种情况
int readModeInt;
if (readModeValue is System.Text.Json.JsonElement jsonElement)
{
if (jsonElement.ValueKind == System.Text.Json.JsonValueKind.Number && jsonElement.TryGetInt32(out var num))
{
readModeInt = num;
}
else
{
readModeInt = Convert.ToInt32(jsonElement.ToString());
}
}
else
{
readModeInt = Convert.ToInt32(readModeValue);
}
deviceInfo.ReadingMode = (DeviceReadingModeEnum)readModeInt;
needUpdate = true;
}
if (needUpdate)
{
await deviceAppService.UpdateDeviceInfos(deviceInfo);
}
return deviceDataResult.Data;
}
catch (Exception ex)
{
if (input.PropertyList.Contains(ThingModelFixedTypeConst.SpecialCommand))
{
ex = new UserFriendlyException("请检查设备是否已经采集数据");
throw ex;
}
else
{
throw;
}
}
}
#endregion
#region CTWing
/// <summary>
/// CTWing 批量设备创建
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
protected async Task<bool> CTWingDeviceBatchCreateAsync(BatchCreateDeviceAggregationInput input)
{
try
{
throw new UserFriendlyException($"CTWing 批量设备创建失败CTWing暂未实现。");
}
catch (Exception)
{
throw;
}
}
#endregion
/// <summary>
/// 更新设备信息并处理缓存
/// </summary>
/// <param name="input"></param>
/// <param name="pushResult">推送结果原始信息</param>
/// <param name="platformPassword">设备接入鉴权key</param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
private async Task<DeviceManagementInfoDto> DeviceUpdateHandler(DeviceManagementInfoDto input, HttpDataResult pushResult, string platformPassword = null)
{
UpdateDeviceInput updateDeviceInput = input.Adapt<UpdateDeviceInput>();
updateDeviceInput.IoTPlatformResponse = pushResult.Serialize();
updateDeviceInput.IsPlatformPushSuccess = true;
if (!string.IsNullOrWhiteSpace(updateDeviceInput.PlatformPassword))
{
updateDeviceInput.PlatformPassword = platformPassword;
}
var updateResult = await deviceAppService.UpdateAsync(updateDeviceInput);
if (updateResult == null)
{
logger.LogError($"{nameof(DeviceUpdateHandler)} 更新设备信息失败:{input.Serialize()}");
throw new UserFriendlyException($"推送结果更新失败。");
}
//设备数据缓存到Redis
DeviceCacheInfos deviceCacheInfos = input.Adapt<DeviceCacheInfos>();
deviceCacheInfos.PlatformPassword = null;
FreeRedisProvider.Instance.HSet<DeviceCacheInfos>(RedisConst.CacheAllDeviceInfoHashKey, input.DeviceAddress, deviceCacheInfos);
return input.Adapt<DeviceManagementInfoDto>();
}
/// <summary>
/// 获取OneNET产品信息和平台端物模型信息
/// </summary>
/// <param name="iotPlatformProductId">物联网平台产品Id</param>
/// <param name="deviceAddress">设备地址(用于错误信息)</param>
/// <returns>返回产品信息和平台端物模型信息的元组</returns>
protected async Task<(dynamic ProductInfo, dynamic PlatformThingModelInfo)> GetOneNETProductAndThingModelInfoAsync(string iotPlatformProductId, string deviceAddress)
{
//获取设备对应的产品信息
var productInfo = await oneNETProductService.GetProductInfoAsync(new IdInput<string>() { Id = iotPlatformProductId });
if (productInfo == null)
{
throw new UserFriendlyException($"{nameof(DeviceUpgradeCommandToOneNET)} OneNET设备升级属性设置失败产品Id{iotPlatformProductId}未找到对应的产品信息。");
}
//获取设备对应的平台端物模型信息,校验前端传入的属性标识集合是否存在不合法的属性标识符
var platformThingModelInfo = await platformThingModelInfoAppService.FindByPlatformProductIdAsync(new IdInput<string>() { Id = iotPlatformProductId });
if (platformThingModelInfo == null)
{
throw new UserFriendlyException($"设备{deviceAddress}的平台端物模型信息不存在。");
}
return (productInfo, platformThingModelInfo);
}
/// <summary>
/// 根据平台处理设备升级指令
/// </summary>
/// <param name="deviceInfo">设备信息</param>
/// <param name="receiveCommandInfoDto">接收指令信息Dto</param>
/// <param name="deviceFirmwareVersionInfo">固件版本信息</param>
/// <param name="fileInfo">文件信息</param>
/// <param name="input">升级输入参数</param>
/// <param name="oneNETProductInfo">OneNET产品信息可选如果已获取则传入以避免重复调用</param>
/// <param name="oneNETPlatformThingModelInfo">OneNET平台端物模型信息可选如果已获取则传入以避免重复调用</param>
/// <returns>处理结果</returns>
protected async Task<bool> ProcessDeviceUpgradeByPlatformAsync(DeviceManagementInfoDto deviceInfo, ReceiveCommandInfoDto receiveCommandInfoDto, DeviceFirmwareInfoDto deviceFirmwareVersionInfo, FileObjectDto fileInfo, DeviceUpgradeForApiInput input, dynamic oneNETProductInfo = null, dynamic oneNETPlatformThingModelInfo = null)
{
if (deviceInfo.IoTPlatform == IoTPlatformTypeEnum.OneNET)
{
//如果未传入产品信息和物模型信息,则获取
if (oneNETProductInfo == null || oneNETPlatformThingModelInfo == null)
{
var (productInfo, platformThingModelInfo) = await GetOneNETProductAndThingModelInfoAsync(deviceInfo.IoTPlatformProductId, deviceInfo.DeviceAddress);
oneNETProductInfo = productInfo;
oneNETPlatformThingModelInfo = platformThingModelInfo;
}
return await DeviceUpgradeCommandToOneNET(deviceInfo, receiveCommandInfoDto, deviceFirmwareVersionInfo, fileInfo, input, oneNETProductInfo, oneNETPlatformThingModelInfo);
}
else if (deviceInfo.IoTPlatform == IoTPlatformTypeEnum.CTWing)
{
//await redisPubSubService.PublishReliableAsync(DistributedMessageCenterConst.CTWingAepCommandIssuedEventName,commandRequest);
//return true;
throw new UserFriendlyException($"发送设备升级指令信息失败CTWing暂未实现。");
}
else
{
throw new UserFriendlyException($"发送设备升级指令信息失败,未找到对应的产品配置信息。");
}
}
}
}