/// /// Copyright (c) 2019 Sensus Slovensko a.s. /// using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Threading; using log4net; using TBF.Resources; namespace TBF.BenchControl.Output.FileWriters.Elde { public class Writer : ComponentBase, IOperation, GenericDevices.IResultsWriter { private static readonly ILog log = LogManager.GetLogger(typeof(Writer)); public override string ToString() { return string.Format("Output.FileWriters.Enhanced({0})", Cfg.ToString(1)); } readonly WriterCfg writerCfg; string separatorStr; /// Items to print string header; IList commonItems; IList testItems; string footer; Results.Entities.Batch batch; bool abort; public Writer() {} public Writer(Generic.IComponentCfg cfg) : base(cfg) { writerCfg = cfg as WriterCfg; ApplyConfig(); log.Debug(this.ToString()); } void ApplyConfig() { switch (writerCfg.Separator) { default: case Separator.None: separatorStr = string.Empty; break; case Separator.Space: separatorStr = " "; break; case Separator.Tabulator: separatorStr = "\t"; break; case Separator.Comma: separatorStr = ","; break; case Separator.Semicolon: separatorStr = ";"; break; } header = string.IsNullOrEmpty(writerCfg.Header) ? string.Empty : writerCfg.Header.Replace("~", Environment.NewLine); commonItems = Results.WMeterRsltItemSpec.FromStrArray(new string[] { "72~Date / Time~~None~: {0:yyyy/MM/dd} / {0:HH:mm} Page : 1/1~~0~Left", "50~Watermeter Type~~None~: {0}~~0~Left", "64~Device No~~None~: {0}~~0~Left", "66~Personal~~None~: {0}~~0~Left", } ); testItems = Results.WMeterRsltItemSpec.FromStrArray(new string[] { "1~Test~~None~~~0~Left", "258~Met. t.~~None~~~0~Left", "257~Fl.~~None~~~0~Left", "25~Dev. [%]~~Pct~~~0~Left", "202~Flow [m3/h]~~m3ph~~F6~0~Left", "14~Test time [s]~~s~~F1~0~Left", "16~T1 [°C]~~C~~F1~0~Left", "17~T2 [°C]~~C~~F1~0~Left", "5~Density [kg/m3]~~kgpm3~~F3~0~Left", "131~Mass [kg]~~kg~~F3~0~Left", "19~Pressure [MPa]~~MPa~~F2~0~Left", "229~Volume [L]~~l~~F4~0~Left", } ); footer = string.IsNullOrEmpty(writerCfg.Footer) ? string.Empty : writerCfg.Footer.Replace("~", Environment.NewLine); } #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) { WriterCfg newCfg = args.Cfg as WriterCfg; if (newCfg != null && newCfg.Name.Equals(Name)) { if (args.Command == CfgChangeCmd.CfgChange) { writerCfg.DestinationPath = newCfg.DestinationPath; writerCfg.DestinationPath2 = newCfg.DestinationPath2; writerCfg.YearFolders = newCfg.YearFolders; writerCfg.MonthFolders = newCfg.MonthFolders; writerCfg.DayFolders = newCfg.DayFolders; writerCfg.FileNameFormat = newCfg.FileNameFormat; writerCfg.Culture = newCfg.Culture; writerCfg.Separator = newCfg.Separator; writerCfg.EliminateSpaces = newCfg.EliminateSpaces; writerCfg.Header = newCfg.Header; writerCfg.CommonItems = newCfg.CommonItems; writerCfg.SelectedItems = newCfg.SelectedItems; writerCfg.Footer = newCfg.Footer; ApplyConfig(); } } }; } #endregion Configuration Change Handling /// /// Eliminate spaces conditionally, depesing on bool WriterCfg.EliminateSpaces /// /// Input string /// Output string string ElSpaces(string item) { if (writerCfg.EliminateSpaces) return item.Replace(" ", string.Empty); else return item; } /// /// Returns a file name derived from a DateTime structure. /// Creates directories on this path as a side effect. /// /// Date and time /// File name string GetFilename(string destinationPath, DateTime time) { string directory = destinationPath; /// Ends with "\\"; switch (writerCfg.YearFolders) { case YearFolders.FourDigit: directory = string.Format("{0}{1:yyyy}\\", directory, time); break; case YearFolders.TwoDigit: directory = string.Format("{0}{1:yy}\\", directory, time); break; } switch (writerCfg.MonthFolders) { case MonthFolders.Name: { string monthStr; switch (time.Month) { default: case 1: monthStr = "January"; break; case 2: monthStr = "February"; break; case 3: monthStr = "March"; break; case 4: monthStr = "April"; break; case 5: monthStr = "May"; break; case 6: monthStr = "June"; break; case 7: monthStr = "July"; break; case 8: monthStr = "August"; break; case 9: monthStr = "September"; break; case 10: monthStr = "October"; break; case 11: monthStr = "November"; break; case 12: monthStr = "December"; break; } directory = string.Format("{0}{1}\\", directory, monthStr); break; } case MonthFolders.Digit: directory = string.Format("{0}{1}\\", directory, time.Month.ToString()); break; case MonthFolders.TwoDigit: directory = string.Format("{0}{1:MM}\\", directory, time); break; } switch (writerCfg.DayFolders) { case DayFolders.Digit: directory = string.Format("{0}{1}\\", directory, time.Day.ToString()); break; case DayFolders.TwoDigit: directory = string.Format("{0}{1:dd}\\", directory, time); break; } Directory.CreateDirectory(directory); try { if (string.IsNullOrEmpty(writerCfg.FileNameFormat)) throw new Exception(); return directory + string.Format(writerCfg.FileNameFormat, time); } catch { return string.Format("{0}{1:yyMMdd-HHmm}.txt", directory, time); } } /// /// Writes the test cycle results into a file /// Events: /// Event.ResultsWritten /// Event.Busy /// /// Results to write into a file /// Reference to the operation public IOperation WriteResultsOp(Results.Entities.Batch batch) { this.batch = batch; return this; } /// Start this operation public void Start() { CultureInfo oriCulture = Thread.CurrentThread.CurrentCulture; if (writerCfg.Culture != Culture.system) { Thread.CurrentThread.CurrentCulture = new CultureInfo(writerCfg.Culture.ToString()); } abort = false; WriteRslts(batch, writerCfg.DestinationPath); WriteRslts(batch, writerCfg.DestinationPath2); Thread.CurrentThread.CurrentCulture = oriCulture; } /// Run this operation /// Event.ResultsWritten public Event Run() { return Event.ResultsWritten; } /// Stop this operation public void Stop() { } void WriteRslts(Results.Entities.Batch batch, string destination) { if (batch == null || batch.WaterMeters == null || batch.WaterMeters.Count <= 0) return; if (!string.IsNullOrEmpty(destination)) { StreamWriter writer = StreamWriter.Null; try { writer = File.AppendText(GetFilename(destination, batch.EndTime)); WriteRsltsEx(batch, writer); log.WarnFormat("Batch {0} written by {1} to file {2}", batch.BatchNr, Name, destination); } catch { log.ErrorFormat("Error writing batch {0} by {1} to file {2}", batch.BatchNr, Name, destination); } finally { writer.Close(); } } } void WriteRsltsEx(Results.Entities.Batch batch, StreamWriter wrtr) { string toPrint; /// /// Get a sample water meter (to save common and test items) /// Results.Entities.WaterMeter sampleWM = null; foreach (var wm in batch.WaterMeters) { if (wm != null) { sampleWM = wm; break; } } if (sampleWM == null) return; /// No water meters ///---------- /// Header ///---------- if (!string.IsNullOrEmpty(writerCfg.Header)) wrtr.Write(header); wrtr.WriteLine(); ///---------------- /// Common items ///---------------- string[] leftColumn = new string[commonItems.Count]; string[] rightColumn = new string[commonItems.Count]; int cnt = 0; foreach (var item in commonItems) { leftColumn[cnt] = item.Caption; rightColumn[cnt] = item.Print(sampleWM); cnt++; } /// Determine max. left column width in characters int maxLen = 0; foreach (var s in leftColumn) if (s.Length > maxLen) maxLen = s.Length; /// Write aligned columns for (int i = 0; i < Math.Min(leftColumn.Length, rightColumn.Length); i++) { if (abort) return; wrtr.Write(leftColumn[i]); if (!writerCfg.EliminateSpaces) { wrtr.Write(new string(' ', maxLen - leftColumn[i].Length + 3)); } wrtr.Write(separatorStr); wrtr.WriteLine(rightColumn[i]); } ///--------- /// Tests ///--------- wrtr.WriteLine(); wrtr.WriteLine("Test Met. Fl. Dev. Flow Test time T1 T2 Density Mass Pressure Volume"); wrtr.WriteLine(" t. [%] [m3/h] [s] [°C] [°C] [kg/m3] [kg] [MPa] [L]"); wrtr.WriteLine("------------------------------------------------------------------------------------------------------------------"); int emptyTestsLeft = 5; int[] widths = new int[] { 11, 5, 6, 9, 12, 12, 8, 11, 10, 15, 8, 1 }; /// Widths of columns foreach (var testRslt in batch.TestRslts) { if (testRslt.Publish() == Config.Entities.Publish.Always) { for (int i = 0; i < Math.Min(testItems.Count, widths.Length); i++) { wrtr.Write(toPrint = testItems[i].Print(sampleWM, testRslt.Name())); for (int j = 0; j < Math.Max(1, widths[i] - toPrint.Length); j++) wrtr.Write(' '); } wrtr.WriteLine(); wrtr.WriteLine(); if (emptyTestsLeft > 0) emptyTestsLeft--; } } ///---------------- /// Water meters ///---------------- wrtr.WriteLine(); wrtr.WriteLine(); wrtr.WriteLine("------------------------------------------------------------------------------------------------------------------------------------------------"); wrtr.WriteLine("No | Prod. No | Initial Volume [L] | End Volume [L] | Deviation [%] |Sen| Result."); wrtr.WriteLine("------------------------------------------------------------------------------------------------------------------------------------------------"); wrtr.Write(" "); for (int i = 0; i < 3; i++) { foreach (var testRslt in batch.TestRslts) { if (testRslt.Publish() == Config.Entities.Publish.Always) { wrtr.Write(toPrint = testRslt.Name()); for (int j = 0; j < Math.Max(1, (i<2 ? 8 : 7) - toPrint.Length); j++) wrtr.Write(' '); } } for (int k = 0; k < emptyTestsLeft; k++) wrtr.Write("- "); } wrtr.WriteLine(); wrtr.WriteLine("------------------------------------------------------------------------------------------------------------------------------------------------"); foreach (var wm in batch.WaterMeters) { if (wm != null) { wrtr.Write(' '); wrtr.Write(toPrint = wm.WMPosition.ToString()); for (int j = 0; j < Math.Max(1, 3 - toPrint.Length); j++) wrtr.Write(' '); wrtr.Write(toPrint = wm.SerialNr); for (int j = 0; j < Math.Max(1, 14 - toPrint.Length); j++) wrtr.Write(' '); /// Volume start foreach (var mtr in wm.MeterTestRslts) { if (mtr.Publish() == Config.Entities.Publish.Always) { wrtr.Write(toPrint = mtr.VolumeStart.ToString("F2")); for (int j = 0; j < Math.Max(1, 8 - toPrint.Length); j++) wrtr.Write(' '); } } for (int k = 0; k < emptyTestsLeft; k++) wrtr.Write(" "); /// Volume end foreach (var mtr in wm.MeterTestRslts) { if (mtr.Publish() == Config.Entities.Publish.Always) { wrtr.Write(toPrint = mtr.VolumeEnd.ToString("F2")); for (int j = 0; j < Math.Max(1, 8 - toPrint.Length); j++) wrtr.Write(' '); } } for (int k = 0; k < emptyTestsLeft; k++) wrtr.Write(" "); /// Error foreach (var mtr in wm.MeterTestRslts) { if (mtr.Publish() == Config.Entities.Publish.Always) { wrtr.Write(toPrint = mtr.Error.ToString("F2")); for (int j = 0; j < Math.Max(1, 7 - toPrint.Length); j++) wrtr.Write(' '); } } for (int k = 0; k < emptyTestsLeft; k++) wrtr.Write(" "); wrtr.Write(" "); wrtr.Write(wm.Passed ? "OK" : "Not suitable"); wrtr.WriteLine(); } } wrtr.WriteLine("------------------------------------------------------------------------------------------------------------------------------------------------"); wrtr.WriteLine(); wrtr.Write("Producer: "); wrtr.Write(toPrint = sampleWM.WaterMeterData.Producer); for (int j = 0; j < Math.Max(1, 30 - toPrint.Length); j++) wrtr.Write(' '); wrtr.WriteLine("Explanation :"); wrtr.WriteLine(" M - Mass Method"); wrtr.WriteLine(" V - Volumetric Method"); wrtr.WriteLine(" P - Start / Stop"); wrtr.WriteLine(" F - Flow start"); wrtr.WriteLine(" S - Synchro"); wrtr.WriteLine(" C - Count imp."); wrtr.WriteLine(); wrtr.WriteLine("-------------------------------------------------------------------------"); ///---------- /// Footer ///---------- if (!string.IsNullOrEmpty(writerCfg.Footer)) wrtr.Write(footer); wrtr.Write('\f'); /// Form feed } } }