2328 lines
105 KiB
Plaintext
2328 lines
105 KiB
Plaintext
/*
|
|
* DTS.Slice.Control.Event.Module.Channel.cs
|
|
*
|
|
* Copyright © 2009
|
|
* Diversified Technical Systems, Inc.
|
|
* All Rights Reserved
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using DTS.DASLib.Service;
|
|
using DTS.Common.DAS.Concepts.DAS.Channel;
|
|
using DTS.Common.DAS.Concepts;
|
|
using DTS.Common.DASResource;
|
|
using DTS.Common.Enums;
|
|
using DTS.Common.Enums.Sensors;
|
|
using DTS.Common.SerializationPlus;
|
|
using DTS.Common.Utilities;
|
|
using DTS.Common.Utilities.DotNetProgrammingConstructs;
|
|
using DTS.Common.Interface.DASFactory.Diagnostics;
|
|
|
|
namespace DTS.Slice.Control
|
|
{
|
|
// *** see DTS.Slice.Control.Event.cs ***
|
|
public partial class Event
|
|
{
|
|
// *** see DTS.Slice.Control.Event.Module.cs ***
|
|
public partial class Module
|
|
{
|
|
/// <summary>
|
|
/// Representation of the DTS.Slice.Control.Event.Module.Channel class.
|
|
/// </summary>
|
|
public abstract partial class Channel
|
|
: Exceptional,
|
|
IFilterable, IDisposable
|
|
{
|
|
public void Dispose() { }
|
|
/// <summary>
|
|
/// Initialize an instance of the Event.Module.Channel class. Specifically, perform initializations
|
|
/// that will be required no matter what constructor parameter signature is invoked.
|
|
/// </summary>
|
|
private Channel()
|
|
{
|
|
try
|
|
{
|
|
//this.InitializeReviewableAttributes(this.ReviewableAttributes);
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem constructing channel", ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialize an instance of the DTS.Slice.Control.Event.Module.Channel class.
|
|
/// </summary>
|
|
///
|
|
/// <param name="parentModule">
|
|
/// The <see cref="DTS.Slice.Control.Event.Module"/> that contains this channel.
|
|
/// </param>
|
|
///
|
|
/// <param name="absoluteNumber">
|
|
/// The unique "absolute" <see cref="int"/> number of the channel with respect to all other channels
|
|
/// on all other DASes in the system.
|
|
/// </param>
|
|
///
|
|
protected Channel(Module parentModule, int absoluteNumber)
|
|
: this()
|
|
{
|
|
try
|
|
{
|
|
var enumerabilityGrabber = new EnumerabilityAttributeCoder();
|
|
ParentModule = parentModule;
|
|
AbsoluteNumber = absoluteNumber;
|
|
foreach (int enumValue in Enum.GetValues(typeof(ChannelFilter)))
|
|
if (enumerabilityGrabber.DecodeAttributeValue((ChannelFilter)enumValue))
|
|
AvailableFilters.Add(
|
|
new SaeJ211Filter((ChannelFilter)enumValue)
|
|
);
|
|
CurrentFilter = AvailableFilters[0];
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem constructing " + GetType().FullName, ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialize an instance of the DTS.Slice.Control.Event.Module.Channel class.
|
|
/// </summary>
|
|
///
|
|
/// <param name="channel">
|
|
/// The <see cref="DTS.DASLib.Service.DASChannel"/> object with which to initialize
|
|
/// this object.
|
|
/// </param>
|
|
///
|
|
/// <param name="parentModule">
|
|
/// The <see cref="DTS.Slice.Control.Event.Module"/> that contains this channel.
|
|
/// </param>
|
|
///
|
|
/// <param name="absoluteNumber">
|
|
/// The unique "absolute" <see cref="int"/> number of the channel with respect to all other channels
|
|
/// on all other DASes in the system.
|
|
/// </param>
|
|
///
|
|
public Channel(DASChannel channel, Module parentModule, int absoluteNumber)
|
|
: this(parentModule, absoluteNumber)
|
|
{
|
|
try
|
|
{ //
|
|
// Initialize basic channel properties with any values we can find
|
|
// in the specified channel object.
|
|
//
|
|
Number = channel.ModuleChannelNumber;
|
|
Start = channel.EventStartTime;
|
|
AbsoluteDisplayOrder = channel.AbsoluteDisplayOrder;
|
|
UnitConversion = channel.UnitConverision;
|
|
AtCapacity = channel.AtCapacity;
|
|
CapacityOutputIsBasedOn = channel.CapacityOutputIsBasedOn;
|
|
SensitivityUnits = channel.SensitivityUnits;
|
|
IsoChannelName = channel.IsoChannelName;
|
|
UserCode = channel.UserCode;
|
|
UserChannelName = channel.UserChannelName;
|
|
LinearSensorCalibration = channel.LinearSensorCalibration;
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem constructing " + GetType().FullName, ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialize an instance of the DTS.Slice.Control.Event.Module.Channel class.
|
|
/// </summary>
|
|
///
|
|
/// <param name="channel">
|
|
/// The <see cref="DTS.Serialization.Test.Module.Channel"/> object with which to initialize
|
|
/// this object.
|
|
/// </param>
|
|
///
|
|
/// <param name="parentModule">
|
|
/// The <see cref="DTS.Slice.Control.Event.Module"/> that contains this channel.
|
|
/// </param>
|
|
///
|
|
/// <param name="absoluteNumber">
|
|
/// The unique "absolute" <see cref="int"/> number of the channel with respect to all other channels
|
|
/// on all other DASes in the system.
|
|
/// </param>
|
|
///
|
|
public Channel(Serialization.Test.Module.Channel channel, Module parentModule, int absoluteNumber)
|
|
: this(parentModule, absoluteNumber)
|
|
{
|
|
try
|
|
{ //
|
|
// Initialize basic channel properties with any values we can find
|
|
// in the specified channel object.
|
|
//
|
|
AbsoluteDisplayOrder = channel.AbsoluteDisplayOrder;
|
|
Multiplier = channel.Data.Multiplier;
|
|
UnitConversion = channel.Data.UnitConversion;
|
|
UserOffsetEU = channel.Data.UserOffsetEU;
|
|
Number = channel.Number;
|
|
Start = channel.Start;
|
|
try
|
|
{
|
|
if (channel.TimeOfFirstSampleValid) { TimeOfFirstSampleSec = channel.TimeOfFirstSampleSec; }
|
|
}
|
|
catch { }
|
|
|
|
if (channel.IsLastCalibrationDateValid) { LastCalibrationDate = channel.LastCalibrationDate; }
|
|
if (channel.IsCalDueDateValid) { CalDueDate = channel.CalDueDate; }
|
|
if (channel.IsSensorIDValid) { SensorID = channel.SensorID; }
|
|
if (channel.IsOffsetToleranceLowMvValid) { OffsetToleranceLowMv = channel.OffsetToleranceLowMv; }
|
|
if (channel.IsOffsetToleranceHighMvValid) { OffsetToleranceHighMv = channel.OffsetToleranceHighMv; }
|
|
|
|
if (channel.IsUserCodeValid) { UserCode = channel.UserCode; }
|
|
if (channel.IsUserChannelNameValid) { UserChannelName = channel.UserChannelName; }
|
|
if (channel.IsIsoChannelNameValid) { IsoChannelName = channel.IsoChannelName; }
|
|
|
|
IsSubsampled = channel.IsSubsampled;
|
|
UnsubsampledSampleRateHz = channel.UnsubsampledSampleRateHz;
|
|
IsSupersampled = channel.IsSupersampled;
|
|
UseEUScaler = channel.Data.UseEUScaleFactors;
|
|
UnsupersampledSampleRateHz = channel.UnsupersampledSampleRateHz;
|
|
try
|
|
{
|
|
if (channel.PersistentChannelInfo == null)
|
|
{
|
|
UnfilteredData = channel.TDASPersistentChannelInfo;
|
|
}
|
|
else
|
|
{
|
|
UnfilteredData = channel.PersistentChannelInfo; //channel.Data;
|
|
}
|
|
|
|
}
|
|
catch (System.Exception) { throw new System.IO.InvalidDataException("Invalid or corrupt data file, channel: " + channel.ChannelDescriptionString + " - " + channel.ChannelName2); }
|
|
// xxx proposal: this.UnfilteredData = ( null != channel.PersistentChannelInfo ? channel.PersistentChannelInfo : channel.Data );
|
|
// If everything uses persistent channel info, that's going to break the data viewer completely in the short term, unless we either
|
|
// change the data viewer to be clever about extracting something displayable or provide the "classic" data structure through the
|
|
// "UnfilteredData" property somehow.
|
|
}
|
|
catch (System.IO.InvalidDataException) { throw; }
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem constructing " + GetType().FullName, ex);
|
|
}
|
|
}
|
|
/*
|
|
/// <summary>
|
|
/// Get "reviewable" (attributes that can be automatically converted to strings and displayed
|
|
/// on the review tab) for this channel.
|
|
/// </summary>
|
|
public List<ReviewableAttribute> ReviewableAttributes
|
|
{
|
|
get { return _ReviewableAttributes.Value; }
|
|
private set { _ReviewableAttributes.Value = value; }
|
|
}
|
|
private Property<List<ReviewableAttribute>> _ReviewableAttributes
|
|
= new Property<List<ReviewableAttribute>>(
|
|
typeof(Event.Module.Channel).Namespace + ".Event.Module.Channel.ReviewableAttributes",
|
|
new List<ReviewableAttribute>(),
|
|
true
|
|
);
|
|
*/
|
|
/*
|
|
/// <summary>
|
|
/// Method to allow derived classes to initialize their own reviewable attributes.
|
|
/// </summary>
|
|
///
|
|
/// <param name="reviewableAttributes">
|
|
/// The <see cref="List"/> or <see cref="ReviewableAttribute"/>s to be initialized.
|
|
/// </param>
|
|
///
|
|
protected abstract void InitializeReviewableAttributes(List<ReviewableAttribute> reviewableAttributes);
|
|
*/
|
|
/// <summary>
|
|
/// Our data display unit options.
|
|
/// </summary>
|
|
public enum DataDisplayUnits
|
|
{
|
|
Adc,
|
|
Mv,
|
|
Eu
|
|
}
|
|
|
|
/// <summary>
|
|
/// The <see cref="Event.Module"/> that contains this channel.
|
|
/// </summary>
|
|
public Module ParentModule
|
|
{
|
|
get => _ParentModule.Value;
|
|
private set => _ParentModule.Value = value;
|
|
}
|
|
private readonly Property<Module> _ParentModule
|
|
= new Property<Module>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.ParentModule",
|
|
null,
|
|
false
|
|
);
|
|
|
|
/// <summary>
|
|
/// Get the data scaler for this channel.
|
|
/// </summary>
|
|
public DataScaler Scaler => _Scaler.Value;
|
|
|
|
private readonly Property<DataScaler> _Scaler
|
|
= new Property<DataScaler>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.Scaler",
|
|
new DataScaler(),
|
|
true
|
|
);
|
|
|
|
/// <summary>
|
|
/// Get/set the <see cref="bool"/> switch to enable/disable filter caching.
|
|
/// </summary>
|
|
public bool UseFilterCaching
|
|
{
|
|
//for now NEVER cache
|
|
/*get { return _UseFilterCaching.Value; }
|
|
set
|
|
{
|
|
if (false == (_UseFilterCaching.Value = value))
|
|
{ //
|
|
// If we disable filter caching on the fly, make sure the caches are cleared.
|
|
//
|
|
_Data = null;
|
|
_DataEu = null;
|
|
_DataMv = null;
|
|
|
|
lock (previouslyFilteredDataLock)
|
|
{
|
|
_previouslyFilteredData.Clear();
|
|
}
|
|
}
|
|
}*/
|
|
get => false;
|
|
set { }
|
|
}
|
|
/*private Property<bool> _UseFilterCaching
|
|
= new Property<bool>(
|
|
typeof(Event.Module.Channel).Namespace + ".Event.Module.Channel.UseFilterCaching",
|
|
true,
|
|
true
|
|
);
|
|
*/
|
|
/// <summary>
|
|
/// Get/set a user-readable description of this channel object; intended to be
|
|
/// the UI representation of this object. Literally, what the API channel's
|
|
/// ToString method said it was.
|
|
/// </summary>
|
|
public string ChannelDescriptionString
|
|
{
|
|
get => _ChannelDescriptionString.Value;
|
|
set => _ChannelDescriptionString.Value = value;
|
|
}
|
|
private readonly Property<string> _ChannelDescriptionString
|
|
= new Property<string>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.ChannelDescriptionString",
|
|
null,
|
|
false
|
|
);
|
|
|
|
/// <summary>
|
|
/// Get/set the manufacturer of this channel object's sensor.
|
|
/// </summary>
|
|
public string Manufacturer
|
|
{
|
|
get => _Manufacturer.Value;
|
|
set => _Manufacturer.Value = value;
|
|
}
|
|
private readonly Property<string> _Manufacturer
|
|
= new Property<string>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.Manufacturer",
|
|
null,
|
|
false
|
|
);
|
|
|
|
/// <summary>
|
|
/// Get/set the manufacturer of this channel object's sensor.
|
|
/// </summary>
|
|
public string Model
|
|
{
|
|
get => _Model.Value;
|
|
set => _Model.Value = value;
|
|
}
|
|
private readonly Property<string> _Model
|
|
= new Property<string>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.Model",
|
|
null,
|
|
false
|
|
);
|
|
|
|
public string OriginalChannelName
|
|
{
|
|
get => _OriginalChannelName.Value;
|
|
set => _OriginalChannelName.Value = value;
|
|
}
|
|
private readonly Property<string> _OriginalChannelName
|
|
= new Property<string>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.OriginalChannelName",
|
|
"",
|
|
true);
|
|
|
|
public string ChannelName2
|
|
{
|
|
get => _ChannelName2.Value;
|
|
set => _ChannelName2.Value = value;
|
|
}
|
|
private readonly Property<string> _ChannelName2
|
|
= new Property<string>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.ChannelName2",
|
|
"",
|
|
true);
|
|
|
|
|
|
private readonly Property<string> _HardwareChannelName
|
|
= new Property<string>(
|
|
typeof(Channel).Name + ".Event.Module.Channel.HardwareChannelName",
|
|
"",
|
|
true);
|
|
public string HardwareChannelName
|
|
{
|
|
get => _HardwareChannelName.Value;
|
|
set => _HardwareChannelName.Value = value;
|
|
}
|
|
|
|
private readonly Property<string> _ChannelId
|
|
= new Property<string>(
|
|
typeof(Channel).Name + ".Event.Module.Channel.ChannelId",
|
|
"",
|
|
true);
|
|
/// <summary>
|
|
/// refers to a unique id for a logical channel in the test
|
|
/// for now this is using TestObjectChannel.GetId()
|
|
/// which is in the form of TestObjectSerial_ChannelType_ChannelId
|
|
/// </summary>
|
|
public string ChannelId
|
|
{
|
|
get => _ChannelId.Value;
|
|
set => _ChannelId.Value = value;
|
|
}
|
|
|
|
private readonly Property<string> _Sensor
|
|
= new Property<string>(
|
|
typeof(Channel).Name + ".Event.Module.Channel.Sensor",
|
|
"",
|
|
true);
|
|
/// <summary>
|
|
/// refers to a unique sensor serial number for a logical channel in the test
|
|
/// </summary>
|
|
public string Sensor
|
|
{
|
|
get => _Sensor.Value;
|
|
set => _Sensor.Value = value;
|
|
}
|
|
|
|
private readonly Property<string> _ChannelGroupName
|
|
= new Property<string>(
|
|
typeof(Channel).Name + ".Event.Module.Channel.ChannelGroupName",
|
|
"",
|
|
true);
|
|
/// <summary>
|
|
/// refers to the Group for a logical channel in the test
|
|
/// </summary>
|
|
public string ChannelGroupName
|
|
{
|
|
get => _ChannelGroupName.Value ?? "";
|
|
set => _ChannelGroupName.Value = value;
|
|
}
|
|
|
|
private readonly Property<string> _SetupEID
|
|
= new Property<string>(
|
|
typeof(Channel).Name + ".Event.Module.Channel.SetupEID",
|
|
string.Empty,
|
|
true);
|
|
/// <summary>
|
|
/// The electronic id for a channel at test setup time
|
|
/// </summary>
|
|
public string SetupEID
|
|
{
|
|
get => _SetupEID.Value ?? string.Empty;
|
|
set => _SetupEID.Value = value;
|
|
}
|
|
|
|
private readonly Property<string> _DataCollectionEID
|
|
= new Property<string>(
|
|
typeof(Channel).Name + ".Event.Module.Channel.DataCollectionEID",
|
|
string.Empty,
|
|
true);
|
|
/// <summary>
|
|
/// the electronic id on a channel at run time
|
|
/// </summary>
|
|
public string DataCollectionEID
|
|
{
|
|
get => _DataCollectionEID.Value ?? string.Empty;
|
|
set => _DataCollectionEID.Value = value;
|
|
}
|
|
|
|
public string UserValue1
|
|
{
|
|
get => _userValue1.Value;
|
|
set => _userValue1.Value = value;
|
|
}
|
|
|
|
private readonly Property<string> _userValue1 = new Property<string>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.UserValue1",
|
|
"", true);
|
|
|
|
public string UserValue2
|
|
{
|
|
get => _userValue2.Value;
|
|
set => _userValue2.Value = value;
|
|
}
|
|
|
|
private readonly Property<string> _userValue2 = new Property<string>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.UserValue2",
|
|
"", true);
|
|
|
|
public string UserValue3
|
|
{
|
|
get => _userValue3.Value;
|
|
set => _userValue3.Value = value;
|
|
}
|
|
|
|
private readonly Property<string> _userValue3 = new Property<string>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.UserValue3",
|
|
"", true);
|
|
|
|
/// <summary>
|
|
/// Get/set the channel's ordinal number within the stack.
|
|
/// </summary>
|
|
public int Number
|
|
{
|
|
get => _Number.Value;
|
|
set => _Number.Value = value;
|
|
}
|
|
private readonly Property<int> _Number = new Property<int>("DTS.Control.Event.Module.Channel.Number", -1, false);
|
|
|
|
/// <summary>
|
|
/// Get the channel's ordinal number within the entire system.
|
|
/// </summary>
|
|
public int AbsoluteNumber
|
|
{
|
|
get => _AbsoluteNumber.Value;
|
|
private set => _AbsoluteNumber.Value = value;
|
|
}
|
|
private readonly Property<int> _AbsoluteNumber
|
|
= new Property<int>(
|
|
typeof(Channel).FullName + ".AbsoluteNumber",
|
|
-1,
|
|
false
|
|
);
|
|
|
|
public bool TimeOfFirstSampleSecValid => _TimeOfFirstSampleSec.IsInitialized;
|
|
|
|
/// <summary>
|
|
/// The <see cref="double"/> time of the first sample. Will be needed if the time
|
|
/// scale can't be determined by the trigger sample number (possible, as it's an unsigned
|
|
/// value and an all-positive ROI that doesn't contain T0 would need it to be negative).
|
|
/// </summary>
|
|
public double TimeOfFirstSampleSec
|
|
{
|
|
get => _TimeOfFirstSampleSec.Value;
|
|
set => _TimeOfFirstSampleSec.Value = value;
|
|
}
|
|
private readonly Property<double> _TimeOfFirstSampleSec
|
|
= new Property<double>(
|
|
typeof(Channel).FullName + ".TimeOfFirstSample",
|
|
0.0,
|
|
false
|
|
);
|
|
|
|
/// <summary>
|
|
/// Get/set the start time for this channel.
|
|
/// </summary>
|
|
public DateTime Start
|
|
{
|
|
get => _Start.Value;
|
|
set => _Start.Value = value;
|
|
}
|
|
private readonly Property<DateTime> _Start = new Property<DateTime>("DTS.Control.Event.Module.Channel.Start", DateTime.Now, false);
|
|
|
|
public LinkedList<short[]> PartialUnfilteredData = null;
|
|
|
|
// Method for creating channel files directly to disk.
|
|
// Change that linked list class to something that will handle this...
|
|
// I think what we need to do instead is to change the class/data structure that represents
|
|
// the channel data into an object that maybe double passes the reference, or derives from the
|
|
// memory-mapped file objecct, but it can also have extra methods and throw an exception if we
|
|
// try to access it before it's been properly initialized.
|
|
|
|
/// <summary>
|
|
/// Get/set the <see cref="short"/> data value list for this channel.
|
|
/// </summary>
|
|
public List<short> UnfilteredData
|
|
{
|
|
get => _UnfilteredData.Value;
|
|
set
|
|
{
|
|
try
|
|
{
|
|
// Reset unfiltered data caches.
|
|
_UnfilteredData.Value = value;
|
|
_UnfilteredDataEu = null;
|
|
_UnfilteredDataMv = null;
|
|
|
|
_DataRangeAdc.UnInitialize();
|
|
//_DataRangeEu .UnInitialize( ); // *** not currently cached.
|
|
//_DataRangeMv .UnInitialize( ); // *** not currently cached.
|
|
|
|
// Reset filtered data caches.
|
|
_Data = null;
|
|
_DataEu = null;
|
|
_DataMv = null;
|
|
|
|
lock (previouslyFilteredDataLock)
|
|
{
|
|
_previouslyFilteredData.Clear();
|
|
}
|
|
|
|
// Update the data count.
|
|
if (value is Serialization.SliceRaw.File.PersistentChannel persistentChannel)
|
|
DataCount = persistentChannel.Count;
|
|
else if (value is Serialization.TDAS.File.PersistentChannel tdasPersistentChannel)
|
|
DataCount = tdasPersistentChannel.Count;
|
|
else DataCount = value.Count;
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting unfiltered data", ex);
|
|
}
|
|
}
|
|
}
|
|
private readonly Property<List<short>> _UnfilteredData = new Property<List<short>>("DTS.Control.Event.Module.Channel.Data", null, false);
|
|
public List<short> UnfilteredAlternateData
|
|
{
|
|
get => _UnfilteredAlternateData.Value;
|
|
set
|
|
{
|
|
try
|
|
{
|
|
// Reset unfiltered data caches.
|
|
_UnfilteredAlternateData.Value = value;
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting unfiltered alternate data", ex);
|
|
}
|
|
}
|
|
}
|
|
private readonly Property<List<short>> _UnfilteredAlternateData = new Property<List<short>>("DTS.Control.Event.Module.Channel.AlternateData", null, false);
|
|
|
|
/// <summary>
|
|
/// Get the filtered double-converted data for this channel.
|
|
/// </summary>
|
|
public List<double> Data
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (UseFilterCaching)
|
|
{
|
|
if (null == _Data)
|
|
return _Data = new List<double>(GetDataFilteredBy(CurrentFilter, DataDisplayUnits.Adc));
|
|
return _Data;
|
|
}
|
|
return new List<double>(GetDataFilteredBy(CurrentFilter, DataDisplayUnits.Adc));
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting filtered data", ex);
|
|
}
|
|
}
|
|
}
|
|
private List<double> _Data = null;
|
|
|
|
/// <summary>
|
|
/// Get the data count for this channel.
|
|
/// </summary>
|
|
public int DataCount
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
public abstract List<double> GetUnfilteredDataEu();
|
|
public abstract List<double> GetUnfilteredDataMV();
|
|
/// <summary>
|
|
/// Get an EU-scaled version of the data <see cref="double"/> <see cref="List"/>.
|
|
/// </summary>
|
|
public List<double> UnfilteredDataEu
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{ //
|
|
// If scaled data has yet to be computed, then compute, cache and return it.
|
|
// Otherwise just return the cached version.
|
|
//
|
|
if (null == _UnfilteredDataEu)
|
|
{
|
|
if (UnfilteredData is ILargeDataAware largeDataAware)
|
|
{
|
|
if (!largeDataAware.IsDataArraySized)
|
|
{
|
|
throw new Serialization.SliceRaw.File.PersistentChannel.DataTooBigForArrayException("Data is too big to be viewed or filtered.");
|
|
}
|
|
}
|
|
|
|
_UnfilteredDataEu = GetUnfilteredDataEu();
|
|
}
|
|
return _UnfilteredDataEu;
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting EU-scaled data", ex);
|
|
}
|
|
}
|
|
}
|
|
private List<double> _UnfilteredDataEu = null;
|
|
|
|
|
|
protected List<double> UnfilteredDataMV
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{ //
|
|
// If scaled data has yet to be computed, then compute, cache and return it.
|
|
// Otherwise just return the cached version.
|
|
//
|
|
if (null == _UnfilteredDataMV)
|
|
{
|
|
if (UnfilteredData is ILargeDataAware largeDataAware)
|
|
{
|
|
if (!largeDataAware.IsDataArraySized)
|
|
{
|
|
throw new Serialization.SliceRaw.File.PersistentChannel.DataTooBigForArrayException("Data is too big to be viewed or filtered.");
|
|
}
|
|
}
|
|
|
|
_UnfilteredDataMV = GetUnfilteredDataMV();
|
|
}
|
|
return _UnfilteredDataMV;
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting EU-scaled data", ex);
|
|
}
|
|
}
|
|
}
|
|
private List<double> _UnfilteredDataMV = null;
|
|
|
|
|
|
private List<double> _DataADC = null;
|
|
public List<double> DataADC
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{ //
|
|
// If scaled data has yet to be computed, then compute, cache and return it.
|
|
// Otherwise just return the cached version.
|
|
//
|
|
if (null == _DataADC)
|
|
{
|
|
if (UnfilteredData is ILargeDataAware largeDataAware)
|
|
if (!largeDataAware.IsDataArraySized)
|
|
throw new Serialization.SliceRaw.File.PersistentChannel.DataTooBigForArrayException("Data is too big to be viewed or filtered.");
|
|
|
|
var persistentUnfilteredData
|
|
= UnfilteredData as Serialization.SliceRaw.File.PersistentChannel;
|
|
|
|
//double scalingFactor = AdcToMvScalingFactor;
|
|
_DataADC = new List<double>();
|
|
var dataCount = persistentUnfilteredData.Count;
|
|
for (var i = 0; i < dataCount; i++)
|
|
_DataADC.Add(persistentUnfilteredData[i]);
|
|
}
|
|
return _DataADC;
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting ADC data", ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
public virtual bool SupportsEU => true;
|
|
public virtual bool SupportsADC => true;
|
|
public virtual bool SupportsmV => true;
|
|
|
|
/// <summary>
|
|
/// Get/set the filtered EU data for this channel.
|
|
/// </summary>
|
|
public IList<double> DataEu
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (UseFilterCaching)
|
|
{
|
|
if (null == _DataEu)
|
|
return _DataEu = new List<double>(GetDataFilteredBy(CurrentFilter, DataDisplayUnits.Eu));
|
|
return _DataEu;
|
|
}
|
|
return new List<double>(GetDataFilteredBy(CurrentFilter, DataDisplayUnits.Eu));
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting filtered EU data", ex);
|
|
}
|
|
}
|
|
}
|
|
private List<double> _DataEu = null;
|
|
|
|
/// <summary>
|
|
/// Get an MV-scaled version of the data <see cref="double"/> list.
|
|
/// </summary>
|
|
public List<double> UnfilteredDataMv
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{ //
|
|
// If scaled data has yet to be computed, then compute, cache and return it.
|
|
// Otherwise just return the cached version.
|
|
//
|
|
if (null == _UnfilteredDataMv)
|
|
{
|
|
if (UnfilteredData is ILargeDataAware largeDataAware)
|
|
if (!largeDataAware.IsDataArraySized)
|
|
throw new Serialization.SliceRaw.File.PersistentChannel.DataTooBigForArrayException("Data is too big to be viewed or filtered.");
|
|
|
|
if (UnfilteredData is Serialization.SliceRaw.File.PersistentChannel persistentUnfilteredData)
|
|
{
|
|
_UnfilteredDataMv = new List<double>();
|
|
var dataCount = persistentUnfilteredData.Count;
|
|
for (var i = 0; i < dataCount; i++)
|
|
{
|
|
_UnfilteredDataMv.Add(Scaler.GetMv(persistentUnfilteredData[i]));
|
|
}
|
|
}
|
|
else if (UnfilteredData is Serialization.TDAS.File.PersistentChannel tdasUnfilteredData)
|
|
{
|
|
_UnfilteredDataMv = new List<double>();
|
|
var dataCount = tdasUnfilteredData.Count;
|
|
for (var i = 0; i < dataCount; i++)
|
|
{
|
|
_UnfilteredDataMv.Add(Scaler.GetMv(tdasUnfilteredData[i]));
|
|
}
|
|
}
|
|
}
|
|
return _UnfilteredDataMv;
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting MV-scaled data", ex);
|
|
}
|
|
}
|
|
}
|
|
private List<double> _UnfilteredDataMv = null;
|
|
|
|
/// <summary>
|
|
/// Get/set the filtered MV data for this channel.
|
|
/// </summary>
|
|
public List<double> DataMv
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (UseFilterCaching)
|
|
{
|
|
if (null == _DataMv)
|
|
return _DataMv = new List<double>(GetDataFilteredBy(CurrentFilter, DataDisplayUnits.Mv));
|
|
return _DataMv;
|
|
}
|
|
return new List<double>(GetDataFilteredBy(CurrentFilter, DataDisplayUnits.Mv));
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting filtered MV data", ex);
|
|
}
|
|
}
|
|
}
|
|
private List<double> _DataMv = null;
|
|
|
|
/// <summary>
|
|
/// Get/set the ADC->MV scale factor for this channel.
|
|
/// </summary>
|
|
public double ScaleFactorMv
|
|
{
|
|
|
|
get => Scaler.GetScaleFactorMv();
|
|
set
|
|
{
|
|
try
|
|
{
|
|
Scaler.SetScaleFactorMv(value);
|
|
_DataEu = null;
|
|
lock (previouslyFilteredDataLock)
|
|
{
|
|
_previouslyFilteredData.Clear();
|
|
}
|
|
_UnfilteredDataEu = null;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + typeof(Channel).Namespace + ".Event.Module.Channel.ScaleFactorMv property value", ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
public double ScaleFactorEU
|
|
{
|
|
|
|
get => Scaler.GetScaleFactorEU();
|
|
set
|
|
{
|
|
try
|
|
{
|
|
Scaler.SetScaleFactorEU(value);
|
|
_DataEu = null;
|
|
lock (previouslyFilteredDataLock)
|
|
{
|
|
_previouslyFilteredData.Clear();
|
|
}
|
|
_UnfilteredDataEu = null;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + typeof(Channel).Namespace + ".Event.Module.Channel.ScaleFactorEU property value", ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
public double UserOffsetEU
|
|
{
|
|
get => Scaler.UserOffsetEU;
|
|
set
|
|
{
|
|
Scaler.UserOffsetEU = value;
|
|
_DataEu = null;
|
|
lock (previouslyFilteredDataLock)
|
|
{
|
|
_previouslyFilteredData.Clear();
|
|
}
|
|
_UnfilteredDataEu = null;
|
|
}
|
|
}
|
|
|
|
public double Multiplier
|
|
{
|
|
get => Scaler.Multiplier;
|
|
set => Scaler.Multiplier = value;
|
|
}
|
|
public double UnitConversion
|
|
{
|
|
get => Scaler.UnitConversion;
|
|
set => Scaler.UnitConversion = value;
|
|
}
|
|
public bool AtCapacity
|
|
{
|
|
get => Scaler.BasedOnOutputAtCapacity;
|
|
set => Scaler.BasedOnOutputAtCapacity = value;
|
|
}
|
|
public double CapacityOutputIsBasedOn
|
|
{
|
|
get => Scaler.CapacityOutputIsBasedOn;
|
|
set => Scaler.CapacityOutputIsBasedOn = value;
|
|
}
|
|
public SensorConstants.SensUnits SensitivityUnits
|
|
{
|
|
get => Scaler.SensitivityUnits;
|
|
set => Scaler.SensitivityUnits = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get/set the sensitivity mV/EU factor for this channel.
|
|
/// </summary>
|
|
public double MvPerEu
|
|
{
|
|
set
|
|
{
|
|
try
|
|
{
|
|
Scaler.SetMvPerEu(value);
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + typeof(Channel).Namespace + ".Event.Module.Channel.MvPerEu property value", ex);
|
|
}
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// putting this in a separate function to force that MvPerEu is not being used directly in calcuations
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public double GetMvPerEu()
|
|
{
|
|
return Scaler.GetMvPerEu();
|
|
}
|
|
public double GetScaleFactorMv()
|
|
{
|
|
return Scaler.GetScaleFactorMv();
|
|
}
|
|
/// <summary>
|
|
/// Get <see cref="bool"/> value indicating whether or not the specified channel is configured.
|
|
/// </summary>
|
|
public abstract bool IsConfigured
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the actual available maximum Adc range based on datum bit resolution.
|
|
/// </summary>
|
|
public double ActualMaxRangeAdc
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
return short.MaxValue;
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem calculating actual max ADC range", ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the actual available minimum ADC range based on datum bit resolution.
|
|
/// </summary>
|
|
public double ActualMinRangeAdc
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
return short.MinValue;
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem calculating actual min ADC range", ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the actual available maximum EU range based on scaling factor and bit resolution.
|
|
/// </summary>
|
|
public abstract double ActualMaxRangeEu { get; }
|
|
|
|
/// <summary>
|
|
/// Get the actual available minimum EU range based on scaling factor and bit resolution.
|
|
/// </summary>
|
|
public abstract double ActualMinRangeEu { get; }
|
|
|
|
|
|
public abstract double DesiredRangeEU { get; }
|
|
public abstract double SensorCapacityEU { get; }
|
|
/// <summary>
|
|
/// Get the actual available maximum MV range based on scaling factor and bit resolution.
|
|
/// </summary>
|
|
public virtual double ActualMaxRangeMv
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
return (Scaler.GetAdcToMvScalingFactor() >= 0) ? Scaler.GetMv(ActualMaxRangeAdc) : Scaler.GetMv(ActualMinRangeAdc);
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem calculating actual max MV range", ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the actual available minimum MV range based on scaling factor and bit resolution.
|
|
/// </summary>
|
|
public virtual double ActualMinRangeMv
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
return (Scaler.GetAdcToMvScalingFactor() >= 0) ? Scaler.GetMv(ActualMinRangeAdc) : Scaler.GetMv(ActualMaxRangeAdc);
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem calculating actual min MV range", ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compute data min and max for this channel.
|
|
/// </summary>
|
|
///
|
|
/// <param name="dataMinAdc">
|
|
/// The <see cref="double"/> data min <see cref="Property"/> to be populated with
|
|
/// the min result of this method.
|
|
/// </param>
|
|
///
|
|
/// <param name="datMaxAdc">
|
|
/// The <see cref="double"/> data max <see cref="Property"/> to be populated with
|
|
/// the max result of this method.
|
|
/// </param>
|
|
///
|
|
private void ComputeDataMinMaxAdc(Property<double> dataMinAdc, Property<double> dataMaxAdc)
|
|
{
|
|
try
|
|
{
|
|
if (UnfilteredData is Serialization.TDAS.File.PersistentChannel) //temp
|
|
{
|
|
ComputeTDASDataMinMaxAdc(dataMinAdc, dataMaxAdc);
|
|
}
|
|
else
|
|
{
|
|
var dataCount = (UnfilteredData is Serialization.SliceRaw.File.PersistentChannel persistentChannel)
|
|
? persistentChannel.Count // use "persistent channel" count
|
|
: UnfilteredData.Count;
|
|
if (dataCount <= 0)
|
|
throw new Exception("channel has no data");
|
|
{
|
|
// Find the max and min in raw ADC.
|
|
double min, max;
|
|
|
|
if (!(UnfilteredData is Serialization.SliceRaw.File.PersistentChannel data))
|
|
throw new ApplicationException("attempting to use channel data object as memory-mapped file, but it apparently is not one");
|
|
min = max = data[0];
|
|
for (var i = 1; i < dataCount; i++)
|
|
{
|
|
min = Math.Min(min, data[i]);
|
|
max = Math.Max(max, data[i]);
|
|
}
|
|
|
|
dataMinAdc.Value = min;
|
|
dataMaxAdc.Value = max;
|
|
}
|
|
}
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem computing channel data min/max", ex);
|
|
}
|
|
}
|
|
|
|
private void ComputeTDASDataMinMaxAdc(Property<double> dataMinAdc, Property<double> dataMaxAdc)
|
|
{
|
|
try
|
|
{
|
|
var dataCount = (UnfilteredData is Serialization.TDAS.File.PersistentChannel persistentChannel)
|
|
? persistentChannel.Count // use "persistent channel" count
|
|
: UnfilteredData.Count;
|
|
if (dataCount <= 0)
|
|
throw new Exception("channel has no data");
|
|
{
|
|
// Find the max and min in raw ADC.
|
|
double min, max;
|
|
|
|
if (!(UnfilteredData is Serialization.TDAS.File.PersistentChannel data))
|
|
throw new ApplicationException("attempting to use channel data object as memory-mapped file, but it apparently is not one");
|
|
min = max = data[0];
|
|
for (var i = 1; i < dataCount; i++)
|
|
{
|
|
min = Math.Min(min, data[i]);
|
|
max = Math.Max(max, data[i]);
|
|
}
|
|
|
|
dataMinAdc.Value = min;
|
|
dataMaxAdc.Value = max;
|
|
}
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem computing channel data min/max", ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compute the min and max values in the given data vector.
|
|
/// </summary>
|
|
///
|
|
/// <param name="dataMin">
|
|
/// Returns the minimum <see cref="double"/> data value in the specified data vector.
|
|
/// </param>
|
|
///
|
|
/// <param name="dataMax">
|
|
/// Returns the maximum <see cref="double"/> data value in the specified data vector.
|
|
/// </param>
|
|
///
|
|
/// <param name="data">
|
|
/// The array of <see cref="double"/>s to be scoured for min and max values.
|
|
/// </param>
|
|
///
|
|
private void ComputeDataMinMax(out double dataMin, out double dataMax, double[] data)
|
|
{
|
|
try
|
|
{
|
|
if (null == data)
|
|
throw new ArgumentException("cannot process null data reference");
|
|
if (data.Length <= 0)
|
|
throw new ArgumentException("cannot process empty data vector");
|
|
dataMin = dataMax = data[0];
|
|
for (var i = 0; i < data.Length; i++)
|
|
{
|
|
dataMin = Math.Min(dataMin, data[i]);
|
|
dataMax = Math.Max(dataMax, data[i]);
|
|
}
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem computing data vector min/max", ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the <see cref="double"/> data min in ADC.
|
|
/// </summary>
|
|
public double DataMinAdc
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!_DataMinAdc.IsInitialized)
|
|
ComputeDataMinMaxAdc(_DataMinAdc, _DataMaxAdc);
|
|
return _DataMinAdc.Value;
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + typeof(Channel).Namespace + ".Event.Module.Channel.DataMinAdc", ex);
|
|
}
|
|
}
|
|
}
|
|
private readonly Property<double> _DataMinAdc
|
|
= new Property<double>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.DataMinAdc",
|
|
0.0,
|
|
false
|
|
);
|
|
|
|
/// <summary>
|
|
/// Get the <see cref="double"/> data min in EU.
|
|
/// </summary>
|
|
public abstract double DataMinEu { get; }
|
|
|
|
/// <summary>
|
|
/// Get the <see cref="double"/> data min in MV.
|
|
/// </summary>
|
|
public double DataMinMv => (Scaler.GetAdcToMvScalingFactor() >= 0) ?
|
|
Scaler.GetMv(DataMinAdc) :
|
|
Scaler.GetMv(DataMaxAdc);
|
|
|
|
/// <summary>
|
|
/// Get the <see cref="double"/> data max in ADC.
|
|
/// </summary>
|
|
public double DataMaxAdc
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!_DataMaxAdc.IsInitialized)
|
|
ComputeDataMinMaxAdc(_DataMinAdc, _DataMaxAdc);
|
|
return _DataMaxAdc.Value;
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + typeof(Channel).Namespace + ".Event.Module.Channel.DataMaxAdc", ex);
|
|
}
|
|
}
|
|
}
|
|
private readonly Property<double> _DataMaxAdc
|
|
= new Property<double>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.DataMaxAdc",
|
|
0.0,
|
|
false
|
|
);
|
|
|
|
/// <summary>
|
|
/// Get the <see cref="double"/> data max in EU.
|
|
/// </summary>
|
|
public abstract double DataMaxEu { get; }
|
|
|
|
/// <summary>
|
|
/// Populate the specified filter-index'd dictionaries with the min/max values for the
|
|
/// specified filter setting.
|
|
/// </summary>
|
|
///
|
|
/// <param name="filteredMins">
|
|
/// The <see cref="ChannelFilter"/>-indexed Dictionary of
|
|
/// <see cref="double"/> data min values.
|
|
/// </param>
|
|
///
|
|
/// <param name="filteredMaxs">
|
|
/// The <see cref="ChannelFilter"/>-indexed Dictionary of
|
|
/// <see cref="double"/> data max values.
|
|
/// </param>
|
|
///
|
|
/// <param name="filter">
|
|
/// The <see cref="ChannelFilter"/> to be applied to the the channel's data prior to
|
|
/// the min/max calculation.
|
|
/// </param>
|
|
///
|
|
private void PopulateFilteredMinMaxValues(IDictionary<double, double> filteredMins,
|
|
IDictionary<double, double> filteredMaxs,
|
|
ChannelFilter filter)
|
|
{
|
|
try
|
|
{
|
|
ComputeDataMinMax(out double min, out double max, CurrentFilter.Apply(this, DataDisplayUnits.Eu, Serialization.File.UseLegacyTDCSoftwareFiltering));
|
|
filteredMins[CurrentFilter.CutoffFrequencyHz] = min;
|
|
filteredMaxs[CurrentFilter.CutoffFrequencyHz] = max;
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem propulating filtered min/max values", ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the minimum EU data value for the current filtering.
|
|
/// </summary>
|
|
public double DataMinFilteredEu
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!_DataMinFilteredEu.Keys.Contains(CurrentFilter.CutoffFrequencyHz))
|
|
PopulateFilteredMinMaxValues(_DataMinFilteredEu, _DataMaxFilteredEu, CurrentFilter.Type);
|
|
return _DataMinFilteredEu[CurrentFilter.CutoffFrequencyHz];
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception(
|
|
"encountered problem getting min filtered EU on DAS: " + ((null != ParentModule && null != ParentModule.DasSerialNumber) ? ParentModule.DasSerialNumber : "<NULL>") + ", Channel: " + Number.ToString(),
|
|
ex
|
|
);
|
|
}
|
|
}
|
|
}
|
|
private readonly IDictionary<double, double> _DataMinFilteredEu
|
|
= new Dictionary<double, double>();
|
|
|
|
/// <summary>
|
|
/// Get the maximum EU data value for the current filtering.
|
|
/// </summary>
|
|
public double DataMaxFilteredEu
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!_DataMaxFilteredEu.Keys.Contains(CurrentFilter.CutoffFrequencyHz))
|
|
PopulateFilteredMinMaxValues(_DataMinFilteredEu, _DataMaxFilteredEu, CurrentFilter.Type);
|
|
return _DataMaxFilteredEu[CurrentFilter.CutoffFrequencyHz];
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception(
|
|
"encountered problem getting max filtered EU on DAS: " + ((null != ParentModule && null != ParentModule.DasSerialNumber) ? ParentModule.DasSerialNumber : "<NULL>") + ", Channel: " + Number.ToString(),
|
|
ex
|
|
);
|
|
}
|
|
}
|
|
}
|
|
private readonly IDictionary<double, double> _DataMaxFilteredEu
|
|
= new Dictionary<double, double>();
|
|
|
|
/// <summary>
|
|
/// Get the <see cref="double"/> data max in MV.
|
|
/// </summary>
|
|
public double DataMaxMv => (Scaler.GetAdcToMvScalingFactor() >= 0) ?
|
|
Scaler.GetMv(DataMaxAdc) :
|
|
Scaler.GetMv(DataMinAdc);
|
|
|
|
/// <summary>
|
|
/// Get the <see cref="double"/> data range of this channel in ADC.
|
|
/// </summary>
|
|
public double DataRangeAdc
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!_DataRangeAdc.IsInitialized)
|
|
return _DataRangeAdc.Value = DataMaxAdc - DataMinAdc;
|
|
return _DataRangeAdc.Value;
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + typeof(Channel).Namespace + ".Event.Module.Channel.DataRangeAdc", ex);
|
|
}
|
|
}
|
|
}
|
|
private readonly Property<double> _DataRangeAdc
|
|
= new Property<double>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.DataRangeAdc",
|
|
0.0,
|
|
false
|
|
);
|
|
|
|
/// <summary>
|
|
/// Get the <see cref="double"/> data range in EU.
|
|
/// </summary>
|
|
public abstract double DataRangeEu { get; }
|
|
|
|
/// <summary>
|
|
/// Get the <see cref="double"/> data range in MV.
|
|
/// </summary>
|
|
public double DataRangeMv => Math.Abs(Scaler.GetMv(DataRangeAdc));
|
|
|
|
/// <summary>
|
|
/// Get the <see cref="double"/> data range/2 of this channel in ADC.
|
|
/// </summary>
|
|
public double DataHalfRangeValueAdc
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!_DataHalfRangeValueAdc.IsInitialized)
|
|
return _DataHalfRangeValueAdc.Value = (DataMaxAdc + DataMinAdc) / 2;
|
|
return _DataHalfRangeValueAdc.Value;
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + typeof(Channel).Namespace + ".Event.Module.Channel.DataMeanAdc", ex);
|
|
}
|
|
}
|
|
}
|
|
private readonly Property<double> _DataHalfRangeValueAdc
|
|
= new Property<double>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.DataHalfRangeValueAdc",
|
|
0.0,
|
|
false
|
|
);
|
|
|
|
/// <summary>
|
|
/// Get the <see cref="double"/> data range/2 in EU.
|
|
/// </summary>
|
|
public abstract double DataHalfRangeValueEu { get; }
|
|
|
|
/// <summary>
|
|
/// Get the <see cref="double"/> data range/2 in MV.
|
|
/// </summary>
|
|
public double DataHalfRangeValueMv => Scaler.GetMv(DataHalfRangeValueAdc);
|
|
|
|
/// <summary>
|
|
/// Get/set <see cref="double"/> noise as percentage of full scale value for this channel.
|
|
/// </summary>
|
|
public double NoiseAsPercentageOfFullScale
|
|
{
|
|
get => _NoiseAsPercentageOfFullScale.Value;
|
|
set => _NoiseAsPercentageOfFullScale.Value = value;
|
|
}
|
|
private readonly Property<double> _NoiseAsPercentageOfFullScale
|
|
= new Property<double>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.NoiseAsPercentageOfFullScale",
|
|
0.0,
|
|
false
|
|
);
|
|
|
|
/// <summary>
|
|
/// Get the pre-test zero level (in ADC).
|
|
/// </summary>
|
|
public short PreTestZeroLevelAdc
|
|
{
|
|
get => _PreTestZeroLevelAdc.Value;
|
|
set => _PreTestZeroLevelAdc.Value = value;
|
|
}
|
|
private readonly Property<short> _PreTestZeroLevelAdc
|
|
= new Property<short>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.PreTestZeroLevelAdc",
|
|
0,
|
|
false
|
|
);
|
|
//zeroMvInADC
|
|
public short ZeroMvInADC
|
|
{
|
|
get => _ZeroMvInADC.Value;
|
|
set => _ZeroMvInADC.Value = value;
|
|
}
|
|
private readonly Property<short> _ZeroMvInADC
|
|
= new Property<short>(
|
|
typeof(Channel).Name + ".Event.Module.Channel.ZeroMvInADC",
|
|
0,
|
|
false
|
|
);
|
|
|
|
/// <summary>
|
|
/// Window Average ADC is the average ADC over the zeroing window specified for the channel
|
|
/// short.MinValue indicates an invalid or uninitialized value
|
|
/// </summary>
|
|
public short WindowAverageADC
|
|
{
|
|
get => _WindowAverageADC.Value;
|
|
set => _WindowAverageADC.Value = value;
|
|
}
|
|
private readonly Property<short> _WindowAverageADC
|
|
= new Property<short>(
|
|
typeof(Channel).Name + ".Event.Module.Channel.WindowAverageADC",
|
|
short.MinValue,
|
|
false
|
|
);
|
|
|
|
/// <summary>
|
|
/// Get the pre-test zero level (in ADC).
|
|
/// </summary>
|
|
public double PreTestZeroLevelMv
|
|
{
|
|
get
|
|
{
|
|
if (_PreTestZeroLevelMv.IsValueInitialized)
|
|
{
|
|
return _PreTestZeroLevelMv.Value;
|
|
}
|
|
return 0D;
|
|
}
|
|
set => _PreTestZeroLevelMv.Value = value;
|
|
}
|
|
private readonly Property<double> _PreTestZeroLevelMv
|
|
= new Property<double>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.PreTestZeroLevelMv",
|
|
0D,
|
|
true
|
|
);
|
|
public int RemovedADC
|
|
{
|
|
get
|
|
{
|
|
if (_removedADC.IsValueInitialized)
|
|
{
|
|
return _removedADC.Value;
|
|
}
|
|
return 0;
|
|
}
|
|
set
|
|
{
|
|
_removedADC.Value = value;
|
|
Scaler.SetRemovedADC(value);
|
|
}
|
|
}
|
|
private readonly Property<int> _removedADC
|
|
= new Property<int>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.RemovedADC",
|
|
0,
|
|
true
|
|
);
|
|
public int RemovedInternalADC
|
|
{
|
|
get
|
|
{
|
|
if (_removedInternalADC.IsValueInitialized)
|
|
{
|
|
return _removedInternalADC.Value;
|
|
}
|
|
return 0;
|
|
}
|
|
set
|
|
{
|
|
_removedInternalADC.Value = value;
|
|
Scaler.SetRemovedInternalADC(value);
|
|
}
|
|
}
|
|
private readonly Property<int> _removedInternalADC
|
|
= new Property<int>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.RemovedInternalADC",
|
|
0,
|
|
true
|
|
);
|
|
/// <summary>
|
|
/// the order of this channel among all channels when displaying channels
|
|
/// AbsoluteNumber on the other hand refers to physical order
|
|
/// -1 is an uninitialized value, which means channels will be sorted by AbsoluteNumber instead
|
|
/// </summary>
|
|
public int AbsoluteDisplayOrder
|
|
{
|
|
get
|
|
{
|
|
if (_absoluteDisplayOrder.IsValueInitialized) { return _absoluteDisplayOrder.Value; }
|
|
return -1;
|
|
}
|
|
set => _absoluteDisplayOrder.Value = value;
|
|
}
|
|
private readonly Property<int> _absoluteDisplayOrder = new Property<int>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.AbsoluteDisplayOrder",
|
|
-1,
|
|
true);
|
|
|
|
|
|
/// <summary>
|
|
/// Get data zero level counts.
|
|
/// </summary>
|
|
public abstract short DataZeroLevelAdc
|
|
{
|
|
get;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get/Set channel Last Calibration Date
|
|
/// </summary>
|
|
public DateTime LastCalibrationDate
|
|
{
|
|
get => _LastCalibrationDate.Value;
|
|
set => _LastCalibrationDate.Value = value;
|
|
}
|
|
private readonly Property<DateTime> _LastCalibrationDate
|
|
= new Property<DateTime>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.LastCalibrationDate",
|
|
(DateTime)System.Data.SqlTypes.SqlDateTime.MinValue,
|
|
true
|
|
);
|
|
|
|
public bool IsLastCalibrationDateValid => _LastCalibrationDate.IsInitialized;
|
|
|
|
/// <summary>
|
|
/// Get/Set channel Last Calibration Date
|
|
/// </summary>
|
|
public DateTime CalDueDate
|
|
{
|
|
get => _CalDueDate.Value;
|
|
set => _CalDueDate.Value = value;
|
|
}
|
|
private readonly Property<DateTime> _CalDueDate
|
|
= new Property<DateTime>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.CalDueDate",
|
|
(DateTime)System.Data.SqlTypes.SqlDateTime.MinValue,
|
|
true
|
|
);
|
|
|
|
public bool IsCalDueDateValid => _CalDueDate.IsInitialized;
|
|
|
|
public string SensorID
|
|
{
|
|
get => _SensorID.Value;
|
|
set => _SensorID.Value = value;
|
|
}
|
|
private readonly Property<string> _SensorID
|
|
= new Property<string>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.SensorID",
|
|
"",
|
|
true
|
|
);
|
|
public bool IsSensorIDValid => _SensorID.IsInitialized;
|
|
|
|
public double OffsetToleranceLowMv
|
|
{
|
|
get => _offsetToleranceLowMv.Value;
|
|
set => _offsetToleranceLowMv.Value = value;
|
|
}
|
|
private readonly Property<double> _offsetToleranceLowMv
|
|
= new Property<double>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.OffsetToleranceLowMv",
|
|
0D,
|
|
true
|
|
);
|
|
public bool IsOffsetToleranceLowMvValid => _offsetToleranceLowMv.IsInitialized;
|
|
|
|
public double OffsetToleranceHighMv
|
|
{
|
|
get => _offsetToleranceHighMv.Value;
|
|
set => _offsetToleranceHighMv.Value = value;
|
|
}
|
|
private readonly Property<double> _offsetToleranceHighMv
|
|
= new Property<double>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.OffsetToleranceHighMv",
|
|
0D,
|
|
true
|
|
);
|
|
public bool IsOffsetToleranceHighMvValid => _offsetToleranceHighMv.IsInitialized;
|
|
|
|
public string IsoChannelName
|
|
{
|
|
get => _isoChannelName.Value;
|
|
set => _isoChannelName.Value = value;
|
|
}
|
|
private readonly Property<string> _isoChannelName
|
|
= new Property<string>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.IsoChannelName",
|
|
"",
|
|
true
|
|
);
|
|
public bool IsIsoChannelNameValid => _isoChannelName.IsInitialized;
|
|
|
|
public string UserCode
|
|
{
|
|
get => _userCode.Value;
|
|
set => _userCode.Value = value;
|
|
}
|
|
private readonly Property<string> _userCode
|
|
= new Property<string>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.UserCode",
|
|
"",
|
|
true
|
|
);
|
|
public bool IsUserCodeValid => _userCode.IsInitialized;
|
|
|
|
public string UserChannelName
|
|
{
|
|
get => _userChannelName.Value;
|
|
set => _userChannelName.Value = value;
|
|
}
|
|
private readonly Property<string> _userChannelName
|
|
= new Property<string>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.UserChannelName",
|
|
"",
|
|
true
|
|
);
|
|
|
|
public string LinearSensorCalibration
|
|
{
|
|
get => _linearSensorCalibration.Value;
|
|
set => _linearSensorCalibration.Value = value;
|
|
}
|
|
private readonly Property<string> _linearSensorCalibration
|
|
= new Property<string>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.LinearSensorCalibration",
|
|
"",
|
|
true
|
|
);
|
|
|
|
public bool IsUserChannelNameValid => _userChannelName.IsInitialized;
|
|
/// <summary>
|
|
/// Get/set channel subsample indicator.
|
|
/// </summary>
|
|
public bool IsSubsampled
|
|
{
|
|
get => _IsSubsampled.Value;
|
|
set => _IsSubsampled.Value = value;
|
|
}
|
|
private readonly Property<bool> _IsSubsampled
|
|
= new Property<bool>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.IsSubsampled",
|
|
false,
|
|
true
|
|
);
|
|
|
|
/// <summary>
|
|
/// Get/set channel unsubsampled sample rate.
|
|
/// </summary>
|
|
public float UnsubsampledSampleRateHz
|
|
{
|
|
get => _UnsubsampledSampleRateHz.Value;
|
|
set => _UnsubsampledSampleRateHz.Value = value;
|
|
}
|
|
private readonly Property<float> _UnsubsampledSampleRateHz
|
|
= new Property<float>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.UnsubsampledSampleRateHz",
|
|
0,
|
|
true
|
|
);
|
|
|
|
/// <summary>
|
|
/// Get/set the sample rate for this test module.
|
|
/// </summary>
|
|
public bool IsSupersampled
|
|
{
|
|
get => _IsSupersampled.Value;
|
|
set => _IsSupersampled.Value = value;
|
|
}
|
|
private readonly Property<bool> _IsSupersampled
|
|
= new Property<bool>(
|
|
typeof(Module).Namespace + ".Event.Module.Channel.IsSupersampled",
|
|
false,
|
|
false
|
|
);
|
|
|
|
/// <summary>
|
|
/// Get/set whether viewing and exporting this channel should use the EU Scalar.
|
|
/// </summary>
|
|
public bool UseEUScaler
|
|
{
|
|
get => _UseEUScalar.Value;
|
|
set => _UseEUScalar.Value = value;
|
|
}
|
|
private readonly Property<bool> _UseEUScalar
|
|
= new Property<bool>(
|
|
typeof(Module).Namespace + ".Event.Module.Channel.UseEUScaler",
|
|
false,
|
|
false
|
|
);
|
|
|
|
/// <summary>
|
|
/// Get/set the sample rate for this test module.
|
|
/// </summary>
|
|
public float UnsupersampledSampleRateHz
|
|
{
|
|
get => _UnsupersampledSampleRateHz.Value;
|
|
set => _UnsupersampledSampleRateHz.Value = value;
|
|
}
|
|
private readonly Property<float> _UnsupersampledSampleRateHz
|
|
= new Property<float>(
|
|
typeof(Module).Namespace + ".Event.Module.Channel.UnsupersampledSampleRateHz",
|
|
0,
|
|
false
|
|
);
|
|
|
|
public void ClearDataCache()
|
|
{
|
|
var filtering = UseFilterCaching;
|
|
|
|
_Data = null;
|
|
_DataEu = null;
|
|
_DataMv = null;
|
|
_UnfilteredDataEu = null;
|
|
_DataADC = null;
|
|
|
|
lock (previouslyFilteredDataLock)
|
|
{
|
|
_previouslyFilteredData.Clear();
|
|
}
|
|
|
|
UseFilterCaching = filtering;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create the matching Event.Module.Channel-based representation of the specified
|
|
/// DTS.DASLib.Service.DASChannel-based object.
|
|
/// </summary>
|
|
///
|
|
/// <param name="channel">
|
|
/// The <see cref="DTS.DASLib.Service.DASChannel"/> object to be represented.
|
|
/// </param>
|
|
///
|
|
/// <param name="parentModule">
|
|
/// The <see cref="DTS.Slice.Control.Event.Module"/> that contains the created channel.
|
|
/// </param>
|
|
///
|
|
/// <returns>
|
|
/// The <see cref="DTS.Slice.Control.Event.Module.Channel"/>-based representation of the specified
|
|
/// channel object.
|
|
/// </returns>
|
|
///
|
|
/// <param name="absoluteNumber">
|
|
/// The unique "absolute" <see cref="int"/> number of the channel with respect to all other channels
|
|
/// on all other DASes in the system.
|
|
/// </param>
|
|
///
|
|
public static Channel CreateChannel(DASChannel channel, Module parentModule, int absoluteNumber)
|
|
{
|
|
try
|
|
{
|
|
switch (channel.GetType().FullName)
|
|
{
|
|
case "DTS.DASLib.Service.AnalogInputDASChannel":
|
|
return new AnalogInputChannel(channel, parentModule, absoluteNumber);
|
|
case "DTS.DASLib.Service.IEPEChannel":
|
|
return new IEPEInputChannel(channel, parentModule, absoluteNumber);
|
|
case "DTS.DASLib.Service.OutputSquibChannel":
|
|
{
|
|
var osc = channel as OutputSquibChannel;
|
|
var output = new SquibEventChannel(channel, parentModule, absoluteNumber);
|
|
output.MeasurementType = osc.MeasurementType;
|
|
output.ChannelName2 = osc.ChannelName2;
|
|
if (osc.MeasurementType == SquibMeasurementType.CURRENT)
|
|
{
|
|
if (osc.SquibDescription.EndsWith(".1"))
|
|
{
|
|
var newName = osc.SquibDescription.Substring(0,
|
|
osc.SquibDescription.Length - 2);
|
|
newName += ".2";
|
|
output.ChannelName2 = output.ChannelName2.Replace(osc.SquibDescription, newName);
|
|
}
|
|
}
|
|
output.ChannelId = osc.ChannelId;
|
|
output.Sensor = osc.Sensor;
|
|
output.SerialNumber = osc.SerialNumber;
|
|
output.ChannelGroupName = osc.ChannelGroupName;
|
|
return output;
|
|
}
|
|
case "DTS.DASLib.Service.OutputTOMDigitalChannel":
|
|
{
|
|
return new SquibDigitalChannel(channel, parentModule, absoluteNumber);
|
|
}
|
|
case "DTS.DASLib.Service.TimestampDASChannel":
|
|
{
|
|
return new TimestampChannel(channel, parentModule, absoluteNumber);
|
|
}
|
|
case "DTS.DASLib.Service.StreamInputDASChannel":
|
|
{
|
|
return new StreamInputChannel(channel, parentModule, absoluteNumber);
|
|
}
|
|
case "DTS.DASLib.Service.CANInputDASChannel":
|
|
{
|
|
return new CANInputChannel(channel, parentModule, absoluteNumber);
|
|
}
|
|
default:
|
|
throw new NotImplementedException("cannot create a " + typeof(Channel).FullName + "-based representation of a " + channel.GetType().FullName + " object");
|
|
}
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new ApplicationException("encountered problem creating channel", ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create the matching Event.Module.Channel-based representation of the specified
|
|
/// DTS.DASLib.Service.DASChannel-based object.
|
|
/// </summary>
|
|
///
|
|
/// <param name="channel">
|
|
/// The <see cref="DTS.DASLib.Service.DASChannel"/> object to be represented.
|
|
/// </param>
|
|
///
|
|
/// <param name="parentModule">
|
|
/// The <see cref="DTS.Slice.Control.Event.Module"/> that contains the created channel.
|
|
/// </param>
|
|
///
|
|
/// <returns>
|
|
/// The <see cref="DTS.Slice.Control.Event.Module.Channel"/>-based representation of the specified
|
|
/// channel object.
|
|
/// </returns>
|
|
///
|
|
/// <param name="absoluteNumber">
|
|
/// The unique "absolute" <see cref="int"/> number of the channel with respect to all other channels
|
|
/// on all other DASes in the system.
|
|
/// </param>
|
|
///
|
|
public static Channel CreateChannel(Serialization.Test.Module.Channel channel, Module parentModule, int absoluteNumber)
|
|
{
|
|
try
|
|
{
|
|
switch (channel.GetType().FullName)
|
|
{
|
|
case "DTS.Serialization.Test+Module+AnalogInputChannel":
|
|
return new AnalogInputChannel(channel, parentModule, absoluteNumber);
|
|
case "DTS.Serialization.Test+Module+CalculatedChannel":
|
|
return new AnalogInputChannel(channel, parentModule, absoluteNumber);
|
|
default:
|
|
throw new NotImplementedException("cannot create a " + typeof(Channel).FullName + "-based representation of a " + channel.GetType().FullName + " object");
|
|
}
|
|
}
|
|
catch (System.IO.InvalidDataException ex2) { throw ex2; }
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new ApplicationException("encountered problem creating channel", ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the appropriate properties in this class from the equivalent properties
|
|
/// of the specified object.
|
|
/// </summary>
|
|
///
|
|
///<param name="dasChannel">
|
|
/// The <see cref="DTS.DASLib.Service.DASChannel"/> object containing the
|
|
/// property values to be copied.
|
|
/// </param>
|
|
///
|
|
public abstract void SetPropertyValuesFrom(DASChannel dasChannel);
|
|
|
|
/// <summary>
|
|
/// Set the appropriate properties in this class from the equivalent properties
|
|
/// of the specified object.
|
|
/// </summary>
|
|
///
|
|
///<param name="diagResults">
|
|
/// The <see cref="DTS.DASLib.Service.DiagnosticsResult"/> object containing the
|
|
/// property values to be copied.
|
|
/// </param>
|
|
///
|
|
public virtual void SetPropertyValuesFrom(IDiagnosticResult diagResults)
|
|
{
|
|
try
|
|
{
|
|
if (null == diagResults)
|
|
throw new ArgumentNullException("cannot set property values from null " + typeof(DiagnosticsResult).FullName);
|
|
ScaleFactorEU = diagResults.ScalefactorEngineeringUnitsPerADC;
|
|
ScaleFactorMv = diagResults.ScalefactorMilliVoltsPerADC;
|
|
if (this is AnalogInputChannel)
|
|
{
|
|
Scaler.IEPE = (this as AnalogInputChannel).Bridge == SensorConstants.BridgeType.IEPE;
|
|
}
|
|
try
|
|
{
|
|
if (this is AnalogInputChannel)
|
|
{
|
|
Scaler.Digital = (this as AnalogInputChannel).Bridge == SensorConstants.BridgeType.DigitalInput;
|
|
}
|
|
else { Scaler.Digital = false; }
|
|
}
|
|
catch (System.Exception) { }
|
|
|
|
if (this is IShuntAware)
|
|
{
|
|
if (null != diagResults.MeasuredShuntDeflectionMv)
|
|
(this as IShuntAware).MeasuredShuntDeflectionMv = (double)diagResults.MeasuredShuntDeflectionMv;
|
|
|
|
if (null != diagResults.TargetShuntDeflectionMv)
|
|
(this as IShuntAware).TargetShuntDeflectionMv = (double)diagResults.TargetShuntDeflectionMv;
|
|
}
|
|
|
|
if (this is ICalSignalAware)
|
|
{
|
|
if (null != diagResults.MeasuredCalSignalMv)
|
|
(this as ICalSignalAware).MeasuredCalSignalMv = (double)diagResults.MeasuredCalSignalMv;
|
|
|
|
if (null != diagResults.TargetCalSignalMv)
|
|
(this as ICalSignalAware).TargetCalSignalMv = (double)diagResults.TargetCalSignalMv;
|
|
}
|
|
try
|
|
{
|
|
NoiseAsPercentageOfFullScale = null != diagResults.NoisePercentFullScale ? (double)diagResults.NoisePercentFullScale : 0.0;
|
|
}
|
|
catch (InvalidOperationException)
|
|
{
|
|
throw new UserException("Diagnostics service did not return a value for requested \"noise as percentage of full scale\" statistic");
|
|
}
|
|
|
|
if (null != diagResults.FinalOffsetADC)
|
|
{
|
|
try
|
|
{
|
|
PreTestZeroLevelAdc = (short)diagResults.FinalOffsetADC;
|
|
}
|
|
catch (InvalidOperationException)
|
|
{
|
|
throw new UserException("Diagnostics service did not return a value for requested \"final offset\" statistic");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PreTestZeroLevelAdc = 0;
|
|
}
|
|
|
|
ZeroMvInADC = Convert.ToInt16(diagResults.ZeroMVInADC);
|
|
|
|
WindowAverageADC = Convert.ToInt16(diagResults.WindowAverageADC);
|
|
|
|
if (null != diagResults.MeasuredOffsetMilliVolts)
|
|
{
|
|
try
|
|
{
|
|
PreTestZeroLevelMv = (double)diagResults.MeasuredOffsetMilliVolts;
|
|
}
|
|
catch (InvalidOperationException)
|
|
{
|
|
/*throw new UserException( "diagnostics service did not return a value for requested \"measured offset\" statistic" ); */
|
|
PreTestZeroLevelMv = 0D;
|
|
}
|
|
}
|
|
else { PreTestZeroLevelMv = 0D; }
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting property value from " + (null != diagResults ? diagResults.GetType().FullName : "<<NULL>>") + " object", ex);
|
|
}
|
|
try
|
|
{
|
|
if (null != diagResults.RemovedOffsetADC)
|
|
{
|
|
RemovedADC = (short)diagResults.RemovedOffsetADC;
|
|
}
|
|
else { RemovedADC = 0; }
|
|
}
|
|
catch (System.Exception) { }
|
|
}
|
|
|
|
///// <summary>
|
|
///// Get the list of available filters.
|
|
///// </summary>
|
|
public List<IFilter> AvailableFilters => _AvailableFilters.Value;
|
|
|
|
private readonly Property<List<IFilter>> _AvailableFilters
|
|
= new Property<List<IFilter>>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.AvailableFilters",
|
|
new List<IFilter>(),
|
|
true
|
|
);
|
|
|
|
/// <summary>
|
|
/// Get/set the default filter for this channel.
|
|
/// </summary>
|
|
public IFilter CurrentFilter
|
|
{
|
|
get => _CurrentFilter.Value;
|
|
set
|
|
{ //
|
|
// Might be lots of gratuitous "changing" of filter to the same value, since
|
|
// last I checked it happens every time you flip from one channel to another
|
|
// in the data viewer. Anyway, if nothing has changed, don't force a reload
|
|
// of the cached data.
|
|
//
|
|
if (_CurrentFilter.Value == null
|
|
|| !value.Name.Equals(_CurrentFilter.Value.Name, StringComparison.OrdinalIgnoreCase))
|
|
{ //
|
|
// Clear filtered data caches and set new filter value.
|
|
//
|
|
_Data = null;
|
|
_DataEu = null;
|
|
_DataMv = null;
|
|
|
|
_CurrentFilter.Value = value;
|
|
}
|
|
}
|
|
}
|
|
private readonly Property<IFilter> _CurrentFilter = new Property<IFilter>(typeof(Channel).Namespace + ".Event.Module.Channel.CurrentFilter",
|
|
null,
|
|
true
|
|
);
|
|
|
|
/// <summary>
|
|
/// Get/set the default filter for this channel.
|
|
/// </summary>
|
|
public IFilter DefaultFilter
|
|
{
|
|
get => _defaultFilter.Value;
|
|
set
|
|
{
|
|
if (_defaultFilter.Value == null || !value.Name.Equals(_defaultFilter.Value.Name, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
_Data = null;
|
|
_DataEu = null;
|
|
_DataMv = null;
|
|
|
|
_defaultFilter.Value = value;
|
|
}
|
|
}
|
|
}
|
|
private readonly Property<IFilter> _defaultFilter = new Property<IFilter>(typeof(Channel).Namespace + ".Event.Module.Channel.DefaultFilter", null, true);
|
|
public string FileName
|
|
{
|
|
get => _FileName.Value;
|
|
set => _FileName.Value = value;
|
|
}
|
|
private readonly Property<string> _FileName
|
|
= new Property<string>(
|
|
typeof(Channel).Namespace + ".Event.Module.Channel.FileName",
|
|
"",
|
|
true
|
|
);
|
|
|
|
protected static readonly object DisplayUnitLock = new object();
|
|
protected static readonly object previouslyFilteredDataLock = new object();
|
|
|
|
public void UnSet()
|
|
{
|
|
if (null != _previouslyFilteredData) { _previouslyFilteredData.Clear(); }
|
|
if (null != _DataEu) { _DataEu.Clear(); _DataEu = null; }
|
|
if (null != _UnfilteredDataEu) { _UnfilteredDataEu.Clear(); _UnfilteredDataEu = null; }
|
|
System.Runtime.GCSettings.LatencyMode = System.Runtime.GCLatencyMode.Batch;
|
|
System.Runtime.GCSettings.LargeObjectHeapCompactionMode = System.Runtime.GCLargeObjectHeapCompactionMode.CompactOnce;
|
|
GC.Collect();
|
|
}
|
|
/// <summary>
|
|
/// Get the channel data after filtering by the specified filter.
|
|
/// </summary>
|
|
///
|
|
/// <param name="filter">
|
|
/// The <see cref="IFilter"/> to be applied to the channel.
|
|
/// </param>
|
|
/// <param name="displayUnits"></param>
|
|
/// <returns>
|
|
/// The requested filtered channel <see cref="double"/> data.
|
|
/// </returns>
|
|
///
|
|
public double[] GetDataFilteredBy(IFilter filter, DataDisplayUnits displayUnits)
|
|
{
|
|
try
|
|
{
|
|
double[] filteredData;
|
|
|
|
if (UseFilterCaching)
|
|
{
|
|
IDictionary<DataDisplayUnits, double[]> displayUnitData;
|
|
|
|
lock (previouslyFilteredDataLock)
|
|
{
|
|
if (!_previouslyFilteredData.Keys.Contains(filter.Name))
|
|
_previouslyFilteredData.Add(filter.Name, new Dictionary<DataDisplayUnits, double[]>());
|
|
|
|
// First cache key should now exist, so work it, baby!
|
|
displayUnitData = _previouslyFilteredData[filter.Name];
|
|
}
|
|
|
|
lock (DisplayUnitLock)
|
|
{
|
|
if (displayUnitData.Keys.Contains(displayUnits))
|
|
filteredData = displayUnitData[displayUnits];
|
|
else
|
|
{
|
|
try
|
|
{
|
|
displayUnitData.Add(displayUnits, filteredData = filter.Apply(this, displayUnits, Serialization.File.UseLegacyTDCSoftwareFiltering));
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem adding; displayUnits " + displayUnits, ex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else filteredData = filter.Apply(this, displayUnits, Serialization.File.UseLegacyTDCSoftwareFiltering);
|
|
|
|
// By hook or crook, filter data should be here.
|
|
return filteredData;
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting data filtered by channel filter " + (null != filter && null != filter.Name ? "\"" + filter.Name + "\"" : "<NULL>"), ex);
|
|
}
|
|
}
|
|
private readonly IDictionary<string, IDictionary<DataDisplayUnits, double[]>> _previouslyFilteredData
|
|
= new Dictionary<string, IDictionary<DataDisplayUnits, double[]>>();
|
|
|
|
/// <summary>
|
|
/// Test the specified object for equality with this object.
|
|
/// </summary>
|
|
///
|
|
/// <param name="obj">
|
|
/// The <see cref="object"/> to be tested for equality.
|
|
/// </param>
|
|
///
|
|
/// <returns>
|
|
/// <see cref="bool"/> true if the specified object has memeberwise equality with
|
|
/// this object; false otherwise.
|
|
/// </returns>
|
|
///
|
|
public override bool Equals(object obj)
|
|
{
|
|
try
|
|
{
|
|
var that = obj as Channel;
|
|
return null != obj
|
|
&& DataEquals(that.UnfilteredData)
|
|
&& Number.Equals(that.Number)
|
|
&& PreTestZeroLevelAdc.Equals(that.PreTestZeroLevelAdc)
|
|
&& IsSubsampled.Equals(that.IsSubsampled)
|
|
&& UnsubsampledSampleRateHz.Equals(that.UnsubsampledSampleRateHz)
|
|
&& IsSupersampled.Equals(that.IsSupersampled)
|
|
&& UseEUScaler.Equals(that.UseEUScaler)
|
|
&& UnsupersampledSampleRateHz.Equals(that.UnsupersampledSampleRateHz)
|
|
&& _TimeOfFirstSampleSec.IsInitialized == that._TimeOfFirstSampleSec.IsInitialized && (TimeOfFirstSampleSecValid ? TimeOfFirstSampleSec == that.TimeOfFirstSampleSec : true)
|
|
&& Start.ToString().Equals(that.Start.ToString())
|
|
&& (ZeroMvInADC == that.ZeroMvInADC)
|
|
&& (WindowAverageADC == that.WindowAverageADC)
|
|
&& (UserCode == that.UserCode)
|
|
&& (IsoChannelName == that.IsoChannelName)
|
|
&& (UserChannelName == that.UserChannelName);
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception(
|
|
string.Format(
|
|
Strings.DTS_Slice_Control_Equals_ComparisonFailedString, null != obj ? "\"" + obj.ToString() + "\"" : Strings.DTS_Slice_Control_Event_Event_NullDasListString),
|
|
ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test the specified object's data list for equality with this object's
|
|
/// data list.
|
|
/// </summary>
|
|
///
|
|
/// <param name="thoseData">
|
|
/// The List of <see cref="UInt16"/> data objects to be compared
|
|
/// for equality with this module's equivalent.
|
|
/// </param>
|
|
///
|
|
/// <returns>
|
|
/// <see cref="bool"/> true if the two lists contain equivalent-valued members;
|
|
/// false otherwise.
|
|
/// </returns>
|
|
///
|
|
private bool DataEquals(List<short> thoseData)
|
|
{
|
|
try
|
|
{
|
|
if (null == thoseData || Data.Count != thoseData.Count)
|
|
return false;
|
|
for (var i = 0; i < thoseData.Count; i++)
|
|
if (!UnfilteredData[i].Equals(thoseData[i]))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception(Strings.DTS_Slice_Control_Event_Module_Channel_DataEquals_ComparisonFailedString, ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return the hash code for this object.
|
|
/// </summary>
|
|
///
|
|
/// <returns>
|
|
/// The <see cref="int"/> hash code for this object.
|
|
/// </returns>
|
|
///
|
|
public override int GetHashCode()
|
|
{
|
|
try
|
|
{
|
|
return base.GetHashCode();
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting has code for " + GetType().FullName, ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Convert this object to a DTS.Serialization.Test.Module.Channel.
|
|
/// </summary>
|
|
///
|
|
/// <returns>
|
|
/// A <see cref="DTS.Serialization.Test.Module.Channel"/> equivalent for this object.
|
|
/// </returns>
|
|
///
|
|
public abstract Serialization.Test.Module.Channel ToDtsSerializationTestModuleChannel(Serialization.Test.Module parentModule);
|
|
|
|
/// <summary>
|
|
/// Initialize this object from the specified DTS.Serialization.Test.Module.Channel.
|
|
/// </summary>
|
|
///
|
|
/// <param name="that">
|
|
/// A <see cref="DTS.Serialization.Test.Module.Channel"/> from which to initialize this object.
|
|
/// </param>
|
|
///
|
|
public abstract void FromDtsSerializationTestModuleChannel(Serialization.Test.Module.Channel that);
|
|
}
|
|
}
|
|
}
|
|
}
|