Files
DP44/Common/DTS.Common.Serialization/SliceRaw/SliceRaw.File.PersistentChannel.cs
2026-04-17 14:55:32 -04:00

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);
}
}
}
}
}
}
}