/* * 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 { /// /// Representation of a channel. Changes made to this representation /// are immediately manifest in the associated channel file. /// public partial class PersistentChannel : ExceptionalList, ILargeDataAware, IChannelHeader, IDisposable { /// /// returns an iso code given a filepath to a SLICE .chn file /// does not have exception handling /// /// filepath to SLICE .chn file /// isocode public static string GetIsoCode(string binaryFileName) { var pch = new PersistentChannel(binaryFileName, null, false); return new string(pch.IsoCode); } /// /// The window into this persistent channel's soul. /// protected FileMapViewArray ViewArray { get => _ViewArray.Value; set => _ViewArray.Value = value; } private readonly Property _ViewArray = new Property( typeof(PersistentChannel).Namespace + ".File.PersistentChannel.ViewArray", null, false ); /// /// Get value indicating whether or not this class has been /// preinitialized from an existing serialization. /// protected bool IsInitializedFromFile { get => _IsInitializedFromFile.Value; private set => _IsInitializedFromFile.Value = value; } private readonly Property _IsInitializedFromFile = new Property( typeof(PersistentChannel).Namespace + ".File.PersistentChannel.IsInitializedFromFile", false, false ); private readonly Property _minADC = new Property( typeof(PersistentChannel).Namespace + ".File.PersistentChannel.MinADC", short.MinValue, false); public short MinADC { get { if (!_minADC.IsInitialized) { ComputeMinMaxADC(); } return _minADC.Value; } } private readonly Property _maxADC = new Property( 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; } /// /// Initialize an instance of this class. /// /// /// /// The filename of the associated channel data file. /// /// /// /// The channel header to be written into the associated channel data file. /// /// 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().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); } } /// /// gets the size of fields as of version 4 /// (includes the sizes of all fields prior to V4) /// /// /// 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; } /// /// 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 /// /// /// private int GetFileOffsetOfV4(Field field) { var previousField = (Field)((int)field - 1); var offset = GetFileOffsetOf(previousField) + GetSizeOfV4(previousField); return offset; } /// /// 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 /// /// /// 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); } } /// /// Get value indicating whether or not all necessary properties of this /// class have been initialized to make this object useable. /// 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); } } } /// /// Determine whether or not this data set is small enough to be safely fit into any array /// i.e., is int-indexable. /// public bool IsDataArraySized => NumberOfSamples < int.MaxValue; /// /// Get/set the number of trigger samples. /// protected int NumberOfTriggerSamples { get; set; } /// /// size of view into memory-mapped file. /// protected int ViewSize { get; set; } private bool _IsViewSizeInitialized = false; /// /// to indicate whether or not a memory mapping has already been /// established for this object. /// public bool IsMemoryMapped { get; set; } /// /// Get the length of this persistent channel's data. /// 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); } } } /// /// Get the data count of this persistent channel's data. /// 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); } } } /// /// Override long-indexing here. Return a sample of the underlying array. /// /// /// /// The index of the sample being affected. /// /// /// /// The sample at the specified index. /// /// 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); } } } /// /// Some older files will need this format to be invoked to fix slight formatting problems. /// 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); } } /// /// Get/set the filename of the associated channel data file. /// public string Filename { get => _Filename.Value; private set => _Filename.Value = value; } private readonly Property _Filename = new Property( 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(); 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); } } /// /// Get/set data array value. /// 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 _Data = new Property( typeof(PersistentChannel).Namespace + ".File.PreAllocatedChannel.Data", null, false ); /// /// Get/set binary writer to be used by data append session. /// private BinaryWriter AppendSessionWriter { get; set; } /// /// Prepare this object for repeated calls to AppendSessionData. /// 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); } } /// /// Secure object from repeated calls to AppendSessionData. /// public void EndAppendSession() { try { AppendSessionWriter.Close(); AppendSessionWriter.Dispose(); AppendSessionWriter = null; } catch (System.Exception ex) { throw new Exception("encountered problem ending data append session", ex); } } /// /// Append data to this persistent channel. Must be bracketed by BeginAppendSession and /// EndAppendSession method invocations. /// /// /// /// The data to be appended to this persisitent channel. /// /// 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); } } /// /// 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. /// /// /// /// The data to be appended to this persistent channel. /// /// 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); } } /// /// Object to cache header property accesses. /// public BinaryChannelHeader channelHeader = new BinaryChannelHeader(); /// /// opens the binary file, reads header field value from file /// /// /// /// 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; } /// /// opens the binary file, reads header field value from file /// /// /// /// 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; } /// /// opens the binary file, reads header field value from file /// /// /// /// 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; } /// /// opens the binary file, reads header field value from file /// /// /// /// 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; } /// /// opens the binary file, reads header field value from file /// /// /// /// 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; } /// /// after anything is written to the header, we re-sync the CRC (apparently) /// 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; } /// /// "Magic Key" relatively-UID for our binary file type. /// 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; /// /// header version number for our binary file type. /// 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; /// /// Get/set offset of sample data start value. /// 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; /// /// Get/set number of samples. /// 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; /// /// Get/set number of bits per sample. /// 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; /// /// Get/set signed sample indicator. /// 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; /// /// Get/set sample rate value. /// 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; /// /// Get/set number of triggers. /// 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; /// /// Get/set the trigger sample numbers /// 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(); 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; /// /// Get/set pre test zero level counts. /// 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; /// /// Get/set pre test zero level counts. /// 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; /// /// Get/set pre test diagnostics level counts. /// 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; /// /// The WindowAverageADC is the average ADC over the Zeroing Window specified for the channel /// handles writing/reading field from the binary channel header /// 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; /// /// Get/set pre test noise percentage of full scale. /// 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; /// /// Get/set pre test noise percentage of full scale. /// 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; /// /// Get/set post test zero level counts. /// 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; /// /// Get/set post test diagnostics level counts. /// 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; /// /// Get/set data zero level counts. /// 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; /// /// Get/set scale factor MV. /// 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; /// /// Get/set sensitivity MV/EU /// 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; /// /// Get/set EU field length (with terminator). /// 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; /// /// Get/set engineering units. /// 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(); 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; /// /// Get/set ISO code. /// 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(); 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; /// /// Get the CRC for the current state of the header. /// 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); } } } /// /// opens Filename and writes the value to the indicated offset /// /// /// 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; } /// /// logs if FileName is use by any processes /// 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(); 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); } } /// /// Populate the persistent CRC property with the CRC of the current state of the channel header. /// 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(EngineeringUnit); var index = lchars.IndexOf('\0'); if (index > 0) { lchars = new List(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); } } /// /// Dispose of this object's Disposables. /// 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.NotInitializedException)) { throw new Exception("encountered problem disposing of " + GetType().FullName, ex); } } } } } } }