--- source_files: - Common/DTS.Common.Serialization/TDMS/TDMS.File.cs - Common/DTS.Common.Serialization/TDMS/TDMS.File.Writer.cs generated_at: "2026-04-16T03:37:03.448977+00:00" model: "Qwen/Qwen3-Coder-Next-FP8" schema_version: 1 sha256: "d8ac04ef5386bd66" --- # TDMS ## Documentation: TDMS.File Module --- ### **Purpose** This module implements serialization and deserialization logic for the TDMS (Technical Data Management Streaming) file format, a binary format used primarily for storing time-series test data. It defines the `DTS.Serialization.TDMS.File` class, which represents a TDMS file and encapsulates metadata and configuration (e.g., filtering options, test plan association), and its nested `Writer` class, responsible for writing `Test` objects to disk in the NI TDMS v2.0 format (as referenced by the whitepaper URL in comments). The module serves as the core serialization backend for exporting test data in a standardized, interoperable format compatible with National Instruments tools. --- ### **Public Interface** #### **Class: `DTS.Serialization.TDMS.File`** - **Constructor** ```csharp public File(Common.ISO.TestPlan plan) ``` Initializes a new `File` instance, associating it with a given `TestPlan`. Calls `base("TDMS")`, indicating it inherits from `Serialization.File`. - **Properties** - `public static string Extension => ".tdms"` Returns the file extension for TDMS files. - `public bool UseZeroForUnfiltered { get; set; } = false` Controls whether unfiltered data should be represented as zero (default: `false`). - `public bool FilteredExport { get; set; } = false` Indicates whether filtered data export is enabled (default: `false`). - `public IWriter Exporter { get; }` Lazily initializes and returns a `Writer` instance associated with this `File`. Sets `Writer.TestPlan` and `Writer.WriterParent` appropriately. Throws a wrapped exception on failure. - **Methods** - `public override void SetEUData(string channelId, FilteredData fd)` Stores or updates Eu (engineering units) unfiltered data for a given channel ID in an internal dictionary `_EUUnfilteredDataForLinearizedChannels`. - `public override FilteredData GetEUData(string channelId)` Retrieves stored Eu unfiltered data for a given channel ID from `_EUUnfilteredDataForLinearizedChannels`. Returns `null` if not present. > **Note**: `_EUUnfilteredDataForLinearizedChannels` is a private `Dictionary`, used to associate channel IDs with precomputed Eu data (likely for linearized channels). --- #### **Nested Class: `DTS.Serialization.TDMS.File.Writer`** - **Constructor** ```csharp internal Writer(File fileType, int encoding) ``` Internal constructor; initializes the base `Writer` with the parent `File` and encoding. The `encoding` parameter is passed to the base class. - **Properties** - `public File WriterParent { get; set; }` Reference back to the parent `File` instance. Set by `File.Exporter` getter. - `public Common.ISO.TestPlan TestPlan { get; set; } = null` Reference to the test plan associated with the export. Set by `File.Exporter`. - `public string ExtensionPrefix { get; set; } = string.Empty` Optional prefix appended to the base filename (e.g., for multi-file exports). - **Methods** - `public void Write(string pathname, string id, Test test, bool bFiltering, bool includeGroupNameInISOExport, double minStartTime, int dataCollectionLength)` Convenience overload of `Write(...)`. Delegates to the 18-parameter overload with many arguments set to `null` or defaults. - `public void Write(string pathname, string id, string dataFolder, Test test, bool bFiltering, bool includeGroupNameInISOExport, FilteredData fd, Test.Module.Channel tmChannel, int channelNumber, BeginEventHandler beginEventHandler, CancelEventHandler cancelEventHandler, EndEventHandler endEventHandler, TickEventHandler tickEventHandler, ErrorEventHandler errorEventHandler, CancelRequested cancelRequested, double minStartTime, int dataCollectionLength)` Main export method. Writes a `Test` to disk as a `.tdms` file. - Creates the target directory if missing. - Constructs filename as `Path.Combine(pathname, id + ExtensionPrefix + ".tdms")`. - Writes a binary stream using `BinaryWriter`. - Invokes event handlers (`begin`, `tick`, `end`, `error`) for progress/error reporting. - Calls `WriteLeadInAndMetaData(...)` and `WriteRawData(...)` to serialize metadata and raw data. - Logs exceptions via `APILogger.Log(...)`. - Throws or reports exceptions depending on presence of `errorEventHandler`. - `public void Initialize(...)` Currently empty stub. No-op implementation. > **Note**: `BeginEventHandler`, `CancelEventHandler`, `EndEventHandler`, `TickEventHandler`, `ErrorEventHandler`, and `CancelRequested` are delegate/event types used for progress reporting but are not defined in this file. --- ### **Invariants** 1. **File Format Compliance**: - The written file adheres to the NI TDMS v2.0 format (per [NI whitepaper 3727](http://www.ni.com/white-paper/3727/en/)). - The lead-in signature is always `"TDSm"` (4 bytes). - The `ToCMask` is fixed to `14` (`TocMetaData | TocNewObjList | TocRawData`). - The file version is fixed to `4713`. - All numeric fields use little-endian encoding (no big-endian support; `TocBigEndian` bit is unset). 2. **Data Layout**: - Raw data is stored as 16-bit signed integers (`DataTypes.I16`), regardless of source data type. - Each channel’s raw data is written sequentially, with no interleaving. - The raw data offset and next segment offset are computed and written as 8-byte unsigned integers. 3. **Metadata Structure**: - Exactly four objects are written: 1. File object (`"/"`), 2. Group object (`"/'TestId'"`), 3. One channel object per `test.Channels`. - Group properties are always written in the order defined by `GroupProperties` enum (TestId, Description, TestDate, TestTime). - Channel properties are written in the order defined by `ChannelProperties` enum. 4. **String Encoding**: - Strings are written as UTF-16 (via `char[]` → `BinaryWriter.Write(char[])`), with length prefix (4 bytes). - SuperScript characters (e.g., in `LinearizationFormula`) increase the string’s byte length beyond the character count (handled by `GetSuperScriptBytes`). --- ### **Dependencies** - **Internal Dependencies** - `DTS.Serialization.Test` (type `Test`, `Test.Module.Channel`, `Test.Module.AnalogInputChannel`, `Test.Module`) - `DTS.Serialization.TDMS.FilteredData` (used in `SetEUData`/`GetEUData`) - `DTS.Common.ISO.TestPlan` (passed to constructor) - `DTS.Common.Utilities.Logging.APILogger` (used for logging exceptions) - **External Dependencies** - `System.IO` (`FileStream`, `BinaryWriter`, `Directory`, `Path`) - `System.Linq` (used for LINQ queries in `GetChannelPropertyLengths`) - `System.Collections.Generic` (`Dictionary`, `HashSet`, `List`, `Enum.GetValues`) - **Inheritance & Interfaces** - `File` inherits from `Serialization.File` (base class). - `File` implements `IWritable`. - `Writer` inherits from `Writer` (generic base class) and implements `IWriter`. --- ### **Gotchas** 1. **Hardcoded Data Type**: Raw data is *always* written as `I16` (`DataTypes.I16`), even if the underlying `PersistentChannelInfo[i]` values are larger or different types. This may cause data truncation if values exceed `Int16` range. 2. **SuperScript Byte Counting Quirk**: The `GetSuperScriptBytes` method computes extra bytes for certain Unicode super/subscript characters (e.g., `⁰`, `²`, `ⁿ`) in `LinearizationFormula`. This logic assumes UTF-16 encoding and checks specific byte patterns (`byteArray[0]`, `byteArray[1]`). This is non-standard and may break if the source strings contain unexpected Unicode. 3. **Unused Parameters in `Write(...)` Overloads**: Several parameters in the 18-argument `Write(...)` overload (`dataFolder`, `fd`, `tmChannel`, `channelNumber`, `minStartTime`, `dataCollectionLength`) are *not used* in the implementation. Their presence suggests partial refactoring or future extensibility. 4. **Empty `Initialize(...)` Method**: The `Initialize(...)` method is a no-op stub. Its signature implies it may have been intended for streaming or multi-pass writes but is currently unused. 5. **No Support for Event Cancellation**: Although `CancelRequested` and `CancelEventHandler` are passed to `Write(...)`, the method does not check for cancellation during iteration over samples. Progress reporting (`tickEventHandler`) occurs only once (at 100%) after full write. 6. **String Concatenation in Paths**: Channel object paths are built via string concatenation (e.g., `groupObjectPath + "/" + "'" + channelName + "':" + description + "'"`). This assumes no special characters (e.g., quotes, slashes) in `channel.ChannelName2` or `channel.ChannelDescriptionString`, which could break path parsing. 7. **No Validation of Input Data**: The `WriteRawData` method writes raw samples directly without validation. If `PersistentChannelInfo.NumberOfSamples` is inconsistent with actual data length, it may cause `IndexOutOfRangeException` or silent truncation. 8. **Hardcoded Property Lists**: Property lists (`GroupProperties`, `ChannelProperties`) are fixed via `Enum.GetValues`. Adding new properties requires updating the enum *and* all switch statements in `WriteGroupProperties` and `WriteChannelSection`. 9. **No Support for Multiple Segments or Append Mode**: The implementation writes a single segment with fixed `nextSegmentOffset = RawDataOffset + totalNumberRawDataBytes`. It does not support appending to existing TDMS files or multi-segment files. 10. **Encoding Assumption**: The `encoding` parameter passed to `Writer` constructor is used only in the base class. No explicit encoding configuration (e.g., UTF-8 vs UTF-16) is exposed or validated in this module. --- *End of Documentation.*