using System.Diagnostics; using System.Drawing; using System.Dynamic; using System.Net; using System.Text; using Infrastructure; using Infrastructure.CloudSdk; using Infrastructure.CloudSdk.minio; using Infrastructure.CloudSdk.wayline; using Infrastructure.Extensions; using Infrastructure.Helpers; using MetadataExtractor; using MetadataExtractor.Formats.Exif; using MetadataExtractor.Formats.Xmp; using Microsoft.AspNetCore.Http; using Microsoft.IdentityModel.Tokens; using Microsoft.Net.Http.Headers; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using OpenAuth.App.BaseApp.Base; using OpenAuth.App.BasicQueryService; using OpenAuth.App.Interface; using OpenAuth.App.Request; using OpenAuth.App.ServiceApp.AirLine.Request; using OpenAuth.App.ServiceApp.FlyTask.Request; using OpenAuth.App.ServiceApp.FlyTask.Response; using OpenAuth.App.ServiceApp.Request; using OpenAuth.App.ServiceApp.Response; using OpenAuth.Repository; using OpenAuth.Repository.Domain; using OpenAuth.WebApi; using OpenAuth.WebApi.CloudSdk; using SqlSugar; using Directory = System.IO.Directory; using RangeHeaderValue = System.Net.Http.Headers.RangeHeaderValue; namespace OpenAuth.App.ServiceApp { public class ManageApp : SqlSugarBaseApp { private readonly MqttClientManager _mqttClientManager; private readonly MinioService _minioService; private readonly OpenJobApp _openJobApp; CommonDataManager _commonDataManager; public ManageApp(ISugarUnitOfWork unitWork, ISimpleClient repository, IAuth auth, MqttClientManager mqttClientManager, CommonDataManager commonDataManager, MinioService minioService, OpenJobApp openJobApp) : base(unitWork, repository, auth) { _mqttClientManager = mqttClientManager; _minioService = minioService; _commonDataManager = commonDataManager; _openJobApp = openJobApp; } #region 机场管理 /// /// 分页获取所有数据 /// /// /// /// /// public async Task>>> GetPageList(int page, int limit, string sn, string type, string workspaceid) { RefAsync totalCount = 0; using (var db = UnitWork.CreateContext()) { var list = await db.LasaDronePort.AsQueryable().Includes(a => a.UavList) .LeftJoin((a, b) => a.Id == b.DeviceId) .Where(a => a.IsDelete == false) .WhereIF(!string.IsNullOrEmpty(sn), (a, b) => a.Sn == sn) .WhereIF(!string.IsNullOrEmpty(type), (a, b) => a.TypeId == type) .WhereIF(!string.IsNullOrEmpty(workspaceid), (a, b) => b.WorkSpaceId == workspaceid) .Select((a, b) => new LasaDronePort { Id = a.Id, Name = a.Name, TypeId = a.TypeId, FirmwareVersion = a.FirmwareVersion, CreateTime = a.CreateTime, UpdateTime = a.UpdateTime, OrgId = a.OrgId, Sn = a.Sn, UavList = a.UavList, GateWay = a.GateWay, WorkSpaceId = b.WorkSpaceId }) .ToPageListAsync(page, limit, totalCount); return new Response>> { Result = new PageInfo> { Items = list, Total = totalCount } }; } } /// /// 编辑机场信息 /// /// /// public async Task> EditDronePort(LasaDronePort info) { using (var db = UnitWork.CreateContext()) { //删除项目中的机场数据 await db.LasaSpaceDevice.DeleteAsync(r => r.DeviceId == info.Id); await db.LasaSpaceDevice.InsertAsync(new LasaSpaceDevice { DeviceId = info.Id, WorkSpaceId = info.WorkSpaceId }); var flag = await db.LasaDronePort.UpdateAsync(it => new LasaDronePort() { Name = info.Name, WorkSpaceId = info.WorkSpaceId, }, it => it.Id == info.Id); if (db.Commit()) return new Response { Result = true, Message = "编辑成功" }; else return new Response { Result = false, Message = "编辑失败" }; } } /// /// 修改机场固件信息 /// /// /// public async Task> EditDronePortFirmware(string id, string firmwareVersion) { using (var db = UnitWork.CreateContext()) { var flag = await db.LasaDronePort.UpdateAsync(it => new LasaDronePort() { FirmwareVersion = firmwareVersion }, it => it.Id == id); if (db.Commit()) return new Response { Result = true, Message = "编辑成功" }; else return new Response { Result = false, Message = "编辑失败" }; } } //删除机场信息 public async Task> DeleteDronePort(string id) { using (var db = UnitWork.CreateContext()) { await db.LasaDronePort.UpdateAsync(u => new LasaDronePort { IsDelete = true }, u => u.Id == id); if (db.Commit()) return new Response { Result = true, Message = "删除成功" }; else return new Response { Result = false, Message = "删除失败" }; } } //添加机场信息 public bool AddDronePort(LasaDronePort info) { using (var db = UnitWork.CreateContext()) { if (db.LasaDronePort.GetFirst(r => r.Sn == info.Sn) != null) return false; var flag = db.LasaDronePort.Insert(info); if (db.Commit()) return true; else return false; } } #endregion /// /// 获取无人机列表 /// /// /// /// /// public async Task>>> GetUavPageList(int page, int limit, string sn, string type, string workspaceid) { RefAsync totalCount = 0; using (var db = UnitWork.CreateContext()) { var list = await db.LasaUav.AsQueryable() .LeftJoin((a, b) => a.PId == b.Id) .LeftJoin((a, b, c) => b.Id == c.DeviceId) .Where((a, b, c) => a.IsDelete == false) .WhereIF(!string.IsNullOrEmpty(sn), (a, b, c) => a.Sn == sn) .WhereIF(!string.IsNullOrEmpty(type), (a, b, c) => a.TypeId == type) .WhereIF(!string.IsNullOrEmpty(workspaceid), (a, b, c) => c.WorkSpaceId == workspaceid) .Select((a, b, c) => new { id = a.Id, name = a.Name, pId = a.PId, typeId = a.TypeId, sn = a.Sn, psn = b.Sn, updateTime = a.UpdateTime, isDelete = a.IsDelete, pName = b.Name, // 机场名称 workSpaceId = c.WorkSpaceId, firmwareVersion = a.FirmwareVersion, createTime = a.CreateTime }) .ToPageListAsync(page, limit, totalCount); return new Response>> { Result = new PageInfo> { Items = list, Total = totalCount } }; } } /// /// 获取机场sn获取无人机列表 /// /// /// /// /// public async Task>>> GetUavPageByDockSnNew(int page, int limit, string sn) { RefAsync totalCount = 0; using (var db = UnitWork.CreateContext()) { var dockinfo = await db.LasaDronePort.GetFirstAsync(r => r.Sn == sn); var list = await db.LasaUav.AsQueryable() .LeftJoin((a, b) => a.PId == b.Id) .Where((a, b) => a.IsDelete == false) .WhereIF(dockinfo != null, (a, b) => a.PId == dockinfo.Id) .Select((a, b) => new { id = a.Id, sn = a.Sn, }) .ToPageListAsync(page, limit, totalCount); return new Response>> { Result = new PageInfo> { Items = list, Total = totalCount } }; } } /// /// 获取机场sn获取无人机列表 /// /// /// /// /// public async Task>>> GetUavPageByDocksn(int page, int limit, string sn) { RefAsync totalCount = 0; using (var db = UnitWork.CreateContext()) { var dockinfo = await db.LasaDronePort.GetFirstAsync(r => r.Sn == sn); var list = await db.LasaUav.AsQueryable() .LeftJoin((a, b) => a.PId == b.Id) .Where((a, b) => a.IsDelete == false) .WhereIF(dockinfo != null, (a, b) => a.PId == dockinfo.Id) .Select((a, b) => new { id = a.Id, name = a.Name, pId = a.PId, typeId = a.TypeId, sn = a.Sn, psn = b.Sn, updateTime = a.UpdateTime, isDelete = a.IsDelete, pName = b.Name, // 机场名称 workSpaceId = a.WorkSpaceId, firmwareVersion = a.FirmwareVersion, gateway = b.GateWay }) .ToPageListAsync(page, limit, totalCount); return new Response>> { Result = new PageInfo> { Items = list, Total = totalCount } }; } } /// /// 编辑无人机 /// /// /// public async Task> EditUav(LasaUav info) { using (var db = UnitWork.CreateContext()) { var flag = await db.LasaUav.UpdateAsync(it => new LasaUav() { Name = info.Name, }, it => it.Id == info.Id); if (db.Commit()) return new Response { Result = true, Message = "编辑成功" }; else return new Response { Result = false, Message = "编辑失败" }; } } /// /// 修改无人机固件信息 /// /// /// /// public async Task> EditUavFirmware(string id, string firmwareVersion) { using (var db = UnitWork.CreateContext()) { var flag = await db.LasaUav.UpdateAsync(it => new LasaUav() { FirmwareVersion = firmwareVersion }, it => it.Id == id); if (db.Commit()) return new Response { Result = true, Message = "编辑成功" }; else return new Response { Result = false, Message = "编辑失败" }; } } //删除无人机 public async Task> DeleteUav(string id) { using (var db = UnitWork.CreateContext()) { await db.LasaUav.UpdateAsync(u => new LasaUav { IsDelete = true }, u => u.Id == id); if (db.Commit()) return new Response { Result = true, Message = "删除成功" }; else return new Response { Result = false, Message = "删除失败" }; } } //添加无人机 public bool AddLasaUav(LasaUav info) { using (var db = UnitWork.CreateContext()) { var flag = db.LasaUav.Insert(info); if (db.Commit()) return true; else return false; } } //导出无人机信息 public async Task GetUavSn() { using (var db = UnitWork.CreateContext()) { var snList = await db.LasaUav.AsQueryable().Where(r => r.IsDelete == false).Select(r => r.Sn) .ToListAsync(); var result = string.Join(Environment.NewLine, snList); return result; } } /// /// 获取任务列表 /// /// /// /// /// public async Task>>> GetTaskPageList(int page, int limit, string key, int? status) { RefAsync totalCount = 0; using (var db = UnitWork.CreateContext()) { var list = await db.LasaTask.AsQueryable() .WhereIF(!string.IsNullOrEmpty(key), a => a.TaskName.Contains(key)) .WhereIF(status != null, a => a.Status.Equals(status)) .OrderByDescending(a => a.CreateTime) .ToPageListAsync(page, limit, totalCount); return new Response>> { Result = new PageInfo> { Items = list, Total = totalCount } }; } } //添加任务 public async Task> AddTask(LasaTask task) { var airLine = await Repository .ChangeRepository>() .GetByIdAsync(task.AirLineId); using (var db = UnitWork.CreateContext()) { var user = _auth.GetCurrentUser().User; task.Id = Guid.NewGuid().ToString(); task.CreateId = user.Id; task.CreateUserName = user.Name; task.CreateTime = DateTime.Now; //task.FlightId = Guid.NewGuid().ToString(); // 如:执行时长,单次定时的时间 // 0-飞行计划 1-手飞任务 task.FlightTaskType = 0; // task.PlanExecuteDuration = airLine.ScheduleTime; task.ExpectedFileCount = airLine.PictureTotal; var flag = await db.LasaTask.InsertAsync(task); if (!string.IsNullOrEmpty(task.AIInspection)) { var aiInspection = new LasaAiInspection { Id = Guid.NewGuid().ToString(), TaskId = task.Id, AlgoInstanceId = task.AlgoInstanceId, WarningTitle = task.WarningTitle, WarningContent = task.WarningContent }; await db.LasaAiInspection.InsertAsync(aiInspection); } // //{"0":"立即任务","1":"定时任务", var type = task.TaskType; if (type.Equals(1) || type.Equals(2)) // 定时任务 { // 0 0 10 23 7 ? 2025 // 计算执行时间 //task.ScheduledStartTime = DateTime.Now; //task.ScheduledEndTime = DateTime.Now.AddHours(1); AddOrUpdateOpenJobReq record = new AddOrUpdateOpenJobReq() { JobName = $"{task.Id}", JobType = 0, // 本地任务 JobCall = "OpenAuth.App.OpenJob.FlyTaskJob", JobCallParams = "{\"taskId\":\"" + task.Id + "\"}", Cron = task.PeriodicFormula, Status = 1, // 开启运行 Remark = "定时任务", }; var jobId = _openJobApp.Add(record); _openJobApp.StartJob(jobId); } else if (type.Equals(0)) { var x = DateTime.Now; // 计算执行时间 task.ExecuteTime = x; task.ScheduledStartTime = x; if (task.PlanExecuteDuration != 0L) { task.ScheduledEndTime = x.AddMilliseconds(task.PlanExecuteDuration); } // 调用飞行任务 await ExecuteFlyTask(task.Id); } if (db.Commit()) return new Response { Result = true, Message = "添加成功" }; else return new Response { Result = false, Message = "添加失败" }; } } /// /// 已禁止使用 /// /// /// [Obsolete] //编辑任务 public async Task> EditTask(LasaTask task) { using (var db = UnitWork.CreateContext()) { var flag = await db.LasaTask.UpdateAsync(task); if (db.Commit()) return new Response { Result = true, Message = "编辑成功" }; else return new Response { Result = false, Message = "编辑失败" }; } } //删除任务 public async Task> DeleteTask(string id) { var task = await Repository .ChangeRepository>() .AsQueryable() .Where(it => it.Id == id).SingleAsync(); if (task == null) { return new Response { Result = false, Message = "任务不存在" }; } if (task.CompletedTime != null) // 已完成任务(无论成功失败) { return new Response { Result = false, Message = "已完成任务禁止删除" }; } using (var db = UnitWork.CreateContext()) { // todo 如果任务已经开始取消任务 if (task.Status.Equals(1)) { var flightId = task.FlightId; if (!string.IsNullOrEmpty(flightId)) { var dronePort = await Repository.ChangeRepository>() .GetByIdAsync(task.TaskDronePort); if (dronePort == null) { throw new Exception("指定机场不存在"); } var serialNo = dronePort.Sn; // 取消任务 var cancelTaskTopic = $"thing/product/{serialNo}/services"; var cancelTaskRequest = new TopicServicesRequest() { method = "flighttask_undo", tid = Guid.NewGuid().ToString(), bid = Guid.NewGuid().ToString(), timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(), data = new { flight_ids = new[] { task.FlightId } } }; await _mqttClientManager.PublishAsync(cancelTaskTopic, JsonConvert.SerializeObject(cancelTaskRequest)); } } var flag = await db.LasaTask.DeleteAsync(it => it.Id == id); var taskId = task.Id; var mediaFileList = await db.LasaMediaFile.AsQueryable() .Where(it => it.TaskId == taskId) .ToListAsync(); foreach (var item in mediaFileList) { await _minioService.DeleteFile(item.ObjectKey); } // 定时任务, if (task.TaskType.Equals(1)) { await db.SysOpenJob.AsDeleteable() .Where(it => it.JobCallParams.Contains(taskId)) .ExecuteCommandAsync(); } if (db.Commit()) return new Response { Result = true, Message = "删除成功" }; return new Response { Result = false, Message = "删除失败" }; } } #region 航线管理 /// /// 获取航线列表 /// /// /// /// /// public async Task>>> GetAirLinePageList(AirLineListRequestPage req) { RefAsync totalCount = 0; using (var db = UnitWork.CreateContext()) { var list = await db.LasaAirLine.AsQueryable().LeftJoin((a, b) => a.UavId == b.Id) .WhereIF(!string.IsNullOrEmpty(req.UavTypeId), (a, b) => b.TypeId == req.UavTypeId) .WhereIF(!string.IsNullOrEmpty(req.UavModel), (a, b) => a.UavModel == req.UavModel) .WhereIF(!string.IsNullOrEmpty(req.key), a => a.AirLineName.Contains(req.key)) .WhereIF(!string.IsNullOrEmpty(req.AirLineName), a => a.AirLineName.Contains(req.AirLineName)) .WhereIF(!string.IsNullOrEmpty(req.AirLineType), a => a.AirLineType.Equals(req.AirLineType)) .WhereIF(!string.IsNullOrEmpty(req.Folder), (a) => a.Folder.Equals(req.Folder)) .OrderByIF(true, a => a.CreateTime, req.Ascending ? OrderByType.Asc : OrderByType.Desc) .ToPageListAsync(req.page, req.limit, totalCount); return new Response>> { Result = new PageInfo> { Items = list, Total = totalCount } }; } } //添加航线 public async Task> AddAirLine(LasaAirLine lasaAirLine) { using (var db = UnitWork.CreateContext()) { lasaAirLine.Id = Guid.NewGuid().ToString(); lasaAirLine.CreateId = _auth.GetCurrentUser().User.Id; lasaAirLine.CreateTime = DateTime.Now; var flag = await db.LasaAirLine.InsertAsync(lasaAirLine); if (db.Commit()) return new Response { Result = lasaAirLine.Id, Message = "添加成功" }; else return new Response { Result = "", Message = "添加失败" }; } } //编辑航线 public async Task> EditAirLine(LasaAirLine lasaAirLine) { var oldLasaAirLine = await Repository.ChangeRepository>() .GetByIdAsync(lasaAirLine.Id); using (var db = UnitWork.CreateContext()) { var flag = await db.LasaAirLine.UpdateAsync(lasaAirLine); if (db.Commit()) { // http://175.27.168.120:6013/test/2025061617111990020017.wpml var wmpl = oldLasaAirLine.WPML; // 为保存副本不再删除旧的航线 //await _minioService.DeleteFile(wmpl); return new Response { Result = true, Message = "编辑成功" }; } return new Response { Result = false, Message = "编辑失败" }; } } //删除航线 public async Task> DeleteAirLine(string id) { using (var db = UnitWork.CreateContext()) { var flag = await db.LasaAirLine.DeleteAsync(it => it.Id == id); if (db.Commit()) return new Response { Result = true, Message = "删除成功" }; else return new Response { Result = false, Message = "删除失败" }; } } //生成航线文件 #endregion #region 项目管理 /// /// 获取项目列表 /// /// /// public async Task>> GetWorkspaceList(int isjoin, string key, int state, string order = "\"CreateTime\" desc") { RefAsync totalCount = 0; using (var db = UnitWork.CreateContext()) { var userid = _auth.GetCurrentUser().User.Id; List ids = new List(); if (isjoin == 1) { ids = db.LasaSpaceUser.AsQueryable().Where(r => r.UserId == userid)?.Select(r => r.WorkSpaceId) .ToList(); } var list = await db.LasaWorkspace.AsQueryable() .Where(a => a.IsDelete == false) .WhereIF(!string.IsNullOrEmpty(key), a => a.WorkspaceName.Contains(key)) .WhereIF(state != 0, a => a.Sate == state) .WhereIF(isjoin == 1, a => ids.Contains(a.Id)) .WhereIF(isjoin == 2, a => !ids.Contains(a.Id)) .LeftJoin((a, u) => a.CreateId == u.Id) .Select((a, u) => new { a.Id, a.WorkspaceName, a.WorkspaceDesc, a.Sate, a.CreateTime, a.CreateId, u.Account, u.Name, UserNames = SqlFunc.Subqueryable().Where(r => r.WorkSpaceId == a.Id) .LeftJoin((r, s) => r.UserId == s.Id).SelectStringJoin((r, s) => s.Name, ",") }).MergeTable() .OrderBy(order) .ToListAsync(); return new Response> { Result = list }; } } //添加项目 public async Task> AddWorkspace(WorkSpace info) { using (var db = UnitWork.CreateContext()) { LasaWorkspace lasaWorkspace = info.LasaWorkspace; lasaWorkspace.Id = Guid.NewGuid().ToString(); lasaWorkspace.CreateId = _auth.GetCurrentUser().User.Id; lasaWorkspace.CreateTime = DateTime.Now; List devices = new List(); foreach (var item in info.DeviceIds) { LasaSpaceDevice sd = new LasaSpaceDevice(); sd.WorkSpaceId = lasaWorkspace.Id; sd.DeviceId = item; devices.Add(sd); } List users = new List(); foreach (var item in info.UserIds) { LasaSpaceUser sd = new LasaSpaceUser(); sd.WorkSpaceId = lasaWorkspace.Id; sd.UserId = item; users.Add(sd); } foreach (var item in info.Lockfly) { item.Id = Guid.NewGuid().ToString(); item.WorkSpaceId = lasaWorkspace.Id; await db.LasaSpaceLockFly.InsertAsync(item); } await db.LasaWorkspace.InsertAsync(lasaWorkspace); await db.LasaSpaceDevice.InsertRangeAsync(devices); await db.LasaSpaceUser.InsertRangeAsync(users); if (db.Commit()) return new Response { Result = true, Message = "添加成功" }; else return new Response { Result = false, Message = "添加失败" }; } } //编辑项目 public async Task> EditWorkspace(WorkSpace info) { using (var db = UnitWork.CreateContext()) { LasaWorkspace lasaWorkspace = info.LasaWorkspace; var flag = await db.LasaWorkspace.UpdateAsync(lasaWorkspace); List devices = new List(); foreach (var item in info.DeviceIds) { LasaSpaceDevice sd = new LasaSpaceDevice(); sd.WorkSpaceId = lasaWorkspace.Id; sd.DeviceId = item; devices.Add(sd); } List users = new List(); foreach (var item in info.UserIds) { LasaSpaceUser sd = new LasaSpaceUser(); sd.WorkSpaceId = lasaWorkspace.Id; sd.UserId = item; users.Add(sd); } foreach (var item in info.Lockfly) { if (!string.IsNullOrEmpty(item.Id)) { await db.LasaSpaceLockFly.UpdateAsync(item); } else { item.Id = Guid.NewGuid().ToString(); item.WorkSpaceId = lasaWorkspace.Id; await db.LasaSpaceLockFly.InsertAsync(item); } } await db.LasaSpaceDevice.DeleteAsync(r => r.WorkSpaceId == lasaWorkspace.Id); await db.LasaSpaceDevice.InsertRangeAsync(devices); await db.LasaSpaceUser.DeleteAsync(r => r.WorkSpaceId == lasaWorkspace.Id); await db.LasaSpaceUser.InsertRangeAsync(users); if (db.Commit()) return new Response { Result = true, Message = "编辑成功" }; else return new Response { Result = false, Message = "编辑失败" }; } } //获取无人机信息 public async Task>> GetUavList() { using (var db = UnitWork.CreateContext()) { var list = db.LasaSpaceDevice.AsQueryable()?.Select(r => r.DeviceId).ToList(); var uavlist = await db.LasaDronePort.AsQueryable() .Where(r => !list.Contains(r.Id)) .ToListAsync(); return new Response> { Result = uavlist }; } } /// /// 根据项目id获取项目信息 /// /// /// public async Task> GetWorkSpaceById(string id) { using (var db = UnitWork.CreateContext()) { WorkSpaceRes res = new WorkSpaceRes(); var workspace = await db.LasaWorkspace.AsQueryable().Where(r => r.Id == id && r.IsDelete == false) .FirstAsync(); if (workspace != null) { var sysuser = db.User.AsQueryable() .LeftJoin((r, a) => r.Id == a.UserId) .Where((r, a) => a.WorkSpaceId == id) .Select((r, a) => r).ToList(); var uav = db.LasaDronePort.AsQueryable() .LeftJoin((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 { Result = res }; } } //删除项目 public async Task> DeleteWorkspace(string id) { using (var db = UnitWork.CreateContext()) { await db.LasaWorkspace.UpdateAsync(u => new LasaWorkspace { IsDelete = true }, u => u.Id == id); if (db.Commit()) return new Response { Result = true, Message = "删除成功" }; else return new Response { Result = false, Message = "删除失败" }; } } //归档项目 public async Task> CompleteWorkspace(string id) { using (var db = UnitWork.CreateContext()) { await db.LasaWorkspace.UpdateAsync(u => new LasaWorkspace { Sate = 2 }, u => u.Id == id); if (db.Commit()) return new Response { Result = true, Message = "归档成功" }; else return new Response { Result = false, Message = "归档失败" }; } } #endregion public async Task ExecuteFlyTask(string taskId) { // 任务信息 var task = await Repository.ChangeRepository>().GetByIdAsync(taskId); var dronePort = await Repository.ChangeRepository>() .GetByIdAsync(task.TaskDronePort); if (dronePort == null) { throw new Exception("指定机场不存在"); } // 取值 Dock 3 var dockTypeId = dronePort.TypeId; var serialNo = dronePort.Sn; if (!string.IsNullOrEmpty(task.WorkspaceId)) { var workspace = await Repository.ChangeRepository>() .GetByIdAsync(task.WorkspaceId); if (workspace != null) { // 机场天气信息 阀值(自定义和) 雨量阀值 风速阀值 // 赋值默认阀值 var rainThreshold = 3; // 大雨 var windSpeedThreshold = 12; double weatherWindSpeed = 0; // 天气预报风速 double weatherWindSpeedThreshold = 12; switch (dockTypeId) { case "Dock": rainThreshold = 3; // 大雨 windSpeedThreshold = 12; break; case "Dock 2": rainThreshold = 3; // 大雨 windSpeedThreshold = 8; break; case "Dock 3": rainThreshold = 3; // 大雨 windSpeedThreshold = 8; break; } // 如果未开始云端阻飞,或者未开启天气阻飞,改变天气阻飞默认阀值 if (!workspace.IsWeatherBlockFlight || !workspace.IsCloudBlockFlight) { weatherWindSpeedThreshold = windSpeedThreshold; } // 1. 云端阻飞开启天气阻飞未开启 2. 云端阻飞未开启 3. 云端阻飞开启,天气阻飞开启 if (workspace.IsCloudBlockFlight) { var type = dockTypeId switch { "Dock" => "大疆机场", "Dock 2" => "大疆机场2", "Dock 3" => "大疆机场3", _ => "大疆机场" }; var spaceLockFly = await Repository .ChangeRepository>() .GetSingleAsync(r => r.WorkSpaceId == task.WorkspaceId && r.Name == type); if (spaceLockFly != null) { rainThreshold = Convert.ToInt32(spaceLockFly.RainFall); windSpeedThreshold = spaceLockFly.WindSpeed; weatherWindSpeedThreshold = spaceLockFly.WeatherWindSpeed; } } if (workspace.IsWeatherBlockFlight) { using (var httpClient = new HttpClient()) { // todo 目前地理位置是写死的兰山 var response = await httpClient.GetAsync( "http://v1.yiketianqi.com/api?unescape=1&version=v61&appid=84261622&appsecret=k0WPY4Cx&city=兰山"); if (response.IsSuccessStatusCode) { var content = await response.Content.ReadAsStringAsync(); var weather = JsonConvert.DeserializeObject(content); var winMeterStr = weather.GetValue("win_meter")?.Value(); //风速 if (!string.IsNullOrEmpty(winMeterStr)) { weatherWindSpeed = int.Parse(winMeterStr.Replace("km/h", "")) / 3.6; } } } } // 取得当前风速及天气 // 当前时间减5秒,然后取最新一条数据 // 取得机场序列号 var topicCondition = $"thing/product/{serialNo}/osd"; var time = DateTime.Now.AddSeconds(-5); var log = await Repository .ChangeRepository>() .AsQueryable() .Where(r => r.Topic == topicCondition) .Where(r => r.CreateTime > time) .Where(r => SqlFunc.JsonLike(r.Data, "wind_speed")) .OrderByDescending(r => r.CreateTime) .FirstAsync(); //不太可能为空 if (log != null) { var dataObject = JsonConvert.DeserializeObject(JsonConvert.DeserializeObject(log.Data)); var windSpeed = dataObject["data"]?["wind_speed"]?.Value(); // m/s var rainfall = dataObject["data"]?["rainfall"]?.Value(); // {"0":"无雨","1":"小雨","2":"中雨","3":"大雨"} // 如果没有天气阻飞? if (windSpeedThreshold <= windSpeed || rainThreshold <= rainfall || weatherWindSpeedThreshold <= weatherWindSpeed) { // 不让起飞 // 更新失败原因 var failTask = new LasaTask() { Id = taskId, Status = 2, Reason = "当前天气条件不允许起飞" }; await Repository .ChangeRepository>() .AsUpdateable(failTask) .IgnoreNullColumns() .ExecuteCommandAsync(); throw new Exception("当前天气条件不允许起飞"); } } } } // 如果任务被挂起,则直接失败 if (task.Status == 3) { var taskUpdate = new LasaTask { Id = taskId, //任务因挂起失败 Status = 2, Reason = "任务因挂起执行失败" }; await Repository .ChangeRepository>() .AsUpdateable(taskUpdate) .IgnoreNullColumns() .ExecuteCommandAsync(); return; } // 航线文件信息 var airLine = await Repository.ChangeRepository>() .GetByIdAsync(task.AirLineId); // 航线文件链接 var wpml = airLine.WPML; // 查询sn /*var dronePort = await Repository.ChangeRepository>() .GetByIdAsync(task.TaskDronePort);*/ var topic = string.Format(GatewayManager.FlightTaskPrepare, serialNo); var request = new TopicServicesRequest() { method = "flighttask_prepare", tid = Guid.NewGuid().ToString(), bid = Guid.NewGuid().ToString(), timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds() }; dynamic data = new ExpandoObject(); data.flight_id = Guid.NewGuid().ToString(); // 用任务id 作为 // external if (!string.IsNullOrEmpty(task.ExternalTaskId)) { // 关联detail var detail = await Repository .ChangeRepository>() .GetSingleAsync(r => r.id == task.ExternalTaskId); if (detail == null) { throw new Exception("找不到原始省任务"); } var record = new DroneDocktaskdetail(); record.id = task.ExternalTaskId; record.flighttaskid = data.flight_id; await Repository.ChangeRepository>() .AsUpdateable(record) .IgnoreNullColumns() .ExecuteCommandAsync(); } data.execute_time = DateTimeOffset.Now.ToUnixTimeMilliseconds(); //{"0":"立即任务","1":"定时任务","2":"条件任务"} // 立即任务和定时任务均由execute_time指定执行时间;条件任务支持ready_conditions字段指定任务就绪条件,设备可在指定时间段内满足就绪条件后即可执行;立即任务媒体上传优先级最高,定时任务和条件任务媒体上传优先级相同 // 若task_type任务类型指定为“立即执行”时,设备端限制了30s的时间误差,若设备收到指令的时间与execute_time相差超过30s,将报错且该任务无法正常执行。 data.task_type = task.TaskType; var md5 = await _minioService.GetMetaObject(wpml, ""); // todo 临时固定代码 // http://175.27.168.120:6013/test/2025062209390863860047.kmz // wpml = await _minioService.GetObjectUrl("test", "2025062209390863860047.kmz"); data.file = new { url = wpml, fingerprint = md5 }; //任务执行条件 data.executable_conditions = new { // 可执行任务的机场或飞行器最低存储容量,机场或飞行器存储容量不满足 storage_capacity 时,任务执行失败。 // todo 容量单位是什么? 一个图片多少兆,预计拍多少图片? // 1. 需要多少容量 // 2. 多少图片需要多少容量 // 3. 要存多少图片 // 4. storage_capacity = 1000 // todo 50GB左右共 ,其它型号不一定啊? 先不考虑等它报错 }; // 查询断信息 if (!string.IsNullOrEmpty(task.BreakPoint)) { data.break_point = JsonConvert.DeserializeObject(task.BreakPoint); } // 返航高度 {"max":1500,"min":20,"step":"","unit_name":"米 / m"} data.rth_altitude = task.ReturnAltitude; // 取自任务 // 返航高度模式 {"0":"智能高度","1":"设定高度"} // 智能返航模式下,飞行器将自动规划最佳返航高度。 // 大疆机场当前不支持设置返航高度模式,只能选择'设定高度'模式。当环境,光线不满足视觉系统要求时(譬如傍晚阳光直射、夜间弱光无光),飞行器将使用您设定的返航高度进行直线返航 data.rth_mode = 0; // {"0":"返航","1":"悬停","2":"降落"} // 失控动作,当前固定传的值是 0,即返航。注意该枚举值定义跟飞控跟机场定义的不一致,机场端会进行转换。 data.out_of_control_action = task.LossOfControlAction; // 航线失控动作 保持跟 KMZ 文件一致 // {"0":"继续执行航线任务","1":"退出航线任务,执行遥控器失控动作"} data.exit_wayline_when_rc_lost = 1; // 航线精度类型 {"0":"GPS 任务","1":"高精度 RTK 任务"} // 高精度 RTK 任务:飞行器起飞后会在空中等待 RTK 收敛后再执行任务,等待 RTK 收敛的过程中无法暂停任务。 // 默认场景建议使用该模式。GPS 任务:飞行器无需等待 RTK 收敛便可以直接开始执行。精度要求不高的任务或对起飞时效性要求较高的任务建议使用该模式。 // data.wayline_precision_type = task.WaylinePrecisionType; // 值来自任务 data.wayline_precision_type = task.WaylinePrecisionType; // 是否在模拟器中执行任务 todo 调试时使用 /*data.simulate_mission = new { //118.309405,35.14035 应用科学城坐标 //是否开启模拟器任务 //{"0":"不开启","1":"开启"} is_enable = 1, // 纬度 {"max":"90.0","min":"-90.0"} latitude = 35.140331, // 经度 {"max":"180.0","min":"-180.0"} longitude = 118.309419, // 高度 {"max":"9999.9","min":"-9999.9"unit_name":"米 / m"} altitude = 120.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 是否已全部满足。 data.ready_conditions = new { // 可执行任务的飞行器电池电量百分比阈值,任务开始执行时的飞行器电量必须大于 // todo 获取指定值 计算 还是 battery_capacity = 77, // 设备电量百分比,范围 0-100 // todo 设定时间 begin_time = DateTimeOffset.Now.ToUnixTimeMilliseconds() + 50000, // 任务开始执行时间必须大于该值 // todo 设定结束时间 end_time = DateTimeOffset.Now.ToUnixTimeMilliseconds() + 100000, // 任务结束时间必须 }; } request.SetData(data); // 任务下发 await _mqttClientManager.PublishAsync(topic, JsonConvert.SerializeObject(request)); // 记录任务中间信息 var taskAssign = new LasaTaskAssign { Id = Guid.NewGuid().ToString(), Bid = request.bid, Tid = request.tid, FlightId = data.flight_id, GatewaySn = dronePort.Sn, AirlineId = task.AirLineId, Status = 0, CreateTime = DateTime.Now, UpdateTime = DateTime.Now, TaskId = taskId, Md5 = md5, Wpml = wpml }; await Repository.ChangeRepository>().InsertAsync(taskAssign); } public async Task PendingFlyTask(string taskId) { // todo // todo 查看任务状态(待执行,任务执行,已暂停,已挂起) 1. 待执行,任务执行,需要先取消任务 2. 已暂停,直接挂起任务 3. 已挂起,返回 throw new NotImplementedException(); } public Task UploadFile(IFormFile xmlFile, string folder) { return _minioService.UploadFile(xmlFile, "", folder); } #region 添加地图作业区域 /// /// 添加地图作业区域 /// /// /// public async Task> 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); string _wktModel = model.Geom; 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 { Result = id, Message = "新增成功" }; else return new Response { Message = "新增失败" }; } } /// /// 更新地图作业区域 /// /// /// public async Task> UpdateWorkArea(LasaShpData model) { using (var db = base.UnitWork.CreateContext().Db.CopyNew()) { //格式化数据 //string _wktModel = _commonDataManager.WktDataConvert(model.Geom, "MULTIPOLYGON ZM", 4); string _wktModel = model.Geom; 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 { Result = model.Id, Message = "更新成功" }; else return new Response { Message = "更新失败" }; } } //删除地图作业区域 public async Task> DeleteWorkArea(string id) { using (var db = UnitWork.CreateContext()) { await db.LasaShpData.DeleteByIdAsync(id); if (db.Commit()) return new Response { Result = true, Message = "删除成功" }; else return new Response { Result = false, Message = "删除失败" }; } } /// /// 获取地图作业区域列表 /// /// 项目id /// public async Task>> GetWorkAreaList(string workspaceid, int? state, string type) { using (var db = UnitWork.CreateContext()) { StringBuilder sql = new StringBuilder(); sql.AppendFormat($" Select * from lasa_shpdata"); var list = db.Db.SqlQueryable(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(); } if (!string.IsNullOrEmpty(workspaceid)) { list = list.Where(r => r.WorkSpaceId == workspaceid).ToList(); } return new Response> { Result = list }; } } #endregion #region 添加地图标注 /// /// 添加地图标注 /// /// /// public async Task> AddAnnotation(LasaAnnotation 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); string _wktModel = model.Geom; model.Geom = null; StringBuilder geomSql = new StringBuilder(); geomSql.AppendFormat( $" update lasa_annotation set \"Geom\" = st_geomfromtext('{_wktModel}',4326) where \"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 { Result = id, Message = "新增成功" }; else return new Response { Message = "新增失败" }; } } /// /// 更新地图标注 /// /// /// public async Task> UpdateAnnotation(LasaAnnotation model) { using (var db = base.UnitWork.CreateContext().Db.CopyNew()) { //格式化数据 //string _wktModel = _commonDataManager.WktDataConvert(model.Geom, "MULTIPOLYGON ZM", 4); string _wktModel = model.Geom; model.Geom = null; StringBuilder geomSql = new StringBuilder(); geomSql.AppendFormat( $" update lasa_annotation set \"Geom\" = st_geomfromtext('{_wktModel}',4326) where \"Id\" = '{model.Id}'"); //使用事务提交数据 var transFlag = await db.UseTranAsync(async () => { //插入图斑数据 var flag = await db.Updateable(model).ExecuteCommandAsync(); //修改图斑数据 var flagGeom = await db.Ado.ExecuteCommandAsync(geomSql.ToString()); }); if (transFlag.IsSuccess) return new Response { Result = model.Id, Message = "更新成功" }; else return new Response { Message = "更新失败" }; } } //删除地图标注 public async Task> DeleteAnnotation(string id) { using (var db = UnitWork.CreateContext()) { await db.LasaAnnotation.DeleteByIdAsync(id); if (db.Commit()) return new Response { Result = true, Message = "删除成功" }; else return new Response { Result = false, Message = "删除失败" }; } } /// /// 获取地图标注列表 /// /// 项目id /// public async Task>> GetAnnotationList(string workspaceid, int? state, int? type) { using (var db = UnitWork.CreateContext()) { StringBuilder sql = new StringBuilder(); sql.AppendFormat($" Select * from lasa_annotation "); var list = await db.Db.SqlQueryable(sql.ToString()).ToListAsync(); if (!string.IsNullOrEmpty(workspaceid)) { list = list.Where(r => r.WorkSpaceId == workspaceid).ToList(); } if (type != null && 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> { Result = list }; } } #endregion public async Task TestPreventFlyTask(string taskId) { // 任务信息 var task = await Repository.ChangeRepository>().GetByIdAsync(taskId); var dronePort = await Repository.ChangeRepository>() .GetByIdAsync(task.TaskDronePort); if (dronePort == null) { throw new Exception("指定机场不存在"); } // 取值 Dock 3 var dockTypeId = dronePort.TypeId; var serialNo = dronePort.Sn; if (!string.IsNullOrEmpty(task.WorkspaceId)) { var workspace = await Repository.ChangeRepository>() .GetByIdAsync(task.WorkspaceId); if (workspace != null) { // 机场天气信息 阀值(自定义和) 雨量阀值 风速阀值 // 赋值默认阀值 var rainThreshold = 3; // 大雨 var windSpeedThreshold = 12; double weatherWindSpeed = 0; // 天气预报风速 double weatherWindSpeedThreshold = 0; switch (dockTypeId) { case "Dock": rainThreshold = 3; // 大雨 windSpeedThreshold = 12; break; case "Dock 2": rainThreshold = 3; // 大雨 windSpeedThreshold = 8; break; case "Dock 3": rainThreshold = 3; // 大雨 windSpeedThreshold = 8; break; } // 1. 云端阻飞开启天气阻飞未开启 2. 云端阻飞未开启 3. 云端阻飞开启,天气阻飞开启 if (workspace.IsCloudBlockFlight) { var type = dockTypeId switch { "Dock" => "大疆机场", "Dock 2" => "大疆机场2", "Dock 3" => "大疆机场3", _ => "大疆机场" }; var spaceLockFly = await Repository .ChangeRepository>() .GetSingleAsync(r => r.WorkSpaceId == task.WorkspaceId && r.Name == type); if (spaceLockFly != null) { rainThreshold = Convert.ToInt32(spaceLockFly.RainFall); windSpeedThreshold = spaceLockFly.WindSpeed; weatherWindSpeedThreshold = spaceLockFly.WeatherWindSpeed; } } if (workspace.IsWeatherBlockFlight) { using (var httpClient = new HttpClient()) { // todo 目前地理位置是写死的兰山 var response = await httpClient.GetAsync( "http://v1.yiketianqi.com/api?unescape=1&version=v61&appid=84261622&appsecret=k0WPY4Cx&city=兰山"); if (response.IsSuccessStatusCode) { var content = await response.Content.ReadAsStringAsync(); var weather = JsonConvert.DeserializeObject(content); var winMeterStr = weather.GetValue("win_meter")?.Value(); //风速 if (!string.IsNullOrEmpty(winMeterStr)) { weatherWindSpeed = int.Parse(winMeterStr.Replace("km/h", "")) / 3.6; } } } } Stopwatch x = new Stopwatch(); x.Start(); // 取得当前风速及天气 // 当前时间减5秒,然后取最新一条数据 // 取得机场序列号 var topic = $"thing/product/{serialNo}/osd"; var time = DateTime.Now.AddSeconds(-5); var log = await Repository .ChangeRepository>() .AsQueryable() .Where(r => r.Topic == topic) .Where(r => r.CreateTime > time) .Where(r => SqlFunc.JsonContainsFieldName(r.Data, "wind_speed")) .OrderByDescending(r => r.CreateTime) .FirstAsync(); //SqlFunc.JsonContainsFieldName(log.Data, "wind_speed") x.Stop(); Console.WriteLine("耗时:" + x.ElapsedMilliseconds / 1000); //不太可能为空 if (log != null) { var dataObject = JsonConvert.DeserializeObject(JsonConvert.DeserializeObject(log.Data)); var windSpeed = dataObject["data"]?["wind_speed"]?.Value(); // m/s var rainfall = dataObject["data"]?["rainfall"]?.Value(); // {"0":"无雨","1":"小雨","2":"中雨","3":"大雨"} // 如果没有天气阻飞? if (windSpeedThreshold <= windSpeed || rainThreshold <= rainfall || weatherWindSpeedThreshold <= weatherWindSpeed) { return "fail"; // 不让起飞 // 更新失败原因 var failTask = new LasaTask() { Id = taskId, Status = 2, Reason = "当前天气条件不允许起飞" }; await Repository .ChangeRepository>() .AsUpdateable(failTask) .IgnoreNullColumns() .ExecuteCommandAsync(); throw new Exception("当前天气条件不允许起飞"); } } } } return "ok"; } public LasaDronePort GetDroneDockBySn(string gatewaySn) { var droneDock = Repository.GetSingle(r => r.Sn == gatewaySn); return droneDock; } public LasaTaskAssign GetTaskAssignByBidAndTid(string bid, string tid, int status = 0) { return Repository .ChangeRepository>() .GetSingle(r => r.Bid == bid && r.Tid == tid && r.Status == status); } public LasaTaskAssign GetTaskAssignByFlightId(string flightId) { return Repository .ChangeRepository>() .GetSingle(r => r.FlightId == flightId); } public async Task> GetAirLine(string airLineId) { return new Response() { Result = await Repository .ChangeRepository>() .GetSingleAsync(r => r.Id == airLineId) }; } public async Task>> GetTaskImageList(string taskId) { var list = await Repository .ChangeRepository>() .AsQueryable() .Where(r => r.Id != r.Name) .Where(r => r.TaskId == taskId) .ToListAsync(); return new Response>() { Result = list }; } public async Task> StartDronePortLive(string streamUrl) { var request = new { tid = Guid.NewGuid().ToString(), bid = Guid.NewGuid().ToString(), timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(), method = "live_start_push", data = new { url = streamUrl, url_type = 1, // 8UUXN5400A079H/165-0-7/normal-0 video_id = "8UUXN5400A079H/165-0-7/normal-0", video_quality = 1, } }; //thing/product/{gateway_sn}/services await _mqttClientManager.PublishAsync("thing/product/8UUXN5400A079H/services", JsonConvert.SerializeObject(request)); return new Response() { Result = true }; } public async Task> StopDronePortLive(string streamUrl) { var request = new { tid = Guid.NewGuid().ToString(), bid = Guid.NewGuid().ToString(), timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(), method = "live_stop_push", data = new { video_id = "8UUXN5400A079H/165-0-7/normal-0" } }; await _mqttClientManager.PublishAsync("thing/product/8UUXN5400A079H/services", JsonConvert.SerializeObject(request)); return new Response() { Result = true }; } #region 基本信息统计 public async Task> GetBasicInfo() { using (var db = UnitWork.CreateContext()) { var dronInfo = await db.LasaDronePort.AsQueryable() .Where(r => r.IsDelete == false && r.TypeId.Contains("Dock")).CountAsync(); var otherInfo = await db.LasaDronePort.AsQueryable() .Where(r => r.IsDelete == false && !r.TypeId.Contains("Dock")).CountAsync(); var pilotInfo = await db.SysUserRole.AsQueryable().Where(r => r.RoleId == 555252989157446).CountAsync(); var taskInfo = await db.LasaTask.AsQueryable().CountAsync(); JObject obj = new JObject { { "dronInfo", dronInfo }, { "otherInfo", otherInfo }, { "pilotInfo", pilotInfo }, { "taskInfo", taskInfo } }; return new Response { Result = obj, Message = "获取数据成功" }; } } #endregion public async Task> CreateAirLineFolder(string folderName, string parentId) { LasaLineFolder folder = new LasaLineFolder() { Id = Guid.NewGuid().ToString(), Path = folderName, ParentId = parentId, CreateTime = DateTime.Now }; if (!string.IsNullOrEmpty(parentId)) { var parent = await Repository .ChangeRepository>() .GetByIdAsync(parentId); folder.Ancestor = parent.Ancestor + "/" + folderName; } else { folder.Ancestor = folderName; } return new Response() { Result = await Repository .ChangeRepository>() .InsertAsync(folder) }; } public async Task> DeleteAirLineFolder(string folderId) { var folder = await Repository .ChangeRepository>() .GetByIdAsync(folderId); var ancestor = folder.Ancestor; // 删除文件夹记录 var x = await Repository.ChangeRepository>().DeleteByIdAsync(folderId); // 删除该文件夹下的航线记录 var y = await Repository .ChangeRepository>() .DeleteAsync(r => r.WPML.Contains(ancestor)); return new Response() { Result = x && y }; } public async Task>> ListAirLineFolder() { var nodes = await Repository.ChangeRepository>() .AsQueryable().ToListAsync(); return new Response>() { Result = BuildFolderTree(nodes) }; } public List BuildFolderTree(List folders) { var folderMap = folders.ToDictionary(f => f.Id, f => new LasaLineFolder { Id = f.Id, Path = f.Path, Ancestor = f.Ancestor, Children = new List() }); var rootNodes = new List(); foreach (var folder in folders) { if (string.IsNullOrEmpty(folder.ParentId)) { rootNodes.Add(folderMap[folder.Id]); } else if (folderMap.TryGetValue(folder.ParentId, out var parentNode)) { parentNode.Children.Add(folderMap[folder.Id]); } } return rootNodes; } public async Task> GetTaskPicList(string flightId, long timestamp) { var lasaMediaFiles = await Repository.ChangeRepository>() .AsQueryable() .Where(r => r.FlightId == flightId) .Where(r => r.ObjectKey.Contains("/Remote-Control")) .Where(r => r.ObjectKey.Contains(".jpeg")) .Where(r => r.CreateTime > DateTimeOffset.FromUnixTimeMilliseconds(timestamp).ToLocalTime()) .OrderBy(r => r.CreateTime) .ToListAsync(); if (lasaMediaFiles.Count == 0) { return new Response() { Result = new CommandPictureResponse() }; } var lasaMediaFile = lasaMediaFiles.First(); /*lasaMediaFile.PicLink = "http://" + _minioService.endPoint + "/" + _minioService._bucketName + "/" + lasaMediaFile.ObjectKey;*/ var tempPath = Path.Combine(Path.GetTempPath(), "lasa"); if (!Directory.Exists(tempPath)) { Directory.CreateDirectory(tempPath); } await _minioService .DownLoadObject(_minioService._bucketName, lasaMediaFile.ObjectKey, tempPath, ""); var picPath = Path.Combine(tempPath, lasaMediaFile.ObjectKey); var dimensions = GetImageDimensions(picPath); return new Response() { Result = new CommandPictureResponse() { Issuccess = true, // minio 下载图片,并取得图片二进制数据 pictureLink = "http://" + _minioService.endPoint + "/" + _minioService._bucketName + "/" + lasaMediaFile.ObjectKey, zpkzxx = new ExtData() { imgHeight = dimensions.height, imgWidth = dimensions.width, imgOriginHeight = dimensions.height, imgOriginWidth = dimensions.width, psjj = (double)lasaMediaFile.FocalLength, time = lasaMediaFile.CreateTime, height = (double)lasaMediaFile.Height, lon = lasaMediaFile.Lng, lat = lasaMediaFile.Lat, pitch = 0, roll = 0, yaw = lasaMediaFile.GimbalYawDegree } } }; } public async Task> GetTaskVideoList(string flightId, long timestamp) { var lasaMediaFiles = await Repository.ChangeRepository>() .AsQueryable() .Where(r => r.FlightId == flightId) .Where(r => r.ObjectKey.Contains("/Remote-Control")) .Where(r => r.ObjectKey.Contains("S.mp4")) .Where(r => r.CreateTime > DateTimeOffset.FromUnixTimeMilliseconds(timestamp).ToLocalTime()) .OrderBy(r => r.CreateTime) .ToListAsync(); if (lasaMediaFiles.Count == 0) { return new Response() { Result = new CommandVideoResponse() }; } var lasaMediaFile = lasaMediaFiles.First(); return new Response() { Result = new CommandVideoResponse() { videoLink = "http://" + _minioService.endPoint + "/" + _minioService._bucketName + "/" + lasaMediaFile.ObjectKey, spkzxx = new VideoExtData() { lon = lasaMediaFile.Lng, lat = lasaMediaFile.Lat, angle = lasaMediaFile.GimbalYawDegree, height = lasaMediaFile.RelativeAltitude } } }; } /// /// 获取图片信息 /// /// /// public (int width, int height) GetImageDimensions(string imagePath) { using (var bmp = new Bitmap(imagePath)) { return (bmp.Width, bmp.Height); } } public async Task> ListMediaFolder(string workspaceId) { // 列出所属项目所有 文件 var result = await Repository.ChangeRepository>() .AsQueryable() .WhereIF(!string.IsNullOrEmpty(workspaceId), r => r.WorkspaceId == workspaceId) .Where(r => r.ObjectKey == null) .OrderBy(r => r.CreateTime) .ToListAsync(); // 构建树结构 return BuildFolderTree1(result); } public List BuildFolderTree1(List folders) { var folderMap = folders.ToDictionary(f => f.Id, f => { var x = f; x.Children = new List(); return x; }); var rootNodes = new List(); foreach (var folder in folders) { if (string.IsNullOrEmpty(folder.ParentKey) || folder.ParentKey == "0") { rootNodes.Add(folderMap[folder.Id]); } else if (folderMap.TryGetValue(folder.ParentKey, out var parentNode)) { parentNode.Children.Add(folderMap[folder.Id]); } } return rootNodes; } public async Task> CreateMediaFolder(string name, string parentKey) { //如果 parentKey 0 则为顶级目录 var mediaFile = new LasaMediaFile() { Id = Guid.NewGuid().ToString(), Name = name, ParentKey = parentKey, CreateTime = DateTime.Now, ObjectKey = null }; await Repository.ChangeRepository>().InsertAsync(mediaFile); return new Response() { Result = true }; } public async Task> Test1111(string msg) { var sn = "8UUXN5400A079H"; var result = JsonConvert.DeserializeObject>(msg); var method = result.method; var data = result.data; int flightType = data.flight_task.flight_type; string flightId = data.file.ext.flight_id; // 关于flightId 没有值的问题怎么办??? var taskAssign = GetTaskAssignByFlightId(flightId); var taskId = ""; var taskName = ""; var workspaceId = ""; if (taskAssign != null) { taskId = taskAssign.TaskId; var executeTask = await Repository.AsSugarClient() .Queryable() .SingleAsync(a => a.Id == taskId); if (!string.IsNullOrEmpty(executeTask.TaskName)) { taskName = executeTask.TaskName; } if (!string.IsNullOrEmpty(executeTask.WorkspaceId)) { workspaceId = executeTask.WorkspaceId; } } else { taskName = "指令"; } string objectKey = data.file.object_key; var folderKey = ((string)data.file.object_key).Split("/"); var parentKey = folderKey[2]; var isExist = await Repository.AsSugarClient() .Queryable() .Where(x => x.Id.Equals(parentKey)).CountAsync(); if (isExist == 0) { var date = DateTime.Now; var timeStr = date.ToString("yyyy-MM-dd HH:mm:ss"); var parent1 = new LasaMediaFile() { Id = folderKey[2], FlightId = flightId, TaskId = taskId, ParentKey = "0", Name = $"{taskName} {timeStr}", WorkspaceId = workspaceId, CreateTime = date, }; await Repository.AsSugarClient().Insertable(parent1).ExecuteCommandAsync(); } // 重复检测 var mediaFile = await Repository.AsSugarClient() .Queryable() .Where(a => a.FlightId.Equals(flightId)) .Where(a => a.ObjectKey.Equals(objectKey)).SingleAsync(); if (mediaFile == null) { var type = 0; var preSize = 1; // 判断是不是图片 if (objectKey.EndsWith(".jpeg")) // todo 是否有其它类型的图片,待确定 { preSize = 65535; var fileName = Path.GetFileNameWithoutExtension(objectKey); var fileNameParts = fileName.Split("_"); var suffix = fileNameParts[^1]; type = suffix switch { // 0 未知 1 可见光 2 红外 3 变焦 4.广角 5 视频 "V" => 1, "T" => 2, "Z" => 3, // 变焦 "W" => 4, // 广角 _ => type }; } else if (objectKey.EndsWith(".mp4")) { type = 5; } string suoluokey = ""; long? fileSize = 0; int width = 0, height = 0, focalLength = 0; int offset = 0, length = 0; string model = ""; float? gimbalRoll = 0, gimbalPitch = 0; float? digitalZoomRatio = 1; var fileUrl = "http://" + _minioService.endPoint + "/" + _minioService._bucketName + "/" + objectKey; using (var httpClient = new HttpClient()) { suoluokey = "minipic/" + data.file.name.ToString(); // 目前读取64KB // 添加Range请求头 httpClient.DefaultRequestHeaders.Range = new RangeHeaderValue(0, preSize); try { var response = httpClient .GetAsync(fileUrl, HttpCompletionOption.ResponseHeadersRead).Result; if (response.StatusCode == HttpStatusCode.PartialContent) { var contentRange = response.Content.Headers.ContentRange; if (contentRange != null) { fileSize = contentRange.Length.Value; } if (objectKey.ToLower().EndsWith("jpeg")) { // 成功获取部分内容 var y = response.Content.ReadAsByteArrayAsync().Result; var ms = new MemoryStream(y); var directories = ImageMetadataReader.ReadMetadata(ms); // 获取 XMP 目录(包含 XMP 原始 XML 数据) var xmpDirectory = directories.OfType().FirstOrDefault(); if (xmpDirectory != null) { // 获取 XMP 的 XML 字符串(原始数据) var xmpXml = xmpDirectory.GetXmpProperties(); foreach (var keyValuePair in xmpXml) { switch (keyValuePair.Key) { // // drone-dji:GimbalPitchDegree: -90.00 //drone-dji:GimbalReverse: 0 //drone-dji:GimbalRollDegree: +180.00 //drone-dji:GimbalYawDegree: -96.40 case "drone-dji:GimbalPitchDegree": gimbalPitch = float.Parse(keyValuePair.Value); break; case "drone-dji:GimbalRollDegree": gimbalRoll = float.Parse(keyValuePair.Value); break; } } } foreach (var directory in directories) { if (directory is ExifDirectoryBase) { if (directory.Name.Equals("Exif IFD0")) { foreach (var tag in directory.Tags) { if (tag.Name.Equals("Model")) { model = tag.Description; } } } if (directory.Name.Equals("Exif SubIFD")) { // Digital Zoom Ratio: 1 Exif SubIFD foreach (var tag in directory.Tags) { if (tag.Name.Equals("Digital Zoom Ratio")) { digitalZoomRatio = float.Parse(tag.Description); } if (tag.Name.Equals("Exif Image Width")) { width = int.Parse(tag.Description.Replace("pixels", "") .Trim()); } if (tag.Name.Equals("Exif Image Height")) { height = int.Parse(tag.Description.Replace("pixels", "") .Trim()); } if (tag.Name.Equals("Focal Length 35")) { focalLength = int.Parse(tag.Description .Replace("mm", "") .Trim()); } } } //Console.WriteLine(directory.Name); if (directory.Name.Equals("Exif Thumbnail")) { foreach (var tag in directory.Tags) { if (tag.Name.Equals("Thumbnail Offset")) { offset = int.Parse(tag.Description.Replace("bytes", "") .Trim()); } if (tag.Name.Equals("Thumbnail Length")) { length = int.Parse(tag.Description.Replace("bytes", "") .Trim()); } } } } } ms.Seek(offset + 6, SeekOrigin.Begin); byte[] buffer = new byte[length]; int bytesRead = ms.Read(buffer, 0, length); // 上传缩略图到MinIO await _minioService.PutObjectAsync("", data.file.name.ToString(), suoluokey, new MemoryStream(buffer)); } } else if (response.StatusCode == HttpStatusCode.OK) { // 服务器不支持Range请求,返回完整内容 throw new InvalidOperationException("服务器不支持Range请求"); } else { throw new HttpRequestException($"请求失败: {response.StatusCode}"); } } catch (Exception ex) { throw new Exception($"执行错误: {ex.Message}", ex); } } var createdTimeStr = (string)data.file.metadata.created_time; var createTime = string.IsNullOrEmpty(createdTimeStr) ? DateTime.Now : createdTimeStr.ToDateTime(); // _logger.LogDebug("执行到保存媒体文件之前"); var fileUpload = new LasaMediaFile() { Id = Guid.NewGuid().ToString(), FlightId = flightId, // 计划id TaskId = taskAssign.TaskId, // 任务id DroneModelKey = data.file.ext.drone_model_key, // 无人机型号 PayloadModelKey = data.file.ext.payload_model_key, //这应该可以标明是什么设置 IsOriginal = data.file.ext.is_original, MediaIndex = data.file.ext.media_index, AbsoluteAltitude = data.file.metadata.absolute_altitude, // 拍摄绝对高度 GimbalYawDegree = data.file.metadata.gimbal_yaw_degree, //云台偏航角度 RelativeAltitude = data.file.metadata.relative_altitude, // 拍摄相对高度 Lat = data.file.metadata.shoot_position.lat, Lng = data.file.metadata.shoot_position.lng, Name = data.file.name, ObjectKey = data.file.object_key, Path = data.file.path, // 目前这个好像没有值 CreateTime = createTime, WorkspaceId = workspaceId, ParentKey = folderKey[2], Tid = result.tid, Bid = result.bid, FlightType = flightType, Width = width, Height = height, minipic = suoluokey, Size = fileSize, ShowOnMap = 1, display = 1, FocalLength = focalLength, PayloadModelName = model, Type = type, GimbalPitchDegree = gimbalPitch, GimbalRollDegree = gimbalRoll, DigitalZoomRatio = digitalZoomRatio }; // 添加事务 await Repository.AsSugarClient().Insertable(fileUpload).ExecuteCommandAsync(); } if (result.need_reply.Equals(1)) { var fileUploadCallbackEventReply = new FileUploadCallbackEventReply() { bid = result.bid, tid = result.tid, method = "file_upload_callback", gateway = sn, data = new { result = 0 }, timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(), }; _ = _mqttClientManager.PublishAsync($"thing/product/{sn}/events_reply", JsonConvert.SerializeObject(fileUploadCallbackEventReply)); } var expectFileCount = data.flight_task.expected_file_count; var uploadedFileCount = data.flight_task.uploaded_file_count; var taskRecord = new LasaTask() { Id = taskId, ExpectedFileCount = expectFileCount, // 期望文件数量 UploadedFileCount = uploadedFileCount // 已上传文件数量 }; await Repository.AsSugarClient().Updateable(taskRecord) .IgnoreNullColumns().ExecuteCommandAsync(); return new Response() { Result = true }; } /// /// 保存手飞任务信息 /// /// /// public async Task> SaveHandFlyTask(LasaHandFlyTask task) { var date = DateTime.Now; var lasaTask = new LasaTask { Id = Guid.NewGuid().ToString(), TaskName = $"手飞任务 {date:yyyyMMddHHmmss}", TaskType = 0, //0 计划任务 1 手飞任务 FlightTaskType = 1, WorkspaceId = task.WorkspaceId, FlightId = task.FlightId, Status = 0, // 待执行 CreateTime = date, ScheduledStartTime = date, // 任务计划执行时间 ExecuteTime = date // 任务开始执行时间 }; await Repository.ChangeRepository>().InsertAsync(lasaTask); return new Response() { Result = new { taskId = lasaTask.Id } }; } public async Task> CallAiModel(CallAiModel req) { var task = await Repository .ChangeRepository>() .GetByIdAsync(req.TaskId); ; using var db = Repository.AsSugarClient(); try { db.Ado.BeginTran(); if (task.FlightTaskType.Equals(1)) { var aiInspection = new LasaAiInspection { Id = Guid.NewGuid().ToString(), TaskId = req.TaskId, AlgoInstanceId = req.AlgoInstanceId, WarningTitle = req.WarningTitle, WarningContent = req.WarningContent }; // 更新 await db.Updateable(aiInspection).IgnoreNullColumns().ExecuteCommandAsync(); } var algoInstances = await db .Queryable() .Where(x => x.Id == req.AlgoInstanceId) .ToListAsync(); var tagsIds = algoInstances.Select(x => x.Tags).ToList(); var algoIds = algoInstances.First().AlgoIds.Split(",").ToArray(); var algo = await db .Queryable() .Where(x => x.Id == algoIds[0]) .FirstAsync(); dynamic json = new ExpandoObject(); var x = SnowFlakeSingle.instance; //var pushUrl = $"rtmp://box.wisestcity.com:1935/live/{x.NextId()}"; var pushUrl = "rtmp://box.wisestcity.com:1935/live/11"; json.rtmp_url = req.RtmpUrl; json.push_url = pushUrl; json.imgsz = 640; json.frame_skip = 1; //json.model_name = algo.Path; json.model_name = "yolo12x.pt"; json.taskname = task.TaskName; json.taskid = req.TaskId; var taskRecord = new LasaTask() { Id = req.TaskId, Status = 1, PushUrl = pushUrl }; await db.Updateable(taskRecord).IgnoreNullColumns().ExecuteCommandAsync(); var tag = await db .Queryable() .Where(x => tagsIds.Contains(x.Id)) .Select(x => x.EnumValue) .ToArrayAsync(); json.tag = new int [0, 1, 2, 3, 4, 5]; var content = new StringContent(JsonConvert.SerializeObject(json), Encoding.UTF8, "application/json"); using var httpClient = new HttpClient(); var response = await httpClient.PostAsync("http://192.168.10.131:9025/start_detection", content); db.Ado.CommitTran(); } catch (Exception ex) { db.Ado.RollbackTran(); throw ex; //throw new Exception("调用智能巡检失败"); } return new Response() { Result = true }; } public async Task> EndHandFlyTask(string taskid) { var task = new LasaTask() { Id = taskid, // ScheduledEndTime = DateTime.Now, CompletedTime = DateTime.Now, Status = 5 // 成功 }; using var db = Repository.AsSugarClient(); try { db.Ado.BeginTran(); await db.Updateable(task).IgnoreNullColumns().ExecuteCommandAsync(); // 结束智能巡检 using var httpClient = new HttpClient(); await httpClient.PostAsync("http://192.168.10.131:9025/stop_detection", null); //var response = await httpClient.PostAsync("http://192.168.10.131:9025/stop_detection"); db.Ado.CommitTran(); } catch (Exception ex) { db.Ado.RollbackTran(); throw new Exception("结束任务失败"); } return new Response() { Result = true }; } } }