// Copyright (c) 2017 schick Informatik // Description: Diese Klasse überwacht Änderungen in Datenbankobjekten (durch zykl. Abfrage eines Threads // und Vgl. des "Changed" Felds bezogen auf den Beginn der Überwachung. Voraussetzung ist das die Entität ein // entsprechendes "Changed" Feld besitzt. (aktuell nur MessageCore, generisch nicht so einfach weil DatabaseEntity // aktuell eine abstrakte Klasse ist und daher nicht direkt instanziert werden kann (DBManager) using System; using System.Collections.Generic; using System.Timers; using bsmd.database; namespace ENI2.Util { internal class DatabaseEntityWatchdog { private readonly Dictionary _watchedEntities = new Dictionary(); private readonly object _entityLock = new object(); private readonly Timer bgTimer; public delegate void DatabaseEntityChangedHandler (DatabaseEntity entity); public event DatabaseEntityChangedHandler DatabaseEntityChanged; public event DatabaseEntityChangedHandler VisitTransitIdUpdated; public DatabaseEntityWatchdog() { this.bgTimer = new Timer(); this.bgTimer.Elapsed += BgTimer_Elapsed; this.bgTimer.AutoReset = true; this.bgTimer.Interval = Properties.Settings.Default.changeTimerTimeout; } public bool Idle => _watchedEntities.Count == 0; private void BgTimer_Elapsed(object sender, ElapsedEventArgs e) { lock (this._entityLock) { List changedCores = new List(); foreach (MessageCore watchedEntity in this._watchedEntities.Keys) { MessageCore entity = DBManager.GetSingleCon(Properties.Settings.Default.ConnectionString).GetMessageCoreById(watchedEntity.Id ?? Guid.Empty); if (entity != null) { // Der Core muss einen dieser Zustände einnehmen bevor das Highlighting erlaubt ist. Diese Zustände werden im Send Prozess gesetzt // (wenn die Nachrichten bearbeitet sind) bool isValidState = (entity.BSMDStatusInternal == MessageCore.BSMDStatus.FAILURE) || (entity.BSMDStatusInternal == MessageCore.BSMDStatus.SENT) || (entity.BSMDStatusInternal == MessageCore.BSMDStatus.PREPARE) || (entity.BSMDStatusInternal == MessageCore.BSMDStatus.RESPONDED); if (isValidState && ((entity.Changed > this._watchedEntities[watchedEntity]) || watchedEntity.IsHighlighted)) { OnDatabaseEntityChanged(entity); changedCores.Add(entity); } // Test ob eventuell Visit/Transit-ID inzwischen gesetzt wurde if(entity.IsTransit) { if (!entity.TransitId.IsNullOrEmpty() && watchedEntity.TransitId.IsNullOrEmpty() && !entity.TransitId.Equals(watchedEntity.TransitId)) { OnVisitTransitIdUpdated(entity); watchedEntity.TransitId = entity.TransitId; } } else if (!entity.VisitId.IsNullOrEmpty() && watchedEntity.VisitId.IsNullOrEmpty() && !entity.VisitId.Equals(watchedEntity.VisitId)) { OnVisitTransitIdUpdated(entity); watchedEntity.VisitId = entity.VisitId; } } } foreach (MessageCore changedCore in changedCores) this._watchedEntities[changedCore] = DateTime.Now; // nur einmal auslösen } } public void Register(MessageCore entity) { lock (this._entityLock) { if (!this._watchedEntities.ContainsKey(entity)) { this._watchedEntities.Add(entity, DateTime.Now); if (this._watchedEntities.Count > 0) this.bgTimer.Start(); } } } public void UnRegister(MessageCore entity) { lock (this._entityLock) { if (this._watchedEntities.ContainsKey(entity)) // hier wird mit Id verglichen (IEquatable impl.) this._watchedEntities.Remove(entity); if (this._watchedEntities.Count == 0) this.bgTimer.Stop(); } } #region private / protected protected void OnDatabaseEntityChanged(DatabaseEntity entity) { this.DatabaseEntityChanged?.Invoke(entity); } protected void OnVisitTransitIdUpdated(DatabaseEntity entity) { this.VisitTransitIdUpdated?.Invoke(entity); } #endregion } }