tbf/TestBenchFramework/BenchControl/Output/FileWriters/Xml/Writer.cs

322 lines
10 KiB
C#

///
/// Copyright (c) 2017 Sensus Metering Systems
///
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using log4net;
using Config.Entities;
using TBF.BenchControl;
using TBF.Resources;
namespace TBF.BenchControl.Output.FileWriters.Xml
{
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<Results.WMeterRsltItemSpec> commonItems1;
IList<Results.WMeterRsltItemSpec> testItems;
IList<Results.WMeterRsltItemSpec> commonItems2;
string footer;
Thread thread1; /// Thread where one file is saved
Thread thread2; /// Thread where another file is saved
bool completed1; /// Set to 'true' by the first thread when writing to the file completed
bool completed2; /// Set to 'true' by the second thread when writing to the file completed
bool abort; /// Set to 'true' by Stop() operation to abort writing to the file
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);
commonItems1 = Results.WMeterRsltItemSpec.FromStrArray(writerCfg.CommonItems1);
testItems = Results.WMeterRsltItemSpec.FromStrArray(writerCfg.SelectedItems); /// TestID info will be overwritten later on
commonItems2 = Results.WMeterRsltItemSpec.FromStrArray(writerCfg.CommonItems2);
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.Separator = newCfg.Separator;
writerCfg.CommonItems1 = newCfg.CommonItems1;
writerCfg.SelectedItems = newCfg.SelectedItems;
writerCfg.CommonItems2 = newCfg.CommonItems2;
writerCfg.Header = newCfg.Header;
writerCfg.Footer = newCfg.Footer;
ApplyConfig();
}
}
};
}
#endregion Configuration Change Handling
/// <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, int wmPosition)
{
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, wmPosition);
}
catch
{
return string.Format("{0}{1:yyMMdd-HHmm}-{2:D2}.xml", directory, time, wmPosition);
}
}
/// <summary>
/// Writes the test cycle results into a file
/// Events:
/// Event.ResultsWritten
/// Event.Busy
/// </summary>
/// <param name="batch">Results to write into files</param>
/// <returns>Reference to the operation</returns>
public IOperation WriteResultsOp(Results.Entities.Batch batch)
{
thread1 = new Thread(() =>
{
WriteRslts(batch, writerCfg.DestinationPath);
completed1 = true;
});
thread2 = new Thread(() =>
{
WriteRslts(batch, writerCfg.DestinationPath2);
completed2 = true;
});
return this;
}
/// <summary>Start this operation</summary>
public void Start()
{
completed1 = false;
completed2 = false;
abort = false;
thread1.Start();
thread2.Start();
}
/// <summary>Run this operation</summary>
/// <returns>Event.ResultsWritten</returns>
public Event Run()
{
if (completed1 && completed2)
return Event.ResultsWritten;
else
return Event.Busy;
}
/// <summary>Stop this operation</summary>
public void Stop()
{
if (!completed1 || !completed2) abort = true;
}
void WriteRslts(Results.Entities.Batch batch, string destination)
{
foreach (var wm in batch.WaterMeters)
{
try
{
StreamWriter writer = File.AppendText(GetFilename(destination, batch.EndTime, wm.WMPosition));
WriteWM(writer, wm);
writer.Close();
if (abort) return;
}
catch
{
}
}
}
/// <summary>
/// Write one water meter results
/// </summary>
/// <param name="wmNr">Water meter number (0-based)</param>
void WriteWM(StreamWriter wr, Results.Entities.WaterMeter wm)
{
/// Header
if (!string.IsNullOrEmpty(writerCfg.Header)) wr.Write(header);
/// Common items 1
foreach (var v in commonItems1)
{
if (abort) return;
/// Fetch the item and strip color information
string itemText = v.Print(wm);
string[] texts = itemText.Split(new char[] { '|' });
if (texts.Length == 2) { itemText = texts[0]; }
wr.WriteLine(" <{0}>{1}</{0}>", v.Caption, itemText);
}
/// Test items
foreach (var mtr in wm.MeterTestRslts)
{
if (abort) return;
if ((mtr != null) && (mtr.Publish() == Config.Entities.Publish.Always))
{
wr.WriteLine(" <prueflauf>");
for (int i = 0; i < testItems.Count; i++)
{
/// Fetch the item and strip color information
string itemText = testItems[i].Print(wm, mtr.Name());
string[] texts = itemText.Split(new char[] { '|' });
if (texts.Length == 2) { itemText = texts[0]; }
/// Print the item
wr.WriteLine(" <{0}>{1}</{0}>", testItems[i].Caption, itemText);
}
wr.WriteLine(" </prueflauf>");
}
}
/// Common items 2
foreach (var v in commonItems2)
{
if (abort) return;
/// Fetch the item and strip color information
string itemText = v.Print(wm);
string[] texts = itemText.Split(new char[] { '|' });
if (texts.Length == 2) { itemText = texts[0]; }
wr.WriteLine(" <{0}>{1}</{0}>", v.Caption, itemText);
}
/// Footer
if (abort) return;
if (!string.IsNullOrEmpty(writerCfg.Footer)) wr.Write(footer);
}
}
}