refactor: migrate project structure by reorganizing realization code snippets into documentation and analysis categories.
This commit is contained in:
BIN
02_Analyse_Konzept/HostBooking/.DS_Store
vendored
Normal file
BIN
02_Analyse_Konzept/HostBooking/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
02_Analyse_Konzept/HostBooking/IST Zustand/.DS_Store
vendored
Normal file
BIN
02_Analyse_Konzept/HostBooking/IST Zustand/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
02_Analyse_Konzept/HostBooking/IST Zustand/Ablaufdiagramm/.DS_Store
vendored
Normal file
BIN
02_Analyse_Konzept/HostBooking/IST Zustand/Ablaufdiagramm/.DS_Store
vendored
Normal file
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 645 KiB |
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0"?>
|
||||
<configuration>
|
||||
<appSettings>
|
||||
<!-- BEGINN Prozesseinstellungen -->
|
||||
<add key="Conn1" value="HostBooking"/>
|
||||
<!-- Telegrammverbuchung aufteilen nach der letzten Stelle der HU Nummer?-->
|
||||
<add key="UseLoadBalancing" value="false"/>
|
||||
<!--Polling Intervall der Producerklasse-->
|
||||
<add key="Intervall" value="201"/>
|
||||
<!-- so viele Telegramme bekommt ein Consumer maximal auf einen Schlag-->
|
||||
<add key="ConsumerQueueLength" value="200"/>
|
||||
<!-- Überprüfungsintervall -->
|
||||
<add key="ctrlTimer" value="12000" />
|
||||
<!-- ENDE Prozesseinstellungen-->
|
||||
|
||||
|
||||
<!-- BEGINN Log -->
|
||||
<!-- 0=ERROR 3=INFO 5=DEBUG 7=LOWLEVEL -->
|
||||
<add key="LogLevel" value="7"/>
|
||||
<!-- Aufteilung der Log-Files per Thread -->
|
||||
<add key="SplitLogFilesByThreadName" value="true"/>
|
||||
<add key="MainThreadNames" value="-"/>
|
||||
<!-- ENDE Log -->
|
||||
|
||||
|
||||
<!-- Datenbankverbindung -->
|
||||
<add key="Eigentuemer" value="Wcs" />
|
||||
|
||||
</appSettings>
|
||||
</configuration>
|
||||
@@ -0,0 +1,45 @@
|
||||
using Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms;
|
||||
|
||||
namespace Gebhardt.StoreWare.Wcs.HostBooking
|
||||
{
|
||||
|
||||
public class LeNoMissingException : FromWmsException
|
||||
{
|
||||
public LeNoMissingException(string message) : base(message) { }
|
||||
}
|
||||
|
||||
public class LeNoWrongFormatException : FromWmsException
|
||||
{
|
||||
public LeNoWrongFormatException(string message) : base(message) { }
|
||||
}
|
||||
|
||||
public class LeNoTooShortException : FromWmsException
|
||||
{
|
||||
public LeNoTooShortException(string message) : base(message) { }
|
||||
}
|
||||
|
||||
public class LeNoTooLongException : FromWmsException
|
||||
{
|
||||
public LeNoTooLongException(string message) : base(message) { }
|
||||
}
|
||||
|
||||
public class TransportDestinationInvalidException : FromWmsException
|
||||
{
|
||||
public TransportDestinationInvalidException(string message) : base(message) { }
|
||||
}
|
||||
|
||||
public class LeAlreadyInStorageException : FromWmsException
|
||||
{
|
||||
public LeAlreadyInStorageException(string message) : base(message) { }
|
||||
}
|
||||
|
||||
public class LeNotReachableException : FromWmsException
|
||||
{
|
||||
public LeNotReachableException(string message) : base(message) { }
|
||||
}
|
||||
|
||||
public class LeHasActiveTransportOrderException : FromWmsException
|
||||
{
|
||||
public LeHasActiveTransportOrderException(string message) : base(message) { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
using Gebhardt.Shared;
|
||||
using Gebhardt.Shared.Process;
|
||||
using Gebhardt.StoreWare.Wcs.Common.Unity;
|
||||
using Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using Unity;
|
||||
|
||||
namespace Gebhardt.StoreWare.Wcs.HostBooking
|
||||
{
|
||||
internal class Haupt
|
||||
{
|
||||
[STAThread]
|
||||
private static void Main()
|
||||
{
|
||||
AppDomain.CurrentDomain.UnhandledException += HandleAppDomainException;
|
||||
try
|
||||
{
|
||||
IUnityContainer container = WcsContainerFactory.GetChildInstance();
|
||||
container.RegisterFromWmsHandlers();
|
||||
container.RegisterFromWmsServices();
|
||||
|
||||
if (AppConfigVerifier.CheckAndWriteToLog())
|
||||
{
|
||||
// LifeTimer Intervall ist in app.config ctrlTimer eingestellt
|
||||
int lifeTimerInterval = Convert.ToInt32(ConfigurationManager.AppSettings["ctrlTimer"]);
|
||||
string[] usedConnections = GetUsedConnections();
|
||||
|
||||
ProcessManager manager = new(lifeTimerInterval, true,
|
||||
ConfigurationManager.AppSettings["ProcessClass"] == "None" ? ProcessClass.None : ProcessClass.Application,
|
||||
usedConnections);
|
||||
//Worker (Producer und Consumer) erstellen und verknüpfen
|
||||
RegisterWorkers(manager, lifeTimerInterval, container);
|
||||
// Starten
|
||||
manager.RunWorkers();
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.WriteException(exception);
|
||||
Console.WriteLine("main thread: e: {0} e: {1}", exception.StackTrace, exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandleAppDomainException(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
Exception exception = (Exception)e.ExceptionObject;
|
||||
if (exception == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
Log.WriteException(exception);
|
||||
Console.WriteLine($@"main thread: e: {exception.StackTrace} e: {exception}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ruft die Conn Einträge aus der App.config ab die nicht leer sind
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private static string[] GetUsedConnections()
|
||||
{
|
||||
ProcessParameter parameter = new();
|
||||
var connections = new List<string> { parameter.Conn1, parameter.Conn2, parameter.Conn3, parameter.Conn4, parameter.Conn5, parameter.Conn6 };
|
||||
string[] usedConnections = connections.FindAll(x => x != "leer").ToArray();
|
||||
return usedConnections;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Erstellt einen Producer und je nach useLoadBalancing einen oder elf Consumer und weißt diese dem Producer und dem
|
||||
/// Manager zu.
|
||||
/// </summary>
|
||||
/// <param name="manager"></param>
|
||||
/// <param name="ctrTimerInterval">ctrTimer aus App.config</param>
|
||||
internal static void RegisterWorkers(ProcessManager manager, int ctrTimerInterval, IUnityContainer unityContainer)
|
||||
{
|
||||
//AppSettings auslesen
|
||||
bool useLoadBalancing;
|
||||
try
|
||||
{
|
||||
useLoadBalancing = Convert.ToBoolean(ConfigurationManager.AppSettings["UseLoadBalancing"]);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
useLoadBalancing = false;
|
||||
Log.Write(LogLevel.Error, "Parameter 'useLoadBalancing' fehlt in der App.config, setze auf false");
|
||||
}
|
||||
int consumerQueueLength;
|
||||
try
|
||||
{
|
||||
consumerQueueLength = Convert.ToInt32(ConfigurationManager.AppSettings["ConsumerQueueLength"]);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
consumerQueueLength = 200;
|
||||
Log.Write(LogLevel.Error, "Parameter 'consumerQueueLength' fehlt in der App.config, setze auf 200");
|
||||
}
|
||||
|
||||
// Producer anmelden
|
||||
int pollingInterval = Convert.ToInt32(ConfigurationManager.AppSettings["Intervall"]);
|
||||
HostBookingProducer producer = new(pollingInterval, useLoadBalancing, consumerQueueLength);
|
||||
manager.RegisterWorker(producer);
|
||||
string consumerName;
|
||||
HostBookingConsumer consumer;
|
||||
|
||||
//Wenn useLoadBalancing = true, dann wird für jede Endziffer der HU ein Consumer erstellt und einer für Telegramme ohne HU (Default)
|
||||
if (useLoadBalancing)
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
consumerName = $"Consumer_{i}";
|
||||
consumer = new HostBookingConsumer(consumerName, ctrTimerInterval / 2, consumerQueueLength, unityContainer);
|
||||
producer.AddConsumer(consumerName, consumer);
|
||||
manager.RegisterWorker(consumer);
|
||||
}
|
||||
consumerName = "Consumer_Default";
|
||||
consumer = new HostBookingConsumer(consumerName, ctrTimerInterval / 2, consumerQueueLength, unityContainer);
|
||||
producer.AddConsumer(consumerName, consumer);
|
||||
manager.RegisterWorker(consumer);
|
||||
}
|
||||
//ohne useLoadBalancing gibt es nur einen Consumer der alle Telegramme verarbeitet
|
||||
else
|
||||
{
|
||||
consumerName = "Consumer_All";
|
||||
consumer = new HostBookingConsumer(consumerName, ctrTimerInterval / 2, consumerQueueLength, unityContainer);
|
||||
producer.AddConsumer(consumerName, consumer);
|
||||
manager.RegisterWorker(consumer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using Gebhardt.Shared;
|
||||
using Gebhardt.Shared.Process.ProducerConsumer;
|
||||
using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Interfaces;
|
||||
using System;
|
||||
using Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms.Interfaces;
|
||||
using Unity;
|
||||
using Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms;
|
||||
|
||||
namespace Gebhardt.StoreWare.Wcs.HostBooking
|
||||
{
|
||||
public class HostBookingConsumer : Consumer<IHostMessage>
|
||||
{
|
||||
private readonly IUnityContainer _unityContainer;
|
||||
|
||||
public HostBookingConsumer(string name, int aliveTime, int queueLength, IUnityContainer unityContainer)
|
||||
: base(name, aliveTime, true, queueLength)
|
||||
{
|
||||
_unityContainer = unityContainer;
|
||||
}
|
||||
|
||||
public override bool DoWork(IHostMessage hostMessage)
|
||||
{
|
||||
IHostMessageFromWmsService service = _unityContainer.Resolve<IHostMessageFromWmsService>();
|
||||
|
||||
try
|
||||
{
|
||||
dynamic handler = _unityContainer.Resolve(typeof(IHandleRecord<>).MakeGenericType(hostMessage.GetType()), hostMessage.RecordType);
|
||||
Log.Write(LogLevel.Info, $"process message [{hostMessage}]");
|
||||
handler.Handle((dynamic)hostMessage);
|
||||
hostMessage.SetFinished();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e.GetType() != typeof(FromWmsException))
|
||||
{
|
||||
Log.WriteException(e);
|
||||
}
|
||||
hostMessage.SetFailed(e.Message);
|
||||
}
|
||||
service.Update(hostMessage);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
using Gebhardt.Shared;
|
||||
using Gebhardt.Shared.Process.ProducerConsumer;
|
||||
using Gebhardt.StoreWare.WcsWms.Constants;
|
||||
using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.EntityFramework;
|
||||
using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.EntityFramework.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Interfaces;
|
||||
using Gebhardt.StoreWare.WcsWms.InterfaceWcsWms.Services;
|
||||
using static Gebhardt.StoreWare.Wcs.Common.ConstantsCommon.WatchdogConstants;
|
||||
|
||||
namespace Gebhardt.StoreWare.Wcs.HostBooking
|
||||
{
|
||||
internal class HostBookingProducer : Producer<IHostMessage>
|
||||
{
|
||||
private bool _firstExecution = true;
|
||||
|
||||
private int _consumerQueueLength;
|
||||
|
||||
private bool _useLoadBalancing;
|
||||
|
||||
private readonly IHostMessageFromWmsService _service = new HostMessageFromWmsService();
|
||||
|
||||
/// <summary>
|
||||
/// Fügt im Dictionary dem angegebenen Consumer das Messagem hinzu
|
||||
/// </summary>
|
||||
/// <param name="dataForConsumers"></param>
|
||||
/// <param name="consumer"></param>
|
||||
/// <param name="fromWms"></param>
|
||||
private void AddDataForConsumer(ref Dictionary<string, List<IHostMessage>> dataForConsumers, string consumer, IHostMessage fromWms)
|
||||
{
|
||||
if (dataForConsumers.ContainsKey(consumer))
|
||||
{
|
||||
dataForConsumers[consumer].Add(fromWms);
|
||||
}
|
||||
else
|
||||
{
|
||||
dataForConsumers.Add(consumer, new List<IHostMessage> { fromWms });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bestimmt den Consumer, der für den Datensatz verantwortlich ist
|
||||
/// </summary>
|
||||
/// <param name="consumersWithDemand"></param>
|
||||
/// <param name="criterion"></param>
|
||||
/// <returns>Consumer Name, wenn ein passender Consumer in der lsite ist, null, wenn kein passender Consumer in der Liste ist</returns>
|
||||
private string GetConsumerForBooking(List<string> 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 Messagem von einem speziellen Consumer bearbeitet werden? Aber dieser ist beschäftigt
|
||||
else if (Regex.IsMatch(endOfLe, "[0-9]"))
|
||||
{
|
||||
//Messagem 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"));
|
||||
}
|
||||
}
|
||||
|
||||
protected override Dictionary<string, List<IHostMessage>> GetDataForConsumers(List<string> consumersWithDemand)
|
||||
{
|
||||
//TODO: jub evtl. die Messagem Stau Meldung aus KW übernehmen
|
||||
Dictionary<string, List<IHostMessage>> dataForConsumers = new Dictionary<string, List<IHostMessage>>();
|
||||
try
|
||||
{
|
||||
//using HostEntities db = new HostEntitiesFactory(HostEntities.DefaultConnectionStringName).Create();
|
||||
if (consumersWithDemand.Count > 0)
|
||||
{
|
||||
List<IHostMessage> fromWmsEntries;
|
||||
|
||||
if (_firstExecution)
|
||||
{
|
||||
//beim ersten mal nach Neustart werden die InProgress Messages nochmal auf Pending zurückgesetzt
|
||||
//Ref == null damit nur Kopfnachrichten gefunden werden
|
||||
fromWmsEntries = _service.GetAllEntries(t => t.Status == Status.InProgress).ToList();
|
||||
using HostEntities db = new HostEntitiesFactory(HostEntities.DefaultConnectionStringName).Create();
|
||||
fromWmsEntries.ForEach(message =>
|
||||
{
|
||||
FromWms fromWms = HostMessageFromWmsService.HostMapper.Map<FromWms>(message);
|
||||
db.Attach(fromWms);
|
||||
fromWms.Status = Status.Pending;
|
||||
});
|
||||
db.SaveChanges();
|
||||
Log.Write(LogLevel.Info, $"Erste Produce Schleife nach Neustart, setze {fromWmsEntries.Count} Telegramme zur Sicherheit nochmals von InProgress auf Pending");
|
||||
_firstExecution = false;
|
||||
}
|
||||
|
||||
//nur Messages, die noch an keinen Consumer gegeben wurden
|
||||
//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
|
||||
//Ref == null damit nur Kopfnachrichten gefunden werden
|
||||
fromWmsEntries = _service.GetAllEntries(a => a.Status == Status.Pending).OrderBy(a => a.Id).Take(_consumerQueueLength).ToList();// db.FromWms.Where(a => a.Ref == null).Where(a => a.Status == Status.Pending).OrderBy(a => a.Id).Take(_consumerQueueLength).ToList();
|
||||
|
||||
|
||||
if (fromWmsEntries.Any())
|
||||
{
|
||||
using HostEntities db = new HostEntitiesFactory(HostEntities.DefaultConnectionStringName).Create();
|
||||
//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, fromWmsEntries);
|
||||
//Status auf InProgress damit jede Message nur einmal abgerufen wird
|
||||
fromWmsEntries.ForEach(message => {
|
||||
FromWms fromWms = HostMessageFromWmsService.HostMapper.Map<FromWms>(message);
|
||||
db.Attach(fromWms);
|
||||
fromWms.Status = Status.InProgress;
|
||||
});
|
||||
Log.Write(LogLevel.Low, $"Kein LoadBalancing aktiv, {fromWmsEntries.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, {fromWmsEntries.Count} neue HostMessages gefunden, verteile auf Consumer");
|
||||
|
||||
foreach (IHostMessage message in fromWmsEntries)
|
||||
{
|
||||
try
|
||||
{
|
||||
FromWms fromWms = HostMessageFromWmsService.HostMapper.Map<FromWms>(message);
|
||||
db.Attach(fromWms);
|
||||
Log.Write(LogLevel.Low, $"Producerschleife für {fromWms}");
|
||||
string consumer;
|
||||
//keine HU in der Message, dann dem Default Consumer zuordnen
|
||||
if (fromWms.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
|
||||
fromWms.Status = Status.InProgress;
|
||||
}
|
||||
}
|
||||
//Messages mit HU
|
||||
else
|
||||
{
|
||||
consumer = GetConsumerForBooking(consumersWithDemand, fromWms.LeNo);
|
||||
AddDataForConsumer(ref dataForConsumers, consumer, message);
|
||||
//Status auf InProgress damit die Message nur einmal abgerufen wird
|
||||
fromWms.Status = 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;
|
||||
}
|
||||
}
|
||||
|
||||
public HostBookingProducer(int workinterval, bool useLoadBalancing, int consumerQueueLength) : base(typeof(HostBookingProducer).Name, workinterval, true)
|
||||
{
|
||||
_consumerQueueLength = consumerQueueLength;
|
||||
_useLoadBalancing = useLoadBalancing;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<IHostMessage>
|
||||
{
|
||||
#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<IHostMessageFromWmsService>();
|
||||
}
|
||||
|
||||
#endregion Public Constructors
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public override bool DoWork(IHostMessage hostMessage)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
IHandleRecord<IHostMessage> handler = null;
|
||||
try
|
||||
{
|
||||
//handler = _unityContainer.Resolve<IHandleRecord>(hostMessage.RecordType);
|
||||
handler = (IHandleRecord<IHostMessage>)_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
|
||||
}
|
||||
}
|
||||
@@ -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<IHostMessage>
|
||||
{
|
||||
#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
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Fügt im Dictionary dem angegebenen Consumer das Messagem hinzu
|
||||
/// </summary>
|
||||
/// <param name="dataForConsumers"></param>
|
||||
/// <param name="consumer"></param>
|
||||
/// <param name="hostMessage">FromWms - IHostMessage</param>
|
||||
private void AddDataForConsumer(ref Dictionary<string, List<IHostMessage>> dataForConsumers, string consumer, IHostMessage hostMessage)
|
||||
{
|
||||
if (dataForConsumers.ContainsKey(consumer))
|
||||
{
|
||||
dataForConsumers[consumer].Add(hostMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
dataForConsumers.Add(consumer, new List<IHostMessage> { hostMessage });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bestimmt den Consumer, der für den Datensatz verantwortlich ist
|
||||
/// </summary>
|
||||
/// <param name="consumersWithDemand"></param>
|
||||
/// <param name="criterion"></param>
|
||||
/// <returns>Consumer Name, wenn ein passender Consumer in der lsite ist, null, wenn kein passender Consumer in der Liste ist</returns>
|
||||
private string GetConsumerForBooking(List<string> 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<string, List<IHostMessage>> GetDataForConsumers(List<string> consumersWithDemand)
|
||||
{
|
||||
//TODO: jub evtl. die Messagem Stau Meldung aus KW übernehmen
|
||||
Dictionary<string, List<IHostMessage>> dataForConsumers = new Dictionary<string, List<IHostMessage>>();
|
||||
try
|
||||
{
|
||||
using HostEntities db = new HostEntitiesFactory(HostEntities.DefaultConnectionStringName).Create();
|
||||
if (consumersWithDemand.Count > 0)
|
||||
{
|
||||
List<IHostMessage> 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<FromWms>(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<IHostMessageFromWmsService>();
|
||||
_consumerQueueLength = consumerQueueLength;
|
||||
_useLoadBalancing = useLoadBalancing;
|
||||
}
|
||||
|
||||
#endregion Public Constructors
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<in THostMessage> where THostMessage : IHostMessage
|
||||
{
|
||||
bool Handle(THostMessage arg);
|
||||
}
|
||||
}
|
||||
@@ -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<IAcknowledgeTransportCompleted>
|
||||
{
|
||||
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<int> 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<OrdersHost> 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<VLoopOverview> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<IDepartureNotification>
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<IHuChange>
|
||||
{
|
||||
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<SubdivisionType>() ?? 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<IShipmentTransportOrder>
|
||||
{
|
||||
private readonly IDestinationService _destinationService;
|
||||
private readonly ITransportOrderService _transportOrderService;
|
||||
private IShipmentTransportOrder _shipmentTransportOrder;
|
||||
private readonly LeFactory _leFactory = LeFactory.GetInstance();
|
||||
private readonly List<string> _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<string> allowedEtraBoxDestinations = new List<string>() {"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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<ITransportOrder>
|
||||
{
|
||||
private readonly IDestinationService _destinationService;
|
||||
private readonly ITransportOrderService _transportOrderService;
|
||||
private ITransportOrder _transportOrder;
|
||||
private readonly LeFactory _leFactory = LeFactory.GetInstance();
|
||||
private readonly List<string> _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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A Handling Unit that rests at a workplace is set to "On conveyer" when it receives a transport order
|
||||
/// </summary>
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
//-----------------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 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"
|
||||
// </auto-generated>
|
||||
//-----------------------------------------------------------------------------------------
|
||||
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<UnsupportedHostMessage>
|
||||
{
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<IHostMessageFromWmsService, HostMessageFromWmsService>();
|
||||
}
|
||||
|
||||
public static void RegisterFromWmsHandlers(this IUnityContainer unityContainer)
|
||||
{
|
||||
|
||||
unityContainer.RegisterType<IHandleRecord<TransportOrder>, TransportOrderHandler>("TransportOrder");
|
||||
|
||||
unityContainer.RegisterType<IHandleRecord<ShipmentTransportOrder>, ShipmentTransportOrderHandler>("ShipmentTransportOrder");
|
||||
|
||||
unityContainer.RegisterType<IHandleRecord<AcknowledgeTransportCompleted>, AcknowledgeTransportCompletedHandler>("AcknowledgeTransportCompleted");
|
||||
|
||||
unityContainer.RegisterType<IHandleRecord<DepartureNotification>, DepartureNotificationHandler>("DepartureNotification");
|
||||
|
||||
//unityContainer.RegisterType<IHandleRecord<RequestEmptyHuReport>, RequestEmptyHuReportHandler>("RequestEmptyHuReport");
|
||||
|
||||
unityContainer.RegisterType<IHandleRecord<HuChange>, HuChangeHandler>("HuChange");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace Gebhardt.StoreWare.Wcs.HostBooking.Properties {
|
||||
|
||||
|
||||
// This class allows you to handle specific events on the settings class:
|
||||
// The SettingChanging event is raised before a setting's value is changed.
|
||||
// The PropertyChanged event is raised after a setting's value is changed.
|
||||
// The SettingsLoaded event is raised after the setting values are loaded.
|
||||
// The SettingsSaving event is raised before the setting values are saved.
|
||||
internal sealed partial class Settings {
|
||||
|
||||
public Settings() {
|
||||
// // To add event handlers for saving and changing settings, uncomment the lines below:
|
||||
//
|
||||
// this.SettingChanging += this.SettingChangingEventHandler;
|
||||
//
|
||||
// this.SettingsSaving += this.SettingsSavingEventHandler;
|
||||
//
|
||||
}
|
||||
|
||||
private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) {
|
||||
// Add code to handle the SettingChangingEvent event here.
|
||||
}
|
||||
|
||||
private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) {
|
||||
// Add code to handle the SettingsSaving event here.
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
02_Analyse_Konzept/HostBooking/SOLL Zustand/.DS_Store
vendored
Normal file
BIN
02_Analyse_Konzept/HostBooking/SOLL Zustand/.DS_Store
vendored
Normal file
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 661 KiB |
Reference in New Issue
Block a user