Files
tnb.server/WarehouseMgr/Tnb.WarehouseMgr/WareHouseService.cs
yang.lee 0dd220ee4f 1、库位导入代码调整
2、电梯通信代码调整
2023-12-11 13:52:54 +08:00

1339 lines
67 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.Diagnostics;
using System.Dynamic;
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.Systems.Interfaces.System;
using Mapster;
using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using NPOI.OpenXmlFormats.Dml;
using SqlSugar;
using Tnb.BasicData.Entities;
using Tnb.Common.Extension;
using Tnb.Common.Utils;
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.Entity;
using Tnb.WarehouseMgr.Entities.Enums;
using Tnb.WarehouseMgr.Interfaces;
namespace Tnb.WarehouseMgr
{
/// <summary>
/// 库房业务类(出入库)
/// </summary>
public class WareHouseService : ServiceLoggerBase<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 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);
public Func<string, int, Task> AddUnExecuteTask { get; set; }
public WareHouseService(ISqlSugarRepository<WmsInstockH> repository, IDictionaryDataService dictionaryDataService,
IBillRullService billRullService, IUserManager userManager, ICacheManager cacheManager, IElevatorControlService elevatorControlService)
//: base(repository.AsSugarClient())
{
_db = repository.AsSugarClient();
_dictionaryDataService = dictionaryDataService;
_billRullService = billRullService;
_userManager = userManager;
_cacheManager = cacheManager;
_elevatorControlService = elevatorControlService;
_ = InitializationTask;
}
/// <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<WmsCarryH>> OutStockStrategy([FromQuery] OutStockStrategyQuery input)
{
Expressionable<WmsCarryH, WmsCarryCode, BasLocation> 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.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>
/// <returns></returns>
[HttpPost]
public async Task GenTaskExecute()
{
await s_taskExecuteSemaphore.WaitAsync();
Stopwatch sw = Stopwatch.StartNew();
CancellationTokenSource agvCts = new();
SqlSugarClient db = _db.CopyNew();
try
{
//获取电梯数据
List<WmsElevatorH> elevatorList = await db.Queryable<WmsElevatorH>().InnerJoin<WmsElevatorD>((a, b) => a.id == b.bill_id)
.Where((a, b) => a.enabled == 1)
.Select((a, b) => new WmsElevatorH
{
bill_id = b.bill_id,
location_id = b.location_id,
location_code = b.location_code,
point_id = b.point_id,
point_code = b.point_code,
floor = b.floor
}, true).ToListAsync();
//获取所有未下发的预任务申请
List<WmsPretaskH> 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)
.InnerJoin<BasLocation>((a, b, c, d) => a.endlocation_id == d.id && d.is_use == "0")
.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, d) => new WmsPretaskH
{
move_num = c.move_num,
third_eqp_type = c.third_eqp_type,
}, true)
.ToListAsync();
List<WmsPretaskH> agvElevatorTasks = preTasks
.Where(it => it.endlocation_code.StartsWith("DT", StringComparison.OrdinalIgnoreCase) &&
!it.area_code.Contains("ELE", StringComparison.OrdinalIgnoreCase))
.ToList();
//it.area_code.Contains("ELE", StringComparison.OrdinalIgnoreCase)
var elePreTasks = preTasks.Where(it => it.area_code.Contains("ELE", StringComparison.OrdinalIgnoreCase)).ToList();
var normalPreTasks = preTasks.Where(it => !agvElevatorTasks.Concat(elePreTasks).Select(x => x.endlocation_code).Contains(it.endlocation_code)).ToList();
IEnumerable<WmsPretaskH?> firstEleGrp = agvElevatorTasks.GroupBy(g => g.endlocation_code).Select(t => t.OrderBy(o => o.bill_code).FirstOrDefault());
//agvElevatorTasks = agvElevatorTasks.FindAll(x => firstEleGrp.Select(y => y.endlocation_code).Contains(x.endlocation_code)).DistinctBy(x=>x.endlocation_code).ToList();
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())
{
agvElevatorTasks = agvElevatorTasks.Where(x => !equalEndLocPreTasks.Contains(x.endlocation_code)).ToList();
}
preTasks = normalPreTasks.Concat(agvElevatorTasks).Concat(elePreTasks).ToList();
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].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}";
}
}
}
}
}
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();
List<string> endPointIds = disTasks.Where(t => t.area_code.StartsWith("ELE", StringComparison.OrdinalIgnoreCase)).Select(t => t.endpoint_id).ToList();
Logger.Information($"endPointIds:{string.Join(",", endPointIds)}");
if (endPointIds?.Count > 0)
{
elevatorList = elevatorList.FindAll(x => endPointIds.Contains(x.point_id));
if (elevatorList?.Count > 0)
{
foreach (WmsElevatorH? e in elevatorList)
{
WmsDistaskH? disTask = disTasks.Find(x => x.endpoint_id == e.point_id);
if (disTask != null)
{
disTask.device_id = e.elevator_id;
}
}
}
}
int row = await db.Insertable(disTasks).ExecuteCommandAsync();
if (preTaskCodes?.Count > 0)
{
row = await db.Insertable(distaskCodes).ExecuteCommandAsync();
}
if (row > 0)
{
List<string> 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();
}
await db.Ado.CommitTranAsync();
Logger.Information("预任务执行完成");
if (string.Equals(_eleCtlCfg.Environment, ElevatorConsts.EnvironmentName, StringComparison.OrdinalIgnoreCase))
{
//呼梯操作
//获取目标库位为电梯库位的任务
var agvDTTasks = disTasks.Where(it => it.endlocation_code.StartsWith("DT", StringComparison.OrdinalIgnoreCase) &&
!it.area_code.Contains("ELE", StringComparison.OrdinalIgnoreCase)).ToList();
foreach (var at in agvDTTasks)
{
var ele = elevatorList.Find(x => x.location_code == at.endlocation_code);
Logger.Information($"ele.elevator_id:{ele?.elevator_id},elevator_code:{ele?.elevator_code}");
if (ele != null)
{
at.device_id = ele.elevator_id;
}
}
List<(string endlocation_code, string device_id, string id, int start_floor)> endLocCodes = agvDTTasks
.Select(it => (it.endlocation_code, it.device_id, it.id, it.start_floor)).ToList();
if (endLocCodes?.Count > 0)
{
if (endLocCodes.Select(x => x.device_id).All(x => !x.IsNullOrWhiteSpace()))
{
Logger.Information("呼梯操作");
_ = CallingLanding(endLocCodes);
}
else
{
Logger.Error("呼梯失败没有设备ID");
return;
}
}
//执行电梯任务
List<WmsDistaskH>? elevatorTasks = disTasks.Where(it => it.area_code.Contains("ELE", StringComparison.OrdinalIgnoreCase)).ToList();
if (elevatorTasks?.Count > 0)
{
Logger.Information($"当前电梯任务数:{elevatorTasks?.Count ?? 0}");
foreach (WmsDistaskH? elevatorTask in elevatorTasks)
{
_ = ExecuteTargetFloorTask(elevatorTask);
}
}
}
//调用AGV创建任务链接口
List<WmsDistaskH> agvTasks = disTasks.Where(it => !it.area_code.Contains("ELE", StringComparison.OrdinalIgnoreCase)).ToList();
if (agvTasks?.Count > 0)
{
Logger.Information($"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("生成预任务执行时出现错误", ex);
Logger.Error(ex.StackTrace!);
await db.Ado.RollbackTranAsync();
throw;
}
finally
{
_ = s_taskExecuteSemaphore.Release();
agvCts.Dispose();
}
}
/// <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($" 开始呼梯操作.............");
try
{
foreach ((_, string devId, string disTaskId, int floorNO) in endLocCodes)
{
Logger.Information($"devId:{devId}");
if (!s_elevatorMap.TryGetValue(devId, out object? elevatorCode))
{
continue;
}
string? devName = elevatorCode.ToString();
Logger.Information($"电梯编号:{devName}");
Logger.Information($"当前:{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);
if (!_elevatorAgvCtlStatusMap.TryGetValue(devId, out int agvCtlStatus) || agvCtlStatus != (int)EnumAgvStatus.AGV运行状态)
{
Logger.Information("获取电梯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;
}
Logger.Information($"任务开始目标楼层为:{floorNO}");
int floorN = await GetRealFloor(floorNO);
//如果电梯在当前楼层则不呼梯
if (floorN == eleStatusMulti.curFloorNo)
{
Logger.Information($"{devName.Match(@"\d+")}#,在当前楼层,无需呼梯");
continue;
}
Logger.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
};
List<WmsElevatorUnexecute> elevatorQueue = await _db.Queryable<WmsElevatorUnexecute>().Where(it => it.distask_id == disTaskId && it.task_status == "执行中").ToListAsync();
if ((elevatorQueue.IsNull() || elevatorQueue.Count < 1) && floorN != eleStatusMulti.curFloorNo)
{
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}");
}
//如果当前电梯有任务在做,将当前呼梯任务放入待执行队列
_ = await _db.Insertable(elevatorQueueItem).ExecuteCommandAsync();
Logger.Information("呼梯任务执行完成");
}
}
catch (Exception ex)
{
Logger.Error("呼梯操作错误", ex);
Logger.Error($"呼梯操作错误堆栈跟踪:{Environment.NewLine}{ex.StackTrace}");
throw;
}
}
/// <summary>
/// 执行到目标楼层电梯任务
/// </summary>
/// <param name="disTask"></param>
/// <returns></returns>
private async Task ExecuteTargetFloorTask(WmsDistaskH disTask)
{
//收到放货确认通知向电梯发送到3楼的指令
Logger.Information($"开始执行电梯任务任务ID:{disTask.id}");
try
{
if (!s_elevatorMap.TryGetValue(disTask.device_id, out object? elevatorCode))
{
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);
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);
Logger.Information($"目标楼层:{floor}");
//发送到目标楼的指令
dynamic reuslt = await _elevatorControlService.WriteTagAsync(devName, ElevatorConsts.FloorExecute, floor);
//电梯任务手动执行任务状态上报
(int sysStatus, int runStatus, int floorNo, int doorStatus, int agvStatus) tuple = (-1, -1, -1, -1, -1);
do
{
tuple = await _elevatorControlService.GetElevatorStatus(devName, tags, CancellationToken.None);
await Task.Delay(1000);
} while (tuple.sysStatus != 3 && tuple.runStatus != 0);
Logger.Information($"sysStatus:{tuple.sysStatus},runStatus:{tuple.runStatus},floorNo:{tuple.floorNo},disTask.end_floor={disTask.end_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("Agv任务执行....");
//调用AGV创建任务链接口
try
{
AgvRequestConfig requestCfg = App.Configuration.Build<AgvRequestConfig>();
string 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,
}));
Logger.Information($"请求地址:{url}");
foreach ((string k, object v) in taskChainCodeDic)
{
dynamic reqBody = new ExpandoObject();
reqBody.taskChainCode = k;
reqBody.type = (int)EnumTaskChainType.AGV;
reqBody.sequential = false;
reqBody.taskChainPriority = 0;
reqBody.taskList = v;
Logger.Information($"请求参数:{JsonConvert.SerializeObject(reqBody)}");
dynamic respBody = await HttpClientHelper.PostStreamAsync(url, reqBody, token);
Logger.Information($"调用Agv接口响应结果:{respBody}");
}
}
catch (Exception ex)
{
Logger.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++)
{
_ = 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
{
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();
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();
//更新电梯任务数量
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();
//更新载具,锁定状态为未锁定,更新载具的库位当前任务的目标库位
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);
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
};
string endLocId = multiList[i].endlocation_id;
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 = 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();
/* var loginType= _userManager?.LoginType ?? "web";
Log.Information($"_userManager.LoginType={loginType}");
*/ //更新业务主表的单据状态
foreach (WmsDistaskH? dt in disTasks)
{
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!, distaskCodes = disTaskCodes, carryIds = disTasks.Select(x => x.carry_id).ToList() };
/*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);
}
}
}
await _db.Ado.CommitTranAsync();
Logger.Information("任务操作完成");
}
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)
{
//如果预任务出现起终库位相同,则删除对应预任务
//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();
}
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 (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();
}
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)
{
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>().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)
{
#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;
}
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;
}
#endregion
}
}