zhangbin 3 months ago
commit 65f4a4eff9

@ -1,4 +1,5 @@
using System;
using StackExchange.Redis;
namespace Infrastructure.Cache
{
@ -30,7 +31,8 @@ namespace Infrastructure.Cache
/// <param name="key">缓存项key</param>
/// <returns>true成功,false失败</returns>
public abstract bool Remove(string key);
public abstract Task<T> Lock<T>(string id, Func<string, Task<T>> func, string key = "hc");
public abstract Task<T> Lock<T>(string id, Func<string, Task<T>> func, string key = "hc");
}
}

@ -1,7 +1,4 @@
using System;
using Enyim.Caching;
using Enyim.Caching.Memcached;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Options;
using StackExchange.Redis;
namespace Infrastructure.Cache
@ -13,11 +10,13 @@ namespace Infrastructure.Cache
{
private ConnectionMultiplexer _conn { get; set; }
private IDatabase iDatabase { get; set; }
private IServer server { get; set; }
public RedisCacheContext(IOptions<AppSetting> options)
{
_conn = ConnectionMultiplexer.Connect(options.Value.RedisConf.Conn);
iDatabase = _conn.GetDatabase(options.Value.RedisConf.Database);
server = _conn.GetServer("175.27.168.120:6050");
}
public override T Get<T>(string key)
@ -59,5 +58,121 @@ namespace Infrastructure.Cache
{
return func(id);
}
/// <summary>
/// HSET - 将哈希表 key 中的字段 field 的值设为 value。
/// </summary>
/// <param name="key">键</param>
/// <param name="field">字段</param>
/// <param name="value">值</param>
public void HashSet<T>(string key, string field, T value)
{
iDatabase.HashSet(new RedisKey(key),
new[] { new HashEntry(new RedisValue(field), new RedisValue(value.ToString() ?? string.Empty)) });
}
/// <summary>
/// HGET - 获取哈希表中的指定字段的值。
/// </summary>
/// <param name="key">键</param>
/// <param name="field">字段</param>
/// <returns>字段对应的值</returns>
public object HashGet(string key, string field)
{
return iDatabase.HashGet(key, field);
}
/// <summary>
/// HEXISTS - 查看哈希表中是否存在指定字段。
/// </summary>
/// <param name="key">键</param>
/// <param name="field">字段</param>
/// <returns>是否存在</returns>
public bool HashCheck(string key, string field)
{
return iDatabase.HashExists(key, field);
}
/// <summary>
/// HDEL - 删除一个或多个哈希表字段。
/// </summary>
/// <param name="key">键</param>
/// <param name="fields">要删除的字段数组</param>
/// <returns>移除数量</returns>
public long HashDel(string key, string[] fields)
{
var redisFields = fields.Select(f => new RedisValue(f)).ToArray();
return iDatabase.HashDelete(new RedisKey(key), redisFields);
}
/// <summary>
/// HLEN - 获取哈希表中字段的数量。
/// </summary>
/// <param name="key">键</param>
/// <returns>字段数量</returns>
public long HashLen(string key)
{
return iDatabase.HashLength(key);
}
/// <summary>
/// EXPIRE - 为键设置过期时间(秒)。
/// </summary>
/// <param name="key">键</param>
/// <param name="timeout">过期时间(秒)</param>
/// <returns>是否设置成功</returns>
public bool ExpireKey(string key, long timeout)
{
return iDatabase.KeyExpire(key, TimeSpan.FromSeconds(timeout));
}
/// <summary>
/// GET - 获取键对应的值。
/// </summary>
/// <param name="key">键</param>
/// <returns>键对应的值</returns>
public object Get(string key)
{
return iDatabase.StringGet(key);
}
/// <summary>
/// EXISTS - 判断键是否存在。
/// </summary>
/// <param name="key">键</param>
/// <returns>是否存在</returns>
public bool CheckExist(string key)
{
return iDatabase.KeyExists(key);
}
/// <summary>
/// DEL - 删除键。
/// </summary>
/// <param name="key">键</param>
/// <returns>是否删除成功</returns>
public bool Del(string key)
{
return CheckExist(key) && iDatabase.KeyDelete(key);
}
/// <summary>
/// LLEN - 获取列表的长度。
/// </summary>
/// <param name="key">键</param>
/// <returns>列表长度</returns>
public long ListLen(string key)
{
return iDatabase.ListLength(key);
}
public IEnumerable<RedisKey> GetAllKeys(string pattern)
{
return server.Keys(pattern: new RedisValue(pattern));
}
}
}

@ -2,6 +2,6 @@
public class GatewayType
{
// 机场、遥控器
public string Dock = "1";
public string M4TD = "2";
}

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Http;
using Infrastructure.Helpers;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Minio;
using Minio.DataModel;
@ -53,6 +54,26 @@ public class MinioService
}
}
public async Task<string> GetMetaObject(string objectName, string bucketName)
{
if (string.IsNullOrEmpty(bucketName))
{
bucketName = _bucketName;
}
if (objectName.StartsWith("http"))
{
objectName = objectName.Replace($"http://{endPoint}/{_bucketName}/", "");
}
var statArgs = new StatObjectArgs()
.WithBucket(bucketName)
.WithObject(objectName);
var meta = await _minioClient.StatObjectAsync(statArgs);
var md5 = meta.MetaData["md5"];
return md5; //
}
public async Task EnsureBucketExistsAsync(string bucketName)
{
var existsArgs = new BucketExistsArgs().WithBucket(bucketName);
@ -165,11 +186,18 @@ public class MinioService
using var stream = new MemoryStream();
await file.CopyToAsync(stream);
stream.Position = 0;
var md5 = Md5.CalculateStreamMd5(stream);
stream.Position = 0;
var putArgs = new PutObjectArgs()
.WithBucket(bucketName)
.WithObject(objectName)
.WithStreamData(stream)
.WithObjectSize(stream.Length)
.WithHeaders(new Dictionary<string, string>()
{
//x-amz-meta-md5
["X-Amz-Meta-Md5"] = md5
})
.WithContentType("application/octet-stream");
//.WithContentType(file.ContentType);
await _minioClient.PutObjectAsync(putArgs);

@ -60,7 +60,7 @@ public class MqttClientManager
public async Task SubscribeAsync(string topic,
Func<MqttApplicationMessageReceivedEventArgs, Task> handler)
{
{
await _inBoundClient.SubscribeAsync(topic, MqttQualityOfServiceLevel.AtLeastOnce, CancellationToken.None);
_inBoundClient.ApplicationMessageReceivedAsync += handler;
}

@ -6,6 +6,21 @@ namespace Infrastructure.Helpers
{
public class Md5
{
/// <summary>
/// 计算字节流的MD5值
/// </summary>
public static string CalculateStreamMd5(Stream stream)
{
if (stream == null)
throw new ArgumentNullException(nameof(stream));
if (!stream.CanRead)
throw new InvalidOperationException("流不可读");
using var md5 = MD5.Create();
var hashBytes = md5.ComputeHash(stream);
return BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
}
public static string Encrypt(string str)
{

@ -28,6 +28,8 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Include="PinYinConverterCore" Version="1.0.2" />
<PackageReference Include="Quartz" Version="3.6.2" />
<PackageReference Include="Quartz.Extensions.DependencyInjection" Version="3.6.2" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.0.1" />
<PackageReference Include="StackExchange.Redis" Version="2.6.111" />
</ItemGroup>

@ -0,0 +1,26 @@
using ce.autofac.extension;
using Infrastructure.Cache;
using Quartz;
namespace OpenAuth.App.BaseApp.HostedService;
public class GlobalSubscribe : IJob
{
public Task Execute(IJobExecutionContext context)
{
Console.WriteLine($"running !{DateTime.Now}");
var redisCacheContext = context.JobDetail.JobDataMap.Get("redisCacheContext") as RedisCacheContext;
/*var ioc = IocManager.Instance;
var redisCacheContext = IocManager.Instance.GetService<ICacheContext>();*/
// todo 如果无人机不在线,则订阅
if (redisCacheContext == null) return Task.CompletedTask;
var keys = redisCacheContext.GetAllKeys("online:*");
foreach (var redisKey in keys)
{
}
// todo
return Task.CompletedTask;
}
}

@ -1,9 +1,13 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Infrastructure.Cache;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using OpenAuth.App.BaseApp.HostedService;
using Quartz;
using Quartz.Impl;
namespace OpenAuth.App.HostedService
{
@ -15,17 +19,31 @@ namespace OpenAuth.App.HostedService
private readonly ILogger<QuartzService> _logger;
private IScheduler _scheduler;
private OpenJobApp _openJobApp;
private IServiceProvider _serviceProvider;
public QuartzService(ILogger<QuartzService> logger, IScheduler scheduler, OpenJobApp openJobApp)
public QuartzService(ILogger<QuartzService> logger, IScheduler scheduler, OpenJobApp openJobApp,
IServiceProvider serviceProvider)
{
_logger = logger;
_scheduler = scheduler;
_openJobApp = openJobApp;
_serviceProvider = serviceProvider;
}
public Task StartAsync(CancellationToken cancellationToken)
{
var map = new JobDataMap();
map.Put("redisCacheContext", _serviceProvider.GetService<ICacheContext>());
map.Put("ServiceProvider", _serviceProvider);
var job = JobBuilder.Create<GlobalSubscribe>().UsingJobData(map)
.WithIdentity("subscribe", "GlobalSubscribe").Build();
//创建一个触发条件
var trigger = TriggerBuilder.Create()
.WithIdentity("SubscribeJobTrigger", "GlobalSubscribe")
.WithSimpleSchedule(x => { x.WithIntervalInSeconds(3).RepeatForever(); })
.Build();
_scheduler.Start();
_scheduler.ScheduleJob(job, trigger);
var result = _openJobApp.StartAll();
return result;
@ -43,7 +61,6 @@ namespace OpenAuth.App.HostedService
public void Dispose()
{
}
}
}

@ -512,17 +512,17 @@ namespace OpenAuth.App
{
using (var uow = base.UnitWork.CreateContext())
{
foreach (var item in uow.Db.DbMaintenance.GetTableInfoList().Where(r => r.Name.ToLower().StartsWith("lasa_spacelockfly")))
foreach (var item in uow.Db.DbMaintenance.GetTableInfoList().Where(r => r.Name.ToLower().StartsWith("lasa_shpdata")))
{
//string entityName = item.Name.Substring(0, 1).ToUpper() + item.Name.Substring(1, 7).ToLower() + item.Name.Substring(9, 1).ToUpper() + item.Name.Substring(10).ToLower();/*实体名大写*/
string entityName = "LasaSpaceLockFly";
string entityName = "LasaShpData";
uow.Db.MappingTables.Add(entityName, item.Name);
//foreach (var col in db.DbMaintenance.GetColumnInfosByTableName(item.Name))
//{
// db.MappingColumns.Add(col.DbColumnName.ToUpper() /*类的属性大写*/, col.DbColumnName, entityName);
//}
}
uow.Db.DbFirst.Where(r => r.ToLower().StartsWith("lasa_spacelockfly")).IsCreateAttribute().CreateClassFile("E:\\低空态势感知\\code\\OpenAuth.Repository\\Domain", "OpenAuth.Repository.Domain");
uow.Db.DbFirst.Where(r => r.ToLower().StartsWith("lasa_shpdata")).IsCreateAttribute().CreateClassFile("E:\\低空态势感知\\code\\OpenAuth.Repository\\Domain", "OpenAuth.Repository.Domain");
uow.Commit();
}
return "更新实体成功";

@ -38,7 +38,6 @@
<PackageReference Include="NPOI" Version="2.5.6" />
<PackageReference Include="NUnit" Version="3.13.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Include="Quartz" Version="3.6.2" />
</ItemGroup>
<ItemGroup>

@ -0,0 +1,127 @@
namespace OpenAuth.App.ServiceApp.DjiCloud;
/// <summary>
/// 设备数据传输对象,用于封装设备相关的信息。
/// </summary>
public class DeviceDTO
{
/// <summary>
/// 设备序列号。
/// </summary>
public string DeviceSn { get; set; }
/// <summary>
/// 设备名称。
/// </summary>
public string DeviceName { get; set; }
/// <summary>
/// 工作区 ID。
/// </summary>
public string WorkspaceId { get; set; }
/// <summary>
/// 控制源。
/// </summary>
//public ControlSourceEnum ControlSource { get; set; }
/// <summary>
/// 设备描述。
/// </summary>
public string DeviceDesc { get; set; }
/// <summary>
/// 子设备序列号。
/// </summary>
public string ChildDeviceSn { get; set; }
/// <summary>
/// 设备域。
/// </summary>
//public DeviceDomainEnum Domain { get; set; }
/// <summary>
/// 设备类型。
/// </summary>
// public DeviceTypeEnum Type { get; set; }
/// <summary>
/// 设备子类型。
/// </summary>
// public DeviceSubTypeEnum SubType { get; set; }
/// <summary>
/// 负载列表。
/// </summary>
// public List<DevicePayloadDTO> PayloadsList { get; set; }
/// <summary>
/// 图标 URL。
/// </summary>
// public DeviceIconUrl IconUrl { get; set; }
/// <summary>
/// 状态。
/// </summary>
public bool Status { get; set; }
/// <summary>
/// 绑定状态。
/// </summary>
public bool BoundStatus { get; set; }
/// <summary>
/// 登录时间。
/// </summary>
public DateTime LoginTime { get; set; }
/// <summary>
/// 绑定时间。
/// </summary>
public DateTime BoundTime { get; set; }
/// <summary>
/// 昵称。
/// </summary>
public string Nickname { get; set; }
/// <summary>
/// 用户 ID。
/// </summary>
public string UserId { get; set; }
/// <summary>
/// 固件版本。
/// </summary>
public string FirmwareVersion { get; set; }
/// <summary>
/// 工作区名称。
/// </summary>
public string WorkspaceName { get; set; }
/// <summary>
/// 子设备 DTO。
/// </summary>
public DeviceDTO Children { get; set; }
/// <summary>
/// 固件状态。
/// </summary>
// public DeviceFirmwareStatusEnum FirmwareStatus { get; set; }
/// <summary>
/// 固件进度。
/// </summary>
public int FirmwareProgress { get; set; }
/// <summary>
/// 父设备序列号。
/// </summary>
public string ParentSn { get; set; }
/// <summary>
/// 物模型版本。
/// </summary>
public string ThingVersion { get; set; }
}

@ -0,0 +1,214 @@
using System.Text.Json.Nodes;
using OpenAuth.WebApi.CloudSdk;
namespace OpenAuth.App.ServiceApp.DjiCloud.Service;
/// <summary>
/// 设备服务接口,定义了设备管理相关的操作。
/// </summary>
public interface IDeviceService
{
/// <summary>
/// 飞行器离线。
/// </summary>
/// <param name="deviceSn">飞行器序列号</param>
void SubDeviceOffline(string deviceSn);
/// <summary>
/// 网关离线。
/// </summary>
/// <param name="gatewaySn">网关序列号</param>
void GatewayOffline(string gatewaySn);
/// <summary>
/// 当网关上线时订阅网关主题,并取消订阅子设备主题。
/// </summary>
/// <param name="gateway">网关管理对象</param>
void GatewayOnlineSubscribeTopic(GatewayManager gateway);
/// <summary>
/// 当无人机上线时订阅网关和子设备主题。
/// </summary>
/// <param name="gateway">网关管理对象</param>
void SubDeviceOnlineSubscribeTopic(GatewayManager gateway);
/// <summary>
/// 当网关设备下线时,取消订阅网关和子设备的主题。
/// </summary>
/// <param name="gateway">网关管理对象</param>
void OfflineUnsubscribeTopic(GatewayManager gateway);
/// <summary>
/// 根据不同查询条件获取设备数据。
/// </summary>
/// <param name="param">查询参数</param>
/// <returns>设备列表</returns>
// List<DeviceDTO> GetDevicesByParams(DeviceQueryParam param);
/// <summary>
/// 获取该工作区所有设备的拓扑信息。
/// </summary>
/// <param name="workspaceId">工作区 ID</param>
/// <returns>设备列表</returns>
List<DeviceDTO> GetDevicesTopoForWeb(string workspaceId);
/// <summary>
/// 设置无人机的遥控器和负载信息。
/// </summary>
/// <param name="device">设备对象</param>
void SpliceDeviceTopo(DeviceDTO device);
/// <summary>
/// 根据设备 SN 查询设备信息。
/// </summary>
/// <param name="sn">设备序列号</param>
/// <returns>拓扑设备 DTO</returns>
// Optional<TopologyDeviceDTO> GetDeviceTopoForPilot(string sn);
/// <summary>
/// 将单个设备信息转换为拓扑对象。
/// </summary>
/// <param name="device">设备对象</param>
/// <returns>拓扑设备 DTO</returns>
// TopologyDeviceDTO DeviceConvertToTopologyDTO(DeviceDTO device);
/// <summary>
/// 服务器接收到同一工作区内的任何设备上下线及拓扑更新请求时,
/// 会通过 WebSocket 向 PILOT 广播推送设备上下线及拓扑更新消息,
/// PILOT 收到推送后重新获取设备拓扑列表。
/// </summary>
/// <param name="workspaceId">工作区 ID</param>
/// <param name="deviceSn">设备序列号</param>
void PushDeviceOfflineTopo(string workspaceId, string deviceSn);
/// <summary>
/// 服务器接收到同一工作区内的任何设备上下线及拓扑更新请求时,
/// 会通过 WebSocket 向 PILOT 广播推送设备上下线及拓扑更新消息,
/// PILOT 收到推送后重新获取设备拓扑列表。
/// </summary>
/// <param name="workspaceId">工作区 ID</param>
/// <param name="gatewaySn">网关序列号</param>
/// <param name="deviceSn">设备序列号</param>
void PushDeviceOnlineTopo(string workspaceId, string gatewaySn, string deviceSn);
/// <summary>
/// 更新设备信息。
/// </summary>
/// <param name="deviceDTO">设备 DTO</param>
/// <returns>是否更新成功</returns>
bool UpdateDevice(DeviceDTO deviceDTO);
/// <summary>
/// 绑定设备到组织和人员。
/// </summary>
/// <param name="device">设备 DTO</param>
/// <returns>是否绑定成功</returns>
bool BindDevice(DeviceDTO device);
/// <summary>
/// 获取工作区内已绑定设备列表。
/// </summary>
/// <param name="workspaceId">工作区 ID</param>
/// <param name="page">页码</param>
/// <param name="pageSize">每页大小</param>
/// <param name="domain">域</param>
/// <returns>分页设备数据</returns>
// PaginationData<DeviceDTO> GetBoundDevicesWithDomain(string workspaceId, long page, long pageSize, int domain);
/// <summary>
/// 解除设备绑定。
/// </summary>
/// <param name="deviceSn">设备序列号</param>
void UnbindDevice(string deviceSn);
/// <summary>
/// 根据设备序列号获取设备信息。
/// </summary>
/// <param name="sn">设备序列号</param>
/// <returns>设备 DTO</returns>
// Optional<DeviceDTO> GetDeviceBySn(string sn);
/// <summary>
/// 创建设备固件升级任务。
/// </summary>
/// <param name="workspaceId">工作区 ID</param>
/// <param name="upgradeDTOS">升级 DTO 列表</param>
/// <returns>HTTP 响应结果</returns>
// HttpResultResponse CreateDeviceOtaJob(string workspaceId, List<DeviceFirmwareUpgradeDTO> upgradeDTOS);
/// <summary>
/// 设置无人机属性参数。
/// </summary>
/// <param name="workspaceId">工作区 ID</param>
/// <param name="dockSn">停机坞序列号</param>
/// <param name="param">JSON 参数节点</param>
/// <returns>受影响记录数</returns>
int DevicePropertySet(string workspaceId, string dockSn, JsonNode param);
/// <summary>
/// 检查停机坞的工作模式。
/// </summary>
/// <param name="dockSn">停机坞序列号</param>
/// <returns>停机坞工作模式枚举</returns>
// DockModeCodeEnum GetDockMode(string dockSn);
/// <summary>
/// 查询飞行器的工作模式。
/// </summary>
/// <param name="deviceSn">设备序列号</param>
/// <returns>飞行器工作模式枚举</returns>
// DroneModeCodeEnum GetDeviceMode(string deviceSn);
/// <summary>
/// 检查停机坞是否处于 DRC 模式。
/// </summary>
/// <param name="dockSn">停机坞序列号</param>
/// <returns>是否处于 DRC 模式</returns>
bool CheckDockDrcMode(string dockSn);
/// <summary>
/// 检查设备是否有飞行控制权限。
/// </summary>
/// <param name="gatewaySn">网关序列号</param>
/// <returns>是否有飞行控制权限</returns>
bool CheckAuthorityFlight(string gatewaySn);
/// <summary>
/// 保存设备信息。
/// </summary>
/// <param name="device">设备 DTO</param>
/// <returns>受影响记录数</returns>
int SaveDevice(DeviceDTO device);
/// <summary>
/// 保存或更新设备信息。
/// </summary>
/// <param name="device">设备 DTO</param>
/// <returns>是否保存或更新成功</returns>
bool SaveOrUpdateDevice(DeviceDTO device);
/// <summary>
/// 推送 OSD 数据给 Pilot。
/// </summary>
/// <param name="workspaceId">工作区 ID</param>
/// <param name="sn">设备序列号</param>
/// <param name="data">OSD 数据</param>
// void PushOsdDataToPilot(string workspaceId, string sn, DeviceOsdHost data);
/// <summary>
/// 推送 OSD 数据给 Web。
/// </summary>
/// <param name="workspaceId">工作区 ID</param>
/// <param name="codeEnum">业务代码枚举</param>
/// <param name="sn">设备序列号</param>
/// <param name="data">OSD 数据</param>
// void PushOsdDataToWeb(string workspaceId, BizCodeEnum codeEnum, string sn, object data);
/// <summary>
/// 更新飞行控制信息。
/// </summary>
/// <param name="gateway">网关设备 DTO</param>
/// <param name="controlSource">控制源枚举</param>
//void UpdateFlightControl(DeviceDTO gateway, ControlSourceEnum controlSource);
}

@ -1,4 +1,4 @@
using DocumentFormat.OpenXml.Wordprocessing;
using DocumentFormat.OpenXml.Wordprocessing;
using Infrastructure;
using Infrastructure.CloudSdk.minio;
using Infrastructure.CloudSdk.wayline;
@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using OpenAuth.App.BaseApp.Base;
using OpenAuth.App.BasicQueryService;
using OpenAuth.App.Interface;
using OpenAuth.App.ServiceApp.Request;
using OpenAuth.App.ServiceApp.Response;
@ -14,6 +15,7 @@ using OpenAuth.Repository.Domain;
using OpenAuth.WebApi;
using OpenAuth.WebApi.CloudSdk;
using SqlSugar;
using System.Text;
namespace OpenAuth.App.ServiceApp
{
@ -21,13 +23,15 @@ namespace OpenAuth.App.ServiceApp
{
private readonly MqttClientManager _mqttClientManager;
private readonly MinioService _minioService;
CommonDataManager _commonDataManager;
public ManageApp(ISugarUnitOfWork<SugarDbContext> unitWork, ISimpleClient<LasaDronePort> repository, IAuth auth,
MqttClientManager mqttClientManager, MinioService minioService)
MqttClientManager mqttClientManager, CommonDataManager commonDataManager, MinioService minioService)
: base(unitWork, repository, auth)
{
_mqttClientManager = mqttClientManager;
_minioService = minioService;
_commonDataManager = commonDataManager;
}
#region 机场管理
@ -488,21 +492,23 @@ namespace OpenAuth.App.ServiceApp
using (var db = UnitWork.CreateContext())
{
WorkSpaceRes res = new WorkSpaceRes();
var workspace = await db.LasaWorkspace.AsQueryable().Where(r => r.Id == id).FirstAsync();
var sysuser = db.User.AsQueryable()
.LeftJoin<LasaSpaceUser>((r, a) => r.Id == a.UserId)
.Where((r, a) => a.WorkSpaceId == id)
.Select((r, a) => r).ToList();
var uav = db.LasaDronePort.AsQueryable()
.LeftJoin<LasaSpaceDevice>((r, a) => r.Id == a.DeviceId)
.Where((r, a) => a.WorkSpaceId == id)
.Select((r, a) => r).ToList();
var lockfly = db.LasaSpaceLockFly.AsQueryable().Where(r => r.WorkSpaceId == id).ToList();
res.users = sysuser;
res.LasaDronePort = uav;
res.lasaSpaceLockFlies = lockfly;
res.workspace = workspace;
var workspace = await db.LasaWorkspace.AsQueryable().Where(r => r.Id == id && r.IsDelete == false).FirstAsync();
if (workspace != null)
{
var sysuser = db.User.AsQueryable()
.LeftJoin<LasaSpaceUser>((r, a) => r.Id == a.UserId)
.Where((r, a) => a.WorkSpaceId == id)
.Select((r, a) => r).ToList();
var uav = db.LasaDronePort.AsQueryable()
.LeftJoin<LasaSpaceDevice>((r, a) => r.Id == a.DeviceId)
.Where((r, a) => a.WorkSpaceId == id)
.Select((r, a) => r).ToList();
var lockfly = db.LasaSpaceLockFly.AsQueryable().Where(r => r.WorkSpaceId == id).ToList();
res.users = sysuser;
res.LasaDronePort = uav;
res.lasaSpaceLockFlies = lockfly;
res.workspace = workspace;
}
return new Response<WorkSpaceRes>
{
Result = res
@ -579,75 +585,82 @@ namespace OpenAuth.App.ServiceApp
var wpmlDir = configuration["WpmlDir"];
// 读取连接字符串
//var serverIp = configuration["MQTT:Server"];
var data = new
{
flight_id = Guid.NewGuid().ToString(),
execute_time = DateTime.Now.Ticks, // 任务开始执行时间毫秒时间戳。可选字段。当 task_type 为 0 或 1 时必填,为 2 时非必填。
//立即任务和定时任务均由 execute_time 指定执行时间。条件任务支持 ready_conditions 字段指定任务就绪条件。任务可在指定时间段内满足一定条件后即可执行。立即任务媒体上传优先级最高,定时任务和条件任务媒体上传优先级相同。
//0":"立即任务","1":"定时任务","2":"条件任务
task_type = task.TaskType,
file = new
{
url = wpml, // todo 拼接地址
fingerprint = "" // todo 计算md5
},
dynamic data = new ExpandoObject();
data.flight_id = Guid.NewGuid().ToString();
data.execute_time = DateTime.Now.Ticks;
data.task_type = task.TaskType;
data.file = new
{
url = wpml,
fingerprint = _minioService.GetMetaObject(wpml, "")
};
//任务执行条件
data.executable_conditions = new
{
// 可执行任务的机场或飞行器最低存储容量,机场或飞行器存储容量不满足 storage_capacity 时,任务执行失败。
// todo 容量单位是什么? 一个图片多少兆,预计拍多少图片?
// 1. 需要多少容量
// 2. 多少图片需要多少容量
// 3. 要存多少图片
// 4.
storage_capacity = 1000
};
// todo 1. 查询上报断点 2. 断点续飞方法支持
data.break_point = new
{
index = 1, // 断点序号
state = 1, // “0":"在航段上","1":"在航点上
progress = 1.0, // {"max":"1.0","min":"0"}
wayline_id = "" // 航线id
};
// 返航高度 {"max":1500,"min":20,"step":"","unit_name":"米 / m"}
data.rth_altitude = 150; // todo 取自任务
// 返航高度模式 {"0":"智能高度","1":"设定高度"}
// 智能返航模式下,飞行器将自动规划最佳返航高度。大疆机场当前不支持设置返航高度模式,只能选择'设定高度'模式。当环境,光线不满足视觉系统要求时(譬如傍晚阳光直射、夜间弱光无光),飞行器将使用您设定的返航高度进行直线返航
data.rth_mode = 1;
// {"0":"返航","1":"悬停","2":"降落"}
// 失控动作,当前固定传的值是 0即返航。注意该枚举值定义跟飞控跟机场定义的不一致机场端会进行转换。
data.out_of_control_action = 0;
// 航线失控动作 保持跟 KMZ 文件一致
// {"0":"继续执行航线任务","1":"退出航线任务,执行遥控器失控动作"}
data.exit_wayline_when_rc_lost = 0;
// 航线精度类型 {"0":"GPS 任务","1":"高精度 RTK 任务"}
// 高精度 RTK 任务:飞行器起飞后会在空中等待 RTK 收敛后再执行任务,等待 RTK 收敛的过程中无法暂停任务。默认场景建议使用该模式。GPS 任务:飞行器无需等待 RTK 收敛便可以直接开始执行。精度要求不高的任务或对起飞时效性要求较高的任务建议使用该模式。
data.wayline_precision_type = task.PeriodicFormula; // todo 值来自任务
// 是否在模拟器中执行任务 todo 调试时使用
/*data.simulate_mission = new
{
//是否开启模拟器任务
//{"0":"不开启","1":"开启"}
is_enable = 1,
// 纬度 {"max":"90.0","min":"-90.0"}
latitude = 35.0,
// 经度 {"max":"180.0","min":"-180.0"}
longitude = 120.0,
// 高度 {"max":"9999.9","min":"-9999.9"unit_name":"米 / m"}
altitude = 200.0
};*/
// 飞行安全预检查
// {"0":"关闭","1":"开启"}
// 设置一键起飞和航线任务中的飞行安全是否预先检查。此字段为可选默认为0值为0表示关闭1表示开启。飞行安全预先检查表示: 飞行器执行任务前,检查自身作业区文件是否与云端一致,如果不一致则拉取文件更新,如果一致则不处理
data.flight_safety_advance_check = 0;
if (data.task_type == 2)
{
// 任务就绪条件 可选字段。条件任务(即 task_type 为2时必填其他类型任务会忽略该字段。下发条件任务后设备会定频检查 ready_conditions 是否全部满足,若全部满足则会有 flighttask_ready 事件通知。且设备端接收 flighttask_execute 指令时,也会检查任务的 ready_conditions 是否已全部满足。
ready_conditions = new
data.ready_conditions = new
{
// 可执行任务的飞行器电池电量百分比阈值,任务开始执行时的飞行器电量必须大于
// todo 去哪里取值 任务限制中是否存在
// todo 获取指定值 计算 还是
battery_capacity = 77, // 设备电量百分比,范围 0-100
// todo 设定时间
begin_time = DateTime.Now.Ticks, // 任务开始执行时间必须大于该值
// todo 设定结束时间
end_time = DateTime.Now.Ticks, // 任务结束时间必须
},
// 任务执行条件
executable_conditions = new
{
// 可执行任务的机场或飞行器最低存储容量,机场或飞行器存储容量不满足 storage_capacity 时,任务执行失败。
// todo 容量单位是什么? 一个图片多少兆,预计拍多少图片?
storage_capacity = 1000
},
break_point = new
{
index = 1, // 断点序号
state = 1, // “0":"在航段上","1":"在航点上
progress = 1.0, // {"max":"1.0","min":"0"}
wayline_id = "" // 航线id
},
// 返航高度 {"max":1500,"min":20,"step":"","unit_name":"米 / m"}
rth_altitude = 150, // todo 取自任务
// 返航高度模式 {"0":"智能高度","1":"设定高度"}
// 智能返航模式下,飞行器将自动规划最佳返航高度。大疆机场当前不支持设置返航高度模式,只能选择'设定高度'模式。当环境,光线不满足视觉系统要求时(譬如傍晚阳光直射、夜间弱光无光),飞行器将使用您设定的返航高度进行直线返航
rth_mode = 1,
// {"0":"返航","1":"悬停","2":"降落"}
// 失控动作,当前固定传的值是 0即返航。注意该枚举值定义跟飞控跟机场定义的不一致机场端会进行转换。
out_of_control_action = 0,
// 航线失控动作 保持跟 KMZ 文件一致
// {"0":"继续执行航线任务","1":"退出航线任务,执行遥控器失控动作"}
exit_wayline_when_rc_lost = 0,
// 航线精度类型 {"0":"GPS 任务","1":"高精度 RTK 任务"}
// 高精度 RTK 任务:飞行器起飞后会在空中等待 RTK 收敛后再执行任务,等待 RTK 收敛的过程中无法暂停任务。默认场景建议使用该模式。GPS 任务:飞行器无需等待 RTK 收敛便可以直接开始执行。精度要求不高的任务或对起飞时效性要求较高的任务建议使用该模式。
wayline_precision_type = 1, // todo 值来自任务
// 是否在模拟器中执行任务
simulate_mission = new
{
//是否开启模拟器任务
//{"0":"不开启","1":"开启"}
is_enable = 1,
// 纬度 {"max":"90.0","min":"-90.0"}
latitude = 35.0,
// 经度 {"max":"180.0","min":"-180.0"}
longitude = 120.0,
// 高度 {"max":"9999.9","min":"-9999.9"unit_name":"米 / m"}
altitude = 200.0
},
// 飞行安全预检查
// {"0":"关闭","1":"开启"}
// 设置一键起飞和航线任务中的飞行安全是否预先检查。此字段为可选默认为0值为0表示关闭1表示开启。飞行安全预先检查表示: 飞行器执行任务前,检查自身作业区文件是否与云端一致,如果不一致则拉取文件更新,如果一致则不处理
flight_safety_advance_check = 0
};
};
}
request.SetData(data);
// 任务下发
await _mqttClientManager.PublishAsync(topic, JsonConvert.SerializeObject(request));
@ -682,5 +695,165 @@ namespace OpenAuth.App.ServiceApp
{
return _minioService.UploadFile(xmlFile, "");
}
#region 添加地图作业区域
/// <summary>
/// 添加地图作业区域
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public async Task<Response<string>> AddWorkArea(LasaShpData model)
{
var _user = _auth.GetCurrentUser().User;
model.CreateTime = DateTime.Now;
model.CreateUser = _user.Id.ToString();
model.CreateUserName = _user.Name.ToString();
using (var db = base.UnitWork.CreateContext().Db.CopyNew())
{
//获取主键
string id = Guid.NewGuid().ToString();
model.Id = id;
//格式化数据
string _wktModel = _commonDataManager.WktDataConvert(model.Geom, "MULTIPOLYGON ZM", 4);
model.Geom = null;
StringBuilder geomSql = new StringBuilder();
geomSql.AppendFormat($" update lasa_shpdata set \"Geom\" = st_geomfromtext('{_wktModel}',4326) where \"Id\" = '{id}'");
//更新面积
StringBuilder sql = new StringBuilder();
sql.AppendFormat($" update lasa_shpdata set \"Area\" = st_area(st_transform(\"Geom\",4527)) where \"Geom\" is not null and \"Id\" = '{id}'");
//更新周长
StringBuilder sqlle = new StringBuilder();
sqlle.AppendFormat($" update lasa_shpdata set \"Length\" = ST_Perimeter(st_transform(\"Geom\",4527)) where \"Geom\" is not null and \"Id\" = '{id}'");
//使用事务提交数据
var transFlag = await db.UseTranAsync(async () =>
{
//插入图斑数据
var flag = await db.Insertable(model).ExecuteCommandAsync();
//修改图斑数据
var flagGeom = await db.Ado.ExecuteCommandAsync(geomSql.ToString());
//修改图斑面积
var flagUpdate = await db.Ado.ExecuteCommandAsync(sql.ToString());
//修改图斑周长
var lengthUpdate = await db.Ado.ExecuteCommandAsync(sqlle.ToString());
});
if (transFlag.IsSuccess)
return new Response<string>
{
Result = id,
Message = "新增成功"
};
else
return new Response<string>
{
Message = "新增失败"
};
}
}
/// <summary>
/// 更新地图作业区域
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public async Task<Response<string>> UpdateWorkArea(LasaShpData model)
{
using (var db = base.UnitWork.CreateContext().Db.CopyNew())
{
//格式化数据
string _wktModel = _commonDataManager.WktDataConvert(model.Geom, "MULTIPOLYGON ZM", 4);
model.Geom = null;
StringBuilder geomSql = new StringBuilder();
geomSql.AppendFormat($" update lasa_shpdata set \"Geom\" = st_geomfromtext('{_wktModel}',4326) where \"Id\" = '{model.Id}'");
//更新面积
StringBuilder sql = new StringBuilder();
sql.AppendFormat($" update lasa_shpdata set \"Area\" = st_area(st_transform(\"Geom\",4527)) where \"Geom\" is not null and \"Id\" = '{model.Id}'");
//更新周长
StringBuilder sqlle = new StringBuilder();
sqlle.AppendFormat($" update lasa_shpdata set \"Length\" = ST_Perimeter(st_transform(\"Geom\",4527)) where \"Geom\" is not null and \"Id\" = '{model.Id}'");
//使用事务提交数据
var transFlag = await db.UseTranAsync(async () =>
{
//插入图斑数据
var flag = await db.Updateable(model).ExecuteCommandAsync();
//修改图斑数据
var flagGeom = await db.Ado.ExecuteCommandAsync(geomSql.ToString());
//修改图斑面积
var flagUpdate = await db.Ado.ExecuteCommandAsync(sql.ToString());
//修改图斑周长
var lengthUpdate = await db.Ado.ExecuteCommandAsync(sqlle.ToString());
});
if (transFlag.IsSuccess)
return new Response<string>
{
Result = model.Id,
Message = "更新成功"
};
else
return new Response<string>
{
Message = "更新失败"
};
}
}
//删除地图作业区域
public async Task<Response<bool>> DeleteWorkArea(string id)
{
using (var db = UnitWork.CreateContext())
{
await db.LasaShpData.DeleteByIdAsync(id);
if (db.Commit())
return new Response<bool> { Result = true, Message = "删除成功" };
else
return new Response<bool> { Result = false, Message = "删除失败" };
}
}
/// <summary>
/// 获取地图作业区域列表
/// </summary>
/// <param name="workspaceid">项目id</param>
/// <returns></returns>
public async Task<Response<List<LasaShpData>>> GetWorkAreaList(string workspaceid,int? state, string type)
{
using (var db = UnitWork.CreateContext())
{
StringBuilder sql = new StringBuilder();
sql.AppendFormat($" Select * from lasa_shpdata where \"WorkSpaceId\" = '{workspaceid}'");
var list = db.Db.SqlQueryable<LasaShpData>(sql.ToString()).ToList();
if (!string.IsNullOrEmpty(type)&&list.Count>0)
{
list=list.Where(r=>r.Type == type).ToList();
}
if(state!=null)
{
list=list.Where(r=>r.State==state).ToList();
}
return new Response<List<LasaShpData>>
{
Result = list
};
}
}
#endregion
}
}

@ -0,0 +1,80 @@
using System;
using System.Linq;
using System.Text;
using SqlSugar;
namespace OpenAuth.Repository.Domain
{
///<summary>
///
///</summary>
[SugarTable("lasa_shpdata")]
public partial class LasaShpData
{
public LasaShpData(){
}
/// <summary>
/// Desc:主键
/// Default:
/// Nullable:False
/// </summary>
[SugarColumn(IsPrimaryKey=true)]
public string Id {get;set;}
/// <summary>
/// Desc:名称
/// Default:
/// Nullable:True
/// </summary>
public string Name {get;set;}
/// <summary>
/// Desc:类型(区分限飞区域,作业区域等)
/// Default:
/// Nullable:True
/// </summary>
public string Type {get;set;}
/// <summary>
/// Desc:属性边框属性颜色宽度等json格式
/// Default:
/// Nullable:True
/// </summary>
public string Properties {get;set;}
/// <summary>
/// Desc:创建时间
/// Default:
/// Nullable:True
/// </summary>
public DateTime? CreateTime {get;set;}
/// <summary>
/// Desc:创建人
/// Default:
/// Nullable:True
/// </summary>
public string CreateUser {get;set;}
/// <summary>
/// Desc:区域
/// Default:
/// Nullable:True
/// </summary>
public string Geom {get;set;}
/// <summary>
/// Desc:项目id
/// Default:
/// Nullable:True
/// </summary>
public string WorkSpaceId {get;set;}
public int State { get;set;}
public string CreateUserName { get; set;}
public decimal Area { get; set; }
public decimal Length { get; set; }
}
}

@ -65,6 +65,7 @@ namespace OpenAuth.Repository
public SugarRepositiry<LasaTask> LasaTask { get; set; }
public SugarRepositiry<LasaAirLine> LasaAirLine { get; set; }
public SugarRepositiry<LasaWorkspace> LasaWorkspace { get; set; }
public SugarRepositiry<LasaShpData> LasaShpData { get; set; }
public SugarRepositiry<LasaSpaceUser> LasaSpaceUser { get; set; }
public SugarRepositiry<LasaSpaceDevice> LasaSpaceDevice { get; set; }
public SugarRepositiry<LasaSpaceLockFly> LasaSpaceLockFly { get; set; }

@ -470,5 +470,84 @@ namespace OpenAuth.WebApi.Controllers.ServiceControllers
var dir = configuration["WpmlDir"];
return dir;
}
#region 添加地图作业区域
/// <summary>
/// 添加地图作业区域
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[HttpPost]
public async Task<Response<string>> AddWorkArea([FromBody] LasaShpData model)
{
var res = new Response<string>();
try
{
return await _app.AddWorkArea(model);
}
catch (Exception ex)
{
res.Code = 500;
res.Message = ex.InnerException?.Message ?? ex.Message;
}
return res;
}
/// <summary>
/// 更新地图作业区域
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[HttpPost]
public async Task<Response<string>> UpdateWorkArea([FromBody] LasaShpData model)
{
var res = new Response<string>();
try
{
return await _app.UpdateWorkArea(model);
}
catch (Exception ex)
{
res.Code = 500;
res.Message = ex.InnerException?.Message ?? ex.Message;
}
return res;
}
/// <summary>
/// 删除地图作业区域
/// </summary>
/// <param name="id">地图作业区域id</param>
/// <returns></returns>
[HttpPost]
public async Task<Response<bool>> DeleteWorkArea(string id)
{
return await _app.DeleteWorkArea(id);
}
/// <summary>
/// 获取地图作业区域列表
/// </summary>
/// <param name="workspaceid">项目id</param>
/// <param name="state">项目状态0已启用1已禁用null全部</param>
/// <param name="type">区域类型(作业区,限飞区等)</param>
/// <returns></returns>
[HttpGet]
public async Task<Response<List<LasaShpData>>> GetWorkAreaList(string workspaceid,int? state,string type)
{
var result = new Response<List<LasaShpData>>();
try
{
result = await _app.GetWorkAreaList(workspaceid,state,type);
}
catch (Exception ex)
{
result.Code = 500;
result.Message = ex.Message;
}
return result;
}
#endregion
}
}

@ -46,8 +46,6 @@
<PackageReference Include="NPOI" Version="2.5.6" />
<PackageReference Include="NUnit" Version="3.13.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Include="Quartz" Version="3.6.2" />
<PackageReference Include="Quartz.Extensions.DependencyInjection" Version="3.6.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>
@ -83,6 +81,7 @@
<ItemGroup>
<Folder Include="Controllers\BaseControllers\Permission\" />
<Folder Include="Schedule\" />
</ItemGroup>
</Project>

@ -5,6 +5,7 @@ using ce.autofac.extension;
using DocumentFormat.OpenXml.Validation;
using IdentityServer4.AccessTokenValidation;
using Infrastructure;
using Infrastructure.Cache;
using Infrastructure.CloudSdk.minio;
using Infrastructure.CloudSdk.mqttmessagecenter;
using Infrastructure.Extensions.AutofacManager;
@ -21,11 +22,14 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Npgsql;
using OpenAuth.App;
using OpenAuth.App.BaseApp.HostedService;
using OpenAuth.App.BaseApp.ImMsgManager;
using OpenAuth.App.HostedService;
using OpenAuth.Repository;
using OpenAuth.WebApi.Model;
using OpenAuth.WebApi.Model.mqtt;
using Quartz;
using Quartz.Impl;
using SqlSugar;
using Swashbuckle.AspNetCore.SwaggerUI;
@ -53,6 +57,7 @@ namespace OpenAuth.WebApi
// minio client
services.AddSingleton(_ => new MinioService());
#region log4net
//在startup中需要强制创建log4net
@ -317,10 +322,8 @@ namespace OpenAuth.WebApi
#endregion
#region Quartz
//设置定时启动的任务
//services.AddHostedService<QuartzService>();
services.AddHostedService<QuartzService>();
#endregion
#region SignalR
@ -329,12 +332,14 @@ namespace OpenAuth.WebApi
#endregion
#region mqtt
#region mqtt
//services.AddSingleton<IMqttMessageHandler, ThingRequestHandler>();
//services.AddSingleton<IMqttMessageHandler, ThingServiceHandler>();
services.AddSingleton<IMqttMessageHandler, ThingOsdHandler>();
//services.AddSingleton<IMqttMessageHandler, ThingEventHandler>();
services.AddHostedService<MqttHostedService>();
#endregion
}

Loading…
Cancel
Save