From 5932630635bf2bfec576979ba8de6e38efc3daf4 Mon Sep 17 00:00:00 2001 From: Daniel Schick Date: Tue, 27 Jan 2026 11:24:19 +0100 Subject: [PATCH] Validation now runs async and hopefully solves the problem of some users where the app got unresponsive in the meantime --- ENI2/DetailRootControl.xaml.cs | 218 +++++++++++++++++++++++---------- 1 file changed, 150 insertions(+), 68 deletions(-) diff --git a/ENI2/DetailRootControl.xaml.cs b/ENI2/DetailRootControl.xaml.cs index 3a7b4a1f..606acd8a 100644 --- a/ENI2/DetailRootControl.xaml.cs +++ b/ENI2/DetailRootControl.xaml.cs @@ -17,6 +17,7 @@ using ENI2.EditControls; using System.Windows.Input; using System.Linq; using ENI2.SheetDisplayControls; +using System.Threading.Tasks; namespace ENI2 { @@ -34,6 +35,9 @@ namespace ENI2 // private readonly Dictionary messageClassControlDict = new Dictionary(); private readonly object messageListLock = new object(); private readonly HighlightService highlightService = new HighlightService(); + private readonly object validationLock = new object(); + private Task activeValidationTask = null; + private bool pendingShowMessages = false; // Referenzen für Fehler/Violation Dialoge (können, müssen aber nicht offen bleiben) protected ErrorListDialog _errorListDialog = null; @@ -703,7 +707,13 @@ namespace ENI2 private void DetailControl_RequestSendValidation() { - this.Validate(false, out _, out List errorList); + DetailControl_RequestSendValidationAsync(); + } + + private async void DetailControl_RequestSendValidationAsync() + { + ValidationResult result = await ValidateAndApplyAsync(false); + List errorList = result.Errors; foreach (Message aMessage in this._messages) { @@ -777,22 +787,61 @@ namespace ENI2 } } - private void DetailControl_RequestValidate(bool showDialog) + private async void DetailControl_RequestValidate(bool showDialog) { - this.Validate(showDialog, out _, out _); + await ValidateAndApplyAsync(showDialog); } - private void Validate(bool showMessages, out List vViolations, out List vErrors) + private async Task ValidateAndApplyAsync(bool showMessages) { - vViolations = new List(); - vErrors = new List(); + Task validationTask = null; + lock (validationLock) + { + if (activeValidationTask != null && !activeValidationTask.IsCompleted) + { + if (showMessages) + pendingShowMessages = true; + validationTask = activeValidationTask; + } + else + { + pendingShowMessages = showMessages; + activeValidationTask = ValidateAndApplyCoreAsync(); + validationTask = activeValidationTask; + } + } - // TODO: clear highlighting + return await validationTask; + } + private async Task ValidateAndApplyCoreAsync() + { Util.UIHelper.SetBusyState(); + ApplyCrewEffectsWarningSuppression(); + + List messagesSnapshot = _messages.ToList(); + ValidationResult result = await Task.Run(() => ValidateCore(messagesSnapshot)); + + bool showMessages; + lock (validationLock) + { + showMessages = pendingShowMessages; + pendingShowMessages = false; + activeValidationTask = null; + } + + ApplyValidationResult(result, showMessages); + return result; + } + + private ValidationResult ValidateCore(List messagesSnapshot) + { + List vViolations = new List(); + List vErrors = new List(); + Dictionary counts = new Dictionary(); RuleEngine ruleEngine = new RuleEngine(); - foreach (Message aMessage in _messages) + foreach (Message aMessage in messagesSnapshot) { if (!aMessage.EvaluateForValidation(this.Core.IsTransit)) continue; @@ -800,20 +849,13 @@ namespace ENI2 List violations = new List(); ruleEngine.ValidateMessage(aMessage, out errors, out violations); - if (errors.Count > 0) - aMessage.ErrorCount = errors.Count; - else - aMessage.ErrorCount = null; - if (violations.Count > 0) + MessageValidationCounts messageCounts = new MessageValidationCounts { - aMessage.ViolationCount = violations.Count; - aMessage.PositionViolationCount = violations.Count(v => !v.Identifier.IsNullOrEmpty()); - } - else - { - aMessage.ViolationCount = null; - aMessage.PositionViolationCount = null; - } + ErrorCount = errors.Count > 0 ? (int?)errors.Count : null, + ViolationCount = violations.Count > 0 ? (int?)violations.Count : null, + PositionViolationCount = violations.Count > 0 ? (int?)violations.Count(v => !v.Identifier.IsNullOrEmpty()) : null + }; + counts[aMessage] = messageCounts; string messageGroup = this.MessageGroupForMessage(aMessage); @@ -831,20 +873,20 @@ namespace ENI2 #region 12.11.18 / 6.3.21 / 23.5.22 / 26.10.24: globale Plausi-Prüfungen - Message crewaMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.CREWA); - Message crewdMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.CREWD); - Message pasaMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.PASA); - Message pasdMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.PASD); - Message pobaMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.POBA); - Message pobdMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.POBD); - Message secMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.SEC); - Message noanodMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.NOA_NOD); - Message mdhMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.MDH); - Message was_rcptMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.WAS_RCPT); - Message wasMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.WAS); - Message servMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.SERV); - Message statMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.STAT); - Message pre72hMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.PRE72H); + Message crewaMessage = messagesSnapshot.Find(message => message.MessageNotificationClass == Message.NotificationClass.CREWA); + Message crewdMessage = messagesSnapshot.Find(message => message.MessageNotificationClass == Message.NotificationClass.CREWD); + Message pasaMessage = messagesSnapshot.Find(message => message.MessageNotificationClass == Message.NotificationClass.PASA); + Message pasdMessage = messagesSnapshot.Find(message => message.MessageNotificationClass == Message.NotificationClass.PASD); + Message pobaMessage = messagesSnapshot.Find(message => message.MessageNotificationClass == Message.NotificationClass.POBA); + Message pobdMessage = messagesSnapshot.Find(message => message.MessageNotificationClass == Message.NotificationClass.POBD); + Message secMessage = messagesSnapshot.Find(message => message.MessageNotificationClass == Message.NotificationClass.SEC); + Message noanodMessage = messagesSnapshot.Find(message => message.MessageNotificationClass == Message.NotificationClass.NOA_NOD); + Message mdhMessage = messagesSnapshot.Find(message => message.MessageNotificationClass == Message.NotificationClass.MDH); + Message was_rcptMessage = messagesSnapshot.Find(message => message.MessageNotificationClass == Message.NotificationClass.WAS_RCPT); + Message wasMessage = messagesSnapshot.Find(message => message.MessageNotificationClass == Message.NotificationClass.WAS); + Message servMessage = messagesSnapshot.Find(message => message.MessageNotificationClass == Message.NotificationClass.SERV); + Message statMessage = messagesSnapshot.Find(message => message.MessageNotificationClass == Message.NotificationClass.STAT); + Message pre72hMessage = messagesSnapshot.Find(message => message.MessageNotificationClass == Message.NotificationClass.PRE72H); #region CREW / PAS Count Plausibility @@ -984,22 +1026,6 @@ namespace ENI2 #endregion - #region 4.1.23 no CREW effects warning for DE - if (crewaMessage != null) - { - MessageViolation mv = crewaMessage.ViolationList.Find((x) => x.PropertyName.Equals("Effects") && (x.ViolationCode == (int)ValidationCode.TRUNCATE)); - if ((mv != null) && !Core.IsDK) - crewaMessage.ViolationList.Remove(mv); - } - - if(crewdMessage != null) - { - MessageViolation mvd = crewdMessage.ViolationList.Find((x) => x.PropertyName.Equals("Effects") && (x.ViolationCode == (int)ValidationCode.TRUNCATE)); - if ((mvd != null) && !Core.IsDK) - crewdMessage.ViolationList.Remove(mvd); - } - #endregion - #region WAS_RCPT double numbers Dictionary identDict = new Dictionary(); @@ -1337,17 +1363,8 @@ namespace ENI2 #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); + bsmd.database.ValidationRule.PrepareNameLookupDict(this.Core, messagesSnapshot); List validationRules = DBManager.Instance.GetValidationRules(); @@ -1374,10 +1391,34 @@ namespace ENI2 } } + return new ValidationResult(vViolations, vErrors, counts); + } + + private void ApplyValidationResult(ValidationResult result, bool showMessages) + { + // TODO: clear highlighting + foreach (KeyValuePair entry in result.Counts) + { + Message aMessage = entry.Key; + MessageValidationCounts messageCounts = entry.Value; + aMessage.ErrorCount = messageCounts.ErrorCount; + aMessage.ViolationCount = messageCounts.ViolationCount; + aMessage.PositionViolationCount = messageCounts.PositionViolationCount; + } + + foreach (MessageError me in result.Errors) + { + this.highlightService.HighlightError(me, this.GetContainerForMessageGroupName(me.MessageGroupName)); + } + foreach (MessageViolation mv in result.Violations) + { + this.highlightService.HighlightViolation(mv, this.GetContainerForMessageGroupName(mv.MessageGroupName)); + } + if (showMessages) { // Show error and violation dialog - if (vErrors.Count > 0) + if (result.Errors.Count > 0) { if(this._errorListDialog == null) { @@ -1392,10 +1433,10 @@ namespace ENI2 { this._errorListDialog.BringUp(); } - this._errorListDialog.Errors = vErrors; + this._errorListDialog.Errors = result.Errors; } - if (vViolations.Count > 0) + if (result.Violations.Count > 0) { if(this._violationListDialog == null) { @@ -1410,17 +1451,58 @@ namespace ENI2 { this._violationListDialog.BringUp(); } - _violationListDialog.Violations = vViolations; - } + _violationListDialog.Violations = result.Violations; + } - if((vErrors.Count == 0) && (vViolations.Count == 0)) + if((result.Errors.Count == 0) && (result.Violations.Count == 0)) { MessageBox.Show(Properties.Resources.textValidationOK, Properties.Resources.textValidation, MessageBoxButton.OK, MessageBoxImage.Information); } - } } + private void ApplyCrewEffectsWarningSuppression() + { + // 4.1.23 no CREW effects warning for DE + Message crewaMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.CREWA); + Message crewdMessage = _messages.Find(message => message.MessageNotificationClass == Message.NotificationClass.CREWD); + + if (crewaMessage != null) + { + MessageViolation mv = crewaMessage.ViolationList.Find((x) => x.PropertyName.Equals("Effects") && (x.ViolationCode == (int)ValidationCode.TRUNCATE)); + if ((mv != null) && !Core.IsDK) + crewaMessage.ViolationList.Remove(mv); + } + + if(crewdMessage != null) + { + MessageViolation mvd = crewdMessage.ViolationList.Find((x) => x.PropertyName.Equals("Effects") && (x.ViolationCode == (int)ValidationCode.TRUNCATE)); + if ((mvd != null) && !Core.IsDK) + crewdMessage.ViolationList.Remove(mvd); + } + } + + private sealed class ValidationResult + { + internal ValidationResult(List violations, List errors, Dictionary counts) + { + Violations = violations; + Errors = errors; + Counts = counts; + } + + internal List Violations { get; } + internal List Errors { get; } + internal Dictionary Counts { get; } + } + + private sealed class MessageValidationCounts + { + internal int? ErrorCount { get; set; } + internal int? ViolationCount { get; set; } + internal int? PositionViolationCount { get; set; } + } + private void _errorListDialog_RefreshClicked() { DetailControl_RequestValidate(true);