/// /// Copyright (c) 2020 Sensus Slovensko a.s. /// using System; using System.Collections.Generic; using log4net; using NHibernate; using Common; using Events.Entities; using Results.Entities; namespace TBF.Rig.Various.StatisticsMonitoring { public class ProcessStatistics : ComponentBase, IOperation, GenericDevices.IStatisticsMonitoring { private static readonly ILog log = LogManager.GetLogger(typeof(ProcessStatistics)); public override string ToString() { return string.Format("{0}({1})", ClassName, Cfg.ToString(-1)); } readonly StatisticsCfg statisticsCfg; int lastBatchNr; public ProcessStatistics() {} public ProcessStatistics(Generic.IComponentCfg cfg) : base(cfg) { statisticsCfg = cfg as StatisticsCfg; } public override void Initialize() { ApplyConfig(); log.FatalFormat("{0} initialized: {1}", Name, this); } void ApplyConfig() { } #region Configuration Change Handling public static void OnCfgChange(object sender, CfgChangeArgs args) { if (CfgChangeHandler == null) return; try { CfgChangeHandler(sender, args); } catch (Exception e) { log.Error("CfgChangeHandler(...) failed", e); } } public static event EventHandler CfgChangeHandler; public override void StartChangeHandler() { CfgChangeHandler += delegate(object sender, CfgChangeArgs args) { StatisticsCfg newCfg = args.Cfg as StatisticsCfg; if (newCfg != null && newCfg.Name.Equals(Name)) { if (args.Command == CfgChangeCmd.CfgChange) { statisticsCfg.EventSource = newCfg.EventSource; statisticsCfg.iPerlHeadFailureRateThreshold = newCfg.iPerlHeadFailureRateThreshold; ApplyConfig(); } } }; } #endregion Configuration Change Handling /// /// Writes the test cycle results into a file /// Events: /// Event.StatisticsProcessed /// Event.Busy /// /// Results to write into a file /// Reference to the operation public IOperation ProcessStatisticsOp(int lastBatchNr) { this.lastBatchNr = lastBatchNr; return this; } public void Start() { Process(lastBatchNr, statisticsCfg); } public Event Run() { return Event.StatisticsProcessed; } public void Stop() { } /// /// Process recent batch results and calculate statistics of iPERL head failures /// /// Number of the last saved batch void Process(int batchNr, StatisticsCfg statisticsCfg) { const int RequiredUsages = 10; const int MaxBatches = 20; float iperlHeadFailureRateThreshold = (float)statisticsCfg.iPerlHeadFailureRateThreshold / 100.0f; int completeBatchesProcessed = 0; int[] usages = new int[TBF.Data.WMsCount]; int[] failures = new int[TBF.Data.WMsCount]; for (int i = 0; i < TBF.Data.WMsCount; i++) usages[i] = failures[i] = 0; /// /// Read results of recent batches (up to 'MaxBatches') /// Take into account only batches where the 1st test in named 'RFID' and /// at least one water meter completed all tests. /// try { ISession session = Results.DB.CreateSession(); int usagesMin; do { usagesMin = 0; IList batches = session.QueryOver() .Where(x => x.Id == batchNr) .List(); batchNr--; if (batches.Count != 1 || batches[0].TestRslts.Count < 1 || batches[0].TestRslts[0].MethodClass != "TestMethods.iPerlCommunication") { continue; } bool isACompleteBatch = false; foreach (var wm in batches[0].WaterMeters) { int i = wm.WMPosition - 1; if (0 <= i && i < TBF.Data.WMsCount && usages[i] < RequiredUsages && !wm.Disabled && wm.CompletedFromTests()) { isACompleteBatch = true; usages[i]++; if (!wm.MeterTestRslts[0].Passed) failures[i]++; } } usagesMin = int.MaxValue; for (int i = 0; i < TBF.Data.WMsCount; i++) if (usagesMin > usages[i]) usagesMin = usages[i]; if (isACompleteBatch) completeBatchesProcessed++; } while ((usagesMin < RequiredUsages) && (batchNr > 0) && (completeBatchesProcessed < MaxBatches)); } catch (Exception exc) { log.ErrorFormat("Failed to calculate iPERL head usage statistics: {0}", exc.Message); } /// /// Find out whether at lest one event needs to be triggered /// bool doTriggerEvents = false; for (int i = 0; i < TBF.Data.WMsCount; i++) { if ((usages[i] > 0) && (iperlHeadFailureRateThreshold > 0) && (float)failures[i] / (float)usages[i] >= iperlHeadFailureRateThreshold) { doTriggerEvents = true; break; } } if (doTriggerEvents && !string.IsNullOrEmpty(Events.DB.ConnectionString)) { /// /// Trigger events /// try { ISession session = Events.DB.CreateSession(); Events.DB.LoadSubscribers(session); for (int i = 0; i < TBF.Data.WMsCount; i++) { if ((usages[i] > 0) && (iperlHeadFailureRateThreshold > 0) && (float)failures[i] / (float)usages[i] >= iperlHeadFailureRateThreshold) { TBF.UiBridge.Bridge.TriggerEvent(session, statisticsCfg.EventSource, EventClass.EquipmentHW, Severity.Error, string.Format("Príliš veľa chýb hlavice č. {0}", i + 1), string.Format("Percento chýb je {0}%", (100 * failures[i]) / usages[i]), SubscriberGroup.Metrology | SubscriberGroup.Maintenance | SubscriberGroup.Production); } } } catch (Exception exc) { log.ErrorFormat("Failed to trigger events: source = {0}, message = {1}", statisticsCfg.EventSource, exc.Message); } } } } }