736 lines
31 KiB
C#
736 lines
31 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.ComponentModel;
|
|
|
|
namespace DTS.Slice.PedestrianAndHeadReports
|
|
{
|
|
/// <summary>
|
|
/// Generic class for handling graphs in reports
|
|
/// </summary>
|
|
public class ReportGraph : INotifyPropertyChanged
|
|
{
|
|
/*private string GetEngineeringUnits(DTS.Slice.Control.Event.Module.Channel channel)
|
|
{
|
|
if (channel is DTS.DAS.Concepts.DAS.Channel.IEngineeringUnitAware)
|
|
{
|
|
return (channel as DTS.DAS.Concepts.DAS.Channel.IEngineeringUnitAware).EngineeringUnits;
|
|
}
|
|
else { return "EU"; }
|
|
}*/
|
|
/// <summary>
|
|
/// colors for lines, start with blue, etc.
|
|
/// </summary>
|
|
private System.Drawing.Color[] COLORS = new System.Drawing.Color[] { System.Drawing.Color.Blue, System.Drawing.Color.Red, System.Drawing.Color.Green, System.Drawing.Color.Orange };
|
|
|
|
/// <summary>
|
|
/// we hold onto an actual chart object if we know what it is,
|
|
/// which we will once we've drawn it atleast once.
|
|
/// this lets us redraw if something changes
|
|
/// </summary>
|
|
private C1.Win.C1Chart.C1Chart _chart = null;
|
|
public C1.Win.C1Chart.C1Chart Chart { get { return _chart; } }
|
|
|
|
public void Draw() { Draw(_chart); }
|
|
public void Draw(C1.Win.C1Chart.C1Chart chart)
|
|
{
|
|
if (null == chart) { return; }
|
|
if (null == _chart) { _chart = chart; }
|
|
chart.ChartGroups[0].ChartData.SeriesList.Clear();
|
|
for (int i = 0; i < _channelIds.Count; i++)
|
|
{
|
|
chart.ChartGroups[0].ChartData.SeriesList.AddNewSeries();
|
|
GraphChannel gc = _channels[_channelIds[i]];
|
|
if (gc.Channel == null) { continue; }
|
|
|
|
chart.ChartGroups[0].ChartData.SeriesList[i].Label = gc.DisplayName;
|
|
chart.ChartGroups[0].ChartData.SeriesList[i].LineStyle.Color = COLORS[i];
|
|
chart.ChartGroups[0].ChartData.SeriesList[i].LineStyle.Thickness = 2;
|
|
chart.ChartGroups[0].ChartData.SeriesList[i].SymbolStyle.Shape = C1.Win.C1Chart.SymbolShapeEnum.None;
|
|
chart.ChartGroups[0].ChartData.SeriesList[i].PlotFilterMethod = C1.Win.C1Chart.PlotFilterMethodEnum.Alternative;
|
|
chart.ChartGroups[0].ChartData.SeriesList[i].PlotFilter = 5;
|
|
DTS.Slice.Control.Event.Module.Channel aic = gc.Channel.Channel as DTS.Slice.Control.Event.Module.Channel;
|
|
if (null == aic) { continue; }
|
|
double dCurrentTime = ((double)aic.ParentModule.StartRecordSampleNumber - (double)aic.ParentModule.TriggerSampleNumbers[0]) / (double)aic.ParentModule.SampleRateHz;
|
|
|
|
double dIncrement = 1/(double)aic.ParentModule.SampleRateHz;
|
|
if (TimeUnits == "ms") { dCurrentTime *= 1000; dIncrement *=1000;}
|
|
List<double> xAxis = new List<double>(500000);
|
|
List<double> yAxis = new List<double>(500000);
|
|
double dMin = double.MaxValue;
|
|
double dMax = double.MinValue;
|
|
double dTimeOfMax = double.NaN;
|
|
double dTimeOfMin = double.NaN;
|
|
double eu;
|
|
MeasurementUnit channelUnit = GetChannelUnit();
|
|
string actualUnit = GetEngineeringUnits(aic);
|
|
double scaleFactor = 1D;
|
|
try
|
|
{
|
|
scaleFactor = channelUnit.GetScalerConversionFrom(actualUnit);//GetScaleFactor(channelUnit, actualUnit);
|
|
System.Diagnostics.Trace.WriteLine("Scaling " + gc.Id + " by " + scaleFactor.ToString());
|
|
}
|
|
catch (System.Exception) { }
|
|
|
|
for (ulong z = 0; z < aic.ParentModule.NumberOfSamples; z++)
|
|
{
|
|
try
|
|
{
|
|
if (dCurrentTime >= DomainMin && dCurrentTime <= DomainMax)
|
|
{
|
|
eu = aic.DataEu[Convert.ToInt32(z)];
|
|
eu *= scaleFactor;
|
|
xAxis.Add(dCurrentTime);
|
|
yAxis.Add(eu);
|
|
if (dMax < eu) { dMax = eu; dTimeOfMax = dCurrentTime; }
|
|
if (dMin > eu) { dMin = eu; dTimeOfMin = dCurrentTime; }
|
|
}
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
DTS.Utilities.Logging.APILogger.Log("Failed to access sample at index: ", z, " of ", aic.ChannelDescriptionString, " which reports: ",
|
|
aic.ParentModule.NumberOfSamples, " samples.", ex);
|
|
if (z < (aic.ParentModule.NumberOfSamples - 1))
|
|
{
|
|
System.Windows.Forms.MessageBox.Show("Incomplete data for " + aic.ChannelDescriptionString + " please check data file: ", ex.Message);
|
|
}
|
|
}
|
|
dCurrentTime += dIncrement;
|
|
}
|
|
chart.ChartGroups[0].ChartData.SeriesList[i].X.CopyDataIn(xAxis.ToArray());
|
|
chart.ChartGroups[0].ChartData.SeriesList[i].Y.CopyDataIn(yAxis.ToArray());
|
|
if (double.MinValue != dMax)
|
|
{
|
|
if (aic is DTS.Slice.Control.Event.Module.AnalogInputChannel)
|
|
{
|
|
System.Diagnostics.Trace.WriteLine("Max for " + gc.Id + " is " + dMax.ToString() + " scale is " + aic.ScaleFactorMv.ToString());
|
|
}
|
|
gc.DataMax = dMax; gc.TimeOfMax = dTimeOfMax;
|
|
}
|
|
if (double.MaxValue != dMin) { gc.DataMin = dMin; gc.TimeOfMin = dTimeOfMin; }
|
|
}
|
|
int startIndex = chart.ChartGroups[0].ChartData.SeriesList.Count;
|
|
if (ThresholdsActive)
|
|
{
|
|
for (int i = 0; i < _thresholds.Count; i++)
|
|
{
|
|
chart.ChartGroups[0].ChartData.SeriesList.AddNewSeries();
|
|
chart.ChartGroups[0].ChartData.SeriesList[startIndex].LineStyle.Color = System.Drawing.Color.Black;
|
|
chart.ChartGroups[0].ChartData.SeriesList[startIndex].LineStyle.Thickness = 2;
|
|
chart.ChartGroups[0].ChartData.SeriesList[startIndex].SymbolStyle.Shape = C1.Win.C1Chart.SymbolShapeEnum.None;
|
|
chart.ChartGroups[0].ChartData.SeriesList[startIndex].X.CopyDataIn(new double[] { DomainMin, DomainMax });
|
|
chart.ChartGroups[0].ChartData.SeriesList[startIndex].Y.CopyDataIn(new double[] { _thresholds[i], _thresholds[i] });
|
|
startIndex++;
|
|
}
|
|
}
|
|
if (UseRangeMin) { chart.ChartArea.AxisY.AutoMin = false; chart.ChartArea.AxisY.Min = RangeMin; }
|
|
else { chart.ChartArea.AxisY.AutoMin = true; RangeMin = chart.ChartGroups[0].ChartData.MinY; }
|
|
if (UseRangeMax) { chart.ChartArea.AxisY.AutoMax = false; chart.ChartArea.AxisY.Max = RangeMax; }
|
|
else { chart.ChartArea.AxisY.AutoMax = true; RangeMax = chart.ChartGroups[0].ChartData.MaxY; }
|
|
chart.ChartArea.AxisX.Text = string.Format("Time ({0})", TimeUnits);
|
|
}
|
|
/// <summary>
|
|
/// since the CFC changes the channel data, we need to know when it changes and redraw accordingly
|
|
/// </summary>
|
|
/// <param name="cfc"></param>
|
|
public void SetCFC(string cfc, ReportBase report)
|
|
{
|
|
SensorDB.FilterClass fc = new DTS.SensorDB.FilterClass(cfc);
|
|
var e = _channels.GetEnumerator();
|
|
while (e.MoveNext())
|
|
{
|
|
if (null != e.Current.Value.Channel)
|
|
{
|
|
DTS.Slice.Control.Event.Module.Channel aic = e.Current.Value.Channel.Channel as DTS.Slice.Control.Event.Module.Channel;
|
|
if (null == aic) { return; }
|
|
switch (fc.FClass)
|
|
{
|
|
case DTS.SensorDB.FilterClass.FilterClassType.AdHoc:
|
|
throw new NotSupportedException();
|
|
case DTS.SensorDB.FilterClass.FilterClassType.CFC10:
|
|
aic.CurrentFilter = new DTS.Slice.Control.Event.Module.Channel.SaeJ211Filter(DTS.Utilities.ChannelFilter.Class10);
|
|
break;
|
|
case DTS.SensorDB.FilterClass.FilterClassType.CFC1000:
|
|
aic.CurrentFilter = new DTS.Slice.Control.Event.Module.Channel.SaeJ211Filter(DTS.Utilities.ChannelFilter.Class1000);
|
|
break;
|
|
case DTS.SensorDB.FilterClass.FilterClassType.CFC180:
|
|
aic.CurrentFilter = new DTS.Slice.Control.Event.Module.Channel.SaeJ211Filter(DTS.Utilities.ChannelFilter.Class180);
|
|
break;
|
|
case DTS.SensorDB.FilterClass.FilterClassType.CFC60:
|
|
aic.CurrentFilter = new DTS.Slice.Control.Event.Module.Channel.SaeJ211Filter(DTS.Utilities.ChannelFilter.Class60);
|
|
break;
|
|
case DTS.SensorDB.FilterClass.FilterClassType.CFC600:
|
|
aic.CurrentFilter = new DTS.Slice.Control.Event.Module.Channel.SaeJ211Filter(DTS.Utilities.ChannelFilter.Class600);
|
|
break;
|
|
case DTS.SensorDB.FilterClass.FilterClassType.None:
|
|
aic.CurrentFilter = new DTS.Slice.Control.Event.Module.Channel.SaeJ211Filter(DTS.Utilities.ChannelFilter.Unfiltered);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
report.DrawGraph(Id, _chart);
|
|
//Draw(_chart);
|
|
}
|
|
/// <summary>
|
|
/// gets the scale factor from two different units
|
|
/// assumes units are linearly scaled (example G's to m/sec^2)
|
|
/// </summary>
|
|
/// <param name="desiredUnits"></param>
|
|
/// <param name="actualUnits"></param>
|
|
/// <returns></returns>
|
|
public static double GetScaleFactor(string desiredUnits, string actualUnits)
|
|
{
|
|
actualUnits = actualUnits.Trim();
|
|
//1 g = 9.80665 m / s2
|
|
desiredUnits = desiredUnits.Trim().ToLower();
|
|
actualUnits = actualUnits.Trim().ToLower();
|
|
|
|
if ( desiredUnits == actualUnits)
|
|
{
|
|
return 1D;
|
|
}
|
|
else if (actualUnits == "g" && desiredUnits.Contains("m/sec") )
|
|
{
|
|
return 9.80665D;
|
|
}
|
|
else if (actualUnits.Contains("m/sec") && desiredUnits == "g")
|
|
{
|
|
return 1D / 9.80665D;
|
|
}//"N", "kN"
|
|
else if (actualUnits == "n" && desiredUnits == "kn")
|
|
{
|
|
return .001D;
|
|
}
|
|
else if (actualUnits == "kn" && desiredUnits == "n")
|
|
{
|
|
return 1000D;
|
|
}
|
|
else { throw new NotSupportedException("unknown conversion"); }
|
|
}
|
|
/// <summary>
|
|
/// gets a value of a channel field in the graph
|
|
/// (properties of channels)
|
|
/// </summary>
|
|
/// <param name="id">channel id</param>
|
|
/// <param name="field">field</param>
|
|
/// <returns>value</returns>
|
|
public string GetValue(string id, GraphChannel.Fields field)
|
|
{
|
|
return _channels[id].GetValue(field);
|
|
}
|
|
public string GetValueTrunc2Places(string id, GraphChannel.Fields field)
|
|
{
|
|
return _channels[id].GetValueTrunc2Places(field);
|
|
}
|
|
/// <summary>
|
|
/// gets a value of a graph field
|
|
/// (properties of the graph)
|
|
/// </summary>
|
|
/// <param name="field"></param>
|
|
/// <returns></returns>
|
|
public string GetValue(Fields field)
|
|
{
|
|
switch (field)
|
|
{
|
|
case Fields.DomainMax:
|
|
return DomainMax.ToString();
|
|
case Fields.DomainMin:
|
|
return DomainMin.ToString();
|
|
case Fields.RangeMax:
|
|
return RangeMax.ToString(Properties.Settings.Default.PROTECTIONREPORT_NumberFormat);
|
|
case Fields.RangeMin:
|
|
return RangeMin.ToString(Properties.Settings.Default.PROTECTIONREPORT_NumberFormat);
|
|
case Fields.ThresholdInUse:
|
|
return ThresholdsActive.ToString();
|
|
case Fields.Thresholds:
|
|
return Thresholds;
|
|
case Fields.UseRangeMax:
|
|
return UseRangeMax.ToString();
|
|
case Fields.UseRangeMin:
|
|
return UseRangeMin.ToString();
|
|
default:
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// sets the value for a field of the graph
|
|
/// (properties of graph)
|
|
/// </summary>
|
|
/// <param name="field"></param>
|
|
/// <param name="value"></param>
|
|
public void SetValue(Fields field, string value)
|
|
{
|
|
switch (field)
|
|
{
|
|
case Fields.DomainMax:
|
|
{
|
|
double d;
|
|
if (double.TryParse(value.Trim(), out d))
|
|
{
|
|
DomainMax = d;
|
|
}
|
|
}
|
|
break;
|
|
case Fields.DomainMin:
|
|
{
|
|
double d;
|
|
if (double.TryParse(value.Trim(), out d))
|
|
{
|
|
DomainMin = d;
|
|
}
|
|
}
|
|
break;
|
|
case Fields.RangeMax:
|
|
{
|
|
double d;
|
|
if (double.TryParse(value.Trim(), out d))
|
|
{
|
|
RangeMax = d;
|
|
}
|
|
}
|
|
break;
|
|
case Fields.RangeMin:
|
|
{
|
|
double d;
|
|
if (double.TryParse(value.Trim(), out d))
|
|
{
|
|
RangeMin = d;
|
|
}
|
|
}
|
|
break;
|
|
case Fields.ThresholdInUse:
|
|
{
|
|
bool b;
|
|
if (Boolean.TryParse(value.Trim(), out b))
|
|
{
|
|
ThresholdsActive = b;
|
|
}
|
|
}
|
|
break;
|
|
case Fields.Thresholds:
|
|
{
|
|
Thresholds = value;
|
|
}
|
|
break;
|
|
case Fields.UseRangeMax:
|
|
{
|
|
UseRangeMax = Convert.ToBoolean(value.Trim());
|
|
}
|
|
break;
|
|
case Fields.UseRangeMin:
|
|
{
|
|
UseRangeMin = Convert.ToBoolean(value.Trim());
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// whether graph minimum range is constrained
|
|
/// </summary>
|
|
private bool _useRangeMin = false;
|
|
public bool UseRangeMin
|
|
{
|
|
get { return _useRangeMin; }
|
|
set
|
|
{
|
|
SetProperty(ref _useRangeMin, value, "UseRangeMin");
|
|
Draw(_chart);
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// whether graph maximum range is constrained
|
|
/// </summary>
|
|
private bool _useRangeMax = false;
|
|
public bool UseRangeMax
|
|
{
|
|
get { return _useRangeMax; }
|
|
set
|
|
{
|
|
SetProperty(ref _useRangeMax, value, "UseRangeMax");
|
|
Draw(_chart);
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// fields/properties of a graph
|
|
/// </summary>
|
|
public enum Fields
|
|
{
|
|
RangeMin,
|
|
UseRangeMin,
|
|
RangeMax,
|
|
UseRangeMax,
|
|
DomainMin,
|
|
DomainMax,
|
|
Thresholds,
|
|
ThresholdInUse
|
|
}
|
|
/// <summary>
|
|
/// time units for the graph (seconds or ms)
|
|
/// </summary>
|
|
private string _timeUnits = "s";
|
|
public string TimeUnits
|
|
{
|
|
get { return _timeUnits; }
|
|
set
|
|
{
|
|
if (_timeUnits != value)
|
|
{
|
|
if (value == "ms") { DomainMin = _domainMin * 1000D; DomainMax = _domainMax * 1000D; }
|
|
else { DomainMin = _domainMin / 1000D; DomainMax = _domainMax / 1000D; }
|
|
SetProperty(ref _timeUnits, value, "TimeUnits");
|
|
//convert domain min/max to new units..
|
|
Draw(_chart);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// these functions make us WPF/dependency property friendly, and also
|
|
/// adds a convenient way for us to notify consumers when a property has changed
|
|
/// </summary>
|
|
public event PropertyChangedEventHandler PropertyChanged;
|
|
|
|
protected bool SetProperty<T>(ref T storage, T value, String propertyName)
|
|
{
|
|
if (object.Equals(storage, value)) return false;
|
|
|
|
storage = value;
|
|
this.OnPropertyChanged(propertyName);
|
|
return true;
|
|
}
|
|
protected void OnPropertyChanged(string propertyName)
|
|
{
|
|
var eventHandler = this.PropertyChanged;
|
|
if (eventHandler != null)
|
|
{
|
|
eventHandler(this, new PropertyChangedEventArgs(propertyName));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// graph id
|
|
/// </summary>
|
|
private string _id;
|
|
public string Id { get { return _id; } }
|
|
|
|
/// <summary>
|
|
/// display name for graph
|
|
/// </summary>
|
|
private string _displayName;
|
|
public string DisplayName { get { return _displayName; } set { SetProperty(ref _displayName, value, "DisplayName"); } }
|
|
|
|
/// <summary>
|
|
/// the minimum range of the graph (use UseRangeMin) to see if the graph is constrained to min or not
|
|
/// </summary>
|
|
private double _rangeMin;
|
|
public double RangeMin
|
|
{
|
|
get { return _rangeMin; }
|
|
set
|
|
{
|
|
if (value != _rangeMin)
|
|
{
|
|
SetProperty(ref _rangeMin, value, "RangeMin");
|
|
if (UseRangeMin) { Draw(_chart); }
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// the maximum range of the graph (use UseRangeMax to determine if graph is constrained to max or not)
|
|
/// </summary>
|
|
private double _rangeMax;
|
|
public double RangeMax
|
|
{
|
|
get { return _rangeMax; }
|
|
set
|
|
{
|
|
if (value != _rangeMax)
|
|
{
|
|
SetProperty(ref _rangeMax, value, "RangeMax");
|
|
if (UseRangeMax) { Draw(_chart); }
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// graph is always domain constrained, this is the min x axis value
|
|
/// default is -.5s
|
|
/// </summary>
|
|
private double _domainMin = -.5;
|
|
public double DomainMin
|
|
{
|
|
get { return _domainMin; }
|
|
set
|
|
{
|
|
SetProperty(ref _domainMin, value, "DomainMin");
|
|
Draw(_chart);
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// max x axis value (default is .5s)
|
|
/// </summary>
|
|
private double _domainMax = .5;
|
|
public double DomainMax
|
|
{
|
|
get { return _domainMax; }
|
|
set
|
|
{
|
|
if (value != _domainMax)
|
|
{
|
|
SetProperty(ref _domainMax, value, "DomainMax");
|
|
Draw(_chart);
|
|
}
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// collection of thresholds (just horizontal lines for now)
|
|
/// </summary>
|
|
private List<double> _thresholds = new List<double>();
|
|
public string Thresholds
|
|
{
|
|
get
|
|
{
|
|
List<string> thresholds = new List<string>();
|
|
foreach (var d in _thresholds)
|
|
{
|
|
if (!thresholds.Contains(d.ToString())) { thresholds.Add(d.ToString()); }
|
|
}
|
|
return string.Join(",", thresholds.ToArray());
|
|
}
|
|
set
|
|
{
|
|
string[] tokens = value.Split(new char[] { ',' });
|
|
List<double> thresholds = new List<double>();
|
|
foreach (string token in tokens)
|
|
{
|
|
double d;
|
|
if (double.TryParse(token.Trim(), out d))
|
|
{
|
|
if (!thresholds.Contains(d)) { thresholds.Add(d); }
|
|
}
|
|
}
|
|
SetProperty(ref _thresholds, thresholds, "Thresholds");
|
|
if (ThresholdsActive) { Draw(_chart); }
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// draw thresholds or not
|
|
/// </summary>
|
|
private bool _bThresholdsActive = false;
|
|
public bool ThresholdsActive
|
|
{
|
|
get { return _bThresholdsActive; }
|
|
set { SetProperty(ref _bThresholdsActive, value, "ThresholdsActive"); Draw(_chart); }
|
|
}
|
|
/// <summary>
|
|
/// list of channel ids for channels in the graph
|
|
/// </summary>
|
|
private List<string> _channelIds = new List<string>();
|
|
private Dictionary<string, GraphChannel> _channels = new Dictionary<string, GraphChannel>();
|
|
private MeasurementUnit[] _channelUnits;
|
|
public MeasurementUnit[] GetChannelUnits() { return _channelUnits; }
|
|
|
|
private MeasurementUnit _channelUnit;
|
|
public MeasurementUnit GetChannelUnit() { return _channelUnit; }
|
|
public void SetChannelUnit(MeasurementUnit mu)
|
|
{
|
|
SetProperty(ref _channelUnit, mu, "ChannelUnit");
|
|
var e = _channels.GetEnumerator();
|
|
while (e.MoveNext())
|
|
{
|
|
string eu = mu.MainDisplayUnit;
|
|
if (null == e.Current.Value) { continue; }
|
|
if (null == e.Current.Value.Channel) { continue; }
|
|
if (null == e.Current.Value.Channel.Channel) { continue; }
|
|
if (e.Current.Value.Channel.Channel is DTS.DAS.Concepts.DAS.Channel.IEngineeringUnitAware)
|
|
{
|
|
if (!(e.Current.Value.Channel.Channel is DTS.Slice.Control.Event.Module.AnalogInputChannel))
|
|
{
|
|
continue;
|
|
}
|
|
eu = ((DTS.DAS.Concepts.DAS.Channel.IEngineeringUnitAware)e.Current.Value.Channel.Channel).EngineeringUnits;
|
|
if (!mu.UnitMatches(eu))
|
|
{
|
|
double scaler = mu.GetScalerConversionFrom(eu);
|
|
System.Diagnostics.Trace.WriteLine("Changed scale factor for " + e.Current.Value.Channel.Channel.ChannelDescriptionString + " from "
|
|
+ e.Current.Value.Channel.Channel.ScaleFactorMv.ToString() + " to " + (e.Current.Value.Channel.Channel.ScaleFactorMv * scaler).ToString());
|
|
|
|
e.Current.Value.Channel.Channel.ScaleFactorMv *= scaler;
|
|
((DTS.DAS.Concepts.DAS.Channel.IEngineeringUnitAware)e.Current.Value.Channel.Channel).EngineeringUnits = mu.ToString();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public ReportGraph(string id, string displayname, MeasurementUnit [] possibleUnits, GraphChannel[] channels, string thresholds)
|
|
{
|
|
_id = id;
|
|
DisplayName = displayname;
|
|
_channelUnits = possibleUnits;
|
|
if (null != possibleUnits && possibleUnits.Length >= 1)
|
|
{
|
|
_channelUnit = possibleUnits[0];
|
|
}
|
|
foreach (var channel in channels) { _channelIds.Add(channel.Id); _channels.Add(channel.Id, channel); channel.PropertyChanged += new PropertyChangedEventHandler(channel_PropertyChanged); }
|
|
Thresholds = thresholds;
|
|
}
|
|
/// <summary>
|
|
/// pass on when a channel or graph property changes
|
|
/// this lets UI code react when a property changes
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
public void channel_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
|
{
|
|
if (e.PropertyName == "Channel")
|
|
{
|
|
OnPropertyChanged("Channel");
|
|
}
|
|
else
|
|
{
|
|
GraphChannel gc = sender as GraphChannel;
|
|
OnPropertyChanged("graph-x-" + Id + "-x-" + gc.Id + "-x-" + e.PropertyName);
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// the reviewtestchannel is the actual analoginputdaschannel, or the actual binary data for the file
|
|
/// </summary>
|
|
/// <param name="graphchannel"></param>
|
|
/// <returns></returns>
|
|
public ReviewTestChannel GetReviewTestChannel(string graphchannel)
|
|
{
|
|
return _channels[graphchannel].Channel;
|
|
}
|
|
public void SetReviewTestChannel(string graphchannel, ReviewTestChannel channel)
|
|
{
|
|
if (null != channel)
|
|
{
|
|
if (channel.Channel is DTS.Slice.Control.Event.Module.AnalogInputChannel)
|
|
{
|
|
MeasurementUnit unit = GetChannelUnit();
|
|
if (null != channel && null != channel.Channel)
|
|
{
|
|
var channelEU = (channel.Channel as DTS.DAS.Concepts.DAS.Channel.IEngineeringUnitAware).EngineeringUnits;
|
|
if (unit.UnitMatches(channelEU))
|
|
{
|
|
DTS.Slice.Control.Event.Module.AnalogInputChannel analog = channel.Channel as DTS.Slice.Control.Event.Module.AnalogInputChannel;
|
|
|
|
double scaler = unit.GetScalerConversionFrom(channelEU);
|
|
System.Diagnostics.Trace.WriteLine("Changing " + analog.ChannelDescriptionString + " from " + analog.ScaleFactorMv.ToString() + " to " + (analog.ScaleFactorMv * scaler).ToString());
|
|
analog.ScaleFactorMv *= scaler;
|
|
analog.EngineeringUnits = unit.ToString();
|
|
}
|
|
var graphChannel = GetGraphChannel(graphchannel);
|
|
if (graphChannel.UseOffset)
|
|
{
|
|
channel.Channel.UserOffsetEU = graphChannel.Offset;
|
|
}
|
|
else
|
|
{
|
|
channel.Channel.UserOffsetEU = 0D;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_channels[graphchannel].Channel = channel;
|
|
|
|
}
|
|
public GraphChannel GetGraphChannel(string graphChannel)
|
|
{
|
|
return _channels[graphChannel];
|
|
}
|
|
/// <summary>
|
|
/// returns all viable channels to fullfill the graph channel
|
|
/// </summary>
|
|
/// <param name="target"></param>
|
|
/// <param name="test"></param>
|
|
/// <returns></returns>
|
|
public ReviewTestChannel[] GetAvailableChannels(string target, ReviewTest test)
|
|
{
|
|
List<ReviewTestChannel> channels = new List<ReviewTestChannel>();
|
|
if (null != test)
|
|
{
|
|
try
|
|
{
|
|
foreach (var channel in test.Channels)
|
|
{
|
|
bool bAdded = false;
|
|
string eu = GetEngineeringUnits(channel.Channel);
|
|
foreach (var mu in GetChannelUnits())
|
|
{
|
|
if (bAdded) { continue; }
|
|
if (mu.UnitMatches(eu.Trim()))
|
|
{
|
|
if (!channels.Contains(channel)) { channels.Add(channel); bAdded = true; }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
System.Windows.Forms.MessageBox.Show(string.Format("Test missing data\r\n[{0}]", ex.Message), "Warning", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
return channels.ToArray();
|
|
}
|
|
|
|
public void Clear(ReviewTest test)
|
|
{
|
|
var e = _channels.GetEnumerator();
|
|
while (e.MoveNext())
|
|
{
|
|
if (null != e.Current.Value.Channel) { e.Current.Value.Channel.Cleanup(); }
|
|
e.Current.Value.Channel = null;
|
|
}
|
|
}
|
|
public void SetDefaults(ReviewTest test)
|
|
{
|
|
var e = _channels.GetEnumerator();
|
|
Dictionary<string, ReviewTestChannel> lookup = new Dictionary<string, ReviewTestChannel>();
|
|
while (e.MoveNext())
|
|
{
|
|
var bestchoices = from channel in GetAvailableChannels(e.Current.Key, test) where (channel.Channel as Slice.Control.Event.Module.AnalogInputChannel).Description.ToLower().Contains(e.Current.Value.ChannelNameHint.ToLower()) select channel;
|
|
if (null != bestchoices && bestchoices.Count() > 0 && null == e.Current.Value.Channel)
|
|
{
|
|
lookup.Add(e.Current.Key, bestchoices.First());
|
|
}
|
|
}
|
|
try
|
|
{
|
|
var e2 = lookup.GetEnumerator();
|
|
while (e2.MoveNext())
|
|
{
|
|
SetReviewTestChannel(e2.Current.Key, e2.Current.Value);
|
|
}
|
|
}
|
|
catch (System.Exception) { }
|
|
|
|
}
|
|
/// <summary>
|
|
/// returns the x plot for a given channel
|
|
/// </summary>
|
|
/// <param name="id">id of channel to get</param>
|
|
/// <returns></returns>
|
|
public double[] GetXPlot(string id)
|
|
{
|
|
int index = _channelIds.IndexOf(id);
|
|
if (null != _chart) { return _chart.ChartGroups[0].ChartData.SeriesList[index].X.Cast<double>().ToArray(); }
|
|
else return new double[0];
|
|
}
|
|
/// <summary>
|
|
/// returns the y plot for a given channel
|
|
/// </summary>
|
|
/// <param name="id">channel id</param>
|
|
/// <returns></returns>
|
|
public double[] GetYPlot(string id)
|
|
{
|
|
int index = _channelIds.IndexOf(id);
|
|
if (null != _chart) { return _chart.ChartGroups[0].ChartData.SeriesList[index].Y.Cast<double>().ToArray(); }
|
|
else return new double[0];
|
|
}
|
|
}
|
|
}
|