Files
tnb.server/WarehouseMgr/Tnb.WarehouseMgr/WareHouseService.cs

1080 lines
52 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Dynamic;
using System.Linq;
using System.Linq.Expressions;
using JNPF;
using JNPF.Common.Contracts;
using JNPF.Common.Core.Manager;
using JNPF.Common.Enums;
using JNPF.Common.Extension;
using JNPF.Common.Manager;
using JNPF.Common.Security;
using JNPF.FriendlyException;
using JNPF.Logging;
using JNPF.Systems.Interfaces.System;
using Mapster;
using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
//using NPOI.SS.Formula.Functions;
using SqlSugar;
using Tnb.BasicData.Entities;
using Tnb.Common.Extension;
using Tnb.Common.Utils;
using Tnb.WarehouseMgr.Entities;
using Tnb.WarehouseMgr.Entities.Attributes;
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.Entity;
using Tnb.WarehouseMgr.Entities.Enums;
using Tnb.WarehouseMgr.Interfaces;
namespace Tnb.WarehouseMgr
{
/// <summary>
/// 库房业务类(出入库)
/// </summary>
public class WareHouseService : BaseWareHouseService, 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 static Dictionary<string, object> _elevatorMap = new Dictionary<string, object>();
private readonly ElevatorControlConfiguration _eleCtlCfg = App.Configuration.Build<ElevatorControlConfiguration>();
public Func<string, int, Task> AddUnExecuteTask { get; set; }
public WareHouseService(ISqlSugarRepository<WmsInstockH> repository, IDictionaryDataService dictionaryDataService,
IBillRullService billRullService, IUserManager userManager, ICacheManager cacheManager, IElevatorControlService elevatorControlService)
{
_db = repository.AsSugarClient();
_dictionaryDataService = dictionaryDataService;
_billRullService = billRullService;
_userManager = userManager;
_cacheManager = cacheManager;
_elevatorControlService = elevatorControlService;
AddUnExecuteTask = async (code, floor) =>
{
await _elevatorControlService.CallLift(code, floor, CancellationToken.None);
};
}
/// <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,
carry_name = a.carry_name,
location_id = b.id,
location_name = 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()
{
var instockD = input.Adapt<TStockD>();
var 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!);
}
var 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 ArgumentOutOfRangeException(nameof(input.inoutStockType), $"Not expected EnumInOutStockType value: {input.inoutStockType}"),
};
return result ?? Enumerable.Empty<dynamic>();
}
/// <summary>
/// 入库策略
/// </summary>
/// <returns></returns>
[HttpGet]
public async Task<List<BasLocation>> InStockStrategy([FromQuery] InStockStrategyQuery input)
{
var items = new List<BasLocation>();
try
{
var policy = await _db.Queryable<WmsInstockPolicies>().Where(it => it.status == 1).FirstAsync();
if (policy == null) throw new AppFriendlyException("没有可用的策略", 500);
var 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.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<WmsCarryH>> OutStockStrategy([FromQuery] OutStockStrategyQuery input)
{
var whereExprable = Expressionable.Create<WmsCarryH, WmsCarryCode, BasLocation>()
.And((a, b, c) => a.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.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);
var whereExpr = whereExprable.ToExpression();
var cyDb = _db.CopyNew();
var policy = await cyDb.Queryable<WmsInstockPolicies>().Where(it => it.status == 1).FirstAsync();
if (policy == null) throw new AppFriendlyException("没有可用策略", 500);
var 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)
.OrderBy(policy.policy)
.Select<WmsCarryH>()
.ToListAsync();
return input.Size > 0 ? items.Take(input.Size).ToList() : items;
}
/// <summary>
/// 生成任务执行
/// </summary>
/// <returns></returns>
[HttpPost, Timed(Name = nameof(IWareHouseService.GenTaskExecute))]
public async Task GenTaskExecute(CancellationToken? ct = default)
{
Stopwatch sw = Stopwatch.StartNew();
CancellationTokenSource agvCts = new();
var db = _db.CopyNew();
try
{
//if (_elevatorMap.Count < 1)
//{
// _elevatorMap = await _db.Queryable<WmsElevatorH>().ToDictionaryAsync(x => x.elevator_id, x => x.elevator_code);
//}
//获取所有未下发的预任务申请
var preTasks = await db.Queryable<WmsPretaskH>().InnerJoin<WmsCarryH>((a, b) => a.startlocation_id == b.location_id && a.carry_id == b.id)
.InnerJoin<WmsAreaH>((a, b, c) => a.area_id == c.id)
.Where(a => a.status == WmsWareHouseConst.PRETASK_BILL_STATUS_DXF_ID && !string.IsNullOrWhiteSpace(a.startlocation_id))
.OrderBy(a => new { priority = SqlFunc.Desc(a.priority), a.bill_code })
.Select((a, b, c) => new WmsPretaskH
{
move_num = c.move_num
}, true)
.ToListAsync();
var ids = preTasks.Select(x => x.id).Distinct().ToList();
var preTaskCodes = await db.Queryable<WmsPretaskCode>().Where(it => ids.Contains(it.bill_id)).ToListAsync();
if (preTasks.Count > 0)
{
//根据预任务管理区分组,获取到所有分组后的预任务,遍历每个预任务 是否为任务链通过管理区ID
var preTaskGroups = preTasks.GroupBy(g => g.area_code).ToList();
List<WmsDistaskH> disTasks = new();
List<WmsDistaskCode> distaskCodes = new();
foreach (var itGroup in preTaskGroups)
{
var items = itGroup.Adapt<List<WmsDistaskH>>();
for (int i = 0, cnt = items.Count; i < cnt; i++)
{
items[i].id = SnowflakeIdHelper.NextId();
items[i].status = WmsWareHouseConst.TASK_BILL_STATUS_DZX_ID;
}
var moveNum = itGroup.First().move_num;
var itemsCount = items.Count;
var mod = itemsCount % moveNum > 0 ? itemsCount / moveNum + 1 : itemsCount / moveNum;
var arrary = items.ToArray();
for (int i = 1; i <= mod; i++)
{
var groupCode = await _billRullService.GetBillNumber(WmsWareHouseConst.WMS_TASK_EXECUTE_ENCODE);
if (moveNum >= 1)
{
var areaPreTasks = itGroup.ToList();
if (moveNum == 1 || (moveNum > areaPreTasks.Count && areaPreTasks.Count == 1))
{
items.ForEach(x =>
{
x.is_chain = 0;
});
items[0].groups = groupCode;
items[0].bill_code = $"{groupCode}-1";
}
else if ((moveNum >= areaPreTasks.Count && areaPreTasks.Count > 1) || moveNum < areaPreTasks.Count)
{
items.ForEach(x => x.is_chain = 1);
var start = 0;
var end = Math.Min(itemsCount, moveNum);
var arrList = new List<WmsDistaskH[]>(mod);
while (start < itemsCount)
{
var subArray = arrary[start..end];
arrList.Add(subArray);
start = end;
end = Math.Min((end + moveNum), arrary.Length);
}
foreach (var 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}";
}
}
}
}
}
if (preTaskCodes?.Count > 0)
{
foreach (var disTask in items)
{
var curPreTaskCodes = preTaskCodes.FindAll(x => x.bill_id == disTask.pretask_id);
var 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();
//disTasks.ForEach(x => x.id = SnowflakeIdHelper.NextId());
var row = await db.Insertable(disTasks).ExecuteCommandAsync();
if (preTaskCodes?.Count > 0)
{
row = await db.Insertable(distaskCodes).ExecuteCommandAsync();
}
if (row > 0)
{
var preTaskIds = preTasks.Select(x => x.id).ToList();
row = await db.Updateable<WmsPretaskH>().SetColumns(it => new WmsPretaskH { status = WmsWareHouseConst.PRETASK_BILL_STATUS_YXF_ID }).Where(it => preTaskIds.Contains(it.id)).ExecuteCommandAsync();
}
sw.Stop();
Log.Information($"程序运行耗时{sw.ElapsedMilliseconds}ms");
await db.Ado.CommitTranAsync();
//呼梯操作
//获取目标库位为电梯库位的任务
var endLocCodes = disTasks
.Where(it => it.endlocation_code.StartsWith("DT", StringComparison.OrdinalIgnoreCase) &&
!it.area_code.Contains("ELE", StringComparison.OrdinalIgnoreCase)
).Select(it => (it.endlocation_code, it.device_id, it.id, it.start_floor)).ToList();
if (endLocCodes?.Count > 0)
{
await CallingLanding(endLocCodes);
}
//执行电梯任务
var elevatorTasks = disTasks.Where(it => it.area_code.Contains("ELE", StringComparison.OrdinalIgnoreCase)).ToList();
Log.Information($"当前电梯任务数:{elevatorTasks?.Count ?? 0}");
Log.Information("准备执行电梯任务");
if (elevatorTasks?.Count > 0)
{
Log.Information("执行电梯任务");
foreach (var elevatorTask in elevatorTasks)
{
await ExecuteTargetFloorTask(elevatorTask);
}
}
////调用AGV创建任务链接口
var agvTasks = disTasks.Where(it => !it.area_code.Contains("ELE", StringComparison.OrdinalIgnoreCase)).ToList();
if (agvTasks?.Count > 0)
{
await AgvDispatch(agvTasks, agvCts.Token);
}
}
}
catch (Exception ex) when (ex is HttpRequestException hReqEx)
{
agvCts.Cancel();
}
catch (Exception ex)
{
Log.Error("生成预任务执行时出现错误", ex);
await db.Ado.RollbackTranAsync();
throw;
}
finally
{
agvCts.Dispose();
}
}
/// <summary>
/// 呼梯操作
/// </summary>
/// <param name="endLocCodes"></param>
/// <returns></returns>
private async Task CallingLanding(List<(string endlocation_code, string device_id, string id, string floorNO)> endLocCodes)
{
Log.Information($"开始呼梯操作.............");
try
{
var item = endLocCodes.FirstOrDefault();
//if (_elevatorMap.ContainsKey(item.device_id))
var devName = _eleCtlCfg.DevName;
{
var agvStatus = await _elevatorControlService.GetTagAsync(devName, ElevatorConsts.AGVControl);
Log.Information($"当前Agv状态:{agvStatus.ToEnum<EnumAgvStatus>().ToString()}");
//判断当前设备是否为运行状态不是则进入Agv电梯控制状态
if (agvStatus.ToEnum<EnumAgvStatus>() != EnumAgvStatus.AGV运行状态)
{
await _elevatorControlService.WriteTagAsync(devName, ElevatorConsts.AGVControl, 1);
}
}
{
(int sysStatus, int runStatus, int curFloorNo, int doorStatus, int agvStatus) = await _elevatorControlService.GetElevatorStatus(devName, CancellationToken.None);
foreach (var (_, devId, disTaskId, floorNO) in endLocCodes)
{
Log.Information($"任务开始目标楼层为:{floorNO}");
var floorN = await GetRealFloor(floorNO.ParseToInt());
Log.Information($"实际目标楼层为:{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
};
var elevatorQueue = await _db.Queryable<WmsElevatorUnexecute>().Where(it => it.elevator_id == devId && it.task_status == "执行中").ToListAsync();
if ((elevatorQueue.IsNull() || elevatorQueue.Count < 1) && floorNO.ParseToInt() != curFloorNo)
{
elevatorQueueItem.task_status = "执行中";
var callLiftRes = await _elevatorControlService.CallLift(devName, floorN, CancellationToken.None);
string successful = "成功", fail = "失败";
var callLiftResult = callLiftRes ? successful : fail;
Log.Information($"呼梯结果:{callLiftResult}");
}
//如果当前电梯有任务在做,将当前呼梯任务放入待执行队列
await _db.Insertable(elevatorQueueItem).ExecuteCommandAsync();
}
}
}
catch (Exception ex)
{
Log.Error("呼梯操作错误", ex);
throw;
}
}
/// <summary>
/// 根据当前目标楼层获取,电梯接口真正的楼层
/// </summary>
/// <param name="floor"></param>
/// <returns></returns>
private Task<int> GetRealFloor(int floor)
{
var realFloor = 0;
if (floor == 4)
{
realFloor = 5;
}
else if (floor == 3)
{
realFloor = 4;
}
else if (floor == 2)
{
realFloor = 3;
}
return Task.FromResult(realFloor);
}
/// <summary>
/// 执行到目标楼层电梯任务
/// </summary>
/// <param name="disTask"></param>
/// <returns></returns>
private async Task ExecuteTargetFloorTask(WmsDistaskH disTask)
{
//收到放货确认通知向电梯发送到3楼的指令
Log.Information($"开始执行电梯任务任务ID:{disTask.id}");
try
{
var elevatorQueueItem = await _db.Queryable<WmsElevatorUnexecute>().FirstAsync(it => it.task_status == "执行中");
if (elevatorQueueItem != null)
{
//var disTask = disTasks.Find(x => x.id == elevatorQueueItem.distask_id);
//if (disTask?.status == WmsWareHouseConst.TASK_BILL_STATUS_COMPLE_ID)
{
var doorStatus = -1;
var closeDoorRes = await _elevatorControlService.SendOpenCloseCmd(elevatorQueueItem.elevator_code, 4); //向电梯发送前门关门指令
Log.Information($"关门结果:{closeDoorRes}");
do
{
doorStatus = await _elevatorControlService.GetTagAsync(elevatorQueueItem.elevator_code, ElevatorConsts.DoorStatus);
} while (doorStatus != 4);
var floor = await GetRealFloor(disTask.end_floor.ParseToInt());
Log.Information($"目标楼层:{floor}");
Log.Information($"当前门状态:{doorStatus}");
//发送到目标楼的指令
var reuslt = await _elevatorControlService.WriteTagAsync(elevatorQueueItem.elevator_code, ElevatorConsts.FloorExecute, floor);
//电梯任务手动执行任务状态上报
(int sysStatus, int runStatus, int floorNo, int doorStatus, int agvStatus) tuple = (-1, -1, -1, -1, -1);
do
{
tuple = await _elevatorControlService.GetElevatorStatus(elevatorQueueItem.elevator_code, CancellationToken.None);
} while (tuple.sysStatus != 3 && tuple.runStatus != 0 && tuple.floorNo != disTask.end_floor.ParseToInt());
Log.Information($"sysStatus:{tuple.sysStatus},runStatus:{tuple.runStatus},floorNo:{tuple.floorNo}");
if (tuple.sysStatus.ToEnum<EnumSysStatus>() == EnumSysStatus. && tuple.runStatus.ToEnum<EnumRunStatus>() == EnumRunStatus.
&& tuple.floorNo == disTask.end_floor.ParseToInt())
{
dynamic input = new ExpandoObject();
input.disTaskIds = new List<string> { disTask.id };
await TaskExecuteAfter(input);
await TaskComplate(input);
}
}
}
}
catch (Exception ex)
{
Log.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)
{
//调用AGV创建任务链接口
try
{
var requestCfg = App.Configuration.Build<AgvRequestConfig>();
var url = requestCfg.AgvRequestUrls.CreateTaskChainUrl;
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,
}));
Log.Information($"请求地址:{url}");
foreach (var (k, v) in taskChainCodeDic)
{
dynamic reqBody = new ExpandoObject();
reqBody.taskChainCode = k;
reqBody.type = (int)EnumTaskChainType.AGV;
reqBody.sequential = false;
reqBody.taskChainPriority = 0;
reqBody.taskList = v;
Log.Information($"请求参数:{JsonConvert.SerializeObject(reqBody)}");
var respBody = await HttpClientHelper.PostStreamAsync(url, reqBody, token);
Log.Information($"调用Agv接口响应结果:{respBody}");
}
}
catch (Exception ex)
{
Log.Error("agv调度失败异常", 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++)
{
if (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();
}
else
{
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();
}
var 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)
{
Log.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 }).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)
{
var 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)
{
var 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)
{
Log.Error("设备取返回操作失败", ex);
await _db.Ado.RollbackTranAsync();
}
}
/// <summary>
/// 任务完成
/// </summary>
/// <returns></returns>
[HttpPost]
public async Task TaskComplate(TaskCompleUpInput input)
{
try
{
await _db.Ado.BeginTranAsync();
//更新任务执行表,单据状态为 完成
await _db.Updateable<WmsDistaskH>().SetColumns(it => new WmsDistaskH { status = WmsWareHouseConst.TASK_BILL_STATUS_COMPLE_ID }).Where(it => input.disTaskIds.Contains(it.id)).ExecuteCommandAsync();
var 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();
if (disTasks?.Count > 0)
{
// 更新预任务申请表,单据状态为 已完成
var 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();
//更新电梯任务数量
var eles = await _db.Queryable<WmsElevatorH>().Where(it => disTasks.Select(x => x.area_code).Contains(it.area_code)).ToListAsync();
if (eles?.Count > 0)
await _db.Updateable(eles).ReSetValue(it => it.task_nums--).ExecuteCommandAsync();
//更新载具,锁定状态为未锁定,更新载具的库位当前任务的目标库位
var multiList = disTasks.Select(it => (it.carry_id, it.carry_status, it.endlocation_id, it.endlocation_code)).ToList();
var locWhIdMap = await _db.Queryable<BasLocation>().Where(it => multiList.Select(x => x.endlocation_id).Contains(it.id)).ToDictionaryAsync(it => it.id, it => it.wh_id);
List<WmsCarryH> carryIts = new();
List<WmsCarryCode> carryCodeIts = new();
List<BasLocation> locIts = new();
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
};
WmsCarryCode carryCode = new()
{
warehouse_id = locWhIdMap[multiList[i].endlocation_id].ToString(),
location_id = multiList[i].endlocation_id,
location_code = multiList[i].endlocation_code
};
carryIts.Add(carry);
carryCodeIts.Add(carryCode);
BasLocation loc = new();
loc.id = multiList[i].endlocation_id;
loc.is_lock = 0;
loc.is_use = multiList[i].carry_status;
if (multiList[i].carry_status.ToEnum<EnumCarryStatus>() == EnumCarryStatus.)
{
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 }).ExecuteCommandAsync();
//更新条码的库位和仓库信息
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();
//更新业务主表的单据状态
foreach (var dt in disTasks)
{
var disTaskCodes = await _db.Queryable<WmsDistaskCode>().Where(it => it.bill_id == dt.id).ToListAsync();
var upInput = new WareHouseUpInput { bizTypeId = dt.biz_type, requireId = dt.require_id!, distaskCodes = disTaskCodes, carryIds = disTasks.Select(x => x.carry_id).ToList() };
upInput.loginType = !_userManager.LoginType.IsNullOrEmpty() ? "app" : "web";
if (dt.is_sign == 1 && dt.chain_type == "3")
{
await DoUpdate(upInput);
}
}
}
await _db.Ado.CommitTranAsync();
}
catch (Exception)
{
await _db.Ado.RollbackTranAsync();
throw;
}
}
/// <summary>
/// 出入库策略启用、禁用状态修改
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost]
public async Task ModifyPoliciesStatus(ModifyPoliciesStatusInput input)
{
async Task _updateStatus<T>(ModifyPoliciesStatusInput 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)
{
//如果预任务出现起终库位相同,则删除对应预任务
//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);
}
var grpList = preTasks.OrderBy(o => o.bill_code).GroupBy(g => g.carry_id).ToList();
if (grpList?.Count > 0)
{
foreach (var grp in grpList)
{
var arr = grp.ToArray();
if (arr.Length > 1)
{
var subArr = arr[..^1];
System.Array.ForEach(subArr, a => a.chain_type = "1");
}
}
}
var 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();
}
return row > 0;
}
/// <summary>
/// 生成预任务后续处理
/// </summary>
/// <returns></returns>
[NonAction]
public async Task GenInStockTaskHandleAfter(GenPreTaskUpInput input, Expression<Func<WmsCarryH, WmsCarryH>> setCarryColumnsExp, Expression<Func<BasLocation, BasLocation>> setLocaionColumbExp)
{
try
{
await _db.Ado.BeginTranAsync();
//根据生成的预任务,插入预任务操作记录
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 (setLocaionColumbExp != null && input.LocationIds?.Count > 0)
{
await _db.Updateable<BasLocation>().SetColumns(setLocaionColumbExp).Where(it => input.LocationIds.Contains(it.id)).ExecuteCommandAsync();
}
await _db.Ado.CommitTranAsync();
}
catch (Exception)
{
await _db.Ado.RollbackTranAsync();
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 roads = await _db.Queryable<WmsRoad>().ToListAsync();
var points = await LocPathCalcAlgorithms(pStartId, pEndId, roads);
try
{
if (points.FindAll(x => x.location_code != null && x.location_code.Contains("ELE"))?.Count > 0)
{
//查询当前电梯点
var curEleDs = await _db.Queryable<WmsElevatorD>().Where(it => points.Select(x => x.id).Contains(it.point_id)).ToListAsync();
//如果有电梯点,则会进行电梯的均匀分配
if (curEleDs?.Count > 0)
{
//当前电梯
var curEle = await _db.Queryable<WmsElevatorH>().SingleAsync(it => it.id == curEleDs.First().bill_id && it.status == 1);
if (curEle == null) throw new AppFriendlyException("电梯被禁用或未配置", 500);
//同电梯组电梯
var sGpEle = await _db.Queryable<WmsElevatorH>().Where(it => it.elevator_group == curEle.elevator_group && it.id != curEle.id && it.status == 1).ToListAsync();
//判断电梯组中各电梯任务数
if (sGpEle.FindAll(x => Math.Abs(x.task_nums - curEle.task_nums) % 2 == 1)?.Count > 0)
{
var sGpDs = await _db.Queryable<WmsElevatorD>().Where(it => it.bill_id == sGpEle.First().id).ToListAsync();
if (sGpDs?.Count > 0)
{
var sGpPoints = await _db.Queryable<WmsPointH>().Where(it => sGpDs.Select(x => x.point_id).Contains(it.id)).ToListAsync();
var sFEndId = sGpDs.Single(x => x.floor == curEleDs.First().floor).point_id;
var eFStartId = sGpDs.Single(x => x.floor == curEleDs.Last().floor).point_id;
var sFPoints = await LocPathCalcAlgorithms(pStartId, sFEndId, roads);
List<WmsPointH> elePoints = new();
foreach (var pt in curEleDs)
{
var elePoint = sGpPoints.Find(x => x.floor == pt.floor);
if (elePoint != null)
elePoints.Add(elePoint);
}
var 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)
{
throw;
}
return points;
}
#region PrivateMethods
private async Task<List<WmsPointH>> LocPathCalcAlgorithms(string pStartId, string pEndId, List<WmsRoad> roads)
{
#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
var points = await _db.Queryable<WmsPointH>().ToListAsync();
var startObj = points.Find(x => x.id == pStartId);
var endObj = points.Find(x => x.id == pEndId);
var sIndex = points.IndexOf(startObj);
var eIndex = points.IndexOf(endObj);
if (eIndex < sIndex)
{
var tempIndex = sIndex;
sIndex = eIndex;
eIndex = tempIndex;
var temp = points[sIndex];
points[sIndex] = points[eIndex];
points[eIndex] = temp;
}
var vexs = points.Select(p => p.id).ToArray();
EData[] edges = new EData[roads.Count];
for (int i = 0; i < edges.Length; i++)
{
var start = roads[i].startpoint_id;
var end = roads[i].endpoint_id;
var 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);
var pointIds = points.Select(p => p.id).ToList();
Stack<string> result = new();
GetPoints(pointIds, prev, result, eIndex);
List<WmsPointH> results = new() { startObj };
if (points?.Count > 0)
{
//points.Where(it => result.Contains(it.id));
foreach (var i in result)
{
WmsPointH? point = points?.Find(x => x.id == i);
if (point != null)
{
results.Add(point);
}
}
}
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)
{
var sRoads = roads.Where(x => x.startpoint_id == pStartId).ToList();
for (int j = 0; j < sRoads.Count; j++)
{
var sPoint = shortestPathPoints.Find(x => x.id == sRoads[j].endpoint_id);
if (sPoint != null && isVisited.ContainsKey(sPoint.id) && !isVisited[sPoint.id] && sPoint.id != pEndId)
{
var 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)
{
var 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()
{
var isOk = false;
try
{
await _db.Ado.BeginTranAsync();
isOk = await _db.Updateable(entity).ExecuteCommandHasChangeAsync();
if (entities?.Count > 0)
{
var 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()
{
var dic = await _dictionaryDataService.GetDictionaryByTypeId(WmsWareHouseConst.WMS_INSTOCK_D_BILL_STATUS_TYPEID);
var 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() : "")
.ToListAsync();
return data;
}
#endregion
}
}