diff --git a/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/AcknowledgeTransportCompleted.cs b/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/AcknowledgeTransportCompleted.cs index e69de29..2ab3c56 100644 --- a/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/AcknowledgeTransportCompleted.cs +++ b/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/AcknowledgeTransportCompleted.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Gebhardt.Shared; +using Gebhardt.StoreWare.Wcs.Common.Application.LeHandling.Interfaces; +using Gebhardt.StoreWare.Wcs.Common.Application.LoopStrategy; +using Gebhardt.StoreWare.Wcs.Common.Application.StorageStrategies.Interfaces; +using Gebhardt.StoreWare.Wcs.Common.Application.TransportHandling.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 Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms.Interfaces; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Interfaces; +using static Gebhardt.StoreWare.Wcs.Common.Constants; + +namespace Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms.MessageImplementation +{ + public class AcknowledgeTransportCompletedHandler : IHandleRecord + { + private readonly IWcsDbContextFactory _dbContextFactory; + private readonly ITransportOrderService _transportOrderService; + private readonly ILeService _leService; + private readonly IDestinationService _destinationService; + private readonly ILoopSelectionService _loopSelectionService; + + public AcknowledgeTransportCompletedHandler(IWcsDbContextFactory dbContextFactory, ITransportOrderService transportOrderService, ILeService leService, IDestinationService destinationService, ILoopSelectionService loopSelectionService) + { + _dbContextFactory = dbContextFactory; + _transportOrderService = transportOrderService; + _destinationService = destinationService; + _leService = leService; + _loopSelectionService = loopSelectionService; + } + + public bool Handle(IAcknowledgeTransportCompleted message) + { + using (IWcsDbContext db = _dbContextFactory.GetDbContext()) + { + List ordersHostIds = db.OrdersHost.Where(o => o.IdOrderWms == message.IdOrderWms).Select(o => o.Id).ToList(); + + if (ordersHostIds.Any()) + { + if (ordersHostIds.Count() > 1) + { + throw new InvalidOperationException($"Multiple OrdersHost found for same WMS id {message.IdOrderWms}"); + } + + _transportOrderService.FinishOrdersHost(ordersHostIds.Single(), "AcknowledgeTransportCompleted"); + Log.Write(LogLevel.Info, $"{nameof(OrdersHost)} with Id {ordersHostIds.Single()} and WMS id {message.IdOrderWms} finished."); + + //Other open OH for HU that must be cancelled? + List others = db.OrdersHost + .OpenByLeNo(message.LeNo).Where(oh => oh.Type == TransportOrderType.Transport).ToList(); + foreach (var other in others) + { + _transportOrderService.CancelOrdersHost(other.Id, $"New destination {message.Destination}", $"OrderHost {ordersHostIds.Single()} was finished"); + } + } + else + { + Log.Write(LogLevel.Error, $"{nameof(AcknowledgeTransportCompletedHandler)}: Cannot find OH for WMS id {message.IdOrderWms}"); + } + + db.SaveChanges(); + } + + // New DB Context since OrdersConveyorService changes data that we otherwise wouldn't notice + using (IWcsDbContext db = _dbContextFactory.GetDbContext()) + { + // le empty? + Le le = db.Le.ByLeNo(message.LeNo); + // If there is no other order for this le, send it back to the storage + if (!db.OrdersHost.OpenByLeNo(message.LeNo).Any() && le != null) + { + DestinationProperty workStation = db.DestinationProperty.FirstOrDefault(d => d.Name == message.Destination && d.Property == nameof(IDestinationProperties.Level)); + var workStationDest = _destinationService.Get(message.Destination); + + List availableLoops = _loopSelectionService.GetSuitableStorageLoops(message.LeNo); + + // Choose a desired destination based on which is the closest to the workstation + string destination = MfcAllDestinations.StorageLoop2; + if (workStationDest != null) + { + destination = workStationDest.StorageArea == MfcAllDestinations.AKL02 ? MfcAllDestinationsOldSystem.LOOP3 : workStationDest.Level == ConveyorLevel.EtraBox.ToString() ? MfcAllDestinations.StorageLoop1 : MfcAllDestinations.StorageLoop2; + + // If only one of the loops is available, choose this, no matter where the LE is located + if (availableLoops.Count == 1) + { + destination = availableLoops.First().PlcName; + } + else + { + // Prefer the destination closest connected to the pick station but select the other if this is full + string fallbackDestination = workStationDest.StorageArea == MfcAllDestinations.AKL02 ? MfcAllDestinationsOldSystem.LOOP3 : workStationDest.Level == ConveyorLevel.EtraBox.ToString() ? MfcAllDestinations.StorageLoop1 : MfcAllDestinations.StorageLoop2; + if (!_loopSelectionService.hasEnoughSpace(destination) && _loopSelectionService.hasEnoughSpace(fallbackDestination)) + { + destination = fallbackDestination; + } + } + } + string source = message.Destination == MfcAllDestinationsOldSystem.TOPUP ? MfcAllDestinationsOldSystem.TOPUP : message.Source ?? "?"; + int ordersHostId = _leService.CreateTransport(message.LeNo, source, destination); + Log.Write(LogLevel.Info, $"Loop selection in acknowledge transport handler for {le.LeNo}, set to {destination}"); + LoopStrategy.ReserveForLoop(le.LeNo, ordersHostId, availableLoops.FirstOrDefault(l => l.PlcName == destination)?.Name); + } + } + return true; + } + } +} \ No newline at end of file diff --git a/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/DepatureNotificationHandler.cs b/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/DepatureNotificationHandler.cs index e69de29..7007f90 100644 --- a/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/DepatureNotificationHandler.cs +++ b/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/DepatureNotificationHandler.cs @@ -0,0 +1,147 @@ +using System.Linq; +using Gebhardt.Shared; +using Gebhardt.StoreWare.Wcs.Common; +using Gebhardt.StoreWare.Wcs.Common.Application.TransportHandling.Interfaces; +using Gebhardt.StoreWare.Wcs.Common.DbAccess; +using Gebhardt.StoreWare.Wcs.Common.DbAccess.Factories; +using Gebhardt.StoreWare.Wcs.Common.DbAccess.Model; +using Gebhardt.StoreWare.Wcs.Common.DbAccess.Model.Enums; +using Gebhardt.StoreWare.Wcs.Common.DbAccess.Queries; +using Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms.Interfaces; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Interfaces; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Models; + +namespace Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms.MessageImplementation +{ + public class DepartureNotificationHandler : IHandleRecord + { + private readonly IDestinationService _destinationService; + private readonly ITransportOrderService _transportOrderService; + private readonly IWcsDbContextFactory _wcsDbContextFactory; + + public DepartureNotificationHandler(IDestinationService destinationService, ITransportOrderService transportOrderService, IWcsDbContextFactory wcsDbContextFactory) + { + _destinationService = destinationService; + _transportOrderService = transportOrderService; + _wcsDbContextFactory = wcsDbContextFactory; + } + + public bool Handle(IDepartureNotification message) + { + using IWcsDbContext db = _wcsDbContextFactory.GetDbContext(); + + // If we don't have the LE, we need to depart. Otherwise we can't get rid of it. + if (db.Le.ByLeNo(message.LeNo) == null || message.LeNo.StartsWith("B") || message.StorageArea.ToLower() == "dummy") + { + ConveyorTelegrams.SendDepartureEtra(db, message.LeNo, message.Position); + //For EtraBoxes we can Finish the OrdersHost here As there is no feedback anymore for this boxes: + if (message.LeNo.StartsWith("B") && message.Position.Contains("PTL")) + { + //Finish all TransportOrders from this PTL Place as the place is empty now. + var etraBoxOrder = db.OrdersHost.ByStatus(TransportOrderStatus.Transmitted).Where(o => o.Source == message.Position); + if (etraBoxOrder.Any()) + { + foreach (var oh in etraBoxOrder) + { + oh.Finish(); + if (oh.LeNo == message.LeNo) + { + Log.Write(LogLevel.Info, $"EtraBox {oh.LeNo} is pushed off, finish orders host {oh.Id}."); + } + else + { + oh.Info = $"CleanedUp by Departure for {message.LeNo}"; + Log.Write(LogLevel.Info, $"EtraBox {oh.LeNo} orders host {oh.Id} is finished because EtraBox {message.LeNo} was pushed off from the same PTL place."); + } + } + } + } + db.SaveChanges(); + return true; + } + var openOH = db.OrdersHost.OpenByLeNo(message.LeNo).ToList(); + if (!openOH.Any()) + { + //Receive and Replenishment places don't need to create a WCS Transport. + if(message.Position.StartsWith(Constants.MfcAllDestinationsOldSystem.IPT01.Substring(0,3)) || message.Position.StartsWith(Constants.MfcAllDestinations.RCV01.Substring(0, 3)) || message.Position== Constants.MfcAllDestinationsOldSystem.TOPUP) + { + //Ignore and go on + return true; + } + OrdersHost ordersHost = OrdersHostFactory.GetInstance() + .InitialForLe(message.LeNo, TransportOrderType.Transport, message.Position, _destinationService.GetDefaultStorage(message.Position)); + db.Add(ordersHost); + + // Always depart a box on the error stations to prevent blocks + IDestinationProperties destination = _destinationService.Get(message.Position); + if (destination != null && destination.IsNio) + { + ConveyorTelegrams.SendDepartureEtra(db, message.LeNo, message.Position); + ordersHost.Le.SetStatus(LeStatus.OnConveyor); + } + + Log.Write(LogLevel.Info, $"Added OrdersHost for {message.LeNo} from {message.Position} to {ordersHost.Destination}."); + } + else + { + // Is there an order to this place InProgress or InDestinationZone? => Start again by setting this order to pending + var currentOrderToPlace = db.OrdersHost + .ByLeNo(message.LeNo) + .ByDestination(message.Position) + .ByStatus(TransportOrderStatus.InProgress, TransportOrderStatus.InDestinationZone) + .FirstOrDefault(); + if (currentOrderToPlace != null) + { + if (currentOrderToPlace.Status == TransportOrderStatus.InDestinationZone) + { + IDestinationProperties destination = _destinationService.Get(currentOrderToPlace.Destination); + if (currentOrderToPlace.HostDestination != null) + { + currentOrderToPlace.Destination = destination.ConnectedSequencer; + } + ConveyorTelegrams.SendDepartureEtra(db, message.LeNo, message.Position); + currentOrderToPlace.UpdateResources(TransportOrderStatus.InProgress, null); + currentOrderToPlace.ForceSetStatusInProgress(); + currentOrderToPlace.Le.SetStatus(LeStatus.OnConveyor); + db.SaveChanges(); + Log.Write(LogLevel.Info, $"OrdersHost for {message.LeNo} to {message.Position} started again by DepartureNotification."); + } + } + else + { + //Todo: Wouldn't it be better to do this in StartInitialOrders? + /*var otherPicOrder = db.OrdersHost.ByLeNo(message.LeNo) + .ByStatus(TransportOrderStatus.Initial) + .ApplyWmsOrdering() + .FirstOrDefault(); + if (otherPicOrder != null) + { + _transportOrderService.StartNextTransport(otherPicOrder.Id); + }*/ + //Set source to TOPUP in case Departure has been sent from TopUP or IPT stations, to be able to accept the box on scale + if (message.Position == Constants.MfcAllDestinationsOldSystem.TOPUP || message.Position.StartsWith(Constants.MfcAllDestinationsOldSystem.IPT01.Substring(0, 3))) + { + var activeOH = openOH.Where(o=>o.Status == TransportOrderStatus.InProgress).FirstOrDefault(); + if (activeOH != null) + { + activeOH.Source = Constants.MfcAllDestinationsOldSystem.TOPUP; + } + } + else + { + _destinationService.SetLeRequestDeparture(message.LeNo, message.Position); + ConveyorTelegrams.SendDepartureEtra(db, message.LeNo, message.Position); + db.Le.FirstOrDefault(l => l.LeNo == message.LeNo)?.SetStatus(LeStatus.OnConveyor); + } + db.SaveChanges(); + return true; + } + } + + // update destination status! + _destinationService.SetLeRequestDeparture(message.LeNo, message.Position); + db.SaveChanges(true); + return true; + } + } +} \ No newline at end of file diff --git a/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/HuChangeHandler.cs b/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/HuChangeHandler.cs index e69de29..5a58663 100644 --- a/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/HuChangeHandler.cs +++ b/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/HuChangeHandler.cs @@ -0,0 +1,94 @@ +using System; +using System.Linq; +using Gebhardt.Shared; +using Gebhardt.StoreWare.Wcs.Common; +using Gebhardt.StoreWare.Wcs.Common.DbAccess; +using Gebhardt.StoreWare.Wcs.Common.DbAccess.Constants; +using Gebhardt.StoreWare.Wcs.Common.DbAccess.Factories; +using Gebhardt.StoreWare.Wcs.Common.DbAccess.Model; +using Gebhardt.StoreWare.Wcs.Common.DbAccess.Model.Enums; +using Gebhardt.StoreWare.Wcs.Common.DbAccess.Queries; +using Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms.Interfaces; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Interfaces; + +namespace Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms.MessageImplementation +{ + public class HuChangeHandler : IHandleRecord + { + private readonly IWcsDbContextFactory _dbContextFactory; + private readonly LeFactory _leFactory = LeFactory.GetInstance(); + public HuChangeHandler(IWcsDbContextFactory dbContextFactory) + { + _dbContextFactory = dbContextFactory; + } + + public bool Handle(IHuChange message) + { + using IWcsDbContext db = _dbContextFactory.GetDbContext(); + + Le le = db.Le.ByLeNo(message.LeNo); + if (le == null) + { + Log.Write(LogLevel.Info, $"HuChange - HU {message.LeNo} is unknown. Create HU..."); + + LeTypeName type = LeType.GetLeTypeFromLeNo(message.LeNo); + if (type == null) + { + throw new LeNoWrongFormatException($"HuChange - The prefix of {message.LeNo} is not known."); + } + + le = db.Add(_leFactory.RegularLe(message.LeNo, type)).Entity; + db.SaveChanges(); + } + UpdateLeTypeIfGiven(message, le, db); + le.IsEmpty = message.IsLeEmpty ?? true; + le.AbcArea = !le.IsEmpty ? SetAbcArea(message, le) : null; + le.Subdivision = message.Subdivision?.ToEnumFromDescription() ?? le.Subdivision; + if (le.IsEmpty) + { + le.Weight = le.LeType.TareWeight; // Since there is no scale in the upper loops + le.ArticleTag = null; + } + else + { + le.ArticleTag = message.ArticleTag; + } + + db.SaveChanges(); + return true; + } + + private static string SetAbcArea(IHuChange message, Le le) + { + if (message.AbcArea == null) + { + return le.AbcArea; + } + if (!Area.IsValid(message.AbcArea) && !Area.IsClassificationForOldStorage(message.AbcArea)) + { + return Area.Z; + } + return message.AbcArea; + } + + private static void UpdateLeTypeIfGiven(IHuChange message, Le le, IWcsDbContext db) + { + if (message.LeType == null) + { + return; + } + if (!Enum.IsDefined(typeof(LeTypeName), message.LeType)) + { + throw new InvalidOperationException($"LeType={message.LeType} does not match an enum. Message: {message}"); + } + + LeType leType = db.LeType.SingleOrDefault(l => l.Name == (LeTypeName)Enum.Parse(typeof(LeTypeName), message.LeType)); + if (leType == null) + { + throw new InvalidOperationException($"LeType={message.LeType} does not match exactly one DB entry -. Message: {message}"); + } + + le.SetLeType(leType); + } + } +} \ No newline at end of file diff --git a/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/ShipmentTransportOrderHandler.cs b/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/ShipmentTransportOrderHandler.cs index e69de29..1ce7aba 100644 --- a/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/ShipmentTransportOrderHandler.cs +++ b/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/ShipmentTransportOrderHandler.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using AutoMapper.Configuration.Conventions; +using Gebhardt.Shared; +using Gebhardt.Shared.DbAccess; +using Gebhardt.StoreWare.Wcs.Common.Application.TransportHandling.Interfaces; +using Gebhardt.StoreWare.Wcs.Common.DbAccess; +using Gebhardt.StoreWare.Wcs.Common.DbAccess.Factories; +using Gebhardt.StoreWare.Wcs.Common.DbAccess.Model; +using Gebhardt.StoreWare.Wcs.Common.DbAccess.Model.Enums; +using Gebhardt.StoreWare.Wcs.Common.DbAccess.Queries; +using Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms.Interfaces; +using Gebhardt.StoreWare.Wcs.InterfaceOldPlc.Services; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.EntityFramework; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Interfaces; +using Microsoft.EntityFrameworkCore; +using static Gebhardt.StoreWare.Wcs.Common.ConstantsCommon; + +namespace Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms.MessageImplementation +{ + public class ShipmentTransportOrderHandler : IHandleRecord + { + private readonly IDestinationService _destinationService; + private readonly ITransportOrderService _transportOrderService; + private IShipmentTransportOrder _shipmentTransportOrder; + private readonly LeFactory _leFactory = LeFactory.GetInstance(); + private readonly List _allStorageDestinations; + + public ShipmentTransportOrderHandler(IDestinationService destinationService, ITransportOrderService transportOrderService) + { + _destinationService = destinationService; + _transportOrderService = transportOrderService; + _allStorageDestinations = _destinationService.Where(d => d.IsStorage).Select(d => d.Name).ToList(); + } + + public bool Handle(IShipmentTransportOrder message) + { + _shipmentTransportOrder = message; + using WcsDbContext db = new(); + CheckTransportOrder(db); + + + string cartonNo = _shipmentTransportOrder.LeNo; + Le le = db.Le.ByLeNo(cartonNo); + if (le == null) + { + Log.Write(LogLevel.Info, $"Carton number {cartonNo} is unknown. Creating a new carton..."); + + // TODO: Add correct prefixes depending on which type of carton? Now it is set as unknown + LeTypeName type = LeType.GetLeTypeFromLeNo(cartonNo); + le = db.Add(_leFactory.RegularLe(cartonNo, type)).Entity; + db.SaveChanges(); + } + + + le.SetStatus(LeStatus.Created); + CreateTransportOrder(); + db.SaveChanges(); + return true; + } + + private void CheckTransportOrder(IWcsDbContext db) + { + if (_shipmentTransportOrder.LeNo == null) + { + throw new FromWmsException("The carton or EtraBox number is missing."); + } + if(_shipmentTransportOrder.Destination == null) + { + throw new FromWmsException("No destination set."); + } + if (_shipmentTransportOrder.LeNo.StartsWith("B")) + { + List allowedEtraBoxDestinations = new List() {"D01", "D02", "D03", "D04", "D05", "D06", "D07", "D08", "D09", "D99", "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12" }; + if(_shipmentTransportOrder.Destination.Split('/').ToList().Any(d => !allowedEtraBoxDestinations.Contains(d))) + { + throw new FromWmsException("Not allowed destination for EtraBox."); + } + } + else + { + HostEntities dbHost = new HostEntitiesFactory(HostEntities.DefaultConnectionStringName).Create(); ; + var allowedDestinationsForCartons = dbHost.RouteSetting.Select(r => r.WmsRouteName).Distinct().ToList(); + if (!allowedDestinationsForCartons.Contains(_shipmentTransportOrder.Destination)) + { + throw new FromWmsException("Not allowed destination for Carton/Paperbag/Pallet."); + } + } + + //Check if there is already an open order for the same LE (WMS is sending for each Pick) + var openOrdes = db.OrdersHost.ByLeNo(_shipmentTransportOrder.LeNo).ByStatus(TransportOrderStatus.Pending, TransportOrderStatus.Initial, TransportOrderStatus.Transmitted).Where(o=>o.Source == _shipmentTransportOrder.Source && o.Destination == _shipmentTransportOrder.Destination); + if(openOrdes.Any()) + { + throw new FromWmsException("Order is already created."); + } + else + { + //Also check Finished orders from the last 2 Minutes, because WMS sometimes sends Departure before the TransportOrder... + var age = DBDateTime.Now - TimeSpan.FromMinutes(2); + openOrdes = db.OrdersHost.ByLeNo(_shipmentTransportOrder.LeNo).ByStatus(TransportOrderStatus.Finished).Where(o => o.Source == _shipmentTransportOrder.Source && o.Destination == _shipmentTransportOrder.Destination && o.Timestamp > age); + if (openOrdes.Any()) + { + throw new FromWmsException("Order is already created."); + } + } + } + + private void CreateTransportOrder() + { + using WcsDbContext db = new(); + string source = _shipmentTransportOrder.Source; + + OrdersHost ordersHost = OrdersHostFactory.GetInstance().InitialForLe(_shipmentTransportOrder.LeNo, TransportOrderType.TransportHost, source == TestToolConstants.TestToolSource ? Common.Constants.MfcAllDestinations.AKL01 : source, _shipmentTransportOrder.Destination); + db.Add(ordersHost); + + //Only for Etra boxes we transmitt the order here. The other orders are started at the checkResult Telegram. + + if (ordersHost.Le.LeType.Name == LeTypeName.EtraBox) + { + //Send Tord to Old PLC if allowed in Settings + SettingsManager.GetParsedValue(SettingNames.SendTordToOldPlc, out bool sendTord, false, true); + if (sendTord) + { + SendToOldPlcService.SendTord(ordersHost.LeNo, ordersHost.Destination.Split('/').ToList()); + } + //We set the Status to transmitted here because we have already sent the telegram and we don't want ConveyorDispo to start those orders (OrdersConveyor is not needed, as there will be no feedback from PLC) + ordersHost.ForceSetStatusTransmitted(); + //The EtraBox order will be closed as soon as we get the Departure message from WMS + } + + + db.SaveChanges(); + } + } +} \ No newline at end of file diff --git a/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/TransportOrderHandler.cs b/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/TransportOrderHandler.cs index e69de29..a05c512 100644 --- a/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/TransportOrderHandler.cs +++ b/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/TransportOrderHandler.cs @@ -0,0 +1,219 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using AutoMapper.Configuration.Conventions; +using Gebhardt.Shared; +using Gebhardt.StoreWare.Wcs.Common.Application.TransportHandling.Interfaces; +using Gebhardt.StoreWare.Wcs.Common.DbAccess; +using Gebhardt.StoreWare.Wcs.Common.DbAccess.Factories; +using Gebhardt.StoreWare.Wcs.Common.DbAccess.Model; +using Gebhardt.StoreWare.Wcs.Common.DbAccess.Model.Enums; +using Gebhardt.StoreWare.Wcs.Common.DbAccess.Queries; +using Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms.Interfaces; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Interfaces; +using Microsoft.EntityFrameworkCore; +using static Gebhardt.StoreWare.Wcs.Common.ConstantsCommon; + +namespace Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms.MessageImplementation +{ + public class TransportOrderHandler : IHandleRecord + { + private readonly IDestinationService _destinationService; + private readonly ITransportOrderService _transportOrderService; + private ITransportOrder _transportOrder; + private readonly LeFactory _leFactory = LeFactory.GetInstance(); + private readonly List _allStorageAndEmptyLeBufferDestinations; + + public TransportOrderHandler(IDestinationService destinationService, ITransportOrderService transportOrderService) + { + _destinationService = destinationService; + _transportOrderService = transportOrderService; + _allStorageAndEmptyLeBufferDestinations = _destinationService.Where(d => d.IsStorage || d.IsStorageArea || d.BuffersEmptyLe).Select(d => d.Name).ToList(); + } + + public bool Handle(ITransportOrder message) + { + _transportOrder = message; + + + using WcsDbContext db = new(); + string leNo = _transportOrder.LeNo; + Le le = db.Le.ByLeNo(leNo); + if (le == null) + { + Log.Write(LogLevel.Info, $"HU {leNo} is unknown. Create HU..."); + LeTypeName type = LeType.GetLeTypeFromLeNo(leNo); + le = db.Add(_leFactory.RegularLe(leNo, type, preferredStorageArea: message.StorageArea)).Entity; + db.SaveChanges(); + } + CheckTransportOrder(); + + IDestinationProperties destinationProperties = _destinationService.Get(_transportOrder.Destination); + if (destinationProperties == null) + { + throw new TransportDestinationInvalidException($"Unknown destination in {message}"); + } + + if (destinationProperties.IsStorage && !destinationProperties.IsSequencer && le.IsInStorage()) + { + throw new LeAlreadyInStorageException($"{le} is in storage."); + } + + CheckLeIsReachable(le); + //CheckActiveOrdersForLe(); + if (_transportOrder.MovementType == TransportOrderType.Storage.ToString()) + { + le.SetStatus(LeStatus.Created); + } + else + { + SetTransportStatus(le); + } + SetAbcArea(le); + // Le is in the old system, but no movement file has been created for it. + bool leNeedsMomFile = le.IsHuInOldSystem == true && le.L8OrderCreated == null; + CreateTransportOrder(leNeedsMomFile); + db.SaveChanges(); + return true; + } + + private void CheckTransportOrder() + { + if (_transportOrder.LeNo == null) + { + throw new LeNoMissingException("The HU number is is missing."); + } + if (_destinationService.Get(_transportOrder.Destination) == null) + { + throw new TransportDestinationInvalidException($"Destination {_transportOrder.Destination} is unknown. Order is confirmed negatively."); + } + + if (string.IsNullOrEmpty(_transportOrder.MovementType)) + { + throw new FromWmsException($"Field {nameof(_transportOrder.MovementType)} is not set or not known."); + } + // TODO: Solche Prüfungen sollten in einem Plausiblitätsmodul für LE-Nummern erfolgen, damit sie leicht kundenspezifisch angepasst werden können + if (_transportOrder.LeNo.Length < ConstantsLE.LengthBarcodeWithType) + { + throw new LeNoTooShortException($"HU number '{_transportOrder.LeNo}' is too short. Should be {ConstantsLE.LengthBarcodeWithType} digits"); + } + + if (_transportOrder.LeNo.Length > ConstantsLE.LengthBarcodeWithType) + { + throw new LeNoTooLongException($"HU number '{_transportOrder.LeNo}' is too long. Should be {ConstantsLE.LengthBarcodeWithType} digits"); + } + } + + private void SetAbcArea(Le le) + { + //le.AbcArea = _transportOrder.AbcArea; + } + + private void CreateTransportOrder(bool oldSystemBoxRequiringMomFile = false) + { + using WcsDbContext db = new(); + string source = _transportOrder.Source; + OrderTypeWms? typeWmsForOrderHost; + if (!Enum.TryParse(_transportOrder.MovementType, true, out OrderTypeWms typeWms)) + { + Log.Write(LogLevel.Error, $"could not parse '{_transportOrder.MovementType}'"); + typeWmsForOrderHost = null; + } + else + { + typeWmsForOrderHost = typeWms; + } + + OrdersHost ordersHost = OrdersHostFactory.GetInstance().InitialForLe(_transportOrder.LeNo, TransportOrderType.TransportHost, source == TestToolConstants.TestToolSource ? Common.Constants.MfcAllDestinations.AKL01 : source, + _transportOrder.Destination, idOrderWms: _transportOrder.IdOrderWms, idOrderWmsHead: _transportOrder.IdOrderWmsHead, idOrderWmsPos: _transportOrder.IdOrderWmsPos, typeWms: typeWmsForOrderHost); + + IDestinationProperties destination = _destinationService.Get(_transportOrder.Destination); + if (destination != null && destination.IsAfterSequencer) + { + ordersHost.Destination = destination.ConnectedSequencer; + ordersHost.HostDestination = _transportOrder.Destination; + } + + // TODO 15585 + ordersHost.SequenceWms = _transportOrder.Sequence; + ordersHost.IsDirectPicking = _transportOrder.IsDirectPicking; + ordersHost.PriorityDate = _transportOrder.Priority; + db.Add(ordersHost); + + if (source == TestToolConstants.TestToolSource) + { + ordersHost.Priority = TestToolConstants.TestToolPriority; + } + if (oldSystemBoxRequiringMomFile) + { + try + { + CreateMomFile(ordersHost.LeNo); + ordersHost.Le.L8OrderCreated = DateTime.Now; + Log.Write(LogLevel.Info, $"Mom file created and saved for HU {ordersHost.LeNo}."); + } + catch (Exception e) + { + Log.Write(LogLevel.Error, $"Error creating mom file for HU {ordersHost.LeNo}, Exception: {e}"); + } + } + db.SaveChanges(); + } + + private void CreateMomFile(string huNumber) + { + // Hardcoded since very temporary + string filePath = $@"\\10.101.71.32\\InterfaceFiles\\ProdGB\\OrdersGB\\{huNumber}_{DateTime.Now:yyyyMMddHHmmss00}.mom"; + File.WriteAllText(filePath, huNumber); + } + + /// + /// A Handling Unit that rests at a workplace is set to "On conveyer" when it receives a transport order + /// + private void SetTransportStatus(Le le) + { + if (le.Status != LeStatus.InStorage) + { + le.SetStatus(LeStatus.OnConveyor, le.LastWhere); + } + } + + private void CheckLeIsReachable(Le le) + { + if (le.IsInStorage()) + { + bool? storageDeviceHasCriticalFault = new WcsDbContext().AisleForLe.GetAisle(le.AisleName, le.StorageArea)?.StorageDevices.Any(s => s.HasCriticalFault); + if (storageDeviceHasCriticalFault == null) + { + Log.Write(LogLevel.Error, $"Unknown aisle {le.AisleName} LE: {le.LeNo}."); + } + else if (storageDeviceHasCriticalFault.Value) + { + // This throw causes, that the TransportOrder in FromWms will be frozen in status 'Failed'. + // Maybe the order gets then forgotten. + Log.Write(LogLevel.Error, $"Order for Le {le.LeNo} will be delayed: {le.AisleName} has a critical fault."); + } + } + } + + private void CheckActiveOrdersForLe() + { + //is the an order to a storage location or EMB? + if (_allStorageAndEmptyLeBufferDestinations.Contains(_transportOrder.Destination)) + { + using WcsDbContext db = new(); + OrdersHost ordersHost = db.OrdersHost.AsNoTracking(). + ByLeNo(_transportOrder.LeNo). + Open(). + ByDestination(_allStorageAndEmptyLeBufferDestinations).FirstOrDefault(); + if (ordersHost != null) + { + // Replace all open orders to storage or EMB with the new destination + _transportOrderService.CancelOrdersHost(ordersHost.Id, $"OH on the way to storage or EMB was ordered to a commisioning destination. This order is cancelled and replaced by this new host order with IdOrderWms {_transportOrder.IdOrderWms}."); + Log.Write(LogLevel.Info, $"HU {ordersHost.LeNo} with destination EMB or storage was ordered to a commisioning destination. The previous OH with IdOrderWms {ordersHost.IdOrderWms} was cancelled"); + } + } + } + } +} \ No newline at end of file diff --git a/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/UnsupportedHostMessageHandler.cs b/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/UnsupportedHostMessageHandler.cs index e69de29..5bdf527 100644 --- a/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/UnsupportedHostMessageHandler.cs +++ b/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/UnsupportedHostMessageHandler.cs @@ -0,0 +1,51 @@ +//----------------------------------------------------------------------------------------- +// +// This code was generated by HostInterfaceConfigurator Version 17.2022.322.1138 +// Generated at 28.03.2022 09:44:04 (Universal Time) +// +// This file can be changed to adapt it to project needs. +// If any file is changed, do not run HostInterfaceConfigurator with answer "Yes" +// to dialog question "Do you want to overwrite existing files?" +// Questions and proposals please address to "StoreWare Development Team" +// +//----------------------------------------------------------------------------------------- +using System; + +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Interfaces; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Models; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Services; + +using Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms.Interfaces; + + +using Gebhardt.Shared; +using Gebhardt.StoreWare.WcsWms.Constants; + +using Gebhardt.StoreWare.Wcs.Common.DbAccess; + +namespace Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms.MessageImplementation +{ + public partial class UnsupportedHostMessageHandler : IHandleRecord + { + + private readonly IWcsDbContextFactory _dbContextFactory; + private readonly IHostMessageFromWmsService _serviceFromWms; + private readonly IHostMessageToWmsService _serviceToWms; + + public UnsupportedHostMessageHandler(IWcsDbContextFactory dbContextFactory, IHostMessageFromWmsService serviceFromWms, IHostMessageToWmsService serviceToWms) + { + _dbContextFactory = dbContextFactory; + _serviceFromWms = serviceFromWms ?? new HostMessageFromWmsService(); + _serviceToWms = serviceToWms ?? new HostMessageToWmsService(); + } + + public bool Handle(UnsupportedHostMessage message) + { + message.Handle(); + throw new Exception(message.ErrorInterface ?? "UnsupportedHostMessage - Wrong message type"); + //using IWcsDbContext db = _dbContextFactory.GetDbContext(); + //TODO - use custom code hier + throw new NotImplementedException(); + } + } +} \ No newline at end of file