2434 lines
113 KiB
C#
2434 lines
113 KiB
C#
/*
|
|
* SliceRaw.File.PersistentChannel.cs
|
|
*
|
|
* Copyright © 2009
|
|
* Diversified Technical Systems, Inc.
|
|
* All Rights Reserved
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using DTS.Common.DAS.Concepts.DAS.Channel;
|
|
using DTS.Common.Exceptions;
|
|
using DTS.Common.Utilities;
|
|
using DTS.Common.Utilities.DotNetProgrammingConstructs;
|
|
using DTS.Common.Utilities.IO.MemoryMap;
|
|
using DTS.Common.Utilities.Logging;
|
|
|
|
namespace DTS.Serialization.SliceRaw
|
|
{
|
|
public partial class File
|
|
{
|
|
/// <summary>
|
|
/// Representation of a channel. Changes made to this representation
|
|
/// are immediately manifest in the associated channel file.
|
|
/// </summary>
|
|
public partial class PersistentChannel
|
|
: ExceptionalList<short>,
|
|
ILargeDataAware,
|
|
IChannelHeader,
|
|
IDisposable
|
|
{
|
|
/// <summary>
|
|
/// returns an iso code given a filepath to a SLICE .chn file
|
|
/// does not have exception handling
|
|
/// </summary>
|
|
/// <param name="binaryFileName">filepath to SLICE .chn file</param>
|
|
/// <returns>isocode</returns>
|
|
public static string GetIsoCode(string binaryFileName)
|
|
{
|
|
var pch = new PersistentChannel(binaryFileName, null, false);
|
|
return new string(pch.IsoCode);
|
|
}
|
|
/// <summary>
|
|
/// The window into this persistent channel's soul.
|
|
/// </summary>
|
|
protected FileMapViewArray ViewArray
|
|
{
|
|
get => _ViewArray.Value;
|
|
set => _ViewArray.Value = value;
|
|
}
|
|
private readonly Property<FileMapViewArray> _ViewArray
|
|
= new Property<FileMapViewArray>(
|
|
typeof(PersistentChannel).Namespace + ".File.PersistentChannel.ViewArray",
|
|
null,
|
|
false
|
|
);
|
|
|
|
/// <summary>
|
|
/// Get <see cref="bool"/> value indicating whether or not this class has been
|
|
/// preinitialized from an existing serialization.
|
|
/// </summary>
|
|
protected bool IsInitializedFromFile
|
|
{
|
|
get => _IsInitializedFromFile.Value;
|
|
|
|
private set => _IsInitializedFromFile.Value = value;
|
|
}
|
|
private readonly Property<bool> _IsInitializedFromFile
|
|
= new Property<bool>(
|
|
typeof(PersistentChannel).Namespace + ".File.PersistentChannel.IsInitializedFromFile",
|
|
false,
|
|
false
|
|
);
|
|
|
|
private readonly Property<short> _minADC
|
|
= new Property<short>(
|
|
typeof(PersistentChannel).Namespace + ".File.PersistentChannel.MinADC",
|
|
short.MinValue,
|
|
false);
|
|
|
|
public short MinADC
|
|
{
|
|
get
|
|
{
|
|
if (!_minADC.IsInitialized) { ComputeMinMaxADC(); }
|
|
return _minADC.Value;
|
|
}
|
|
}
|
|
|
|
private readonly Property<short> _maxADC
|
|
= new Property<short>(
|
|
typeof(PersistentChannel).Namespace + ".File.PersistentChannel.MaxADC",
|
|
short.MaxValue,
|
|
false);
|
|
|
|
private readonly object LockObject;
|
|
|
|
public short MaxADC
|
|
{
|
|
get
|
|
{
|
|
if (!_maxADC.IsInitialized) { ComputeMinMaxADC(); }
|
|
return _maxADC.Value;
|
|
}
|
|
}
|
|
|
|
internal void ComputeMinMaxADC()
|
|
{
|
|
var min = short.MaxValue;
|
|
var max = short.MinValue;
|
|
for (ulong sample = 0; sample < NumberOfSamples; sample++)
|
|
{
|
|
min = Math.Min(min, Data[sample]);
|
|
max = Math.Max(max, Data[sample]);
|
|
}
|
|
_minADC.Value = min;
|
|
_maxADC.Value = max;
|
|
}
|
|
/// <summary>
|
|
/// Initialize an instance of this class.
|
|
/// </summary>
|
|
///
|
|
/// <param name="filename">
|
|
/// The <see cref="string"/> filename of the associated channel data file.
|
|
/// </param>
|
|
///
|
|
/// <param name="channelHeader">
|
|
/// The channel header to be written into the associated channel data file.
|
|
/// </param>
|
|
///
|
|
public PersistentChannel(string filename, BinaryChannelHeader channelHeader, bool overwriteIfExists)
|
|
{
|
|
LockObject = new object();
|
|
try
|
|
{
|
|
IsInitializedFromFile = System.IO.File.Exists(Filename = filename);
|
|
|
|
IsMemoryMapped = false;
|
|
|
|
if (IsInitializedFromFile && !overwriteIfExists) return;
|
|
if (null == channelHeader)
|
|
throw new ArgumentNullException("channel header is required to generate new file but none was provided");
|
|
NumberOfTriggerSamples = channelHeader.NumberOfTriggers;
|
|
|
|
//APILogger.Log("writing 2 ", this.Filename);
|
|
//using (var mutex = new System.Threading.Mutex(false, Filename.Replace(Path.DirectorySeparatorChar, '_')))
|
|
lock (LockObject)
|
|
{
|
|
//while (!mutex.WaitOne(50, false)) { System.Threading.Thread.Sleep(5); }
|
|
|
|
//try
|
|
{
|
|
using (var fileWriter = new BinaryWriter(new FileStream(Filename, FileMode.OpenOrCreate)))
|
|
{
|
|
var fields = Enum.GetValues(typeof(Field)).Cast<Field>().ToArray();
|
|
// convert to byte[]
|
|
|
|
var euBA = Encoding.UTF8.GetBytes(channelHeader.EngineeringUnit);
|
|
if (euBA.Length % 2 != 0)
|
|
{
|
|
var xStr = new string(channelHeader.EngineeringUnit) + ' ';
|
|
euBA = Encoding.UTF8.GetBytes(xStr);
|
|
}
|
|
foreach (var field in fields)
|
|
{
|
|
switch (field)
|
|
{
|
|
case Field.AreSamplesSigned:
|
|
fileWriter.Write(this.channelHeader.AreSamplesSigned = channelHeader.AreSamplesSigned); _AreSamplesSignedInitialized = true;
|
|
break;
|
|
case Field.BeginningOfData:
|
|
break;
|
|
case Field.BeginningOfFile:
|
|
break;
|
|
case Field.Crc32:
|
|
fileWriter.Write(channelHeader.Crc32); // public UInt32
|
|
break;
|
|
case Field.DataZeroLevelCounts:
|
|
fileWriter.Write(this.channelHeader.DataZeroLevelCounts = channelHeader.DataZeroLevelCounts); _DataZeroLevelCountsInitialized = true;
|
|
break;
|
|
case Field.EngineeringUnit:
|
|
fileWriter.Write(euBA);
|
|
this.channelHeader.EngineeringUnit = channelHeader.EngineeringUnit;
|
|
_EngineeringUnitInitialized = true; // public Char[]
|
|
break;
|
|
case Field.EuFieldLengthWithTerminator:
|
|
channelHeader.EuFieldLengthWithTerminator = (ushort)(euBA.Length + 1);
|
|
fileWriter.Write(this.channelHeader.EuFieldLengthWithTerminator = channelHeader.EuFieldLengthWithTerminator); _EuFieldLengthWithTerminatorInitialized = true; // public UInt16
|
|
break;
|
|
case Field.Excitation:
|
|
fileWriter.Write(this.channelHeader.Excitation = channelHeader.Excitation); _ExcitationInitialized = true;
|
|
break;
|
|
case Field.HeaderVersionNumber:
|
|
fileWriter.Write(this.channelHeader.HeaderVersionNumber = BinaryChannelHeader.CurrentVersionNumber); _HeaderVersionNumberInitialized = true;
|
|
break;
|
|
case Field.IsoCode:
|
|
for (var k = 0; k < 16; k++)
|
|
fileWriter.Write(channelHeader.IsoCode[k] = char.IsLetterOrDigit(channelHeader.IsoCode[k])
|
|
? channelHeader.IsoCode[k]
|
|
: '?');
|
|
this.channelHeader.IsoCode = channelHeader.IsoCode; _IsoCodeInitialized = true; // public Char[]
|
|
break;
|
|
case Field.MagicKey:
|
|
fileWriter.Write(this.channelHeader.MagicKey = BinaryChannelHeader.RequiredMagicKey); _MagicKeyInitialized = true;
|
|
break;
|
|
case Field.MvPerEu:
|
|
fileWriter.Write(this.channelHeader.MvPerEu = channelHeader.MvPerEu); _MvPerEuInitialized = true;
|
|
break;
|
|
case Field.NumberOfBitsPerSample:
|
|
fileWriter.Write(this.channelHeader.NumberOfBitsPerSample = channelHeader.NumberOfBitsPerSample); _NumberOfBitsPerSampleInitialized = true;
|
|
break;
|
|
case Field.NumberOfSamples:
|
|
fileWriter.Write(this.channelHeader.NumberOfSamples = channelHeader.NumberOfSamples); _NumberOfSamplesInitialized = true;
|
|
break;
|
|
case Field.NumberOfTriggers:
|
|
fileWriter.Write(this.channelHeader.NumberOfTriggers = channelHeader.NumberOfTriggers); _NumberOfTriggersInitialized = true;
|
|
break;
|
|
case Field.OffsetOfSampleDataStart:
|
|
fileWriter.Write(this.channelHeader.OffsetOfSampleDataStart = channelHeader.OffsetOfSampleDataStart); _OffsetOfSampleDataStartInitialized = true;
|
|
break;
|
|
case Field.OriginalOffsetADC:
|
|
fileWriter.Write(this.channelHeader.OriginalOffsetADC = channelHeader.OriginalOffsetADC); _OriginalOffsetADCInitialized = true;
|
|
break;
|
|
case Field.PostTestDiagnosticsLevelCounts:
|
|
fileWriter.Write(this.channelHeader.PostTestDiagnosticsLevelCounts = channelHeader.PostTestDiagnosticsLevelCounts); _PostTestDiagnosticsLevelCountsInitialized = true;
|
|
break;
|
|
case Field.PostTestZeroLevelCounts:
|
|
fileWriter.Write(this.channelHeader.PostTestZeroLevelCounts = channelHeader.PostTestZeroLevelCounts); _PostTestZeroLevelCountsInitialized = true;
|
|
break;
|
|
case Field.PreTestDiagnosticsLevelCounts:
|
|
fileWriter.Write(this.channelHeader.PreTestDiagnosticsLevelCounts = channelHeader.PreTestDiagnosticsLevelCounts); _PreTestDiagnosticsLevelCountsInitialized = true;
|
|
break;
|
|
case Field.PreTestNoisePercentageOfFullScale:
|
|
fileWriter.Write(this.channelHeader.PreTestNoisePercentageOfFullScale = channelHeader.PreTestNoisePercentageOfFullScale); _PreTestNoisePercentageOfFullScaleInitialized = true;
|
|
break;
|
|
case Field.PreTestZeroLevelCounts:
|
|
fileWriter.Write(this.channelHeader.PreTestZeroLevelCounts = channelHeader.PreTestZeroLevelCounts); _PreTestZeroLevelCountsInitialized = true;
|
|
break;
|
|
case Field.RemovedADC:
|
|
fileWriter.Write(this.channelHeader.RemovedADC = channelHeader.RemovedADC); _RemovedADCInitialized = true;
|
|
break;
|
|
case Field.SampleRate:
|
|
fileWriter.Write(this.channelHeader.SampleRate = channelHeader.SampleRate); _SampleRateInitialized = true;
|
|
break;
|
|
case Field.ScaleFactorMv:
|
|
fileWriter.Write(this.channelHeader.ScaleFactorMv = channelHeader.ScaleFactorMv); _ScaleFactorMvInitialized = true;
|
|
break;
|
|
case Field.TriggerAdjustmentSamples:
|
|
fileWriter.Write(this.channelHeader.TriggerAdjustmentSamples = channelHeader.TriggerAdjustmentSamples); _TriggerAdjustmentSamplesInitialized = true;
|
|
break;
|
|
case Field.TriggerSampleNumbers:
|
|
this.channelHeader.TriggerSampleNumbers = channelHeader.TriggerSampleNumbers; _TriggerSampleNumbersInitialized = true;
|
|
for (var i = 0; i < channelHeader.NumberOfTriggers; i++)
|
|
fileWriter.Write(channelHeader.TriggerSampleNumbers[i]);
|
|
break;
|
|
case Field.ZeroMvInADC:
|
|
fileWriter.Write(this.channelHeader.ZeroMvInADC = channelHeader.ZeroMvInADC); _ZeroMvInADCInitialized = true;
|
|
break;
|
|
case Field.WindowAverageADC:
|
|
fileWriter.Write(this.channelHeader.WindowAverageADC = channelHeader.WindowAverageADC); _WindowAverageADCInitialized = true;
|
|
break;
|
|
default:
|
|
throw new NotSupportedException("SliceRaw::File::PersistantChannel(filename, channelheader, overwriteIfExists) field not supported: " + field.ToString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//finally { mutex.ReleaseMutex(); }
|
|
}
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem constructing " + GetType().FullName, ex);
|
|
}
|
|
}
|
|
|
|
// xxx Make this a recursive function, with the terminating condition being a call with the first case, which
|
|
// returns the offset of "start of file".
|
|
//
|
|
|
|
public enum Field
|
|
{
|
|
BeginningOfFile, // UInt32
|
|
MagicKey, // public UInt32
|
|
HeaderVersionNumber, // public UInt32
|
|
OffsetOfSampleDataStart, // public UInt64
|
|
NumberOfSamples, // public UInt64
|
|
NumberOfBitsPerSample, // public UInt32
|
|
AreSamplesSigned, // public UInt32
|
|
SampleRate, // public Double
|
|
NumberOfTriggers, // public UInt16
|
|
TriggerSampleNumbers, // public UInt64[]
|
|
PreTestZeroLevelCounts, // public Int32
|
|
RemovedADC, // V2
|
|
PreTestDiagnosticsLevelCounts, // public Int32
|
|
PreTestNoisePercentageOfFullScale, // public Double
|
|
PostTestZeroLevelCounts, // public Int32
|
|
PostTestDiagnosticsLevelCounts, // public Int32
|
|
DataZeroLevelCounts, // public Int32
|
|
ScaleFactorMv, // public Double
|
|
MvPerEu, // public Double
|
|
EuFieldLengthWithTerminator, // public UInt16
|
|
EngineeringUnit, // public Char[]
|
|
Excitation, // public Double V3
|
|
TriggerAdjustmentSamples, // public Int32 V3
|
|
ZeroMvInADC, // public Int32 V3
|
|
WindowAverageADC, // public Int32 V4
|
|
OriginalOffsetADC, // public Int32 V3
|
|
IsoCode, // public Char[]
|
|
Crc32, // public UInt32
|
|
BeginningOfData
|
|
|
|
}
|
|
//this is wasteful, we should improve it to use cached results when we've already computed the offset of previous fields rather than
|
|
//recursively calculating everytime ... however we don't do it much so it's probably low priority
|
|
|
|
private int GetSizeOfV1(Field field)
|
|
{
|
|
switch (field)
|
|
{
|
|
case Field.AreSamplesSigned:
|
|
return sizeof(uint);
|
|
case Field.BeginningOfData:
|
|
return 0;
|
|
case Field.BeginningOfFile:
|
|
return sizeof(uint);
|
|
case Field.Crc32:
|
|
return sizeof(uint);
|
|
case Field.DataZeroLevelCounts:
|
|
return sizeof(int);
|
|
case Field.EngineeringUnit:
|
|
return EuFieldLengthWithTerminator - 1;
|
|
case Field.EuFieldLengthWithTerminator:
|
|
return sizeof(short);
|
|
case Field.HeaderVersionNumber:
|
|
return sizeof(uint);
|
|
case Field.IsoCode:
|
|
return 16;
|
|
case Field.MagicKey:
|
|
return sizeof(uint);
|
|
case Field.MvPerEu:
|
|
return sizeof(double);
|
|
case Field.NumberOfBitsPerSample:
|
|
return sizeof(uint);
|
|
case Field.NumberOfSamples:
|
|
return sizeof(ulong);
|
|
case Field.NumberOfTriggers:
|
|
return sizeof(ushort);
|
|
case Field.OffsetOfSampleDataStart:
|
|
return sizeof(ulong);
|
|
case Field.PostTestDiagnosticsLevelCounts:
|
|
return sizeof(int);
|
|
case Field.PostTestZeroLevelCounts:
|
|
return sizeof(int);
|
|
case Field.PreTestDiagnosticsLevelCounts:
|
|
return sizeof(int);
|
|
case Field.PreTestNoisePercentageOfFullScale:
|
|
return sizeof(double);
|
|
case Field.PreTestZeroLevelCounts:
|
|
return sizeof(int);
|
|
case Field.SampleRate:
|
|
return sizeof(double);
|
|
case Field.ScaleFactorMv:
|
|
return sizeof(double);
|
|
case Field.TriggerSampleNumbers:
|
|
return sizeof(ulong) * NumberOfTriggers;
|
|
}
|
|
return 0;
|
|
}
|
|
private int GetSizeOfV2(Field field)
|
|
{
|
|
switch (field)
|
|
{
|
|
case Field.RemovedADC:
|
|
return sizeof(int);
|
|
default:
|
|
return GetSizeOfV1(field);
|
|
}
|
|
}
|
|
private int GetSizeOfV3(Field field)
|
|
{
|
|
switch (field)
|
|
{
|
|
case Field.OriginalOffsetADC:
|
|
return sizeof(int);
|
|
case Field.ZeroMvInADC:
|
|
return sizeof(int);
|
|
case Field.TriggerAdjustmentSamples:
|
|
return sizeof(int);
|
|
case Field.Excitation:
|
|
return sizeof(double);
|
|
default:
|
|
return GetSizeOfV2(field);
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// gets the size of fields as of version 4
|
|
/// (includes the sizes of all fields prior to V4)
|
|
/// </summary>
|
|
/// <param name="field"></param>
|
|
/// <returns></returns>
|
|
private int GetSizeOfV4(Field field)
|
|
{
|
|
switch (field)
|
|
{
|
|
case Field.WindowAverageADC:
|
|
return sizeof(int);
|
|
default:
|
|
return GetSizeOfV3(field);
|
|
}
|
|
}
|
|
private int GetFileOffsetOfV1(Field field)
|
|
{
|
|
var previousField = (Field)((int)field - 1);
|
|
var offset = GetFileOffsetOf(previousField) + GetSizeOfV1(previousField);
|
|
return offset;
|
|
}
|
|
private int GetFileOffsetOfV2(Field field)
|
|
{
|
|
var previousField = (Field)((int)field - 1);
|
|
var offset = GetFileOffsetOf(previousField) + GetSizeOfV2(previousField);
|
|
return offset;
|
|
}
|
|
private int GetFileOffsetOfV3(Field field)
|
|
{
|
|
var previousField = (Field)((int)field - 1);
|
|
var offset = GetFileOffsetOf(previousField) + GetSizeOfV3(previousField);
|
|
return offset;
|
|
}
|
|
/// <summary>
|
|
/// gets the file offset for a field as of version 4 of the header
|
|
/// this method is aware of field sizes up to version 4 of the header
|
|
/// </summary>
|
|
/// <param name="field"></param>
|
|
/// <returns></returns>
|
|
private int GetFileOffsetOfV4(Field field)
|
|
{
|
|
var previousField = (Field)((int)field - 1);
|
|
var offset = GetFileOffsetOf(previousField) + GetSizeOfV4(previousField);
|
|
return offset;
|
|
}
|
|
/// <summary>
|
|
/// if field is one of the first 3, the offset doesn't change
|
|
/// however for the rest we need to know the header version number and then we
|
|
/// can get the file offset
|
|
/// </summary>
|
|
/// <param name="field"></param>
|
|
/// <returns></returns>
|
|
public int GetFileOffsetOf(Field field)
|
|
{
|
|
try
|
|
{
|
|
var offset = 0;
|
|
|
|
//if we are header version number or less, the fields don't change position ...
|
|
if (field == Field.HeaderVersionNumber || field == Field.MagicKey || field == Field.BeginningOfFile)
|
|
{
|
|
switch (field)
|
|
{
|
|
case Field.BeginningOfFile:
|
|
offset = 0;
|
|
break;
|
|
case Field.MagicKey:
|
|
offset = GetFileOffsetOf(Field.BeginningOfFile);
|
|
break;
|
|
case Field.HeaderVersionNumber:
|
|
offset = GetFileOffsetOf(Field.MagicKey) + sizeof(uint);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (HeaderVersionNumber)
|
|
{
|
|
case 0x01:
|
|
return GetFileOffsetOfV1(field);
|
|
case 0x02:
|
|
return GetFileOffsetOfV2(field);
|
|
case 0x03:
|
|
return GetFileOffsetOfV3(field);
|
|
case 0x04:
|
|
return GetFileOffsetOfV4(field);
|
|
}
|
|
}
|
|
return offset;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting file offset for property", ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get <see cref="bool"/> value indicating whether or not all necessary properties of this
|
|
/// class have been initialized to make this object useable.
|
|
/// </summary>
|
|
public bool IsInitialized
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
return _AreSamplesSignedInitialized
|
|
&& _DataZeroLevelCountsInitialized
|
|
&& _EngineeringUnitInitialized
|
|
&& _EuFieldLengthWithTerminatorInitialized
|
|
&& _HeaderVersionNumberInitialized
|
|
&& _IsoCodeInitialized
|
|
&& _MagicKeyInitialized
|
|
&& _MvPerEuInitialized
|
|
&& _NumberOfBitsPerSampleInitialized
|
|
&& _NumberOfSamplesInitialized
|
|
&& _NumberOfTriggersInitialized
|
|
&& _OffsetOfSampleDataStartInitialized
|
|
&& _PostTestDiagnosticsLevelCountsInitialized
|
|
&& _PostTestZeroLevelCountsInitialized
|
|
&& _PreTestDiagnosticsLevelCountsInitialized
|
|
&& _PreTestNoisePercentageOfFullScaleInitialized
|
|
&& _PreTestZeroLevelCountsInitialized
|
|
&& _SampleRateInitialized
|
|
&& _ScaleFactorMvInitialized
|
|
&& _TriggerSampleNumbersInitialized;
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem determining initialization status", ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determine whether or not this data set is small enough to be safely fit into any array
|
|
/// i.e., is int-indexable.
|
|
/// </summary>
|
|
public bool IsDataArraySized => NumberOfSamples < int.MaxValue;
|
|
|
|
/// <summary>
|
|
/// Get/set the number of trigger samples.
|
|
/// </summary>
|
|
protected int NumberOfTriggerSamples
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// <see cref="int"/> size of view into memory-mapped file.
|
|
/// </summary>
|
|
protected int ViewSize
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
private bool _IsViewSizeInitialized = false;
|
|
|
|
|
|
/// <summary>
|
|
/// <see cref="bool"/> to indicate whether or not a memory mapping has already been
|
|
/// established for this object.
|
|
/// </summary>
|
|
public bool IsMemoryMapped
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the <see cref="long"/> length of this persistent channel's data.
|
|
/// </summary>
|
|
public long Length
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (NumberOfSamples > long.MaxValue)
|
|
throw new ApplicationException("ulong overflowed while being cast to long");
|
|
return (long)NumberOfSamples;
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem determining total amount of channel data", ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the <see cref="int"/> data count of this persistent channel's data.
|
|
/// </summary>
|
|
public new int Count
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (Length > int.MaxValue)
|
|
throw new ApplicationException("long overflowed while being cast to int");
|
|
return (int)Length;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting channel data count", ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Override long-indexing here. Return a sample of the underlying array.
|
|
/// </summary>
|
|
///
|
|
/// <param name="i">
|
|
/// The <see cref="long"/> index of the sample being affected.
|
|
/// </param>
|
|
///
|
|
/// <returns>
|
|
/// The <see cref="short"/> sample at the specified index.
|
|
/// </returns>
|
|
///
|
|
public short this[long i]
|
|
{
|
|
get
|
|
{
|
|
try { return this[(ulong)i]; }
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting long-indexable value at index " + i.ToString(), ex);
|
|
}
|
|
}
|
|
|
|
set
|
|
{
|
|
try { this[(ulong)i] = value; }
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting long-indexable value at index " + i.ToString(), ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void UnSet()
|
|
{
|
|
lock (LockObject)
|
|
{
|
|
_Data.Value = new short[0];
|
|
_Data.UnInitialize();
|
|
}
|
|
}
|
|
public short[] GetAllData()
|
|
{
|
|
var retArray = new short[NumberOfSamples];
|
|
|
|
using (var stream = new FileStream(Filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
|
{
|
|
using (var br = new BinaryReader(stream))
|
|
{
|
|
br.BaseStream.Seek((long)OffsetOfSampleDataStart, SeekOrigin.Begin);
|
|
for (var i = 0; i < retArray.Length; i++)
|
|
{
|
|
retArray[i] = br.ReadInt16();
|
|
}
|
|
}
|
|
}
|
|
return retArray;
|
|
}
|
|
// Override indexing operator here, return a sample of the underlying array.
|
|
public short this[ulong i]
|
|
{
|
|
get
|
|
{
|
|
lock (LockObject)
|
|
{
|
|
try
|
|
{
|
|
var offsetSampleDataStart = OffsetOfSampleDataStart;
|
|
if (!_IsViewSizeInitialized)
|
|
{
|
|
//
|
|
// Memory map array object map view paging math seems to be a bit
|
|
// screwed. If you don't make the page exactly the same size as the
|
|
// system memory allocation granularity, it will start returning trash
|
|
// data after the first page fault.
|
|
//
|
|
const int StandardPageSize = 65536;
|
|
if (NumberOfSamples + offsetSampleDataStart > StandardPageSize)
|
|
{
|
|
ViewSize = StandardPageSize;
|
|
}
|
|
else
|
|
{ //
|
|
// If the map size exceeds the file size, we'll get strange COM error
|
|
// codes.
|
|
//
|
|
var numberOfDataBytes = NumberOfSamples * 2;
|
|
if (numberOfDataBytes + offsetSampleDataStart > int.MaxValue)
|
|
throw new Exception("overflowed view size variable (max value: " + int.MaxValue.ToString() + ") when downcasting number of bytes in test data (" + numberOfDataBytes.ToString() + ")");
|
|
ViewSize = (int)(numberOfDataBytes + offsetSampleDataStart);
|
|
}
|
|
|
|
_IsViewSizeInitialized = true;
|
|
}
|
|
|
|
|
|
// MMBAD: Possible inefficiency. Perhaps either for "debug" build, or find some other way
|
|
// to verify this outside of the array access.
|
|
//
|
|
if (!IsMemoryMapped)
|
|
{
|
|
var fileInfo = new FileInfo(Filename);
|
|
// Make a new mapViewArray for the file with proper offset and
|
|
// readonly privilages to protect sample integrity.
|
|
ViewArray = new FileMapViewArray(Path.GetFullPath(Filename),
|
|
MapAccess.FileMapRead,
|
|
MapProtection.PageReadOnly,
|
|
offsetSampleDataStart,
|
|
fileInfo.Length,
|
|
ViewSize);
|
|
IsMemoryMapped = true;
|
|
}
|
|
|
|
//http://manuscript.dts.local/f/cases/16463/HDF-export-runs-very-slowly
|
|
long osdsLong = (long)(offsetSampleDataStart + 2 * i);
|
|
return (short)((ViewArray[osdsLong] & 0xFFFF) + ((ViewArray[osdsLong + 1L] & 0xFFFF) << 8));
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting short value at index " + i.ToString(), ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
set
|
|
{
|
|
try
|
|
{
|
|
throw new NotSupportedException("SliceRaw::File::PersistentChannel::[] Sample Data is READ_ONLY.");
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting short value at index " + i.ToString(), ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Some older files will need this format to be invoked to fix slight formatting problems.
|
|
/// </summary>
|
|
public void RecalculateDataOffset()
|
|
{
|
|
try
|
|
{ //
|
|
// Older SliceWARE data files have incorrect values for the offset of sample data start,
|
|
// and number of data samples. Adjust them or we may run into trouble later.
|
|
//
|
|
if (0 == OffsetOfSampleDataStart)
|
|
{
|
|
var realDataStart = GetFileOffsetOf(Field.BeginningOfData);
|
|
OffsetOfSampleDataStart = (ulong)realDataStart;
|
|
NumberOfSamples -= (ulong)realDataStart;
|
|
}
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem recalculating data offset", ex);
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Get/set the filename of the associated channel data file.
|
|
/// </summary>
|
|
public string Filename
|
|
{
|
|
get => _Filename.Value;
|
|
private set => _Filename.Value = value;
|
|
}
|
|
private readonly Property<string> _Filename
|
|
= new Property<string>(
|
|
typeof(PersistentChannel).Namespace + ".File.PreAllocatedChannel.Filename",
|
|
null,
|
|
false
|
|
);
|
|
|
|
public void ReplaceData(short[] newData)
|
|
{
|
|
if (newData.Length != _Data.Value.Length)
|
|
{
|
|
return;
|
|
}
|
|
_Data.Value = newData;
|
|
|
|
try
|
|
{
|
|
var bytes = new List<byte>();
|
|
foreach (var dataShort in newData)
|
|
{
|
|
bytes.AddRange(BitConverter.GetBytes(dataShort));
|
|
}
|
|
var dummyVar = false;
|
|
OpenAndWrite(Field.BeginningOfData, bytes.ToArray(), ref dummyVar);
|
|
PostWrite();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem replacing data", ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get/set <see cref="short"/> data array value.
|
|
/// </summary>
|
|
public short[] Data
|
|
{
|
|
get
|
|
{
|
|
var lastDataSpot = -1;
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!IsDataArraySized)
|
|
throw new DataTooBigForArrayException("Data size (" + Count.ToString() + ") is too large to be safely handled within the application");
|
|
if (!_Data.IsInitialized)
|
|
{ //
|
|
// If we haven't handed out one of these yet, make one up and then hang onto it
|
|
// to use for subsequent requests to we don't have clones all over the place.
|
|
//
|
|
var dataArray = new short[Count];
|
|
for (var i = 0; i < dataArray.Length; i++)
|
|
{
|
|
lastDataSpot = i;
|
|
dataArray[i] = this[i];
|
|
}
|
|
|
|
_Data.Value = dataArray;
|
|
}
|
|
return _Data.Value;
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
string msg = string.Empty;
|
|
if (lastDataSpot > 0)
|
|
{
|
|
throw new OutOfDataException($"Problem accessing sample {lastDataSpot} - {GetType().FullName} {ex}", lastDataSpot);
|
|
}
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".Data property value", ex);
|
|
}
|
|
}
|
|
|
|
set
|
|
{
|
|
try
|
|
{
|
|
_Data.Value = value;
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".Data property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private readonly Property<short[]> _Data
|
|
= new Property<short[]>(
|
|
typeof(PersistentChannel).Namespace + ".File.PreAllocatedChannel.Data",
|
|
null,
|
|
false
|
|
);
|
|
|
|
/// <summary>
|
|
/// Get/set binary writer to be used by data append session.
|
|
/// </summary>
|
|
private BinaryWriter AppendSessionWriter
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Prepare this object for repeated calls to AppendSessionData.
|
|
/// </summary>
|
|
public void BeginAppendSession()
|
|
{
|
|
try
|
|
{
|
|
AppendSessionWriter = new BinaryWriter(new FileStream(Filename, FileMode.Open));
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem beginning data append session", ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Secure object from repeated calls to AppendSessionData.
|
|
/// </summary>
|
|
public void EndAppendSession()
|
|
{
|
|
try
|
|
{
|
|
AppendSessionWriter.Close();
|
|
AppendSessionWriter.Dispose();
|
|
AppendSessionWriter = null;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem ending data append session", ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Append data to this persistent channel. Must be bracketed by BeginAppendSession and
|
|
/// EndAppendSession method invocations.
|
|
/// </summary>
|
|
///
|
|
/// <param name="data">
|
|
/// The <see cref="short"/> data to be appended to this persisitent channel.
|
|
/// </param>
|
|
///
|
|
public void AppendSessionData(short[] data)
|
|
{
|
|
try
|
|
{
|
|
if (!_NumberOfTriggersInitialized)
|
|
throw new DependencyNotInitializedException("cannot append data before the number of trigger samples has been specified");
|
|
lock (LockObject)
|
|
{
|
|
{
|
|
AppendSessionWriter.Seek(0, SeekOrigin.End);
|
|
for (var i = 0; i < data.Length; i++)
|
|
AppendSessionWriter.Write(data[i]);
|
|
}
|
|
}
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem appending data to persistent channel representation", ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Append data to this persistent channel. To be used for one-time applications. If we're streaming
|
|
/// data to this object, AppendSessionData is more efficient.
|
|
/// </summary>
|
|
///
|
|
/// <param name="data">
|
|
/// The <see cref="short"/> data to be appended to this persistent channel.
|
|
/// </param>
|
|
///
|
|
public void DisabledAppendData(short[] data)
|
|
{
|
|
try
|
|
{
|
|
if (!_NumberOfTriggersInitialized)
|
|
throw new DependencyNotInitializedException("cannot append data before the number of trigger samples has been specified");
|
|
lock (LockObject)
|
|
{
|
|
using (var fileWriter = new BinaryWriter(new FileStream(Filename, FileMode.Open)))
|
|
{
|
|
fileWriter.Seek(0, SeekOrigin.End);
|
|
for (var i = 0; i < data.Length; i++)
|
|
fileWriter.Write(data[i]);
|
|
}
|
|
}
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem appending data to persistent channel representation", ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Object to cache header property accesses.
|
|
/// </summary>
|
|
public BinaryChannelHeader channelHeader = new BinaryChannelHeader();
|
|
|
|
|
|
/// <summary>
|
|
/// opens the binary file, reads header field value from file
|
|
/// </summary>
|
|
/// <param name="field"></param>
|
|
/// <param name="output"></param>
|
|
/// <param name="_initField"></param>
|
|
private void OpenAndRead(Field field, out int output, ref bool _initField)
|
|
{
|
|
var offset = GetFileOffsetOf(field);
|
|
lock (LockObject)
|
|
{
|
|
using (var fileReader = new BinaryReader(new FileStream(Filename, FileMode.Open)))
|
|
{
|
|
var prevPosition = fileReader.BaseStream.Position;
|
|
fileReader.BaseStream.Position = offset;
|
|
output = fileReader.ReadInt32();
|
|
fileReader.BaseStream.Position = prevPosition;
|
|
}
|
|
}
|
|
_initField = true;
|
|
}
|
|
/// <summary>
|
|
/// opens the binary file, reads header field value from file
|
|
/// </summary>
|
|
/// <param name="field"></param>
|
|
/// <param name="output"></param>
|
|
/// <param name="_initField"></param>
|
|
private void OpenAndRead(Field field, out ushort output, ref bool _initField)
|
|
{
|
|
var offset = GetFileOffsetOf(field);
|
|
lock (LockObject)
|
|
{
|
|
using (var fileReader = new BinaryReader(new FileStream(Filename, FileMode.Open)))
|
|
{
|
|
var prevPosition = fileReader.BaseStream.Position;
|
|
fileReader.BaseStream.Position = offset;
|
|
output = fileReader.ReadUInt16();
|
|
fileReader.BaseStream.Position = prevPosition;
|
|
}
|
|
}
|
|
_initField = true;
|
|
}
|
|
/// <summary>
|
|
/// opens the binary file, reads header field value from file
|
|
/// </summary>
|
|
/// <param name="field"></param>
|
|
/// <param name="output"></param>
|
|
/// <param name="_initField"></param>
|
|
private void OpenAndRead(Field field, out uint output, ref bool _initField)
|
|
{
|
|
var offset = GetFileOffsetOf(field);
|
|
lock (LockObject)
|
|
{
|
|
using (var fileReader = new BinaryReader(new FileStream(Filename, FileMode.Open)))
|
|
{
|
|
var prevPosition = fileReader.BaseStream.Position;
|
|
fileReader.BaseStream.Position = offset;
|
|
output = fileReader.ReadUInt32();
|
|
fileReader.BaseStream.Position = prevPosition;
|
|
}
|
|
}
|
|
_initField = true;
|
|
}
|
|
/// <summary>
|
|
/// opens the binary file, reads header field value from file
|
|
/// </summary>
|
|
/// <param name="field"></param>
|
|
/// <param name="output"></param>
|
|
/// <param name="_initField"></param>
|
|
private void OpenAndRead(Field field, out ulong output, ref bool _initField)
|
|
{
|
|
var offset = GetFileOffsetOf(field);
|
|
lock (LockObject)
|
|
{
|
|
using (var fileReader = new BinaryReader(new FileStream(Filename, FileMode.Open)))
|
|
{
|
|
var prevPosition = fileReader.BaseStream.Position;
|
|
fileReader.BaseStream.Position = offset;
|
|
output = fileReader.ReadUInt64();
|
|
fileReader.BaseStream.Position = prevPosition;
|
|
}
|
|
}
|
|
_initField = true;
|
|
}
|
|
/// <summary>
|
|
/// opens the binary file, reads header field value from file
|
|
/// </summary>
|
|
/// <param name="field"></param>
|
|
/// <param name="output"></param>
|
|
/// <param name="_initField"></param>
|
|
private void OpenAndRead(Field field, out double output, ref bool _initField)
|
|
{
|
|
var offset = GetFileOffsetOf(field);
|
|
lock (LockObject)
|
|
{
|
|
using (var fileReader = new BinaryReader(new FileStream(Filename, FileMode.Open)))
|
|
{
|
|
var prevPosition = fileReader.BaseStream.Position;
|
|
fileReader.BaseStream.Position = offset;
|
|
output = fileReader.ReadDouble();
|
|
fileReader.BaseStream.Position = prevPosition;
|
|
}
|
|
}
|
|
_initField = true;
|
|
}
|
|
/// <summary>
|
|
/// after anything is written to the header, we re-sync the CRC (apparently)
|
|
/// </summary>
|
|
private void PostWrite()
|
|
{
|
|
// If at this point the persistent channel object has been initialized, go ahead and
|
|
// keep the CRC current.
|
|
if (IsInitialized)
|
|
Crc32 = channelHeader.Crc32;
|
|
}
|
|
/// <summary>
|
|
/// "Magic Key" relatively-UID <see cref="UInt32"/> for our binary file type.
|
|
/// </summary>
|
|
public uint MagicKey
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!_MagicKeyInitialized)
|
|
{
|
|
OpenAndRead(Field.MagicKey, out uint temp, ref _MagicKeyInitialized);
|
|
channelHeader.MagicKey = temp;
|
|
}
|
|
return channelHeader.MagicKey;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".MagicKey property value", ex);
|
|
}
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
OpenAndWrite(Field.MagicKey, BitConverter.GetBytes(value), ref _MagicKeyInitialized);
|
|
channelHeader.MagicKey = value;
|
|
PostWrite();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".MagicKey property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private bool _MagicKeyInitialized = false;
|
|
|
|
/// <summary>
|
|
/// <see cref="UInt32"/> header version number for our binary file type.
|
|
/// </summary>
|
|
public uint HeaderVersionNumber
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!_HeaderVersionNumberInitialized)
|
|
{
|
|
OpenAndRead(Field.HeaderVersionNumber, out uint temp, ref _HeaderVersionNumberInitialized);
|
|
channelHeader.HeaderVersionNumber = temp;
|
|
}
|
|
return channelHeader.HeaderVersionNumber;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".HeaderVersionNumber property value", ex);
|
|
}
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
OpenAndWrite(Field.HeaderVersionNumber, BitConverter.GetBytes(value), ref _HeaderVersionNumberInitialized);
|
|
channelHeader.HeaderVersionNumber = value;
|
|
PostWrite();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".HeaderVersionNumber property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private bool _HeaderVersionNumberInitialized = false;
|
|
|
|
/// <summary>
|
|
/// Get/set offset of sample data start value.
|
|
/// </summary>
|
|
public ulong OffsetOfSampleDataStart
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!_OffsetOfSampleDataStartInitialized)
|
|
{
|
|
OpenAndRead(Field.OffsetOfSampleDataStart, out ulong temp, ref _OffsetOfSampleDataStartInitialized);
|
|
channelHeader.OffsetOfSampleDataStart = temp;
|
|
}
|
|
return channelHeader.OffsetOfSampleDataStart;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".OffsetOfSampleDataStart property value", ex);
|
|
}
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
OpenAndWrite(Field.OffsetOfSampleDataStart, BitConverter.GetBytes(value), ref _OffsetOfSampleDataStartInitialized);
|
|
channelHeader.OffsetOfSampleDataStart = value;
|
|
PostWrite();
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".OffsetOfSampleDataStart property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private bool _OffsetOfSampleDataStartInitialized = false;
|
|
|
|
/// <summary>
|
|
/// Get/set number of samples.
|
|
/// </summary>
|
|
public ulong NumberOfSamples
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!_NumberOfSamplesInitialized)
|
|
{
|
|
OpenAndRead(Field.NumberOfSamples, out ulong temp, ref _NumberOfSamplesInitialized);
|
|
channelHeader.NumberOfSamples = temp;
|
|
}
|
|
return channelHeader.NumberOfSamples;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".NumberOfSamples property value", ex);
|
|
}
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
OpenAndWrite(Field.NumberOfSamples, BitConverter.GetBytes(value), ref _NumberOfSamplesInitialized);
|
|
channelHeader.NumberOfSamples = value;
|
|
PostWrite();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".NumberOfSamples property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private bool _NumberOfSamplesInitialized = false;
|
|
|
|
/// <summary>
|
|
/// Get/set number of bits per sample.
|
|
/// </summary>
|
|
public uint NumberOfBitsPerSample
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!_NumberOfBitsPerSampleInitialized)
|
|
{
|
|
OpenAndRead(Field.NumberOfBitsPerSample, out uint temp, ref _NumberOfBitsPerSampleInitialized);
|
|
channelHeader.NumberOfBitsPerSample = temp;
|
|
}
|
|
return channelHeader.NumberOfBitsPerSample;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".NumberOfBitsPerSample property value", ex);
|
|
}
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
OpenAndWrite(Field.NumberOfBitsPerSample, BitConverter.GetBytes(value), ref _NumberOfBitsPerSampleInitialized);
|
|
channelHeader.NumberOfBitsPerSample = value;
|
|
PostWrite();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".NumberOfBitsPerSample property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private bool _NumberOfBitsPerSampleInitialized = false;
|
|
|
|
/// <summary>
|
|
/// Get/set signed sample indicator.
|
|
/// </summary>
|
|
public uint AreSamplesSigned
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!_AreSamplesSignedInitialized)
|
|
{
|
|
OpenAndRead(Field.AreSamplesSigned, out uint temp, ref _AreSamplesSignedInitialized);
|
|
channelHeader.AreSamplesSigned = temp;
|
|
}
|
|
return channelHeader.AreSamplesSigned;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".AreSamplesSigned property value", ex);
|
|
}
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
OpenAndWrite(Field.AreSamplesSigned, BitConverter.GetBytes(value), ref _AreSamplesSignedInitialized);
|
|
channelHeader.AreSamplesSigned = value;
|
|
PostWrite();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".AreSamplesSigned property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private bool _AreSamplesSignedInitialized = false;
|
|
|
|
/// <summary>
|
|
/// Get/set sample rate value.
|
|
/// </summary>
|
|
public double SampleRate
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!_SampleRateInitialized)
|
|
{
|
|
OpenAndRead(Field.SampleRate, out double temp, ref _SampleRateInitialized);
|
|
channelHeader.SampleRate = temp;
|
|
}
|
|
return channelHeader.SampleRate;
|
|
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".SampleRate property value", ex);
|
|
}
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
OpenAndWrite(Field.SampleRate, BitConverter.GetBytes(value), ref _SampleRateInitialized);
|
|
channelHeader.SampleRate = value;
|
|
PostWrite();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".SampleRate property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private bool _SampleRateInitialized = false;
|
|
|
|
/// <summary>
|
|
/// Get/set number of triggers.
|
|
/// </summary>
|
|
public ushort NumberOfTriggers
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!_NumberOfTriggersInitialized)
|
|
{
|
|
OpenAndRead(Field.NumberOfTriggers, out ushort temp, ref _NumberOfTriggersInitialized);
|
|
channelHeader.NumberOfTriggers = temp;
|
|
}
|
|
return channelHeader.NumberOfTriggers;
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".NumberOfTriggers property value", ex);
|
|
}
|
|
}
|
|
|
|
set
|
|
{
|
|
try
|
|
{
|
|
if (_NumberOfTriggersInitialized)
|
|
throw new AlreadyInitializedException("number of triggers cannot be changed once established");
|
|
|
|
OpenAndWrite(Field.NumberOfTriggers, BitConverter.GetBytes(value), ref _NumberOfTriggersInitialized);
|
|
channelHeader.NumberOfTriggers = value;
|
|
PostWrite();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".NumberOfTriggers property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private bool _NumberOfTriggersInitialized = false;
|
|
|
|
/// <summary>
|
|
/// Get/set the trigger sample numbers
|
|
/// </summary>
|
|
public ulong[] TriggerSampleNumbers
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!_TriggerSampleNumbersInitialized)
|
|
{
|
|
lock (LockObject)
|
|
{
|
|
// Go initialize our cached version from disk.
|
|
//
|
|
int numberOfTriggers = NumberOfTriggers;
|
|
channelHeader.TriggerSampleNumbers = new ulong[numberOfTriggers];
|
|
var valueOffset = GetFileOffsetOf(Field.TriggerSampleNumbers);
|
|
using (var fileReader = new BinaryReader(new FileStream(Filename, FileMode.Open)))
|
|
{
|
|
var prevPosition = fileReader.BaseStream.Position;
|
|
fileReader.BaseStream.Position = valueOffset;
|
|
|
|
for (var i = 0; i < numberOfTriggers; i++)
|
|
channelHeader.TriggerSampleNumbers[i] = fileReader.ReadUInt64();
|
|
channelHeader.TriggerSampleNumbers = channelHeader.TriggerSampleNumbers;
|
|
|
|
fileReader.BaseStream.Position = prevPosition;
|
|
_TriggerSampleNumbersInitialized = true;
|
|
}
|
|
}
|
|
}
|
|
return channelHeader.TriggerSampleNumbers;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".TriggerSampleNumbers property value", ex);
|
|
}
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
if (!_NumberOfTriggersInitialized)
|
|
throw new DependencyNotInitializedException("NumberOfTriggers must be initialized before this property value can be set");
|
|
var bytes = new List<byte>();
|
|
foreach (var trigger in value)
|
|
{
|
|
bytes.AddRange(BitConverter.GetBytes(trigger));
|
|
}
|
|
OpenAndWrite(Field.TriggerSampleNumbers, bytes.ToArray(), ref _TriggerSampleNumbersInitialized);
|
|
channelHeader.TriggerSampleNumbers = value;
|
|
PostWrite();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".TriggerSampleNumbers property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private bool _TriggerSampleNumbersInitialized = false;
|
|
|
|
|
|
public int TriggerAdjustmentSamples
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!_TriggerAdjustmentSamplesInitialized)
|
|
{
|
|
OpenAndRead(Field.TriggerAdjustmentSamples, out int temp, ref _TriggerAdjustmentSamplesInitialized);
|
|
channelHeader.TriggerAdjustmentSamples = temp;
|
|
}
|
|
return channelHeader.TriggerAdjustmentSamples;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".TriggerAdjustmentSamples property value", ex);
|
|
}
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
if (!_NumberOfTriggersInitialized)
|
|
throw new DependencyNotInitializedException("NumberOfTriggers must be initialized before this property value can be set");
|
|
OpenAndWrite(Field.TriggerAdjustmentSamples, BitConverter.GetBytes(value), ref _TriggerAdjustmentSamplesInitialized);
|
|
channelHeader.TriggerAdjustmentSamples = value;
|
|
PostWrite();
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".TriggerAdjustmentSamples property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private bool _TriggerAdjustmentSamplesInitialized = false;
|
|
|
|
/// <summary>
|
|
/// Get/set pre test zero level counts.
|
|
/// </summary>
|
|
public int PreTestZeroLevelCounts
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!_PreTestZeroLevelCountsInitialized)
|
|
{
|
|
OpenAndRead(Field.PreTestZeroLevelCounts, out int temp, ref _PreTestZeroLevelCountsInitialized);
|
|
channelHeader.PreTestZeroLevelCounts = temp;
|
|
}
|
|
return channelHeader.PreTestZeroLevelCounts;
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".PreTestZeroLevelCounts property value", ex);
|
|
}
|
|
}
|
|
|
|
set
|
|
{
|
|
try
|
|
{
|
|
if (!_NumberOfTriggersInitialized)
|
|
throw new DependencyNotInitializedException("NumberOfTriggers must be initialized before this property value can be set");
|
|
|
|
OpenAndWrite(Field.PreTestZeroLevelCounts, BitConverter.GetBytes(value), ref _PreTestZeroLevelCountsInitialized);
|
|
channelHeader.PreTestZeroLevelCounts = value;
|
|
PostWrite();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".PreTestZeroLevelCounts property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private bool _PreTestZeroLevelCountsInitialized = false;
|
|
|
|
/// <summary>
|
|
/// Get/set pre test zero level counts.
|
|
/// </summary>
|
|
public int RemovedADC
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!_RemovedADCInitialized)
|
|
{
|
|
OpenAndRead(Field.RemovedADC, out int temp, ref _RemovedADCInitialized);
|
|
channelHeader.RemovedADC = temp;
|
|
}
|
|
return channelHeader.RemovedADC;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".RemovedADC property value", ex);
|
|
}
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
if (!_NumberOfTriggersInitialized)
|
|
throw new DependencyNotInitializedException("NumberOfTriggers must be initialized before this property value can be set");
|
|
|
|
OpenAndWrite(Field.RemovedADC, BitConverter.GetBytes(value), ref _RemovedADCInitialized);
|
|
channelHeader.RemovedADC = value;
|
|
PostWrite();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".RemovedADC property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private bool _RemovedADCInitialized = false;
|
|
/// <summary>
|
|
/// Get/set pre test diagnostics level counts.
|
|
/// </summary>
|
|
public int PreTestDiagnosticsLevelCounts
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!_PreTestDiagnosticsLevelCountsInitialized)
|
|
{
|
|
OpenAndRead(Field.PreTestDiagnosticsLevelCounts, out int temp, ref _PreTestDiagnosticsLevelCountsInitialized);
|
|
channelHeader.PreTestDiagnosticsLevelCounts = temp;
|
|
}
|
|
return channelHeader.PreTestDiagnosticsLevelCounts;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".PreTestCalLevelCounts property value", ex);
|
|
}
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
OpenAndWrite(Field.PreTestDiagnosticsLevelCounts, BitConverter.GetBytes(value), ref _PreTestDiagnosticsLevelCountsInitialized);
|
|
channelHeader.PreTestDiagnosticsLevelCounts = value;
|
|
PostWrite();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
if (!_NumberOfTriggersInitialized)
|
|
throw new DependencyNotInitializedException("NumberOfTriggers must be initialized before this property value can be set");
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".PreTestCalLevelCounts property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private bool _PreTestDiagnosticsLevelCountsInitialized = false;
|
|
|
|
|
|
public int OriginalOffsetADC
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!_OriginalOffsetADCInitialized)
|
|
{
|
|
OpenAndRead(Field.OriginalOffsetADC, out int temp, ref _OriginalOffsetADCInitialized);
|
|
channelHeader.OriginalOffsetADC = temp;
|
|
}
|
|
return channelHeader.OriginalOffsetADC;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".OriginalOffsetADC property value", ex);
|
|
}
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
OpenAndWrite(Field.OriginalOffsetADC, BitConverter.GetBytes(value), ref _OriginalOffsetADCInitialized);
|
|
channelHeader.OriginalOffsetADC = value;
|
|
PostWrite();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
if (!_NumberOfTriggersInitialized)
|
|
throw new DependencyNotInitializedException("NumberOfTriggers must be initialized before this property value can be set");
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".OriginalOffsetADC property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private bool _OriginalOffsetADCInitialized = false;
|
|
|
|
/// <summary>
|
|
/// The WindowAverageADC is the average ADC over the Zeroing Window specified for the channel
|
|
/// handles writing/reading field from the binary channel header
|
|
/// </summary>
|
|
public int WindowAverageADC
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!_WindowAverageADCInitialized)
|
|
{
|
|
OpenAndRead(Field.WindowAverageADC, out int temp, ref _WindowAverageADCInitialized);
|
|
channelHeader.WindowAverageADC = temp;
|
|
}
|
|
return channelHeader.WindowAverageADC;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".WindowAverageADC property value", ex);
|
|
}
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
OpenAndWrite(Field.WindowAverageADC, BitConverter.GetBytes(value), ref _WindowAverageADCInitialized);
|
|
channelHeader.WindowAverageADC = value;
|
|
PostWrite();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
if (!_NumberOfTriggersInitialized)
|
|
throw new DependencyNotInitializedException("NumberOfTriggers must be initialized before this property value can be set");
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".WindowAverageADC property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private bool _WindowAverageADCInitialized = false;
|
|
|
|
public int ZeroMvInADC
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!_ZeroMvInADCInitialized)
|
|
{
|
|
OpenAndRead(Field.ZeroMvInADC, out int temp, ref _ZeroMvInADCInitialized);
|
|
channelHeader.ZeroMvInADC = temp;
|
|
}
|
|
return channelHeader.ZeroMvInADC;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".ZeroMvInADC property value", ex);
|
|
}
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
OpenAndWrite(Field.ZeroMvInADC, BitConverter.GetBytes(value), ref _ZeroMvInADCInitialized);
|
|
channelHeader.ZeroMvInADC = value;
|
|
PostWrite();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
if (!_NumberOfTriggersInitialized)
|
|
throw new DependencyNotInitializedException("NumberOfTriggers must be initialized before this property value can be set");
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".ZeroMvInADC property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private bool _ZeroMvInADCInitialized = false;
|
|
|
|
/// <summary>
|
|
/// Get/set pre test noise percentage of full scale.
|
|
/// </summary>
|
|
public double PreTestNoisePercentageOfFullScale
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!_PreTestNoisePercentageOfFullScaleInitialized)
|
|
{
|
|
OpenAndRead(Field.PreTestNoisePercentageOfFullScale, out double temp, ref _PreTestNoisePercentageOfFullScaleInitialized);
|
|
channelHeader.PreTestNoisePercentageOfFullScale = temp;
|
|
}
|
|
return channelHeader.PreTestNoisePercentageOfFullScale;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".PreTestNoisePercentageOfFullScale property value", ex);
|
|
}
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
if (!_NumberOfTriggersInitialized)
|
|
throw new DependencyNotInitializedException("NumberOfTriggers must be initialized before this property value can be set");
|
|
OpenAndWrite(Field.PreTestNoisePercentageOfFullScale, BitConverter.GetBytes(value), ref _PreTestNoisePercentageOfFullScaleInitialized);
|
|
channelHeader.PreTestNoisePercentageOfFullScale = value;
|
|
PostWrite();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".PreTestNoisePercentageOfFullScale property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private bool _PreTestNoisePercentageOfFullScaleInitialized = false;
|
|
|
|
|
|
/// <summary>
|
|
/// Get/set pre test noise percentage of full scale.
|
|
/// </summary>
|
|
public double Excitation
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!_ExcitationInitialized)
|
|
{
|
|
OpenAndRead(Field.Excitation, out double temp, ref _ExcitationInitialized);
|
|
channelHeader.Excitation = temp;
|
|
}
|
|
return channelHeader.Excitation;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".Excitation property value", ex);
|
|
}
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
if (!_NumberOfTriggersInitialized)
|
|
throw new DependencyNotInitializedException("NumberOfTriggers must be initialized before this property value can be set");
|
|
OpenAndWrite(Field.Excitation, BitConverter.GetBytes(value), ref _ExcitationInitialized);
|
|
channelHeader.Excitation = value;
|
|
PostWrite();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".Excitation property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private bool _ExcitationInitialized = false;
|
|
/// <summary>
|
|
/// Get/set post test zero level counts.
|
|
/// </summary>
|
|
public int PostTestZeroLevelCounts
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!_PostTestZeroLevelCountsInitialized)
|
|
{
|
|
OpenAndRead(Field.PostTestZeroLevelCounts, out int temp, ref _PostTestZeroLevelCountsInitialized);
|
|
channelHeader.PostTestZeroLevelCounts = temp;
|
|
}
|
|
return channelHeader.PostTestZeroLevelCounts;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".PostTestZeroLevelCounts property value", ex);
|
|
}
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
if (!_NumberOfTriggersInitialized)
|
|
throw new DependencyNotInitializedException("NumberOfTriggers must be initialized before this property value can be set");
|
|
OpenAndWrite(Field.PostTestZeroLevelCounts, BitConverter.GetBytes(value), ref _PostTestZeroLevelCountsInitialized);
|
|
channelHeader.PostTestZeroLevelCounts = value;
|
|
PostWrite();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".PostTestZeroLevelCounts property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private bool _PostTestZeroLevelCountsInitialized = false;
|
|
|
|
/// <summary>
|
|
/// Get/set post test diagnostics level counts.
|
|
/// </summary>
|
|
public int PostTestDiagnosticsLevelCounts
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!_PostTestDiagnosticsLevelCountsInitialized)
|
|
{
|
|
OpenAndRead(Field.PostTestDiagnosticsLevelCounts, out int temp, ref _PostTestDiagnosticsLevelCountsInitialized);
|
|
channelHeader.PostTestDiagnosticsLevelCounts = temp;
|
|
}
|
|
return channelHeader.PostTestDiagnosticsLevelCounts;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".PostTestCalLevelCounts property value", ex);
|
|
}
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
if (!_NumberOfTriggersInitialized)
|
|
throw new DependencyNotInitializedException("NumberOfTriggers must be initialized before this property value can be set");
|
|
OpenAndWrite(Field.PostTestDiagnosticsLevelCounts, BitConverter.GetBytes(value), ref _PostTestDiagnosticsLevelCountsInitialized);
|
|
channelHeader.PostTestDiagnosticsLevelCounts = value;
|
|
PostWrite();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".PostTestCalLevelCounts property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private bool _PostTestDiagnosticsLevelCountsInitialized = false;
|
|
|
|
/// <summary>
|
|
/// Get/set data zero level counts.
|
|
/// </summary>
|
|
public int DataZeroLevelCounts
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!_DataZeroLevelCountsInitialized)
|
|
{
|
|
OpenAndRead(Field.DataZeroLevelCounts, out int temp, ref _DataZeroLevelCountsInitialized);
|
|
channelHeader.DataZeroLevelCounts = temp;
|
|
}
|
|
return channelHeader.DataZeroLevelCounts;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".DataZeroLevelCounts property value", ex);
|
|
}
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
if (!_NumberOfTriggersInitialized)
|
|
throw new DependencyNotInitializedException("NumberOfTriggers must be initialized before this property value can be set");
|
|
OpenAndWrite(Field.DataZeroLevelCounts, BitConverter.GetBytes(value), ref _DataZeroLevelCountsInitialized);
|
|
channelHeader.DataZeroLevelCounts = value;
|
|
PostWrite();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".DataZeroLevelCounts property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private bool _DataZeroLevelCountsInitialized = false;
|
|
|
|
/// <summary>
|
|
/// Get/set scale factor MV.
|
|
/// </summary>
|
|
public double ScaleFactorMv
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!_ScaleFactorMvInitialized)
|
|
{
|
|
OpenAndRead(Field.ScaleFactorMv, out double temp, ref _ScaleFactorMvInitialized);
|
|
channelHeader.ScaleFactorMv = temp;
|
|
}
|
|
return channelHeader.ScaleFactorMv;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".ScaleFactorMv property value", ex);
|
|
}
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
if (!_NumberOfTriggersInitialized)
|
|
throw new DependencyNotInitializedException("NumberOfTriggers must be initialized before this property value can be set");
|
|
OpenAndWrite(Field.ScaleFactorMv, BitConverter.GetBytes(value), ref _ScaleFactorMvInitialized);
|
|
channelHeader.ScaleFactorMv = value;
|
|
PostWrite();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".ScaleFactorMv property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private bool _ScaleFactorMvInitialized = false;
|
|
|
|
/// <summary>
|
|
/// Get/set sensitivity MV/EU
|
|
/// </summary>
|
|
public double MvPerEu
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!_MvPerEuInitialized)
|
|
{
|
|
OpenAndRead(Field.MvPerEu, out double temp, ref _MvPerEuInitialized);
|
|
channelHeader.MvPerEu = temp;
|
|
}
|
|
return channelHeader.MvPerEu;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".MvPerEu property value", ex);
|
|
}
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
if (!_NumberOfTriggersInitialized)
|
|
throw new DependencyNotInitializedException("NumberOfTriggers must be initialized before this property value can be set");
|
|
OpenAndWrite(Field.MvPerEu, BitConverter.GetBytes(value), ref _MvPerEuInitialized);
|
|
channelHeader.MvPerEu = value;
|
|
PostWrite();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".MvPerEu property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private bool _MvPerEuInitialized = false;
|
|
|
|
/// <summary>
|
|
/// Get/set EU field length (with terminator).
|
|
/// </summary>
|
|
public ushort EuFieldLengthWithTerminator
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!_EuFieldLengthWithTerminatorInitialized)
|
|
{
|
|
OpenAndRead(Field.EuFieldLengthWithTerminator, out ushort temp, ref _EuFieldLengthWithTerminatorInitialized);
|
|
channelHeader.EuFieldLengthWithTerminator = temp;
|
|
}
|
|
return channelHeader.EuFieldLengthWithTerminator;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".EuFieldLengthWithTerminator property value", ex);
|
|
}
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
if (!_NumberOfTriggersInitialized)
|
|
throw new DependencyNotInitializedException("NumberOfTriggers must be initialized before this property value can be set");
|
|
OpenAndWrite(Field.EuFieldLengthWithTerminator, BitConverter.GetBytes(value), ref _EuFieldLengthWithTerminatorInitialized);
|
|
channelHeader.EuFieldLengthWithTerminator = value;
|
|
PostWrite();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".EuFieldLengthWithTerminator property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private bool _EuFieldLengthWithTerminatorInitialized = false;
|
|
|
|
/// <summary>
|
|
/// Get/set engineering units.
|
|
/// </summary>
|
|
public char[] EngineeringUnit
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!_EngineeringUnitInitialized)
|
|
{
|
|
lock (LockObject)
|
|
{
|
|
// Go initialize our cached version from disk.
|
|
//
|
|
var numberOfEuChars = EuFieldLengthWithTerminator - 1;
|
|
channelHeader.EngineeringUnit = new char[numberOfEuChars];
|
|
long valueOffset = GetFileOffsetOf(Field.EngineeringUnit);
|
|
using (var fileReader = new BinaryReader(new FileStream(Filename, FileMode.Open)))
|
|
{
|
|
var prevPosition = fileReader.BaseStream.Position;
|
|
fileReader.BaseStream.Position = valueOffset;
|
|
|
|
for (var i = 0; i < numberOfEuChars; i++)
|
|
channelHeader.EngineeringUnit[i] = fileReader.ReadChar();
|
|
channelHeader.EngineeringUnit = channelHeader.EngineeringUnit;
|
|
|
|
fileReader.BaseStream.Position = prevPosition;
|
|
_EngineeringUnitInitialized = true;
|
|
}
|
|
}
|
|
}
|
|
return channelHeader.EngineeringUnit;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".EngineeringUnit property value", ex);
|
|
}
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
if (!_NumberOfTriggersInitialized)
|
|
throw new DependencyNotInitializedException("NumberOfTriggers must be initialized before this property value can be set");
|
|
|
|
var bytes = new List<byte>();
|
|
foreach (var c in value) { bytes.Add((byte)c); }
|
|
OpenAndWrite(Field.EngineeringUnit, bytes.ToArray(), ref _EngineeringUnitInitialized);
|
|
channelHeader.EngineeringUnit = 1 == value.Length % 2 ?
|
|
new string(value).PadRight(value.Length + 1, ' ').ToCharArray() : value;
|
|
PostWrite();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".EngineeringUnit property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private bool _EngineeringUnitInitialized = false;
|
|
|
|
/// <summary>
|
|
/// Get/set ISO code.
|
|
/// </summary>
|
|
public char[] IsoCode
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized && !IsInitializedFromFile)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
if (!_IsoCodeInitialized)
|
|
{
|
|
lock (LockObject)
|
|
{
|
|
// Go initialize our cached version from disk.
|
|
//
|
|
channelHeader.IsoCode = new char[16];
|
|
long valueOffset = GetFileOffsetOf(Field.IsoCode);
|
|
using (var fileReader = new BinaryReader(new FileStream(Filename, FileMode.Open)))
|
|
{
|
|
var prevPosition = fileReader.BaseStream.Position;
|
|
fileReader.BaseStream.Position = valueOffset;
|
|
|
|
for (var i = 0; i < channelHeader.IsoCode.Length; i++)
|
|
channelHeader.IsoCode[i] = fileReader.ReadChar();
|
|
channelHeader.IsoCode = channelHeader.IsoCode;
|
|
|
|
fileReader.BaseStream.Position = prevPosition;
|
|
_IsoCodeInitialized = true;
|
|
}
|
|
}
|
|
}
|
|
return channelHeader.IsoCode;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".IsoCode property value", ex);
|
|
}
|
|
}
|
|
set
|
|
{
|
|
try
|
|
{
|
|
if (!_NumberOfTriggersInitialized)
|
|
throw new DependencyNotInitializedException("NumberOfTriggers must be initialized before this property value can be set");
|
|
var bytes = new List<byte>();
|
|
foreach (var c in value) { bytes.Add((byte)c); }
|
|
OpenAndWrite(Field.IsoCode, bytes.ToArray(), ref _IsoCodeInitialized);
|
|
channelHeader.IsoCode = value;
|
|
PostWrite();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".IsoCode property value", ex);
|
|
}
|
|
}
|
|
}
|
|
private bool _IsoCodeInitialized = false;
|
|
|
|
/// <summary>
|
|
/// Get the CRC for the current state of the header.
|
|
/// </summary>
|
|
public uint Crc32
|
|
{
|
|
get
|
|
{
|
|
try
|
|
{
|
|
if (!IsInitialized)
|
|
throw new NotInitializedException("object initialization has not completed");
|
|
return channelHeader.Crc32;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem getting " + GetType().FullName + ".Crc32 property value", ex);
|
|
}
|
|
}
|
|
private set
|
|
{
|
|
try
|
|
{
|
|
var dummyVar = false;
|
|
OpenAndWrite(Field.Crc32, BitConverter.GetBytes(value), ref dummyVar);
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem setting " + GetType().FullName + ".Crc32 property", ex);
|
|
}
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// opens Filename and writes the value to the indicated offset
|
|
/// </summary>
|
|
/// <param name="offset"></param>
|
|
/// <param name="value"></param>
|
|
private void OpenAndWrite(Field field, byte[] value, ref bool initField)
|
|
{
|
|
var offset = GetFileOffsetOf(field);
|
|
//use this mutex to lock reading and writing to the file across processes
|
|
//I just take the failename here as it was causing exceptions early here when it
|
|
//the mutex here can throw a filename if we use the full file path to the file
|
|
//for our purposes a filename is more than sufficient, we don't need to fail the mutex
|
|
//because the path would be too long
|
|
var fileName = (new FileInfo(Filename)).Name;
|
|
using (var mutex = new Mutex(false,
|
|
fileName.Replace(Path.DirectorySeparatorChar, '_')))
|
|
{
|
|
var attempt = 0;
|
|
var failed = false;
|
|
while (attempt < 30 && !failed)
|
|
{
|
|
attempt++;
|
|
//first wait upto 50 ms to see if we can get access to the file
|
|
while (!mutex.WaitOne(50, false)) { Thread.Sleep(50); }
|
|
try
|
|
{
|
|
lock (LockObject)
|
|
{
|
|
{
|
|
|
|
using (var fileWriter = new BinaryWriter(new FileStream(Filename, FileMode.Open)))
|
|
{
|
|
fileWriter.Seek(offset, SeekOrigin.Begin);
|
|
fileWriter.Write(value);
|
|
return;//we are done, don't continue with writes!
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (FileNotFoundException ex)
|
|
{
|
|
//well this maybe is fatal, no point in spinning anymore
|
|
failed = true;
|
|
APILogger.Log($"File {Filename} not found");
|
|
}
|
|
catch (IOException ex)
|
|
{
|
|
APILogger.Log($"Could not write file {Filename} count {attempt}", ex);
|
|
ReportFileInUse();
|
|
//assume this is not fatal, spin and try again
|
|
Thread.Sleep(100);
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
//well this might be fatal too, but lets give it a chance to retry I guess?
|
|
APILogger.Log($"Failed to write {Filename} count {attempt}", ex);
|
|
}
|
|
}
|
|
}
|
|
initField = true;
|
|
}
|
|
/// <summary>
|
|
/// logs if FileName is use by any processes
|
|
/// </summary>
|
|
private void ReportFileInUse()
|
|
{
|
|
try
|
|
{
|
|
var inUse = Common.Utils.FileUtils.WhoIsLocking(Filename);
|
|
if (null == inUse || !inUse.Any())
|
|
{
|
|
APILogger.Log($"file {Filename} not reported as in use by windows ...");
|
|
return;
|
|
}
|
|
var list = new List<string>();
|
|
foreach (var process in inUse)
|
|
{
|
|
list.Add($"[{process.Id}] - {process.ProcessName}");
|
|
}
|
|
APILogger.Log($"file {Filename} reported as in use by: {string.Join(", ", list.ToArray())}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
APILogger.Log(ex);
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Populate the persistent CRC property with the CRC of the current state of the channel header.
|
|
/// </summary>
|
|
public void StampCrc()
|
|
{
|
|
try
|
|
{ //
|
|
// Calling the "get" accessor for all properties will ensure that they
|
|
// are populated, which is important if the CRC is going to be running
|
|
// over them.
|
|
//
|
|
var header = new BinaryChannelHeader();
|
|
|
|
header.MagicKey = MagicKey;
|
|
header.HeaderVersionNumber = HeaderVersionNumber;
|
|
header.OffsetOfSampleDataStart = OffsetOfSampleDataStart;
|
|
header.NumberOfSamples = NumberOfSamples;
|
|
header.NumberOfBitsPerSample = NumberOfBitsPerSample;
|
|
header.AreSamplesSigned = AreSamplesSigned;
|
|
header.SampleRate = SampleRate;
|
|
header.NumberOfTriggers = NumberOfTriggers;
|
|
|
|
if (header.NumberOfTriggers > 0)
|
|
header.TriggerSampleNumbers = TriggerSampleNumbers;
|
|
|
|
header.PreTestZeroLevelCounts = PreTestZeroLevelCounts;
|
|
//header.PreTestZeroLevelMv = this.PreTestZeroLevelMv;
|
|
header.RemovedADC = RemovedADC;
|
|
header.Excitation = Excitation;
|
|
header.ZeroMvInADC = ZeroMvInADC;
|
|
header.WindowAverageADC = WindowAverageADC;
|
|
header.OriginalOffsetADC = OriginalOffsetADC;
|
|
header.TriggerAdjustmentSamples = TriggerAdjustmentSamples;
|
|
|
|
header.PreTestDiagnosticsLevelCounts = PreTestDiagnosticsLevelCounts;
|
|
header.PreTestNoisePercentageOfFullScale = PreTestNoisePercentageOfFullScale;
|
|
header.PostTestZeroLevelCounts = PostTestZeroLevelCounts;
|
|
header.PostTestDiagnosticsLevelCounts = PostTestDiagnosticsLevelCounts;
|
|
header.ScaleFactorMv = ScaleFactorMv;
|
|
header.MvPerEu = MvPerEu;
|
|
header.EuFieldLengthWithTerminator = EuFieldLengthWithTerminator;
|
|
|
|
var lchars = new List<char>(EngineeringUnit);
|
|
var index = lchars.IndexOf('\0');
|
|
if (index > 0)
|
|
{
|
|
lchars = new List<char>(lchars.Take(index));
|
|
}
|
|
header.EngineeringUnit = lchars.ToArray();
|
|
header.IsoCode = IsoCode;
|
|
header.DataZeroLevelCounts = DataZeroLevelCounts;
|
|
//this.Crc32 = channelHeader.Crc32;
|
|
Crc32 = header.Crc32;
|
|
}
|
|
|
|
catch (System.Exception ex)
|
|
{
|
|
throw new Exception("encountered problem stamping persistent channel CRC", ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Dispose of this object's Disposables.
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
lock (LockObject)
|
|
{
|
|
try
|
|
{
|
|
if (_ViewArray.IsInitialized)
|
|
{
|
|
IsMemoryMapped = false; //Is this right?
|
|
}
|
|
try
|
|
{
|
|
ViewArray?.Dispose();
|
|
}
|
|
catch (System.Exception)
|
|
{
|
|
}
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
// If the ViewArray was not yet initialized, that's OK. Eat the exception
|
|
if (null == ex.InnerException || ex.InnerException.GetType() != typeof(Property<FileMapViewArray>.NotInitializedException))
|
|
{
|
|
throw new Exception("encountered problem disposing of " + GetType().FullName, ex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|