// Copyright (c) 2017 schick Informatik
// Description: Control für die Auftragsbearbeitung (Rahmen)
//
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Data;
using bsmd.database;
using ENI2.DetailViewControls;
using ENI2.Util;
using ENI2.EditControls;
namespace ENI2
{
///
/// Interaction logic for DetailRootControl.xaml
///
public partial class DetailRootControl : UserControl
{
#region Fields
private readonly List _listBoxList = new List();
private List _messages;
private readonly Dictionary controlCache = new Dictionary();
// private readonly Dictionary messageClassControlDict = new Dictionary();
private readonly object messageListLock = new object();
private readonly HighlightService highlightService = new HighlightService();
// Referenzen für Fehler/Violation Dialoge (können, müssen aber nicht offen bleiben)
protected ErrorListDialog _errorListDialog = null;
protected ViolationListDialog _violationListDialog = null;
#endregion
#region Properties
public MessageCore Core { get; private set; }
public bool LockedByOtherUser { get; set; }
public ReportingParty LockedBy { get; set; }
internal event DatabaseEntityWatchdog.DatabaseEntityChangedHandler HighlightReset;
internal event Action OpenNewCoreRequested;
internal event Action ReloadCoreRequested;
public bool HasUnsavedChanges
{
get { return this.buttonSave.Visibility == Visibility.Visible; } // schwach aber es wird's tun
}
public List HasUnsentMessages
{
get
{
// Bedingung:
// wenn in einer Meldeklasse zwar Daten vorhanden sind, eingespielt durch Excel import oder
// Handeingabe, diese aber NICHT gesendet wurden.
// TODO: Hier wird noch ein Flag benötigt, dass die erfolgte Anzeige des Warndialogs speichert
List result = new List();
foreach (Message aMessage in _messages)
{
if (((aMessage.InternalStatus == Message.BSMDStatus.UPDATED) ||
(aMessage.InternalStatus == Message.BSMDStatus.SAVED)) && // ||
// (aMessage.InternalStatus == Message.BSMDStatus.EXCEL)) &&
!aMessage.UnsentMessageWarningShown)
{
aMessage.UnsentMessageWarningShown = true;
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(aMessage);
result.Add(aMessage.MessageNotificationClassDisplay);
}
}
return result;
}
}
public List HasUnConfirmedMessages
{
get
{
List result = new List();
foreach(Message aMessage in _messages)
{
if (((aMessage.InternalStatus == Message.BSMDStatus.SENT) ||
(aMessage.InternalStatus == Message.BSMDStatus.TOSEND)) &&
!aMessage.UnconfirmedMessageWarningShown)
{
aMessage.UnconfirmedMessageWarningShown = true;
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(aMessage);
result.Add(aMessage.MessageNotificationClassDisplay);
}
}
return result;
}
}
#endregion
#region Construction
public DetailRootControl(MessageCore aCore)
{
Core = aCore;
InitializeComponent();
shipNameLabel.Text = aCore.Shipname;
displayIdLabel.Text = aCore.DisplayId;
// Listbox befüllen
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textOverview, MessageGroupControlType = typeof(OverViewDetailControl), ImagePath = "Resources/documents.png" });
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textPortCall, MessageGroupControlType = typeof(PortCallDetailControl), ImagePath = "Resources/eye_blue.png" });
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textPortNotification, MessageGroupControlType = typeof(PortNotificationDetailControl), ImagePath = "Resources/anchor.png" });
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textWaste, MessageGroupControlType = typeof(WasteDetailControl), ImagePath = "Resources/garbage.png" });
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textArrivalNotification, MessageGroupControlType = typeof(ArrivalNotificationDetailControl), ImagePath = "Resources/arrow_down_right_red.png" });
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textSecurity, MessageGroupControlType = typeof(SecurityDetailControl), ImagePath = "Resources/shield_yellow.png" });
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textPSC72h, MessageGroupControlType = typeof(PSC72hDetailControl), ImagePath = "Resources/alarmclock.png" });
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textMDH, MessageGroupControlType = typeof(MaritimeHealthDeclarationDetailControl), ImagePath = "Resources/medical_bag.png" });
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textDepartureNotification, MessageGroupControlType = typeof(DepartureNotificationDetailControl), ImagePath = "Resources/arrow_up_right_green.png" });
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textShipData, MessageGroupControlType = typeof(ShipDataDetailControl), ImagePath = "Resources/containership.png" });
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textBorderPolice, MessageGroupControlType = typeof(BorderPoliceDetailControl), ImagePath = "Resources/policeman_german.png" });
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textDGArrival, MessageGroupControlType = typeof(DangerousGoodsDetailControl), ImagePath = "Resources/sign_warning_radiation.png" });
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textDGDeparture, MessageGroupControlType = typeof(DangerousGoodsDetailControl), ImagePath = "Resources/sign_warning_radiation.png" });
this._listBoxList.Add(new MessageGroup() { MessageGroupName = Properties.Resources.textTowage, MessageGroupControlType = typeof(TowageDetailControl), ImagePath = "Resources/ship2.png" });
this.listBoxMessages.ItemsSource = this._listBoxList;
_messages = DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).GetMessagesForCore(Core, DBManager.MessageLoad.ALL);
List missingMessages = bsmd.database.Util.CreateMessagesForCore(Core, _messages, this.LockedBy);
_messages.AddRange(missingMessages);
BindingOperations.EnableCollectionSynchronization(_messages, this.messageListLock);
Dispatcher.BeginInvoke((Action)(() => this.listBoxMessages.SelectedIndex = 0));
}
#endregion
#region public methods
public void CoreChanged(MessageCore newCore)
{
this.Core = newCore;
Application.Current.Dispatcher.Invoke(delegate
{
if(controlCache.ContainsKey(Properties.Resources.textOverview))
{
OverViewDetailControl ovdc = controlCache[Properties.Resources.textOverview] as OverViewDetailControl;
ovdc.Core = newCore;
ovdc.UpdateCore();
}
});
}
public void ReloadCore()
{
this.DetailControl_RequestReload(this.Core.Id.Value);
}
#endregion
#region class MessageGroup
///
/// Klasse um ein Element der Listbox darzustellen (notwendig für das Databinding)
///
public class MessageGroup
{
public Type MessageGroupControlType { get; set; }
public string MessageGroupName { get; set; }
public string ImagePath { get; set; }
}
#endregion
#region event handler
private void listBoxMessages_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(listBoxMessages.SelectedItem != null)
{
MessageGroup mg = this.listBoxMessages.SelectedItem as MessageGroup;
if (!controlCache.ContainsKey(mg.MessageGroupName))
{
// create control instance for display:
DetailBaseControl detailControl = (DetailBaseControl)Activator.CreateInstance(mg.MessageGroupControlType);
// Spezial-Balkon für die Wiederverwendung von HAZD / HAZA als ein Control (es tut mir leid :D)
if (mg.MessageGroupName.Equals(Properties.Resources.textDGDeparture))
((DangerousGoodsDetailControl)detailControl).IsDeparture = true;
detailControl.Core = Core;
detailControl.Messages = _messages;
detailControl.LockedByOtherUser = this.LockedByOtherUser;
detailControl.JumpToListElementRequest += (index) =>
{
if ((index >= 0) && (index < _listBoxList.Count))
{
this.listBoxMessages.SelectedIndex = index;
}
};
detailControl.RequestReload += DetailControl_RequestReload;
detailControl.NotificationClassChanged += DetailControl_NotificationClassChanged;
detailControl.ResetControlCache += DetailControl_ResetControlCache;
detailControl.RequestValidate += DetailControl_RequestValidate;
detailControl.RequestDisable += DetailControl_RequestDisable;
detailControl.RequestCopy += DetailControl_RequestCopy;
detailControl.RequestSendValidation += DetailControl_RequestSendValidation;
detailControl.Initialize();
bool isEnabled = !this.LockedByOtherUser;
detailControl.SetEnabled(isEnabled);
if (!isEnabled && (detailControl is OverViewDetailControl control) && !(Core.Cancelled ?? false))
control.ShowLockedBy(this.LockedBy);
controlCache.Add(mg.MessageGroupName, detailControl);
this.buttonSave.Visibility = Visibility.Hidden;
}
else
{
// Control has been created before: Set visibility of "save" button:
bool hasDirtyMessages = false;
DetailBaseControl dbc = controlCache[mg.MessageGroupName];
foreach (Message message in dbc.ControlMessages)
if (message.IsDirty)
hasDirtyMessages = true;
this.buttonSave.Visibility = hasDirtyMessages ? Visibility.Visible : Visibility.Hidden;
}
// plug it in ;-)
detailView.Children.Clear();
// zuerst Vio dann Error
controlCache[mg.MessageGroupName].HighlightViolationMessageContainer();
controlCache[mg.MessageGroupName].HighlightErrorMessageContainer();
detailView.Children.Add(controlCache[mg.MessageGroupName]);
}
}
private void DetailControl_RequestCopy()
{
CopyDeclarationDialog cdd = new CopyDeclarationDialog();
MessageCore newCore = new MessageCore();
cdd.NewCore = newCore;
cdd.OldCore = this.Core;
cdd.Closed += (senderDialog, closeArgs) =>
{
CopyDeclarationDialog closedDialog = senderDialog as CopyDeclarationDialog;
if (closedDialog.IsOK)
{
MessageCore existingCore = null;
if (!newCore.IsDK && newCore.VisitId.IsNullOrEmpty() && newCore.TransitId.IsNullOrEmpty())
{
// deutsche Häfen fordern eine Visit-Id an, für DK erfolgt hier nur die Anlage eines Datensatzes
newCore.BSMDStatusInternal = MessageCore.BSMDStatus.TOSEND;
}
if (newCore.PoC.Equals("ZZNOK")) newCore.IsTransit = true;
newCore.Incoming = true;
newCore.DefaultReportingPartyId = this.LockedBy.Id;
if(!newCore.VisitId.IsNullOrEmpty() || !newCore.TransitId.IsNullOrEmpty())
{
if (newCore.IsTransit)
existingCore = DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).GetMessageCoreByTransitId(newCore.TransitId);
else
existingCore = DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).GetMessageCoreByVisitId(newCore.VisitId);
}
bool skipCopyTransit = false;
bool skipCopyVisit = false;
if(existingCore != null)
{
if(MessageBox.Show(Properties.Resources.textDeclarationAlreadyExists, Properties.Resources.textCaptionExists,
MessageBoxButton.OKCancel, MessageBoxImage.Question) == MessageBoxResult.OK)
{
List existingMessages = DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).GetMessagesForCore(existingCore, DBManager.MessageLoad.ALL);
foreach(Message existingMessage in existingMessages)
{
if((existingMessage.InternalStatus == Message.BSMDStatus.SENT) &&
(existingMessage.MessageNotificationClass != Message.NotificationClass.VISIT) &&
(existingMessage.MessageNotificationClass != Message.NotificationClass.TRANSIT))
{
MessageBox.Show(Properties.Resources.textMessagesAlreadySent, Properties.Resources.textCaptionError, MessageBoxButton.OK, MessageBoxImage.Stop);
return;
}
}
// delete all existing data of core
existingCore.ETA = newCore.ETA;
existingCore.ETAKielCanal = newCore.ETAKielCanal;
newCore = existingCore;
foreach (Message existingMessage in existingMessages)
{
// Bearbeitungsinformationen für bestehende ID-Beantragung beibehalten, falls bereits vorhanden
if(existingCore.IsTransit &&
(existingMessage.MessageNotificationClass == Message.NotificationClass.TRANSIT) &&
(existingMessage.InternalStatus == Message.BSMDStatus.CONFIRMED))
{
skipCopyTransit = true;
continue;
}
if(!existingCore.IsTransit &&
(existingMessage.MessageNotificationClass == Message.NotificationClass.VISIT) &&
(existingMessage.InternalStatus == Message.BSMDStatus.CONFIRMED))
{
skipCopyVisit = true;
continue;
}
if (existingMessage is ISublistContainer sublistContainer)
{
(sublistContainer).DeleteElements();
}
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Delete(existingMessage);
}
}
else
{
return;
}
}
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(newCore);
// Meldeklassen für neuen Anlauf erzeugen
List newMessages = new List();
foreach (Message oldMessage in this._messages)
{
if (skipCopyTransit && (oldMessage.MessageNotificationClass == Message.NotificationClass.TRANSIT)) continue;
if (skipCopyVisit && (oldMessage.MessageNotificationClass == Message.NotificationClass.VISIT)) continue;
// 10.9.19: bestimmte Meldeklassen nicht kopieren. Muss getestet werden, ob hier
// stattdessen "Leer"-Messages erzeugt werden müssen
if (oldMessage.MessageNotificationClass == Message.NotificationClass.ATA) continue;
if (oldMessage.MessageNotificationClass == Message.NotificationClass.ATD) continue;
if (oldMessage.MessageNotificationClass == Message.NotificationClass.NOA_NOD) continue;
bool isAndienKlasse = ((oldMessage.MessageNotificationClass == Message.NotificationClass.AGNT) ||
(oldMessage.MessageNotificationClass == Message.NotificationClass.STAT) ||
(oldMessage.MessageNotificationClass == Message.NotificationClass.INFO) ||
(oldMessage.MessageNotificationClass == Message.NotificationClass.HAZA) ||
(oldMessage.MessageNotificationClass == Message.NotificationClass.HAZD));
if (!cdd.CopyAll && !isAndienKlasse) continue;
Message newMessage = oldMessage.Clone() as Message;
newMessage.MessageCore = newCore;
newMessage.MessageCoreId = newCore.Id;
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(newMessage);
newMessage.SaveElements();
}
this.OnOpenNewCoreRequested(newCore);
}
};
cdd.Show();
}
private void DetailControl_RequestDisable()
{
this.LockedByOtherUser = true; // fake flag
// clear existing controls
this.detailView.Children.Clear();
this.controlCache.Clear();
// return to "new" overview
Dispatcher.BeginInvoke((Action)(() => this.listBoxMessages_SelectionChanged(this, null)));
}
private void DetailControl_ResetControlCache(string messageGroupName)
{
if (messageGroupName.IsNullOrEmpty()) return;
if (controlCache.ContainsKey(messageGroupName))
controlCache.Remove(messageGroupName);
}
private void buttonSave_Click(object sender, RoutedEventArgs e)
{
MessageBoxResult result = MessageBox.Show(Properties.Resources.textQuestionSavePage, Properties.Resources.textConfirmation,
MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
Util.UIHelper.SetBusyState();
if (this.detailView.Children[0] is DetailBaseControl currentControl)
{
foreach (Message message in currentControl.ControlMessages)
{
this.SaveMessage(message);
}
this.buttonSave.Visibility = Visibility.Hidden;
if (currentControl is OverViewDetailControl)
{
// ggf. hat sich die Ticketnr geändert..
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(currentControl.Core);
}
}
}
this.DetailControl_RequestReload(this.Core.Id.Value);
}
private void SaveMessage(Message message)
{
if (message.IsDirty || message.IsNew)
{
if ((message.Status == Message.MessageStatus.ACCEPTED) &&
((message.InternalStatus == Message.BSMDStatus.CONFIRMED) || (message.InternalStatus == Message.BSMDStatus.VIOLATION)))
message.InternalStatus = Message.BSMDStatus.UPDATED;
else
message.InternalStatus = Message.BSMDStatus.SAVED;
string userName = "?";
if(App.UserId.HasValue && DBManager.Instance.GetReportingPartyDict().ContainsKey(App.UserId.Value))
{
userName = DBManager.Instance.GetReportingPartyDict()[App.UserId.Value].Logon;
}
message.ChangedBy = string.Format("{0} at {1}", userName, DateTime.Now);
message.IsDirty = false;
message.UnsentMessageWarningShown = false;
message.UnconfirmedMessageWarningShown = false;
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(message);
message.SaveElements();
if(message.MessageNotificationClass == Message.NotificationClass.ATA)
{
DetailBaseControl currentControl = this.detailView.Children[0] as DetailBaseControl;
// ggf. hat sich die Ticketnr geändert..
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(currentControl.Core);
}
if(message.MessageNotificationClass == Message.NotificationClass.STAT)
{
DetailBaseControl currentControl = this.detailView.Children[0] as DetailBaseControl;
currentControl.Core.IsSmallShip = ((STAT)message.Elements[0]).GrossTonnage < 500;
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(currentControl.Core);
}
}
}
private void buttonSaveAll_Click(object sender, RoutedEventArgs e)
{
MessageBoxResult result = MessageBox.Show(Properties.Resources.textQuestionSaveAll, Properties.Resources.textConfirmation,
MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
Util.UIHelper.SetBusyState();
foreach (Message message in this._messages)
{
this.SaveMessage(message);
}
DetailBaseControl currentControl = this.detailView.Children[0] as DetailBaseControl;
if (currentControl is OverViewDetailControl)
{
// ggf. hat sich die Ticketnr geändert..
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(currentControl.Core);
}
this.buttonSaveAll.Visibility = Visibility.Hidden;
this.buttonSave.Visibility = Visibility.Hidden;
}
this.DetailControl_RequestReload(this.Core.Id.Value);
}
private void DetailControl_NotificationClassChanged(Message.NotificationClass notificationClass)
{
// in der Übersicht die Meldeklasse als geändert markieren..?
this.buttonSaveAll.Visibility = Visibility.Visible;
this.buttonSave.Visibility = Visibility.Visible;
}
/*
private void DetailControl_RequestLock(bool shouldLock)
{
if(App.LockingServiceClient == null)
{
return;
}
if (shouldLock)
{
try
{
Guid lockedUserId = App.LockingServiceClient.Lock(this.Core.Id.Value, this.userId);
if (lockedUserId == Guid.Empty)
{
// lock successful
this.Core.Locked = true;
}
else
{
// TODO: locking failed: Notify User
}
}
catch(Exception ex)
{
Trace.WriteLine(ex.ToString());
// TODO
}
}
else
{
App.LockingServiceClient.Unlock(this.Core.Id.Value, this.userId);
this.Core.Locked = false;
}
}
*/
private void DetailControl_RequestReload(Guid id)
{
/// core und messages neu laden
///
if (this.Core.Id.Value != id)
{
this.ReloadCoreRequested?.Invoke(id);
}
else
{
this.Core = DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).GetMessageCoreById(this.Core.Id.Value);
this._messages = DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).GetMessagesForCore(this.Core, DBManager.MessageLoad.ALL);
// clear existing controls
this.detailView.Children.Clear();
this.controlCache.Clear();
// return to "new" overview
Dispatcher.BeginInvoke((Action)(() => this.listBoxMessages_SelectionChanged(this, null)));
// if the entity has been highlighted (through remote change detection), reset this here
this.OnHighlightReset();
}
}
private void DetailControl_RequestSendValidation()
{
this.Validate(false, out _, out List errorList);
foreach (Message aMessage in this._messages)
{
if(aMessage.InternalStatus == Message.BSMDStatus.TOSEND)
{
foreach(MessageError messageError in errorList)
{
if(messageError.NotificationClass == aMessage.MessageNotificationClassDisplay)
{
aMessage.InternalStatus = Message.BSMDStatus.SUSPENDED;
aMessage.ChangedBy = "";
aMessage.StatusInfo = string.Format("Validation error: {0}", messageError.ErrorText);
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(aMessage);
break;
}
}
}
}
// NOA_NOD vor ATA vor ATD Versendereihenfolge sicherstellen
if ((this.Core.InitialHIS == Message.NSWProvider.DUDR) || (this.Core.InitialHIS == Message.NSWProvider.DUDR_TEST))
{
bool noa_nod_queued_or_sent = false;
bool ata_queued_or_sent = false;
foreach (Message aMessage in this._messages)
{
if (aMessage.MessageNotificationClass == Message.NotificationClass.NOA_NOD)
{
if ((aMessage.SendSuccess ?? false) || (aMessage.InternalStatus == Message.BSMDStatus.TOSEND))
noa_nod_queued_or_sent = true;
}
if (aMessage.MessageNotificationClass == Message.NotificationClass.ATA)
{
if ((aMessage.SendSuccess ?? false) || (aMessage.InternalStatus == Message.BSMDStatus.TOSEND))
ata_queued_or_sent = true;
}
}
foreach (Message aMessage in this._messages)
{
if ((aMessage.MessageNotificationClass == Message.NotificationClass.ATA) && (aMessage.InternalStatus == Message.BSMDStatus.TOSEND))
{
if (!noa_nod_queued_or_sent)
{
aMessage.InternalStatus = Message.BSMDStatus.SUSPENDED;
aMessage.ChangedBy = "";
aMessage.StatusInfo = string.Format("Validation error: NOA_NOD must be sent before ATA");
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(aMessage);
}
}
if ((aMessage.MessageNotificationClass == Message.NotificationClass.ATD) && (aMessage.InternalStatus == Message.BSMDStatus.TOSEND))
{
if (!noa_nod_queued_or_sent)
{
aMessage.InternalStatus = Message.BSMDStatus.SUSPENDED;
aMessage.ChangedBy = "";
aMessage.StatusInfo = string.Format("Validation error: NOA_NOD must be sent before ATD");
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(aMessage);
}
if (!ata_queued_or_sent)
{
aMessage.InternalStatus = Message.BSMDStatus.SUSPENDED;
aMessage.ChangedBy = "";
aMessage.StatusInfo = string.Format("Validation error: ATA must be sent before ATD");
DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).Save(aMessage);
}
}
}
}
}
private void DetailControl_RequestValidate()
{
this.Validate(true, out _, out _);
}
private void Validate(bool showMessages, out List vViolations, out List vErrors)
{
vViolations = new List();
vErrors = new List();
// TODO: clear highlighting
Util.UIHelper.SetBusyState();
RuleEngine ruleEngine = new RuleEngine();
foreach (Message aMessage in _messages)
{
if (!aMessage.EvaluateForValidation(this.Core.IsTransit)) continue;
List errors = new List();
List violations = new List();
ruleEngine.ValidateMessage(aMessage, out errors, out violations);
string messageGroup = this.MessageGroupForMessage(aMessage);
if (messageGroup != null)
{
foreach (MessageError me in errors)
me.MessageGroupName = messageGroup;
foreach (MessageViolation mv in violations)
mv.MessageGroupName = messageGroup;
}
vErrors.AddRange(errors);
vViolations.AddRange(violations);
}
#region 12.11.18 / 6.3.21: globale Plausi-Prüfungen
Message crewMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.CREW);
Message pasMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.PAS);
Message pobaMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.POBA);
Message secMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.SEC);
Message noanodMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.NOA_NOD);
#region CREW / PAS Count Plausibility
if (pobaMessage.Elements.Count == 1)
{
POBA poba = pobaMessage.Elements[0] as POBA;
if (crewMessage.Elements.Count != poba.TotalCrewMembersOnBoardUponArrival)
{
MessageViolation mv = RuleEngine.CreateViolation(ValidationCode.IMPLAUSIBLE, "POBA crew member count different from CREW count!", null, "Crew count mismatch", null, "CREW");
mv.MessageGroupName = Properties.Resources.textOverview;
vViolations.Add(mv);
}
if(pasMessage.Elements.Count != poba.TotalPassengersOnBoardUponArrival)
{
MessageViolation mv = RuleEngine.CreateViolation(ValidationCode.IMPLAUSIBLE, "POBA passenger count different from PAS count!", null, "Passenger count mismatch", null, "PAS");
mv.MessageGroupName = Properties.Resources.textOverview;
vViolations.Add(mv);
}
}
#endregion
#region Kiel Canal Timing Plausibility
if(!this.Core.IsTransit && (secMessage?.Elements.Count > 0) && (noanodMessage?.Elements.Count > 0))
{
SEC sec = secMessage.Elements[0] as SEC;
NOA_NOD noa_nod = noanodMessage.Elements[0] as NOA_NOD;
if (sec.KielCanalPassagePlanned ?? false)
{
// Überprüfung, ob die eingehende NOK-Durchfahrt auch wirklich innerhalb der eingehenden Reise liegt (bei VISIT)
bool isValidIncoming = (noa_nod.ETDFromLastPort < sec.KielCanalPassagePlannedIncomming) &&
(sec.KielCanalPassagePlannedIncomming < noa_nod.ETAToPortOfCall);
if (!sec.KielCanalPassagePlannedIncomming.HasValue) isValidIncoming = true;
if(!isValidIncoming)
{
MessageViolation mv = RuleEngine.CreateViolation(ValidationCode.IMPLAUSIBLE, "Kiel Canal incoming implausible (ETD / ETA ports)!", null, "Kiel Canal timing", null, "SEC");
mv.MessageGroupName = Properties.Resources.textOverview;
vViolations.Add(mv);
}
bool isValidOutgoing = (noa_nod.ETDFromPortOfCall < sec.KielCanalPassagePlannedOutgoing) &&
((noa_nod.NextPort == "ZZUKN") || (sec.KielCanalPassagePlannedOutgoing < noa_nod.ETAToNextPort));
if (!sec.KielCanalPassagePlannedOutgoing.HasValue) isValidOutgoing = true;
if(!isValidOutgoing)
{
MessageViolation mv = RuleEngine.CreateViolation(ValidationCode.IMPLAUSIBLE, "Kiel Canal outgoing implausible (ETD / ETA ports)!", null, "Kiel Canal timing", null, "SEC");
mv.MessageGroupName = Properties.Resources.textOverview;
vViolations.Add(mv);
}
}
}
#endregion
#region IMO plausibility
if (!this.Core.IMO.IsNullOrEmpty() && !bsmd.database.Util.IsIMOValid(this.Core.IMO))
{
MessageViolation mv = RuleEngine.CreateViolation(ValidationCode.IMPLAUSIBLE, "IMO number does not follow the rules", null, "GLOBAL");
mv.MessageGroupName = Properties.Resources.textOverview;
vViolations.Add(mv);
}
#endregion
#region last port plausibility SEC vs NOANOD
if((secMessage.Elements.Count > 0) && (noanodMessage.Elements.Count > 0))
{
SEC sec = secMessage.Elements[0] as SEC;
NOA_NOD noanod = noanodMessage.Elements[0] as NOA_NOD;
if((sec.LastTenPortFacilitesCalled.Count > 0) && (!sec.SECSimplification ?? false))
{
if((sec.LastTenPortFacilitesCalled[0].PortFacilityPortLoCode == null) || !sec.LastTenPortFacilitesCalled[0].PortFacilityPortLoCode.Equals(noanod.LastPort))
{
MessageViolation mv = RuleEngine.CreateViolation(ValidationCode.IMPLAUSIBLE, "SEC last port doesn't match NOANOD last port", null, "Last port mismatch", null, "SEC");
mv.MessageGroupName = Properties.Resources.textOverview;
vViolations.Add(mv);
}
DateTime comparison;
if (noanod.ETDFromLastPort.HasValue) comparison = noanod.ETDFromLastPort.Value.ToLocalTime().Date;
else comparison = DateTime.Now.Date;
if((sec.LastTenPortFacilitesCalled[0].PortFacilityDateOfDeparture == null) || (sec.LastTenPortFacilitesCalled[0].PortFacilityDateOfDeparture != comparison))
{
MessageViolation mv = RuleEngine.CreateViolation(ValidationCode.IMPLAUSIBLE, "SEC last port departure doesn't match NOANOD ETDFromLastPort", null, "Last port departure mismatch", null, "SEC");
mv.MessageGroupName = Properties.Resources.textOverview;
vViolations.Add(mv);
}
}
}
#endregion
#endregion
foreach (MessageError me in vErrors)
{
this.highlightService.HighlightError(me, this.GetContainerForMessageGroupName(me.MessageGroupName));
}
foreach (MessageViolation mv in vViolations)
{
this.highlightService.HighlightViolation(mv, this.GetContainerForMessageGroupName(mv.MessageGroupName));
}
// "neue" regelbasierte Validierung: Hier werden die einzelnen Regeln geprüft.
bsmd.database.ValidationRule.PrepareNameLookupDict(this.Core, this._messages);
List validationRules = DBManager.Instance.GetValidationRules();
foreach (bsmd.database.ValidationRule validationRule in validationRules) {
if (!validationRule.IsActive ?? false) continue;
// Regel auspacken
ConditionGroup cg = ValidationCondition.LoadFromString(validationRule.Rule);
// außer der Nachricht müssten noch die "gescheiterten" Felder zurückgeliefert werden, damit ein Highlighting stattfinden kann!
List failedFieldList = new List();
string resultMessage = string.Empty;
if (cg != null) {
if (ValidationCondition.CheckConditions(this.Core, this._messages, cg, failedFieldList, out resultMessage) ?? false)
{
// Regel hat zugeschlagen -> reporting
Trace.WriteLine(string.Format("Rule {0} resulted in {1}", validationRule.Name, resultMessage));
} else
{
Trace.WriteLine(string.Format("Rule {0} passed", validationRule.Name));
}
}
}
if (showMessages)
{
// Show error and violation dialog
if (vErrors.Count > 0)
{
if(this._errorListDialog == null)
{
this._errorListDialog = new ErrorListDialog();
this._errorListDialog.Closed += (o, e) => this._errorListDialog = null;
this._errorListDialog.Loaded += (o, e) => this._errorListDialog.RefreshVisible = true;
this._errorListDialog.ErrorSelected += _errorListDialog_ErrorSelected;
this._errorListDialog.RefreshClicked += _errorListDialog_RefreshClicked;
this._errorListDialog.Show();
}
this._errorListDialog.Errors = vErrors;
}
if (vViolations.Count > 0)
{
if(this._violationListDialog == null)
{
this._violationListDialog = new ViolationListDialog();
this._violationListDialog.Closed += (o, e) => this._violationListDialog = null;
this._violationListDialog.Loaded += (o, e) => this._violationListDialog.RefreshVisible = true;
this._violationListDialog.ViolationSelected += _errorListDialog_ErrorSelected;
this._violationListDialog.RefreshClicked += _errorListDialog_RefreshClicked;
this._violationListDialog.Show();
}
_violationListDialog.Violations = vViolations;
}
if((vErrors.Count == 0) && (vViolations.Count == 0))
{
MessageBox.Show(Properties.Resources.textValidationOK, Properties.Resources.textValidation, MessageBoxButton.OK, MessageBoxImage.Information);
}
}
}
private void _errorListDialog_RefreshClicked()
{
DetailControl_RequestValidate();
}
private void _errorListDialog_ErrorSelected(DatabaseEntity obj)
{
string msgGroupName = null;
if(obj is MessageError)
{
MessageError me = obj as MessageError;
msgGroupName = me.MessageGroupName;
}
else if(obj is MessageViolation)
{
MessageViolation mv = obj as MessageViolation;
msgGroupName = mv.MessageGroupName;
}
if(msgGroupName != null)
{
for(int i=0;i<_listBoxList.Count;i++)
{
if(_listBoxList[i].MessageGroupName.Equals(msgGroupName))
{
this.listBoxMessages.SelectedIndex = i;
break;
}
}
}
}
#endregion
#region private / protected methods
protected virtual void OnHighlightReset()
{
this.HighlightReset?.Invoke(this.Core);
}
protected virtual void OnOpenNewCoreRequested(MessageCore newCore)
{
this.OpenNewCoreRequested?.Invoke(newCore);
}
private DependencyObject GetContainerForMessageGroupName(string messageGroupName)
{
if (controlCache.ContainsKey(messageGroupName))
return controlCache[messageGroupName];
return null;
}
protected string MessageGroupForMessage(Message mh)
{
if (mh == null) return null;
switch(mh.MessageNotificationClass)
{
case Message.NotificationClass.ATA:
case Message.NotificationClass.TIEFA:
case Message.NotificationClass.POBA:
case Message.NotificationClass.BKRA:
return Properties.Resources.textArrivalNotification;
case Message.NotificationClass.BPOL:
case Message.NotificationClass.CREW:
case Message.NotificationClass.CREWD:
case Message.NotificationClass.PAS:
case Message.NotificationClass.PASD:
return Properties.Resources.textBorderPolice;
case Message.NotificationClass.HAZA:
return Properties.Resources.textDGArrival;
case Message.NotificationClass.HAZD:
return Properties.Resources.textDGDeparture;
case Message.NotificationClass.ATD:
case Message.NotificationClass.TIEFD:
case Message.NotificationClass.POBD:
case Message.NotificationClass.BKRD:
return Properties.Resources.textDepartureNotification;
case Message.NotificationClass.MDH:
return Properties.Resources.textMDH;
case Message.NotificationClass.NOA_NOD:
case Message.NotificationClass.AGNT:
return Properties.Resources.textPortCall;
case Message.NotificationClass.NAME:
case Message.NotificationClass.INFO:
case Message.NotificationClass.SERV:
case Message.NotificationClass.LADG:
return Properties.Resources.textPortNotification;
case Message.NotificationClass.PRE72H:
return Properties.Resources.textPSC72h;
case Message.NotificationClass.SEC:
return Properties.Resources.textSecurity;
case Message.NotificationClass.STAT:
return Properties.Resources.textShipData;
case Message.NotificationClass.TOWA:
case Message.NotificationClass.TOWD:
return Properties.Resources.textTowage;
case Message.NotificationClass.WAS:
return Properties.Resources.textWaste;
}
return null;
}
#endregion
}
}