3419 lines
192 KiB
C#
3419 lines
192 KiB
C#
using System;
|
||
using System.Diagnostics;
|
||
using System.Dynamic;
|
||
using System.Linq;
|
||
using System.Linq.Expressions;
|
||
using System.Net;
|
||
using System.Security.Policy;
|
||
using System.Text;
|
||
using System.Threading.Tasks;
|
||
using Aop.Api.Domain;
|
||
using Aspose.Cells;
|
||
using JNPF;
|
||
using JNPF.Common.Contracts;
|
||
using JNPF.Common.Core.Manager;
|
||
using JNPF.Common.Dtos.VisualDev;
|
||
using JNPF.Common.Enums;
|
||
using JNPF.Common.Extension;
|
||
using JNPF.Common.Manager;
|
||
using JNPF.Common.Security;
|
||
using JNPF.FriendlyException;
|
||
using JNPF.Systems.Entitys.System;
|
||
using JNPF.Systems.Interfaces.System;
|
||
using JNPF.VisualDev.Entitys;
|
||
using JNPF.VisualDev.Interfaces;
|
||
using Mapster;
|
||
using Microsoft.AspNetCore.Authorization;
|
||
using Microsoft.AspNetCore.Mvc;
|
||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||
using Microsoft.CodeAnalysis;
|
||
using Microsoft.Extensions.Configuration;
|
||
using Microsoft.Extensions.Logging;
|
||
using NetTaste;
|
||
using Newtonsoft.Json;
|
||
using Newtonsoft.Json.Linq;
|
||
using NPOI.OpenXmlFormats.Dml;
|
||
using NPOI.SS.Formula.Functions;
|
||
using Org.BouncyCastle.Crypto;
|
||
using SqlSugar;
|
||
using Tnb.BasicData.Entities;
|
||
using Tnb.Common.Extension;
|
||
using Tnb.Common.Redis;
|
||
using Tnb.Common.Utils;
|
||
using Tnb.ProductionMgr.Entities;
|
||
using Tnb.ProductionMgr.Entities.Entity;
|
||
using Tnb.QcMgr.Entities;
|
||
using Tnb.WarehouseMgr.Entities;
|
||
using Tnb.WarehouseMgr.Entities.Configs;
|
||
using Tnb.WarehouseMgr.Entities.Consts;
|
||
using Tnb.WarehouseMgr.Entities.Dto;
|
||
using Tnb.WarehouseMgr.Entities.Dto.Inputs;
|
||
using Tnb.WarehouseMgr.Entities.Dto.Outputs;
|
||
using Tnb.WarehouseMgr.Entities.Dto.Queries;
|
||
using Tnb.WarehouseMgr.Entities.Entity;
|
||
using Tnb.WarehouseMgr.Entities.Enums;
|
||
using Tnb.WarehouseMgr.Interfaces;
|
||
using static NPOI.HSSF.Util.HSSFColor;
|
||
using Tnb.BasicData;
|
||
using System.Reflection;
|
||
|
||
namespace Tnb.WarehouseMgr
|
||
{
|
||
/// <summary>
|
||
/// 库房业务类(出入库)
|
||
/// </summary>
|
||
public class WareHouseService : DevServBase<WareHouseService>, IWareHouseService
|
||
{
|
||
private readonly ISqlSugarClient _db;
|
||
private readonly IDictionaryDataService _dictionaryDataService;
|
||
private readonly IBillRullService _billRullService;
|
||
private readonly IUserManager _userManager;
|
||
private readonly ICacheManager _cacheManager;
|
||
private readonly IElevatorControlService _elevatorControlService;
|
||
private readonly IWmsCarryBindService _wmsCarryBindService;
|
||
private readonly IWmsCarryUnbindService _wmsCarryUnbindService;
|
||
private static readonly Dictionary<string, int> _elevatorAgvCtlStatusMap = new(StringComparer.OrdinalIgnoreCase);
|
||
private readonly ElevatorControlConfiguration _eleCtlCfg = App.Configuration.Build<ElevatorControlConfiguration>();
|
||
private static Dictionary<string, object> locMap = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||
private readonly IConfiguration _configuration;
|
||
private readonly StackExRedisHelper _redisData;
|
||
private readonly IVisualDevService _visualDevService;
|
||
private readonly IRunService _runService;
|
||
|
||
public static SemaphoreSlim s_floor2CreatePretask = new(1);
|
||
public static SemaphoreSlim s_taskCommonCreatePretask = new(1);
|
||
public static SemaphoreSlim s_taskGenPreTask = new(1);
|
||
public static SemaphoreSlim s_GenTaskExecute = new(1);
|
||
|
||
public Func<string, int, Task> AddUnExecuteTask { get; set; }
|
||
|
||
|
||
public WareHouseService(ISqlSugarRepository<WmsInstockH> repository, IDictionaryDataService dictionaryDataService, StackExRedisHelper redisData,
|
||
IBillRullService billRullService, IUserManager userManager, ICacheManager cacheManager, IElevatorControlService elevatorControlService,
|
||
IWmsCarryBindService wmsCarryBindService,
|
||
IWmsCarryUnbindService wmsCarryUnbindService,
|
||
IRunService runService,
|
||
IVisualDevService visualDevService
|
||
//IConfiguration configuration
|
||
) : base(repository.AsSugarClient())
|
||
{
|
||
_db = repository.AsSugarClient();
|
||
_dictionaryDataService = dictionaryDataService;
|
||
_billRullService = billRullService;
|
||
_userManager = userManager;
|
||
_cacheManager = cacheManager;
|
||
_elevatorControlService = elevatorControlService;
|
||
_redisData = redisData;
|
||
_wmsCarryBindService = wmsCarryBindService;
|
||
_wmsCarryUnbindService = wmsCarryUnbindService;
|
||
_runService = runService;
|
||
_visualDevService = visualDevService;
|
||
//_configuration = configuration;
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据载具Id带出库位、仓库信息
|
||
/// </summary>
|
||
/// <param name="carryId">载具id</param>
|
||
/// <remarks>
|
||
/// returns:
|
||
/// <br/>{
|
||
/// <br/> carry_id:载具Id
|
||
/// <br/> carry_name:载具名称
|
||
/// <br/> location_id:库位Id
|
||
/// <br/> location_name:库位名称
|
||
/// <br/> warehouse_id:库房Id
|
||
/// <br/> warehouse_name:库房名称
|
||
/// <br/>}
|
||
/// </remarks>
|
||
[HttpGet]
|
||
public async Task<dynamic> GetLocationAndWorkHouseByCarryId([FromRoute] string carryId)
|
||
{
|
||
var items = await _db.Queryable<WmsCarryH>().LeftJoin<BasLocation>((a, b) => a.location_id == b.id)
|
||
.LeftJoin<BasWarehouse>((a, b, c) => b.wh_id == c.id)
|
||
.Where(a => a.id == carryId)
|
||
.Select((a, b, c) => new
|
||
{
|
||
carry_id = a.id,
|
||
a.carry_name,
|
||
location_id = b.id,
|
||
|
||
b.location_name,
|
||
warehouse_id = c.id,
|
||
warehouse_name = c.whname,
|
||
})
|
||
.ToListAsync();
|
||
return items ?? Enumerable.Empty<dynamic>();
|
||
}
|
||
/// <summary>
|
||
/// 库房业务,入库、出库申请新增修改功能
|
||
/// </summary>
|
||
/// <param name="input"></param>
|
||
/// <returns></returns>
|
||
[HttpPost]
|
||
public async Task ApplyFor(InOutStockApplyforUpInput input)
|
||
{
|
||
if (input == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(input));
|
||
}
|
||
|
||
async Task<bool> _updateLocalFunc<TStockD, TStockCode>(InOutStockApplyforUpInput input)
|
||
where TStockD : BaseEntity<string>, new()
|
||
where TStockCode : BaseEntity<string>, IInOutStockCode, new()
|
||
{
|
||
TStockD instockD = input.Adapt<TStockD>();
|
||
global::System.Collections.Generic.List<TStockCode>? stockCodes = input.InstockCodes?.Adapt<List<TStockCode>>();
|
||
if (stockCodes?.Count > 0)
|
||
{
|
||
stockCodes.ForEach(x =>
|
||
{
|
||
if (x.id.IsNullOrWhiteSpace())
|
||
{
|
||
x.id = SnowflakeIdHelper.NextId();
|
||
}
|
||
x.bill_d_id = instockD.id;
|
||
});
|
||
}
|
||
return await Update(instockD, stockCodes!);
|
||
}
|
||
|
||
bool isOk = input.inoutStockType switch
|
||
{
|
||
EnumInOutStockType.In => await _updateLocalFunc<WmsInstockD, WmsInstockCode>(input),
|
||
EnumInOutStockType.Out => await _updateLocalFunc<WmsOutstockD, WmsOutstockCode>(input),
|
||
_ => throw new ArgumentOutOfRangeException(nameof(input.inoutStockType), $"Not expected EnumInOutStockType value: {input.inoutStockType}"),
|
||
};
|
||
if (!isOk)
|
||
{
|
||
throw Oops.Oh(ErrorCode.COM1001);
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 根据明细Id获取出入库明细信息
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
[HttpGet]
|
||
public async Task<dynamic?> GetInOutStockCodesById([FromQuery] InOutStockDetailQuery input)
|
||
{
|
||
dynamic? result = input.inoutStockType switch
|
||
{
|
||
EnumInOutStockType.In => await FetchInOutStockCodesById<WmsInstockD, InStockDetailOutput, WmsInstockCode>(input.bill_d_id),
|
||
EnumInOutStockType.Out => await FetchInOutStockCodesById<WmsOutstockD, OutStockDetailOutput, WmsOutstockCode>(input.bill_d_id),
|
||
_ => throw new NotImplementedException(),
|
||
};
|
||
return result ?? Enumerable.Empty<dynamic>();
|
||
}
|
||
/// <summary>
|
||
/// 入库策略
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
[HttpGet]
|
||
public async Task<List<BasLocation>> InStockStrategy([FromQuery] InStockStrategyQuery input)
|
||
{
|
||
List<BasLocation> items = new();
|
||
try
|
||
{
|
||
WmsInstockPolicies policy = await _db.CopyNew().Queryable<WmsInstockPolicies>().Where(it => it.status == 1).FirstAsync();
|
||
if (policy == null)
|
||
{
|
||
throw new AppFriendlyException("没有可用的策略", 500);
|
||
}
|
||
|
||
Expression<Func<BasLocation, bool>> whereExp = Expressionable.Create<BasLocation>()
|
||
.And(it => it.wh_id == input.warehouse_id)
|
||
.And(it => it.is_lock == 0)
|
||
.And(it => it.is_type == ((int)EnumLocationType.存储库位).ToString())
|
||
.And(it => it.is_use == ((int)EnumCarryStatus.空闲).ToString())
|
||
.ToExpression();
|
||
items = await _db.CopyNew().Queryable<BasLocation>().Where(whereExp).OrderBy(policy.policy).ToListAsync();
|
||
}
|
||
catch (Exception)
|
||
{
|
||
throw;
|
||
}
|
||
return items.Take(input.Size).ToList();
|
||
}
|
||
/// <summary>
|
||
/// 入库策略
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
[HttpGet]
|
||
public async Task<List<BasLocation>> InStockStrategyBCK([FromQuery] InStockStrategyQuery input)
|
||
{
|
||
List<BasLocation> items = new();
|
||
try
|
||
{
|
||
WmsInstockPolicies policy = await _db.CopyNew().Queryable<WmsInstockPolicies>().Where(it => it.status == 1).FirstAsync();
|
||
if (policy == null)
|
||
{
|
||
throw new AppFriendlyException("没有可用的策略", 500);
|
||
}
|
||
|
||
Expression<Func<BasLocation, bool>> whereExp = Expressionable.Create<BasLocation>()
|
||
.And(it => it.wh_id == input.warehouse_id)
|
||
.And(it => it.is_lock == 0)
|
||
.And(it => it.is_type == ((int)EnumLocationType.分拣库位).ToString())
|
||
.And(it => it.is_use == ((int)EnumCarryStatus.空闲).ToString())
|
||
.ToExpression();
|
||
items = await _db.CopyNew().Queryable<BasLocation>().Where(whereExp).OrderBy(policy.policy).ToListAsync();
|
||
}
|
||
catch (Exception)
|
||
{
|
||
throw;
|
||
}
|
||
return items.Take(input.Size).ToList();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 包材库2楼入库策略
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
[HttpGet]
|
||
public async Task<List<BasLocation>> BCKF2InStockStrategy([FromQuery] InStockStrategyQuery input)
|
||
{
|
||
List<BasLocation> items = new();
|
||
try
|
||
{
|
||
WmsInstockPolicies policy = await _db.CopyNew().Queryable<WmsInstockPolicies>().Where(it => it.status == 1).FirstAsync();
|
||
if (policy == null)
|
||
{
|
||
throw new AppFriendlyException("没有可用的策略", 500);
|
||
}
|
||
|
||
Expression<Func<BasLocation, bool>> whereExp = Expressionable.Create<BasLocation>()
|
||
.And(it => it.wh_id == input.warehouse_id)
|
||
.And(it => it.is_lock == 0)
|
||
.And(it => it.is_type == ((int)EnumLocationType.出入库位).ToString())
|
||
.And(it => it.is_use == ((int)EnumCarryStatus.空闲).ToString())
|
||
.ToExpression();
|
||
items = await _db.CopyNew().Queryable<BasLocation>().Where(whereExp).OrderBy(policy.policy).ToListAsync();
|
||
}
|
||
catch (Exception)
|
||
{
|
||
throw;
|
||
}
|
||
return items.Take(input.Size).ToList();
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 是否为一楼出库工位
|
||
/// </summary>
|
||
/// <param name="location_id"></param>
|
||
/// <returns></returns>
|
||
public string[] GetFloor1OutstockLocation()
|
||
{
|
||
return new string[23] { "30018211902485", "34355463261205", "34355450098709", "34355446145813"
|
||
, "34355443336981", "34355440377365", "34355436327189", "34355432397077", "34355428852501", "34355424568341"
|
||
, "34355421064213", "34355416966165", "34355407509269", "34355402216469", "34355397484565", "34355394965013"
|
||
, "34355391740181", "34355387110933", "34355383562005", "34355377989397", "34355374481173", "34355369617173"
|
||
, "30018211902485"};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否为二楼包材出库工位
|
||
/// </summary>
|
||
/// <param name="location_id"></param>
|
||
/// <returns></returns>
|
||
public string[] GetFloor2BCOutstockLocation()
|
||
{
|
||
return new string[1] { "30018211902485" };
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否为供料三工位库位
|
||
/// </summary>
|
||
/// <param name="location_id"></param>
|
||
/// <returns></returns>
|
||
public string[] GetFloor1GLSGWOutstockLocation()
|
||
{
|
||
return new string[5] { WmsWareHouseConst.Floor1GLSGWOutstockStation1 , WmsWareHouseConst.Floor1GLSGWOutstockStation2
|
||
, WmsWareHouseConst.Floor1GLSGWOutstockStation3, WmsWareHouseConst.Floor1GLSGWOutstockStation4, WmsWareHouseConst.Floor1GLSGWOutstockStation5 };
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否为外协三工位库位
|
||
/// </summary>
|
||
/// <param name="location_id"></param>
|
||
/// <returns></returns>
|
||
public string[] GetFloor1WXSGWOutstockLocation()
|
||
{
|
||
return new string[3] { WmsWareHouseConst.Floor1WXSGWOutstockStation1 , WmsWareHouseConst.Floor1WXSGWOutstockStation2
|
||
, WmsWareHouseConst.Floor1WXSGWOutstockStation3 };
|
||
}
|
||
|
||
/// <summary>
|
||
/// 出库策略-销售出库下发
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
[HttpGet]
|
||
public async Task<List<Tuple<string, WmsCarryH, WmsCarryCode, BasLocation>>> OutStockStrategy_saleRelease([FromQuery] OutStockStrategyQuery input)
|
||
{
|
||
Expressionable<WmsCarryH, WmsCarryCode, BasLocation> whereExprable = Expressionable.Create<WmsCarryH, WmsCarryCode, BasLocation>()
|
||
.And((a, b, c) => a.is_lock == 0 && c.is_lock == 0)
|
||
.And((a, b, c) => !string.IsNullOrEmpty(a.location_id))
|
||
.And((a, b, c) => c.is_type == ((int)EnumLocationType.存储库位).ToString())
|
||
.And((a, b, c) => a.out_status == "0")
|
||
.And((a, b, c) => c.wh_id == input.warehouse_id)
|
||
.AndIF(!string.IsNullOrEmpty(input.material_id), (a, b, c) => b.material_id == input.material_id)
|
||
.AndIF(!string.IsNullOrEmpty(input.code_batch), (a, b, c) => b.code_batch == input.code_batch)
|
||
.AndIF(!string.IsNullOrEmpty(input.material_specification), (a, b, c) => b.material_specification == input.material_specification)
|
||
.AndIF(!string.IsNullOrEmpty(input.container_no), (a, b, c) => b.container_no == input.container_no)
|
||
.AndIF(!string.IsNullOrEmpty(input.carrystd_id), (a, b, c) => a.carrystd_id == input.carrystd_id);
|
||
Expression<Func<WmsCarryH, WmsCarryCode, BasLocation, bool>> carryStatusFilterExp = !input.material_id.IsNullOrWhiteSpace()
|
||
? (a, b, c) => a.carry_status == ((int)EnumCarryStatus.占用).ToString()
|
||
: (a, b, c) => a.carry_status == ((int)EnumCarryStatus.空闲).ToString();
|
||
_ = whereExprable.And(carryStatusFilterExp);
|
||
Expression<Func<WmsCarryH, WmsCarryCode, BasLocation, bool>> whereExpr = whereExprable.ToExpression();
|
||
|
||
SqlSugarClient cyDb = _db.CopyNew();
|
||
|
||
|
||
List<Tuple<string, WmsCarryH, WmsCarryCode, BasLocation>> items = cyDb.Queryable<WmsCarryH>().LeftJoin<WmsCarryCode>((a, b) => a.id == b.carry_id)
|
||
.LeftJoin<BasLocation>((a, b, c) => a.location_id == c.id)
|
||
.Where(whereExpr)
|
||
//.OrderByIF((a,b,c)=>SqlFunc.IsNullOrEmpty())
|
||
.OrderBy("a.location_code,layers,loc_line,loc_column")
|
||
.Select((a, b, c) => new
|
||
{
|
||
WmsCarryH = a,
|
||
WmsCarryCode = b,
|
||
BasLocation = c,
|
||
codeqty = b.codeqty
|
||
}).ToList().Select(r =>
|
||
{
|
||
// 如果可出库数量已经扣减完
|
||
if (input.qty <= 0)
|
||
{
|
||
return new Tuple<string, WmsCarryH, WmsCarryCode, BasLocation>("", r.WmsCarryH, r.WmsCarryCode, r.BasLocation);
|
||
}
|
||
|
||
// 扣减可出库数量
|
||
input.qty = input.qty - r.codeqty;
|
||
|
||
// 出库数量与托盘上的数量不一致需要进行分拣
|
||
if (input.qty < 0)
|
||
{
|
||
r.WmsCarryCode.codeqty = input.qty + r.codeqty;
|
||
return new Tuple<string, WmsCarryH, WmsCarryCode, BasLocation>("分拣任务", r.WmsCarryH, r.WmsCarryCode, r.BasLocation);
|
||
}
|
||
else
|
||
{
|
||
// 正常预任务出库
|
||
return new Tuple<string, WmsCarryH, WmsCarryCode, BasLocation>("预任务", r.WmsCarryH, r.WmsCarryCode, r.BasLocation);
|
||
}
|
||
}).Where(r => r.Item1 != "").ToList();
|
||
return items;
|
||
}
|
||
|
||
public async Task<List<WmsCarryH>> OutStockStrategy([FromQuery] OutStockStrategyQuery input)
|
||
{
|
||
Expressionable<WmsCarryH, WmsCarryCode, BasLocation> whereExprable = Expressionable.Create<WmsCarryH, WmsCarryCode, BasLocation>()
|
||
.And((a, b, c) => a.is_lock == 0 && c.is_lock == 0)
|
||
.And((a, b, c) => !string.IsNullOrEmpty(a.location_id))
|
||
.And((a, b, c) => c.is_type == ((int)EnumLocationType.存储库位).ToString())
|
||
.And((a, b, c) => a.out_status == "0")
|
||
.And((a, b, c) => c.wh_id == input.warehouse_id)
|
||
.AndIF(!string.IsNullOrEmpty(input.material_id), (a, b, c) => b.material_id == input.material_id)
|
||
.AndIF(!string.IsNullOrEmpty(input.code_batch), (a, b, c) => b.code_batch == input.code_batch)
|
||
.AndIF(!string.IsNullOrEmpty(input.material_specification), (a, b, c) => b.material_specification == input.material_specification)
|
||
.AndIF(!string.IsNullOrEmpty(input.container_no), (a, b, c) => b.container_no == input.container_no)
|
||
.AndIF(!string.IsNullOrEmpty(input.carrystd_id), (a, b, c) => a.carrystd_id == input.carrystd_id);
|
||
Expression<Func<WmsCarryH, WmsCarryCode, BasLocation, bool>> carryStatusFilterExp = !input.material_id.IsNullOrWhiteSpace()
|
||
? (a, b, c) => a.carry_status == ((int)EnumCarryStatus.占用).ToString()
|
||
: (a, b, c) => a.carry_status == ((int)EnumCarryStatus.空闲).ToString();
|
||
_ = whereExprable.And(carryStatusFilterExp);
|
||
Expression<Func<WmsCarryH, WmsCarryCode, BasLocation, bool>> whereExpr = whereExprable.ToExpression();
|
||
|
||
SqlSugarClient cyDb = _db.CopyNew();
|
||
WmsInstockPolicies policy = await cyDb.Queryable<WmsInstockPolicies>().Where(it => it.status == 1).FirstAsync();
|
||
if (policy == null)
|
||
{
|
||
throw new AppFriendlyException("没有可用策略", 500);
|
||
}
|
||
|
||
List<WmsCarryH> items = await cyDb.Queryable<WmsCarryH>().LeftJoin<WmsCarryCode>((a, b) => a.id == b.carry_id)
|
||
.LeftJoin<BasLocation>((a, b, c) => a.location_id == c.id)
|
||
.Where(whereExpr)
|
||
//.OrderByIF((a,b,c)=>SqlFunc.IsNullOrEmpty())
|
||
.OrderBy(policy.policy)
|
||
|
||
.Select<WmsCarryH>()
|
||
.ToListAsync();
|
||
return input.Size > 0 ? items.Take(input.Size).ToList() : items;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 原材料仓出库策略
|
||
/// </summary>
|
||
/// <param name="input"></param>
|
||
/// <returns></returns>
|
||
/// <exception cref="AppFriendlyException"></exception>
|
||
public async Task<List<WmsCarryH>> OutStockStrategyYCL([FromQuery] OutStockStrategyQuery input)
|
||
{
|
||
Expressionable<WmsCarryH, WmsCarryCode, BasLocation> whereExprable = Expressionable.Create<WmsCarryH, WmsCarryCode, BasLocation>()
|
||
.And((a, b, c) => a.is_lock == 0 && c.is_lock == 0)
|
||
.And((a, b, c) => !string.IsNullOrEmpty(a.location_id))
|
||
.And((a, b, c) => c.is_type == ((int)EnumLocationType.存储库位).ToString())
|
||
.And((a, b, c) => a.out_status == "0")
|
||
.And((a, b, c) => c.wh_id == input.warehouse_id)
|
||
.AndIF(!string.IsNullOrEmpty(input.material_id), (a, b, c) => b.material_id == input.material_id)
|
||
.AndIF(!string.IsNullOrEmpty(input.code_batch), (a, b, c) => b.code_batch == input.code_batch)
|
||
.AndIF(!string.IsNullOrEmpty(input.material_specification), (a, b, c) => b.material_specification == input.material_specification)
|
||
.AndIF(!string.IsNullOrEmpty(input.container_no), (a, b, c) => b.container_no == input.container_no)
|
||
.AndIF(!string.IsNullOrEmpty(input.carrystd_id), (a, b, c) => a.carrystd_id == input.carrystd_id);
|
||
Expression<Func<WmsCarryH, WmsCarryCode, BasLocation, bool>> carryStatusFilterExp = !input.material_id.IsNullOrWhiteSpace()
|
||
? (a, b, c) => a.carry_status == ((int)EnumCarryStatus.占用).ToString()
|
||
: (a, b, c) => a.carry_status == ((int)EnumCarryStatus.空闲).ToString();
|
||
_ = whereExprable.And(carryStatusFilterExp);
|
||
Expression<Func<WmsCarryH, WmsCarryCode, BasLocation, bool>> whereExpr = whereExprable.ToExpression();
|
||
|
||
SqlSugarClient cyDb = _db.CopyNew();
|
||
WmsInstockPolicies policy = await cyDb.Queryable<WmsInstockPolicies>().Where(it => it.status == 1).FirstAsync();
|
||
if (policy == null)
|
||
{
|
||
throw new AppFriendlyException("没有可用策略", 500);
|
||
}
|
||
|
||
List<WmsCarryH> items = await cyDb.Queryable<WmsCarryH>().LeftJoin<WmsCarryCode>((a, b) => a.id == b.carry_id)
|
||
.LeftJoin<BasLocation>((a, b, c) => a.location_id == c.id)
|
||
.Where(whereExpr)
|
||
//.OrderByIF((a,b,c)=>SqlFunc.IsNullOrEmpty())
|
||
.OrderBy(policy.policy)
|
||
|
||
.Select<WmsCarryH>()
|
||
.ToListAsync();
|
||
|
||
items = items.Distinct().ToList();
|
||
return input.Size > 0 ? items.Take(input.Size).ToList() : items;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 缓存仓出库策略
|
||
/// </summary>
|
||
/// <param name="input"></param>
|
||
/// <returns></returns>
|
||
/// <exception cref="AppFriendlyException"></exception>
|
||
public async Task<List<WmsCarryH>> OutStockStrategyHCC([FromQuery] OutStockStrategyQuery input)
|
||
{
|
||
Expressionable<WmsCarryH, WmsCarryCode, BasLocation> whereExprable = Expressionable.Create<WmsCarryH, WmsCarryCode, BasLocation>()
|
||
.And((a, b, c) => a.is_lock == 0 && c.is_lock == 0)
|
||
.And((a, b, c) => !string.IsNullOrEmpty(a.location_id))
|
||
.And((a, b, c) => c.is_type == ((int)EnumLocationType.存储库位).ToString())
|
||
.And((a, b, c) => a.out_status == "0")
|
||
.And((a, b, c) => c.wh_id == input.warehouse_id)
|
||
.AndIF(!string.IsNullOrEmpty(input.material_id), (a, b, c) => b.material_id == input.material_id)
|
||
.AndIF(!string.IsNullOrEmpty(input.code_batch), (a, b, c) => b.code_batch == input.code_batch)
|
||
.AndIF(!string.IsNullOrEmpty(input.material_specification), (a, b, c) => b.material_specification == input.material_specification)
|
||
.AndIF(!string.IsNullOrEmpty(input.container_no), (a, b, c) => b.container_no == input.container_no)
|
||
.AndIF(!string.IsNullOrEmpty(input.carrystd_id), (a, b, c) => a.carrystd_id == input.carrystd_id);
|
||
Expression<Func<WmsCarryH, WmsCarryCode, BasLocation, bool>> carryStatusFilterExp = !input.material_id.IsNullOrWhiteSpace()
|
||
? (a, b, c) => a.carry_status == ((int)EnumCarryStatus.占用).ToString()
|
||
: (a, b, c) => a.carry_status == ((int)EnumCarryStatus.空闲).ToString();
|
||
_ = whereExprable.And(carryStatusFilterExp);
|
||
Expression<Func<WmsCarryH, WmsCarryCode, BasLocation, bool>> whereExpr = whereExprable.ToExpression();
|
||
|
||
SqlSugarClient cyDb = _db.CopyNew();
|
||
WmsInstockPolicies policy = await cyDb.Queryable<WmsInstockPolicies>().Where(it => it.status == 1).FirstAsync();
|
||
if (policy == null)
|
||
{
|
||
throw new AppFriendlyException("没有可用策略", 500);
|
||
}
|
||
|
||
List<WmsCarryH> items = await cyDb.Queryable<WmsCarryH>().LeftJoin<WmsCarryCode>((a, b) => a.id == b.carry_id)
|
||
.LeftJoin<BasLocation>((a, b, c) => a.location_id == c.id)
|
||
.Where(whereExpr)
|
||
//.OrderByIF((a,b,c)=>SqlFunc.IsNullOrEmpty())
|
||
.OrderBy(policy.policy)
|
||
|
||
.Select<WmsCarryH>()
|
||
.ToListAsync();
|
||
|
||
|
||
items = items.Distinct().ToList();
|
||
return input.Size > 0 ? items.Take(input.Size).ToList() : items;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 1->2出库策略
|
||
/// </summary>
|
||
/// <param name="input"></param>
|
||
/// <returns></returns>
|
||
/// <exception cref="AppFriendlyException"></exception>
|
||
public async Task<List<Tuple<WmsCarryH, decimal, BasLocation>>> OutStockStrategyZCC2Floor2([FromQuery] OutStockStrategyZCC2Floor2Query input)
|
||
{
|
||
Expressionable<WmsCarryH, WmsCarryCode, BasLocation> whereExprable = Expressionable.Create<WmsCarryH, WmsCarryCode, BasLocation>()
|
||
.And((a, b, c) => a.is_lock == 0 && c.is_lock == 0)
|
||
.And((a, b, c) => !string.IsNullOrEmpty(a.location_id))
|
||
.And((a, b, c) => c.is_type == ((int)EnumLocationType.存储库位).ToString())
|
||
.And((a, b, c) => a.out_status == "0")
|
||
.And((a, b, c) => c.wh_id == input.warehouse_id)
|
||
.AndIF(!string.IsNullOrEmpty(input.material_id), (a, b, c) => b.material_id == input.material_id)
|
||
.AndIF(!string.IsNullOrEmpty(input.code_batch), (a, b, c) => b.code_batch == input.code_batch)
|
||
.AndIF(!string.IsNullOrEmpty(input.material_specification), (a, b, c) => b.material_specification == input.material_specification)
|
||
.AndIF(!string.IsNullOrEmpty(input.container_no), (a, b, c) => b.container_no == input.container_no)
|
||
.AndIF(!string.IsNullOrEmpty(input.carrystd_id), (a, b, c) => a.carrystd_id == input.carrystd_id);
|
||
Expression<Func<WmsCarryH, WmsCarryCode, BasLocation, bool>> carryStatusFilterExp = !input.material_id.IsNullOrWhiteSpace()
|
||
? (a, b, c) => a.carry_status == ((int)EnumCarryStatus.占用).ToString()
|
||
: (a, b, c) => a.carry_status == ((int)EnumCarryStatus.空闲).ToString();
|
||
_ = whereExprable.And(carryStatusFilterExp);
|
||
Expression<Func<WmsCarryH, WmsCarryCode, BasLocation, bool>> whereExpr = whereExprable.ToExpression();
|
||
|
||
SqlSugarClient cyDb = _db.CopyNew();
|
||
WmsInstockPolicies policy = await cyDb.Queryable<WmsInstockPolicies>().Where(it => it.status == 1).FirstAsync();
|
||
if (policy == null)
|
||
{
|
||
throw new AppFriendlyException("没有可用策略", 500);
|
||
}
|
||
|
||
#region 只解决少数情况(比如只有60 59 60 60四个料箱,下发180,要取60 60 60) 其它情况不考虑
|
||
|
||
var itemsASC = await cyDb.Queryable<WmsCarryH>().LeftJoin<WmsCarryCode>((a, b) => a.id == b.carry_id)
|
||
.LeftJoin<BasLocation>((a, b, c) => a.location_id == c.id)
|
||
.Where(whereExpr)
|
||
//.OrderByIF((a,b,c)=>SqlFunc.IsNullOrEmpty())
|
||
//.OrderBy(policy.policy)
|
||
.OrderBy("codeqty desc,a.location_code,layers,loc_line,loc_column")
|
||
|
||
.Select((a, b) => new { wmsCarryH = a, wmsCarryCode = b })
|
||
.ToListAsync();
|
||
//items = itemsASC.Count < itemsDESC.Count ? itemsASC : itemsDESC;
|
||
|
||
itemsASC = itemsASC.Distinct().ToList();
|
||
|
||
List<Tuple<WmsCarryH, decimal, BasLocation>>? carrys = new List<Tuple<WmsCarryH, decimal, BasLocation>>();
|
||
|
||
// 6个下发一条任务链
|
||
int move_num = 6;
|
||
int endlocation_index = 0;
|
||
string[] endlocations = new string[2] { "32609229889045", "32609238573589" };
|
||
BasLocation endlocation_ssx = null;
|
||
|
||
for (int i = 0; i < itemsASC.Count; i++)
|
||
{
|
||
WmsCarryH wmsCarryH = itemsASC[i].wmsCarryH;
|
||
WmsCarryCode wmsCarryCode = itemsASC[i].wmsCarryCode;
|
||
|
||
if (input.needOut <= 0)
|
||
{
|
||
break;
|
||
}
|
||
|
||
// 每6个重新获取一次终点
|
||
if (i % move_num == 0)
|
||
{
|
||
endlocation_ssx = await _db.Queryable<BasLocation>().Where(r => endlocations.Contains(r.id)).OrderBy("is_lock, task_nums, location_code").FirstAsync();
|
||
}
|
||
|
||
// 查找是否有一个料箱可以正好满足剩余需求数量(目前只做这个额外判断,其它情形不考虑)
|
||
bool isFind = false;
|
||
for (int j = i + 1; j < itemsASC.Count; j++)
|
||
{
|
||
WmsCarryCode _wmsCarryCode = itemsASC[j].wmsCarryCode;
|
||
if (_wmsCarryCode.codeqty == input.needOut)
|
||
{
|
||
input.needOut -= _wmsCarryCode.codeqty;
|
||
WmsCarryH _wmsCarryH = itemsASC[j].wmsCarryH;
|
||
|
||
await _db.Updateable<BasLocation>().SetColumns(it => it.task_nums == it.task_nums + 1).Where(it => endlocation_ssx.id == it.id).ExecuteCommandAsync();
|
||
|
||
carrys.Add(new Tuple<WmsCarryH, decimal, BasLocation>(_wmsCarryH, _wmsCarryCode.codeqty, endlocation_ssx));
|
||
|
||
isFind = true;
|
||
break;
|
||
}
|
||
|
||
}
|
||
if (isFind)
|
||
break;
|
||
|
||
// 目前只支持一个料箱只有一个物料
|
||
input.needOut -= wmsCarryCode.codeqty;
|
||
|
||
await _db.Updateable<BasLocation>().SetColumns(it => it.task_nums == it.task_nums + 1).Where(it => endlocation_ssx.id == it.id).ExecuteCommandAsync();
|
||
|
||
carrys.Add(new Tuple<WmsCarryH, decimal, BasLocation>(wmsCarryH, wmsCarryCode.codeqty, endlocation_ssx));
|
||
}
|
||
#endregion
|
||
|
||
if (input.needOut > 0)
|
||
{
|
||
throw new AppFriendlyException($"物料{input.material_code}没有足够的库存!,缺失数量为{input.needOut}", 500);
|
||
}
|
||
|
||
|
||
return carrys;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 判断CTU是否可以放货
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
[HttpPost]
|
||
[AllowAnonymous]
|
||
public async Task CheckPut(CheckPutInput input)
|
||
{
|
||
Logger.Information("联核请求CheckPut接口传入参数为:" + JsonConvert.SerializeObject(input));
|
||
|
||
Dictionary<string, string[]> putdic = new Dictionary<string, string[]>();
|
||
|
||
putdic.Add("SSX-011-006", new string[] { "YTCS", "AllowEmptyIn_CS06" });
|
||
putdic.Add("SSX-021-007", new string[] { "东面提升机输送线", "出库输送线7允许入箱" });
|
||
putdic.Add("SSX-121-009", new string[] { "东面提升机输送线", "上升降机9允许入箱" });
|
||
putdic.Add("SSX-121-010", new string[] { "东面提升机输送线", "上升降机10允许入箱" });
|
||
putdic.Add("SSX-021-003", new string[] { "YTCS", "AllowCtuEmptyIn_CS03" });
|
||
putdic.Add("SSX-021-001", new string[] { "YTCS", "AllowCtuEmptyIn_CS01" });
|
||
putdic.Add("ZSSSXCTU02", new string[] { "YTCS", "AllowCtuFullOut_CS04", });
|
||
putdic.Add("ZSSSXCTU01", new string[] { "YTCS", "AllowCtuFullOut_CS02", });
|
||
var strs = new string[] { };
|
||
|
||
if (!putdic.ContainsKey(input.targetName))
|
||
throw new AppFriendlyException("点位" + input.targetName + "不存在", 500);
|
||
strs = putdic.Where(p => p.Key == input.targetName).First().Value;
|
||
|
||
bool flag = await _redisData.HashExists(strs[0], strs[1]);
|
||
if (!flag)
|
||
{
|
||
throw new AppFriendlyException("点位" + input.targetName + "不存在", 500);
|
||
}
|
||
string data = await _redisData.GetHash(strs[0], strs[1]);
|
||
Logger.Information("wcs请求CheckPut接口查询X2Server数据:" + data);
|
||
JObject? res = JsonConvert.DeserializeObject<JObject>(data);
|
||
bool result = res != null && res["Value"] != null ? res.Value<bool>("Value") : false;
|
||
if (!result)
|
||
throw new AppFriendlyException("点位" + input.targetName + "不可放", 500);
|
||
Logger.Information("wcs请求CheckPut接口结果:CTU可放货" + data);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 判断CTU是否可以取货
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
|
||
public async Task<bool> Check(string code, string action)
|
||
{
|
||
Logger.Information($"【Check】 判断KIVA是否可以{action} {code}");
|
||
Dictionary<string, string[]> putdic = new Dictionary<string, string[]>();
|
||
Dictionary<string, string[]> getdic = new Dictionary<string, string[]>();
|
||
|
||
putdic.Add("ZSSSXCTU02", new string[] { "YTCS", "AllowAgvFullIn_CS04", });
|
||
putdic.Add("ZSSSXCTU01", new string[] { "YTCS", "AllowAgvFullIn_CS02", });
|
||
getdic.Add("ZSSSXCTU01", new string[] { "YTCS", "AllowAgvEmptyOut_CS01" });
|
||
getdic.Add("ZSSSXCTU02", new string[] { "YTCS", "AllowAgvEmptyOut_CS03" });
|
||
|
||
#region 注塑车间点位
|
||
putdic.Add("ZS-C01-1", new string[] { "hxjC", "A2允许入空箱", });
|
||
getdic.Add("ZS-C01-2", new string[] { "hxjC", "A2允许取满箱" });
|
||
|
||
putdic.Add("ZS-C02-1", new string[] { "hxjC", "A3允许入空箱", });
|
||
getdic.Add("ZS-C02-2", new string[] { "hxjC", "A3允许取满箱" });
|
||
|
||
putdic.Add("ZS-C03-1", new string[] { "hxjC", "A4允许入空箱", });
|
||
getdic.Add("ZS-C03-2", new string[] { "hxjC", "A4允许取满箱" });
|
||
|
||
putdic.Add("ZS-C04-1", new string[] { "hxjC", "A5允许入空箱", });
|
||
getdic.Add("ZS-C04-2", new string[] { "hxjC", "A5允许取满箱" });
|
||
|
||
putdic.Add("ZS-C05-1", new string[] { "hxjC", "A6允许入空箱", });
|
||
getdic.Add("ZS-C05-2", new string[] { "hxjC", "A6允许取满箱" });
|
||
|
||
putdic.Add("ZS-C06-1", new string[] { "hxjC", "A7允许入空箱", });
|
||
getdic.Add("ZS-C06-2", new string[] { "hxjC", "A7允许取满箱" });
|
||
|
||
putdic.Add("ZS-C07-1", new string[] { "hxjC", "A8允许入空箱", });
|
||
getdic.Add("ZS-C07-2", new string[] { "hxjC", "A8允许取满箱" });
|
||
|
||
putdic.Add("ZS-C08-1", new string[] { "hxjC", "A9允许入空箱", });
|
||
getdic.Add("ZS-C08-2", new string[] { "hxjC", "A9允许取满箱" });
|
||
|
||
putdic.Add("ZS-C09-1", new string[] { "hxjC", "A10允许入空箱", });
|
||
getdic.Add("ZS-C09-2", new string[] { "hxjC", "A10允许取满箱" });
|
||
|
||
putdic.Add("ZS-C10-1", new string[] { "hxjC", "A11允许入空箱", });
|
||
getdic.Add("ZS-C10-2", new string[] { "hxjC", "A11允许取满箱" });
|
||
|
||
putdic.Add("ZS-C11-1", new string[] { "hxjC", "A12允许入空箱", });
|
||
getdic.Add("ZS-C11-2", new string[] { "hxjC", "A12允许取满箱" });
|
||
|
||
putdic.Add("ZS-C12-1", new string[] { "hxjC", "A13允许入空箱", });
|
||
getdic.Add("ZS-C12-2", new string[] { "hxjC", "A13允许取满箱" });
|
||
|
||
putdic.Add("ZS-C13-1", new string[] { "hxjC", "A14允许入空箱", });
|
||
getdic.Add("ZS-C13-2", new string[] { "hxjC", "A14允许取满箱" });
|
||
|
||
putdic.Add("ZS-C14-1", new string[] { "hxjC", "A1允许入空箱", });
|
||
getdic.Add("ZS-C14-2", new string[] { "hxjC", "A1允许取满箱" });
|
||
|
||
|
||
putdic.Add("ZS-A01-1", new string[] { "hxjA", "A3允许入空箱", });
|
||
getdic.Add("ZS-A01-2", new string[] { "hxjA", "A3允许取满箱" });
|
||
|
||
putdic.Add("ZS-A02-1", new string[] { "hxjA", "A4允许入空箱", });
|
||
getdic.Add("ZS-A02-2", new string[] { "hxjA", "A4允许取满箱" });
|
||
|
||
putdic.Add("ZS-A03-1", new string[] { "hxjA", "A5允许入空箱", });
|
||
getdic.Add("ZS-A03-2", new string[] { "hxjA", "A5允许取满箱" });
|
||
|
||
putdic.Add("ZS-A04-1", new string[] { "hxjA", "A6允许入空箱", });
|
||
getdic.Add("ZS-A04-2", new string[] { "hxjA", "A6允许取满箱" });
|
||
|
||
putdic.Add("ZS-A05-1", new string[] { "hxjA", "A7允许入空箱", });
|
||
getdic.Add("ZS-A05-2", new string[] { "hxjA", "A7允许取满箱" });
|
||
|
||
putdic.Add("ZS-A06-1", new string[] { "hxjA", "A8允许入空箱", });
|
||
getdic.Add("ZS-A06-2", new string[] { "hxjA", "A8允许取满箱" });
|
||
|
||
putdic.Add("ZS-A07-1", new string[] { "hxjA", "A9允许入空箱", });
|
||
getdic.Add("ZS-A07-2", new string[] { "hxjA", "A9允许取满箱" });
|
||
|
||
putdic.Add("ZS-A08-1", new string[] { "hxjA", "A10允许入空箱", });
|
||
getdic.Add("ZS-A08-2", new string[] { "hxjA", "A10允许取满箱" });
|
||
|
||
putdic.Add("ZS-A09-1", new string[] { "hxjA", "A11允许入空箱", });
|
||
getdic.Add("ZS-A09-2", new string[] { "hxjA", "A11允许取满箱" });
|
||
|
||
putdic.Add("ZS-A10-1", new string[] { "hxjA", "A12允许入空箱", });
|
||
getdic.Add("ZS-A10-2", new string[] { "hxjA", "A12允许取满箱" });
|
||
|
||
putdic.Add("ZS-A11-1", new string[] { "hxjA", "A13允许入空箱", });
|
||
getdic.Add("ZS-A11-2", new string[] { "hxjA", "A13允许取满箱" });
|
||
|
||
putdic.Add("ZS-A12-1", new string[] { "hxjA", "A14允许入空箱", });
|
||
getdic.Add("ZS-A12-2", new string[] { "hxjA", "A14允许取满箱" });
|
||
#endregion
|
||
|
||
var strs = new string[] { };
|
||
|
||
if (action == "LOAD")//取货
|
||
{
|
||
if (!getdic.ContainsKey(code))
|
||
{
|
||
return false;
|
||
}
|
||
strs = getdic.Where(p => p.Key == code).First().Value;
|
||
|
||
bool flag = await _redisData.HashExists(strs[0], strs[1]);
|
||
Logger.Information($"【Check】{action} 判断KIVA是否可以取货(信号是否存在) 获取{code}的标签{strs[1]}是否存在 结果为:{flag} ");
|
||
if (!flag)
|
||
{
|
||
throw new Exception($"【Check】{action} 判断KIVA是否可以取货(信号是否存在) 获取{code}的标签{strs[1]}是否存在 结果为:{flag} ");
|
||
}
|
||
string data = _redisData.GetHash(strs[0], strs[1]).Result;
|
||
JObject? res = JsonConvert.DeserializeObject<JObject>(data);
|
||
bool result = res != null && res["Value"] != null ? res.Value<bool>("Value") : false;
|
||
Logger.Information($"【Check】{action} 判断KIVA是否可以取货(信号是否允许) 获取{code}的标签{strs[1]}信号值 结果为:{result} ");
|
||
if (!result)
|
||
{
|
||
throw new Exception($"【Check】{action} 判断KIVA是否可以取货(信号是否允许) 获取{code}的标签{strs[1]}信号值 结果为:{result} ");
|
||
}
|
||
return true;
|
||
}
|
||
else if (action == "UNLOAD")//放货
|
||
{
|
||
if (!putdic.ContainsKey(code))
|
||
return false;
|
||
strs = putdic.Where(p => p.Key == code).First().Value;
|
||
|
||
bool flag = await _redisData.HashExists(strs[0], strs[1]);
|
||
Logger.Information($"【Check】 判断CTU是否可以放货(信号是否存在) 获取{code}的标签{strs[1]}是否存在 结果为:{flag} ");
|
||
if (!flag)
|
||
{
|
||
throw new Exception($"【Check】 判断CTU是否可以放货(信号是否存在) 获取{code}的标签{strs[1]}是否存在 结果为:{flag} ");
|
||
}
|
||
string data = _redisData.GetHash(strs[0], strs[1]).Result;
|
||
JObject? res = JsonConvert.DeserializeObject<JObject>(data);
|
||
bool result = res != null && res["Value"] != null ? res.Value<bool>("Value") : false;
|
||
Logger.Information($"【Check】 判断CTU是否可以放货(信号是否允许) 获取{code}的标签{strs[1]}信号值 结果为:{result}");
|
||
if (!result)
|
||
{
|
||
throw new Exception($"【Check】 判断CTU是否可以放货(信号是否允许) 获取{code}的标签{strs[1]}信号值 结果为:{result} ");
|
||
}
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
public async Task SsxControl(WmsDistaskH disTask, string action)
|
||
{
|
||
Logger.Information($"输送线控制SsxControl传入参数: {JsonConvert.SerializeObject(disTask)} {action}");
|
||
|
||
Dictionary<string, string[]> putdic = new Dictionary<string, string[]>();
|
||
Dictionary<string, string[]> getdic = new Dictionary<string, string[]>();
|
||
getdic.Add("SSX-021-005", new string[] { "YTCS", "FullOut_CS05Done", "true" });
|
||
getdic.Add("SSX-111-011", new string[] { "东面提升机输送线", "下升降机11出箱完毕", "true" });
|
||
getdic.Add("SSX-111-012", new string[] { "东面提升机输送线", "下升降机12出箱完毕", "true" });
|
||
getdic.Add("ZSSSXCTU02", new string[] { "YTCS", "右输送线上层允许出箱3", "true" });
|
||
getdic.Add("ZSSSXCTU01", new string[] { "YTCS", "左输送线上层允许出箱1", "true" });
|
||
getdic.Add("SSX-011-008", new string[] { "东面提升机输送线", "入库输送线8出箱完毕", "true" });
|
||
getdic.Add("ZS-C01-2", new string[] { "hxjC", "A2AGV允许入满箱", "true" });
|
||
getdic.Add("ZS-C02-2", new string[] { "hxjC", "A3AGV允许入满箱", "true" });
|
||
getdic.Add("ZS-C03-2", new string[] { "hxjC", "A4AGV允许入满箱", "true" });
|
||
getdic.Add("ZS-C04-2", new string[] { "hxjC", "A5AGV允许入满箱", "true" });
|
||
getdic.Add("ZS-C05-2", new string[] { "hxjC", "A6AGV允许入满箱", "true" });
|
||
getdic.Add("ZS-C06-2", new string[] { "hxjC", "A7AGV允许入满箱", "true" });
|
||
getdic.Add("ZS-C07-2", new string[] { "hxjC", "A8AGV允许入满箱", "true" });
|
||
getdic.Add("ZS-C08-2", new string[] { "hxjC", "A9AGV允许入满箱", "true" });
|
||
getdic.Add("ZS-C09-2", new string[] { "hxjC", "A10AGV允许入满箱", "true" });
|
||
getdic.Add("ZS-C10-2", new string[] { "hxjC", "A11AGV允许入满箱", "true" });
|
||
getdic.Add("ZS-C11-2", new string[] { "hxjC", "A12AGV允许入满箱", "true" });
|
||
getdic.Add("ZS-C12-2", new string[] { "hxjC", "A13AGV允许入满箱", "true" });
|
||
getdic.Add("ZS-C13-2", new string[] { "hxjC", "A14AGV允许入满箱", "true" });
|
||
getdic.Add("ZS-C14-2", new string[] { "hxjC", "A1AGV允许入满箱", "true" });
|
||
|
||
getdic.Add("ZS-A01-2", new string[] { "hxjA", "A3AGV允许入满箱", "true" });
|
||
getdic.Add("ZS-A02-2", new string[] { "hxjA", "A4AGV允许入满箱", "true" });
|
||
getdic.Add("ZS-A03-2", new string[] { "hxjA", "A5AGV允许入满箱", "true" });
|
||
getdic.Add("ZS-A04-2", new string[] { "hxjA", "A6AGV允许入满箱", "true" });
|
||
getdic.Add("ZS-A05-2", new string[] { "hxjA", "A7AGV允许入满箱", "true" });
|
||
getdic.Add("ZS-A06-2", new string[] { "hxjA", "A8AGV允许入满箱", "true" });
|
||
getdic.Add("ZS-A07-2", new string[] { "hxjA", "A9AGV允许入满箱", "true" });
|
||
getdic.Add("ZS-A08-2", new string[] { "hxjA", "A10AGV允许入满箱", "true" });
|
||
getdic.Add("ZS-A09-2", new string[] { "hxjA", "A11AGV允许入满箱", "true" });
|
||
getdic.Add("ZS-A10-2", new string[] { "hxjA", "A12AGV允许入满箱", "true" });
|
||
getdic.Add("ZS-A11-2", new string[] { "hxjA", "A13AGV允许入满箱", "true" });
|
||
getdic.Add("ZS-A12-2", new string[] { "hxjA", "A14AGV允许入满箱", "true" });
|
||
|
||
|
||
|
||
putdic.Add("SSX-021-007", new string[] { "东面提升机输送线", "出库输送线7入箱完毕", "true" });
|
||
putdic.Add("SSX-011-006", new string[] { "YTCS", "EmptyIn_CS06Done", "true" });
|
||
putdic.Add("SSX-021-003", new string[] { "YTCS", "AgvFullIn_CS03Done", "true" });
|
||
putdic.Add("SSX-021-001", new string[] { "YTCS", "CtuEmptyIn_CS01Done", "true" });
|
||
putdic.Add("ZSSSXCTU02", new string[] { "YTCS", "右输送线下层允许入箱4", "true" });
|
||
putdic.Add("ZSSSXCTU01", new string[] { "YTCS", "左输送线下层允许入箱2", "true" });
|
||
putdic.Add("SSX-121-009", new string[] { "东面提升机输送线", "上升降机9入箱完毕", "true" });
|
||
putdic.Add("SSX-121-010", new string[] { "东面提升机输送线", "上升降机10入箱完毕", "true" });
|
||
putdic.Add("YCLCKBGW", new string[] { "CP8", "PutDoneEmptyBox", "true" });
|
||
putdic.Add("ZS-C01-1", new string[] { "hxjC", "A2AGV允许出空箱", "true" });
|
||
putdic.Add("ZS-C02-1", new string[] { "hxjC", "A3AGV允许出空箱", "true" });
|
||
putdic.Add("ZS-C03-1", new string[] { "hxjC", "A4AGV允许出空箱", "true" });
|
||
putdic.Add("ZS-C04-1", new string[] { "hxjC", "A5AGV允许出空箱", "true" });
|
||
putdic.Add("ZS-C05-1", new string[] { "hxjC", "A6AGV允许出空箱", "true" });
|
||
putdic.Add("ZS-C06-1", new string[] { "hxjC", "A7AGV允许出空箱", "true" });
|
||
putdic.Add("ZS-C07-1", new string[] { "hxjC", "A8AGV允许出空箱", "true" });
|
||
putdic.Add("ZS-C08-1", new string[] { "hxjC", "A9AGV允许出空箱", "true" });
|
||
putdic.Add("ZS-C09-1", new string[] { "hxjC", "A10AGV允许出空箱", "true" });
|
||
putdic.Add("ZS-C10-1", new string[] { "hxjC", "A11AGV允许出空箱", "true" });
|
||
putdic.Add("ZS-C11-1", new string[] { "hxjC", "A12AGV允许出空箱", "true" });
|
||
putdic.Add("ZS-C12-1", new string[] { "hxjC", "A13AGV允许出空箱", "true" });
|
||
putdic.Add("ZS-C13-1", new string[] { "hxjC", "A14AGV允许出空箱", "true" });
|
||
putdic.Add("ZS-C14-1", new string[] { "hxjC", "A1AGV允许出空箱", "true" });
|
||
|
||
putdic.Add("ZS-A01-1", new string[] { "hxjA", "A3AGV允许出空箱", "true" });
|
||
putdic.Add("ZS-A02-1", new string[] { "hxjA", "A4AGV允许出空箱", "true" });
|
||
putdic.Add("ZS-A03-1", new string[] { "hxjA", "A5AGV允许出空箱", "true" });
|
||
putdic.Add("ZS-A04-1", new string[] { "hxjA", "A6AGV允许出空箱", "true" });
|
||
putdic.Add("ZS-A05-1", new string[] { "hxjA", "A7AGV允许出空箱", "true" });
|
||
putdic.Add("ZS-A06-1", new string[] { "hxjA", "A8AGV允许出空箱", "true" });
|
||
putdic.Add("ZS-A07-1", new string[] { "hxjA", "A9AGV允许出空箱", "true" });
|
||
putdic.Add("ZS-A08-1", new string[] { "hxjA", "A10AGV允许出空箱", "true" });
|
||
putdic.Add("ZS-A09-1", new string[] { "hxjA", "A11AGV允许出空箱", "true" });
|
||
putdic.Add("ZS-A10-1", new string[] { "hxjA", "A12AGV允许出空箱", "true" });
|
||
putdic.Add("ZS-A11-1", new string[] { "hxjA", "A13AGV允许出空箱", "true" });
|
||
putdic.Add("ZS-A12-1", new string[] { "hxjA", "A14AGV允许出空箱", "true" });
|
||
|
||
if (action == "LOAD")//取货
|
||
{
|
||
if (getdic.Keys.Contains(disTask.startlocation_code))
|
||
{
|
||
var strarr = getdic.Where(p => p.Key == disTask.startlocation_code).First().Value;
|
||
Dictionary<string, string> dicCommand = new(StringComparer.OrdinalIgnoreCase)
|
||
{
|
||
["DevName"] = strarr[0],
|
||
["token"] = _eleCtlCfg.token,
|
||
["TagName"] = strarr[1],
|
||
["Value"] = strarr[2],
|
||
};
|
||
Logger.Information($"SsxControlLOAD:{JsonConvert.SerializeObject(dicCommand)}");
|
||
var str = await HttpClientHelper.GetRequestAsync(_eleCtlCfg.WriteTagUrl, dicCommand);
|
||
Logger.Information($"SsxControlLOAD:{str}");
|
||
}
|
||
}
|
||
else if (action == "UNLOAD")//放货
|
||
{
|
||
if (putdic.Keys.Contains(disTask.endlocation_code))
|
||
{
|
||
var strarr = putdic.Where(p => p.Key == disTask.endlocation_code).First().Value;
|
||
Dictionary<string, string> dicCommand = new(StringComparer.OrdinalIgnoreCase)
|
||
{
|
||
["DevName"] = strarr[0],
|
||
["token"] = _eleCtlCfg.token,
|
||
["TagName"] = strarr[1],
|
||
["Value"] = strarr[2],
|
||
};
|
||
Logger.Information($"SsxControlUNLOAD:{JsonConvert.SerializeObject(dicCommand)}");
|
||
var str = await HttpClientHelper.GetRequestAsync(_eleCtlCfg.WriteTagUrl, dicCommand);
|
||
Logger.Information($"SsxControlUNLOAD:{str}");
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 二楼机械臂
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public async Task Floor2MechanicalConfirm(WmsDistaskH disTask, string action)
|
||
{
|
||
List<string> rackAreaPointsUp = new List<string>();
|
||
List<string> rackAreaPointsDown = new List<string>();
|
||
// 二楼上升降区料架区点位
|
||
rackAreaPointsUp.Add("AS01");
|
||
rackAreaPointsUp.Add("AS02");
|
||
// 二楼下升降区料架区点位
|
||
rackAreaPointsDown.Add("AX01");
|
||
rackAreaPointsDown.Add("AX02");
|
||
|
||
try {
|
||
List<BasLocation> basLocations = _db.Queryable<BasLocation>().Where(r => (r.location_code == disTask.endlocation_code || r.location_code == disTask.startlocation_code)
|
||
&& r.wh_id != "33780009364245").ToList();
|
||
|
||
// 暂存仓内任务
|
||
if (disTask.area_code == "E" && basLocations.Count() == 0)
|
||
{
|
||
await _db.Ado.BeginTranAsync();
|
||
Logger.Information($"【二楼机械臂Floor2MechanicalComplete】收到到货确认信号 传入参数: {disTask.bill_code} {action}");
|
||
|
||
if (action == "UNLOAD")
|
||
{
|
||
// 去料架区放货
|
||
if (rackAreaPointsUp.Contains(disTask.endlocation_code) || rackAreaPointsDown.Contains(disTask.endlocation_code))
|
||
{
|
||
ISugarQueryable<WmsMechanicalArmH> WmsMechanicalArmHs = _db.Queryable<WmsMechanicalArmH>().Where(r => r.location_code == disTask.endlocation_code);
|
||
if (WmsMechanicalArmHs.Count() == 0)
|
||
{
|
||
Logger.Information($"【二楼机械臂Floor2MechanicalComplete】 任务执行终点{disTask.endpoint_code} 与料架区的点位不匹配");
|
||
throw new Exception($"【二楼机械臂Floor2MechanicalComplete】 任务执行终点{disTask.endpoint_code} 与料架区的点位不匹配");
|
||
}
|
||
WmsMechanicalArmH target = WmsMechanicalArmHs.First();
|
||
|
||
// 回写料架和AGV确认
|
||
await _db.Updateable<WmsMechanicalArmH>().SetColumns(r => new WmsMechanicalArmH
|
||
{
|
||
agvconfirm = 1,
|
||
rackid = disTask.carry_id,
|
||
rackcode = disTask.carry_code
|
||
}).Where(r => r.id == target.id).ExecuteCommandAsync();
|
||
Logger.Information($"【二楼机械臂Floor2MechanicalComplete】{disTask.bill_code} AGV已到货 {disTask.endpoint_code} 更新缓存表{target.id}");
|
||
|
||
if (rackAreaPointsDown.Contains(disTask.endlocation_code))
|
||
{
|
||
int LXCount = _db.Queryable<WmsCarryD>().Where(a => a.carry_id == disTask.carry_id).Count();
|
||
|
||
// 绑定料架
|
||
await _db.Updateable<WmsMechanicalArmH>().SetColumns(r => new WmsMechanicalArmH
|
||
{
|
||
mechanicalconfirm = 1,
|
||
maxnum = LXCount
|
||
}).Where(r => r.id == target.id).ExecuteCommandAsync();
|
||
|
||
Logger.LogInformation($@"【送满托到下升降区】 料架{target.rackid}下的料箱开始生成预任务");
|
||
bool pretask_result = await Floor2EmptyCarryCreateZZCPretask(disTask.carry_id);
|
||
if (pretask_result)
|
||
{
|
||
Logger.LogInformation($@"【送满托到下升降区】 料架{target.rackid}下的料箱生成预任务完成");
|
||
}
|
||
else
|
||
{
|
||
Logger.LogInformation($@"【送满托到下升降区】 料架{target.rackid}下的料箱生成预任务失败");
|
||
throw new Exception($@"【送满托到下升降区】 料架{target.rackid}下的料箱生成预任务失败");
|
||
}
|
||
}
|
||
}
|
||
else // 去暂存仓放货
|
||
{
|
||
// 回写料架料箱绑定表的库位
|
||
ISugarQueryable<WmsCarryCode> WmsCarryCodes = _db.Queryable<WmsCarryCode>()
|
||
.InnerJoin<WmsCarryD>((a, b) => b.membercarry_id == a.carry_id)
|
||
.Where((a, b) => b.carry_id == disTask.carry_id);
|
||
var WmsCarryCodeList = WmsCarryCodes.ToList();
|
||
WmsCarryCodeList.ForEach(r =>
|
||
{
|
||
r.location_id = disTask.endlocation_id;
|
||
r.location_code = disTask.endlocation_code;
|
||
});
|
||
await _db.Updateable(WmsCarryCodeList).ExecuteCommandAsync();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 去暂存仓取货
|
||
if (rackAreaPointsUp.Contains(disTask.endlocation_code) || rackAreaPointsDown.Contains(disTask.endlocation_code))
|
||
{
|
||
|
||
Logger.Information($"【二楼机械臂Floor2MechanicalComplete】{disTask.bill_code} AGV在暂存仓取货确认完成");
|
||
}
|
||
else // 去料架区取货
|
||
{
|
||
ISugarQueryable<WmsMechanicalArmH> WmsMechanicalArmHs = _db.Queryable<WmsMechanicalArmH>().Where(r => r.location_code == disTask.startlocation_code);
|
||
if (WmsMechanicalArmHs.Count() == 0)
|
||
{
|
||
Logger.Information($"【二楼机械臂Floor2MechanicalComplete】 任务执行终点{disTask.endpoint_code} 与料架区的点位不匹配");
|
||
throw new Exception($"【二楼机械臂Floor2MechanicalComplete】 任务执行终点{disTask.endpoint_code} 与料架区的点位不匹配");
|
||
}
|
||
WmsMechanicalArmH target = WmsMechanicalArmHs.First();
|
||
|
||
if (target.note == "上升降机")
|
||
{
|
||
bool result = await Floor2UpDownMachinecode_SetTag($"上升降机满托{target.stackingposition}移走", "true");
|
||
Logger.LogInformation($@"【上升降机】设定升上升降机满托{target.stackingposition}移走 结果为 {result}");
|
||
if (!result)
|
||
{
|
||
throw new Exception($@"【上升降机】设定升上升降机满托{target.stackingposition}移走 结果为 {result}");
|
||
}
|
||
|
||
// 更新料架在二楼配送的目标工位
|
||
// todo erp工位字段取数位置未确定
|
||
WmsMaterialTransferD wmsMaterialTransferD = await _db.Queryable<WmsMaterialTransfer>()
|
||
.LeftJoin<WmsMaterialTransferD>((a, b) => a.id == b.bill_id).Where((a, b) => a.bill_code == target.outbill)
|
||
.Select((a, b) => b).FirstAsync();
|
||
if (wmsMaterialTransferD == null)
|
||
{
|
||
Logger.LogWarning($@"【上升降机】 发生异常!,转库单{target.outbill}没有载具明细");
|
||
// 转库单删除 不执行后续,不返回失败是为了不卡流程
|
||
return;
|
||
}
|
||
|
||
string targetWorkstation = wmsMaterialTransferD.station_code;
|
||
if (string.IsNullOrEmpty(wmsMaterialTransferD.station_code))
|
||
{
|
||
throw new Exception($@"【上升降机】 发生异常!,转库单{target.outbill}的工位为空");
|
||
}
|
||
|
||
Logger.LogInformation($@"【上升降机】更新料架 {target.rackcode} 在二楼配送的目标工位 {targetWorkstation}");
|
||
await _db.Updateable<WmsCarryH>().SetColumns(r => new WmsCarryH
|
||
{
|
||
work_station = targetWorkstation
|
||
}).Where(r => r.id == target.rackid).ExecuteCommandAsync();
|
||
}
|
||
else if (target.note == "下升降机")
|
||
{
|
||
bool result = await Floor2UpDownMachinecode_SetTag($"下升降机空托{target.stackingposition}移走", "true");
|
||
Logger.LogInformation($@"【上升降机】设定升下升降机空托{target.stackingposition}移走 结果为 {result}");
|
||
if (!result)
|
||
{
|
||
throw new Exception($@"【上升降机】设定升下升降机空托{target.stackingposition}移走 结果为 {result}");
|
||
}
|
||
}
|
||
|
||
// 重置料架区
|
||
await _db.Updateable<WmsMechanicalArmH>().SetColumns(r => new WmsMechanicalArmH
|
||
{
|
||
stackingcount = 0,
|
||
barcodes = "",
|
||
outbill = "",
|
||
maxnum = 0,
|
||
iscreatepretask = 0,
|
||
rackcode = "",
|
||
rackid = "",
|
||
agvconfirm = 0,
|
||
mechanicalconfirm = 0
|
||
}).Where(r => r.id == target.id).ExecuteCommandAsync();
|
||
Logger.Information($"【二楼机械臂Floor2MechanicalComplete】{disTask.bill_code} AGV在料架区取货完成");
|
||
}
|
||
}
|
||
await _db.Ado.CommitTranAsync();
|
||
}
|
||
}
|
||
catch(Exception ex)
|
||
{
|
||
Logger.LogError("【Floor2MechanicalComplete】" + ex.ToString());
|
||
await _db.Ado.RollbackTranAsync();
|
||
throw;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// 二楼机械臂
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public async Task Floor2MechanicalComplete(WmsDistaskH disTask, string action)
|
||
{
|
||
List<string> rackAreaPointsUp = new List<string>();
|
||
List<string> rackAreaPointsDown = new List<string>();
|
||
// 二楼上升降区料架区点位
|
||
rackAreaPointsUp.Add("AS01");
|
||
rackAreaPointsUp.Add("AS02");
|
||
// 二楼下升降区料架区点位
|
||
rackAreaPointsDown.Add("AX01");
|
||
rackAreaPointsDown.Add("AX02");
|
||
|
||
try
|
||
{
|
||
List<BasLocation> basLocations = _db.Queryable<BasLocation>().Where(r => (r.location_code == disTask.endlocation_code || r.location_code == disTask.startlocation_code)
|
||
&& r.wh_id != "33780009364245").ToList();
|
||
|
||
// 暂存仓内任务
|
||
if (disTask.area_code == "E" && basLocations.Count() == 0)
|
||
{
|
||
Logger.Information($"【二楼机械臂Floor2MechanicalComplete】收到到货完成信号 传入参数: {disTask.bill_code} {action}");
|
||
|
||
if (action == "UNLOAD")
|
||
{
|
||
// 去料架区放货
|
||
if (rackAreaPointsUp.Contains(disTask.endlocation_code) || rackAreaPointsDown.Contains(disTask.endlocation_code))
|
||
{
|
||
ISugarQueryable<WmsMechanicalArmH> WmsMechanicalArmHs = _db.Queryable<WmsMechanicalArmH>().Where(r => r.location_code == disTask.endlocation_code);
|
||
if (WmsMechanicalArmHs.Count() == 0)
|
||
{
|
||
Logger.Information($"【二楼机械臂Floor2MechanicalComplete】 任务执行终点{disTask.endpoint_code} 与料架区的点位不匹配");
|
||
throw new Exception($"【二楼机械臂Floor2MechanicalComplete】 任务执行终点{disTask.endpoint_code} 与料架区的点位不匹配");
|
||
}
|
||
WmsMechanicalArmH target = WmsMechanicalArmHs.First();
|
||
|
||
// 下升降机写满托数量和送到信号
|
||
if (rackAreaPointsDown.Contains(disTask.endlocation_code))
|
||
{
|
||
int LXCount = _db.Queryable<WmsCarryD>().Where(a => a.carry_id == disTask.carry_id).Count();
|
||
|
||
bool result = await Floor2UpDownMachinecode_SetTag($"下升降机满托{target.stackingposition}数量", LXCount.ToString());
|
||
Logger.LogInformation($@"【送满托到下升降区】设定下升降机满托{target.stackingposition}满托数量为 {LXCount} 结果为 {result}");
|
||
if (!result)
|
||
{
|
||
throw new Exception($"下升降机满托{target.stackingposition}数量 写入失败");
|
||
}
|
||
|
||
// 尝试写入满托送到信号
|
||
bool result下升降机空托送到 = await Floor2UpDownMachinecode_SetTag($"下升降机满托{target.stackingposition}送到", "true");
|
||
Logger.LogInformation($@"【送满托到下升降区】回写 下升降机满托{target.stackingposition}送到 结果为{result下升降机空托送到}");
|
||
if (!result下升降机空托送到)
|
||
{
|
||
throw new Exception($"下升降机满托{target.stackingposition}送到 写入失败");
|
||
}
|
||
}
|
||
}
|
||
else // 去暂存仓放货
|
||
{
|
||
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 去暂存仓取货
|
||
if (rackAreaPointsUp.Contains(disTask.endlocation_code) || rackAreaPointsDown.Contains(disTask.endlocation_code))
|
||
{
|
||
Logger.Information($"【二楼机械臂Floor2MechanicalComplete】{disTask.bill_code} AGV在暂存仓取货完成");
|
||
}
|
||
else // 去料架区取货
|
||
{
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.LogError("【Floor2MechanicalComplete】" + ex.ToString());
|
||
throw;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
private async Task<bool> Floor2UpDownMachinecode_SetTag(string tag, string value)
|
||
{
|
||
string DevName = "东面提升机输送线";
|
||
Dictionary<string, string> dicCommand = new(StringComparer.OrdinalIgnoreCase)
|
||
{
|
||
["DevName"] = DevName,
|
||
["token"] = _eleCtlCfg.token,
|
||
["TagName"] = tag,
|
||
["Value"] = value,
|
||
};
|
||
string result = await HttpClientHelper.GetRequestAsync(_eleCtlCfg.WriteTagUrl, dicCommand);
|
||
|
||
return result.Contains("Ok");
|
||
|
||
// 测试
|
||
//string DevName = "东面提升机输送线";
|
||
|
||
//JObject valueJson = new JObject();
|
||
//valueJson["Value"] = value;
|
||
|
||
//_redisData.SetHash(DevName, tag, valueJson.ToString());
|
||
//return true;
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 生成任务执行
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
[HttpPost]
|
||
public async Task GenTaskExecute()
|
||
{
|
||
if (s_GenTaskExecute.CurrentCount == 0)
|
||
{
|
||
Logger.Information("【GenTaskExecute】 【测试】 丢弃此执行");
|
||
return;
|
||
}
|
||
await s_GenTaskExecute.WaitAsync();
|
||
Stopwatch sw = Stopwatch.StartNew();
|
||
CancellationTokenSource agvCts = new();
|
||
SqlSugarClient db = _db.CopyNew();
|
||
try
|
||
{
|
||
await CTUTaskExecute();
|
||
|
||
//获取所有未下发的预任务申请
|
||
Logger.Information("【GenTaskExecute】 开始获取未下发的预任务...");
|
||
|
||
ISugarQueryable<WmsPretaskH> sugarQueryable = db.Queryable<WmsPretaskH>()
|
||
.LeftJoin<WmsCarryH>((a, b) => a.carry_id == b.id)
|
||
.InnerJoin<WmsAreaH>((a, b, c) => a.area_id == c.id)
|
||
.InnerJoin<BasLocation>((a, b, c, d) => a.endlocation_id == d.id && d.is_use == "0")
|
||
.Where((a, b) => a.status == WmsWareHouseConst.PRETASK_BILL_STATUS_DXF_ID && !string.IsNullOrWhiteSpace(a.startlocation_id)
|
||
// 载具为空时 不校验载具当前位置是否与预任务起点相同
|
||
&& (string.IsNullOrEmpty(a.carry_id) || (!string.IsNullOrEmpty(a.carry_id) && a.startlocation_id == b.location_id)))
|
||
.OrderBy(a => new { priority = SqlFunc.Desc(a.priority), a.bill_code })
|
||
.Select((a, b, c, d) => new WmsPretaskH
|
||
{
|
||
move_num = c.move_num,
|
||
third_eqp_type = c.third_eqp_type,
|
||
}, true);
|
||
|
||
Logger.Information($"【GenTaskExecute】 获取到{sugarQueryable.Count()}条可执行的预任务...");
|
||
if (sugarQueryable.Count() == 0)
|
||
return;
|
||
|
||
//Logger.Information("【GenTaskExecute】 执行SQL: " + sugarQueryable.ToSqlString());
|
||
|
||
List<WmsPretaskH> preTasks = await sugarQueryable.ToListAsync();
|
||
|
||
|
||
|
||
//List<WmsPretaskH> executedPreTasks = await db.Queryable<WmsPretaskH>().Where(it => it.status != WmsWareHouseConst.PRETASK_BILL_STATUS_DXF_ID && it.status != WmsWareHouseConst.PRETASK_BILL_STATUS_COMPLE_ID).ToListAsync();
|
||
|
||
List<WmsPretaskH> agvElevatorTasks = preTasks
|
||
.Where(it => it.endlocation_code.StartsWith("DT", StringComparison.OrdinalIgnoreCase) &&
|
||
!it.area_code.Contains("ELE", StringComparison.OrdinalIgnoreCase))
|
||
.ToList();
|
||
|
||
|
||
var elePreTasks = preTasks.Where(it => it.area_code.Contains("ELE", StringComparison.OrdinalIgnoreCase)).ToList();
|
||
var normalPreTasks = preTasks.Where(it => it.area_code != "B" && !agvElevatorTasks.Concat(elePreTasks).Select(x => x.endlocation_code).Contains(it.endlocation_code)).ToList();
|
||
|
||
|
||
//Logger.Information("【GenTaskExecute】 电梯预任务elePreTasks:" + JsonConvert.SerializeObject(elePreTasks));
|
||
//Logger.Information("【GenTaskExecute】 AGV/CTU/KIVA预任务normalPreTasks:" + JsonConvert.SerializeObject(normalPreTasks));
|
||
//Logger.Information("【GenTaskExecute】 AGV电梯预任务agvElevatorTasks:" + JsonConvert.SerializeObject(agvElevatorTasks));
|
||
|
||
/* IEnumerable<WmsPretaskH?> firstEleGrp = agvElevatorTasks.GroupBy(g => g.endlocation_code).Select(t => t.OrderBy(o => o.bill_code).FirstOrDefault());
|
||
agvElevatorTasks = firstEleGrp?.ToList() ?? Enumerable.Empty<WmsPretaskH>().ToList()!;
|
||
*/
|
||
|
||
//如果电梯任务,预Agv任务存在相同目标库位,删除Agv任务保证电梯任务先行
|
||
var equalEndLocPreTasks = elePreTasks.Select(x => x.endlocation_code).Intersect(agvElevatorTasks.Select(x => x.endlocation_code));
|
||
if (equalEndLocPreTasks.Any())
|
||
{
|
||
Logger.Information("【GenTaskExecute】 执行:如果电梯任务,预Agv任务存在相同目标库位,删除Agv任务保证电梯任务先行 ");
|
||
agvElevatorTasks = agvElevatorTasks.Where(x => !equalEndLocPreTasks.Contains(x.endlocation_code)).ToList();
|
||
}
|
||
preTasks = normalPreTasks.Concat(agvElevatorTasks).Concat(elePreTasks).ToList();
|
||
|
||
//一楼中储仓CTU
|
||
|
||
List<string> ids = preTasks.Select(x => x.id).Distinct().ToList();
|
||
List<WmsPretaskCode>? preTaskCodes = await db.Queryable<WmsPretaskCode>().Where(it => ids.Contains(it.bill_id)).ToListAsync();
|
||
if (preTasks.Count > 0)
|
||
{
|
||
//根据预任务管理区分组,获取到所有分组后的预任务,遍历每个预任务 是否为任务链,通过管理区ID
|
||
List<IGrouping<string, WmsPretaskH>> preTaskGroups = preTasks.GroupBy(g => g.area_code).ToList();
|
||
List<WmsDistaskH> disTasks = new();
|
||
List<WmsDistaskCode> distaskCodes = new();
|
||
foreach (IGrouping<string, WmsPretaskH>? itGroup in preTaskGroups)
|
||
{
|
||
List<WmsDistaskH> items = itGroup.Adapt<List<WmsDistaskH>>();
|
||
for (int i = 0, cnt = items.Count; i < cnt; i++)
|
||
{
|
||
items[i].id = SnowflakeIdHelper.NextId();
|
||
items[i].create_time = DateTime.Now;
|
||
items[i].status = WmsWareHouseConst.TASK_BILL_STATUS_DZX_ID;
|
||
}
|
||
int moveNum = itGroup.First().move_num;
|
||
int itemsCount = items.Count;
|
||
int mod = itemsCount % moveNum > 0 ? (itemsCount / moveNum) + 1 : itemsCount / moveNum;
|
||
WmsDistaskH[] arrary = items.ToArray();
|
||
//for (int i = 1; i <= mod; i++)
|
||
{
|
||
|
||
if (moveNum >= 1)
|
||
{
|
||
List<WmsPretaskH> areaPreTasks = itGroup.ToList();
|
||
|
||
if (areaPreTasks.Any(x => x.third_eqp_type.ToEnum<EnumTaskChainType>() != EnumTaskChainType.CTU))
|
||
{
|
||
Logger.Information("非CTU任务链生成");
|
||
for (int i = 0; i < items.Count; i++)
|
||
{
|
||
var num = (i + 1);
|
||
var x = items[i];
|
||
string groupCode = _billRullService.GetBillNumber(WmsWareHouseConst.WMS_TASK_EXECUTE_ENCODE).Result;
|
||
x.is_chain = 0;
|
||
x.groups = groupCode;
|
||
x.bill_code = $"{groupCode}-1";
|
||
}
|
||
}
|
||
else if ((moveNum >= areaPreTasks.Count && areaPreTasks.Count > 1) || moveNum <= areaPreTasks.Count)
|
||
{
|
||
Logger.Information("CTU任务链生成");
|
||
|
||
string groupCode = await _billRullService.GetBillNumber(WmsWareHouseConst.WMS_TASK_EXECUTE_ENCODE);
|
||
items.ForEach(x => x.is_chain = 1);
|
||
|
||
int start = 0;
|
||
int end = Math.Min(itemsCount, moveNum);
|
||
List<WmsDistaskH[]> arrList = new(mod);
|
||
|
||
while (start < itemsCount)
|
||
{
|
||
WmsDistaskH[] subArray = arrary[start..end];
|
||
arrList.Add(subArray);
|
||
start = end;
|
||
end = Math.Min(end + moveNum, arrary.Length);
|
||
}
|
||
foreach (WmsDistaskH[] arr in arrList)
|
||
{
|
||
for (int j = 1, len = arr.Length; j <= len; j++)
|
||
{
|
||
arr[j - 1].groups = groupCode;
|
||
arr[j - 1].bill_code = $"{groupCode}-{j}";
|
||
}
|
||
}
|
||
}
|
||
Logger.Information($"已生成任务执行编码");
|
||
}
|
||
}
|
||
|
||
if (preTaskCodes?.Count > 0)
|
||
{
|
||
foreach (WmsDistaskH disTask in items)
|
||
{
|
||
List<WmsPretaskCode> curPreTaskCodes = preTaskCodes.FindAll(x => x.bill_id == disTask.pretask_id);
|
||
List<WmsDistaskCode> curDisTaskCodes = curPreTaskCodes.Adapt<List<WmsDistaskCode>>();
|
||
curDisTaskCodes.ForEach(x =>
|
||
{
|
||
x.id = SnowflakeIdHelper.NextId();
|
||
x.bill_id = disTask.id;
|
||
x.create_time = DateTime.Now;
|
||
});
|
||
distaskCodes.AddRange(curDisTaskCodes);
|
||
}
|
||
}
|
||
|
||
disTasks.AddRange(items);
|
||
}
|
||
await db.Ado.BeginTranAsync();
|
||
|
||
int row = await db.Insertable(disTasks).ExecuteCommandAsync();
|
||
Logger.Information("【GenTaskExecute】 插入任务执行表数据: " + JsonConvert.SerializeObject(disTasks));
|
||
if (preTaskCodes?.Count > 0)
|
||
{
|
||
row = await db.Insertable(distaskCodes).ExecuteCommandAsync();
|
||
Logger.Information("【GenTaskExecute】 插入任务执行条码表数据: " + JsonConvert.SerializeObject(disTasks));
|
||
}
|
||
if (row > 0)
|
||
{
|
||
List<string> preTaskIds = preTasks.Select(x => x.id).ToList();
|
||
List<string> preTaskBill_codes = preTasks.Select(x => x.bill_code).ToList();
|
||
row = await db.Updateable<WmsPretaskH>().SetColumns(it => new WmsPretaskH { status = WmsWareHouseConst.PRETASK_BILL_STATUS_YXF_ID }).Where(it => preTaskIds.Contains(it.id)).ExecuteCommandAsync();
|
||
Logger.Information("【GenTaskExecute】 更改这些预任务执行状态为 已下发: " + JsonConvert.SerializeObject(preTaskBill_codes));
|
||
}
|
||
|
||
await db.Ado.CommitTranAsync();
|
||
|
||
Logger.Information("【GenTaskExecute】 预任务执行完成");
|
||
Logger.Information($"【GenTaskExecute】_eleCtlCfg.Environment={_eleCtlCfg.Environment}");
|
||
|
||
|
||
if (string.Equals(_eleCtlCfg.Environment, ElevatorConsts.EnvironmentName, StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
//呼梯操作
|
||
//获取目标库位为电梯库位的任务
|
||
Logger.Information("【GenTaskExecute】操作设备");
|
||
|
||
var agvDTTasks = disTasks.Where(it => it.endlocation_code.StartsWith("DT", StringComparison.OrdinalIgnoreCase) &&
|
||
!it.area_code.Contains("ELE", StringComparison.OrdinalIgnoreCase)).ToList();
|
||
|
||
foreach (var task in agvDTTasks)
|
||
{
|
||
ElevagorInfoQuery q = new() { endlocation_id = task.endlocation_id, taskCode = task.bill_code };
|
||
Logger.Information($"【GenTaskExecute】呼梯时 根据任务单号获取电梯参数 {JsonConvert.SerializeObject(q)}");
|
||
var e = await FindElevatorFromPars(q);
|
||
Logger.Information($"【GenTaskExecute】呼梯时 根据任务单号获取电梯结果 {JsonConvert.SerializeObject(e)}");
|
||
|
||
var tags = _eleCtlCfg.tags;
|
||
(int sysStatus, int runStatus, int floorNo, int doorStatus, int agvStatus) = await _elevatorControlService.GetElevatorStatus(e.elevator_code, tags, CancellationToken.None);
|
||
Logger.Information($"【GenTaskExecute】 电梯当前状态->系统状态:{sysStatus.ToEnum<EnumSysStatus>()},运行状态:{runStatus.ToEnum<EnumRunStatus>()},Agv状态:{agvStatus.ToEnum<EnumAgvStatus>()},当前楼层:{floorNo},电梯占用状态{s_eleUseStatusDic[e.device_id]}");
|
||
var curFloor = await GetRealFloor(e.end_floor);
|
||
|
||
if (e != null && s_eleUseStatusDic[e.device_id] == (int)EnumElevatorUseStatus.空闲 && curFloor != floorNo)
|
||
{
|
||
task.device_id = e.device_id;
|
||
}
|
||
}
|
||
|
||
Logger.Information($"【GenTaskExecute】呼梯任务数:{agvDTTasks.Count}");
|
||
List<(string endlocation_code, string device_id, string id, int start_floor)> endLocCodes = agvDTTasks.Where(r => !string.IsNullOrEmpty(r.device_id))
|
||
.Select(it => (it.endlocation_code, it.device_id, it.id, it.start_floor)).ToList();
|
||
var callLiftCnt = endLocCodes?.Count ?? 0;
|
||
Logger.Information($"【GenTaskExecute】实际可呼梯任务数:{callLiftCnt}");
|
||
|
||
if (endLocCodes?.Count > 0)
|
||
{
|
||
if (endLocCodes.Select(x => x.device_id).All(x => !x.IsNullOrWhiteSpace()))
|
||
{
|
||
Logger.Information("【GenTaskExecute】呼梯操作");
|
||
_ = CallingLanding(endLocCodes);
|
||
}
|
||
else
|
||
{
|
||
Logger.Error("【GenTaskExecute】呼梯失败,没有设备ID");
|
||
return;
|
||
}
|
||
}
|
||
|
||
//执行电梯任务
|
||
List<WmsDistaskH>? elevatorTasks = disTasks.Where(it => it.area_code.Contains("ELE", StringComparison.OrdinalIgnoreCase)).ToList();
|
||
|
||
Logger.Information($"【GenTaskExecute】当前电梯任务数:{elevatorTasks?.Count ?? 0}");
|
||
if (elevatorTasks?.Count > 0)
|
||
{
|
||
foreach (WmsDistaskH? elevatorTask in elevatorTasks)
|
||
{
|
||
ElevagorInfoQuery q = new() { endlocation_id = elevatorTask.endlocation_id, taskCode = elevatorTask.bill_code};
|
||
Logger.Information($"【GenTaskExecute】执行电梯任务时 根据任务单号获取电梯参数 {JsonConvert.SerializeObject(q)}");
|
||
var e = await FindElevatorFromPars(q);
|
||
Logger.Information($"【GenTaskExecute】执行电梯任务时 根据任务单号获取电梯结果 {JsonConvert.SerializeObject(e)}");
|
||
if (e != null)
|
||
{
|
||
elevatorTask.device_id = e.device_id;
|
||
}
|
||
|
||
_ = ExecuteTargetFloorTask(elevatorTask);
|
||
}
|
||
}
|
||
List<WmsDistaskH> agvTasks = disTasks.Where(it => !it.area_code.Contains("ELE", StringComparison.OrdinalIgnoreCase)).ToList();
|
||
|
||
if (agvTasks?.Count > 0)
|
||
{
|
||
Logger.Information($"【GenTaskExecute】Agv任务数量:{agvTasks.Count},taskCodes:{string.Join(",", agvTasks.Select(x => x.bill_code).Distinct())}");
|
||
_ = AgvDispatch(agvTasks, agvCts.Token);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
catch (Exception ex) when (ex is HttpRequestException hReqEx)
|
||
{
|
||
agvCts.Cancel();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Error("【GenTaskExecute】任务执行时出现错误", ex);
|
||
Logger.Error(ex.StackTrace!);
|
||
await db.Ado.RollbackTranAsync();
|
||
throw;
|
||
}
|
||
finally
|
||
{
|
||
_ = s_GenTaskExecute.Release();
|
||
agvCts.Dispose();
|
||
sw.Stop();
|
||
Logger.Information($"【GenTaskExecute】 任务执行耗时{sw.ElapsedMilliseconds}毫秒");
|
||
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 获取电梯根据任务单号
|
||
/// </summary>
|
||
/// <param name="input">
|
||
/// taskCode:子任务编号
|
||
/// endlocation_id:目标库位ID
|
||
/// </param>
|
||
/// <returns></returns>
|
||
|
||
public async Task<WmsElevatorH> FindElevatorFromPars(ElevagorInfoQuery input)
|
||
{
|
||
var whereExpable = Expressionable.Create<WmsElevatorH, WmsElevatorD, WmsDistaskH>()
|
||
.And((a, b, c) => a.enabled == 1);
|
||
|
||
Logger.Information($"【FindElevatorFromPars】 {JsonConvert.SerializeObject(input)}");
|
||
|
||
if (!input.taskCode.IsNullOrEmpty())
|
||
{
|
||
whereExpable.AndIF(!SqlFunc.IsNullOrEmpty(input.taskCode), (a, b, c) => c.bill_code == input.taskCode);
|
||
}
|
||
if (!input.endlocation_id.IsNullOrEmpty())
|
||
{
|
||
whereExpable.AndIF(!SqlFunc.IsNullOrEmpty(input.endlocation_id), (a, b, c) => b.location_id == input.endlocation_id);
|
||
}
|
||
if (!input.startlocation_id.IsNullOrEmpty())
|
||
{
|
||
whereExpable.AndIF(!SqlFunc.IsNullOrEmpty(input.startlocation_id), (a, b, c) => b.location_id == input.startlocation_id);
|
||
}
|
||
|
||
ISugarQueryable<WmsElevatorH> queryable = _db.CopyNew().Queryable<WmsElevatorH>().InnerJoin<WmsElevatorD>((a, b) => a.id == b.bill_id)
|
||
.InnerJoin<WmsDistaskH>((a, b, c) => b.location_code == c.endlocation_code || b.location_code == c.startlocation_code)
|
||
.Where(whereExpable.ToExpression())
|
||
.WhereIF(!SqlFunc.IsNullOrEmpty(input.sourceName) && SqlFunc.StartsWith("DT-R", input.sourceName), (a, b, c) => c.startpoint_code == input.sourceName)
|
||
.WhereIF(!SqlFunc.IsNullOrEmpty(input.sourceName) && SqlFunc.StartsWith("DT-C", input.sourceName), (a, b, c) => c.endpoint_code == input.sourceName)
|
||
.Select((a, b, c) => new WmsElevatorH
|
||
{
|
||
bill_code = c.bill_code,
|
||
device_id = a.elevator_id,
|
||
end_floor = c.end_floor
|
||
}, true);
|
||
|
||
var ele = await queryable.FirstAsync();
|
||
|
||
Logger.Information($"【FindElevatorFromPars】 " + queryable.ToSqlString());
|
||
|
||
return ele;
|
||
|
||
}
|
||
|
||
|
||
#region CTU
|
||
/// <summary>
|
||
/// 生成CTU任务执行
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
[HttpPost]
|
||
public async Task CTUTaskExecute()
|
||
{
|
||
try
|
||
{
|
||
Dictionary<string, string[]> indic = new Dictionary<string, string[]>();
|
||
indic.Add("SSX-021-005", new string[] { "YTCS", "CallCtuFullIn_CS05" });
|
||
indic.Add("SSX-111-011", new string[] { "东面提升机输送线", "下升降机11呼叫CTU" });
|
||
indic.Add("SSX-111-012", new string[] { "东面提升机输送线", "下升降机12呼叫CTU" });
|
||
var db = _db.CopyNew();
|
||
List<WmsPretaskH> CTUTasks = await db.Queryable<WmsPretaskH>()
|
||
.InnerJoin<WmsAreaH>((a, b) => a.area_id == b.id)
|
||
.Where(a => a.status == WmsWareHouseConst.PRETASK_BILL_STATUS_DXF_ID)
|
||
.Where((a, b) => b.code == "B")//一楼中储仓
|
||
.OrderBy(a => a.create_id)
|
||
.Select((a, b) => new WmsPretaskH
|
||
{
|
||
move_num = b.move_num,
|
||
third_eqp_type = b.third_eqp_type,
|
||
}, true).ToListAsync();
|
||
|
||
if (CTUTasks.Count == 0)
|
||
return;
|
||
|
||
List<WmsPretaskCode> TaskCodes = await db.Queryable<WmsPretaskCode>().Where(it => CTUTasks.Select(p => p.id).ToList().Contains(it.bill_id)).ToListAsync();
|
||
|
||
//Logger.Information($@"【CTUTaskExecute】 获取任务TaskCodes: {JsonConvert.SerializeObject(TaskCodes)}");
|
||
|
||
var InTasks = CTUTasks.Where(a => a.task_type == WmsWareHouseConst.WMS_PRETASK_INSTOCK_TYPE_ID).ToList();
|
||
var OutTasks = CTUTasks.Where(a => a.task_type == WmsWareHouseConst.WMS_PRETASK_OUTSTOCK_TYPE_ID || a.task_type == WmsWareHouseConst.BIZTYPE_WMSTRANSFER_ID).ToList();
|
||
|
||
//Logger.Information($@"【CTUTaskExecute】 获取任务InTasks: {JsonConvert.SerializeObject(InTasks)}");
|
||
//Logger.Information($@"【CTUTaskExecute】 获取任务OutTasks: {JsonConvert.SerializeObject(OutTasks)}");
|
||
|
||
var OriginDistaskHs = await db.Queryable<WmsDistaskH>()
|
||
.InnerJoin<WmsAreaH>((a, b) => a.area_id == b.id)
|
||
.Where((a, b) => b.code == "B" && a.status == WmsWareHouseConst.TASK_BILL_STATUS_DZX_ID)
|
||
.OrderBy(a => a.bill_code)
|
||
.ToListAsync();
|
||
|
||
|
||
//Logger.Information($@"【CTUTaskExecute】 OriginDistaskHs: {JsonConvert.SerializeObject(OriginDistaskHs)}");
|
||
|
||
|
||
List<WmsDistaskH> DistaskHs = new List<WmsDistaskH>();
|
||
List<WmsDistaskH> UpDistaskHs = new List<WmsDistaskH>();
|
||
List<WmsDistaskCode> DistaskCodes = new List<WmsDistaskCode>();
|
||
var inCtuExec = new List<WmsDistaskH>();
|
||
var outCtuExec = new List<WmsDistaskH>();
|
||
|
||
foreach (var item in InTasks)
|
||
{
|
||
|
||
|
||
/*
|
||
if (indic.Keys.Contains(item.startlocation_code))
|
||
{
|
||
var strs = indic.Where(p => p.Key == item.startlocation_code).First().Value;
|
||
bool flag = _redisData.HashExist(strs[0], strs[1]).Result;
|
||
if (!flag)
|
||
continue;
|
||
string data = _redisData.GetHash(strs[0], strs[1]).Result;
|
||
JObject? res = JsonConvert.DeserializeObject<JObject>(data);
|
||
bool result = res != null && res["Value"] != null ? res.Value<bool>("Value") : false;
|
||
if (!result)
|
||
continue;
|
||
}*/
|
||
|
||
WmsDistaskH distaskH = item.Adapt<WmsDistaskH>();
|
||
distaskH.id = SnowflakeIdHelper.NextId();
|
||
distaskH.status = WmsWareHouseConst.TASK_BILL_STATUS_DZX_ID;
|
||
distaskH.is_chain = 1;
|
||
distaskH.create_time = DateTime.Now;
|
||
var billcode = GetBillCode(OriginDistaskHs, DistaskHs, distaskH);
|
||
distaskH.groups = billcode.Substring(0, billcode.Length - 2);
|
||
distaskH.bill_code = billcode;
|
||
var num = int.Parse(distaskH.bill_code.Substring(billcode.Length - 1, 1));
|
||
Logger.Information($@"【CTUTaskExecute】 比对billcode:{num}和单次搬运数量move_num:{item.move_num},如果一致 会添加到inCtuExec");
|
||
if (num == item.move_num)
|
||
{
|
||
distaskH.status = WmsWareHouseConst.TASK_BILL_STATUS_YXD_ID;
|
||
inCtuExec.Add(distaskH);
|
||
if (OriginDistaskHs.Where(p => p.groups == distaskH.groups).Any())
|
||
{
|
||
OriginDistaskHs.Where(p => p.groups == distaskH.groups).ToList().ForEach(p => p.status = WmsWareHouseConst.TASK_BILL_STATUS_YXD_ID);
|
||
|
||
UpDistaskHs.AddRange(OriginDistaskHs.Where(p => p.groups == distaskH.groups).ToList());
|
||
inCtuExec.AddRange(OriginDistaskHs.Where(p => p.groups == distaskH.groups).ToList());
|
||
}
|
||
if (DistaskHs.Where(p => p.groups == distaskH.groups).Any())
|
||
{
|
||
DistaskHs.Where(p => p.groups == distaskH.groups).ToList().ForEach(p => p.status = WmsWareHouseConst.TASK_BILL_STATUS_YXD_ID);
|
||
inCtuExec.AddRange(DistaskHs.Where(p => p.groups == distaskH.groups).ToList());
|
||
}
|
||
}
|
||
|
||
|
||
List<WmsPretaskCode> preTaskCodes = TaskCodes.FindAll(x => x.bill_id == distaskH.pretask_id);
|
||
List<WmsDistaskCode> disTaskCodes = preTaskCodes.Adapt<List<WmsDistaskCode>>();
|
||
disTaskCodes.ForEach(x =>
|
||
{
|
||
x.id = SnowflakeIdHelper.NextId();
|
||
x.bill_id = distaskH.id;
|
||
x.create_time = DateTime.Now;
|
||
});
|
||
DistaskHs.Add(distaskH);
|
||
DistaskCodes.AddRange(disTaskCodes);
|
||
|
||
}
|
||
foreach (var item in OutTasks)
|
||
{
|
||
WmsDistaskH distaskH = item.Adapt<WmsDistaskH>();
|
||
distaskH.id = SnowflakeIdHelper.NextId();
|
||
distaskH.status = WmsWareHouseConst.TASK_BILL_STATUS_DZX_ID;
|
||
distaskH.is_chain = 1;
|
||
distaskH.create_time = DateTime.Now;
|
||
var billcode = GetBillCode(OriginDistaskHs, DistaskHs, distaskH);
|
||
distaskH.groups = billcode.Substring(0, billcode.Length - 2);
|
||
distaskH.bill_code = billcode;
|
||
var num = int.Parse(distaskH.bill_code.Substring(billcode.Length - 1, 1));
|
||
|
||
Logger.Information($@"【CTUTaskExecute】 比对billcode:{num}和单次搬运数量move_num:{item.move_num},如果一致 会添加到outCtuExec");
|
||
if (num == item.move_num)
|
||
{
|
||
distaskH.status = WmsWareHouseConst.TASK_BILL_STATUS_YXD_ID;
|
||
outCtuExec.Add(distaskH);
|
||
if (OriginDistaskHs.Where(p => p.groups == distaskH.groups).Any())
|
||
{
|
||
OriginDistaskHs.Where(p => p.groups == distaskH.groups).ToList().ForEach(p => p.status = WmsWareHouseConst.TASK_BILL_STATUS_YXD_ID);
|
||
|
||
UpDistaskHs.AddRange(OriginDistaskHs.Where(p => p.groups == distaskH.groups).ToList());
|
||
outCtuExec.AddRange(OriginDistaskHs.Where(p => p.groups == distaskH.groups).ToList());
|
||
}
|
||
if (DistaskHs.Where(p => p.groups == distaskH.groups).Any())
|
||
{
|
||
DistaskHs.Where(p => p.groups == distaskH.groups).ToList().ForEach(p => p.status = WmsWareHouseConst.TASK_BILL_STATUS_YXD_ID);
|
||
outCtuExec.AddRange(DistaskHs.Where(p => p.groups == distaskH.groups).ToList());
|
||
}
|
||
}
|
||
|
||
List<WmsPretaskCode> preTaskCodes = TaskCodes.FindAll(x => x.bill_id == distaskH.pretask_id);
|
||
List<WmsDistaskCode> disTaskCodes = preTaskCodes.Adapt<List<WmsDistaskCode>>();
|
||
disTaskCodes.ForEach(x =>
|
||
{
|
||
x.id = SnowflakeIdHelper.NextId();
|
||
x.bill_id = distaskH.id;
|
||
x.create_time = DateTime.Now;
|
||
});
|
||
DistaskHs.Add(distaskH);
|
||
DistaskCodes.AddRange(disTaskCodes);
|
||
}
|
||
await db.Ado.BeginTranAsync();
|
||
int row = 0;
|
||
if (UpDistaskHs.Count > 0)
|
||
{
|
||
await db.Updateable<WmsDistaskH>().SetColumns(it => new WmsDistaskH { status = WmsWareHouseConst.TASK_BILL_STATUS_YXD_ID }).Where(it => UpDistaskHs.Select(p => p.id).ToList().Contains(it.id)).ExecuteCommandAsync();
|
||
}
|
||
if (DistaskHs.Count > 0)
|
||
{
|
||
row = await db.Insertable(DistaskHs).ExecuteCommandAsync();
|
||
}
|
||
if (DistaskCodes.Count > 0)
|
||
{
|
||
await db.Insertable(DistaskCodes).ExecuteCommandAsync();
|
||
}
|
||
if (row > 0)
|
||
{
|
||
List<string> preTaskIds = DistaskHs.Select(x => x.pretask_id).ToList();
|
||
await db.Updateable<WmsPretaskH>().SetColumns(it => new WmsPretaskH { status = WmsWareHouseConst.PRETASK_BILL_STATUS_YXF_ID }).Where(it => preTaskIds.Contains(it.id)).ExecuteCommandAsync();
|
||
}
|
||
await db.Ado.CommitTranAsync();
|
||
//判断
|
||
Logger.Information($"【CTUTaskExecute】 判断单据状态(status)是否为待执行 {JsonConvert.SerializeObject(DistaskHs)}");
|
||
if (DistaskHs.Where(p => p.status == WmsWareHouseConst.TASK_BILL_STATUS_DZX_ID).Any())
|
||
{
|
||
var time = int.Parse(db.Queryable<BasFactoryConfig>().Where(P => P.key == "getinterval").First().value);
|
||
timer = new Timer(TimerExec, null, TimeSpan.FromMinutes(time), TimeSpan.FromMinutes(time));
|
||
}
|
||
//Logger.Information($@"【CTUTaskExecute】 可执行的CTU任务inCtuExec: {JsonConvert.SerializeObject(inCtuExec)}");
|
||
if (inCtuExec.Count > 0)
|
||
{
|
||
Logger.Information($"开始执行CTU入库任务: {JsonConvert.SerializeObject(inCtuExec)}");
|
||
//呼叫ctu入库
|
||
// await db.Updateable<WmsDistaskH>().SetColumns(it => new WmsDistaskH { status = WmsWareHouseConst.TASK_BILL_STATUS_RUNING_ID }).Where(it => inCtuExec.Select(p => p.id).ToList().Contains(it.id)).ExecuteCommandAsync();
|
||
// await db.Updateable<WmsPretaskH>().SetColumns(it => new WmsPretaskH { status = WmsWareHouseConst.PRETASK_BILL_STATUS_START_ID }).Where(it => inCtuExec.Select(x => x.pretask_id).ToList().Contains(it.id)).ExecuteCommandAsync();
|
||
CancellationTokenSource Ctu = new();
|
||
|
||
await CallingCTU(inCtuExec, Ctu.Token, 1);
|
||
Ctu.Dispose();
|
||
}
|
||
//Logger.Information($@"【CTUTaskExecute】 可执行的CTU任务outCtuExec: {JsonConvert.SerializeObject(outCtuExec)}");
|
||
if (outCtuExec.Count > 0)
|
||
{
|
||
Logger.Information($"开始执行CTU出库任务: {JsonConvert.SerializeObject(outCtuExec)}");
|
||
//呼叫ctu出库
|
||
// await db.Updateable<WmsDistaskH>().SetColumns(it => new WmsDistaskH { status = WmsWareHouseConst.TASK_BILL_STATUS_RUNING_ID }).Where(it => outCtuExec.Select(p => p.id).ToList().Contains(it.id)).ExecuteCommandAsync();
|
||
// await db.Updateable<WmsPretaskH>().SetColumns(it => new WmsPretaskH { status = WmsWareHouseConst.PRETASK_BILL_STATUS_START_ID }).Where(it => outCtuExec.Select(x => x.pretask_id).ToList().Contains(it.id)).ExecuteCommandAsync();
|
||
CancellationTokenSource Ctu = new();
|
||
await CallingCTU(outCtuExec, Ctu.Token, 0);
|
||
Ctu.Dispose();
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
throw;
|
||
}
|
||
}
|
||
//判断生成bill_code
|
||
private string GetBillCode(List<WmsDistaskH> OriginDistaskH, List<WmsDistaskH> NewdistaskHs, WmsDistaskH distaskH)
|
||
{
|
||
string BillCode = string.Empty;
|
||
//入库
|
||
if (distaskH.task_type == WmsWareHouseConst.WMS_PRETASK_INSTOCK_TYPE_ID)
|
||
{
|
||
var orgdistaskHs = OriginDistaskH.Where(p => p.status == WmsWareHouseConst.TASK_BILL_STATUS_DZX_ID && p.task_type == WmsWareHouseConst.WMS_PRETASK_INSTOCK_TYPE_ID && p.startpoint_id == distaskH.startpoint_id).ToList();
|
||
var newdistaskHs = NewdistaskHs.Where(p => p.status == WmsWareHouseConst.TASK_BILL_STATUS_DZX_ID && p.task_type == WmsWareHouseConst.WMS_PRETASK_INSTOCK_TYPE_ID && p.startpoint_id == distaskH.startpoint_id).ToList();
|
||
if ((orgdistaskHs.Count + newdistaskHs.Count) == 0)
|
||
{
|
||
var groups = _billRullService.GetBillNumber(WmsWareHouseConst.WMS_TASK_EXECUTE_ENCODE).Result;
|
||
BillCode = $"{groups}-1";
|
||
}
|
||
else
|
||
{
|
||
var groups = orgdistaskHs.Count > 0 ? orgdistaskHs.First().groups : newdistaskHs.First().groups;
|
||
BillCode = $"{groups}-" + (orgdistaskHs.Count + newdistaskHs.Count + 1);
|
||
}
|
||
}
|
||
else if (distaskH.task_type == WmsWareHouseConst.WMS_PRETASK_OUTSTOCK_TYPE_ID || distaskH.task_type == WmsWareHouseConst.BIZTYPE_WMSTRANSFER_ID)
|
||
{
|
||
var orgdistaskHs = OriginDistaskH.Where(p => p.status == WmsWareHouseConst.TASK_BILL_STATUS_DZX_ID && p.task_type == WmsWareHouseConst.WMS_PRETASK_OUTSTOCK_TYPE_ID && p.endpoint_id == distaskH.endpoint_id).ToList();
|
||
var newdistaskHs = NewdistaskHs = OriginDistaskH.Where(p => p.status == WmsWareHouseConst.TASK_BILL_STATUS_DZX_ID && p.task_type == WmsWareHouseConst.WMS_PRETASK_OUTSTOCK_TYPE_ID && p.endpoint_id == distaskH.endpoint_id).ToList();
|
||
if ((orgdistaskHs.Count + newdistaskHs.Count) == 0)
|
||
{
|
||
var groups = _billRullService.GetBillNumber(WmsWareHouseConst.WMS_TASK_EXECUTE_ENCODE).Result;
|
||
BillCode = $"{groups}-1";
|
||
}
|
||
else
|
||
{
|
||
var groups = orgdistaskHs.Count > 0 ? orgdistaskHs.First().groups : newdistaskHs.First().groups;
|
||
BillCode = $"{groups}-" + (orgdistaskHs.Count + newdistaskHs.Count + 1);
|
||
}
|
||
}
|
||
return BillCode;
|
||
}
|
||
private Timer? timer;
|
||
private async void TimerExec(object e)
|
||
{
|
||
try
|
||
{
|
||
SqlSugarClient db = _db.CopyNew();
|
||
var list = db.Queryable<WmsDistaskH>().InnerJoin<WmsAreaH>((a, b) => a.area_id == b.id)
|
||
.Where((a, b) => b.code == "B" && a.status == WmsWareHouseConst.TASK_BILL_STATUS_DZX_ID)
|
||
.OrderBy(a => a.bill_code)
|
||
.ToList();
|
||
var data = list.GroupBy(p => p.groups).Select(p => new
|
||
{
|
||
groups = p.Key,
|
||
time = p.Max(a => a.create_time)
|
||
|
||
}).ToList();
|
||
var date = DateTime.Now;
|
||
var time = int.Parse(db.Queryable<BasFactoryConfig>().Where(P => P.key == "getinterval").First().value);
|
||
foreach (var item in data)
|
||
{
|
||
if (date.Subtract(item.time).Minutes >= time)
|
||
{
|
||
var execlist = list.Where(p => p.groups == item.groups).ToList();
|
||
await db.Updateable<WmsDistaskH>().SetColumns(it => new WmsDistaskH { status = WmsWareHouseConst.TASK_BILL_STATUS_YXD_ID }).Where(it => execlist.Select(p => p.id).ToList().Contains(it.id)).ExecuteCommandAsync();
|
||
// await db.Updateable<WmsPretaskH>().SetColumns(it => new WmsPretaskH { status = WmsWareHouseConst.PRETASK_BILL_STATUS_START_ID }).Where(it => execlist.Select(x => x.pretask_id).ToList().Contains(it.id)).ExecuteCommandAsync();
|
||
CancellationTokenSource Ctu = new();
|
||
int type = execlist.First().task_type == WmsWareHouseConst.WMS_PRETASK_INSTOCK_TYPE_ID ? 1 : 0;
|
||
await CallingCTU(execlist, Ctu.Token, type);
|
||
Ctu.Dispose();
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
}
|
||
finally
|
||
{
|
||
timer?.Dispose();
|
||
}
|
||
|
||
}
|
||
private async Task CallingCTU(List<WmsDistaskH> distaskHs, CancellationToken token, int type)
|
||
{
|
||
try
|
||
{
|
||
AgvRequestConfig requestCfg = App.Configuration.Build<AgvRequestConfig>();
|
||
string url = requestCfg.AgvRequestUrls.CreateTaskChainUrl;
|
||
var taskChainCodeDic = distaskHs.Where(t => !t.groups.IsNullOrWhiteSpace()).GroupBy(g => g.groups!)
|
||
.ToDictionary(x => x.Key, x => x.Select(it => new
|
||
{
|
||
taskCode = it.bill_code,
|
||
sourceName = it.startpoint_code,
|
||
targetName = it.endpoint_code,
|
||
containerCode = it.carry_code,
|
||
}));
|
||
foreach ((string k, object v) in taskChainCodeDic)
|
||
{
|
||
dynamic reqBody = new ExpandoObject();
|
||
reqBody.taskChainCode = k;
|
||
reqBody.type = (int)EnumTaskChainType.CTU;
|
||
reqBody.sequential = false;
|
||
reqBody.taskChainPriority = 0;
|
||
reqBody.taskList = v;
|
||
reqBody.inOut = type;
|
||
Logger.Information($"【CallingCTU】 CTU任务下发 开始请求联核/task-chain/create接口 请求地址:{url} 请求参数:{JsonConvert.SerializeObject(reqBody)} type:{(type == 0 ? "CTU出库" : "CTU入库")}");
|
||
dynamic respBody = await HttpClientHelper.PostStreamAsync(url, reqBody, token);
|
||
Logger.Information($"【CallingCTU】 CTU任务下发 接收到联核/task-chain/create接口信息:{respBody}");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Information($"【CallingCTU】 CTU任务下发 请求联核/task-chain/create接口失败 异常信息:{ex}");
|
||
}
|
||
}
|
||
#endregion
|
||
/// <summary>
|
||
/// 呼梯操作
|
||
/// </summary>
|
||
/// <param name="endLocCodes"></param>
|
||
/// <returns></returns>
|
||
private async Task CallingLanding(List<(string endlocation_code, string device_id, string id, int floorNO)> endLocCodes)
|
||
{
|
||
Logger.Information($"【CallingLanding】 开始呼梯操作.............");
|
||
try
|
||
{
|
||
foreach ((_, string devId, string disTaskId, int floorNO) in endLocCodes)
|
||
{
|
||
Logger.Information($"【CallingLanding】 devId:{devId}");
|
||
if (!s_elevatorMap.TryGetValue(devId, out object? elevatorCode))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
string? devName = elevatorCode.ToString();
|
||
Logger.Information($"【CallingLanding】 电梯编号:{devName}");
|
||
Logger.Information($"【CallingLanding】 当前:{devName.Match(@"\d+")}#梯");
|
||
|
||
await _elevatorControlService.WriteTagAsync(devName, ElevatorConsts.AGVControl, 1);
|
||
(int sysStatus, int runStatus, int curFloorNo, int doorStatus, int agvStatus) eleStatusMulti = (-1, -1, -1, -1, -1);
|
||
Logger.Information("【CallingLanding】 获取电梯AGV运行状态");
|
||
if (!_elevatorAgvCtlStatusMap.TryGetValue(devId, out int agvCtlStatus) || agvCtlStatus != (int)EnumAgvStatus.AGV运行状态)
|
||
{
|
||
var tags = new[] { "SysStatus", "RunStatus", "FloorNo", "DoorStatus", "AGVStatus" };
|
||
do
|
||
{
|
||
eleStatusMulti = await _elevatorControlService.GetElevatorStatus(devName, tags, CancellationToken.None);
|
||
await Task.Delay(1000);
|
||
} while (eleStatusMulti.agvStatus != (int)EnumAgvStatus.AGV运行状态);
|
||
Logger.Information($"{devName.Match(@"\d+")}#, 当前Agv状态:{eleStatusMulti.agvStatus.ToEnum<EnumAgvStatus>()}");
|
||
_elevatorAgvCtlStatusMap[devId] = eleStatusMulti.agvStatus;
|
||
}
|
||
else
|
||
{
|
||
Logger.Information("【CallingLanding】 AGV运行状态:" + agvCtlStatus);
|
||
}
|
||
|
||
Logger.Information($"【CallingLanding】 任务开始目标楼层为:{floorNO}");
|
||
|
||
int floorN = await GetRealFloor(floorNO);
|
||
//如果电梯在当前楼层则不呼梯
|
||
if (floorN == eleStatusMulti.curFloorNo)
|
||
{
|
||
Logger.Information($"【CallingLanding】 {devName.Match(@"\d+")}#,在当前楼层,无需呼梯");
|
||
continue;
|
||
}
|
||
|
||
Logger.Information($"【CallingLanding】 实际目标楼层为:{floorN}");
|
||
|
||
WmsElevatorUnexecute elevatorQueueItem = new()
|
||
{
|
||
distask_id = disTaskId,
|
||
elevator_id = devId,
|
||
elevator_code = devName,
|
||
floor = floorN, //5代表4楼
|
||
task_status = "待执行",
|
||
create_id = _userManager.UserId,
|
||
create_time = DateTime.Now
|
||
};
|
||
List<WmsElevatorUnexecute> elevatorQueue = await _db.Queryable<WmsElevatorUnexecute>().Where(it => it.distask_id == disTaskId && it.task_status == "执行中").ToListAsync();
|
||
|
||
Logger.Information($"【CallingLanding】 电梯{devName}状态为{s_eleUseStatusDic[devId]}");
|
||
Logger.Information($"【CallingLanding】 电梯{devName} 任务id{disTaskId} 执行中的队列数量为{elevatorQueue.Count}");
|
||
if ((elevatorQueue.IsNull() || elevatorQueue.Count < 1) && floorN != eleStatusMulti.curFloorNo && s_eleUseStatusDic[devId] == (int)EnumElevatorUseStatus.空闲)
|
||
{
|
||
Logger.Information($"【CallingLanding】 判断当前电梯{devName.Match(@"\d+")}#梯 无任务在做执行呼梯");
|
||
|
||
elevatorQueueItem.task_status = "执行中";
|
||
bool callLiftRes = await _elevatorControlService.CallLift(devName, floorN, CancellationToken.None);
|
||
string successful = "成功", fail = "失败";
|
||
string callLiftResult = callLiftRes ? successful : fail;
|
||
Logger.Information($"{devName.Match(@"\d+")}#, 呼梯结果:{callLiftResult}");
|
||
}
|
||
else
|
||
{
|
||
Logger.Information("【CallingLanding】 当前有任务在做,不会呼梯");
|
||
}
|
||
|
||
//如果当前电梯有任务在做,将当前呼梯任务放入待执行队列
|
||
_ = await _db.Insertable(elevatorQueueItem).ExecuteCommandAsync();
|
||
|
||
Logger.Information("【CallingLanding】 呼梯任务执行完成");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Error("【CallingLanding】 呼梯操作错误", ex);
|
||
Logger.Error($"【CallingLanding】 呼梯操作错误堆栈跟踪:{Environment.NewLine}{ex.StackTrace}");
|
||
throw;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行到目标楼层电梯任务
|
||
/// </summary>
|
||
/// <param name="disTask"></param>
|
||
/// <returns></returns>
|
||
public async Task ExecuteTargetFloorTask(WmsDistaskH disTask)
|
||
{
|
||
//收到放货确认通知,向电梯发送到3楼的指令
|
||
Logger.Information($"开始执行电梯任务,任务ID:{disTask.id}");
|
||
try
|
||
{
|
||
if (!s_elevatorMap.TryGetValue(disTask.device_id, out object? elevatorCode))
|
||
{
|
||
Logger.Information($"开始执行电梯任务,没有取到设备{disTask.device_id}的 elevatorCode! s_elevatorMap: {JsonConvert.SerializeObject(s_elevatorMap)}");
|
||
return;
|
||
}
|
||
string devName = s_elevatorMap[disTask.device_id]?.ToString() ?? _eleCtlCfg.DevName3;
|
||
|
||
Logger.Information($"当前:{devName.Match(@"\d+")}#梯");
|
||
|
||
var tags = new[] { "SysStatus", "RunStatus", "FloorNo", "DoorStatus", "AGVStatus" };
|
||
(int sysStatus, int runStatus, int curFloorNo, int doorStatus, int agvStatus) eleStatusMulti = await _elevatorControlService.GetElevatorStatus(devName, tags, CancellationToken.None);
|
||
if (eleStatusMulti.agvStatus != (int)EnumAgvStatus.AGV运行状态)
|
||
{
|
||
_ = await _elevatorControlService.WriteTagAsync(devName, ElevatorConsts.AGVControl, 1);
|
||
}
|
||
|
||
if (!_elevatorAgvCtlStatusMap.TryGetValue(disTask.id, out int agvCtlStatus) || agvCtlStatus != (int)EnumAgvStatus.AGV运行状态)
|
||
{
|
||
do
|
||
{
|
||
eleStatusMulti = await _elevatorControlService.GetElevatorStatus(devName, tags, CancellationToken.None);
|
||
Logger.Information($"{devName.Match(@"\d+")}#, 当前Agv状态:{eleStatusMulti.agvStatus.ToEnum<EnumAgvStatus>()}");
|
||
await Task.Delay(1000);
|
||
} while (eleStatusMulti.agvStatus != (int)EnumAgvStatus.AGV运行状态 );
|
||
Logger.Information($"{devName.Match(@"\d+")}#, 当前Agv状态:{eleStatusMulti.agvStatus.ToEnum<EnumAgvStatus>()}");
|
||
_elevatorAgvCtlStatusMap[disTask.id] = eleStatusMulti.agvStatus;
|
||
}
|
||
|
||
int doorStatus = -1;
|
||
bool closeDoorRes = await _elevatorControlService.SendOpenCloseCmd(devName, 4); //向电梯发送前门关门指令
|
||
Logger.Information($"关门结果:{closeDoorRes}");
|
||
do
|
||
{
|
||
doorStatus = await _elevatorControlService.GetTagAsync(devName, ElevatorConsts.DoorStatus);
|
||
await Task.Delay(1000);
|
||
} while (doorStatus != 4);
|
||
Logger.Information($"当前门状态:{doorStatus}");
|
||
|
||
int floor = await GetRealFloor(disTask.end_floor);
|
||
|
||
//电梯任务手动执行任务状态上报
|
||
(int sysStatus, int runStatus, int floorNo, int doorStatus, int agvStatus) tuple = (-1, -1, -1, -1, -1);
|
||
tuple = await _elevatorControlService.GetElevatorStatus(devName, tags, CancellationToken.None);
|
||
Logger.Information($"目标楼层:{floor} 当前楼层:{tuple.floorNo}");
|
||
|
||
Logger.Information($"开始呼梯 {devName}到{floor}");
|
||
//发送到目标楼的指令
|
||
dynamic result = await _elevatorControlService.WriteTagAsync(devName, ElevatorConsts.FloorExecute, floor);
|
||
|
||
do
|
||
{
|
||
tuple = await _elevatorControlService.GetElevatorStatus(devName, tags, CancellationToken.None);
|
||
await Task.Delay(1000);
|
||
} while (tuple.sysStatus != 3 || tuple.runStatus != 0 || floor != tuple.floorNo);
|
||
|
||
Logger.Information($"sysStatus:{tuple.sysStatus},runStatus:{tuple.runStatus},当前楼层floorNo:{tuple.floorNo},目标楼层disTask.end_floor={floor}");
|
||
|
||
if (tuple.sysStatus.ToEnum<EnumSysStatus>() == EnumSysStatus.正常状态 && tuple.runStatus.ToEnum<EnumRunStatus>() == EnumRunStatus.停梯)
|
||
{
|
||
Logger.Information($"disTask.require_id={disTask.require_id}");
|
||
List<string> disTaskIds = new() { disTask.id };
|
||
var upInput = new { disTaskIds = disTask.id };
|
||
TaskExecuteAfterUpInput teaUpInput = new()
|
||
{
|
||
disTaskIds = disTaskIds,
|
||
};
|
||
await TaskExecuteAfter(teaUpInput);
|
||
TaskCompleUpInput tcUpInput = new()
|
||
{
|
||
disTaskIds = disTaskIds,
|
||
};
|
||
await TaskComplate(tcUpInput);
|
||
|
||
Logger.Information("电梯任务执行完成");
|
||
}
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Error("执行到目标楼层电梯任务失败", ex);
|
||
throw;
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// Agv调度
|
||
/// </summary>
|
||
/// <param name="disTasks"></param>
|
||
/// <param name="token"></param>
|
||
/// <returns></returns>
|
||
private async Task AgvDispatch(List<WmsDistaskH> disTasks, CancellationToken token)
|
||
{
|
||
Logger.Information("【AgvDispatch】 Agv任务执行....");
|
||
List<string> kiva = new List<string>();
|
||
// 一楼中储仓
|
||
kiva.Add("ZSSSXCTU02");
|
||
kiva.Add("ZSSSXCTU01");
|
||
List<string> floor2 = new List<string>();
|
||
// 二楼暂存仓
|
||
//floor2.Add("AS01");
|
||
//floor2.Add("AS02");
|
||
//floor2.Add("AX01");
|
||
//floor2.Add("AX02");
|
||
//调用AGV创建任务链接口
|
||
try
|
||
{
|
||
AgvRequestConfig requestCfg = App.Configuration.Build<AgvRequestConfig>();
|
||
string url = requestCfg.AgvRequestUrls.CreateTaskChainUrl;
|
||
|
||
Logger.Information($"【AgvDispatch】 Agv任务执行的disTasks:{JsonConvert.SerializeObject(disTasks)}");
|
||
|
||
var taskChainCodeDic = disTasks.Where(t => !t.groups.IsNullOrWhiteSpace()).GroupBy(g => g.groups!)
|
||
.ToDictionary(x => x.Key, x => x.Select(it => new
|
||
{
|
||
taskCode = it.bill_code,
|
||
sourceName = it.startpoint_code,
|
||
targetName = it.endpoint_code,
|
||
containerCode = it.carry_code,
|
||
shelfNumber = it.area_code == "E" ? it.carry_code : "" // 二楼暂存仓传料架
|
||
}));
|
||
|
||
Logger.Information($"【AgvDispatch】 Agv任务执行的taskChainCodeDic:{JsonConvert.SerializeObject(taskChainCodeDic)}");
|
||
|
||
foreach ((string k, object v) in taskChainCodeDic)
|
||
{
|
||
var typeflag = false;
|
||
var dis = disTasks.Where(p => p.groups == k).First();
|
||
if (kiva.Contains(dis.startlocation_code) || kiva.Contains(dis.endlocation_code)
|
||
//|| floor2.Contains(dis.startlocation_code) || floor2.Contains(dis.endlocation_code)
|
||
)
|
||
{
|
||
typeflag = true;
|
||
}
|
||
|
||
// 如果管理区是E(二楼暂存仓和二楼缓存仓) 统一发KIVA
|
||
if (dis.area_code == "E")
|
||
{
|
||
typeflag = true;
|
||
}
|
||
|
||
dynamic reqBody = new ExpandoObject();
|
||
reqBody.taskChainCode = k;
|
||
reqBody.type = typeflag ? (int)EnumTaskChainType.KIVA : (int)EnumTaskChainType.AGV;
|
||
reqBody.sequential = false;
|
||
reqBody.taskChainPriority = 0;
|
||
reqBody.taskList = v;
|
||
reqBody.floor = dis.end_floor;
|
||
Logger.Information($"【AgvDispatch】 Agv任务执行 开始请求联核/task-chain/create接口 请求地址:{url} 请求参数:{JsonConvert.SerializeObject(reqBody)} ");
|
||
dynamic respBody = await HttpClientHelper.PostStreamAsync(url, reqBody, token);
|
||
Logger.Information($"【AgvDispatch】 Agv任务执行 接收到联核/task-chain/create接口信息:{respBody}");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Information($"【AgvDispatch】 agv任务执行 请求联核/task-chain/create接口失败 异常信息:{ex}");
|
||
throw;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// 任务执行
|
||
/// </summary>
|
||
/// <param name="input"></param>
|
||
/// <returns></returns>
|
||
[HttpPost]
|
||
public async Task TaskExecute(TaskExecuteUpInput input)
|
||
{
|
||
try
|
||
{
|
||
await _db.Ado.BeginTranAsync();
|
||
|
||
//更任务执行
|
||
for (int i = 0, cnt = input.disTaskIds.Count; i < cnt; i++)
|
||
{
|
||
_ = input.EqpIds?.Count > 0
|
||
? await _db.Updateable<WmsDistaskH>().SetColumns(it => new WmsDistaskH { status = WmsWareHouseConst.TASK_BILL_STATUS_YXD_ID, device_id = input.EqpIds[i] }).Where(it => input.disTaskIds.Contains(it.id)).ExecuteCommandAsync()
|
||
: await _db.Updateable<WmsDistaskH>().SetColumns(it => new WmsDistaskH { status = WmsWareHouseConst.TASK_BILL_STATUS_YXD_ID }).Where(it => input.disTaskIds.Contains(it.id)).ExecuteCommandAsync();
|
||
//await _db.Updateable<WmsDistaskH>().SetColumns(it => setColVal).Where(it => input.disTaskIds.Contains(it.id)).ExecuteCommandAsync();
|
||
}
|
||
List<string> preTaskIds = await _db.Queryable<WmsDistaskH>().Where(it => input.disTaskIds.Contains(it.id)).Select(it => it.pretask_id).ToListAsync();
|
||
if (preTaskIds.Count > 0)
|
||
{
|
||
//更预任务申请表状态
|
||
_ = await _db.Updateable<WmsPretaskH>().SetColumns(it => new WmsPretaskH { status = WmsWareHouseConst.PRETASK_BILL_STATUS_START_ID }).Where(it => preTaskIds.Contains(it.id)).ExecuteCommandAsync();
|
||
}
|
||
|
||
await _db.Ado.CommitTranAsync();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Error("任务执行失败", ex);
|
||
await _db.Ado.RollbackTranAsync();
|
||
throw;
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 任务执行取操作返回(后续操作)
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
[HttpPost]
|
||
public async Task TaskExecuteAfter(TaskExecuteAfterUpInput input)
|
||
{
|
||
//更新任务执行表单据状态
|
||
try
|
||
{
|
||
await _db.Ado.BeginTranAsync();
|
||
|
||
_ = await _db.Updateable<WmsDistaskH>().SetColumns(it => new WmsDistaskH { status = WmsWareHouseConst.TASK_BILL_STATUS_RUNING_ID, act_start_date = DateTime.Now }).Where(it => input.disTaskIds.Contains(it.id)).ExecuteCommandAsync();
|
||
//清空载具库位数据
|
||
var carryAndLocIds = await _db.Queryable<WmsDistaskH>().Where(it => input.disTaskIds.Contains(it.id)).Select(it => new { it.carry_id, it.startlocation_id }).ToListAsync();
|
||
if (carryAndLocIds?.Count > 0)
|
||
{
|
||
List<string> carryIds = carryAndLocIds.Select(x => x.carry_id).ToList();
|
||
_ = await _db.Updateable<WmsCarryH>().SetColumns(it => new WmsCarryH { location_id = null, location_code = null }).Where(it => carryIds.Contains(it.id)).ExecuteCommandAsync();
|
||
}
|
||
//更新起始库位,状态改为空闲、锁定状态,未锁定
|
||
if (carryAndLocIds?.Count > 0)
|
||
{
|
||
List<string> startLocationIds = carryAndLocIds.Select(x => x.startlocation_id).ToList();
|
||
_ = await _db.Updateable<BasLocation>().SetColumns(it => new BasLocation { is_use = ((int)EnumCarryStatus.空闲).ToString(), is_lock = 0 }).Where(it => startLocationIds.Contains(it.id)).ExecuteCommandAsync();
|
||
}
|
||
|
||
await _db.Ado.CommitTranAsync();
|
||
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Error("设备取返回操作失败", ex);
|
||
await _db.Ado.RollbackTranAsync();
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 任务完成
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
[HttpPost]
|
||
public async Task TaskComplate(TaskCompleUpInput input)
|
||
{
|
||
try
|
||
{
|
||
Logger.Information($"【TaskComplate】 开始执行TaskComplate");
|
||
await _db.Ado.BeginTranAsync();
|
||
|
||
//更新任务执行表,单据状态为 完成
|
||
_ = await _db.Updateable<WmsDistaskH>().SetColumns(it => new WmsDistaskH { status = WmsWareHouseConst.TASK_BILL_STATUS_COMPLE_ID, act_end_date = DateTime.Now }).Where(it => input.disTaskIds.Contains(it.id)).ExecuteCommandAsync();
|
||
List<WmsDistaskH> disTasks = await _db.Queryable<WmsDistaskH>().InnerJoin<WmsCarryH>((a, b) => a.carry_id == b.id).Where(a => input.disTaskIds.Contains(a.id)).Select((a, b) => new WmsDistaskH { carry_status = b.carry_status }, true).ToListAsync();
|
||
|
||
Logger.Information($"【TaskComplate】 disTasks 值 {JsonConvert.SerializeObject(disTasks)}");
|
||
if (disTasks?.Count > 0)
|
||
{
|
||
// 更新预任务申请表,单据状态为 已完成
|
||
List<string> preTaskIds = disTasks.Select(x => x.pretask_id).ToList();
|
||
_ = await _db.Updateable<WmsPretaskH>().SetColumns(it => new WmsPretaskH { status = WmsWareHouseConst.PRETASK_BILL_STATUS_COMPLE_ID }).Where(it => preTaskIds.Contains(it.id)).ExecuteCommandAsync();
|
||
|
||
//更新电梯任务数量
|
||
_ = await _db.Updateable<WmsElevatorH>().SetColumns(it => new WmsElevatorH { task_nums = it.task_nums - 1}).Where(it => disTasks.Select(x => x.area_code).Contains(it.area_code)).ExecuteCommandAsync();
|
||
|
||
// 更新任务数量
|
||
_ = await _db.Updateable<BasLocation>().SetColumns(it => new BasLocation { task_nums = it.task_nums - 1 }).Where(it => disTasks.Select(x => x.endlocation_code).Contains(it.location_code)).ExecuteCommandAsync();
|
||
|
||
|
||
//更新载具,锁定状态为未锁定,更新载具的库位当前任务的目标库位
|
||
|
||
List<(string carry_id, string carry_status, string endlocation_id, string endlocation_code)> multiList = disTasks.Select(it => (it.carry_id, it.carry_status, it.endlocation_id, it.endlocation_code)).ToList();
|
||
Dictionary<string, object> locWhIdMap = await _db.Queryable<BasLocation>().Where(it => multiList.Select(x => x.endlocation_id).Contains(it.id)).ToDictionaryAsync(it => it.id, it => it.wh_id);
|
||
Dictionary<string, object> locTypeMap = await _db.Queryable<BasLocation>().Where(it => multiList.Select(x => x.endlocation_id).Contains(it.id)).ToDictionaryAsync(it => it.id, it => it.is_type);
|
||
List<WmsCarryH> carryIts = new();
|
||
List<WmsCarryCode> carryCodeIts = new();
|
||
List<BasLocation> locIts = new();
|
||
|
||
Logger.Information($"【TaskComplate】 multiList 值 {JsonConvert.SerializeObject(multiList)}");
|
||
for (int i = 0, cnt = multiList.Count; i < cnt; i++)
|
||
{
|
||
WmsCarryH carry = new()
|
||
{
|
||
id = multiList[i].carry_id,
|
||
is_lock = 0,
|
||
location_id = multiList[i].endlocation_id,
|
||
location_code = multiList[i].endlocation_code,
|
||
instock_time = DateTime.Now
|
||
};
|
||
string endLocId = multiList[i].endlocation_id;
|
||
|
||
WmsCarryH wmsCarryH = await _db.Queryable<WmsCarryH>().Where(r => r.id == carry.id).FirstAsync();
|
||
// 同步料架下的载具位置
|
||
if (wmsCarryH != null && wmsCarryH.carrystd_id == WmsWareHouseConst.CARRY_LJSTD_ID)
|
||
{
|
||
List<WmsCarryH> carrys = _db.Queryable<WmsCarryD>()
|
||
.InnerJoin<WmsCarryH>((a, b) => a.membercarry_id == b.id).Where((a, b) => a.carry_id == carry.id).Select((a, b) => b).ToList();
|
||
carrys.ForEach(r =>
|
||
{
|
||
r.location_id = carry.location_id;
|
||
r.location_code = carry.location_code;
|
||
carryIts.Add(r);
|
||
});
|
||
}
|
||
|
||
WmsCarryCode carryCode = new()
|
||
{
|
||
warehouse_id = locWhIdMap.ContainsKey(endLocId) ? locWhIdMap[endLocId].ToString() : "",
|
||
location_id = multiList[i].endlocation_id,
|
||
location_code = multiList[i].endlocation_code
|
||
};
|
||
carryIts.Add(carry);
|
||
carryCodeIts.Add(carryCode);
|
||
|
||
BasLocation loc = new()
|
||
{
|
||
id = multiList[i].endlocation_id,
|
||
is_lock = 0,
|
||
is_use = string.IsNullOrEmpty(multiList[i].carry_status) ? ((int)EnumCarryStatus.占用).ToString() : multiList[i].carry_status
|
||
};
|
||
if (!string.IsNullOrEmpty(multiList[i].carry_status))
|
||
{
|
||
loc.is_use = ((int)EnumCarryStatus.占用).ToString();
|
||
}
|
||
|
||
|
||
|
||
// 如果是出入库位 完成后解除占用
|
||
string? is_type = locTypeMap.ContainsKey(endLocId) ? locTypeMap[endLocId].ToString() : "";
|
||
if (is_type == ((int)EnumLocationType.出入库位).ToString())
|
||
loc.is_use = ((int)EnumCarryStatus.空闲).ToString();
|
||
|
||
locIts.Add(loc);
|
||
}
|
||
|
||
_ = await _db.Updateable(carryIts).UpdateColumns(it => new { it.is_lock, it.location_id, it.location_code, it.instock_time }).ExecuteCommandAsync();
|
||
Logger.Information($"【TaskComplate】 更新载具 {JsonConvert.SerializeObject(carryIts)}");
|
||
//更新条码的库位和仓库信息
|
||
_ = await _db.Updateable(carryCodeIts).UpdateColumns(it => new { it.warehouse_id, it.location_id, it.location_code }).Where(it => multiList.Select(x => x.carry_id).Contains(it.carry_id)).ExecuteCommandAsync();
|
||
//更新库位信息,使用状态为 使用,锁定状态为未锁定
|
||
_ = await _db.Updateable(locIts).UpdateColumns(it => new { it.is_use, it.is_lock }).ExecuteCommandAsync();
|
||
Logger.Information($"【TaskComplate】 更新库位 {JsonConvert.SerializeObject(locIts)}");
|
||
/* var loginType= _userManager?.LoginType ?? "web";
|
||
Log.Information($"_userManager.LoginType={loginType}");
|
||
*/ //更新业务主表的单据状态
|
||
foreach (WmsDistaskH? dt in disTasks)
|
||
{
|
||
#region todo 移到service中
|
||
// 销售出库
|
||
if (dt.biz_type == WmsWareHouseConst.BIZTYPE_WMSSALERELEASE_ID)
|
||
{
|
||
// TODO 一楼工位放货完成后更新出库明细的实际出库数量
|
||
if (dt.start_floor == 1 && dt.end_floor == 1)
|
||
{
|
||
WmsCarryCode wmsCarryCode = await _db.Queryable<WmsCarryCode>().Where(r => r.carry_id == dt.carry_id).FirstAsync();
|
||
await _db.Updateable<WmsSaleD>().SetColumns(r => r.purchase_prqty == r.purchase_prqty + wmsCarryCode.codeqty).Where(r => r.id == dt.require_id).ExecuteCommandAsync();
|
||
|
||
CarryCodeUnbindInput carryCodeUnbindInput = new CarryCodeUnbindInput();
|
||
carryCodeUnbindInput.carry_id = dt.carry_id;
|
||
await _wmsCarryUnbindService.CarryCodeUnbind(carryCodeUnbindInput, _db);
|
||
}
|
||
}
|
||
// 成品调拨入库
|
||
else if (dt.biz_type == WmsWareHouseConst.BIZTYPE_WMSTRANSFERINSTOCK_ID)
|
||
{
|
||
if (dt.start_floor == 3 && dt.end_floor == 3)
|
||
{
|
||
WmsCarryCode wmsCarryCode = await _db.Queryable<WmsCarryCode>().Where(r => r.carry_id == dt.carry_id).FirstAsync();
|
||
await _db.Updateable<WmsTransferInstockD>().SetColumns(r => r.qty == r.qty + wmsCarryCode.codeqty).Where(r => r.id == dt.require_id).ExecuteCommandAsync();
|
||
}
|
||
}
|
||
// 二楼物料呼叫任务结束后清空工位信息
|
||
else if (dt.biz_type == "FloorCallMaterial")
|
||
{
|
||
await _db.Updateable<WmsCarryH>().SetColumns(r => new WmsCarryH
|
||
{
|
||
work_station = ""
|
||
}).Where(r => r.id == dt.carry_id).ExecuteCommandAsync();
|
||
|
||
// require_code是任务单
|
||
//string mo_task_code = dt.require_code;
|
||
}
|
||
else if (dt.biz_type == "erp_qtrk")
|
||
{
|
||
WmsMaterialTransfer wmsMaterialTransfer = await _db.Queryable<WmsMaterialTransfer>().SingleAsync(x=>x.id==dt.source_id);
|
||
List<WmsMaterialTransferD> wmsMaterialTransferDs = await _db.Queryable<WmsMaterialTransferD>().Where(x=>x.bill_id==dt.source_id).ToListAsync();
|
||
List<String> materialIds = wmsMaterialTransferDs.Select(x=>x.material_id).Distinct().ToList();
|
||
List<String> unitCodes = wmsMaterialTransferDs.Select(x => x.unit_id).Distinct().ToList();
|
||
List<DictionaryDataEntity> unitDatas = await _db.Queryable<DictionaryTypeEntity>()
|
||
.LeftJoin<DictionaryDataEntity>((x, y) => x.Id == y.DictionaryTypeId)
|
||
.Where((x, y) => x.EnCode == DictConst.MeasurementUnit && unitCodes.Contains(y.EnCode))
|
||
.Select((x,y)=>y)
|
||
.ToListAsync();
|
||
|
||
List<string> ids = new List<string>();
|
||
ids.Add(wmsMaterialTransfer.create_id);
|
||
ids.Add(WmsWareHouseConst.AdministratorOrgId);
|
||
ids.Add(wmsMaterialTransfer.warehouse_instock);
|
||
ids.AddRange(materialIds);
|
||
ids.AddRange(unitDatas.Select(x=>x.Id).ToList());
|
||
|
||
List<ErpExtendField> erpExtendFields = await _db.Queryable<ErpExtendField>().Where(x=>ids.Contains(x.table_id)).ToListAsync();
|
||
string erpCreateId = erpExtendFields.Find(x=>x.table_id==wmsMaterialTransfer.create_id)?.user_id ?? "";
|
||
ErpExtendField erpOrg = erpExtendFields.Find(x => x.table_id == (wmsMaterialTransfer.org_id ?? WmsWareHouseConst.AdministratorOrgId));
|
||
string nowStr = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
||
|
||
List<Dictionary<string, object>> requestData = new List<Dictionary<string, object>>();
|
||
Dictionary<string, object> erpRequestData = new Dictionary<string, object>();
|
||
erpRequestData.Add("approver",erpCreateId);
|
||
erpRequestData.Add("billmaker",erpCreateId);
|
||
erpRequestData.Add("corpoid",erpOrg.corpoid);
|
||
erpRequestData.Add("corpvid",erpOrg.corpvid);
|
||
erpRequestData.Add("creationtime",nowStr);
|
||
erpRequestData.Add("creator",erpCreateId);
|
||
erpRequestData.Add("ctrantypeid","0001H11000000000D30Z");
|
||
erpRequestData.Add("cwarehouseid",erpExtendFields.Find(x=>x.table_id==wmsMaterialTransfer.warehouse_instock)?.cotherwhid ?? "");
|
||
erpRequestData.Add("cwhsmanagerid","");
|
||
erpRequestData.Add("dbilldate",nowStr);
|
||
erpRequestData.Add("dmakedate",nowStr);
|
||
erpRequestData.Add("ntotalnum",wmsMaterialTransferDs.Sum(x=>x.qty));
|
||
erpRequestData.Add("pk_group",erpOrg.pk_group);
|
||
erpRequestData.Add("pk_org",erpOrg.pk_org);
|
||
erpRequestData.Add("pk_org_v",erpOrg.pk_org_v);
|
||
erpRequestData.Add("vbillcode",wmsMaterialTransfer.bill_code);
|
||
erpRequestData.Add("vtrantypecode","4A-01");
|
||
List<Dictionary<string, object>> erpRequestDataDetails = new List<Dictionary<string, object>>();
|
||
foreach(WmsMaterialTransferD item in wmsMaterialTransferDs)
|
||
{
|
||
erpRequestDataDetails.Add(new Dictionary<string, object>()
|
||
{
|
||
["cbodytranstypecode"] = "4A-01",
|
||
["cbodywarehouseid"] = erpExtendFields.Find(x=>x.table_id==wmsMaterialTransfer.warehouse_instock)?.cotherwhid ?? "",
|
||
["cgeneralbid"] = erpExtendFields.Find(x=>x.table_id==wmsMaterialTransfer.warehouse_instock)?.cotherwhid ?? "",
|
||
["cgeneralbid"] = item.erp_line_pk,
|
||
["cgeneralhid"] = wmsMaterialTransfer.erp_pk,
|
||
["cmaterialoid"] = erpExtendFields.Find(x=>x.table_id==item.material_id)?.cmaterialoid ?? "",
|
||
["cmaterialvid"] = erpExtendFields.Find(x=>x.table_id==item.material_id)?.cmaterialvid ?? "",
|
||
["corpoid"] = erpOrg.corpoid,
|
||
["corpvid"] = erpOrg.corpvid,
|
||
["crowno"] = (wmsMaterialTransferDs.FindIndex(x=>x.id==item.id)+1) * 10,
|
||
["cunitid"] = erpExtendFields.Find(x=>x.table_id==(unitDatas.Find(x=>x.EnCode==item.unit_id)?.Id ?? ""))?.cunitid ?? "",
|
||
["cvendorid"] = "",
|
||
["cvendorvid"] = "",
|
||
["dbizdate"] = nowStr,
|
||
["nnum"] = item.qty,
|
||
["pk_group"] = erpOrg.pk_group,
|
||
["pk_org"] = erpOrg.pk_org,
|
||
["pk_org_v"] = erpOrg.pk_org_v,
|
||
["vbatchcode"] = item.code_batch,
|
||
});
|
||
}
|
||
erpRequestData.Add("dtls",erpRequestDataDetails);
|
||
requestData.Add(erpRequestData);
|
||
|
||
ThirdWebapiRecord thirdWebapiRecord = new ThirdWebapiRecord();
|
||
thirdWebapiRecord.id = SnowflakeIdHelper.NextId();
|
||
thirdWebapiRecord.third_name = WmsWareHouseConst.BIP;
|
||
thirdWebapiRecord.name = "其它入库";
|
||
thirdWebapiRecord.method = "POST";
|
||
thirdWebapiRecord.url = WmsWareHouseConst.BIP_DOMAIN+"uapws/rest/generalin/save";
|
||
thirdWebapiRecord.request_data = JsonConvert.SerializeObject(requestData);
|
||
thirdWebapiRecord.create_time = DateTime.Now;
|
||
|
||
await _db.Insertable(thirdWebapiRecord).ExecuteCommandAsync();
|
||
}
|
||
#endregion
|
||
|
||
List<WmsDistaskCode> disTaskCodes = await _db.Queryable<WmsDistaskCode>().Where(it => it.bill_id == dt.id).ToListAsync();
|
||
WareHouseUpInput upInput = new() { bizTypeId = dt.biz_type, requireId = dt.require_id!, require_code = dt.require_code!, source_id = dt.source_id!
|
||
, source_code = dt.source_code!, distaskCodes = disTaskCodes, carryIds = disTasks.Select(x => x.carry_id).ToList(),
|
||
bill_code = dt.bill_code!,area_code= dt.area_code!,wmsDistaskH = dt!
|
||
};
|
||
|
||
/*if (!_userManager?.LoginType.IsNullOrEmpty() ?? false)
|
||
{
|
||
upInput.loginType = "app";
|
||
}
|
||
else
|
||
{
|
||
upInput.loginType = "web";
|
||
}*/
|
||
upInput.loginType = "web";//(!string.IsNullOrEmpty(_userManager?.LoginType) ? "app" : "web") ?? "web";
|
||
if (dt.is_sign == 1 && dt.chain_type == "3")
|
||
{
|
||
await DoUpdate(upInput);
|
||
}
|
||
}
|
||
}
|
||
Logger.Information($"【TaskComplate】 任务操作完成提交事务 {string.Join(",", input.disTaskIds)}");
|
||
await _db.Ado.CommitTranAsync();
|
||
|
||
Logger.Information($"【TaskComplate】 任务操作完成 {string.Join(",", input.disTaskIds)}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Error($"任务结束失败", ex);
|
||
Logger.Error($"任务结束失败堆栈异常", ex);
|
||
await _db.Ado.RollbackTranAsync();
|
||
throw;
|
||
}
|
||
finally
|
||
{
|
||
_ = GenTaskExecute();
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 出入库策略启用、禁用状态修改
|
||
/// </summary>
|
||
/// <param name="input"></param>
|
||
/// <returns></returns>
|
||
[HttpPost]
|
||
public async Task ModifyPoliciesStatus(ModifyEnabledInput input)
|
||
{
|
||
async Task _updateStatus<T>(ModifyEnabledInput input) where T : BaseEntity<string>, IUpdatePoliciesStatus, new()
|
||
{
|
||
T obj = new() { status = input.status };
|
||
_ = await _db.Updateable(obj).UpdateColumns(it => it.status).Where(it => input.ids.Contains(it.id)).ExecuteCommandAsync();
|
||
}
|
||
|
||
switch (input.strategyType)
|
||
{
|
||
case EnumInOutStockType.In:
|
||
await _updateStatus<WmsInstockPolicies>(input);
|
||
break;
|
||
case EnumInOutStockType.Out:
|
||
await _updateStatus<WmsOutstockPolicies>(input);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成预任务
|
||
/// </summary>
|
||
/// <param name="preTasks">预任务集合</param>
|
||
/// <param name="preTaskCodes">预任务编码集合</param>
|
||
/// <returns></returns>
|
||
public async Task<bool> GenPreTask(List<WmsPretaskH> preTasks, List<WmsPretaskCode> preTaskCodes, ISqlSugarClient dbConn = null)
|
||
{
|
||
try
|
||
{
|
||
await s_taskGenPreTask.WaitAsync();
|
||
|
||
var db = _db;
|
||
if (dbConn != null)
|
||
db = dbConn;
|
||
|
||
//如果预任务出现起终库位相同,则删除对应预任务
|
||
//modifiy by ly on 20230922 将当前预任务操作者设为四场管理员
|
||
preTasks.ForEach(pt =>
|
||
{
|
||
pt.org_id = WmsWareHouseConst.AdministratorOrgId;
|
||
pt.create_id = WmsWareHouseConst.AdministratorUserId;
|
||
});
|
||
if (preTasks.FindAll(it => it.startlocation_id == it.endlocation_id)?.Count > 0)
|
||
{
|
||
_ = preTasks.RemoveAll(it => it.startlocation_id == it.endlocation_id);
|
||
}
|
||
List<IGrouping<string, WmsPretaskH>> grpList = preTasks.OrderBy(o => o.bill_code).GroupBy(g => g.carry_id).ToList();
|
||
if (grpList?.Count > 0)
|
||
{
|
||
foreach (IGrouping<string, WmsPretaskH>? grp in grpList)
|
||
{
|
||
WmsPretaskH[] arr = grp.ToArray();
|
||
if (arr.Length > 1)
|
||
{
|
||
WmsPretaskH[] subArr = arr[..^1];
|
||
System.Array.ForEach(subArr, a => a.chain_type = "1");
|
||
}
|
||
}
|
||
}
|
||
int row = await db.Insertable(preTasks).ExecuteCommandAsync();
|
||
if (preTaskCodes?.Count > 0)
|
||
{
|
||
row = await db.Insertable(preTaskCodes).ExecuteCommandAsync();
|
||
}
|
||
var eleP = preTasks.Find(x => x.area_code.Contains("ELE"));
|
||
if (eleP != null)
|
||
{
|
||
row = await db.Updateable<WmsElevatorH>().SetColumns(it => it.task_nums == it.task_nums + 1).Where(it => it.area_code == eleP.area_code).ExecuteCommandAsync();
|
||
}
|
||
|
||
// 累加终点库位任务数
|
||
row = await db.Updateable<BasLocation>().SetColumns(it => it.task_nums == it.task_nums + 1)
|
||
.Where(it => preTasks.Select(r => r.endlocation_id).Contains(it.id)).ExecuteCommandAsync();
|
||
|
||
return row > 0;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
throw;
|
||
}
|
||
finally
|
||
{
|
||
s_taskGenPreTask.Release();
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 生成预任务后续处理
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
[NonAction]
|
||
public async Task GenInStockTaskHandleAfter(GenPreTaskUpInput input, Expression<Func<WmsCarryH, WmsCarryH>> setCarryColumnsExp, Expression<Func<BasLocation, BasLocation>> setLocaionColumbExp, ISqlSugarClient dbConn = null)
|
||
{
|
||
try
|
||
{
|
||
var db = _db;
|
||
if (dbConn != null)
|
||
db = dbConn;
|
||
|
||
//根据生成的预任务,插入预任务操作记录
|
||
if (input.PreTaskRecord != null)
|
||
{
|
||
_ = await db.Insertable(input.PreTaskRecord).ExecuteCommandAsync();
|
||
}
|
||
if (input.PreTaskHandleCodes.Count > 0)
|
||
{
|
||
_ = await db.Insertable(input.PreTaskHandleCodes).ExecuteCommandAsync();
|
||
}
|
||
//根据载具ID,更新是否锁定和赋值起始库位
|
||
if (setCarryColumnsExp != null)
|
||
{
|
||
Expression<Func<WmsCarryH, bool>> whereExp = input.CarryIds?.Count > 0 ? it => input.CarryIds.Contains(it.id) : it => it.id == input.CarryId;
|
||
_ = await db.Updateable<WmsCarryH>().SetColumns(setCarryColumnsExp).Where(whereExp).ExecuteCommandAsync();
|
||
}
|
||
if (input.CarryStartLocationId.IsNullOrWhiteSpace() == false)
|
||
{
|
||
_ = await db.Updateable<BasLocation>().SetColumns(setLocaionColumbExp).Where(it => input.LocationIds.Contains(it.id)).ExecuteCommandAsync();
|
||
}
|
||
//根据所有库位更新库位的锁定状态为“锁定”
|
||
if (setLocaionColumbExp != null && input.LocationIds?.Count > 0)
|
||
{
|
||
_ = await db.Updateable<BasLocation>().SetColumns(setLocaionColumbExp).Where(it => input.LocationIds.Contains(it.id)).ExecuteCommandAsync();
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.LogError("【GenInStockTaskHandleAfter】" + ex.Message);
|
||
Logger.LogError("【GenInStockTaskHandleAfter】" + ex.StackTrace);
|
||
throw;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 路径算法
|
||
/// </summary>
|
||
/// <param name="pStartId"></param>
|
||
/// <param name="pEndId"></param>
|
||
/// <returns></returns>
|
||
[NonAction]
|
||
public async Task<List<WmsPointH>> PathAlgorithms(string pStartId, string pEndId)
|
||
{
|
||
/*
|
||
var sp= await _db.Queryable<WmsPointH>().Where(it => it.id == pStartId).FirstAsync();
|
||
var ep = await _db.Queryable<WmsPointH>().Where(it => it.id == pEndId).FirstAsync();
|
||
|
||
if (sp.area_code == "B" || ep.area_code == "B")
|
||
{
|
||
List<WmsPointH> Points = new List<WmsPointH>();
|
||
var zp = await _db.Queryable<WmsPointH>().Where(it => it.point_code == "ZSCJZJD").FirstAsync();
|
||
Points.Add(sp);
|
||
Points.Add(ep);
|
||
Points.Add(zp);
|
||
Points.ForEach(p =>
|
||
{
|
||
p.area_code = zp.area_code;
|
||
p.area_id = zp.area_id;
|
||
});
|
||
return Points;
|
||
}*/
|
||
|
||
|
||
|
||
List<WmsPointH> wmsPointHs = await _db.Queryable<WmsPointH>().Where(it => it.status == 1).ToListAsync();
|
||
|
||
List<WmsPointH> points = new List<WmsPointH>();
|
||
|
||
// 待验证 算法太慢 电梯不跑算法 电梯起终点仓库对应表
|
||
BasLocation startlocation = await _db.Queryable<BasLocation>().InnerJoin<WmsPointH>((a, b) => a.id == b.location_id).Where((a, b) => b.id == pStartId).FirstAsync();
|
||
BasLocation endlocation = await _db.Queryable<BasLocation>().InnerJoin<WmsPointH>((a, b) => a.id == b.location_id).Where((a, b) => b.id == pEndId).FirstAsync();
|
||
|
||
// 后续可整理成配置表
|
||
// 3-1
|
||
if (startlocation.wh_id == WmsWareHouseConst.WAREHOUSE_CP_ID && endlocation.wh_id == WmsWareHouseConst.WAREHOUSE_CPCRK_ID)
|
||
{
|
||
WmsElevatorH wmsElevatorH = await _db.Queryable<WmsElevatorH>().Where(it => it.elevator_group == "2" && it.enabled == 1).OrderBy(r => r.task_nums).FirstAsync();
|
||
string elevatorSno = wmsElevatorH.elevator_code.Replace("Elevator", "");
|
||
|
||
points.Add(wmsPointHs.Where(r => r.id == pStartId).First());
|
||
points.Add(wmsPointHs.Where(r => r.point_code == $"DT-C-03-0{elevatorSno}").First());
|
||
points.Add(wmsPointHs.Where(r => r.point_code == $"DT-3-{elevatorSno}").First());
|
||
points.Add(wmsPointHs.Where(r => r.point_code == $"DT-1-{elevatorSno}").First());
|
||
points.Add(wmsPointHs.Where(r => r.point_code == $"DT-R-01-0{elevatorSno}").First());
|
||
points.Add(wmsPointHs.Where(r => r.id == pEndId).First());
|
||
}
|
||
//1-3
|
||
else if (startlocation.wh_id == WmsWareHouseConst.WAREHOUSE_CPCRK_ID && endlocation.wh_id == WmsWareHouseConst.WAREHOUSE_CP_ID)
|
||
{
|
||
WmsElevatorH wmsElevatorH = await _db.Queryable<WmsElevatorH>().Where(it => it.elevator_group == "2" && it.enabled == 1).OrderBy(r => r.task_nums).FirstAsync();
|
||
string elevatorSno = wmsElevatorH.elevator_code.Replace("Elevator", "");
|
||
|
||
points.Add(wmsPointHs.Where(r => r.id == pStartId).First());
|
||
points.Add(wmsPointHs.Where(r => r.point_code == $"DT-C-01-0{elevatorSno}").First());
|
||
points.Add(wmsPointHs.Where(r => r.point_code == $"DT-1-{elevatorSno}").First());
|
||
points.Add(wmsPointHs.Where(r => r.point_code == $"DT-3-{elevatorSno}").First());
|
||
points.Add(wmsPointHs.Where(r => r.point_code == $"DT-R-03-0{elevatorSno}").First());
|
||
points.Add(wmsPointHs.Where(r => r.id == pEndId).First());
|
||
}
|
||
// 2-4
|
||
else if(startlocation.wh_id == WmsWareHouseConst.WAREHOUSE_ZZXBK_ID && endlocation.wh_id == WmsWareHouseConst.WAREHOUSE_DMJC_ID)
|
||
{
|
||
WmsElevatorH wmsElevatorH = await _db.Queryable<WmsElevatorH>().Where(it => it.elevator_group == "1" && it.enabled == 1).OrderBy(r => r.task_nums).FirstAsync();
|
||
string elevatorSno = wmsElevatorH.elevator_code.Replace("Elevator", "");
|
||
|
||
points.Add(wmsPointHs.Where(r => r.id == pStartId).First());
|
||
points.Add(wmsPointHs.Where(r => r.point_code == $"DT-C-02-0{elevatorSno}").First());
|
||
points.Add(wmsPointHs.Where(r => r.point_code == $"DT-02-0{elevatorSno}").First());
|
||
points.Add(wmsPointHs.Where(r => r.point_code == $"DT-04-0{elevatorSno}").First());
|
||
points.Add(wmsPointHs.Where(r => r.point_code == $"DT-R-04-0{elevatorSno}").First());
|
||
points.Add(wmsPointHs.Where(r => r.id == pEndId).First());
|
||
}
|
||
// 1-4
|
||
else if(startlocation.wh_id == WmsWareHouseConst.WAREHOUSE_F1BCK_ID && endlocation.wh_id == WmsWareHouseConst.WAREHOUSE_BCK_ID)
|
||
{
|
||
WmsElevatorH wmsElevatorH = await _db.Queryable<WmsElevatorH>().Where(it => it.elevator_group == "1" && it.enabled == 1).OrderBy(r => r.task_nums).FirstAsync();
|
||
string elevatorSno = wmsElevatorH.elevator_code.Replace("Elevator", "");
|
||
|
||
points.Add(wmsPointHs.Where(r => r.id == pStartId).First());
|
||
points.Add(wmsPointHs.Where(r => r.point_code == $"DT-C-01-0{elevatorSno}").First());
|
||
points.Add(wmsPointHs.Where(r => r.point_code == $"DT-01-0{elevatorSno}").First());
|
||
points.Add(wmsPointHs.Where(r => r.point_code == $"DT-04-0{elevatorSno}").First());
|
||
points.Add(wmsPointHs.Where(r => r.point_code == $"DT-R-04-0{elevatorSno}").First());
|
||
points.Add(wmsPointHs.Where(r => r.id == pEndId).First());
|
||
}
|
||
// 4-2
|
||
else if (startlocation.wh_id == WmsWareHouseConst.WAREHOUSE_BCK_ID && endlocation.wh_id == WmsWareHouseConst.WAREHOUSE_F2BCQ_ID)
|
||
{
|
||
WmsElevatorH wmsElevatorH = await _db.Queryable<WmsElevatorH>().Where(it => it.elevator_group == "1" && it.enabled == 1).OrderBy(r => r.task_nums).FirstAsync();
|
||
string elevatorSno = wmsElevatorH.elevator_code.Replace("Elevator", "");
|
||
|
||
points.Add(wmsPointHs.Where(r => r.id == pStartId).First());
|
||
points.Add(wmsPointHs.Where(r => r.point_code == $"DT-C-04-0{elevatorSno}").First());
|
||
points.Add(wmsPointHs.Where(r => r.point_code == $"DT-04-0{elevatorSno}").First());
|
||
points.Add(wmsPointHs.Where(r => r.point_code == $"DT-02-0{elevatorSno}").First());
|
||
points.Add(wmsPointHs.Where(r => r.point_code == $"DT-R-02-0{elevatorSno}").First());
|
||
points.Add(wmsPointHs.Where(r => r.id == pEndId).First());
|
||
}
|
||
else
|
||
{
|
||
List<WmsRoad> roads = await _db.Queryable<WmsRoad>().Where(it => it.status == 1).ToListAsync();
|
||
points = await LocPathCalcAlgorithms(pStartId, pEndId, roads);
|
||
}
|
||
|
||
|
||
Logger.Information($"起点{startlocation.location_code} 终点{endlocation.location_code} 获取到路径 {JsonConvert.SerializeObject(points)}");
|
||
if (points.Count < 6)
|
||
{
|
||
throw new AppFriendlyException($"路径错误,需要检查路径配置{JsonConvert.SerializeObject(points)}", 500);
|
||
}
|
||
|
||
//List<WmsRoad> wmsRoads = await _db.Queryable<WmsRoad>().Where(r => r.startpoint_id == pStartId).ToListAsync();
|
||
//if (wmsRoads?.FindAll(x => x.endpoint_code != null && x.location_code.Contains("dt", StringComparison.OrdinalIgnoreCase))?.Count > 0)
|
||
//{
|
||
|
||
//}
|
||
|
||
//List<WmsRoad> wmsRoads = await _db.Queryable<WmsRoad>().Where(r => r.startpoint_id == pStartId).ToListAsync();
|
||
|
||
|
||
|
||
|
||
//try
|
||
//{
|
||
// if (points?.FindAll(x => x.location_code != null && x.location_code.Contains("dt", StringComparison.OrdinalIgnoreCase))?.Count > 0)
|
||
// {
|
||
// //查询当前电梯点
|
||
// List<WmsElevatorD> curEleDs = await _db.Queryable<WmsElevatorD>().InnerJoin<WmsElevatorH>((a, b) => a.bill_id == b.id).Where((a, b) => points.Select(x => x.id).Contains(a.point_id)).ToListAsync();
|
||
// Logger.Information($"curEleDs==null :{curEleDs == null},curEleDs:{string.Join(",", curEleDs.Select(x => x.bill_id))}");
|
||
// //如果有电梯点,则会进行电梯的均匀分配
|
||
// if (curEleDs?.Count > 0)
|
||
// {
|
||
// //当前电梯
|
||
// WmsElevatorH curEle = await _db.Queryable<WmsElevatorH>().SingleAsync(it => it.id == curEleDs.First().bill_id);
|
||
|
||
// //同电梯组电梯
|
||
// List<WmsElevatorH> sGpEle = await _db.Queryable<WmsElevatorH>().Where(it => it.elevator_group == curEle.elevator_group && it.id != curEle.id && it.enabled == 1).ToListAsync();
|
||
// if (curEle.enabled == 0 && (sGpEle == null || sGpEle.Count < 1))
|
||
// {
|
||
// throw new AppFriendlyException("电梯被禁用或未配置", 500);
|
||
// }
|
||
// if ((sGpEle == null || sGpEle.Count < 1) && curEle.enabled == 1)
|
||
// {
|
||
// return points;
|
||
// }
|
||
// if (sGpEle?.Count > 0 && curEle.enabled == 0)
|
||
// {
|
||
// return points;
|
||
// }
|
||
// //判断电梯组中各电梯任务数
|
||
// if (sGpEle.FindAll(x => Math.Abs(x.task_nums - curEle.task_nums) % 2 == 1)?.Count > 0)
|
||
// {
|
||
// List<WmsElevatorD> sGpDs = await _db.Queryable<WmsElevatorD>().Where(it => it.bill_id == sGpEle.First().id).ToListAsync();
|
||
// if (sGpDs?.Count > 0)
|
||
// {
|
||
// List<WmsPointH> sGpPoints = await _db.Queryable<WmsPointH>().Where(it => sGpDs.Select(x => x.point_id).Contains(it.id)).ToListAsync();
|
||
// string sFEndId = sGpDs.Single(x => x.floor == curEleDs.First().floor).point_id;
|
||
// string eFStartId = sGpDs.Single(x => x.floor == curEleDs.Last().floor).point_id;
|
||
// List<WmsPointH> sFPoints = await LocPathCalcAlgorithms(pStartId, sFEndId, roads);
|
||
// List<WmsPointH> elePoints = new();
|
||
// foreach (WmsElevatorD pt in curEleDs)
|
||
// {
|
||
// WmsPointH? elePoint = sGpPoints.Find(x => x.floor == pt.floor);
|
||
// if (elePoint != null)
|
||
// {
|
||
// elePoints.Add(elePoint);
|
||
// }
|
||
// }
|
||
// List<WmsPointH> eFPoints = await LocPathCalcAlgorithms(eFStartId, pEndId, roads);
|
||
// _ = elePoints.Remove(elePoints.First());
|
||
// _ = elePoints.Remove(elePoints.Last());
|
||
// points.Clear();
|
||
// points.AddRange(sFPoints);
|
||
// points.AddRange(elePoints);
|
||
// points.AddRange(eFPoints);
|
||
// }
|
||
// }
|
||
|
||
// }
|
||
// }
|
||
//}
|
||
//catch (Exception ex)
|
||
//{
|
||
// Logger.Error("路径算法异常", ex);
|
||
// throw;
|
||
//}
|
||
|
||
return points;
|
||
}
|
||
/// <summary>
|
||
/// 路径算法 当出现多个载具同时出库,可能需要进入电梯时
|
||
/// </summary>
|
||
/// <param name="pStartId"></param>
|
||
/// <param name="pEndId"></param>
|
||
/// <param name="ele"></param>
|
||
/// <returns></returns>
|
||
[NonAction]
|
||
public async Task<List<WmsPointH>> PathAlgorithmsEle(string pStartId, string pEndId, int ele)
|
||
{
|
||
List<WmsRoad> roads = await _db.Queryable<WmsRoad>().Where(it => it.status == 1).ToListAsync();
|
||
List<WmsPointH> points = await LocPathCalcAlgorithms(pStartId, pEndId, roads);
|
||
try
|
||
{
|
||
if (points.FindAll(x => x.location_code != null && x.location_code.Contains("dt", StringComparison.OrdinalIgnoreCase))?.Count > 0)
|
||
{
|
||
//查询当前电梯点
|
||
List<WmsElevatorD> curEleDs = await _db.Queryable<WmsElevatorD>().Where(it => points.Select(x => x.id).Contains(it.point_id)).ToListAsync();
|
||
//如果有电梯点,则会进行电梯的均匀分配
|
||
if (curEleDs?.Count > 0)
|
||
{
|
||
//当前电梯
|
||
WmsElevatorH curEle = await _db.Queryable<WmsElevatorH>().SingleAsync(it => it.id == curEleDs.First().bill_id && it.enabled == 1);
|
||
|
||
|
||
//同电梯组电梯
|
||
List<WmsElevatorH> sGpEle = await _db.Queryable<WmsElevatorH>().Where(it => it.elevator_group == curEle.elevator_group && it.id != curEle.id && it.enabled == 1).ToListAsync();
|
||
|
||
|
||
if (curEle == null && sGpEle?.Count > 0)
|
||
{
|
||
throw new AppFriendlyException("电梯被禁用或未配置", 500);
|
||
}
|
||
if (ele % 2 == 1)
|
||
{
|
||
//判断电梯组中各电梯任务数
|
||
if (curEle == null || sGpEle.FindAll(x => Math.Abs(x.task_nums - curEle.task_nums) % 2 == 1)?.Count > 0)
|
||
{
|
||
List<WmsElevatorD> sGpDs = await _db.Queryable<WmsElevatorD>().Where(it => it.bill_id == sGpEle.First().id).ToListAsync();
|
||
if (sGpDs?.Count > 0)
|
||
{
|
||
List<WmsPointH> sGpPoints = await _db.Queryable<WmsPointH>().Where(it => sGpDs.Select(x => x.point_id).Contains(it.id)).ToListAsync();
|
||
string sFEndId = sGpDs.Single(x => x.floor == curEleDs.First().floor).point_id;
|
||
string eFStartId = sGpDs.Single(x => x.floor == curEleDs.Last().floor).point_id;
|
||
List<WmsPointH> sFPoints = await LocPathCalcAlgorithms(pStartId, sFEndId, roads);
|
||
List<WmsPointH> elePoints = new();
|
||
foreach (WmsElevatorD pt in curEleDs)
|
||
{
|
||
WmsPointH? elePoint = sGpPoints.Find(x => x.floor == pt.floor);
|
||
if (elePoint != null)
|
||
{
|
||
elePoints.Add(elePoint);
|
||
}
|
||
}
|
||
List<WmsPointH> eFPoints = await LocPathCalcAlgorithms(eFStartId, pEndId, roads);
|
||
_ = elePoints.Remove(elePoints.First());
|
||
_ = elePoints.Remove(elePoints.Last());
|
||
points.Clear();
|
||
points.AddRange(sFPoints);
|
||
points.AddRange(elePoints);
|
||
points.AddRange(eFPoints);
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Error("路径算法异常", ex);
|
||
throw;
|
||
}
|
||
|
||
return points;
|
||
}
|
||
#region PrivateMethods
|
||
|
||
private async Task<List<WmsPointH>> LocPathCalcAlgorithms(string pStartId, string pEndId, List<WmsRoad> roads)
|
||
{
|
||
Logger.Information($"【LocPathCalcAlgorithms】开始执行 pStartId:{pStartId} pEndId:{pEndId}");
|
||
Stopwatch stopwatch = Stopwatch.StartNew();
|
||
#region dp
|
||
//List<WmsPointH> results = new();
|
||
//var points = await _db.Queryable<WmsPointH>().ToListAsync();
|
||
//Dictionary<string, bool> isVisited = roads.Select(x => x.startpoint_id).Distinct().ToDictionary(x => x, x => false);
|
||
|
||
//List<string> pointIds = new();
|
||
//List<string> codes = new();
|
||
//Dp dp = new();
|
||
//dynamic obj = new ExpandoObject();
|
||
//obj.isArrivedEpoint = false;
|
||
//dp.DpFunc(roads, pointIds, isVisited, pStartId, pEndId, obj);
|
||
//foreach (var pid in pointIds)
|
||
//{
|
||
// var point = points.Find(x => x.id == pid);
|
||
// if (point != null)
|
||
// {
|
||
// results.Add(point);
|
||
// }
|
||
//}
|
||
//return results;
|
||
#endregion
|
||
|
||
#region dijkstra
|
||
List<WmsPointH> results = new();
|
||
|
||
try
|
||
{
|
||
List<WmsPointH>? points = await _db.Queryable<WmsPointH>().ToListAsync();
|
||
WmsPointH? startObj = points.Find(x => x.id == pStartId);
|
||
WmsPointH? endObj = points.Find(x => x.id == pEndId);
|
||
int sIndex = points.IndexOf(startObj);
|
||
int eIndex = points.IndexOf(endObj);
|
||
if (eIndex < sIndex)
|
||
{
|
||
(eIndex, sIndex) = (sIndex, eIndex);
|
||
(points[eIndex], points[sIndex]) = (points[sIndex], points[eIndex]);
|
||
}
|
||
|
||
string[] vexs = points.Select(p => p.id).ToArray();
|
||
EData[] edges = new EData[roads.Count];
|
||
for (int i = 0; i < edges.Length; i++)
|
||
{
|
||
string start = roads[i].startpoint_id;
|
||
string end = roads[i].endpoint_id;
|
||
int weight = roads[i].distance;
|
||
edges[i] = new EData(start, end, weight);
|
||
}
|
||
|
||
Dijkstra pG = new(vexs, edges);
|
||
|
||
|
||
int[] prev = new int[pG.mVexs.Length];
|
||
int[] dist = new int[pG.mVexs.Length];
|
||
List<WmsPointH> vertexs = new() { startObj };
|
||
pG.CalcDijkstra(sIndex, prev, dist);
|
||
List<string> pointIds = points.Select(p => p.id).ToList();
|
||
Stack<string> result = new();
|
||
GetPoints(pointIds, prev, result, eIndex);
|
||
|
||
results = new() { startObj };
|
||
if (points?.Count > 0)
|
||
{
|
||
//points.Where(it => result.Contains(it.id));
|
||
foreach (string i in result)
|
||
{
|
||
WmsPointH? point = points?.Find(x => x.id == i);
|
||
if (point != null)
|
||
{
|
||
results.Add(point);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Error("路径算法错误", ex);
|
||
throw;
|
||
}
|
||
finally
|
||
{
|
||
stopwatch.Stop();
|
||
Logger.Information($"【LocPathCalcAlgorithms】耗时{stopwatch.ElapsedMilliseconds}");
|
||
}
|
||
|
||
|
||
return results;
|
||
#endregion
|
||
|
||
}
|
||
/// <summary>
|
||
/// 获取匹配的最短路径节点
|
||
/// </summary>
|
||
/// <param name="results"></param>
|
||
/// <param name="roads"></param>
|
||
/// <param name="shortestPathPoints"></param>
|
||
/// <param name="isVisited"></param>
|
||
/// <param name="pStartId"></param>
|
||
/// <param name="pEndId"></param>
|
||
private void MatchPoint(List<WmsPointH> results, List<WmsRoad> roads, List<WmsPointH> shortestPathPoints, Dictionary<string, bool> isVisited, string pStartId, string pEndId)
|
||
{
|
||
List<WmsRoad> sRoads = roads.Where(x => x.startpoint_id == pStartId).ToList();
|
||
for (int j = 0; j < sRoads.Count; j++)
|
||
{
|
||
WmsPointH? sPoint = shortestPathPoints.Find(x => x.id == sRoads[j].endpoint_id);
|
||
if (sPoint != null && isVisited.ContainsKey(sPoint.id) && !isVisited[sPoint.id] && sPoint.id != pEndId)
|
||
{
|
||
string code = sPoint.point_code;
|
||
results.Add(sPoint);
|
||
isVisited[sPoint.id] = true;
|
||
MatchPoint(results, roads, shortestPathPoints, isVisited, sPoint.id, pEndId);
|
||
}
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 根据终止节点获取最短路径顶点
|
||
/// </summary>
|
||
/// <param name="pointIds"></param>
|
||
/// <param name="prev"></param>
|
||
/// <param name="result"></param>
|
||
/// <param name="eIdx"></param>
|
||
private static void GetPoints(List<string> pointIds, int[] prev, Stack<string> result, int eIdx)
|
||
{
|
||
int index = eIdx;
|
||
while (index != 0)
|
||
{
|
||
result.Push(pointIds[index]);
|
||
index = prev[index];
|
||
}
|
||
}
|
||
|
||
private async Task<bool> Update<T1, T2>(T1 entity, List<T2> entities) where T1 : BaseEntity<string>, new() where T2 : BaseEntity<string>, new()
|
||
{
|
||
bool isOk = false;
|
||
try
|
||
{
|
||
await _db.Ado.BeginTranAsync();
|
||
isOk = await _db.Updateable(entity).ExecuteCommandHasChangeAsync();
|
||
if (entities?.Count > 0)
|
||
{
|
||
int row = await _db.Storageable(entities).ExecuteCommandAsync();
|
||
isOk = row > 0;
|
||
}
|
||
await _db.Ado.CommitTranAsync();
|
||
}
|
||
catch (Exception)
|
||
{
|
||
await _db.Ado.RollbackTranAsync();
|
||
}
|
||
return isOk;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据明细Id获取出入库明细信息
|
||
/// </summary>
|
||
/// <typeparam name="TStockD">出入库明细</typeparam>
|
||
/// <typeparam name="TOutput">明细输出类</typeparam>
|
||
/// <typeparam name="TStockCode">出入库条码</typeparam>
|
||
/// <param name="billDId">明细Id</param>
|
||
/// <returns></returns>
|
||
private async Task<dynamic?> FetchInOutStockCodesById<TStockD, TOutput, TStockCode>(string billDId)
|
||
where TStockD : BaseEntity<string>, new()
|
||
where TOutput : IInOutStockDetail<TStockCode>, new()
|
||
where TStockCode : BaseEntity<string>, IInOutStockCode, new()
|
||
{
|
||
Dictionary<string, object> dic = await _dictionaryDataService.GetDictionaryByTypeId(WmsWareHouseConst.WMS_INSTOCK_D_BILL_STATUS_TYPEID);
|
||
List<TOutput> data = await _db.Queryable<TStockD>()
|
||
.Where(a => a.id == billDId)
|
||
.Select(a => new TOutput
|
||
{
|
||
CodeDetails = SqlFunc.Subqueryable<TStockCode>().Where(it => it.bill_d_id == a.id).ToList(),
|
||
}, true)
|
||
.Mapper(it =>
|
||
{
|
||
it.line_status = it.line_status != null && dic.ContainsKey(key: it.line_status) ? dic[it.line_status]?.ToString() : "";
|
||
it.warehouse_id = _db.Queryable<BasWarehouse>().Single(y => y.id == it.warehouse_id)?.whname ?? "";
|
||
})
|
||
.ToListAsync();
|
||
|
||
return data;
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 二楼kiva把料架送到下升降机后生成中储仓入库任务
|
||
/// </summary>
|
||
/// <param name="rackid">料架id</param>
|
||
/// <returns></returns>
|
||
|
||
public async Task<bool> Floor2EmptyCarryCreateZZCPretask(string rackid)
|
||
{
|
||
try
|
||
{
|
||
List<WmsCarryD> wmsCarryCodes = _db.Queryable<WmsCarryD>().Where(r => r.carry_id == rackid).ToList();
|
||
|
||
int move_num = 6;
|
||
string[] startlocations = new string[2] { WmsWareHouseConst.ZZCSSX111011, WmsWareHouseConst.ZZCSSX111012 };
|
||
BasLocation startLocation = null;
|
||
|
||
int index = 0;
|
||
foreach (WmsCarryD wmsCarryD in wmsCarryCodes)
|
||
{
|
||
await s_floor2CreatePretask.WaitAsync();
|
||
|
||
//入库取终点 //出库起点
|
||
InStockStrategyQuery inStockStrategyInput = new() { warehouse_id = WmsWareHouseConst.WAREHOUSE_ZC_ID, Size = 1 };
|
||
List<BasLocation> endLocations = await InStockStrategy(inStockStrategyInput);
|
||
if (endLocations.Count == 0)
|
||
{
|
||
Logger.LogWarning("没有可用的入库库位");
|
||
continue;
|
||
}
|
||
|
||
// 每6个重新获取一次起点
|
||
if (index % move_num == 0)
|
||
{
|
||
startLocation = await _db.Queryable<BasLocation>().Where(r => startlocations.Contains(r.id)).OrderBy("is_lock, task_nums, location_code").FirstAsync();
|
||
}
|
||
|
||
BasLocation endLocation = endLocations[0];
|
||
|
||
WmsCarryH carry = await _db.Queryable<WmsCarryH>().SingleAsync(it => it.carry_code == wmsCarryD.membercarry_code);
|
||
await _db.Updateable<WmsCarryH>().SetColumns(r => new WmsCarryH
|
||
{
|
||
location_id = startLocation.id,
|
||
location_code = startLocation.location_code,
|
||
}).Where(r => r.id == carry.id).ExecuteCommandAsync();
|
||
|
||
bool isMatch = await IsCarryAndLocationMatchByCarryStd(carry, endLocation);
|
||
if (!isMatch)
|
||
{
|
||
Logger.LogWarning("库位与载具规格不匹配");
|
||
continue;
|
||
}
|
||
|
||
CommonCreatePretaskInput commonCreatePretaskInput = new CommonCreatePretaskInput();
|
||
commonCreatePretaskInput.startlocation_id = startLocation.id;
|
||
commonCreatePretaskInput.endlocation_id = endLocation.id;
|
||
commonCreatePretaskInput.carry_id = wmsCarryD.membercarry_id;
|
||
commonCreatePretaskInput.carry_code = wmsCarryD.membercarry_code;
|
||
commonCreatePretaskInput.task_type = WmsWareHouseConst.WMS_PRETASK_INSTOCK_TYPE_ID;
|
||
commonCreatePretaskInput.biz_type = WmsWareHouseConst.BIZTYPE_WMSEMPTYINSTOCK_ID;
|
||
commonCreatePretaskInput.isExcuteMission = false;
|
||
commonCreatePretaskInput.moduleConsts = ModuleConsts.MODULE_WMSEMPTYINSTOCK_ID;
|
||
|
||
VisualDevModelDataCrInput visualDevInput = new VisualDevModelDataCrInput();
|
||
visualDevInput.data = new Dictionary<string, object>();
|
||
visualDevInput.data.Add("bill_code", "");
|
||
visualDevInput.data.Add("biz_type", WmsWareHouseConst.BIZTYPE_WMSEMPTYINSTOCK_ID);
|
||
visualDevInput.data.Add("carry_code", wmsCarryD.membercarry_code);
|
||
visualDevInput.data.Add("carry_id", wmsCarryD.membercarry_id);
|
||
//visualDevInput.data.Add("create_id", "");
|
||
visualDevInput.data.Add("create_time", "");
|
||
visualDevInput.data.Add("flowId", "");
|
||
visualDevInput.data.Add("id", "");
|
||
visualDevInput.data.Add("location_id", startLocation.id);
|
||
visualDevInput.data.Add("modify_id", "");
|
||
visualDevInput.data.Add("modify_time", "");
|
||
//visualDevInput.data.Add("org_id", "");
|
||
visualDevInput.data.Add("status", WmsWareHouseConst.BILLSTATUS_ADD_ID);
|
||
visualDevInput.data.Add("warehouse_id", WmsWareHouseConst.WAREHOUSE_ZC_ID);
|
||
commonCreatePretaskInput.visualDevInput = visualDevInput;
|
||
|
||
Logger.LogInformation($"【送满托到下升降区】 开始生成一楼CTU预任务 起点{startLocation.location_code} 终点{endLocation.location_code} 料箱 {wmsCarryD.membercarry_code}");
|
||
|
||
await _db.Updateable<BasLocation>().SetColumns(it => it.is_lock == 1).Where(r => r.id == endLocations[0].id).ExecuteCommandAsync();
|
||
await _db.Updateable<BasLocation>().SetColumns(it => it.task_nums == it.task_nums + 1).Where(r => r.id == startLocation.id).ExecuteCommandAsync();
|
||
|
||
Entities.Dto.Outputs.Result res = await CommonCreatePretask(commonCreatePretaskInput);
|
||
if (res.code == JNPF.Common.Enums.HttpStatusCode.OK)
|
||
{
|
||
}
|
||
else
|
||
{
|
||
Logger.LogInformation($"【二楼kiva把料架送到下升降机后生成中储仓入库任务】 未成功生成预任务 起点{startLocation.location_code} 终点{endLocation.location_code} 料箱 {wmsCarryD.membercarry_code}");
|
||
}
|
||
|
||
index++;
|
||
|
||
s_floor2CreatePretask.Release();
|
||
}
|
||
return true;
|
||
}
|
||
catch(Exception ex)
|
||
{
|
||
Logger.LogInformation($"【二楼kiva把料架送到下升降机后生成中储仓入库任务】:{ex}");
|
||
s_floor2CreatePretask.Release();
|
||
return false;
|
||
}
|
||
finally
|
||
{
|
||
s_floor2CreatePretask.Release();
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
|
||
#region 通用接口
|
||
|
||
/// <summary>
|
||
/// 生成预任务接口 CTU需要传task_type
|
||
/// </summary>
|
||
/// <param name="input"></param>
|
||
/// <returns></returns>
|
||
[HttpPost, NonUnify, AllowAnonymous]
|
||
public async Task<Tnb.WarehouseMgr.Entities.Dto.Outputs.Result> CommonCreatePretask(CommonCreatePretaskInput input, ISqlSugarClient dbConn = null)
|
||
{
|
||
var db = _db;
|
||
if (dbConn != null)
|
||
db = dbConn;
|
||
|
||
|
||
{
|
||
await s_taskCommonCreatePretask.WaitAsync();
|
||
Logger.LogInformation($"【createPretask】 接收到请求 参数:{JsonConvert.SerializeObject(input)}");
|
||
try
|
||
{
|
||
|
||
WmsPointH? sPoint = null;
|
||
WmsPointH? ePoint = null;
|
||
|
||
if (!string.IsNullOrEmpty(input.startlocation_id))
|
||
{
|
||
sPoint = await db.Queryable<WmsPointH>().FirstAsync(it => it.location_id == input.startlocation_id);
|
||
}
|
||
if (!string.IsNullOrEmpty(input.endlocation_id))
|
||
{
|
||
ePoint = await db.Queryable<WmsPointH>().FirstAsync(it => it.location_id == input.endlocation_id);
|
||
}
|
||
|
||
if (sPoint == null)
|
||
{
|
||
throw new Exception($"库位{input.startlocation_id} 的起点点位未维护");
|
||
}
|
||
if (ePoint == null)
|
||
{
|
||
throw new Exception($"库位{input.endlocation_id} 的终点点位未维护");
|
||
}
|
||
|
||
|
||
List<WmsPointH> points = new List<WmsPointH>();
|
||
if (sPoint.area_code != ePoint.area_code)
|
||
{
|
||
points = await PathAlgorithms(sPoint.id, ePoint.id);
|
||
if (points.Count <= 2)
|
||
{
|
||
throw new AppFriendlyException($"sPoint {sPoint.point_code} ePoint{ePoint.point_code}该路径不存在", 500);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
points.Add(sPoint);
|
||
points.Add(ePoint);
|
||
}
|
||
|
||
//根据获取的路径点生成预任务,生成顺序必须预路径算法返回的起终点的顺序一致(预任务顺序)
|
||
WmsPretaskH preTask = null;
|
||
string bill_code = await _billRullService.GetBillNumber(WmsWareHouseConst.WMS_PRETASK_H_ENCODE);
|
||
|
||
string carry_id = "";
|
||
string carry_code = "";
|
||
WmsCarryH wmsCarryH = null;
|
||
|
||
|
||
if (!string.IsNullOrEmpty(input.carry_id) && !string.IsNullOrEmpty(input.carry_code))
|
||
{
|
||
carry_id = input.carry_id;
|
||
carry_code = input.carry_code;
|
||
}
|
||
// 如果指定了carry_id
|
||
else if (!string.IsNullOrEmpty(input.carry_id))
|
||
{
|
||
wmsCarryH = db.Queryable<WmsCarryH>().Where(r => r.id == input.carry_id).First();
|
||
carry_id = wmsCarryH.id;
|
||
carry_code = wmsCarryH.carry_code;
|
||
}
|
||
// carry_code
|
||
else if (!string.IsNullOrEmpty(input.carry_code))
|
||
{
|
||
wmsCarryH = db.Queryable<WmsCarryH>().Where(r => r.carry_code == input.carry_code).First();
|
||
carry_id = wmsCarryH.id;
|
||
carry_code = wmsCarryH.carry_code;
|
||
}
|
||
|
||
|
||
if (input.visualDevInput != null)
|
||
{
|
||
//在线开发moduleConsts
|
||
VisualDevEntity? templateEntity = await _visualDevService.GetInfoById(input.moduleConsts, true);
|
||
await _runService.Create(templateEntity, input.visualDevInput);
|
||
input.require_id = input.visualDevInput.data["ReturnIdentity"].ToString() ?? "";
|
||
input.require_code = input.visualDevInput.data["bill_code"].ToString() ?? "";
|
||
}
|
||
|
||
|
||
List<WmsPretaskH> preTasks = points.Where(it => !it.location_id.IsNullOrEmpty()).GroupBy(g => g.area_code).Select(it =>
|
||
{
|
||
WmsPointH? sPoint = it.FirstOrDefault();
|
||
WmsPointH? ePoint = it.LastOrDefault();
|
||
|
||
preTask = new()
|
||
{
|
||
org_id = _userManager.User?.OrganizeId,
|
||
startlocation_id = sPoint?.location_id!,
|
||
startlocation_code = sPoint?.location_code!,
|
||
endlocation_id = ePoint?.location_id!,
|
||
endlocation_code = ePoint?.location_code!,
|
||
start_floor = sPoint?.floor.ToString(),
|
||
end_floor = ePoint?.floor.ToString(),
|
||
startpoint_id = sPoint?.id!,
|
||
startpoint_code = sPoint?.point_code!,
|
||
endpoint_id = ePoint?.id!,
|
||
endpoint_code = ePoint?.point_code!,
|
||
bill_code = bill_code,
|
||
status = WmsWareHouseConst.PRETASK_BILL_STATUS_DXF_ID,
|
||
biz_type = !string.IsNullOrEmpty(input.biz_type) ? input.biz_type : "",
|
||
task_type = !string.IsNullOrEmpty(input.task_type) ? input.task_type : "",
|
||
carry_id = carry_id,
|
||
carry_code = carry_code,
|
||
area_id = sPoint?.area_id!,
|
||
area_code = it.Key,
|
||
source_id = input.source_id,
|
||
source_code = input.source_code,
|
||
require_id = input.require_id,
|
||
require_code = input.require_code,
|
||
create_id = _userManager.UserId,
|
||
create_time = DateTime.Now
|
||
};
|
||
|
||
return preTask;
|
||
}).ToList();
|
||
//更新页面
|
||
//赋值签收状态
|
||
|
||
Logger.LogInformation($"【CommonCreatePretask】 开始执行 GenPreTask {JsonConvert.SerializeObject(preTasks)}");
|
||
|
||
bool result = await GenPreTask(preTasks, null!, db);
|
||
Logger.LogInformation($"【CommonCreatePretask】 GenPreTask 结果 {result}");
|
||
if (result)
|
||
{
|
||
GenPreTaskUpInput preTaskUpInput = new()
|
||
{
|
||
RquireId = input.require_id,
|
||
CarryId = carry_id,
|
||
CarryStartLocationId = points.FirstOrDefault()!.location_id!,
|
||
CarryStartLocationCode = points.FirstOrDefault()!.location_code!,
|
||
LocationIds = points.Select(x => x.location_id).ToList()!
|
||
};
|
||
|
||
//根据载具移入Id,回更单据状态
|
||
_ = await db.Updateable<WmsMoveInstock>().SetColumns(it => new WmsMoveInstock { status = WmsWareHouseConst.BILLSTATUS_ON_ID }).Where(it => it.id == preTaskUpInput.RquireId).ExecuteCommandAsync();
|
||
|
||
|
||
Expression<Func<WmsCarryH, WmsCarryH>> wmsCarryHChangeExp = a => new WmsCarryH { is_lock = 1 };
|
||
|
||
if (input.isChangeCarryLoc2StartLoc)
|
||
{
|
||
wmsCarryHChangeExp = a => new WmsCarryH { is_lock = 1, location_id = preTaskUpInput.CarryStartLocationId, location_code = preTaskUpInput.CarryStartLocationCode };
|
||
}
|
||
|
||
await GenInStockTaskHandleAfter(preTaskUpInput,
|
||
wmsCarryHChangeExp,
|
||
it => new BasLocation { is_lock = 1 }, db);
|
||
Logger.LogInformation($"【CommonCreatePretask】 成功生成预任务:{preTasks.First().bill_code}");
|
||
return await ToApiResult(JNPF.Common.Enums.HttpStatusCode.OK, "成功");
|
||
}
|
||
else
|
||
{
|
||
throw new AppFriendlyException("生成预任务失败(可能是因为预任务起点与终点相同)", 500);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.LogInformation($"【CommonCreatePretask】 {ex.Message}");
|
||
Logger.LogInformation($"【CommonCreatePretask】 {ex.StackTrace}");
|
||
if (!db.Ado.Transaction.IsNull())
|
||
await db.Ado.CommitTranAsync();
|
||
return await ToApiResult(JNPF.Common.Enums.HttpStatusCode.InternalServerError, ex.Message);
|
||
}
|
||
finally
|
||
{
|
||
s_taskCommonCreatePretask.Release();
|
||
if (input.isExcuteMission)
|
||
{
|
||
GenTaskExecute();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
}
|
||
}
|