refactor: migrate project structure by reorganizing realization code snippets into documentation and analysis categories.
This commit is contained in:
225
03_Realisierung/ConveyorDispo/StartInitialOrdersHost.cs
Normal file
225
03_Realisierung/ConveyorDispo/StartInitialOrdersHost.cs
Normal file
@@ -0,0 +1,225 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Gebhardt.Shared;
|
||||
using Gebhardt.Shared.DbAccess;
|
||||
using Gebhardt.Shared.Process;
|
||||
using Gebhardt.StoreWare.Wcs.Common;
|
||||
using Gebhardt.StoreWare.Wcs.Common.Application.TransportHandling.Interfaces;
|
||||
using Gebhardt.StoreWare.Wcs.Common.Dao;
|
||||
using Gebhardt.StoreWare.Wcs.Common.Dao.Interfaces;
|
||||
using Gebhardt.StoreWare.Wcs.Common.DbAccess;
|
||||
using Gebhardt.StoreWare.Wcs.Common.DbAccess.Model;
|
||||
using Gebhardt.StoreWare.Wcs.Common.DbAccess.Model.Enums;
|
||||
using Gebhardt.StoreWare.Wcs.Common.DbAccess.Queries;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using static Gebhardt.StoreWare.Wcs.Common.Constants;
|
||||
|
||||
namespace Gebhardt.StoreWare.Wcs.ConveyorDispo
|
||||
{
|
||||
public class StartInitialOrdersHost : ProcessWorker
|
||||
{
|
||||
private readonly IWcsDbContextFactory _dbContextFactory;
|
||||
private readonly ITransportOrderService _transportOrderService;
|
||||
|
||||
public StartInitialOrdersHost(int workInterval, ITransportOrderService transportOrderService, IWcsDbContextFactory dbContextFactory) : base(nameof(StartInitialOrdersHost), workInterval, true)
|
||||
{
|
||||
_transportOrderService = transportOrderService;
|
||||
_dbContextFactory = dbContextFactory;
|
||||
}
|
||||
|
||||
public override bool DoWork()
|
||||
{
|
||||
try
|
||||
{
|
||||
using IWcsDbContext db = _dbContextFactory.GetDbContext();
|
||||
OrderList initialOrders = new(db.OrdersHost
|
||||
.ByStatus(TransportOrderStatus.Initial)
|
||||
.ExcludeNextEmpty()
|
||||
.Where(o => o.Le.Status != LeStatus.Created)
|
||||
.ApplyWmsOrderingSequencerRetrievalTime(db)
|
||||
.Select(o => new OrderListItem(o.Id, o.Status, o.Le, o.Destination, o.Priority, o.IdOrderWmsHead, o.Created, o.HostDestination, o.DepartureFlag, o.DepartureLocation))
|
||||
.ToList());
|
||||
//This step is needed to only start orders with all LEs available
|
||||
initialOrders = OnlyFirstOrderPerLeNo(db, initialOrders);
|
||||
|
||||
return StartOrders(initialOrders);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.WriteException(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns only the first order per LE
|
||||
/// </summary>
|
||||
/// <param name="initialOrders"></param>
|
||||
/// <returns>Filtered List</returns>
|
||||
private OrderList OnlyFirstOrderPerLeNo(IWcsDbContext db, OrderList initialOrders)
|
||||
{
|
||||
List<OrderListItem> distinctOrderList = new ();
|
||||
List<OrderListItem> lockedOrders = new();
|
||||
|
||||
foreach (OrderListItem order in initialOrders)
|
||||
{
|
||||
//Only take first order per LE
|
||||
if (!distinctOrderList.Any(d=>d.Le.LeNo == order.Le.LeNo))
|
||||
{
|
||||
distinctOrderList.Add(order);
|
||||
}
|
||||
//Others are marked as locked (for info message)
|
||||
else
|
||||
{
|
||||
lockedOrders.Add(order);
|
||||
Log.Write(LogLevel.Info, 60, $"OrdersHost ID {order.OrdersHostId} for LeNo {order.Le.LeNo} waits for OrdersHost: ID {distinctOrderList.Where(d => d.Le.LeNo == order.Le.LeNo).First().OrdersHostId} which is started first.");
|
||||
}
|
||||
}
|
||||
//Make visible in DB (and to user)
|
||||
string infoMessage = "Le has transports that are started first.";
|
||||
var markOrders = db.OrdersHost.Where(o => lockedOrders.Select(l => l.OrdersHostId).Contains(o.Id) && o.Info != infoMessage).ToList();
|
||||
foreach (var order in markOrders)
|
||||
{
|
||||
order.Info = infoMessage;
|
||||
}
|
||||
//Rest Info if order can be started
|
||||
var unMarkOrders = db.OrdersHost.Where(o => distinctOrderList.Select(l => l.OrdersHostId).Contains(o.Id) && o.Info == infoMessage).ToList();
|
||||
foreach (var order in unMarkOrders)
|
||||
{
|
||||
order.Info = null;
|
||||
}
|
||||
db.SaveChanges();
|
||||
|
||||
return new OrderList(distinctOrderList);
|
||||
}
|
||||
|
||||
private bool StartOrders(OrderList orderList)
|
||||
{
|
||||
bool workDone = false;
|
||||
|
||||
using IWcsDbContext db = _dbContextFactory.GetDbContext();
|
||||
for (int i = 0; i < orderList.Count; i++)
|
||||
{
|
||||
OrderListItem orderToBeScheduled = orderList[i];
|
||||
|
||||
try
|
||||
{
|
||||
// If there exists a cancelled sequencer orders for this LE this should always be started first
|
||||
var cancelledSequencerOrder = db.OrdersHost
|
||||
.ByLeNo(orderToBeScheduled.Le.LeNo)
|
||||
.ByStatus(TransportOrderStatus.Initial)
|
||||
.ByCancelledSequencerOrder()
|
||||
.Select(o => new OrderListItem(o.Id, o.Status, o.Le, o.Destination, o.Priority, o.IdOrderWmsHead, o.Created, o.HostDestination, o.DepartureFlag, o.DepartureLocation))
|
||||
.ToList();
|
||||
|
||||
if (cancelledSequencerOrder.FirstOrDefault() != null)
|
||||
{
|
||||
var cancelledOrderToBeScheduled = cancelledSequencerOrder.FirstOrDefault();
|
||||
Log.Write(LogLevel.Debug, $"{nameof(OrdersHost)} with Id {cancelledOrderToBeScheduled.OrdersHostId} will be scheduled since this is a cancelled sequencer order.");
|
||||
_transportOrderService.ScheduleOrdersHost(cancelledOrderToBeScheduled.OrdersHostId);
|
||||
//Order has been scheduled. Do not consider orders for the same LE.
|
||||
orderList.RemoveSubsequentWithEqualLeNo(orderToBeScheduled);
|
||||
workDone = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If there exists an order that has been flagged by HostBooking for departure
|
||||
var departureFlaggedOrder = db.OrdersHost
|
||||
.ByLeNo(orderToBeScheduled.Le.LeNo)
|
||||
.ByStatus(TransportOrderStatus.Initial)
|
||||
.IsDepartureReady()
|
||||
.Select(o => new OrderListItem(o.Id, o.Status, o.Le, o.Destination, o.Priority,
|
||||
o.IdOrderWmsHead, o.Created, o.HostDestination, o.DepartureFlag, o.DepartureLocation))
|
||||
.ToList();
|
||||
|
||||
if (departureFlaggedOrder.FirstOrDefault() != null)
|
||||
{
|
||||
var departureOrderToBeScheduled = departureFlaggedOrder.FirstOrDefault();
|
||||
Log.Write(LogLevel.Debug, $"{nameof(OrdersHost)} with Id {departureOrderToBeScheduled.OrdersHostId} will be scheduled since it has been flagged for departure");
|
||||
_transportOrderService.ScheduleOrdersHost(departureOrderToBeScheduled.OrdersHostId);
|
||||
//Order has been scheduled. Do not consider orders for the same LE.
|
||||
orderList.RemoveSubsequentWithEqualLeNo(orderToBeScheduled);
|
||||
workDone = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
OrdersHost orderForSameLe = db.OrdersHost
|
||||
.ByLeNo(orderToBeScheduled.Le.LeNo)
|
||||
.ByStatus(TransportOrderStatus.Pending, TransportOrderStatus.InProgress, TransportOrderStatus.InDestinationZone, TransportOrderStatus.InSequencer)
|
||||
.OrderBy(o => o.Status != TransportOrderStatus.Pending ? 0 : 1) // get active order (if any), pending otherwise
|
||||
.FirstOrDefault();
|
||||
|
||||
if (orderForSameLe != null && orderForSameLe.Id != orderToBeScheduled.OrdersHostId)
|
||||
{
|
||||
workDone = TryScheduleWithExistingOrder(orderForSameLe, db, orderToBeScheduled);
|
||||
|
||||
//Check equal LEs with open order only once
|
||||
orderList.RemoveSubsequentWithEqualLeNo(orderToBeScheduled);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (TryScheduleWithoutExistingOrder(orderToBeScheduled, db))
|
||||
{
|
||||
//Order has been scheduled. Do not consider orders for the same LE.
|
||||
orderList.RemoveSubsequentWithEqualLeNo(orderToBeScheduled);
|
||||
workDone = true;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Write(LogLevel.Debug, $"Destination: {orderToBeScheduled.Destination} has no ressources, skip all orders to this destination");
|
||||
orderList.RemoveSubsequentWithEqualDestination(orderToBeScheduled);
|
||||
}
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Log.Write(LogLevel.Error, $"Can not set ordersHost {orderToBeScheduled.OrdersHostId} for LE {orderToBeScheduled.Le.LeNo} to pending. Due to Exception:");
|
||||
Log.WriteException(ex);
|
||||
}
|
||||
//We can remove all subsequent orders with same LE, because the list is already ordered in a way that priorities are taken into account.
|
||||
orderList.RemoveSubsequentWithEqualLeNo(orderToBeScheduled);
|
||||
}
|
||||
return workDone;
|
||||
}
|
||||
|
||||
private bool TryScheduleWithoutExistingOrder(OrderListItem orderToBeScheduled, IWcsDbContext db)
|
||||
{
|
||||
if (CanScheduleOrder(orderToBeScheduled, db))
|
||||
{
|
||||
Log.Write(LogLevel.Debug, $"{nameof(OrdersHost)} with Id {orderToBeScheduled.OrdersHostId} will be scheduled, as there are no other {nameof(OrdersHost)} scheduled or active orders for the same LE.");
|
||||
_transportOrderService.ScheduleOrdersHost(orderToBeScheduled.OrdersHostId);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool CanScheduleOrder(OrderListItem orderToBeScheduled, IWcsDbContext db)
|
||||
{
|
||||
// Orders with their associated LE not being in storage can always be scheduled...
|
||||
//TODO Check for OnConveyor/InDestinationZone? IsInStorage() does not cover OnInputLeLifter/OnLhd/OnOutputLeLifter...
|
||||
if (!orderToBeScheduled.Le.IsInStorage())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
//...if LE is in storage, whether the order can be scheduled is determined by the amount of pending orders that may exist towards the destination and the destination's demand.
|
||||
if (!SettingsManager.GetParsedValue(ConstantsCommon.SettingNames.MaxPendingOrdersPerDestination, out int maxPendingOrdersPerDestination))
|
||||
{
|
||||
maxPendingOrdersPerDestination = 10;
|
||||
}
|
||||
|
||||
Resource resourceDestination = db.ResourceSetting.GetResourceByName(orderToBeScheduled.Destination);
|
||||
// for unmanaged resources, a demand of 1 is used.
|
||||
int destinationDemand = resourceDestination?.Demand ?? 1;
|
||||
|
||||
int countPendingOrdersToDestination = db.OrdersHost.ByStatus(TransportOrderStatus.Pending).ByDestination(orderToBeScheduled.Destination).Count();
|
||||
return destinationDemand - countPendingOrdersToDestination > 0 || countPendingOrdersToDestination - destinationDemand < maxPendingOrdersPerDestination;
|
||||
}
|
||||
|
||||
private bool TryScheduleWithExistingOrder(OrdersHost orderForSameLe, IWcsDbContext db, OrderListItem orderToBeScheduled)
|
||||
{
|
||||
// We not want to overwrite existing orders. We wait until we are back in Storage. (This happened only about 40 times a day.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user