/// /// Copyright (c) 2017 Sensus Metering Systems /// 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.OneFilePerMeter { 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; /// Set to 'true' by Stop() operation to abort writing to files 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(writerCfg.CommonItems); testItems = Results.WMeterRsltItemSpec.FromStrArray(writerCfg.SelectedItems); /// TestID info will be overwritten later on 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.CommonItems = newCfg.CommonItems; writerCfg.SelectedItems = newCfg.SelectedItems; writerCfg.Header = newCfg.Header; 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()); } 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) { StreamWriter writer = StreamWriter.Null; if (!string.IsNullOrEmpty(destination)) { try { writer = File.AppendText(GetFilename(destination, batch.EndTime)); } catch { } } WriteRsltsEx(batch, writer); writer.Close(); } void WriteRsltsEx(Results.Entities.Batch batch, StreamWriter wrtr) { ///---------- /// 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 v in commonItems) { leftColumn[cnt] = v.Caption; rightColumn[cnt] = (batch.WaterMeters.Count > 0) ? v.Print(batch.WaterMeters[0]) : string.Empty; 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]); wrtr.Write(new string(' ', maxLen - leftColumn[i].Length + 3)); wrtr.WriteLine(rightColumn[i]); } ///-------- /// Body ///-------- foreach (var wm in batch.WaterMeters) { WriteWM(wrtr, wm); if (abort) return; } ///---------- /// Footer ///---------- if (!string.IsNullOrEmpty(writerCfg.Footer)) wrtr.Write(footer); } /// /// Write one water meter results /// /// Water meter number (0-based) void WriteWM(StreamWriter wr, Results.Entities.WaterMeter wm) { wr.WriteLine(); /// Write water meter number and s/n #if BADGER_MALA_TRAT || BADGER_STREDNA_TRAT || BADGER_VELKA_TRAT wr.Write(string.Format("Water meter {0} s/n: {1}", wm.WMPosition, wm.SerialNr)); #else wr.Write(string.Format("{0} {1}", Strings.Water_Meter, wm.WMPosition)); if (!wm.Compound()) { if (!string.IsNullOrEmpty(wm.SerialNr)) { wr.Write(string.Format(", {0} {1}", Strings.SerialNr, wm.SerialNr)); } } else { if (!string.IsNullOrEmpty(wm.SerialNr)) { wr.Write(string.Format(", {0} {1}", Strings.Main_WM_SerialNr, wm.SerialNr)); } if (!string.IsNullOrEmpty(wm.SerialNrAux)) { wr.Write(string.Format(", {0} {1}", Strings.Aux_WM_SerialNr, wm.SerialNrAux)); } } #endif wr.WriteLine(); /// Determine column widths int[] columnWidths = new int[testItems.Count]; int totalWidth = 0; for (int i = 0; i < testItems.Count; i++) { if (abort) return; columnWidths[i] = ElSpaces(testItems[i].Caption).Length; foreach (var mtr in wm.MeterTestRslts) { if ((mtr != null) && (mtr.Publish() == Config.Entities.Publish.Always)) { string itemText = testItems[i].Print(wm, mtr.Name()); /// Strip color information string[] texts = itemText.Split(new char[] { '|' }); if (texts.Length == 2) { itemText = texts[0]; } int len = ElSpaces(itemText).Length; if (len > columnWidths[i]) columnWidths[i] = len; } } totalWidth += columnWidths[i]; } totalWidth += 3 * (testItems.Count - 1); if (totalWidth < 0) totalWidth = 0; string horizontalLine = new String('-', totalWidth); wr.WriteLine(horizontalLine); /// Horizontal line above the header /// Write column headers for (int i = 0; i < testItems.Count; i++) { if (abort) return; string caption = ElSpaces(testItems[i].Caption); wr.Write(caption); if (i < testItems.Count - 1) { if (!writerCfg.EliminateSpaces) { wr.Write(new string(' ', columnWidths[i] - testItems[i].Caption.Length + 3)); } wr.Write(separatorStr); } else { wr.WriteLine(); } } wr.WriteLine(horizontalLine); /// Horizontal line between the header and the body /// Write table data foreach (var mtr in wm.MeterTestRslts) { if (abort) return; if (mtr != null && mtr.IsPilotRslt() && mtr.Publish() == Config.Entities.Publish.Always) { for (int i = 0; i < testItems.Count; i++) { /// Fetch the item string itemText = testItems[i].Print(wm, mtr.Name()); /// Strip color information string[] texts = itemText.Split(new char[] { '|' }); if (texts.Length == 2) { itemText = texts[0]; } /// Print the item string itemText2 = ElSpaces(itemText); wr.Write(itemText2); if (i < testItems.Count - 1) { if (!writerCfg.EliminateSpaces) { wr.Write(new string(' ', columnWidths[i] - itemText.Length + 3)); } wr.Write(separatorStr); } else { wr.WriteLine(); } } } } wr.WriteLine(horizontalLine); /// Horizontal line below the body } } }