444 lines
14 KiB
C#
444 lines
14 KiB
C#
///
|
|
/// Copyright (c) 2017-2021 Sensus Slovensko a.s.
|
|
///
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Threading;
|
|
using log4net;
|
|
using Common;
|
|
using TBF.Resources;
|
|
|
|
namespace TBF.Rig.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("{0}({1})", ClassName, Cfg.ToString(-1)); }
|
|
|
|
readonly WriterCfg writerCfg;
|
|
|
|
string separatorStr;
|
|
|
|
///
|
|
/// Items to print
|
|
///
|
|
string header;
|
|
IList<Results.WMeterRsltItemSpec> commonItems;
|
|
IList<Results.WMeterRsltItemSpec> testItems;
|
|
string footer;
|
|
|
|
Results.Entities.Batch batch;
|
|
|
|
|
|
public Writer() {}
|
|
|
|
public Writer(Generic.IComponentCfg cfg)
|
|
: base(cfg)
|
|
{
|
|
writerCfg = cfg as WriterCfg;
|
|
}
|
|
|
|
public override void Initialize()
|
|
{
|
|
ApplyConfig();
|
|
log.FatalFormat("{0} initialized: {1}", Name, this);
|
|
}
|
|
|
|
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<CfgChangeArgs> 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
|
|
|
|
|
|
/// <summary>
|
|
/// Eliminate spaces conditionally, depesing on bool WriterCfg.EliminateSpaces
|
|
/// </summary>
|
|
/// <param name="item">Input string</param>
|
|
/// <returns>Output string</returns>
|
|
string ElSpaces(string item)
|
|
{
|
|
if (writerCfg.EliminateSpaces)
|
|
return item.Replace(" ", string.Empty);
|
|
else
|
|
return item;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Returns a file name derived from a DateTime structure.
|
|
/// Creates directories on this path as a side effect.
|
|
/// </summary>
|
|
/// <param name="time">Date and time</param>
|
|
/// <returns>File name</returns>
|
|
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))
|
|
{
|
|
return Path.Combine(directory, string.Format("{0:yyMMdd-HHmm}.txt", time));
|
|
}
|
|
else
|
|
{
|
|
return Path.Combine(directory, string.Format(writerCfg.FileNameFormat, time));
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
return Path.Combine(directory, string.Format("{0:yyMMdd-HHmm}.txt", time));
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Writes the test cycle results into a file
|
|
/// Events:
|
|
/// Event.ResultsWritten
|
|
/// Event.Busy
|
|
/// </summary>
|
|
/// <param name="batch">Results to write into a file</param>
|
|
/// <returns>Reference to the operation</returns>
|
|
public IOperation ProcessResultsOp(Results.Entities.Batch batch)
|
|
{
|
|
this.batch = batch;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>Start this operation</summary>
|
|
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;
|
|
}
|
|
|
|
/// <summary>Run this operation</summary>
|
|
/// <returns>Event.ResultsWritten</returns>
|
|
public Event Run()
|
|
{
|
|
return Event.ResultsWritten;
|
|
}
|
|
|
|
/// <summary>Stop this operation</summary>
|
|
public void Stop()
|
|
{
|
|
}
|
|
|
|
|
|
void WriteRslts(Results.Entities.Batch batch, string destination)
|
|
{
|
|
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)
|
|
{
|
|
///----------
|
|
/// 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++)
|
|
{
|
|
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);
|
|
}
|
|
|
|
///----------
|
|
/// Footer
|
|
///----------
|
|
if (!string.IsNullOrEmpty(writerCfg.Footer)) wrtr.Write(footer);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write one water meter results
|
|
/// </summary>
|
|
/// <param name="wmNr">Water meter number (0-based)</param>
|
|
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++)
|
|
{
|
|
columnWidths[i] = ElSpaces(testItems[i].Caption).Length;
|
|
foreach (var mtr in wm.RegularMeterTestRslts())
|
|
{
|
|
if ((mtr != null) && (mtr.Publish() == 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++)
|
|
{
|
|
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.RegularMeterTestRslts())
|
|
{
|
|
if (mtr != null && mtr.IsPilotRslt() && mtr.Publish() == 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
|
|
}
|
|
}
|
|
}
|