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 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.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<LasaDronePort, SugarDbContext>
    {
        private readonly MqttClientManager _mqttClientManager;
        private readonly MinioService _minioService;
        private readonly OpenJobApp _openJobApp;
        CommonDataManager _commonDataManager;

        public ManageApp(ISugarUnitOfWork<SugarDbContext> unitWork, ISimpleClient<LasaDronePort> repository, IAuth auth,
            MqttClientManager mqttClientManager, CommonDataManager commonDataManager, MinioService minioService,
            OpenJobApp openJobApp)
            : base(unitWork, repository, auth)
        {
            _mqttClientManager = mqttClientManager;
            _minioService = minioService;
            _commonDataManager = commonDataManager;
            _openJobApp = openJobApp;
        }

        #region 机场管理

        /// <summary>
        /// 分页获取所有数据 
        /// </summary>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<Response<PageInfo<List<LasaDronePort>>>> GetPageList(int page, int limit, string sn,
            string type, string workspaceid)
        {
            RefAsync<int> totalCount = 0;
            using (var db = UnitWork.CreateContext())
            {
                var list = await db.LasaDronePort.AsQueryable().Includes(a => a.UavList)
                    .LeftJoin<LasaSpaceDevice>((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<PageInfo<List<LasaDronePort>>>
                {
                    Result = new PageInfo<List<LasaDronePort>> { Items = list, Total = totalCount }
                };
            }
        }

        /// <summary>
        /// 编辑机场信息
        /// </summary>
        /// <param name="task"></param>
        /// <returns></returns>
        public async Task<Response<bool>> 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<bool> { Result = true, Message = "编辑成功" };
                else
                    return new Response<bool> { Result = false, Message = "编辑失败" };
            }
        }

        /// <summary>
        /// 修改机场固件信息
        /// </summary>
        /// <param name="info"></param>
        /// <returns></returns>
        public async Task<Response<bool>> 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<bool> { Result = true, Message = "编辑成功" };
                else
                    return new Response<bool> { Result = false, Message = "编辑失败" };
            }
        }

        //删除机场信息
        public async Task<Response<bool>> 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<bool> { Result = true, Message = "删除成功" };
                else
                    return new Response<bool> { 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

        /// <summary>
        /// 获取无人机列表
        /// </summary>
        /// <param name="page"></param>
        /// <param name="limit"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<Response<PageInfo<List<dynamic>>>> GetUavPageList(int page, int limit, string sn, string type,
            string workspaceid)
        {
            RefAsync<int> totalCount = 0;
            using (var db = UnitWork.CreateContext())
            {
                var list = await db.LasaUav.AsQueryable()
                    .LeftJoin<LasaDronePort>((a, b) => a.PId == b.Id)
                    .LeftJoin<LasaSpaceDevice>((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<dynamic>((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<PageInfo<List<dynamic>>>
                {
                    Result = new PageInfo<List<dynamic>> { Items = list, Total = totalCount }
                };
            }
        }

        /// <summary>
        /// 获取机场sn获取无人机列表
        /// </summary>
        /// <param name="page"></param>
        /// <param name="limit"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<Response<PageInfo<List<dynamic>>>> GetUavPageByDockSnNew(int page, int limit, string sn)
        {
            RefAsync<int> totalCount = 0;
            using (var db = UnitWork.CreateContext())
            {
                var dockinfo = await db.LasaDronePort.GetFirstAsync(r => r.Sn == sn);

                var list = await db.LasaUav.AsQueryable()
                    .LeftJoin<LasaDronePort>((a, b) => a.PId == b.Id)
                    .Where((a, b) => a.IsDelete == false)
                    .WhereIF(dockinfo != null, (a, b) => a.PId == dockinfo.Id)
                    .Select<dynamic>((a, b) => new
                    {
                        id = a.Id,
                        sn = a.Sn,
                    })
                    .ToPageListAsync(page, limit, totalCount);
                return new Response<PageInfo<List<dynamic>>>
                {
                    Result = new PageInfo<List<dynamic>> { Items = list, Total = totalCount }
                };
            }
        }


        /// <summary>
        /// 获取机场sn获取无人机列表
        /// </summary>
        /// <param name="page"></param>
        /// <param name="limit"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<Response<PageInfo<List<dynamic>>>> GetUavPageByDocksn(int page, int limit, string sn)
        {
            RefAsync<int> totalCount = 0;
            using (var db = UnitWork.CreateContext())
            {
                var dockinfo = await db.LasaDronePort.GetFirstAsync(r => r.Sn == sn);

                var list = await db.LasaUav.AsQueryable()
                    .LeftJoin<LasaDronePort>((a, b) => a.PId == b.Id)
                    .Where((a, b) => a.IsDelete == false)
                    .WhereIF(dockinfo != null, (a, b) => a.PId == dockinfo.Id)
                    .Select<dynamic>((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<PageInfo<List<dynamic>>>
                {
                    Result = new PageInfo<List<dynamic>> { Items = list, Total = totalCount }
                };
            }
        }

        /// <summary>
        /// 编辑无人机
        /// </summary>
        /// <param name="task"></param>
        /// <returns></returns>
        public async Task<Response<bool>> 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<bool> { Result = true, Message = "编辑成功" };
                else
                    return new Response<bool> { Result = false, Message = "编辑失败" };
            }
        }

        /// <summary>
        /// 修改无人机固件信息
        /// </summary>
        /// <param name="id"></param>
        /// <param name="firmwareVersion"></param>
        /// <returns></returns>
        public async Task<Response<bool>> 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<bool> { Result = true, Message = "编辑成功" };
                else
                    return new Response<bool> { Result = false, Message = "编辑失败" };
            }
        }

        //删除无人机
        public async Task<Response<bool>> 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<bool> { Result = true, Message = "删除成功" };
                else
                    return new Response<bool> { 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<string> 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;
            }
        }

        /// <summary>
        /// 获取任务列表
        /// </summary>
        /// <param name="page"></param>
        /// <param name="limit"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<Response<PageInfo<List<LasaTask>>>> GetTaskPageList(int page, int limit, string key,
            int? status)
        {
            RefAsync<int> 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<PageInfo<List<LasaTask>>>
                {
                    Result = new PageInfo<List<LasaTask>> { Items = list, Total = totalCount }
                };
            }
        }

        //添加任务
        public async Task<Response<bool>> AddTask(LasaTask task)
        {
            var airLine = await Repository
                .ChangeRepository<SugarRepositiry<LasaAirLine>>()
                .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);
                //   //{"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.ServiceApp.FlyTaskJob",
                        JobCallParams = "{\"taskId\":\"" + task.Id + "\"}",
                        Cron = task.PeriodicFormula,
                        Status = 1, // 开启运行 todo 关于修改后关闭问题
                        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<bool> { Result = true, Message = "添加成功" };
                else
                    return new Response<bool> { Result = false, Message = "添加失败" };
            }
        }

        /// <summary>
        /// 已禁止使用
        /// </summary>
        /// <param name="task"></param>
        /// <returns></returns>
        [Obsolete]
        //编辑任务
        public async Task<Response<bool>> EditTask(LasaTask task)
        {
            using (var db = UnitWork.CreateContext())
            {
                var flag = await db.LasaTask.UpdateAsync(task);
                if (db.Commit())
                    return new Response<bool> { Result = true, Message = "编辑成功" };
                else
                    return new Response<bool> { Result = false, Message = "编辑失败" };
            }
        }

        //删除任务
        public async Task<Response<bool>> DeleteTask(string id)
        {
            var task = await Repository
                .ChangeRepository<SugarRepositiry<LasaTask>>()
                .AsQueryable()
                .Where(it => it.Id == id).SingleAsync();
            if (task == null)
            {
                return new Response<bool> { Result = false, Message = "任务不存在" };
            }

            if (task.CompletedTime != null) // 已完成任务（无论成功失败）
            {
                return new Response<bool> { 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<SugarRepositiry<LasaDronePort>>()
                            .GetByIdAsync(task.TaskDronePort);
                        if (dronePort == null)
                        {
                            throw new Exception("指定机场不存在");
                        }

                        var serialNo = dronePort.Sn;
                        // 取消任务
                        var cancelTaskTopic = $"thing/product/{serialNo}/services";
                        var cancelTaskRequest = new TopicServicesRequest<object>()
                        {
                            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<bool> { Result = true, Message = "删除成功" };
                return new Response<bool> { Result = false, Message = "删除失败" };
            }
        }

        #region 航线管理

        /// <summary>
        /// 获取航线列表
        /// </summary>
        /// <param name="page"></param>
        /// <param name="limit"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<Response<PageInfo<List<LasaAirLine>>>> GetAirLinePageList(AirLineListRequestPage req)
        {
            RefAsync<int> totalCount = 0;
            using (var db = UnitWork.CreateContext())
            {
                var list = await db.LasaAirLine.AsQueryable().LeftJoin<LasaUav>((a, b) => a.UavId == b.Id)
                    .WhereIF(!string.IsNullOrEmpty(req.UavTypeId), (a, b) => b.TypeId == req.UavTypeId)
                    .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<PageInfo<List<LasaAirLine>>>
                {
                    Result = new PageInfo<List<LasaAirLine>> { Items = list, Total = totalCount }
                };
            }
        }

        //添加航线
        public async Task<Response<bool>> 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<bool> { Result = true, Message = "添加成功" };
                else
                    return new Response<bool> { Result = false, Message = "添加失败" };
            }
        }

        //编辑航线
        public async Task<Response<bool>> EditAirLine(LasaAirLine lasaAirLine)
        {
            var oldLasaAirLine = await Repository.ChangeRepository<SugarRepositiry<LasaAirLine>>()
                .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<bool> { Result = true, Message = "编辑成功" };
                }

                return new Response<bool> { Result = false, Message = "编辑失败" };
            }
        }

        //删除航线
        public async Task<Response<bool>> DeleteAirLine(string id)
        {
            using (var db = UnitWork.CreateContext())
            {
                var flag = await db.LasaAirLine.DeleteAsync(it => it.Id == id);
                if (db.Commit())
                    return new Response<bool> { Result = true, Message = "删除成功" };
                else
                    return new Response<bool> { Result = false, Message = "删除失败" };
            }
        }
        //生成航线文件

        #endregion

        #region 项目管理

        /// <summary>
        /// 获取项目列表
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<Response<List<dynamic>>> GetWorkspaceList(int isjoin, string key, int state,
            string order = "\"CreateTime\" desc")
        {
            RefAsync<int> totalCount = 0;
            using (var db = UnitWork.CreateContext())
            {
                var userid = _auth.GetCurrentUser().User.Id;
                List<string> ids = new List<string>();
                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<SysUser>((a, u) => a.CreateId == u.Id)
                    .Select<dynamic>((a, u) => new
                    {
                        a.Id,
                        a.WorkspaceName,
                        a.WorkspaceDesc,
                        a.Sate,
                        a.CreateTime,
                        a.CreateId,
                        u.Account,
                        u.Name,
                        UserNames = SqlFunc.Subqueryable<LasaSpaceUser>().Where(r => r.WorkSpaceId == a.Id)
                            .LeftJoin<SysUser>((r, s) => r.UserId == s.Id).SelectStringJoin((r, s) => s.Name, ",")
                    }).MergeTable()
                    .OrderBy(order)
                    .ToListAsync();

                return new Response<List<dynamic>>
                {
                    Result = list
                };
            }
        }

        //添加项目 
        public async Task<Response<bool>> 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<LasaSpaceDevice> devices = new List<LasaSpaceDevice>();
                foreach (var item in info.DeviceIds)
                {
                    LasaSpaceDevice sd = new LasaSpaceDevice();
                    sd.WorkSpaceId = lasaWorkspace.Id;
                    sd.DeviceId = item;
                    devices.Add(sd);
                }

                List<LasaSpaceUser> users = new List<LasaSpaceUser>();
                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<bool> { Result = true, Message = "添加成功" };
                else
                    return new Response<bool> { Result = false, Message = "添加失败" };
            }
        }

        //编辑项目 
        public async Task<Response<bool>> EditWorkspace(WorkSpace info)
        {
            using (var db = UnitWork.CreateContext())
            {
                LasaWorkspace lasaWorkspace = info.LasaWorkspace;
                var flag = await db.LasaWorkspace.UpdateAsync(lasaWorkspace);
                List<LasaSpaceDevice> devices = new List<LasaSpaceDevice>();
                foreach (var item in info.DeviceIds)
                {
                    LasaSpaceDevice sd = new LasaSpaceDevice();
                    sd.WorkSpaceId = lasaWorkspace.Id;
                    sd.DeviceId = item;
                    devices.Add(sd);
                }

                List<LasaSpaceUser> users = new List<LasaSpaceUser>();
                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<bool> { Result = true, Message = "编辑成功" };
                else
                    return new Response<bool> { Result = false, Message = "编辑失败" };
            }
        }

        //获取无人机信息
        public async Task<Response<List<LasaDronePort>>> 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<List<LasaDronePort>>
                {
                    Result = uavlist
                };
            }
        }

        /// <summary>
        /// 根据项目id获取项目信息
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<Response<WorkSpaceRes>> 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<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
                };
            }
        }

        //删除项目 
        public async Task<Response<bool>> 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<bool> { Result = true, Message = "删除成功" };
                else
                    return new Response<bool> { Result = false, Message = "删除失败" };
            }
        }

        //归档项目 
        public async Task<Response<bool>> 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<bool> { Result = true, Message = "归档成功" };
                else
                    return new Response<bool> { Result = false, Message = "归档失败" };
            }
        }

        #endregion

        public async Task ExecuteFlyTask(string taskId)
        {
            // 任务信息
            var task = await Repository.ChangeRepository<SugarRepositiry<LasaTask>>().GetByIdAsync(taskId);
            var dronePort = await Repository.ChangeRepository<SugarRepositiry<LasaDronePort>>()
                .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<SugarRepositiry<LasaWorkspace>>()
                    .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<SugarRepositiry<LasaSpaceLockFly>>()
                            .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<JObject>(content);
                                var winMeterStr =
                                    weather.GetValue("win_meter")?.Value<string>(); //风速
                                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<SugarRepositiry<LasaLog>>()
                        .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<JObject>(JsonConvert.DeserializeObject<string>(log.Data));
                        var windSpeed = dataObject["data"]?["wind_speed"]?.Value<float>(); // m/s
                        var rainfall =
                            dataObject["data"]?["rainfall"]?.Value<int>(); // {"0":"无雨","1":"小雨","2":"中雨","3":"大雨"}

                        // 如果没有天气阻飞？
                        if (windSpeedThreshold <= windSpeed || rainThreshold <= rainfall ||
                            weatherWindSpeedThreshold <= weatherWindSpeed)
                        {
                            // 不让起飞
                            // 更新失败原因
                            var failTask = new LasaTask()
                            {
                                Id = taskId,
                                Status = 2,
                                Reason = "当前天气条件不允许起飞"
                            };
                            await Repository
                                .ChangeRepository<SugarRepositiry<LasaTask>>()
                                .AsUpdateable(failTask)
                                .IgnoreNullColumns()
                                .ExecuteCommandAsync();
                            throw new Exception("当前天气条件不允许起飞");
                        }
                    }
                }
            }

            // 如果任务被挂起，则直接失败
            if (task.Status == 3)
            {
                var taskUpdate = new LasaTask
                {
                    Id = taskId,
                    //任务因挂起失败
                    Status = 2,
                    Reason = "任务因挂起执行失败"
                };
                await Repository
                    .ChangeRepository<SugarRepositiry<LasaTask>>()
                    .AsUpdateable(taskUpdate)
                    .IgnoreNullColumns()
                    .ExecuteCommandAsync();
                return;
            }

            // 航线文件信息
            var airLine = await Repository.ChangeRepository<SugarRepositiry<LasaAirLine>>()
                .GetByIdAsync(task.AirLineId);
            // 航线文件链接
            var wpml = airLine.WPML;
            //  查询sn
            /*var dronePort = await Repository.ChangeRepository<SugarRepositiry<LasaDronePort>>()
                .GetByIdAsync(task.TaskDronePort);*/

            var topic = string.Format(GatewayManager.FlightTaskPrepare, serialNo);
            var request = new TopicServicesRequest<object>()
            {
                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<SugarRepositiry<DroneDocktaskdetail>>()
                    .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<SugarRepositiry<DroneDocktaskdetail>>()
                    .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<SugarRepositiry<LasaTaskAssign>>().InsertAsync(taskAssign);
            // todo 更新任务状态？
        }

        public async Task PendingFlyTask(string taskId)
        {
            // todo 
            // todo 查看任务状态（待执行，任务执行，已暂停，已挂起） 1. 待执行，任务执行，需要先取消任务 2. 已暂停，直接挂起任务 3. 已挂起，返回
            throw new NotImplementedException();
        }

        public Task<string> UploadFile(IFormFile xmlFile, string folder)
        {
            return _minioService.UploadFile(xmlFile, "", folder);
        }

        #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);
                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<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);
                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<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");
                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();
                }

                if (!string.IsNullOrEmpty(workspaceid))
                {
                    list = list.Where(r => r.WorkSpaceId == workspaceid).ToList();
                }

                return new Response<List<LasaShpData>>
                {
                    Result = list
                };
            }
        }

        #endregion

        #region 添加地图标注

        /// <summary>
        /// 添加地图标注
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public async Task<Response<string>> 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<string>
                    {
                        Result = id,
                        Message = "新增成功"
                    };
                else
                    return new Response<string>
                    {
                        Message = "新增失败"
                    };
            }
        }

        /// <summary>
        /// 更新地图标注
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public async Task<Response<string>> 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<string>
                    {
                        Result = model.Id,
                        Message = "更新成功"
                    };
                else
                    return new Response<string>
                    {
                        Message = "更新失败"
                    };
            }
        }

        //删除地图标注
        public async Task<Response<bool>> DeleteAnnotation(string id)
        {
            using (var db = UnitWork.CreateContext())
            {
                await db.LasaAnnotation.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<LasaAnnotation>>> 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<LasaAnnotation>(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<List<LasaAnnotation>>
                {
                    Result = list
                };
            }
        }

        #endregion

        public async Task<string> TestPreventFlyTask(string taskId)
        {
// 任务信息
            var task = await Repository.ChangeRepository<SugarRepositiry<LasaTask>>().GetByIdAsync(taskId);
            var dronePort = await Repository.ChangeRepository<SugarRepositiry<LasaDronePort>>()
                .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<SugarRepositiry<LasaWorkspace>>()
                    .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<SugarRepositiry<LasaSpaceLockFly>>()
                            .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<JObject>(content);
                                var winMeterStr =
                                    weather.GetValue("win_meter")?.Value<string>(); //风速
                                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<SugarRepositiry<LasaLog>>()
                        .AsQueryable()
                        .Where(r => r.Topic == topic)
                        .Where(r => r.CreateTime > time)
                        .Where(r => SqlFunc.JsonLike(r.Data, "wind_speed"))
                        .OrderByDescending(r => r.CreateTime)
                        .FirstAsync();
                    x.Stop();
                    Console.WriteLine("耗时：" + x.ElapsedMilliseconds / 1000);
                    //不太可能为空
                    if (log != null)
                    {
                        var dataObject =
                            JsonConvert.DeserializeObject<JObject>(JsonConvert.DeserializeObject<string>(log.Data));
                        var windSpeed = dataObject["data"]?["wind_speed"]?.Value<float>(); // m/s
                        var rainfall =
                            dataObject["data"]?["rainfall"]?.Value<int>(); // {"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<SugarRepositiry<LasaTask>>()
                                .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<SugarRepositiry<LasaTaskAssign>>()
                .GetSingle(r => r.Bid == bid && r.Tid == tid && r.Status == status);
        }


        public LasaTaskAssign GetTaskAssignByFlightId(string flightId)
        {
            return Repository
                .ChangeRepository<SugarRepositiry<LasaTaskAssign>>()
                .GetSingle(r => r.FlightId == flightId);
        }

        public async Task<Response<LasaAirLine>> GetAirLine(string airLineId)
        {
            return new Response<LasaAirLine>()
            {
                Result = await Repository
                    .ChangeRepository<SugarRepositiry<LasaAirLine>>()
                    .GetSingleAsync(r => r.Id == airLineId)
            };
        }

        public async Task<Response<List<LasaMediaFile>>> GetTaskImageList(string taskId)
        {
            var list = await Repository
                .ChangeRepository<SugarRepositiry<LasaMediaFile>>()
                .AsQueryable()
                .Where(r => r.Id != r.Name)
                .Where(r => r.TaskId == taskId)
                .ToListAsync();
            return new Response<List<LasaMediaFile>>()
            {
                Result = list
            };
        }

        public async Task<Response<bool>> 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<bool>()
            {
                Result = true
            };
        }

        public async Task<Response<bool>> 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<bool>()
            {
                Result = true
            };
        }

        #region 基本信息统计

        public async Task<Response<JObject>> 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<JObject> { Result = obj, Message = "获取数据成功" };
            }
        }

        #endregion

        public async Task<Response<bool>> 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<SugarRepositiry<LasaLineFolder>>()
                    .GetByIdAsync(parentId);
                folder.Ancestor = parent.Ancestor + "/" + folderName;
            }
            else
            {
                folder.Ancestor = folderName;
            }

            return new Response<bool>()
            {
                Result = await Repository
                    .ChangeRepository<SugarRepositiry<LasaLineFolder>>()
                    .InsertAsync(folder)
            };
        }

        public async Task<Response<bool>> DeleteAirLineFolder(string folderId)
        {
            var folder = await Repository
                .ChangeRepository<SugarRepositiry<LasaLineFolder>>()
                .GetByIdAsync(folderId);
            var ancestor = folder.Ancestor;
            // 删除文件夹记录
            var x = await Repository.ChangeRepository<SugarRepositiry<LasaLineFolder>>().DeleteByIdAsync(folderId);
            // 删除该文件夹下的航线记录
            var y = await Repository
                .ChangeRepository<SugarRepositiry<LasaAirLine>>()
                .DeleteAsync(r => r.WPML.Contains(ancestor));
            return new Response<bool>()
            {
                Result = x && y
            };
        }

        public async Task<Response<List<LasaLineFolder>>> ListAirLineFolder()
        {
            var nodes = await Repository.ChangeRepository<SugarRepositiry<LasaLineFolder>>()
                .AsQueryable().ToListAsync();
            return new Response<List<LasaLineFolder>>()
            {
                Result = BuildFolderTree(nodes)
            };
        }

        public List<LasaLineFolder> BuildFolderTree(List<LasaLineFolder> folders)
        {
            var folderMap = folders.ToDictionary(f => f.Id, f => new LasaLineFolder
            {
                Id = f.Id,
                Path = f.Path,
                Ancestor = f.Ancestor,
                Children = new List<LasaLineFolder>()
            });

            var rootNodes = new List<LasaLineFolder>();

            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<Response<CommandPictureResponse>> GetTaskPicList(string flightId, long timestamp)
        {
            var lasaMediaFiles = await Repository.ChangeRepository<SugarRepositiry<LasaMediaFile>>()
                .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<CommandPictureResponse>()
                {
                    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<CommandPictureResponse>()
            {
                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 = lasaMediaFile.FocalLength,
                        time = lasaMediaFile.CreateTime,
                        height = lasaMediaFile.Height,
                        lon = lasaMediaFile.Lng,
                        lat = lasaMediaFile.Lat,
                        pitch = 0,
                        roll = 0,
                        yaw = lasaMediaFile.GimbalYawDegree
                    }
                }
            };
        }


        public async Task<Response<CommandVideoResponse>> GetTaskVideoList(string flightId, long timestamp)
        {
            var lasaMediaFiles = await Repository.ChangeRepository<SugarRepositiry<LasaMediaFile>>()
                .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<CommandVideoResponse>()
                {
                    Result = new CommandVideoResponse()
                };
            }

            var lasaMediaFile = lasaMediaFiles.First();
            return new Response<CommandVideoResponse>()
            {
                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
                    }
                }
            };
        }

        /// <summary>
        /// 获取图片信息
        /// </summary>
        /// <param name="imagePath"></param>
        /// <returns></returns>
        public (int width, int height) GetImageDimensions(string imagePath)
        {
            using (var bmp = new Bitmap(imagePath))
            {
                return (bmp.Width, bmp.Height);
            }
        }

        public async Task<List<LasaMediaFile>> ListMediaFolder(string workspaceId)
        {
            // 列出所属项目所有 文件
            var result = await Repository.ChangeRepository<SugarRepositiry<LasaMediaFile>>()
                .AsQueryable()
                .WhereIF(!string.IsNullOrEmpty(workspaceId), r => r.WorkspaceId == workspaceId)
                .Where(r => r.ObjectKey == null)
                .OrderBy(r => r.CreateTime)
                .ToListAsync();
            // 构建树结构 
            return BuildFolderTree1(result);
        }


        public List<LasaMediaFile> BuildFolderTree1(List<LasaMediaFile> folders)
        {
            var folderMap = folders.ToDictionary(f => f.Id, f =>
            {
                var x = f;
                x.Children = new List<LasaMediaFile>();
                return x;
            });

            var rootNodes = new List<LasaMediaFile>();

            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<Response<bool>> 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<SugarRepositiry<LasaMediaFile>>().InsertAsync(mediaFile);
            return new Response<bool>()
            {
                Result = true
            };
        }

        public async Task<Response<bool>> Test1111(string msg)
        {
            var sn = "8UUXN5400A079H";
            var result = JsonConvert.DeserializeObject<TopicServicesRequest<dynamic>>(msg);
            var method = result.method;
            var data = result.data;
            int flightType = data.flight_task.flight_type;
            string flightId = data.file.ext.flight_id;
            var taskAssign = GetTaskAssignByFlightId(flightId);
            var taskId = taskAssign.TaskId;
            var executeTask = await Repository.AsSugarClient()
                .Queryable<LasaTask>()
                .SingleAsync(a => a.Id == taskId);
            string objectKey = data.file.object_key;
            var folderKey = ((string)data.file.object_key).Split("/");
            var parentKey = folderKey[2];
            var isExist = await Repository.AsSugarClient()
                .Queryable<LasaMediaFile>()
                .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 = string.IsNullOrEmpty(executeTask.TaskName)
                        ? timeStr
                        : $"{executeTask.TaskName} {timeStr}",
                    WorkspaceId = executeTask.WorkspaceId,
                    CreateTime = date,
                };
                await Repository.AsSugarClient().Insertable(parent1).ExecuteCommandAsync();
            }

            // 重复检测
            var mediaFile = await Repository.AsSugarClient()
                .Queryable<LasaMediaFile>()
                .Where(a => a.FlightId.Equals(flightId))
                .Where(a => a.ObjectKey.Equals(objectKey)).SingleAsync();
            if (mediaFile == null)
            {
                var type = 0;
                var preSize = 5;
                // todo 判断是不是图片
                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 = "";

                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)
                        {
                            //Focal Length 35: 24 mm
                            //Exif Image Width: 4032 pixels : Exif SubIFD
                            //Exif Image Height: 3024 pixels : Exif SubIFD
                            // 3.1 解析Content-Range头（格式：bytes start-end/total）
                            var contentRange = response.Content.Headers.ContentRange;
                            if (contentRange != null)
                            {
                                fileSize = contentRange.Length.Value;
                                Console.WriteLine(
                                    $"获取范围：{contentRange.From}-{contentRange.To}/{contentRange.Length}");
                            }

                            if (objectKey.ToLower().EndsWith("jpeg"))
                            {
                                // 成功获取部分内容
                                var y = response.Content.ReadAsByteArrayAsync().Result;
                                MemoryStream ms = new MemoryStream(y);
                                var directories = ImageMetadataReader.ReadMetadata(ms);
                                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"))
                                        {
                                            foreach (var tag in directory.Tags)
                                            {
                                                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();
                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 = executeTask.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
                };
                // todo 添加事务
                await Repository.AsSugarClient().Insertable(fileUpload).ExecuteCommandAsync();
            }

            if (result.need_reply.Equals(1))
            {
                var fileUploadCallbackEventReply = new FileUploadCallbackEventReply<object>()
                {
                    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 // 已上传文件数量
            };
            //  当expectFileCount 等于uploadedFileCount时，则表示航线执行完成
            /*
            if (uploadedFileCount.Equals(expectFileCount))
            {
                taskRecord.Status = 5; // 成功状态
            }
            */

            await Repository.AsSugarClient().Updateable(taskRecord)
                .IgnoreNullColumns().ExecuteCommandAsync();

            return new Response<bool>()
            {
                Result = true
            };
        }
    }
}