using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using SlnMesnac.Business.@base;
using SlnMesnac.Model.domain;
using SlnMesnac.Model.dto.taskType;
using SlnMesnac.Plc;
using SlnMesnac.Serilog;
using SlnMesnac.WCS.Global;
using SlnMesnac.WCS.Library;
using SqlSugar;
using System.Threading.Tasks;

namespace SlnMesnac.WCS.WCS
{
    /// <summary>
    /// 根据条件创建任务
    /// </summary>
    public class CreateTaskByRecord : BaseBusiness
    {
        private readonly SerilogHelper _logger;

        //3#车间上料点plc
        private readonly PlcAbsractFactory workShop3Plc;

        //2#计量室相关点位
        private readonly PlcAbsractFactory workShop2Plc;

        private readonly ISqlSugarClient sqlSugarClient;

        public CreateTaskByRecord(IServiceProvider serviceProvider) : base(serviceProvider)
        {
            workShop3Plc = base.GetPlcByKey("workShop3Plc");
            workShop2Plc = base.GetPlcByKey("workShop2Plc");
            sqlSugarClient = serviceProvider.GetRequiredService<ISqlSugarClient>();
            _logger = serviceProvider.GetRequiredService<SerilogHelper>();
            //test();
        }

        /// <summary>
        /// 启动扫描监听
        /// </summary>
        public void StartPoint()
        {
            CreateTaskByReadPlcSignal();

            //监听2#计量室旋转移栽plc信号，处理相关流程
            var CreateProductTaskByLocationCodeThread = new Thread(ListeningWorkShop2TransplantingMachine);
            CreateProductTaskByLocationCodeThread.IsBackground = true;
            CreateProductTaskByLocationCodeThread.Name = "监听1#2#计量室旋转移栽plc信号处理连廊箱体旋转相关流程";
            CreateProductTaskByLocationCodeThread.Start();
        }

        /// <summary>
        /// 通过读plc条件创建任务
        ///
        /// </summary>
        private void CreateTaskByReadPlcSignal()
        {
            Task.Run(() =>
            {
                while (true)
                {
                    try
                    {
                        if (workShop3Plc == null || !workShop3Plc.IsConnected)
                        {
                            _logger.Plc(DateTime.Now + "3#PLC未连接,请检查网络!");
                            continue;
                        }

                        #region 3#车间补四宫格空箱流程---监听3#1号光电状态

                        bool signal1 = workShop3Plc.readBoolByAddress(StaticData.GetPlcAddress("3#大料箱进料为空"));
                        if (signal1)
                        {
                            CreateSupplementBigPalletTask();
                        }

                        #endregion 3#车间补四宫格空箱流程---监听3#1号光电状态

                        #region 从下料点到1-16机台送料任务--或者1-12移库任务  --监听3#2号光电状态

                        bool getBigFlag = workShop3Plc.readBoolByAddress(StaticData.GetPlcAddress("3#大料箱agv取料通知"));
                        string machineCodeS = workShop3Plc.readInt16ByAddress(StaticData.GetPlcAddress("3#四宫格下料点机台号")).ToString();
                        string rfidS = workShop3Plc.readStringByAddress(StaticData.GetPlcAddress("3#四宫格下料点RFID号"), 12).Replace("\0", "");
                        int amount = workShop3Plc.readInt16ByAddress(StaticData.GetPlcAddress("3#四宫格下料点数量"));

                        if (getBigFlag && machineCodeS != "0" && rfidS.Contains("SHS"))
                        {
                            CreatDeliverWorkShop3Task(machineCodeS, rfidS, amount);
                        }

                        #endregion 从下料点到1-16机台送料任务--或者1-12移库任务  --监听3#2号光电状态

                        #region 3#往2#计量室送料流程

                        bool getSmallFlag = workShop3Plc.readBoolByAddress(StaticData.GetPlcAddress("3#小料箱agv取料通知"));
                        int smallAmount = workShop3Plc.readInt16ByAddress(StaticData.GetPlcAddress("3#小料箱下料点数量"));
                        string machineCodeX = workShop3Plc.readInt16ByAddress(StaticData.GetPlcAddress("3#小料箱下料点机台号")).ToString();
                        string rfidX = workShop3Plc.readStringByAddress(StaticData.GetPlcAddress("3#小料箱下料点RFID号"), 12).Replace("\0", "");

                        if (getSmallFlag && machineCodeX != "0" && rfidX.Contains("SHX"))
                        {
                            if (machineCodeX == "1")
                            {
                                machineCodeX = "1006";
                            }
                            CreatDeliverWorkShop2Task(machineCodeX, rfidX);
                        }

                        #endregion 3#往2#计量室送料流程

                        #region 3#补充小空托盘流程--3#车间从色粉存放点补充小托盘任务 或者   3#车间从2#接驳位补充小托盘任务

                        int work3EmptyAmount = workShop3Plc.readInt16ByAddress(StaticData.GetPlcAddress("3#缓存皮带线库存数"));
                        int busy3Flag = workShop3Plc.readInt16ByAddress(StaticData.GetPlcAddress("3#线体忙碌状态"));
                        if ((work3EmptyAmount == 1 && busy3Flag == 0) || work3EmptyAmount == 0)
                        {
                            //3#车间从色粉存放点补充小托盘任务 或者   3#车间从2#接驳位补充小托盘任务
                            CreateSupplySmallPalletToWorkshop3Task();
                        }

                        #endregion 3#补充小空托盘流程--3#车间从色粉存放点补充小托盘任务 或者   3#车间从2#接驳位补充小托盘任务
                    }
                    catch (Exception ex)
                    {
                        _logger.Error($"CreateTaskByReadPlcSignal方法异常:" + ex.StackTrace);
                    }
                    finally
                    {
                        Thread.Sleep(1000);
                    }
                }
            });
        }

        /// <summary>
        /// 监听2#计量室旋转移栽plc信号，处理连廊箱体旋转相关流程
        /// </summary>
        private void ListeningWorkShop2TransplantingMachine()
        {
            while (true)
            {
                try
                {
                    if (workShop2Plc == null || !workShop2Plc.IsConnected)
                    {
                        continue;
                    }

                    #region 2#计量室料箱返回处理流程：空箱入缓存链条线，色粉派送至色粉存放点

                    //2#接驳位色粉派送至色粉存放点任务
                    if (workShop2Plc != null && workShop2Plc.IsConnected)
                    {
                        bool returnJudgeSignal = workShop2Plc.readBoolByAddress(StaticData.GetPlcAddress("2#料箱运回RFID读取"));
                        int busy2Flag = workShop2Plc.readInt16ByAddress(StaticData.GetPlcAddress("2#线体忙碌状态"));
                        int Amount = workShop2Plc.readInt16ByAddress(StaticData.GetPlcAddress("2#缓存链条线库存数"));
                        //是否还有未在色粉库位的色粉箱体
                        bool anyTonerPallet = HasAnyTonerInLine();
                        if (returnJudgeSignal && busy2Flag == 0 && (Amount < 3 || anyTonerPallet))
                        {
                            int type = 0;
                            string rfid = ReadEpcStrByRfidKeyAndReturnType("2#Transplant", out type);
                            if (type == 0 || type == 2)
                            {//二次读取
                                rfid = ReadEpcStrByRfidKeyAndReturnType("2#Transplant", out type);
                            }
                            if (type == 1) //读到1个标签
                            {
                                int hoistFloor = workShop2Plc.readInt16ByAddress(StaticData.GetPlcAddress("2#提升机当前层"));
                                if (Amount < 3 && hoistFloor == 1)  //如果提升机正好在1楼
                                {
                                    Thread.Sleep(300);
                                }
                                else if (anyTonerPallet)  //有色粉空转
                                {
                                    Thread.Sleep(3500);
                                }

                                busy2Flag = workShop2Plc.readInt16ByAddress(StaticData.GetPlcAddress("2#线体忙碌状态"));
                                if (busy2Flag == 0)
                                {
                                    // 判断有无色粉, 如果没有色粉，wcs给 旋转移栽方向信号 写2，plc将料箱运到 缓存皮带线。
                                    WmsPalletInfo? wmsPalletInfo = sqlSugarClient.Queryable<WmsPalletInfo>().First(it => it.PalletInfoCode == rfid);
                                    if (wmsPalletInfo != null && wmsPalletInfo.Amount > 0 && wmsPalletInfo.TonerFlag == 1)  //携带色粉
                                    {
                                        //注意：plc上旋转移栽必须锁住线体状态才可以，2#接驳位如果有满料，那么通知plc信号，plc在连廊不再把箱体放到旋转移栽上
                                        //调度系统结合上面缓存链条线库存，如果上面低于3，那么空箱可以上来缓存线
                                        workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#缓存空箱线体状态"), 1);
                                        workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#线体忙碌状态"), 1);
                                        workShop2Plc.writeInt16ByAddress("DB100.DBW40", 1);
                                        bool createResult = CreateDeliverTonerTask(rfid);
                                        if (createResult)
                                        {
                                            //3色粉上提升机移栽等待
                                            _logger.Info($"2#移栽===={rfid}携带色粉去往色粉库位");
                                            workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#移栽平台任务"), 3);
                                            workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运回RFID读取"), false);
                                        }
                                        else
                                        {   //色粉库位无空闲位置，空转一圈
                                            //2空箱转运
                                            _logger.Info($"2#移栽===={rfid}携带色粉，色粉库位无空闲位置，空转一圈");
                                            workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#移栽平台任务"), 2);
                                            workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运回RFID读取"), false);
                                        }
                                    }
                                    else if (wmsPalletInfo != null && wmsPalletInfo.Amount > 0 && wmsPalletInfo.TonerFlag == 0) //携带满料
                                    {
                                        //2转运
                                        _logger.Info($"2#移栽===={rfid}携带满料，空转一圈");
                                        workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#线体忙碌状态"), 1);
                                        workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#移栽平台任务"), 2);
                                        workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运回RFID读取"), false);
                                    }
                                    else if (wmsPalletInfo != null && wmsPalletInfo.Amount == 0)  //空托盘
                                    {
                                        if (Amount < 3)
                                        {
                                            //1上提升机去缓存线
                                            _logger.Info($"2#移栽===={rfid}空箱，去缓存区");
                                            workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#线体忙碌状态"), 1);
                                            workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#移栽平台任务"), 1);
                                            workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运回RFID读取"), false);
                                        }
                                        else
                                        {
                                            //2空箱转运
                                            _logger.Info($"2#移栽===={rfid}空箱,空箱转运");
                                            workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#线体忙碌状态"), 1);
                                            workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#移栽平台任务"), 2);
                                            workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运回RFID读取"), false);
                                        }
                                    }
                                }
                            }
                            else if (type == 0)  //读到0个标签
                            {
                                //RFID 未读到报警（2#移栽平台处）
                                workShop2Plc.writeBoolByAddress("DB100.DBX42.1", true);
                                _logger.Error("RFID 未读取报警（2#移栽平台处）");
                                workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运回RFID读取"), false);
                                //没读到2空箱转运
                                // _logger.Info($"1#移栽====没读到,空箱转运");
                                //workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#线体忙碌状态"), 1);
                                //workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#移栽平台任务"), 2);
                                //workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运回RFID读取"), false);
                            }
                            else if (type == 2) //串读
                            {
                                //RFID 串读报警（2#移栽平台处）
                                workShop2Plc.writeBoolByAddress("DB100.DBX42.0", true);
                                _logger.Error("RFID 串读报警（2#移栽平台处）");
                                workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运回RFID读取"), false);
                            }
                        }
                    }

                    #endregion 2#计量室料箱返回处理流程：空箱入缓存链条线，色粉派送至色粉存放点

                    #region 1#连廊料箱在计量室连廊移栽处理流程：wcs读RFID判断是否空箱，空箱下plc信号旋转，非空箱通知plc上提升机计量室

                    if (workShop2Plc != null && workShop2Plc.IsConnected)
                    {
                        bool returnJudgeSignal = workShop2Plc.readBoolByAddress(StaticData.GetPlcAddress("2#料箱运进计量室RFID读取"));
                        int busy1Flag = workShop2Plc.readInt16ByAddress(StaticData.GetPlcAddress("1#提升机忙碌状态"));
                        if (returnJudgeSignal && busy1Flag == 0)
                        {
                            //test
                            int type = 0;
                            string rfid = ReadEpcStrByRfidKeyAndReturnType("1#MetrologyRoom", out type);
                            if (string.IsNullOrEmpty(rfid))
                            {
                                rfid = ReadEpcStrByRfidKeyAndReturnType("1#MetrologyRoom", out type);
                            }
                            if (type == 1)
                            {
                                // 判断有无料, 如果没有料，wcs下发空箱旋转信号，有料，通知plc上提升机计量室
                                WmsPalletInfo? wmsPalletInfo = sqlSugarClient.Queryable<WmsPalletInfo>().First(it => it.PalletInfoCode == rfid);
                                if (wmsPalletInfo != null && wmsPalletInfo.Amount > 0 && wmsPalletInfo.TonerFlag == 1)  //携带色粉
                                {
                                    //2空箱转运
                                    _logger.Info($"1#移栽===={rfid}携带色粉,空箱转运");
                                    workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("1#移栽平台任务"), 2);
                                    workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运进计量室RFID读取"), false);
                                }
                                if (wmsPalletInfo != null && wmsPalletInfo.Amount > 0 && wmsPalletInfo.TonerFlag == 0)  //携带满料
                                {
                                    //1#移栽平台任务（1上提升机,2空箱转运）
                                    _logger.Info($"1#移栽===={rfid}携带满料,去提升机计量室");
                                    workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("1#移栽平台任务"), 1);
                                    workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运进计量室RFID读取"), false);
                                }
                                else if (wmsPalletInfo != null && wmsPalletInfo.Amount == 0)
                                {
                                    //1#移栽平台任务（1上提升机,2空箱转运）
                                    _logger.Info($"1#移栽===={rfid}空箱,空箱转运");
                                    workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("1#移栽平台任务"), 2);
                                    workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运进计量室RFID读取"), false);
                                }
                            }
                            else if (type == 0)
                            {
                                //RFID 未读到报警（1#移栽平台处）
                                workShop2Plc.writeBoolByAddress("DB100.DBX42.3", true);
                                _logger.Error("RFID 未读取报警（1#移栽平台处）");
                                workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运进计量室RFID读取"), false);

                                //没读到，当成空箱子旋转一圈
                                //1#移栽平台任务（1上提升机,2空箱转运）
                                // _logger.Info($"1#移栽====没读到,空箱转运");
                                //workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("1#移栽平台任务"), 2);
                                //workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运进计量室RFID读取"), false);
                            }
                            else if (type == 2)//串读
                            {
                                //RFID 串读报警（1#移栽平台处）
                                workShop2Plc.writeBoolByAddress("DB100.DBX42.2", true);
                                _logger.Error("RFID 串读报警（1#移栽平台处）");
                                workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运进计量室RFID读取"), false);
                            }
                        }
                    }

                    #endregion 1#连廊料箱在计量室连廊移栽处理流程：wcs读RFID判断是否空箱，空箱下plc信号旋转，非空箱通知plc上提升机计量室

                    #region 暂时不使用--色粉存放点空托盘派送至2#接驳位

                    /////如果色粉存放点可用空库位小于等于2个，并且有空托盘在库位，判断2#缓存链条线库存（容量3）低于2个，并且无 从3#接驳位到2#接驳位的送料任务或 色粉存放点到2#缓存链条线任务，
                    /////2.生成色粉存放点到2#缓存链条线任务
                    //int canUseAmount = sqlSugarClient.Queryable<WcsBaseEquip>().Count(it => it.EquipType == 7 && it.EquipStatus == 0 && string.IsNullOrEmpty(it.ContainerCode));
                    ////判断色粉存放处是否有空托盘
                    //WcsBaseEquip? startEquip = sqlSugarClient.Queryable<WcsBaseEquip>().InnerJoin<WmsPalletInfo>(
                    //    (wbe, wpi) => wbe.ContainerCode == wpi.PalletInfoCode && wpi.Amount == 0 && wbe.EquipStatus == 0)
                    //    .Where(wbe => wbe.EquipType == 7 && !string.IsNullOrEmpty(wbe.ContainerCode)).First();
                    //if (canUseAmount < 2 && startEquip != null)
                    //{
                    //    int cacheLineAmount = workShop2Plc.readInt16ByAddress(StaticData.GetPlcAddress("2#缓存链条线库存数"));
                    //    bool hasTask = sqlSugarClient.Queryable<WcsTask>().Any(it => it.TaskType == StaticTaskType.EmptyReturnFromTonerTask || it.TaskType == StaticTaskType.TransferMaterialMetrologyRoomBoxTask);
                    //    if (cacheLineAmount < 2 && !hasTask)
                    //    {
                    //        try
                    //        {
                    //            sqlSugarClient.AsTenant().BeginTran();
                    //            WcsTask task = new WcsTask();
                    //            task.TaskType = StaticTaskType.EmptyReturnFromTonerTask;
                    //            task.CurrPointNo = startEquip.AgvPositionCode;
                    //            task.EndPointNo = "2DeliverMetrologyRoomPoint";
                    //            task.TaskStatus = 0;
                    //            task.CreatedTime = DateTime.Now;
                    //            task.CreatedBy = "wcs";
                    //            task.TaskName = "色粉存放点空托盘派送至2#接驳位";
                    //            task.PalletInfoCode = startEquip.ContainerCode;
                    //            int id = sqlSugarClient.Insertable(task).ExecuteReturnIdentity();
                    //            WcsTaskLog wcsTaskLog = CoreMapper.Map<WcsTaskLog>(task);
                    //            wcsTaskLog.Id = id;
                    //            startEquip.EquipStatus = 1;
                    //            sqlSugarClient.Updateable(startEquip).ExecuteCommand();
                    //            sqlSugarClient.Insertable(wcsTaskLog).ExecuteCommand();
                    //            sqlSugarClient.AsTenant().CommitTran();
                    //            _logger.Agv($"生成{task.TaskName},起点:{task.CurrPointNo},终点:{task.EndPointNo}");
                    //        }
                    //        catch (Exception ex)
                    //        {
                    //            sqlSugarClient.AsTenant().RollbackTran();
                    //            _logger.Error($"色粉存放点空托盘派送至2#接驳位任务提交事务异常{ex.Message}");
                    //        }
                    //    }
                    //}

                    #endregion 暂时不使用--色粉存放点空托盘派送至2#接驳位
                }
                catch (Exception ex)
                {
                    _logger.Error($"ListeningWorkShop2TransplantingMachine方法异常:" + ex.StackTrace);
                }
                finally
                {
                    Thread.Sleep(1000 * 1);
                }
            }
        }

        #region 任务流程

        /// <summary>
        /// 3#车间补四宫格空箱流程
        /// </summary>
        private async void CreateSupplementBigPalletTask()
        {
            try
            {
                //判断agv是否有来这里的任务
                bool hasTask = sqlSugarClient.Queryable<WcsTask>().Any(it => it.TaskType == StaticTaskType.SupplyEmptyPalletTask && it.EndPointNo == "3SuppleEmptyPalletPoint");
                if (hasTask)
                {
                    //_logger.Info("Agv已有3#车间补四宫格空箱任务");
                    return;
                }

                #region 寻找机台空托盘

                WmsBaseLocation? startLocation = null;
                //todo:优化点：为避免送料时尽可能少的移库任务，因此在这里可以先读取下一个料包要去的机台号，提前调度这里的空箱
                //string nextGoMachineCode = workShop3Plc.readInt16ByAddress(StaticData.GetPlcAddress("3#实时机台号")).ToString();
                //bool hasAny = StaticData.WmsMachineInfos.Any(x => x.MachineCode == nextGoMachineCode && x.WorkshopId == 3);
                //if (hasAny)
                //{
                //    int machineId = StaticData.WmsMachineInfos.First(x => x.MachineCode == nextGoMachineCode).MachineId;
                //    if (machineId >= 12 && machineId <= 16)
                //    {
                //        //12 - 16号机台都送往12号机台
                //        machineId = 12;
                //    }
                //    //判断该机台有无空料箱，优先补充
                //    string sql = $"SELECT wbl.* FROM wms_base_location  wbl join wms_pallet_info  wpi on  wbl.container_code = wpi.pallet_info_code where wbl.container_code is not null and  wbl.container_code!='' and wbl.location_status=0 and wbl.machineid ={machineId} and wpi.amount=0";
                //    List<WmsBaseLocation> startLocations = sqlSugarClient.Ado.SqlQuery<WmsBaseLocation>(sql).OrderByDescending(x => x.MachineId).ToList();
                //    if (startLocations.Count > 0)
                //    {
                //        startLocation = startLocations[0];
                //    }
                //}
                //1-12机台，优先12
                if (startLocation == null)
                {
                    string sql = "SELECT wbl.* FROM wms_base_location  wbl join wms_pallet_info  wpi on  wbl.container_code = wpi.pallet_info_code where wbl.container_code is not null and  wbl.container_code!='' and wbl.location_status=0 and wbl.machineid<=12 and wpi.amount=0";

                    List<WmsBaseLocation> emptyLocations = sqlSugarClient.Ado.SqlQuery<WmsBaseLocation>(sql).OrderBy(x => x.MachineId).ToList();
                    if (emptyLocations.Count == 0)
                    {
                        _logger.Info("暂时没有可用空托盘库位");
                        //todo报警
                        return;
                    }
                    //暂时排序，后期结合小料正在生产的计划提前调度要配送机台的空托盘，或者可以根据实际位置调度最近的空料箱
                    startLocation = emptyLocations.Last();//从12机台开始送
                    //startLocation = emptyLocations.First();//从头开始送
                }

                WcsBaseEquip? endEquip = sqlSugarClient.Queryable<WcsBaseEquip>().Where(it => it.EquipNo == "3SuppleEmptyPalletPoint").ToList().First();

                #endregion 寻找机台空托盘

                sqlSugarClient.AsTenant().BeginTran();

                WcsTask task = new WcsTask();
                task.TaskType = StaticTaskType.SupplyEmptyPalletTask;
                task.TaskName = StaticTaskType.GetDescription(task.TaskType);

                task.CurrPointNo = startLocation.AgvPositionCode;
                startLocation.LocationStatus = 1;
                sqlSugarClient.Updateable(startLocation).ExecuteCommand();
                // task.NextPointNo = agvEquip.AgvPositionCode;
                task.EndPointNo = endEquip.AgvPositionCode;
                task.TaskStatus = 0;
                task.CreatedTime = DateTime.Now;
                task.CreatedBy = "wcs";
                task.PalletInfoCode = startLocation.ContainerCode;
                int id = sqlSugarClient.Insertable(task).ExecuteReturnIdentity();
                WcsTaskLog wcsTaskLog = CoreMapper.Map<WcsTaskLog>(task);
                wcsTaskLog.Id = id;
                sqlSugarClient.Insertable(wcsTaskLog).ExecuteCommand();
                sqlSugarClient.AsTenant().CommitTran();
                _logger.Agv($"生成{task.TaskName},起点:{task.CurrPointNo},终点:{task.EndPointNo}");
            }
            catch (Exception ex)
            {
                sqlSugarClient.AsTenant().RollbackTran();
                _logger.Error($"生成3#车间补四宫格空箱流程异常{ex.Message}");
            }
        }

        /// <summary>
        ///  从下料点到1-16机台送料任务   创建移库任务
        /// </summary>
        private async void CreatDeliverWorkShop3Task(string machineCode, string rfid, int amount)
        {
            try
            {
                bool hasMachineCode = StaticData.WmsMachineInfos.Any(x => x.MachineCode == machineCode);
                if (!string.IsNullOrEmpty(rfid) && hasMachineCode)
                {
                    //判断该料箱是否已经有送料任务
                    bool hasTask = sqlSugarClient.Queryable<WcsTask>().Any(it => it.TaskType == StaticTaskType.TransferMaterialBoxTask && it.PalletInfoCode == rfid);
                    if (hasTask)
                    {
                        _logger.Info($"该托盘{rfid}已有下料点到1-16机台送料任务");
                        return;
                    }
                    //判断该托盘是否已经在库里
                    bool isInLocation = sqlSugarClient.Queryable<WmsBaseLocation>().Any(it => it.ContainerCode == rfid);
                    if (isInLocation)
                    {
                        //todo推送报警
                        _logger.Info($"该托盘{rfid}已存在库位，请检查托盘号是否重复");
                        return;
                    }
                    List<WmsBaseLocation> AllWmsBaseLocationList = await sqlSugarClient.Queryable<WmsBaseLocation>().Where(x => x.MachineId <= 12).ToListAsync();

                    WmsBaseLocation? targetLocation;
                    // 根据机台号寻找终点库位、判断机台的两个库位是否有空位置、没有的话先生成移库任务
                    targetLocation = GetTargetLocation(machineCode, AllWmsBaseLocationList);
                    if (targetLocation == null)
                    {
                        //todo:推送预警
                        _logger.Info($"机台:{machineCode}没有可用空库位,无法配送");
                        return;
                    }

                    #region 二次核查确认目标库位是否有托盘

                    bool hasPallet = sqlSugarClient.Queryable<WmsBaseLocation>().Any(x => x.LocationCode == targetLocation.LocationCode && string.IsNullOrEmpty(x.ContainerCode));
                    if (!hasPallet)
                    {
                        _logger.Info($"库位:{targetLocation.LocationCode}有托盘暂不处理");
                        return;
                    }

                    #endregion 二次核查确认目标库位是否有托盘

                    WcsBaseEquip startEquip = sqlSugarClient.Queryable<WcsBaseEquip>().First(it => it.EquipNo == "3DeliverBigGoodsStartPoint");
                    WmsPalletInfo wmsPalletInfo = sqlSugarClient.Queryable<WmsPalletInfo>().First(it => it.PalletInfoCode == rfid);
                    WcsTask task = new WcsTask();
                    task.TaskType = StaticTaskType.TransferMaterialBoxTask;
                    task.CurrPointNo = startEquip.AgvPositionCode;
                    task.EndPointNo = targetLocation.AgvPositionCode;
                    task.TaskStatus = 0;
                    task.CreatedTime = DateTime.Now;
                    task.CreatedBy = "wcs";
                    task.TaskName = "下料点到1-16机台送料任务";
                    task.PalletInfoCode = rfid;
                    task.MachineCode = machineCode;
                    sqlSugarClient.AsTenant().BeginTran();
                    try
                    {
                        int id = sqlSugarClient.Insertable(task).ExecuteReturnIdentity();
                        WcsTaskLog wcsTaskLog = CoreMapper.Map<WcsTaskLog>(task);
                        wcsTaskLog.Id = id;
                        targetLocation.LocationStatus = 1;
                        int LockFlag =  sqlSugarClient.Updateable(targetLocation).ExecuteCommand();
                        sqlSugarClient.Insertable(wcsTaskLog).ExecuteCommand();
                        while (LockFlag<=0)
                        {
                            LockFlag = sqlSugarClient.Updateable(targetLocation).ExecuteCommand();
                        }
                        #region 修改托盘信息

                        if (wmsPalletInfo != null)
                        {
                            wmsPalletInfo.Amount = amount != 0 ? amount : 1;
                            wmsPalletInfo.UpdatedTime = DateTime.Now;
                            sqlSugarClient.Updateable(wmsPalletInfo).ExecuteCommand();
                        }

                        #endregion 修改托盘信息

                        sqlSugarClient.AsTenant().CommitTran();
                        workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#大料箱agv取料通知"), false);
                        _logger.Agv($"生成{task.TaskName},起点:{task.CurrPointNo},终点:{task.EndPointNo}");
                    }
                    catch (Exception ex)
                    {
                        sqlSugarClient.AsTenant().RollbackTran();
                        _logger.Error($"生成3#车间从下料点到1-16机台送料任务提交事务异常{ex.Message}");
                    }
                }
            }
            catch (Exception ex)
            {
                sqlSugarClient.AsTenant().RollbackTran();
                _logger.Error($"生成3#车间从下料点到1-16机台送料任务异常{ex.Message}");
            }
        }

        /// <summary>
        /// 从3#接驳位到2#计量室接驳位的送料任务
        /// </summary>
        private void CreatDeliverWorkShop2Task(string machineCode, string rfid)
        {
            try
            {
                bool hasMachineCode = StaticData.WmsMachineInfos.Any(x => x.MachineCode == machineCode);
                if (!string.IsNullOrEmpty(rfid) && hasMachineCode)
                {
                    //判断agv是否有来这里的任务
                    bool hasTask = sqlSugarClient.Queryable<WcsTask>().Any(it => it.TaskType == StaticTaskType.TransferMaterialMetrologyRoomBoxTask && it.PalletInfoCode == rfid);
                    
                    if (hasTask)
                    {
                        _logger.Info($"该托盘{rfid}已有3#接驳位到2#计量室接驳位的送料任务");
                        return;
                    }

                    //限制送料任务生成数量
                    int transTaskAmount = sqlSugarClient.Queryable<WcsTask>().Where(it => it.TaskType == StaticTaskType.TransferMaterialMetrologyRoomBoxTask).ToList().Count();

                    if (transTaskAmount > 2)
                    {
                        _logger.Info($"小托盘任务生成任务已大于上限（上限为2）！请稍等，所有小车同时运输小托盘将会导致卡死！");
                        return;
                    }

                    //起点库位 3#车间小料箱取送货接驳点
                    WcsBaseEquip startEquip = sqlSugarClient.Queryable<WcsBaseEquip>().First(it => it.EquipNo == "3DeliverSmallGoodsPoint");
                    //终点库位 2#车间计量室接驳点
                    WcsBaseEquip endEquip = sqlSugarClient.Queryable<WcsBaseEquip>().First(it => it.EquipNo == "2DeliverMetrologyRoomPoint");
                    WmsPalletInfo wmsPalletInfo = sqlSugarClient.Queryable<WmsPalletInfo>().First(it => it.PalletInfoCode == rfid);
                    sqlSugarClient.AsTenant().BeginTran();
                    WcsTask task = new WcsTask();
                    task.TaskType = StaticTaskType.TransferMaterialMetrologyRoomBoxTask;
                    task.CurrPointNo = startEquip.AgvPositionCode;
                    // task.NextPointNo = agvEquip.AgvPositionCode;
                    task.EndPointNo = endEquip.AgvPositionCode;
                    task.TaskStatus = 0;
                    task.CreatedTime = DateTime.Now;
                    task.CreatedBy = "wcs";
                    task.TaskName = "3#接驳位到2#计量室接驳位的送料任务";
                    task.PalletInfoCode = rfid;
                    task.MachineCode = machineCode;
                    int id = sqlSugarClient.Insertable(task).ExecuteReturnIdentity();
                    WcsTaskLog wcsTaskLog = CoreMapper.Map<WcsTaskLog>(task);
                    wcsTaskLog.Id = id;

                    sqlSugarClient.Insertable(wcsTaskLog).ExecuteCommand();

                    #region 修改托盘信息

                    if (wmsPalletInfo != null)
                    {
                        wmsPalletInfo.TonerFlag = 0;
                        wmsPalletInfo.Amount = 1;
                        wmsPalletInfo.UpdatedTime = DateTime.Now;
                        sqlSugarClient.Updateable(wmsPalletInfo).ExecuteCommand();
                    }

                    #endregion 修改托盘信息

                    sqlSugarClient.AsTenant().CommitTran();
                    workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#小料箱agv取料通知"), false);
                    _logger.Agv($"生成{task.TaskName},起点:{task.CurrPointNo},终点:{task.EndPointNo}");
                }
            }
            catch (Exception ex)
            {
                sqlSugarClient.AsTenant().RollbackTran();
                _logger.Error($"3#接驳位到2#计量室接驳位的送料任务{ex.Message}");
            }
        }

        /// <summary>
        /// 3#车间从色粉存放点补充小托盘任务 或者   3#车间从2#接驳位补充小托盘任务
        /// .wcs检测到 3#缓存皮带线库存数 (容量2)不满时，且色粉存放处有空托盘时，优先生成色粉存放处补充小料箱任务
        ///2.如果色粉存放处无空托盘，且2#缓存链条线库存数 大于0个，并且 2#线体忙碌状态 0时，2s后 2#线体忙碌状态 0时，wcs把 2#线体忙碌状态 改为1，wcs下发缓存链条线 2#出一个空托盘信号（plc自己复位）， wcs并且生成一个3#补充空托盘任务呼叫agv
        /// </summary>
        private void CreateSupplySmallPalletToWorkshop3Task()
        {
            try
            {
                //判断是否已经有空托盘补充任务
                bool hasTask = sqlSugarClient.Queryable<WcsTask>().Any(x => x.TaskType == StaticTaskType.SupplySmallPalletTask || x.TaskType == StaticTaskType.SupplySmallPalletFromTonerTask);
                if (hasTask)
                {
                    return;
                }
                //判断色粉存放处是否有空托盘
                WcsBaseEquip? emptyPalletEquip = sqlSugarClient.Queryable<WcsBaseEquip>().InnerJoin<WmsPalletInfo>(
                    (wbe, wpi) => wbe.ContainerCode == wpi.PalletInfoCode && wpi.Amount == 0 && wbe.EquipStatus == 0)
                    .Where(wbe => wbe.EquipType == 7 && !string.IsNullOrEmpty(wbe.ContainerCode)).OrderBy(wbe => wbe.Id).First();
                if (emptyPalletEquip != null)  //从色粉存放处生成补充空托盘任务
                {
                    WcsTask task = new WcsTask();
                    task.TaskType = StaticTaskType.SupplySmallPalletFromTonerTask;
                    task.CurrPointNo = emptyPalletEquip.AgvPositionCode;
                    task.EndPointNo = "3DeliverSmallGoodsPoint";
                    task.TaskStatus = 0;
                    task.CreatedTime = DateTime.Now;
                    task.CreatedBy = "wcs";
                    task.TaskName = "3#车间从色粉存放点补充小托盘任务";
                    task.PalletInfoCode = emptyPalletEquip.ContainerCode;

                    sqlSugarClient.AsTenant().BeginTran();
                    try
                    {
                        int id = sqlSugarClient.Insertable(task).ExecuteReturnIdentity();
                        WcsTaskLog wcsTaskLog = CoreMapper.Map<WcsTaskLog>(task);
                        wcsTaskLog.Id = id;
                        sqlSugarClient.Insertable(wcsTaskLog).ExecuteCommand();
                        //锁住开始色粉库位
                        emptyPalletEquip.EquipStatus = 1;
                        sqlSugarClient.Updateable(emptyPalletEquip).ExecuteCommand();
                        sqlSugarClient.AsTenant().CommitTran();
                    }
                    catch (Exception ex)
                    {
                        sqlSugarClient.AsTenant().RollbackTran();
                        _logger.Error($"3#车间从色粉存放点补充小托盘任务生成提交事务异常{ex.Message}");
                    }
                    _logger.Agv($"生成{task.TaskName},起点:{task.CurrPointNo},终点:{task.EndPointNo}");
                    return;
                }
                else  //从2#缓存链条线生成补充空托盘任务
                {
                    if (workShop2Plc == null || !workShop2Plc.IsConnected)
                    {
                        _logger.Plc(DateTime.Now + "2#PLC未连接,请检查网络!");
                        return;
                    }
                    int work2EmptyAmount = workShop2Plc.readInt16ByAddress(StaticData.GetPlcAddress("2#缓存链条线库存数"));
                    int work2LineBusyFlag = workShop2Plc.readInt16ByAddress(StaticData.GetPlcAddress("2#缓存空箱线体状态"));
                    if (work2EmptyAmount > 0 && work2LineBusyFlag == 0)
                    {
                        //防止抢占其他任务节奏
                        Thread.Sleep(1000);
                        work2LineBusyFlag = workShop2Plc.readInt16ByAddress(StaticData.GetPlcAddress("2#缓存空箱线体状态"));
                        work2EmptyAmount = workShop2Plc.readInt16ByAddress(StaticData.GetPlcAddress("2#缓存链条线库存数"));
                        if (work2EmptyAmount > 0 && work2LineBusyFlag == 0)
                        {
                            bool hasTonerTask = sqlSugarClient.Queryable<WcsTask>().Any(x => x.TaskType == StaticTaskType.DeliverTonerTask && x.TaskStatus < 5);
                            if (hasTask)
                            {    //如果有agv等着接色粉，暂时不生成补充空托盘任务
                                return;
                            }
                            workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#缓存空箱线体状态"), 1);
                            WcsTask task = new WcsTask();
                            task.TaskType = StaticTaskType.SupplySmallPalletTask;
                            task.CurrPointNo = "2DeliverMetrologyRoomPoint";
                            task.EndPointNo = "3DeliverSmallGoodsPoint";
                            task.TaskStatus = 0;
                            task.CreatedTime = DateTime.Now;
                            task.CreatedBy = "wcs";
                            task.TaskName = " 3#车间从2#接驳位补充小托盘任务";
                            sqlSugarClient.AsTenant().BeginTran();
                            try
                            {
                                int id = sqlSugarClient.Insertable(task).ExecuteReturnIdentity();
                                WcsTaskLog wcsTaskLog = CoreMapper.Map<WcsTaskLog>(task);
                                wcsTaskLog.Id = id;
                                sqlSugarClient.Insertable(wcsTaskLog).ExecuteCommand();

                                sqlSugarClient.AsTenant().CommitTran();
                            }
                            catch (Exception ex)
                            {
                                sqlSugarClient.AsTenant().RollbackTran();
                                _logger.Error($"3#车间从2#接驳位补充小托盘任务生成提交事务异常{ex.Message}");
                                workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#线体忙碌状态"), 0);
                            }
                            _logger.Agv($"生成{task.TaskName},起点:{task.CurrPointNo},终点:{task.EndPointNo}");
                            return;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.Error($"CreateSupplySmallPalletToWorkshop3Task异常{ex.Message}");
            }
        }

        /// <summary>
        /// 从2#接驳位到3#接驳位补小空箱任务  :3#车间补小空箱流程
        /// </summary>
        private void CreateSupplementSmallPalletTask()
        {
            try
            {
                //判断是否有该任务
                bool hasTask = sqlSugarClient.Queryable<WcsTask>().Any(it => it.TaskType == StaticTaskType.SupplySmallPalletTask && it.TaskStatus < 5);
                if (hasTask)
                {
                    _logger.Info("Agv已有2#接驳位到3#接驳位补小空箱任务");
                    return;
                }
                //寻找可用agv
                //WcsBaseEquip? agvEquip = GetCanUseAgv();
                //if (agvEquip == null)
                //{
                //    _logger.Info("暂时没有可用agv");
                //    return;
                //}
                //起点库位 3#车间小料箱取送货接驳点
                WcsBaseEquip startEquip = sqlSugarClient.Queryable<WcsBaseEquip>().First(it => it.EquipNo == "2DeliverMetrologyRoomPoint");
                //终点库位 2#车间计量室接驳点
                WcsBaseEquip endEquip = sqlSugarClient.Queryable<WcsBaseEquip>().First(it => it.EquipNo == "3DeliverSmallGoodsPoint");
                sqlSugarClient.AsTenant().BeginTran();
                WcsTask task = new WcsTask();
                task.TaskType = StaticTaskType.SupplySmallPalletTask;
                task.CurrPointNo = startEquip.AgvPositionCode;
                // task.NextPointNo = agvEquip.AgvPositionCode;
                task.EndPointNo = endEquip.AgvPositionCode;
                task.TaskStatus = 0;
                task.CreatedTime = DateTime.Now;
                task.CreatedBy = "wcs";
                task.TaskName = "2#接驳位到3#接驳位补小空箱任务";
                int id = sqlSugarClient.Insertable(task).ExecuteReturnIdentity();
                WcsTaskLog wcsTaskLog = CoreMapper.Map<WcsTaskLog>(task);
                wcsTaskLog.Id = id;
                startEquip.EquipStatus = 1;
                endEquip.EquipStatus = 1;
                sqlSugarClient.Updateable(startEquip).ExecuteCommand();
                sqlSugarClient.Updateable(endEquip).ExecuteCommand();
                sqlSugarClient.Insertable(wcsTaskLog).ExecuteCommand();
                sqlSugarClient.AsTenant().CommitTran();
                _logger.Agv($"生成{task.TaskName},起点:{task.CurrPointNo},终点:{task.EndPointNo}");
            }
            catch (Exception ex)
            {
                sqlSugarClient.AsTenant().RollbackTran();
                _logger.Error($"2#接驳位到3#接驳位补小空箱任务异常{ex.Message}");
            }
        }

        /// <summary>
        /// 2#色粉派送至色粉存放点任务
        /// </summary>
        private bool CreateDeliverTonerTask(string rfid)
        {
            bool createResult = false;
            try
            {
                //判断是否有该任务
                bool hasTask = sqlSugarClient.Queryable<WcsTask>().Any(it => it.TaskType == StaticTaskType.DeliverTonerTask && it.PalletInfoCode == rfid);
                if (hasTask)
                {
                    _logger.Info($"Agv已有托盘:{rfid}2#色粉派送至色粉存放点任务");
                    return true;
                }
                //起点库位 2#计量室车间小料箱取送货接驳点
                WcsBaseEquip startEquip = sqlSugarClient.Queryable<WcsBaseEquip>().First(it => it.EquipNo == "2DeliverMetrologyRoomPoint");
                //终点库位 2#返程色粉人工拿取点
                WcsBaseEquip? endEquip = sqlSugarClient.Queryable<WcsBaseEquip>().Where(it => it.EquipStatus == 0 && it.EquipType == 7 && string.IsNullOrEmpty(it.ContainerCode)).OrderBy(x => x.Id).First();
                if (endEquip == null)
                {
                    //todo推送报警
                    _logger.Error("暂时没有可用色粉存放点库位");
                    return false;
                }
                sqlSugarClient.AsTenant().BeginTran();
                try
                {
                    WcsTask task = new WcsTask();
                    task.TaskType = StaticTaskType.DeliverTonerTask;
                    task.CurrPointNo = startEquip.AgvPositionCode;
                    task.EndPointNo = endEquip.AgvPositionCode;
                    task.TaskStatus = 0;
                    task.CreatedTime = DateTime.Now;
                    task.CreatedBy = "wcs";
                    task.TaskName = "2#色粉派送至色粉存放点任务";
                    task.PalletInfoCode = rfid;
                    int id = sqlSugarClient.Insertable(task).ExecuteReturnIdentity();
                    WcsTaskLog wcsTaskLog = CoreMapper.Map<WcsTaskLog>(task);
                    wcsTaskLog.Id = id;
                    startEquip.EquipStatus = 1;
                    endEquip.EquipStatus = 1;
                    sqlSugarClient.Updateable(startEquip).ExecuteCommand();
                    sqlSugarClient.Updateable(endEquip).ExecuteCommand();
                    sqlSugarClient.Insertable(wcsTaskLog).ExecuteCommand();
                    sqlSugarClient.AsTenant().CommitTran();
                    _logger.Agv($"生成{task.TaskName},起点:{task.CurrPointNo},终点:{task.EndPointNo}");
                    createResult = true;
                }
                catch (Exception ex)
                {
                    sqlSugarClient.AsTenant().RollbackTran();
                    _logger.Error($"2#色粉派送至色粉存放点任务提交事务异常{ex.Message}");
                }
            }
            catch (Exception ex)
            {
                sqlSugarClient.AsTenant().RollbackTran();
                _logger.Error($"生成2#色粉派送至色粉存放点并空箱送回2#接驳位任务异常{ex.Message}");
            }
            return createResult;
        }

        #endregion 任务流程

        #region 辅助方法

        /// <summary>
        /// 判断是否还有色粉在连廊输送线或者没在色粉库位
        /// </summary>
        /// <returns></returns>
        private bool HasAnyTonerInLine()
        {
            try
            {
                // 色粉料箱
                List<WmsPalletInfo> tonerPallets = sqlSugarClient.Queryable<WmsPalletInfo>().Where(x => x.TonerFlag == 1 && x.Amount > 0).ToList();

                // 色粉库位的料箱码
                List<WcsBaseEquip> equipPalletInfos = sqlSugarClient.Queryable<WcsBaseEquip>().Where(x => x.EquipType == 7 && !string.IsNullOrEmpty(x.ContainerCode)).ToList();

                // 获取 tonerPallets 中的所有 PalletInfoCode
                var tonerPalletCodes = tonerPallets.Select(x => x.PalletInfoCode).ToList();

                // 获取 equipPalletInfos 中的所有 ContainerCode
                var equipPalletCodes = equipPalletInfos.Select(x => x.ContainerCode).ToList();

                // 检查是否有 tonerPalletCodes 不在 equipPalletCodes 中
                bool result = tonerPalletCodes.Any(code => !equipPalletCodes.Contains(code));

                return result;
            }
            catch (Exception ex)
            {
                return false;
            }
        }

        ///// <summary>
        ///// 从12号机台获取空托盘，返回库位信息
        ///// </summary>
        ///// <param name="machineId"></param>
        ///// <returns></returns>
        //private async Task<WmsBaseLocation?> GetEmptyContainerByMachine12Async(string rfidKey)
        //{
        //    try
        //    {
        //        bool flag1 = await ReadSignalByRfidKeyAsync(rfidKey);
        //        if (flag1)
        //        {
        //            string code = await ReadEpcStrByRfidKeyAsync(rfidKey);
        //            if (!string.IsNullOrEmpty(code))
        //            {
        //                // 通过该料箱是否绑定有物料来判断是否为空托盘
        //                bool hasMaterial = await sqlSugarClient.Queryable<WmsPalletInfo>().AnyAsync(it => it.PalletInfoCode == code);
        //                if (!hasMaterial)
        //                {
        //                    WmsBaseLocation? location = await sqlSugarClient.Queryable<WmsBaseLocation>().FirstAsync(it => it.EquipKey == rfidKey);
        //                    return location;
        //                }
        //            }
        //        }
        //        //_logger.Error($"12号机台库位{rfidKey}无空托盘");
        //        return null;
        //    }
        //    catch (Exception ex)
        //    {
        //        return null;
        //    }
        //}

        /// <summary>
        /// 根据机台号寻找终点库位、判断机台的两个库位是否有空位置、没有的话先生成移库任务
        /// machineId为该料箱目的地机台号
        /// </summary>
        /// <param name="machineId"></param>
        /// <returns></returns>
        private WmsBaseLocation? GetTargetLocation(string machineCode, List<WmsBaseLocation> AllWmsBaseLocationList)
        {
            try
            {
                bool hasMoveTask = sqlSugarClient.Queryable<WcsTask>().Any(it => it.TaskType == StaticTaskType.MoveLocationTask && it.TaskStatus < 3);
                if (hasMoveTask)
                {
                    _logger.Info($"目标库位无空位置，正在执行移库任务，暂时不生成送料任务,请等待....");
                    return null;
                }

                WmsBaseLocation? targetLocation;
                //移库起点终点
                WmsBaseLocation? moveStartLocation;
                WmsBaseLocation? moveEndLocation;
                int machineId = StaticData.WmsMachineInfos.First(x => x.MachineCode == machineCode).MachineId;
                if (machineId >= 12 && machineId <= 16)
                {
                    //12 - 16号机台都送往12号机台
                    machineId = 12;
                }
                //12号机台读RFID判断
                //if (machineId >= 12)
                //{
                //    //光电信号
                //    bool flag1 = await ReadSignalByRfidKeyAsync("12-1");
                //    bool flag2 = await ReadSignalByRfidKeyAsync("12-2");
                //    if (!flag1)
                //    {  //12-1有空位
                //        targetLocation = AllWmsBaseLocationList.First(it => it.EquipKey == "12-1" && it.LocationStatus == 1);
                //        return targetLocation;
                //    }
                //    //if (!flag2) //12-2有空位
                //    {
                //        //默认全送到12-2库位，如果有料箱，agv送过去停着,人工取走库位上的箱子
                //        targetLocation = AllWmsBaseLocationList.First(it => it.EquipKey == "12-2" && it.LocationStatus == 1);
                //        return targetLocation;
                //    }
                //    // 后期考虑优化，12号机台暂不考虑移库，因为库位距离其他机台比较远
                //}
                //else if (machineId >= 1 && machineId <= 11)
                {
                    targetLocation = AllWmsBaseLocationList.FirstOrDefault(it => it.MachineId == machineId && it.LocationStatus == 0 && string.IsNullOrEmpty(it.ContainerCode));
                    if (targetLocation != null) return targetLocation;
                    //目标机台两个都有料箱，判断是否有机台里面的料箱是空的，有空箱可生成移库任务，否则等待
                    String sql = $"SELECT wbl.* FROM wms_base_location  wbl join wms_pallet_info  wpi on  wbl.container_code = wpi.pallet_info_code  where wbl.container_code is not null and  wbl.container_code!='' and wbl.location_status=0 and wbl.machineid={machineId} and wpi.amount=0";
                    moveStartLocation = sqlSugarClient.SqlQueryable<WmsBaseLocation>(sql).First();
                    if (moveStartLocation == null)
                    {
                        return null;
                        // todo推送预警   // 目标机台没有空库位，没有可移库的空箱，等待
                    }

                    #region 如果有补空箱任务，并且起点是要送料机台的库位，就不再生成移库任务

                    //目标机台的两个库位编号
                    List<string> locationCodes = AllWmsBaseLocationList.Where(x => x.MachineId == machineId).Select(x => x.AgvPositionCode).ToList();
                    bool hasTask = sqlSugarClient.Queryable<WcsTask>().Any(x => x.TaskType == StaticTaskType.SupplyEmptyPalletTask && locationCodes.Contains(x.CurrPointNo));
                    if (hasTask)
                    {
                        return null;
                    }

                    #endregion 如果有补空箱任务，并且起点是要送料机台的库位，就不再生成移库任务

                    //需要生成从该库位到其他机台的移库任务，找寻移库终点库位
                    moveEndLocation = AllWmsBaseLocationList.LastOrDefault(x => x.LocationId != moveStartLocation.LocationId && x.LocationStatus == 0 && string.IsNullOrEmpty(x.ContainerCode) && x.LocationId<=20);
                    if (moveEndLocation == null)
                    {
                        // 没有可以移库的库位，请等待
                        return null;
                        // todo:推送预警
                    }

                    #region 如果有送料任务，并且终点是要送料机台的库位，就不再生成移库任务
                    bool hasSendTask = sqlSugarClient.Queryable<WcsTask>().Any(it => it.TaskType == StaticTaskType.TransferMaterialBoxTask && it.EndPointNo == moveEndLocation.AgvPositionCode);
                    if (hasSendTask)
                    {
                        //_logger.Info("Agv已有该库位的送料任务");
                        return null;
                    }
                    #endregion 如果有送料任务，并且终点是要送料机台的库位，就不再生成移库任务

                    else  //生成移库任务
                    {
                        //二次确认寻找可用agv，避免在这期间agv被其他线程占用
                        try
                        {
                            //WcsBaseEquip? agvEquip = GetCanUseAgv();
                            //if (agvEquip == null)
                            //{
                            //    _logger.Info("暂时没有可用agv");
                            //    return null;
                            //}
                            WcsTask task = new WcsTask();
                            task.TaskType = StaticTaskType.MoveLocationTask;
                            task.CurrPointNo = moveStartLocation.AgvPositionCode;
                            task.EndPointNo = moveEndLocation.AgvPositionCode;
                            task.TaskStatus = 0;
                            task.CreatedTime = DateTime.Now;
                            task.CreatedBy = "wcs";
                            task.TaskName = "1-12机台之间空托盘移库任务";
                            task.PalletInfoCode = moveStartLocation.ContainerCode;
                            sqlSugarClient.AsTenant().BeginTran();
                            try
                            {
                                int id = sqlSugarClient.Insertable(task).ExecuteReturnIdentity();
                                WcsTaskLog wcsTaskLog = CoreMapper.Map<WcsTaskLog>(task);
                                wcsTaskLog.Id = id;
                                //锁定库位
                                moveStartLocation.LocationStatus = 1;
                                moveEndLocation.LocationStatus = 1;
                                sqlSugarClient.Updateable(moveStartLocation).ExecuteCommand();
                                sqlSugarClient.Updateable(moveEndLocation).ExecuteCommand();
                                sqlSugarClient.Insertable(wcsTaskLog).ExecuteCommand();
                                sqlSugarClient.AsTenant().CommitTran();
                                _logger.Agv($"生成{task.TaskName},起点:{task.CurrPointNo},终点:{task.EndPointNo}");
                            }
                            catch (Exception ex)
                            {
                                _logger.Error($"生成移库任务提交事务异常:{ex.Message}");
                                sqlSugarClient.AsTenant().RollbackTran();
                            }
                        }
                        catch (Exception ex)
                        {
                            _logger.Error($"生成移库任务异常:{ex.Message}");
                            return null;
                        }
                    }
                }
                return null;
            }
            catch (Exception ex)
            {
                _logger.Error($"GetTargetLocationAsync方法异常:{ex.StackTrace}");
                return null;
            }
        }

        #endregion 辅助方法

        #region 测试方法

        private async void test()
        {
            // 读RFID条码
            // string code =  await ReadEpcStrByRfidKeyAsync("1");
            //读光电信号
            //bool flag = await ReadSignalByRfidKeyAsync("1");
            //bool flag1 = await ReadSignalByRfidKeyAsync("1");

            //testTransitional();
            //testTransitionalRollback();
        }

        /// <summary>
        /// 测多线程事务影响程度
        /// </summary>
        private void testTransitional()
        {
            Task.Run(() =>
            {
                try
                {
                    sqlSugarClient.AsTenant().BeginTran();
                    for (int i = 0; i < 10; i++)
                    {
                        Console.WriteLine($"线程1{i}");
                        //WmsStock stock = new WmsStock();
                        // stock.WorkshopId = i;
                        //  sqlSugarClient.Insertable(stock).ExecuteCommand();
                        Thread.Sleep(1000);
                    }
                    sqlSugarClient.AsTenant().CommitTran();
                }
                catch (Exception ex)
                {
                    sqlSugarClient.AsTenant().RollbackTran();
                }
            });

            Task.Run(() =>
            {
                try
                {
                    sqlSugarClient.AsTenant().BeginTran();
                    for (int i = 20; i < 30; i++)
                    {
                        //Console.WriteLine($"线程2{i}");
                        //WmsStock stock = new WmsStock();
                        //stock.WorkshopId = i;
                        //sqlSugarClient.Insertable(stock).ExecuteCommand();
                        //Thread.Sleep(1000);
                        //if (i == 25)
                        //{
                        //    Console.WriteLine($"事务回滚");
                        //    throw new Exception("事务回滚");
                        //}
                    }
                    sqlSugarClient.AsTenant().CommitTran();
                }
                catch (Exception ex)
                {
                    sqlSugarClient.AsTenant().RollbackTran();
                }
            });
        }

        /// <summary>
        /// 测事务未开启但回滚
        /// </summary>
        private void testTransitionalRollback()
        {
            try
            {
                throw new Exception("事务回滚");
                sqlSugarClient.AsTenant().BeginTran();
                for (int i = 0; i < 10; i++)
                {
                    //Console.WriteLine($"线程1{i}");
                    //WmsStock stock = new WmsStock();
                    //stock.WorkshopId = i;
                    //sqlSugarClient.Insertable(stock).ExecuteCommand();
                    //Thread.Sleep(1000);
                }
                sqlSugarClient.AsTenant().CommitTran();
            }
            catch (Exception ex)
            {
                sqlSugarClient.AsTenant().RollbackTran();
            }
        }

        #endregion 测试方法
    }
}