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,131 @@
---
source_files:
- Common/DTS.Common.Serialization/Test/Module/IConvertable.cs
- Common/DTS.Common.Serialization/Test/Module/CalculatedChannel.cs
generated_at: "2026-04-16T03:40:25.931280+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "ac5cc711621ec54b"
---
# Module
## Documentation Page: `DTS.Serialization.Test.Module.IConvertable` and `DTS.Serialization.Test.Module.CalculatedChannel`
---
### 1. Purpose
This module provides serialization infrastructure and a concrete implementation for handling *calculated channels* in the DTS test data model. Specifically, the `IConvertable` interface defines a contract for objects that can be converted to/from the internal `Test.Module` serialization format, enabling interoperability with external or legacy serialization layers. The `CalculatedChannel` class implements this interface (via inheritance from `AnalogInputChannel`, which presumably implements `IConvertable`) and represents a derived channel whose values are computed from one or more source channels using a user-defined formula. It supports XML serialization/deserialization with custom attribute tagging and array-based source channel/module metadata.
---
### 2. Public Interface
#### `IConvertable` Interface (Nested in `Test.Module`)
- **`Module ToDtsSerializationTestModule(Test parentTest)`**
Converts the implementing object into a `Test.Module` instance. The `parentTest` parameter identifies the containing test context.
- **`void FromDtsSerializationTestModule(Module testModule, ReportErrors reportErrors)`**
Initializes the implementing object from a `Test.Module` instance. The `reportErrors` delegate is used to surface validation or parsing errors encountered during deserialization.
> **Note**: The interface is declared in `IConvertable.cs`, but its implementation is not shown in the provided source. The `CalculatedChannel` class inherits from `AnalogInputChannel`, which is assumed to implement `IConvertable`.
#### `CalculatedChannel` Class (Nested in `Test.Module`)
- **`CalculatedChannel(Module parentModule)`**
Constructor. Initializes a new `CalculatedChannel` instance with the given parent module.
- **`int[] SourceChannelNumber { get; set; }`**
Gets/sets the array of channel numbers from which this calculated channel derives its data.
- **`int[] SourceModuleNumber { get; set; }`**
Gets/sets the array of module numbers corresponding to each source channel.
- **`string[] SourceModuleSerialNumber { get; set; }`**
Gets/sets the array of serial numbers for the modules containing the source channels.
- **`string Calculation { get; set; }`**
Gets/sets the calculation expression (e.g., `"A + B * 2"`) applied to the source channels. Default: `"NONE"`.
- **`ulong T1 { get; set; }`**
Gets/sets a time parameter (likely for HIC calculations). Default: `0`.
- **`ulong T2 { get; set; }`**
Gets/sets a second time parameter (likely for HIC calculations). Default: `0`.
- **`double HIC { get; set; }`**
Gets/sets the Head Injury Criterion value (a derived metric). Default: `0.0`.
- **`double SampleRateHz { get; set; }`**
Gets/sets the sample rate (in Hz) for this calculated channel. Inherited from base class or set explicitly.
- **`override void WriteXml(XmlWriter writer)`**
Serializes the object to XML. Writes attributes for all public properties (including `SourceChannelNumber`, `SourceModuleNumber`, `SourceModuleSerialNumber`, `Calculation`, `SampleRateHz`, and conditionally `HIC`, `T1`, `T2`). Uses `AttributeExtractor<XmlSerializationTagAttribute>` to resolve XML tag names.
- **`override void ReadXml(XmlReader reader)`**
Deserializes the object from XML. Reads attributes using `PropertyAttributeDecoder<CalculatedChannel>`. Handles parsing of comma-separated integer arrays (`SourceChannelNumber`, `SourceModuleNumber`) and custom-delimited string arrays (`SourceModuleSerialNumber`, using `"_.-._"` as delimiter).
- **`static CalculatedChannel CreateInstance(Channel[] channels)`**
Factory method to create a `CalculatedChannel` from multiple source `Channel`s. Validates that the maximum sample rate is a multiple of each source channels sample rate. Copies all properties from the first channel using `TypeDescriptor`. Initializes `SourceChannelNumber`, `SourceModuleNumber`, and `SourceModuleSerialNumber` arrays from all input channels.
- **`static CalculatedChannel CreateInstance(Channel sourceChannel)`**
Factory method to create a `CalculatedChannel` from a single `Channel`. Sets `SourceChannelNumber`, `SourceModuleNumber`, and `SourceModuleSerialNumber` to single-element arrays derived from the source channel.
- **`override bool Equals(object obj)`**
Performs memberwise equality comparison with another `CalculatedChannel`. Compares: `ChannelId`, `SourceChannelNumber`, `ChannelDescriptionString`, `EngineeringUnits` (case-insensitive), `Calculation`, and `IsoCode`.
- **`override int GetHashCode()`**
Returns the hash code from the base class (no custom logic visible).
- **`override string ToString()`**
Returns `ChannelDescriptionString`.
---
### 3. Invariants
- **Array Length Consistency**: For `SourceChannelNumber`, `SourceModuleNumber`, and `SourceModuleSerialNumber`, all three arrays must have identical length. This is *enforced by convention* in `CreateInstance`, but not validated in `ReadXml`/`WriteXml`.
- **Sample Rate Compatibility**: In `CreateInstance(Channel[])`, the maximum sample rate among source channels must be an integer multiple of each source channels sample rate; otherwise, an `InvalidOperationException` is thrown.
- **Optional Fields**: `HIC`, `T1`, and `T2` are only serialized if `_HIC.IsValueInitialized` is `true`. This implies they are initialized conditionally (e.g., only when non-zero or explicitly set).
- **Default Values**: `Calculation` defaults to `"NONE"`, `SampleRateHz` defaults to `0`, numeric arrays default to `null`, and `HIC`/`T1`/`T2` default to `0`/`0U`/`0U`.
- **Serialization Format**:
- Integer arrays are comma-separated (e.g., `"1,2,3"`).
- String arrays use `"_.-._"` as a delimiter (e.g., `"SN1_.-._SN2_.-._SN3"`).
- XML attributes use tags defined by `[XmlSerializationTag(...)]` attributes.
---
### 4. Dependencies
#### Module Dependencies
- **`DTS.Common.Utilities.Xml.AttributeExtractor<T>`**
Used in `WriteXml` to extract XML tag names from attributes.
- **`DTS.Common.Utilities.Xml.PropertyAttributeDecoder<T>`**
Used in `ReadXml` to decode XML attributes into properties.
- **`DTS.Common.Utilities.Logging.APILogger`**
Used for logging in `WriteXml` and `ReadXml`.
- **`System.ComponentModel.TypeDescriptor`**
Used in `CreateInstance` to copy property values via reflection.
- **`DTS.Common.Classes.Sensors.LinearizationFormula`**
Used in `CreateInstance(Channel[])` to clone the linearization formula.
#### Inferred Dependencies
- `AnalogInputChannel` (base class of `CalculatedChannel`) must implement `IConvertable`, though its implementation is not shown here.
- `Test` and `Module` classes are nested containers (see namespace structure).
- `ReportErrors` delegate type is referenced in `IConvertable.FromDtsSerializationTestModule` but not defined in the provided source.
---
### 5. Gotchas
- **Array Delimiter Ambiguity**: The string array delimiter `"_.-._"` is not documented in comments and may conflict with legitimate serial numbers containing that substring. No escaping or quoting is applied.
- **Partial Initialization in `ReadXml`**: `HIC`, `T1`, and `T2` are only deserialized if the `HIC` attribute is present and non-whitespace. If only `T1`/`T2` are present, they will remain at default (`0`), potentially masking incomplete data.
- **Sample Rate Validation Only in Factory**: The sample rate divisibility check occurs *only* in `CreateInstance(Channel[])`, not during XML deserialization or direct property assignment. Invalid rates may be silently stored.
- **Property Copying via Reflection**: `CreateInstance` uses `TypeDescriptor.GetProperties` to copy properties. This may fail silently for properties that throw exceptions during `GetValue`/`SetValue` (exceptions are logged or swallowed).
- **No Deep Clone for `LinearizationFormula`**: The `LinearizationFormula` is cloned via constructor, but other reference-type properties (e.g., `LinearizationFormula` is the only one explicitly cloned) may be shared if not handled.
- **Case-Insensitive Comparison Only for `EngineeringUnits`**: The `Equals` method uses `StringComparison.OrdinalIgnoreCase` for `EngineeringUnits`, but other string properties (`Calculation`, `ChannelDescriptionString`, `IsoCode`) use case-sensitive comparison—this inconsistency may cause confusion.
- **`SourceModuleNumber` Not Populated in Factories**: In both `CreateInstance` overloads, `SourceModuleNumber` is initialized as an empty array (`moduleNumbers` is never populated), which is likely a bug.
> **Note**: The `ReportErrors` delegate signature and `AnalogInputChannel`s implementation of `IConvertable` are not visible in the provided source. Their behavior is inferred.

View File

@@ -0,0 +1,189 @@
---
source_files:
- Common/DTS.Common.Serialization/Test/Module/Channel/IConvertable.cs
- Common/DTS.Common.Serialization/Test/Module/Channel/ChannelWithMeta.cs
- Common/DTS.Common.Serialization/Test/Module/Channel/DataArray.cs
generated_at: "2026-04-16T03:40:40.401644+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "6e59e4819411113f"
---
# Channel
## Documentation: `DTS.Serialization.Test.Module.Channel` Submodule
---
### 1. Purpose
This module defines core serialization abstractions for channel data within the DTS test data serialization framework. It provides interfaces and classes to represent channel metadata, raw data arrays, and conversion mechanisms between domain-specific objects and a canonical `Channel` representation used for persistence or transmission. The module serves as the foundational layer for serializing and deserializing time-series channel data—including scaling, timing, and unit-conversion metadata—into and from XML.
---
### 2. Public Interface
#### `IConvertable` Interface
*(Nested in `DTS.Serialization.Test.Module.Channel`)*
- **`Channel ToDtsSerializationTestModuleChannel()`**
Converts the implementing object into a canonical `Channel` instance.
- **`void FromDtsSerializationTestModuleChannel(Channel channel)`**
Initializes the implementing object using data from a `Channel` instance.
> **Note**: This interface is intended to be implemented by domain objects that need to be serialized/deserialized as `Channel` instances. The interface name is intentionally misspelled (`IConvertable` instead of `IConvertible`) per source.
---
#### `ChannelWithMeta` Class
*(Top-level in `DTS.Serialization`)*
- **`Channel Channel { get; }`**
The canonical channel object to be serialized.
- **`DataScaler Scaler { get; }`**
Scaling metadata associated with the channel.
- **`double SampleRate { get; }`**
Sample rate (in Hz) of the channel.
- **`double StartTime { get; }`**
Start time (in seconds) of the channels data.
- **`ChannelWithMeta(Channel channel, DataScaler scaler, double samplerate, double starttime)`**
Constructor initializing all properties.
- **`static double GetMinStartTime(double start, IList<ChannelWithMeta> channelsWithMeta)`**
Computes the earliest *common* start time across all provided channels and the given `start` offset. Uses `Math.Max(start, channelsWithMeta.Min(cwm => cwm.StartTime))`.
> **Comment in source**: “Start is a generally a negative value, so to get the value for the start time of the minimum common data set, run Max()”.
- **`static double GetMinStopTime(double stop, IList<ChannelWithMeta> channelsWithMeta)`**
Computes the latest *common* stop time across all channels. Uses:
`Math.Min(stop, min(cwm.StartTime + (cwm.Channel.PersistentChannelInfo.Length - 1) / cwm.SampleRate))`,
with intermediate casting to `decimal` to mitigate floating-point rounding errors.
---
#### `DataArray<DataType>` Class
*(Nested in `DTS.Serialization.Test.Module.Channel`)*
- **`DataType[] Values { get; set; }`**
Underlying array of data values. Returns an empty array if uninitialized or null.
- **`double ScaleFactorMv { get; set; }`**
ADC-to-millivolt scaling factor.
- **`double MvPerEu { get; set; }`**
Sensitivity: millivolts per engineering unit.
- **`double ScaleFactorEU { get; set; }`**
EU scaling factor (unused in current XML schema per tags).
- **`bool UseEUScaleFactors { get; set; }`**
Flag indicating whether EU scale factors should be applied.
- **`short DataZeroLevel { get; set; }`**
Zero-level offset for ADC data.
- **`double UnitConversion { get; set; }`**
Unit conversion multiplier (default `1.0`).
- **`double Multiplier { get; set; }`**
General-purpose multiplier (default `1.0`).
- **`double UserOffsetEU { get; set; }`**
User-defined offset in engineering units (default `0.0`).
- **`DataArray()`**
Default constructor.
- **`DataArray(DataType[] values)`**
Constructor initializing `Values` with the provided array.
- **Implicit operator `List<DataType> → DataArray<DataType>`**
Converts a `List<DataType>` to `DataArray<DataType>`.
- **Implicit operator `DataArray<DataType> → List<DataType>`**
Converts `DataArray<DataType>` to `List<DataType>`.
- **`void WriteXml(XmlWriter writer)`**
Serializes the instance to XML with the following structure:
```xml
<Data Length="N" Type="System.Double" ScaleFactorMv="..." SensitivityMvEu="...">
<Datum Value="..." />
...
</Data>
```
Includes attributes for `Length`, `Type`, `ScaleFactorMv`, and `SensitivityMvEu`. Each datum is serialized as `<Datum Value="..."/>`.
- **`void ReadXml(XmlReader reader)`**
Deserializes XML into the instance. Requires:
- `DataType` to have a public static `Parse(string)` method.
- `DataType` to be constructible via `Activator.CreateInstance`.
- XML to contain `Length`, `Type`, `ScaleFactorMv`, and `SensitivityMvEu` attributes.
- **`XmlSchema GetSchema()`**
Always returns `null`. Per comment: *“This method is never invoked during XML object serialization.”*
- **`bool Equals(object obj)`**
Memberwise equality test: compares `ScaleFactorMv`, `MvPerEu`, `Values.Length`, and element-wise `Values` equality.
- **`int GetHashCode()`**
Returns base hash code (no custom hashing implemented).
---
### 3. Invariants
- **`DataArray<DataType>`**
- `Values` is never `null` when accessed (returns empty array if uninitialized).
- XML serialization/deserialization requires `DataType` to implement `Parse(string)` and be instantiable via `Activator.CreateInstance`.
- `ScaleFactorMv` and `MvPerEu` are always serialized/deserialized as XML attributes (not child elements).
- `DataZeroLevel`, `UseEUScaleFactors`, `UnitConversion`, `Multiplier`, `UserOffsetEU` are *not* serialized in XML (no corresponding XML tags), though they are properties.
- **`ChannelWithMeta`**
- `GetMinStartTime` and `GetMinStopTime` assume `StartTime` and `SampleRate` are non-negative and consistent across channels.
- `GetMinStopTime` uses `decimal` arithmetic internally to avoid floating-point rounding artifacts.
- **`IConvertable`**
- Implementers must ensure `FromDtsSerializationTestModuleChannel` fully initializes the object from the `Channel` argument.
- `ToDtsSerializationTestModuleChannel` must produce a fully populated `Channel` instance.
---
### 4. Dependencies
#### Imports/Usings
- `System`, `System.Collections.Generic`, `System.Linq` → used in `ChannelWithMeta`.
- `DTS.Common.DAS.Concepts` → provides `DataScaler` type (used in `ChannelWithMeta`).
- `System.Xml`, `System.Xml.Schema`, `System.Xml.Serialization` → used in `DataArray<DataType>`.
- `DTS.Common.Utilities`, `DTS.Common.Utilities.DotNetProgrammingConstructs` → provides `Exceptional` base class and `Property<T>` wrapper (used in `DataArray<DataType>`).
#### Known Dependencies
- `DTS.Serialization.Test.Module.Channel` (itself) — `ChannelWithMeta` references `Channel` and `PersistentChannelInfo.Length`.
- `DataScaler` — assumed to be defined in `DTS.Common.DAS.Concepts`.
- `Property<T>` — internal wrapper used for observable/validated properties in `DataArray<T>`.
#### Dependents (Inferred)
- Serialization/deserialization pipelines (e.g., XML writers/readers) that consume `DataArray<T>`.
- Higher-level serialization modules that use `IConvertable` to bridge domain objects and `Channel`.
- `ChannelWithMeta` consumers (e.g., file writers) that need to align multiple channels in time.
---
### 5. Gotchas
- **Typo in interface name**: `IConvertable` (not `IConvertible`) — likely historical.
- **`DataArray<T>` XML schema is incomplete**: Only `ScaleFactorMv` and `MvPerEu` are serialized; other properties (`ScaleFactorEU`, `DataZeroLevel`, etc.) are ignored in XML.
- **`GetSchema()` always returns `null`**: This is intentional per comment, but may confuse developers expecting a schema for validation.
- **Rounding mitigation in `GetMinStopTime`**: Uses `decimal` cast to avoid floating-point artifacts (e.g., `4.999999999999995` vs `5.0`), but this may be fragile if `StartTime` or `SampleRate` are very large/small.
- **`DataArray<T>` requires `Parse(string)`**: If `DataType` lacks a static `Parse(string)` method, `ReadXml` throws an exception.
- **`Values` property returns empty array if null**: This avoids null-reference exceptions but may mask uninitialized state.
- **Implicit conversions are one-way in practice**: While both implicit operators exist, `DataArray<T>` is not a `List<T>`—conversions copy data, not reference.
> **None identified from source alone** beyond the above.
---
*Documentation generated from provided source files. No external assumptions or API speculation applied.*