diff --git a/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/Interfaces/IHandleRecord.cs b/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/Interfaces/IHandleRecord.cs new file mode 100644 index 0000000..157b464 --- /dev/null +++ b/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/Interfaces/IHandleRecord.cs @@ -0,0 +1,25 @@ +using System; +using System.ComponentModel; +using System.Collections.Generic; +using Newtonsoft.Json; +using System.Linq; +using System.Reflection; +using System.Xml.Serialization; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Interfaces; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Helpers; +using Model = Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Models; + +using Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms.Interfaces; + + +using Gebhardt.Shared; +using Gebhardt.StoreWare.WcsWms.Constants; + + +namespace Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms.Interfaces +{ + public interface IHandleRecord where THostMessage : IHostMessage + { + bool Handle(THostMessage arg); + } +} \ No newline at end of file diff --git a/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/FromWmsBookingConsumer.cs b/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/FromWmsBookingConsumer.cs new file mode 100644 index 0000000..213049a --- /dev/null +++ b/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/FromWmsBookingConsumer.cs @@ -0,0 +1,90 @@ +using System; +using System.ComponentModel; +using System.Collections.Generic; +using Newtonsoft.Json; +using System.Linq; +using System.Reflection; +using System.Xml.Serialization; +using Unity; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Interfaces; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Services; +using Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms.Interfaces; +using Gebhardt.Shared.Process; +using Gebhardt.Shared.Process.ProducerConsumer; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Interfaces; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Models; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.EntityFramework; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.EntityFramework.Models; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Mapping; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Helpers; + +using Gebhardt.Shared; +using Gebhardt.StoreWare.WcsWms.Constants; + + +namespace Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms +{ + internal class FromWmsBookingConsumer : Consumer + { + #region Private Fields + + private readonly IUnityContainer _unityContainer; + private readonly IHostMessageFromWmsService _serviceFromWms; + + #endregion Private Fields + + #region Public Constructors + + public FromWmsBookingConsumer(string name, int aliveTime, int queueLength, IUnityContainer unityContainer) + : base(name, aliveTime, true, queueLength) + { + _unityContainer = unityContainer; + _serviceFromWms = _unityContainer.Resolve(); + } + + #endregion Public Constructors + + #region Public Methods + + public override bool DoWork(IHostMessage hostMessage) + { + + try + { + IHandleRecord handler = null; + try + { + //handler = _unityContainer.Resolve(hostMessage.RecordType); + handler = (IHandleRecord)_unityContainer.Resolve(hostMessage.GetType()); //TODO test + } + catch (Exception e) + { + Log.Write(LogLevel.Error, $"IHandleRecord on UnityContainer in RegisterFromWmsServices from MessageInitializer.cs not registired. '{e.Message}'"); + } + if (handler == null) + { + hostMessage.Handle(); + } + else + { + handler.Handle(hostMessage); + hostMessage.SetFinished(); + } + } + catch (Exception e) + { + //Log.WriteException(e); + string exceptionMessage = e.InnerException?.InnerException?.Message ?? e.InnerException?.Message ?? e.Message; + Log.Write(LogLevel.Error, $"Nachricht {hostMessage.RecordType} kann nicht verbucht werden: {exceptionMessage}"); + hostMessage.SetFailed(e.Message); + } + finally + { + _serviceFromWms.Update(hostMessage); + } + return true; + } + + #endregion Public Methods + } +} \ No newline at end of file diff --git a/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/FromWmsBookingProducer.cs b/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/FromWmsBookingProducer.cs new file mode 100644 index 0000000..d5902d5 --- /dev/null +++ b/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/FromWmsBookingProducer.cs @@ -0,0 +1,224 @@ +using System; +using System.ComponentModel; +using System.Collections.Generic; +using Newtonsoft.Json; +using System.Linq; +using System.Reflection; +using System.Xml.Serialization; +using System.Text.RegularExpressions; +using Unity; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Interfaces; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Services; +using Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms.Interfaces; +using Gebhardt.Shared.Process; +using Gebhardt.Shared.Process.ProducerConsumer; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Interfaces; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Models; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.EntityFramework; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.EntityFramework.Models; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Mapping; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Helpers; + +using Gebhardt.Shared; +using Gebhardt.StoreWare.WcsWms.Constants; + + +namespace Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms +{ + internal class FromWmsBookingProducer : Producer + { + #region Private Fields + + private bool _firstExecution = true; + + private int _consumerQueueLength; + + private bool _useLoadBalancing; + + private readonly IUnityContainer _unityContainer; + + private readonly IHostMessageFromWmsService _serviceFromWms; + + #endregion Private Fields + + #region Private Methods + + + /// + /// Fügt im Dictionary dem angegebenen Consumer das Messagem hinzu + /// + /// + /// + /// FromWms - IHostMessage + private void AddDataForConsumer(ref Dictionary> dataForConsumers, string consumer, IHostMessage hostMessage) + { + if (dataForConsumers.ContainsKey(consumer)) + { + dataForConsumers[consumer].Add(hostMessage); + } + else + { + dataForConsumers.Add(consumer, new List { hostMessage }); + } + } + + /// + /// Bestimmt den Consumer, der für den Datensatz verantwortlich ist + /// + /// + /// + /// Consumer Name, wenn ein passender Consumer in der lsite ist, null, wenn kein passender Consumer in der Liste ist + private string GetConsumerForBooking(List consumersWithDemand, string criterion) + { + //Gibt es eine Letzte Stelle, sonst default + if (!criterion.IsNullOrEmptyOrDbEmpty()) + { + //Wir entscheiden mit der letzten Stelle der LE, welcher LE-Consumer verbucht oder ob der default Consumer das tun muss + string endOfLe = criterion.Substring(criterion.Length - 1); + //Der Name des Consumer endet mit dem gleichen Zeichen + if (consumersWithDemand.Any(c => c.EndsWith(endOfLe))) + { + return consumersWithDemand.First(c => c.EndsWith(endOfLe)); + } + //Soll dieses Message von einem speziellen Consumer bearbeitet werden? Aber dieser ist beschäftigt + else if (Regex.IsMatch(endOfLe, "[0-9]")) + { + //Message auslassen - keinem Consumer zuordnen + return null; + } + //HU endet mit einem Zeichen, dass zu keinem Consumer passt also Default oder auslassen + else + { + //Default Consumer muss mit Default enden! + return consumersWithDemand.FirstOrDefault(c => c.EndsWith("Default")); + } + } + else + { + //Default Consumer muss mit Default enden! + return consumersWithDemand.FirstOrDefault(c => c.EndsWith("Default")); + } + } + + #endregion Private Methods + + #region Protected Methods + + protected override Dictionary> GetDataForConsumers(List consumersWithDemand) + { + //TODO: jub evtl. die Messagem Stau Meldung aus KW übernehmen + Dictionary> dataForConsumers = new Dictionary>(); + try + { + using HostEntities db = new HostEntitiesFactory(HostEntities.DefaultConnectionStringName).Create(); + if (consumersWithDemand.Count > 0) + { + List hostMessageEntries; + + //es werden maximal so viele Messages abgerufen, wie ein einzelner Consumer annehmen könnte, damit das TryAdd sicher klappt und der Status nicht fälschlich + //gesetzt wird + if (_firstExecution) + { + //beim ersten mal nach Neustart werden auch die InProgress Messages nochmal mit abgerufen + //Ref == null damit nur Kopfnachrichten gefunden werden + hostMessageEntries = _serviceFromWms.GetAllEntries(a => a.Status == "Pending" || a.Status == "InProgress", takeCount: _consumerQueueLength).OrderBy(a => a.Id).ToList(); + _firstExecution = false; + } + else + { + //sonst nur Messages, die noch an keinen Consumer gegeben wurden + //Ref == null damit nur Kopfnachrichten gefunden werden + hostMessageEntries = _serviceFromWms.GetAllEntries(a => a.Status == "Pending", takeCount: _consumerQueueLength).OrderBy(a => a.Id).ToList(); + } + + if (hostMessageEntries.Any()) + { + //ohne LoadBalancing erhält der erste Consumer alle Messages zum Verbuchen + if (!_useLoadBalancing) + { + //der Consumer muss also alle Messages verarbeiten und nicht nur loggen + dataForConsumers.Add(consumersWithDemand.FirstOrDefault() ?? string.Empty, hostMessageEntries); + //Status auf InProgress damit jede Message nur einmal abgerufen wird + hostMessageEntries.ForEach(message => message.Status = "InProgress"); + Log.Write(LogLevel.Low, $"Kein LoadBalancing aktiv, {hostMessageEntries.Count} neue HostMessages für Consumer {consumersWithDemand.FirstOrDefault()} gefunden"); + } + //mit LoadBalancing wird nach der letzen Ziffer der ersten HU der Message auf 10 Consumer verteilt, + //enthält die Message keine HU wird es an Consumer 11 übergeben. + else + { + Log.Write(LogLevel.Low, $"LoadBalancing aktiv, {hostMessageEntries.Count} neue HostMessages gefunden, verteile auf Consumer"); + + foreach (IHostMessage message in hostMessageEntries) + { + try + { + FromWms entityFromWms = HostMessageFromWmsService.HostMapper.Map(message); + Log.Write(LogLevel.Low, $"Producerschleife für {entityFromWms}"); + string consumer; + //keine HU in der Message, dann dem Default Consumer zuordnen + if (entityFromWms.LeNo.IsNullOrEmptyOrDbEmpty()) + { + consumer = GetConsumerForBooking(consumersWithDemand, null); + //wenn der Default Consumer nicht in der Liste war, Message auslassen + if (consumer != null) + { + AddDataForConsumer(ref dataForConsumers, consumer, message); + //Status auf InProgress damit die Message nur einmal abgerufen wird + entityFromWms.Status = "InProgress"; + } + } + //Messages mit HU + else + { + consumer = GetConsumerForBooking(consumersWithDemand, entityFromWms.LeNo); + AddDataForConsumer(ref dataForConsumers, consumer, message); + //Status auf InProgress damit die Message nur einmal abgerufen wird + entityFromWms.Status = "InProgress"; + } + } + catch (Exception ex) + { + Log.WriteException(ex); + } + } + Log.Write(LogLevel.Low, $"Producerschleife beendet"); + } + //Status Updates für alle weitergereichten Messages + db.SaveChanges(); + } + else + { + Log.Write(LogLevel.Debug, 60, "Keine Messages in FromWms"); + } + } + else + { + Log.Write(LogLevel.Debug, 60, "Kein Consumer hat demand"); + } + + return dataForConsumers; + } + catch (Exception e) + { + Log.WriteException(e); + return dataForConsumers; + } + } + + #endregion Protected Methods + + #region Public Constructors + + public FromWmsBookingProducer(int workinterval, bool useLoadBalancing, int consumerQueueLength, IUnityContainer unityContainer) : base(typeof(FromWmsBookingProducer).Name, workinterval, true) + { + _unityContainer = unityContainer; + _serviceFromWms = _unityContainer.Resolve(); + _consumerQueueLength = consumerQueueLength; + _useLoadBalancing = useLoadBalancing; + } + + #endregion Public Constructors + + } + +} \ No newline at end of file diff --git a/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/FromWmsException.cs b/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/FromWmsException.cs new file mode 100644 index 0000000..fdde58b --- /dev/null +++ b/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/FromWmsException.cs @@ -0,0 +1,17 @@ +using System; + +namespace Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms +{ + + public class FromWmsException : Exception + { + public FromWmsException(string message) : base(message) + { + } + + public FromWmsException(string message, Exception innerException) : base(message, innerException) + { + } + } + +} \ No newline at end of file diff --git a/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/MessageInitializer.cs b/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/MessageInitializer.cs new file mode 100644 index 0000000..c830d3b --- /dev/null +++ b/03_Realisierung/Code Snippets/HostBooking/InterfaceWcsWms/MessageImplementation/MessageInitializer.cs @@ -0,0 +1,33 @@ +using Unity; +using Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms.MessageImplementation; +using Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms.Interfaces; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Interfaces; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Models; +using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Services; + +namespace Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms +{ + public static class MessageInitializer + { + public static void RegisterFromWmsServices(this IUnityContainer unityContainer) + { + unityContainer.RegisterType(); + } + + public static void RegisterFromWmsHandlers(this IUnityContainer unityContainer) + { + + unityContainer.RegisterType, TransportOrderHandler>("TransportOrder"); + + unityContainer.RegisterType, ShipmentTransportOrderHandler>("ShipmentTransportOrder"); + + unityContainer.RegisterType, AcknowledgeTransportCompletedHandler>("AcknowledgeTransportCompleted"); + + unityContainer.RegisterType, DepartureNotificationHandler>("DepartureNotification"); + + //unityContainer.RegisterType, RequestEmptyHuReportHandler>("RequestEmptyHuReport"); + + unityContainer.RegisterType, HuChangeHandler>("HuChange"); + } + } +} \ No newline at end of file diff --git a/03_Realisierung/Code Snippets/OrdersHostQuerries/ApplyWmsOrderingSequencerRetrievalTime.cs b/03_Realisierung/Code Snippets/OrdersHostQuerries/ApplyWmsOrderingSequencerRetrievalTime.cs index e69de29..2cc40b1 100644 --- a/03_Realisierung/Code Snippets/OrdersHostQuerries/ApplyWmsOrderingSequencerRetrievalTime.cs +++ b/03_Realisierung/Code Snippets/OrdersHostQuerries/ApplyWmsOrderingSequencerRetrievalTime.cs @@ -0,0 +1,25 @@ + public static IOrderedQueryable ApplyWmsOrderingSequencerRetrievalTime(this IQueryable entity, + IWcsDbContext db) + { + var query = entity + .GroupJoin( + db.OrdersHost + .GroupBy(xx => xx.IdOrderWmsHead) + .Select(g => new + { + IdOrderWmsHead = g.Key, + SequencerRetrievalTime = g.Min(y => y.SequencerRetrievalTime) + }), + o => o.IdOrderWmsHead, + m => m.IdOrderWmsHead, + (o, m) => new { o, m } + ) + .SelectMany(x => x.m.DefaultIfEmpty(), (x, m) => new { x.o, m }) + .OrderByDescending(x => x.o.IsStolen) + .ThenByDescending(x => x.o.IsDirectPicking) + .ThenByDescending(x => x.m.SequencerRetrievalTime != null) + .ThenBy(x => x.m.SequencerRetrievalTime) + .ThenBy(x => x.o.Created) + .Select(x => x.o); + return (IOrderedQueryable) query; + } \ No newline at end of file diff --git a/03_Realisierung/Code Snippets/OrdersHostQuerries/ByCancelledSequencerOrder.cs b/03_Realisierung/Code Snippets/OrdersHostQuerries/ByCancelledSequencerOrder.cs index e69de29..8444567 100644 --- a/03_Realisierung/Code Snippets/OrdersHostQuerries/ByCancelledSequencerOrder.cs +++ b/03_Realisierung/Code Snippets/OrdersHostQuerries/ByCancelledSequencerOrder.cs @@ -0,0 +1,4 @@ + public static IQueryable ByCancelledSequencerOrder(this IQueryable entity) + { + return entity.Where(o => o.Source.Contains(WcsNames.SEQ) && o.Destination == MfcAllDestinations.StorageLoop2); + } \ No newline at end of file diff --git a/03_Realisierung/Code Snippets/OrdersHostQuerries/ExcludeNextEmpty.cs b/03_Realisierung/Code Snippets/OrdersHostQuerries/ExcludeNextEmpty.cs index e69de29..e9785e6 100644 --- a/03_Realisierung/Code Snippets/OrdersHostQuerries/ExcludeNextEmpty.cs +++ b/03_Realisierung/Code Snippets/OrdersHostQuerries/ExcludeNextEmpty.cs @@ -0,0 +1,4 @@ + public static IQueryable ExcludeNextEmpty(this IQueryable entity) + { + return entity.Where(o => o.LeNo != LeTypeName.NextEmptyMiniloadSmall.ToString() && o.LeNo != LeTypeName.NextEmptyMiniloadBig.ToString()); + } \ No newline at end of file diff --git a/03_Realisierung/Code Snippets/OrdersHostQuerries/RemoveSubsequentWithEqualDestination.cs b/03_Realisierung/Code Snippets/OrdersHostQuerries/RemoveSubsequentWithEqualDestination.cs deleted file mode 100644 index e69de29..0000000 diff --git a/03_Realisierung/Code Snippets/OrdersHostQuerries/RemoveSubsequentWithEqualLeNo.cs b/03_Realisierung/Code Snippets/OrdersHostQuerries/RemoveSubsequentWithEqualLeNo.cs deleted file mode 100644 index e69de29..0000000 diff --git a/03_Realisierung/Datenbank Tabellen/Snippet Entity Relationship Modell der Datenbank.png b/03_Realisierung/Datenbank Tabellen/Snippet Entity Relationship Modell der Datenbank.png new file mode 100644 index 0000000..2fdcf18 Binary files /dev/null and b/03_Realisierung/Datenbank Tabellen/Snippet Entity Relationship Modell der Datenbank.png differ diff --git a/03_Realisierung/Taskboard.md b/03_Realisierung/Taskboard.md index 1eb28b2..298c3e9 100644 --- a/03_Realisierung/Taskboard.md +++ b/03_Realisierung/Taskboard.md @@ -51,11 +51,29 @@ Schriftliche Ausarbeitung nur im Maße wie es die IHK will. ## 00 Vorbereitung -### Status: 🟩 Active +### Status: ⬛ Done - [x] Etra Repo Fork clonen -- [ ] Zugang zur Etra Emulation prüfen -- [ ] ConveyorDispo debuggen +- [x] Zugang zur Etra Emulation prüfen +- [x] ConveyorDispo debuggen + +------------------------------------------ + + +## 00.5 ERD erstellen +### Status: ⬛ Done + +Entity Relationship Diagramm der relevanten Tabellen der Datenbank erstellen. + +- Destination +- LeType +- Le +- Location +- OrdersHost +- OrdersConveyor +- OrdersMiniload +- Aisle +- StorageDevice ------------------------------------------