/// /// Copyright (c) 2013-2017 Sensus Metering Systems /// using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Threading; using log4net; namespace TBF.BenchControl.Output.FileWriters.Basic { 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.Basic({0})", Cfg.ToString(1)); } readonly WriterCfg writerCfg; string separatorStr; /// /// Result items to print /// string header; IList commonItems; IList rsltItems; 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(writerCfg.CommonItems); rsltItems = Results.WMeterRsltItemSpec.FromStrArray(writerCfg.SelectedItems); 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.SaveGoodOnly = newCfg.SaveGoodOnly; 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 (string.IsNullOrEmpty(destination) || batch == null || batch.WaterMeters == null || batch.WaterMeters.Count == 0) { return; } StreamWriter writer = StreamWriter.Null; try { writer = File.AppendText(GetFilename(destination, batch.EndTime)); WriteRsltsEx(batch, writer); } catch { } finally { writer.Close(); } } void WriteRsltsEx(Results.Entities.Batch batch, StreamWriter wrtr) { ///---------- /// Header ///---------- if (!string.IsNullOrEmpty(writerCfg.Header)) { wrtr.Write(header); } ///---------------- /// 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]); if (!writerCfg.EliminateSpaces) { wrtr.Write(new string(' ', maxLen - leftColumn[i].Length + 3)); } wrtr.Write(separatorStr); wrtr.WriteLine(rightColumn[i]); } wrtr.WriteLine(); ///-------- /// Body ///-------- WriteWMs(batch, wrtr); ///---------- /// Footer ///---------- if (!string.IsNullOrEmpty(writerCfg.Footer)) { wrtr.Write(footer); } } /// /// Write one water meter results /// /// Water meter number (0-based) void WriteWMs(Results.Entities.Batch batch, StreamWriter wr) { /// Determine column widths int[] columnWidths = new int[rsltItems.Count]; int totalWidth = 0; for (int i = 0; i < rsltItems.Count; i++) { if (abort) return; columnWidths[i] = ElSpaces(rsltItems[i].Caption).Length; foreach (var wm in batch.WaterMeters) { if (!writerCfg.SaveGoodOnly || wm.Passed) { string itemText = rsltItems[i].Print(wm); /// 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 * (rsltItems.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 < rsltItems.Count; i++) { if (abort) return; string caption = ElSpaces(rsltItems[i].Caption); wr.Write(caption); if (i < rsltItems.Count - 1) { if (!writerCfg.EliminateSpaces) { wr.Write(new string(' ', columnWidths[i] - rsltItems[i].Caption.Length + 3)); } wr.Write(separatorStr); } else { wr.WriteLine(); } } //wr.WriteLine(horizontalLine); /// Horizontal line between the header and the body int rowsCount = 0; foreach (var wm in batch.WaterMeters) { if (!writerCfg.SaveGoodOnly || wm.Passed) { if (abort) return; Results.WMeterRsltItemSpec.TableRowNr = ++rowsCount; for (int i = 0; i < rsltItems.Count; i++) { Results.WMeterRsltItemSpec.TableColumnNr = i + 1; /// Fetch the item string itemText = rsltItems[i].Print(wm); /// 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 < rsltItems.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 between the header and the body } } }