270 lines
14 KiB
Markdown
270 lines
14 KiB
Markdown
|
|
---
|
||
|
|
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<IHardware>`**
|
||
|
|
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`).
|