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 } }