--- source_files: - DataPRO/Modules/Hardware/HardwareList/Model/HardwareChannelAssignment.cs - DataPRO/Modules/Hardware/HardwareList/Model/SLICE6TreeNode.cs - DataPRO/Modules/Hardware/HardwareList/Model/Hardware.cs generated_at: "2026-04-16T04:38:28.250792+00:00" model: "Qwen/Qwen3-Coder-Next-FP8" schema_version: 1 sha256: "e010e06b613fee2c" --- # HardwareList.Model Module Documentation ## 1. Purpose This module provides data models and persistence logic for representing and managing hardware devices (DAS units) in the DataPRO diagnostics system. It defines core entities (`HardwareChannelAssignment`, `SLICE6TreeNode`, `HardwareModel`) that model hardware topology, channel assignments, and device metadata. The module enables querying device hierarchies (e.g., SLICE6DB parent-child relationships), persisting association changes to the database, and exposing hardware properties for UI binding (via `INotifyPropertyChanged`). It serves as the data layer for the Hardware List view, supporting configuration, sorting, filtering, and calibration tracking. ## 2. Public Interface ### `HardwareChannelAssignment` - **`HardwareChannelAssignment(string channelNumber, string sensor, string name)`** Constructor. Initializes immutable properties with provided values. Represents a mapping between a hardware channel, its connected sensor type, and a user-defined name. - **`string ChannelNumber { get; }`** Channel identifier (e.g., "CH1", "A0"). - **`string Sensor { get; }`** Type of sensor connected to the channel. - **`string Name { get; }`** User-assigned name for the channel. --- ### `SLICE6TreeNode` - **`SLICE6TreeNode(int dasId, string serialNumber, int port, int number, int positionOnChain)`** Constructor. Initializes properties for a SLICE6 device node in a tree structure. - **`int DASId { get; set; }`** Primary key ID from the DAS database table. - **`string SerialNumber { get; set; }`** Device serial number. - **`int Port { get; set; }`** Physical port number on the S6DB (distributor) to which the device is connected. - **`string PortString { get; }`** Readable string representation of `Port`; returns `"---"` if `Port < 0`. - **`int Number { get; set; }`** Order of the device on the S6DB (i.e., its position index among siblings). - **`int PositionOnChain { get; set; }`** Position of the device within its daisy-chain on a given port. - **`string PositionOnChainString { get; }`** Readable string representation of `PositionOnChain`; returns `"---"` if `PositionOnChain < 0`. - **`static ISLICE6TreeNode[] GetAvailableTreeNodes(string serialNumberParent)`** Queries the database for all SLICE6 devices *not* associated with `serialNumberParent`. Returns an array of `ISLICE6TreeNode` instances sorted by `SerialNumber`. Returns empty array if `serialNumberParent` is null/empty. - **`static ISLICE6TreeNode[] GetTreeNodes(string serialNumberParent)`** Queries the database for all SLICE6 devices *associated* with `serialNumberParent`. Returns an array of `ISLICE6TreeNode` instances sorted by `Number` (i.e., `PositionOnDistributor`). Returns empty array if `serialNumberParent` is null/empty. - **`static void SwapNodes(string serialNumberA, string serialNumberB)`** Swaps all child devices between two SLICE6DB units (`serialNumberA` and `serialNumberB`) in the database. *Note: There is a bug in the implementation—both `listA` and `listB` are associated to `serialNumberB` in the second `Associate` call, not `serialNumberA` and `serialNumberB` respectively.* - **`static void SaveAssociations(string serialNumber, ISLICE6TreeNode[] attachedSLICE6)`** Commits the current association state of SLICE6 devices to a parent SLICE6DB (`serialNumber`). First clears all existing associations for `serialNumber`, then updates each device in `attachedSLICE6` with its new `ParentDAS`, `PositionOnDistributor`, `PositionOnChain`, and `Port`. --- ### `HardwareModel` - **`HardwareModel(IISOHardware d, ...)`** Constructor. Populates properties from an `IISOHardware` instance and calculates derived values (e.g., `CalDueDate`, `ChannelCount`). Requires calibration period parameters per hardware type. - **`int DASId { get; set; }`** Database ID of the device. - **`bool Disabled { get; set; }`** Whether the device is disabled. - **`string SerialNumber { get; set; }`** Device serial number. - **`string HardwareType { get; set; }`** Localized string description of the hardware type (e.g., `"SLICE6_BASE"`). - **`string ChannelCount { get; set; }`** Human-readable string listing channel types and counts (e.g., `"8 Analog, 2 Digital In"`). - **`bool HasIncludedChildren { get; set; }`** Indicates if this device has child devices marked as `Included`. - **`string Firmware { get; set; }`** Firmware version string. - **`double? MaxSampleRate { get; set; }`** Maximum supported sample rate (Hz); `null` if unknown or not applicable. - **`double TestSampleRate { get; set; }`** Current test/sample rate selected for the device. - **`double TestAAFilterRateHz { get; set; }`** Anti-aliasing filter rate (Hz) corresponding to `TestSampleRate`. - **`DateTime? CalDate { get; set; }`** Last calibration date. - **`DateTime? CalDueDate { get; set; }`** Calculated calibration due date based on `CalDate` and hardware-specific period. - **`string[] AvailableSampleRates { get; set; }`** Array of available sample rate strings (e.g., `["1000", "2000"]`). - **`string SelectedSampleRateItem { get; set; }`** Currently selected sample rate string. - **`int SelectedSampleRateIndex { get; set; }`** Index of the current sample rate in `_availableSampleRates`. Setter updates `TestSampleRate` and `TestAAFilterRateHz`. - **`object Hardware { get; set; }`** Backing `IISOHardware` instance. - **`int AnalogChannels { get; set; }`** Number of analog input channels. - **`int SquibChannels { get; set; }`** Number of squib (explosive device) channels. - **`int DigitalInChannels { get; set; }`**, **`int DigitalOutChannels { get; set; }`**, **`int UartChannels { get; set; }`**, **`int StreamOutChannels { get; set; }`**, **`int StreamInChannels { get; set; }`**, **`int CanChannels { get; set; }`** Channel counts for respective interface types. - **`string IPAddress { get; set; }`** IP address or `"USB"` if connected via USB. - **`bool Included { get; set; }`** Whether the device is included in the current configuration. - **`double DSPStreamingFilter { get; set; }`** DSP streaming filter value. - **`DTS.Common.ClockSyncProfile MasterProfile { get; set; }`**, **`DTS.Common.ClockSyncProfile SlaveProfile { get; set; }`** Clock synchronization profiles (master/slave). - **`bool IsClockMaster { get; set; }`** Whether the device is configured as a clock master. Only valid if `IsClockedDAS` is true. - **`bool IsClockedDAS { get; }`** Returns `true` if the device type supports clocking (e.g., SLICE6, S6A_EthernetRecorder, TSR_AIR). - **`byte PTPDomainID { get; set; }`** PTP domain ID for precision time protocol. - **`bool IsPTPSync { get; }`** Returns `true` if PTP synchronization is active (E2E profile in use). - **`bool MixedRates { get; set; }`** Whether the device is part of a mixed-sample-rate configuration. - **`bool IsDistributor { get; }`** Returns `true` if the device is a distributor (e.g., SLICE6DB, SLICE_Distributor). - **`bool IsBattery { get; }`** Returns `true` if the device is a PowerPro. - **`bool IsTSRAIR { get; }`** Returns `true` if the device is a TSR_AIR or embedded sensor type. - **`bool IncludedAndNotMixedRatesAndCompactOrNotDistributor { get; }`**, **`bool IncludedAndMixedRatesAndCompact { get; }`** Derived UI flags for conditional rendering. - **`string ParentDAS { get; set; }`** Serial number of the parent device (distributor). - **`int PositionOnChain { get; set; }`**, **`int PositionOnDistributor { get; set; }`**, **`int Port { get; set; }`** Position metadata for hierarchical placement. - **`bool HasTreeView { get; set; }`** Indicates if a tree view should be shown for this device (e.g., for SLICE6DB). - **`string SerialNumberDisplay { get; }`** Display name: `SerialNumber` unless `StandIn` is true, in which case it returns the hardware type description. - **`static IHardware[] GetAvailableSLICE6DB(string serialNumber)`** Returns all SLICE6DB devices (including `SLICE6DB_InDummy`) not currently assigned to any parent, excluding `serialNumber`. - **`void DetermineChannelCount(bool showCompact, IHardware[] allHardware)`** Updates `ChannelCount` and `MaxSampleRate` based on device type and child devices. For distributors, calculates min sample rate among children if `showCompact` is true. - **`void SetIncluded(bool bIncluded)`**, **`void SetMixedRates(bool mixedRates)`** Updates state and fires property change notifications. - **`bool Filter(string term)`** Returns `true` if `SerialNumber`, `Firmware`, or `HardwareType` contains `term` (case-insensitive). - **`class HardwareComparer : IComparer`** Implements sorting for `IHardware` objects. Supports sorting by `HardwareListTags` (e.g., `SerialNumber`, `CalDate`, `FirstUseDate`). Handles nulls and `double?/DateTime?` comparisons. `FirstUseDate` sorts nulls last when valid. --- ## 3. Invariants - **`SLICE6TreeNode`** - `Port`, `Number`, and `PositionOnChain` must be non-negative integers for valid hardware placements. Negative values are represented as `"---"` in UI via `PortString`/`PositionOnChainString`. - `GetAvailableTreeNodes` and `GetTreeNodes` only query devices with `Type = SLICE6_Base` and `Position ≠ 'Prototype'` (for `GetAvailableTreeNodes`). - `SwapNodes` assumes all child devices of both parents are retrieved in one query and re-associated. *Bug: Both lists are associated to `serialNumberB`.* - **`HardwareModel`** - `CalDueDate` is derived from `CalDate` (or `FirstUseDate` if `IsFirstUseValid`) plus a hardware-specific calibration period. - `MaxSampleRate` is `null` if `d.MaxSampleRate` is `<= 0` or `uint.MaxValue`. - `IsClockedDAS` is determined by a fixed set of `HardwareTypes`. - `Included` and `MixedRates` flags affect derived properties (`IncludedAndNotMixedRatesAndCompactOrNotDistributor`, `IncludedAndMixedRatesAndCompact`). - `ChannelCount` for distributors is `"N/A"` if `showCompact` is false or no children exist. - **General** - All database operations use `DTS.Common.Storage.DbOperations.GetSQLCommand()` and explicitly dispose connections in `finally` blocks. - `HardwareModel` instances are constructed with a non-null `HardwareListViewModel` (`_vm`) reference for event callbacks. ## 4. Dependencies ### Dependencies *of* this module: - **`DTS.Common.*`** - `DTS.Common.Base.BasePropertyChanged` (base class for `SLICE6TreeNode`) - `DTS.Common.Storage.DbOperations` (database access) - `DTS.Common.Enums.Hardware.HardwareTypes` (device type enumeration) - `DTS.Common.Interface.DASFactory.Diagnostics.IISOHardware`, `IHardware` (hardware abstraction interfaces) - `DTS.Common.Converters.EnumDescriptionTypeConverter` (localized enum descriptions) - `DTS.Common.ClockSyncProfile` (clock sync profiles) - **`HardwareList.Resources.StringResources`** - Used for localized strings (e.g., `"Analog"`, `"USB"`, `"N/A"`). ### Dependencies *on* this module: - **`HardwareList.HardwareListViewModel`** - Passed into `HardwareModel` constructor; used for event callbacks (`FireSampleRate`, `FireClockMaster`, etc.). - **UI Layer** - `HardwareModel` properties (e.g., `Included`, `CalDueDate`, `ChannelCount`) are bound to views. - `SLICE6TreeNode` is used to populate tree views for SLICE6DB hierarchies. - **HardwareList Module** - `HardwareList.Model` is the core data model for the `HardwareList` module. ## 5. Gotchas - **`SLICE6TreeNode.SwapNodes` Bug**: The `Associate` method is called twice—once for `listA` with `serialNumberB`, and again for `listB` *also* with `serialNumberB`. This means devices originally under `serialNumberB` are incorrectly moved to `serialNumberB` (no-op), and devices under `serialNumberA` are moved to `serialNumberB`, but the reverse move does not occur. The intended behavior is likely `Associate(serialNumberB, listA)` and `Associate(serialNumberA, listB)`. - **`HardwareModel.CalDueDate` Calculation**: Uses `FirstUseDate` only if `IsFirstUseValid` is true. If `FirstUseDate` is `null` and `IsFirstUseValid` is true, it defaults to `DateTime.Today`. This may cause unexpected recalculations if `FirstUseDate` is set later. - **`HardwareModel.TestSampleRate` Setter Side Effect**: Setting `TestSampleRate` triggers `OnPropertyChanged("SelectedSampleRateIndex")`, but the getter for `SelectedSampleRateIndex` re-computes `SelectedSampleRateItem` and calls `_vm.FireSampleRate`. This could cause redundant UI updates if `TestSampleRate` is set multiple times. - **`HardwareModel.DetermineChannelCount` for Distributors**: `MaxSampleRate` is recalculated as the *minimum* sample rate of child devices. If children have no sample rate (`null`), `ChannelCount` becomes `"N/A"` and `MaxSampleRate` is set to `null`. This may mask missing configuration. - **`HardwareModel.Filter` Case Sensitivity**: Filtering is case-insensitive (`term.ToLower()`), but `SerialNumberDisplay` uses `isoHW.StandIn` logic, which may cause display mismatches if `StandIn` is toggled dynamically. - **`HardwareModel.Hardware` is `object`**: The `Hardware` property is typed as `object`, requiring casting to `IISOHardware` for type-specific checks (e.g., `IsClockedDAS`, `IsDistributor`). This risks runtime errors if the underlying type is not `IISOHardware`. - **`SLICE6TreeNode` SQL Injection Risk**: `GetAvailableTreeNodes` uses string interpolation for `HardwareTypes.SLICE6_Base` in the `WHERE` clause (`[(int)HardwareTypes.SLICE6_Base]`). While the enum value is fixed, this pattern is fragile and error-prone. - **`HardwareModel` Constructor Side Effects**: The constructor calls `d.GetChannelsString(...)` and computes `CalDueDate` based on `d.DASTypeEnum`. If `Hardware` is `null` or `DASTypeEnum` is invalid, it throws `ArgumentOutOfRangeException`. - **`HardwareComparer.NumericCompare` Logic**: For `double`/`DateTime`, `a > b` returns `-1` (descending order), but the method returns `1` for all other cases. This may cause inconsistent sorting if types are mixed (e.g., `double` vs `DateTime`).