This commit is contained in:
2026-04-17 14:55:32 -04:00
commit bc3ac1d4c9
18017 changed files with 4371742 additions and 0 deletions

View File

@@ -0,0 +1,613 @@
/*
* Chapter10.File.Writer.cs
*
* Copyright © 2020
* Diversified Technical Systems, Inc.
* All Rights Reserved
*/
using System;
using System.Collections.Generic;
using System.Drawing.Text;
using System.IO;
using System.Linq;
using System.Text;
using DTS.Common;
using DTS.Common.DAS.Concepts;
using DTS.Common.Enums;
using DTS.Common.Utilities.Logging;
using DTS.Common.Utils;
using static DTS.Serialization.Test.Module;
// ReSharper disable PossiblyMistakenUseOfParamsMethod
namespace DTS.Serialization.IRIGCH10
{
public partial class File
{
/// <summary>
/// implementation of the Serialization.File.Writer class for Chapter10
/// </summary>
public class Writer : Writer<File>, IWriter<Test>
{
#region properties
/// <summary>
/// the owning file that controls this writer
/// </summary>
internal File WriterParent { get; }
public double Start { get; set; }
public double Stop { get; set; }
public bool Filtered { get; set; }
#endregion
#region methods
/// <summary>
/// writes out test to given path
/// </summary>
/// <param name="pathname"></param>
/// <param name="id"></param>
/// <param name="test"></param>
/// <param name="bFiltering"></param>
/// <param name="includeGroupNameInISOExport"></param>
/// <param name="dataCollectionLength"></param>
/// <param name="minStartTime"></param>
public void Write(string pathname, string id, Test test, bool bFiltering, bool includeGroupNameInISOExport, double minStartTime, int dataCollectionLength)
{
throw new NotImplementedException();
}
/// <summary>
/// updates the progress if possible
/// </summary>
/// <param name="dValue"></param>
/// <param name="tickEventHandler"></param>
private void UpdateProgress(double dValue, TickEventHandler tickEventHandler)
{
tickEventHandler?.Invoke(this, dValue);
}
/// <summary>
/// returns a name for the given channel
/// </summary>
/// <param name="channel"></param>
/// <returns></returns>
private static string GetChannelName(Channel channel)
{
if (!(channel is AnalogInputChannel aic))
{
return channel.ChannelName2;
}
switch (IsoViewModeStatic.ViewMode)
{
case IsoViewMode.ISOOnly:
return $"{aic.IsoChannelName}";
case IsoViewMode.ISOAndUserCode:
return $"{aic.IsoChannelName}\\{aic.UserChannelName}";
case IsoViewMode.UserCodeOnly:
return $"{aic.UserCode}";
case IsoViewMode.ChannelNameOnly:
return $"{aic.UserChannelName}";
}
return aic.HardwareChannelName;
}
/// <summary>
/// we don't really have an RTC, so we make up one
/// with an arbitrary value, this is actually a 6byte value,
/// we're just using a long for convenience
/// </summary>
public const long BASE_RTC = 141989612500056L;
private void GetTimeStamp(Test test, out int nanoseconds, out int seconds)
{
nanoseconds = 0;
seconds = 0;
var basemodules = test.Modules.GroupBy(module => module.BaseSerialNumber).Select(group => group.First());
var testModuleTimeStamps = GetModuleTimeStamps(basemodules, out var testModuleStartTimestamps);
Tuple<double, double> minUnixTime = null;
if (testModuleTimeStamps.Any())
{
minUnixTime = TestUtils.MinUnixTime(testModuleTimeStamps);
}
else if (testModuleStartTimestamps.Any())
{
minUnixTime = TestUtils.MinUnixTime(testModuleStartTimestamps);
}
if (null == minUnixTime)
{
var ticks = test.InceptionDate.ToUniversalTime().Ticks;
var s = Math.Truncate((decimal)ticks / TimeSpan.TicksPerSecond);
ticks -= Convert.ToInt64(s * TimeSpan.TicksPerSecond);
var n = Math.Truncate(ticks * Common.Constants.NANOS_PER_SECOND / TimeSpan.TicksPerSecond);
minUnixTime = new Tuple<double, double>(Convert.ToDouble(n), Convert.ToDouble(s));
}
var minStartTime = double.MinValue;
foreach (var module in test.Modules)
{
var dStartTime = (double)module.StartRecordSampleNumber / module.SampleRateHz;
if (module.TriggerSampleNumbers.Count > 0)
{
dStartTime -= (double)module.TriggerSampleNumbers[0] / module.SampleRateHz;
}
minStartTime = Math.Max(minStartTime, dStartTime);
}
var nanos = ((decimal)(minStartTime) * Common.Constants.NANOS_PER_SECOND) + (decimal)minUnixTime.Item2 + (decimal)minUnixTime.Item1 * Common.Constants.NANOS_PER_SECOND;
seconds = Convert.ToInt32(Math.Truncate(nanos / Common.Constants.NANOS_PER_SECOND));
nanos -= seconds * Common.Constants.NANOS_PER_SECOND;
nanoseconds = Convert.ToInt32(nanos);
}
/// <summary>
/// whether to include secondary time header or not
/// 33199 Add Time format 1 as an export option for CH 10 export
/// </summary>
public bool IncludeSecondaryHeader { get; set; } = true;
/// <summary>
/// Whether to use analog format or not during export
/// </summary>
public bool UseAnalogFormat { get; set; } = true;
/// <summary>
/// Whether to use PCM format or not during export
/// </summary>
public bool UsePCMFormat { get; set; } = false;
/// <summary>
/// returns a list of binary readers given a test that all point at the start of data
/// </summary>
/// <param name="test"></param>
/// <returns></returns>
private List<BinaryReader> GetBinaryReaders(Test test, IReadOnlyDictionary<Channel, ChannelInformation> lookup)
{
var binaryReaders = new List<BinaryReader>();
for (var i = 0; i < test.Channels.Count; i++)
{
var channel = test.Channels[i];
//get the filename and the start of data before we go and nuke the persistantchannelinfo object ...
var fileName = lookup[channel].ChannelFileName;
var offset = lookup[channel].OffsetDataStart;
//get the reader and advance it to the start of data
var br = new BinaryReader(new FileStream(fileName, FileMode.Open));
br.ReadBytes(Convert.ToInt32(offset));
lookup[channel].Reader = br;
binaryReaders.Add(br);
}
return binaryReaders;
}
/// <summary>
/// internal class for encapsulating some of the information from analoginputdaschannels without keeping any files open
/// </summary>
internal class ChannelInformation
{
public string ChannelFileName;
public ulong OffsetDataStart;
public ulong NumberOfSamples;
public short MinADC { get; set; } = short.MaxValue;
public short MaxADC { get; set; } = short.MinValue;
public BinaryReader Reader;
public ChannelInformation(AnalogInputChannel channel)
{
ChannelFileName = channel.PersistentChannelInfo.Filename;
OffsetDataStart = channel.PersistentChannelInfo.OffsetOfSampleDataStart;
NumberOfSamples = channel.PersistentChannelInfo.NumberOfSamples;
}
}
/// <summary>
/// retrieves channel summary information for channels in a test
/// </summary>
/// <param name="test"></param>
/// <returns></returns>
private IReadOnlyDictionary<Channel, ChannelInformation> GetChannelSummaries(Test test)
{
var lookup = new Dictionary<Channel, ChannelInformation>();
foreach (var channel in test.Channels)
{
if (channel is AnalogInputChannel aic)
{
lookup[channel] = new ChannelInformation(aic);
}
}
return lookup;
}
/// <summary>
/// closes and disposes of all readers in list, then clears the list
/// </summary>
/// <param name="readers"></param>
private void CloseAndDisposeReaders(List<BinaryReader> readers)
{
for (var i = 0; i < readers.Count; i++)
{
readers[i].Close();
readers[i].Dispose();
}
readers.Clear();
}
/// <summary>
/// go throughs all binary readers and determines the min and max ADC from files
/// </summary>
/// <param name="test"></param>
/// <param name="lookup"></param>
/// <param name="tickEventHandler"></param>
private void GetMinAndMax(Test test, IReadOnlyDictionary<Channel, ChannelInformation> lookup,
TickEventHandler tickEventHandler)
{
var index = 0;
foreach (var channel in test.Channels)
{
if (!lookup.ContainsKey(channel)) { return; }
var info = lookup[channel];
try
{
for (var i = 0UL; i < info.NumberOfSamples; i++)
{
var adc = ReadShort(info.Reader);
if (adc < info.MinADC) { info.MinADC = adc; }
else if (adc > info.MaxADC) { info.MaxADC = adc; }
}
index++;
tickEventHandler?.Invoke(this, (50D * index) / (double)test.Channels.Count);
}
catch (Exception ex)
{
APILogger.Log($"Failed to get Min/Max for file {info.ChannelFileName} - ", ex);
throw;
}
}
}
/// <summary>
/// Readers a single signed short from file
/// </summary>
/// <param name="reader"></param>
/// <returns></returns>
private short ReadShort(BinaryReader reader)
{
try
{
var bytes = reader.ReadBytes(2);
//to get the next sample from the file we just read two bytes and make a short out of it
//I'm not sure why to use this version rather than alternatives, but the existing code
//used this method, so I just preserved it here ...
return (short)((bytes[0] & 0xFFFF) + ((bytes[1] & 0xFFFF) << 8));
}
catch (Exception ex)
{
APILogger.Log($"Failed to read from file", ex);
throw;
}
}
/// <summary>
/// writes out test to given path
/// </summary>
/// <param name="pathname"></param>
/// <param name="id"></param>
/// <param name="dataFolder"></param>
/// <param name="test"></param>
/// <param name="bFiltering"></param>
/// <param name="includeGroupNameInISOExport"></param>
/// <param name="fd"></param>
/// <param name="tmChannel"></param>
/// <param name="channelNumber"></param>
/// <param name="beginEventHandler"></param>
/// <param name="cancelEventHandler"></param>
/// <param name="endEventHandler"></param>
/// <param name="tickEventHandler"></param>
/// <param name="errorEventHandler"></param>
/// <param name="cancelRequested"></param>
/// <param name="minStartTime"></param>
/// <param name="dataCollectionLength"></param>
public void Write(string pathname,
string id,
string dataFolder,
Test test,
bool bFiltering,
bool includeGroupNameInISOExport,
FilteredData fd,
Channel tmChannel,
int channelNumber,
BeginEventHandler beginEventHandler,
CancelEventHandler cancelEventHandler,
EndEventHandler endEventHandler,
TickEventHandler tickEventHandler,
ErrorEventHandler errorEventHandler,
CancelRequested cancelRequested,
double minStartTime,
int dataCollectionLength)
{
System.Exception caughtException = null;
try
{
if (!Directory.Exists(Path.GetDirectoryName(pathname)))
{
_ = Directory.CreateDirectory(Path.GetDirectoryName(pathname));
}
var summaryLookup = GetChannelSummaries(test);
var binaryReaders = GetBinaryReaders(test, summaryLookup);
GetMinAndMax(test, summaryLookup, tickEventHandler);
tickEventHandler?.Invoke(this, 50D);
CloseAndDisposeReaders(binaryReaders);
string tmatsDoc = UseAnalogFormat ? GetTMATSAnalog(test, summaryLookup) : GetTMATSPCM(test, summaryLookup);
GetTimeStamp(test, out var nanoseconds, out var seconds);
var channelsCount = test.Channels.Count;
binaryReaders = GetBinaryReaders(test, summaryLookup);
if (UseAnalogFormat)
{
Chapter10File.WriteFileAnalog(tmatsDoc,
(int chIdx) =>
{
return ReadShort(binaryReaders[chIdx]);
},
(int chIdx) => (long)summaryLookup[test.Channels[chIdx]].NumberOfSamples,
channelsCount, nanoseconds, seconds, test.Modules[0].SampleRateHz,
IncludeSecondaryHeader, pathname, tickEventHandler, this);
}
else
{
Chapter10File.WriteFilePCM(tmatsDoc,
(int chIdx) => { return ReadShort(binaryReaders[chIdx]); },
(int chIdx) => (long)summaryLookup[test.Channels[chIdx]].NumberOfSamples,
channelsCount, nanoseconds, seconds, test.Modules[0].SampleRateHz,
IncludeSecondaryHeader, pathname, tickEventHandler, this);
}
CloseAndDisposeReaders(binaryReaders);
tickEventHandler?.Invoke(this, 100D);
}
catch (IOException ioException)
{
if (ioException.HResult == -2147024816)
{
var msg = $"failed to create file, check that file isn't currently open: {pathname}";
APILogger.Log(msg, ioException);
caughtException = new IOException(msg, ioException);
}
else
{
APILogger.Log("encountered problem writing Ch10 file", pathname, ioException);
caughtException = ioException;
}
}
catch (System.Exception ex)
{
APILogger.Log("encountered problem writing Ch10 file", pathname, ex);
caughtException = ex;
}
finally
{
tickEventHandler?.Invoke(this, 100D);
endEventHandler?.Invoke(this);
if (null != caughtException)
{
errorEventHandler?.Invoke(this, caughtException);
}
}
}
private const int DATA_CHANNEL_ID = 3;
/// <summary>
/// returns a string with multiple lines for a single channel for a PCM TMATS file
/// </summary>
private static string GetTMATSChannelPCM(Test test, IReadOnlyDictionary<Channel, ChannelInformation> summaryLookup)
{
var sb = new StringBuilder();
var baseText = System.IO.File.ReadAllText(@"TMTTemplates\S6ATMTTemplate_PCM_ExportChannel.tmt");
for ( var i = 0; i< test.Channels.Count; i++)
{
sb.AppendLine(GetTMATSChannelPCM(test, summaryLookup, i, baseText));
}
return sb.ToString();
}
/// <summary>
/// returns a string with multiple lines for a single channel for a PCM TMATS file
/// <summary>
private static string GetTMATSChannelPCM(Test test, IReadOnlyDictionary<Channel, ChannelInformation> summaryLookup, int channelIdx,
string unmodifiedText)
{
//we'll return the basetext, but just make it clear the original string isn't modified, strings are immutable and we
//are creating new strings and we're holding onto the old string
var baseText = unmodifiedText;
var channel = test.Channels[channelIdx];
try
{
var summary = summaryLookup[channel];
if (channel is AnalogInputChannel aic)
{
var ds = SliceRaw.File.Reader.GetDataScaler(aic);
var eu = aic.EngineeringUnits?.Trim() ?? "";
var scaler = ds.GetAdcToEuScalingFactor();
//we want 0 * ds = ds.GetEU(0), but the way to do that is to add in ds.GetEU(0)
//so that's the offset to add back in ... I'm not sure why analog is using a different formula
var offset = ds.GetEU(0);
var minEU = ds.GetEU(summary.MinADC);
var maxEU = ds.GetEU(summary.MaxADC);
var channelName = GetChannelName(channel);
baseText = baseText.Replace("{CHANNEL NUMBER}", $"{1 + channelIdx}");
baseText = baseText.Replace("{CHANNEL NAME}", channelName);
baseText = baseText.Replace("{CHANNEL OFFSET EU}", $"{offset:0.0000}");
baseText = baseText.Replace("{CHANNEL SCALEFACTOR EU}", $"{scaler:0.0000}");
baseText = baseText.Replace("{CHANNEL EU}", PrepareOutput(eu));
baseText = baseText.Replace("{CHANNEL MAX RANGE EU}", $"{maxEU:0.0000}");
baseText = baseText.Replace("{CHANNEL MIN RANGE EU}", $"{minEU:0.0000}");
}
}
catch (Exception ex)
{
APILogger.Log($"Failed to get TMATS for channel: {channelIdx} - {channel.ChannelName2}", ex);
}
return baseText;
}
private static string GetTMATSChannelsAnalog(Test test, IReadOnlyDictionary<Channel, ChannelInformation> summaryLookup)
{
var sb = new StringBuilder();
sb.AppendLine(@"R-1\COM:--------------------- Start of channels ---------------------;");
for (var i = 0; i < test.Channels.Count; i++)
{
var channel = test.Channels[i];
try
{
var summary = summaryLookup[channel];
var ds = new DataScaler();
var eu = "EU";
if (channel is AnalogInputChannel aic)
{
ds = SliceRaw.File.Reader.GetDataScaler(aic);
eu = aic.EngineeringUnits?.Trim() ?? "";
}
var channelName = PrepareOutput(GetChannelName(channel));
sb.Append(GetTMATSChannel(channelName, summary.MinADC, summary.MaxADC, 1 + i, ds, eu));
sb.AppendLine();
}
catch (Exception ex)
{
APILogger.Log($"Failed to get TMATS for channel: {i} - {channel.ChannelName2}", ex);
}
}
sb.AppendLine(@"R-1\COM:--------------------- End of channels ---------------------;");
sb.AppendLine(@"R-1\COM:--------------------- End of TMATS ---------------------;");
return sb.ToString();
}
private static string GetTMATSChannel(string channelName, short ADCMin, short ADCMax, int channelNumber, DataScaler ds,
string eu)
{
var baseText = System.IO.File.ReadAllText(@"TMTTemplates\S6ATMTTemplate_ANALOG_ExportChannel.tmt");
baseText = baseText.Replace("{CHANNEL_NUMBER}", $"{channelNumber}");
baseText = baseText.Replace("{CHANNEL_NAME}", channelName);
var scaler = ds.GetAdcToEuScalingFactor();
var adcToEU = ds.GetAdcToEuScalingFactor();
var midPointRemoval = adcToEU * Constants.ADC_MIDPOINT;
//the scaler is already aware of datazerolevelADC, so just get 0 and datazerolevel is applied, as is
//initial eu or user offset
var offset = ds.GetEU(0) - midPointRemoval;
var minEU = ds.GetEU(ADCMin);
var maxEU = ds.GetEU(ADCMax);
baseText = baseText.Replace("{CHANNEL_OFFSETEU}", $"{offset}");
baseText = baseText.Replace("{CHANNEL_SCALEFACTOREU}", $"{scaler}");
baseText = baseText.Replace("{CHANNEL_EU}", PrepareOutput(eu));
baseText = baseText.Replace("{CHANNEL_MAXRANGEEU}", $"{maxEU}");
baseText = baseText.Replace("{CHANNEL_MINRANGEEU}", $"{minEU}");
return baseText;
}
private static string PrepareOutput(string s)
{
if (string.IsNullOrEmpty(s)) { return string.Empty; }
return s.Replace(' ', '_');
}
/// <summary>
/// returns the entire TMATS document as a string for a PCM export
/// <summary>
private static string GetTMATSPCM(Test test, IReadOnlyDictionary<Channel, ChannelInformation> summaryLookup)
{
var sb = new StringBuilder();
var baseText = System.IO.File.ReadAllText(@"TMTTemplates\S6ATMTTemplate_PCM_ExportBase.tmt");
baseText = baseText.Replace("{NAME OF PROGRAM}", "DataPRO");
baseText = baseText.Replace("{TEST ID}", test.Modules[0].SerialNumber);
baseText = baseText.Replace("{STREAM TIME FORMAT}", "2");
baseText = baseText.Replace("{DAS SERIAL NUMBER}", test.Modules[0].SerialNumber);
int channelCount = test.Modules.Sum(m => m.Channels.Count);
var bitsPerFrame = 32 + 16 * channelCount;
baseText = baseText.Replace("{DAS BIT RATE}", $"{bitsPerFrame * test.Modules[0].SampleRateHz:0}");
var now = DateTime.UtcNow;
baseText = baseText.Replace("{CREATE DATE}", $"{now.Year:0000}-{now.Month:00}-{now.Day:00} {now.Hour:00}:{now.Minute:00}:{now.Second:00}");
baseText = baseText.Replace("{DAS SAMPLE RATE}", $"{test.Modules[0].SampleRateHz}");
baseText = baseText.Replace("{NUMBER OF CHANNELS}", $"{channelCount}");
baseText = baseText.Replace("{NUMBER OF WORDS}", $"{1 + channelCount}");
baseText = baseText.Replace("{NUMBER OF BITS}", $"{32 + 16*channelCount}");
sb.Append(baseText);
sb.Append(GetTMATSChannelPCM(test, summaryLookup));
return sb.ToString();
}
private string GetTMATSAnalog(Test test, IReadOnlyDictionary<Channel, ChannelInformation> summaryLookup)
{
var sb = new StringBuilder();
var baseText = System.IO.File.ReadAllText(@"TMTTemplates\S6ATMTTemplate_ANALOG_ExportBase.tmt");
baseText = baseText.Replace("{NAME OF PROGRAM}", PrepareOutput(GetApplicationVersion()));
baseText = baseText.Replace("{TEST ID}", PrepareOutput(test.Id));
var now = DateTime.UtcNow;
baseText = baseText.Replace("{CREATE DATE}", $"{now.Year:0000}-{now.Month:00}-{now.Day:00} {now.Hour:00}:{now.Minute:00}:{now.Second:00}");
baseText = baseText.Replace("{UDP STREAM DATA CHANNEL ID}", $"{DATA_CHANNEL_ID}");
baseText = baseText.Replace("{DAS SAMPLE RATE}", $"{test.Modules[0].SampleRateHz}");
baseText = baseText.Replace("{NUMBER_OF_CHANNELS}", $"{test.Modules.Sum(m => m.Channels.Count)}");
sb.Append(baseText);
sb.AppendLine();
sb.Append(GetTMATSChannelsAnalog(test, summaryLookup));
return sb.ToString();
}
private static string GetApplicationVersion()
{
var v = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
return string.Format("DataPRO {0}.{1:00}.{2:00000}", v.Major, v.Minor, v.Build);
}
#endregion
/// <summary>
/// constructs the writer with a given file and encoding
/// </summary>
/// <param name="fileType"></param>
/// <param name="encoding"></param>
internal Writer(File fileType, int encoding)
: base(fileType, encoding)
{
WriterParent = fileType;
}
/// <summary>
/// initializes the writer
/// </summary>
/// <param name="pathname"></param>
/// <param name="id"></param>
/// <param name="dataFolder"></param>
/// <param name="test"></param>
/// <param name="bFiltering"></param>
/// <param name="includeGroupNameInISOExport"></param>
/// <param name="fd"></param>
/// <param name="tmChannel"></param>
/// <param name="channelNumber"></param>
/// <param name="beginEventHandler"></param>
/// <param name="cancelEventHandler"></param>
/// <param name="endEventHandler"></param>
/// <param name="tickEventHandler"></param>
/// <param name="errorEventHandler"></param>
/// <param name="cancelRequested"></param>
public void Initialize(string pathname,
string id,
string dataFolder,
Test test,
bool bFiltering,
bool includeGroupNameInISOExport,
FilteredData fd,
Channel tmChannel,
int channelNumber,
BeginEventHandler beginEventHandler,
CancelEventHandler cancelEventHandler,
EndEventHandler endEventHandler,
TickEventHandler tickEventHandler,
ErrorEventHandler errorEventHandler,
CancelRequested cancelRequested)
{
}
}
}
}

View File

@@ -0,0 +1,206 @@
using DTS.Serialization.IRIGCH10.Enums;
using DTS.Serialization.IRIGCH10.Packets;
using System;
using System.ComponentModel;
using System.IO;
namespace DTS.Serialization.IRIGCH10
{
/// <summary>
/// this is the interface that all datapackets implement, it defines how packets can be interacted with in general
/// </summary>
public interface IDataPacket
{
/// <summary>
/// all chapter 10 packets have a packet header
/// </summary>
IPacketHeader PacketHeader { get; }
/// <summary>
/// computes the checksum for the datapacket data
/// </summary>
uint ComputeCheckSum();
/// <summary>
/// returns all bytes for the packet
/// </summary>
/// <returns></returns>
byte[] GetBytes();
/// <summary>
/// sets the Realtime Counter (10 MHZ) for the packet
/// </summary>
/// <param name="rtc"></param>
void SetRTC(long rtc);
/// <summary>
/// sets the DataVersion of the packet
/// (part of the packet header)
/// </summary>
/// <param name="version"></param>
void SetDataVersion(DataTypeVersion version);
/// <summary>
/// sets the packet flags for the packet (part of packet header)
/// </summary>
/// <param name="SecondaryHeaderPresent"></param>
/// <param name="SecondaryHeaderTime"></param>
/// <param name="RTCSyncerror"></param>
/// <param name="DataOverflow"></param>
/// <param name="fmt"></param>
/// <param name="checkSum"></param>
//void SetPacketFlags(bool SecondaryHeaderPresent,
// bool SecondaryHeaderTime,
// bool RTCSyncerror,
// bool DataOverflow,
// SecondaryHeaderTimeFormat fmt,
// DataCheckSumType checkSum
//);
/// <summary>
/// sets the channel id for the packet (part of packet header)
/// </summary>
/// <param name="channelID"></param>
void SetChannelID(ushort channelID);
/// <summary>
/// sets the sequence number for the packet,
/// in general should be increasing and flip back to 0
/// after max value
/// </summary>
/// <param name="seq"></param>
void SetSequenceNumber(ushort seq);
}
/// <summary>
/// this is the general base class all packets extend, it
/// has common features like computing the checksum, setting the
/// packet specific Channel Data Word, etc
/// </summary>
public abstract class AbstractDataPacket
{
protected int CommonHeaderWork(ushort channelId, byte dataVersion,
byte sequenceNumber, long rtc, uint dataLength, int nanoseconds, int seconds)
{
PacketHeader.ChannelId = channelId;
PacketHeader.DataVersion = dataVersion;
PacketHeader.SequenceNum = sequenceNumber;
PacketHeader.IPTSTimeSource = PacketHeader.SecondaryHeaderPresent;
PacketHeader.RTCSyncError = false;
PacketHeader.SecondaryHeaderTimeFormat = IRIGCH10.PacketHeader.SecondaryHeaderTimeFormats.IEEE1588;
PacketHeader.DataOverflowError = false;
PacketHeader.CheckSumPresence = IRIGCH10.PacketHeader.DataCheckSumPresences.NotPresent;
PacketHeader.SetRTC(rtc);
PacketHeader.DataLength = dataLength;
PacketHeader.PacketLength = PacketHeader.DataLength + IRIGCH10.PacketHeader.PACKET_HEADER_LENGTH;
if (PacketHeader.SecondaryHeaderPresent)
{
PacketHeader.PacketLength += SecondaryTimeFormatHeader.SECONDARY_TIME_HEADER_LENGTH;
}
if (0 != PacketHeader.PacketLength % 4)
{
var pad = 4 - PacketHeader.PacketLength % 4;
//pad out packet length to a multiple of 4 ...
PacketHeader.PacketLength += pad;
//_dataBytes = new byte[_dataBytes.Length + pad];
}
_dataBytes = new byte[PacketHeader.PacketLength - IRIGCH10.PacketHeader.PACKET_HEADER_LENGTH];
var offset = 0;
if (PacketHeader.SecondaryHeaderPresent)
{
var secondaryHeader = SecondaryTimeFormatHeader.GetBytes(nanoseconds, seconds);
Buffer.BlockCopy(secondaryHeader, 0, _dataBytes, offset, secondaryHeader.Length);
offset += secondaryHeader.Length;
}
var channelSpecificDataWord = BitConverter.GetBytes(ChannelSpecificDataWord);
Buffer.BlockCopy(channelSpecificDataWord, 0, _dataBytes, offset, channelSpecificDataWord.Length);
offset += channelSpecificDataWord.Length;
return offset;
}
private uint _ChannelSpecificDataWork = 0;
[Browsable(false)]
public uint ChannelSpecificDataWord
{
get => _ChannelSpecificDataWork;
protected set => _ChannelSpecificDataWork = value;
}
[Browsable(false)]
public IPacketHeader PacketHeader { get; protected set; }
public uint ComputeCheckSum()
{
return Utils.Utils.GetCheckSum32(_dataBytes);
}
/// <summary>
/// sets the sequence number for the packet
/// </summary>
/// <param name="seq"></param>
public void SetSequenceNumber(ushort seq)
{
PacketHeader.SequenceNum = BitConverter.GetBytes(seq)[0];
}
private long _rtc;
/// <summary>
/// returns the Realtime Counter (10Mhz) value for the packet
/// </summary>
/// <returns></returns>
public long GetRTC() { return _rtc; }
/// <summary>
/// sets the Realtime Counter (10Mhz) value for the packet
/// </summary>
/// <param name="rtc"></param>
public void SetRTC(long rtc)
{
_rtc = rtc;
PacketHeader.SetRTC(rtc);
}
public const long BASE_RTC = 141989612500056L;
/// <summary>
/// returns all bytes for the packet
/// is virtual so extending classes can define their own behavior as needed
/// </summary>
/// <returns></returns>
public virtual byte[] GetBytes()
{
using (var ms = new MemoryStream())
{
using (var bw = new BinaryWriter(ms))
{
bw.Write(PacketHeader.GetBytes());
bw.Write(_dataBytes);
}
return ms.ToArray();
}
}
public AbstractDataPacket(DataFileDataTypes dataType, bool secondaryHeaderPresent)
{
PacketHeader = new PacketHeader(dataType);
PacketHeader.SecondaryHeaderPresent = secondaryHeaderPresent;
}
public AbstractDataPacket(byte[] bytes)
{
var header = new byte[24];
Buffer.BlockCopy(bytes, 0, header, 0, 24);
PacketHeader = new PacketHeader(header);
FromBytes(bytes);
}
protected virtual void FromBytes(byte[] bytes)
{
_dataBytes = new byte[PacketHeader.DataLength];
Buffer.BlockCopy(bytes, 24, _dataBytes, 0, _dataBytes.Length);
}
protected byte[] _dataBytes;
public void SetDataVersion(DataTypeVersion version)
{
PacketHeader.SetDataVersion(version);
}
public void SetChannelID(ushort channelID)
{
PacketHeader.ChannelId = channelID;
}
}
}

View File

@@ -0,0 +1,45 @@
/*
* Test.IConvertable.cs
*
* Copyright © 2009
* Diversified Technical Systems, Inc.
* All Rights Reserved
*/
using System.Collections.Generic;
namespace DTS.Serialization
{
// *** see Test.cs ***
public partial class Test
{
public delegate void ReportErrors(List<string> errors);
/// <summary>
/// An object that expresses this interface can convert itself to and from a
/// <see cref="DTS.Serialization.Test"/> object.
/// </summary>
///
public interface IConvertable
{
/// <summary>
/// Convert this object to a <see cref="DTS.Serialization.Test"/>.
/// </summary>
///
/// <returns>
/// The <see cref="DTS.Serialization.Test"/> equivalent of this object.
/// </returns>
///
Test ToDtsSerializationTest();
/// <summary>
/// Initialize this object using the specified <see cref="DTS.Serialization.Test"/>.
/// </summary>
///
/// <param name="test">
/// The <see cref="DTS.Serialization.Test"/> with which this object will be initialized from.
/// </param>
///
void FromDtsSerializationTest(Test test, ReportErrors reportErrors);
}
}
}