init
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
---
|
||||
source_files:
|
||||
- DataPRO/Modules/RegionOfInterest/RegionOfInterestChannels/Properties/AssemblyInfo.cs
|
||||
generated_at: "2026-04-16T04:34:05.129036+00:00"
|
||||
model: "Qwen/Qwen3-Coder-Next-FP8"
|
||||
schema_version: 1
|
||||
sha256: "19de53eedae81d63"
|
||||
---
|
||||
|
||||
# Properties
|
||||
|
||||
## 1. Purpose
|
||||
This module (`RegionOfInterestChannels`) is an assembly containing components related to region-of-interest (ROI) channel handling within the DataPRO system. Based solely on the assembly metadata, it serves as a dedicated library module—likely referenced by other parts of the system—to encapsulate functionality for managing channels associated with user-defined regions of interest, though the actual implementation details (e.g., types, logic) are not present in this file and must reside in other source files within the same project.
|
||||
|
||||
## 2. Public Interface
|
||||
**No public interface elements are defined in this file.**
|
||||
`AssemblyInfo.cs` is a metadata file used for assembly-level attributes (e.g., version, title, COM visibility). It contains no class, method, property, or field declarations, and therefore contributes no public API surface.
|
||||
|
||||
## 3. Invariants
|
||||
- The assembly identity is fixed at version `1.0.0.0` (both `AssemblyVersion` and `AssemblyFileVersion`), as specified by the attributes.
|
||||
- The assembly is **not visible to COM** (`ComVisible(false)`), meaning it is not intended for interop with COM clients.
|
||||
- The GUID `bdb25406-3e28-4e62-ae2a-622120f933da` uniquely identifies the typelib if the assembly were exposed to COM (though it is not, per `ComVisible(false)`).
|
||||
- The assembly name is `"RegionOfInterestChannels"` (per `AssemblyTitle`), and it carries a copyright notice dated 2018.
|
||||
|
||||
## 4. Dependencies
|
||||
- **System.Reflection**, **System.Runtime.CompilerServices**, **System.Runtime.InteropServices** (via `using` directives and attribute usage).
|
||||
- This assembly likely *depends on* other modules in the DataPRO ecosystem (e.g., core ROI or channel management libraries), but no such dependencies are declared in this file.
|
||||
- It is *consumed by* other modules that reference this assembly (e.g., via project or NuGet references), but the consumer(s) are not visible here.
|
||||
|
||||
## 5. Gotchas
|
||||
- **Misleading file location**: The path `DataPRO/Modules/RegionOfInterest/RegionOfInterestChannels/Properties/AssemblyInfo.cs` suggests this assembly is part of a modular ROI feature, but the file itself contains no ROI-specific logic—only metadata. Developers may incorrectly assume ROI behavior resides here.
|
||||
- **Versioning rigidity**: The use of `[assembly: AssemblyVersion("1.0.0.0")]` without wildcard (`*`) means build/revision numbers are static and must be manually updated—potentially complicating deployment or patching if not managed via build scripts.
|
||||
- **COM visibility**: Though `ComVisible(false)` is set, the presence of a `Guid` attribute may cause confusion; this GUID is only relevant if `ComVisible(true)` is later enabled (e.g., for legacy interop), which is currently not the case.
|
||||
- **No internal documentation**: The empty `AssemblyDescription` field indicates no inline documentation exists at the assembly level; developers must consult external docs or source code.
|
||||
- **None identified from source alone.**
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
source_files:
|
||||
- DataPRO/Modules/RegionOfInterest/RegionOfInterestChannels/Resources/TranslateExtension.cs
|
||||
- DataPRO/Modules/RegionOfInterest/RegionOfInterestChannels/Resources/StringResources.Designer.cs
|
||||
generated_at: "2026-04-16T04:33:56.334065+00:00"
|
||||
model: "Qwen/Qwen3-Coder-Next-FP8"
|
||||
schema_version: 1
|
||||
sha256: "1d36778ed3ef747c"
|
||||
---
|
||||
|
||||
# Resources
|
||||
|
||||
## Documentation: `TranslateExtension` Markup Extension
|
||||
|
||||
### 1. Purpose
|
||||
This module provides a WPF `MarkupExtension` (`TranslateExtension`) to enable declarative, localized string resolution in XAML. It allows UI elements (e.g., `TextBlock.Text`, `Button.Content`) to bind to localized strings defined in `StringResources` by specifying a resource key. This supports internationalization of the Region of Interest (ROI) channels UI module by decoupling UI text from code and enabling culture-specific translations.
|
||||
|
||||
### 2. Public Interface
|
||||
- **`TranslateExtension` class**
|
||||
- **Inherits**: `System.Windows.Markup.MarkupExtension`
|
||||
- **Constructor**: `public TranslateExtension(string key)`
|
||||
- Initializes the extension with a resource key (`_key`).
|
||||
- Throws no exceptions; invalid keys are handled at runtime (see *Invariants* and *Gotchas*).
|
||||
- **Method**: `public override object ProvideValue(IServiceProvider serviceProvider)`
|
||||
- Returns the localized string corresponding to `_key` from `StringResources.ResourceManager`.
|
||||
- If `_key` is null/empty → returns `"#stringnotfound#"`.
|
||||
- If `_key` is non-empty but no matching resource exists → returns `"#stringnotfound# <key>"`.
|
||||
- Otherwise → returns the resolved string (e.g., `"Channel Name"` for key `"ChannelName"`).
|
||||
|
||||
### 3. Invariants
|
||||
- `_key` is immutable after construction (no setter, no modification).
|
||||
- The returned value is always a `string` (per `[MarkupExtensionReturnType(typeof(string))]`).
|
||||
- **Resource lookup behavior**:
|
||||
- `string.IsNullOrEmpty(_key)` → guaranteed to return `"#stringnotfound#"`.
|
||||
- Non-empty `_key` with no matching resource → guaranteed to return `"#stringnotfound# " + _key`.
|
||||
- `StringResources.ResourceManager.GetString(...)` is used directly (no caching of individual lookups beyond .NET’s internal `ResourceManager` caching).
|
||||
|
||||
### 4. Dependencies
|
||||
- **Internal dependencies**:
|
||||
- `RegionOfInterestChannels.Resources.StringResources` (strongly-typed resource class)
|
||||
- `System.Windows.Markup.MarkupExtension` (WPF framework)
|
||||
- `System.Resources.ResourceManager` (for runtime resource lookup)
|
||||
- **External dependencies**:
|
||||
- WPF runtime (for `MarkupExtension` and XAML integration).
|
||||
- `StringResources.resx` (source file for localized strings; not included, but implied by `StringResources.Designer.cs`).
|
||||
- **Used by**: XAML files in the `RegionOfInterestChannels` module (e.g., `TranslateExtension` is likely used as `{local:Translate SomeKey}`).
|
||||
|
||||
### 5. Gotchas
|
||||
- **No fallback to default culture**: If `StringResources.Culture` is set, lookups respect it; otherwise, the thread’s `CurrentUICulture` is used. No explicit culture fallback logic is implemented.
|
||||
- **Error strings are visible to users**: `"#stringnotfound#"` and `"#stringnotfound# <key>"` are returned verbatim—these are *not* silent failures and may appear in the UI if keys are misspelled or missing.
|
||||
- **No validation of key existence at compile time**: Keys are string literals; typos (e.g., `"ChannelName"` vs `"ChannelName "`) cause runtime errors.
|
||||
- **Thread-safety**: Relies on `StringResources.ResourceManager`’s thread-safety (assumed per .NET docs), but no explicit synchronization is present.
|
||||
- **No support for parameterized strings**: While `StringResources` contains format strings (e.g., `"Channel \"{0}\" has not been assigned..."`), `TranslateExtension` does *not* support passing arguments (e.g., no `string.Format`-style interpolation). Users must handle formatting separately.
|
||||
- **Auto-generated resource class**: `StringResources.Designer.cs` is auto-generated; manual edits are overwritten. Resource keys must match exactly with `.resx` entries.
|
||||
|
||||
None identified beyond the above.
|
||||
@@ -0,0 +1,172 @@
|
||||
---
|
||||
source_files:
|
||||
- DataPRO/Modules/RegionOfInterest/RegionOfInterestChannels/View/StateListIndexConverter.cs
|
||||
- DataPRO/Modules/RegionOfInterest/RegionOfInterestChannels/View/RegionOfInterestChannelsView.xaml.cs
|
||||
- DataPRO/Modules/RegionOfInterest/RegionOfInterestChannels/View/RegionOfInterestChannelsDataTemplateSelector.cs
|
||||
- DataPRO/Modules/RegionOfInterest/RegionOfInterestChannels/View/GridViewColumns.cs
|
||||
generated_at: "2026-04-16T04:34:31.801575+00:00"
|
||||
model: "Qwen/Qwen3-Coder-Next-FP8"
|
||||
schema_version: 1
|
||||
sha256: "b5797f8ba3342db7"
|
||||
---
|
||||
|
||||
# View
|
||||
|
||||
## Documentation: RegionOfInterestChannels View Layer
|
||||
|
||||
---
|
||||
|
||||
### 1. Purpose
|
||||
|
||||
This module provides WPF UI infrastructure for rendering and interacting with a `GridView`-based view of region-of-interest (ROI) channel data. It enables dynamic column generation from a data source, per-column type-aware cell templating (e.g., `TextBlock` vs. `CheckBox`), column header interactivity (sorting, filtering, multi-select), and automatic column width adjustment for content-fit. It serves as the view layer in an MVVM pattern, implementing `IRegionOfInterestChannelsView` and delegating user actions to an associated `IRegionOfInterestChannelsViewModel`.
|
||||
|
||||
---
|
||||
|
||||
### 2. Public Interface
|
||||
|
||||
#### `StateListIndexConverter`
|
||||
**Namespace:** `RegionOfInterestChannels`
|
||||
**Implements:** `IMultiValueConverter`
|
||||
|
||||
- **`Convert(object[] values, Type targetType, object parameter, CultureInfo culture)`**
|
||||
Converts a 2-element multi-value binding: expects `[0] = IEnumerable<State>`, `[1] = int index`. Returns the `State` at the given index in the collection, or `null` if inputs are invalid or index is out of bounds.
|
||||
**Note:** Does *not* support `ConvertBack`.
|
||||
|
||||
#### `RegionOfInterestChannelsView`
|
||||
**Namespace:** `RegionOfInterestChannels`
|
||||
**Implements:** `IRegionOfInterestChannelsView` (partial class)
|
||||
|
||||
- **`RegionOfInterestChannelsView()`**
|
||||
Constructor; calls `InitializeComponent()`.
|
||||
|
||||
- **`GridViewColumnHeader_OnClick(object sender, RoutedEventArgs e)`**
|
||||
Handles column header click events. Extracts the `Tag` from the clicked header (supporting `GridViewColumnHeaderSearchable` or `GridViewColumnHeaderSelectable` types), then invokes `vm.Sort(columnTag, true)` on the `DataContext` if it implements `IRegionOfInterestChannelsViewModel`.
|
||||
|
||||
- **`TextBlock_Loaded(object sender, RoutedEventArgs e)`**
|
||||
Adjusts column width dynamically when a `TextBlock` in a cell is loaded and its content width exceeds the column’s current width (only if `Width` is `double.NaN`). Finds the column index via visual tree traversal and temporarily sets `Width = ActualWidth`, then resets to `NaN` to trigger remeasurement.
|
||||
|
||||
#### `RegionOfInterestChannelsDataTemplateSelector`
|
||||
**Namespace:** `RegionOfInterestChannels`
|
||||
**Inherits:** `DataTemplateSelector`
|
||||
|
||||
- **`TextBlockDataTemplate` / `CheckBoxDataTemplate`**
|
||||
Public properties for the two templates to select between.
|
||||
|
||||
- **`SelectTemplate(object item, DependencyObject container)`**
|
||||
Selects a `DataTemplate` based on the cell’s underlying data type:
|
||||
- If `item` is a `ChannelEnabler`, inspects the column’s header (as property name) to determine the property type on `item`.
|
||||
- If type is `string` → returns `TextBlockDataTemplate`; sets `ContentPresenter.Tag` to the property value.
|
||||
- If type is `boolean` (or other) → returns `CheckBoxDataTemplate`; sets `ContentPresenter.Tag` to the *relative index* of the column among boolean columns (`i - checkStartColumn`).
|
||||
- If `item` is not a `ChannelEnabler`, defaults to `TextBlockDataTemplate`.
|
||||
**Note:** Uses reflection (`GetProperty`, `GetValue`, `GetType`) on the `item` and `presenter.Columns[i].Header`. Assumes header text matches property names (with spaces removed).
|
||||
|
||||
#### `GridViewColumns` (Static Class)
|
||||
**Namespace:** `RegionOfInterestChannels`
|
||||
**Purpose:** Attached property provider for configuring `GridView` columns dynamically from an `ICollectionView` source.
|
||||
|
||||
##### Attached Properties:
|
||||
- **`ColumnsSource`** (`object`)
|
||||
Source collection (typically `ICollectionView`) whose items define columns. Changing this clears and recreates columns.
|
||||
|
||||
- **`CellDataTemplateSelector`** (`DataTemplateSelector`)
|
||||
Optional selector used to choose per-cell templates.
|
||||
|
||||
- **`CellDataTemplate`** (`DataTemplate`)
|
||||
Fallback template for cells if `CellDataTemplateSelector` is `null`.
|
||||
|
||||
- **`HeaderTextMember`** (`string`)
|
||||
Property name on column source items whose value is used for column header text.
|
||||
|
||||
- **`DisplayMember`** (`string`)
|
||||
Property name on column source items whose value is used as the binding path for cell content (currently commented out in `CreateColumn`).
|
||||
|
||||
##### Key Methods (Internal/Static):
|
||||
- **`CreateColumn(GridView gridView, object columnSource)`**
|
||||
Constructs a `GridViewColumn` from a `columnSource` item:
|
||||
- Reads `HeaderTextMember` and `DisplayMember` from `gridView`.
|
||||
- Determines column header type based on the *column source item’s* `MemberType` property (retrieved via `GetPropertyValue(columnSource, "MemberType")`):
|
||||
- `typeof(bool)` → `GridViewColumnHeaderSelectable` with `SelectAll` event handler.
|
||||
- `typeof(string)` → `GridViewColumnHeaderSearchable` with `Search` and `ClickHandler` events.
|
||||
- Otherwise → plain `string` header.
|
||||
- Assigns `CellTemplateSelector`, `CellTemplate`, and (commented-out) `DisplayMemberBinding`.
|
||||
|
||||
- **`GetPropertyValue(object obj, string propertyName)`**
|
||||
Helper: uses reflection to get `propertyName` from `obj`.
|
||||
|
||||
- **`GetArrayIndex(string fromString)`**
|
||||
Parses an integer from a string like `"SomeProperty[3]"`, returning `-1` on failure.
|
||||
|
||||
##### Event Handlers (Internal):
|
||||
- **`ColumnsSource_CollectionChanged(...)`**
|
||||
Syncs `gridView.Columns` with changes (`Add`, `Remove`, `Replace`, `Move`, `Reset`) in the `ColumnsSource` collection.
|
||||
|
||||
- **`GridViewColumnHeaderSearchable_OnSearch(...)`**
|
||||
Invokes `vm.Filter(columnTag, searchTerm)`.
|
||||
|
||||
- **`GridViewColumnHeader_OnClick(...)`**
|
||||
Invokes `vm.Sort(columnTag, true)` (same behavior as `RegionOfInterestChannelsView.GridViewColumnHeader_OnClick`).
|
||||
|
||||
- **`GridViewColumnHeaderSelectable_OnSelectAll(...)`**
|
||||
Parses column index from `columnTag` (e.g., `"Enabled[2]"`), then invokes `vm.SelectAll(columnIndex, isSelected)`.
|
||||
|
||||
---
|
||||
|
||||
### 3. Invariants
|
||||
|
||||
- **Column Source Contract:**
|
||||
Items in the `ColumnsSource` collection must have:
|
||||
- A property named `"MemberType"` (e.g., `typeof(string)` or `typeof(bool)`), used to determine header type.
|
||||
- Properties named by `HeaderTextMember` and `DisplayMember` (if set), or default to `null`.
|
||||
|
||||
- **Header Tag Format:**
|
||||
For `GridViewColumnHeaderSelectable`, the `Tag` must be a string containing an array index in brackets (e.g., `"Enabled[0]"`) to be parsed by `GetArrayIndex`.
|
||||
|
||||
- **`StateListIndexConverter` Input Expectation:**
|
||||
Exactly two values: an `IEnumerable<State>` and an `int`. Any deviation yields `null`.
|
||||
|
||||
- **`ChannelEnabler` Assumption:**
|
||||
`RegionOfInterestChannelsDataTemplateSelector.SelectTemplate` assumes `item` is a `ChannelEnabler` when inspecting property types. Behavior for other types is not defined in source.
|
||||
|
||||
- **Column Width Adjustment Scope:**
|
||||
`TextBlock_Loaded` only adjusts the *first* matching `ContentPresenter` in the row. If multiple `TextBlock`s exist per row, only the first is considered.
|
||||
|
||||
---
|
||||
|
||||
### 4. Dependencies
|
||||
|
||||
#### Dependencies *of* this module:
|
||||
- **WPF Framework:** `System.Windows`, `System.Windows.Controls`, `System.Windows.Data`, `System.Windows.Media`.
|
||||
- **Common Libraries:**
|
||||
- `DTS.Common.Controls` (for `GridViewColumnHeaderSearchable`, `GridViewColumnHeaderSelectable`)
|
||||
- `DTS.Common.Interface.RegionOfInterest.RegionOfInterestChannels` (for `IRegionOfInterestChannelsView`, `IRegionOfInterestChannelsViewModel`)
|
||||
- `DTS.Common.Utils` (for `Utils.FindChild<T>`)
|
||||
|
||||
#### Dependencies *on* this module:
|
||||
- **`IRegionOfInterestChannelsViewModel`** must be implemented by the `DataContext` for `RegionOfInterestChannelsView` to handle sorting, filtering, and selection.
|
||||
- **`State` type** is used by `StateListIndexConverter` (imported from elsewhere in the codebase).
|
||||
- **`ChannelEnabler` type** is used by `RegionOfInterestChannelsDataTemplateSelector` (imported from elsewhere).
|
||||
|
||||
---
|
||||
|
||||
### 5. Gotchas
|
||||
|
||||
- **`ConvertBack` is Unimplemented:**
|
||||
`StateListIndexConverter.ConvertBack` throws `NotImplementedException`. It is strictly a one-way converter.
|
||||
|
||||
- **`DisplayMemberBinding` is Commented Out:**
|
||||
In `CreateColumn`, the line setting `column.DisplayMemberBinding` is commented. Cell binding relies entirely on `CellDataTemplateSelector` or `CellDataTemplate`. This may cause confusion if `DisplayMember` is set but unused.
|
||||
|
||||
- **Header Property Name Matching:**
|
||||
`RegionOfInterestChannelsDataTemplateSelector.SelectTemplate` uses `presenter.Columns[i].Header.ToString().Replace(" ", "")` to match property names. If headers contain non-alphanumeric characters beyond spaces, reflection will fail.
|
||||
|
||||
- **`MemberType` Property Requirement:**
|
||||
Column source items *must* expose a `"MemberType"` property (via reflection). If missing or non-`Type`, `columnPropertyType` becomes `null`, causing `checkStartColumn` to be set and defaulting to `CheckBoxDataTemplate`.
|
||||
|
||||
- **Duplicate Event Handlers Risk:**
|
||||
`ColumnsSource_CollectionChanged` adds handlers to `ICollectionView.CollectionChanged` but does not deduplicate if `ColumnsSource` is reassigned to the *same* collection instance. This could lead to multiple handler invocations per event.
|
||||
|
||||
- **`TextBlock_Loaded` Fragility:**
|
||||
Relies on a specific visual tree structure (`GridViewRowPresenter` → `ContentPresenter` → `TextBlock`). Changes to control templates may break the width adjustment logic.
|
||||
|
||||
- **No Null-Safety for `e?.OriginalSource`:**
|
||||
In `GridViewColumnHeaderSelectable_OnSelectAll`, `(bool)(e?.OriginalSource ?? false)` may throw `InvalidCastException` if `e.OriginalSource` is not a `bool` or `null`.
|
||||
@@ -0,0 +1,227 @@
|
||||
---
|
||||
source_files:
|
||||
- DataPRO/Modules/RegionOfInterest/RegionOfInterestChannels/ViewModel/RegionOfInterestChannelsViewModel.cs
|
||||
generated_at: "2026-04-16T04:34:24.697647+00:00"
|
||||
model: "Qwen/Qwen3-Coder-Next-FP8"
|
||||
schema_version: 1
|
||||
sha256: "2e798c0127d02e4a"
|
||||
---
|
||||
|
||||
# ViewModel
|
||||
|
||||
**Documentation: RegionOfInterestChannelsViewModel**
|
||||
|
||||
---
|
||||
|
||||
### 1. **Purpose**
|
||||
|
||||
This module implements the `RegionOfInterestChannelsViewModel`, a core view model for managing and displaying channels associated with Regions of Interest (ROIs) in the DTS test configuration and analysis system. It serves as the data context for the ROI Channels UI, enabling users to view, filter, sort, and select channels for inclusion in one or more ROIs. The view model bridges UI interactions with underlying channel data (from `IGroupChannel`, `ITestChannel`, or test metadata), handles ROI channel assignment validation (e.g., scrubbing stale channel references), and coordinates with the event aggregator for cross-component communication (e.g., busy indicators, notifications). It is used in contexts such as ROI definition, CSV/HDF export, and data download workflows.
|
||||
|
||||
---
|
||||
|
||||
### 2. **Public Interface**
|
||||
|
||||
#### **Constructor**
|
||||
```csharp
|
||||
RegionOfInterestChannelsViewModel(
|
||||
IRegionOfInterestChannelsView view,
|
||||
Prism.Regions.IRegionManager regionManager,
|
||||
Prism.Events.IEventAggregator eventAggregator,
|
||||
Unity.IUnityContainer unityContainer)
|
||||
```
|
||||
- Initializes the view model, sets up data binding, registers event subscriptions (`RaiseNotification`, `BusyIndicatorChangeNotification`), and initializes `RegionsOfInterest` as a `BindingList<IRegionOfInterest>`.
|
||||
|
||||
#### **Properties**
|
||||
- `bool IsDirty { get; private set; }`
|
||||
Indicates whether the underlying data has unsaved changes. *Currently always `false` in source.*
|
||||
|
||||
- `bool IsBusy { get; set; }`
|
||||
Binds to UI busy indicator state. Set via `OnBusyIndicatorNotification`.
|
||||
|
||||
- `bool IsMenuIncluded { get; set; }`
|
||||
- `bool IsNavigationIncluded { get; set; }`
|
||||
UI layout flags for optional navigation elements.
|
||||
|
||||
- `IRegionOfInterestChannelsView View { get; set; }`
|
||||
Reference to the associated view.
|
||||
|
||||
- `InteractionRequest<Notification> NotificationRequest { get; }`
|
||||
- `InteractionRequest<Confirmation> ConfirmationRequest { get; }`
|
||||
Prism Interactivity triggers for modal dialogs.
|
||||
|
||||
- `BindingList<IRegionOfInterest> RegionsOfInterest { get; set; }`
|
||||
List of ROI definitions. On setter, performs scrubbing of stale channel references (see *Gotchas*), then calls `ResetDataView()`.
|
||||
|
||||
- `List<DTS.Common.Classes.Groups.GroupChannel> AllChannelsUnfiltered { get; set; }`
|
||||
Raw list of all channels (from `IGroupChannel[]`) before filtering.
|
||||
|
||||
- `ObservableCollection<GroupChannel> AllChannels { get; set; }`
|
||||
Filtered list of `GroupChannel` instances displayed in the UI.
|
||||
|
||||
- `ObservableCollection<ITestChannel> AllTestChannels { get; set; }`
|
||||
Filtered list of `ITestChannel` instances (used when loading from test summary/metadata).
|
||||
|
||||
- `string[] AllChannelSSNs { get; }`
|
||||
Returns array of `Hardware\SerialNumber` strings for all *enabled* channels (used for ROI export). Handles both `GroupChannel` and `ITestChannel` sources.
|
||||
|
||||
- `BindingList<ChannelEnabler> ChannelList { get; set; }`
|
||||
List of `ChannelEnabler` objects representing UI rows (one per channel), each containing ROI inclusion checkboxes (`ROIIncludes`). Setter wires `ListChanged` event.
|
||||
|
||||
- `ObservableCollection<ColumnDescriptor> Columns { get; set; }`
|
||||
Defines grid column metadata (header, display member, type) for `ChannelList`.
|
||||
|
||||
- `IsoViewMode ISOViewMode { get; set; }`
|
||||
Controls which channel name variant is used (e.g., ISO, User Code, or raw).
|
||||
|
||||
#### **Methods**
|
||||
- `void SetParent(object o)`
|
||||
Stores `Parent` object (used in event args for ROI channel selection events).
|
||||
|
||||
- `void SetTest(string path, IsoViewMode viewMode)`
|
||||
Loads test metadata from `.dts` file at `path`, populates `_testSummary`, and triggers `Filter()` and `ResetDataView()`.
|
||||
|
||||
- `void SetGroups(ITestSetup testSetup, Dictionary<string, IDASHardware> serialNumberToHardware, IsoViewMode viewMode)`
|
||||
Initializes channel data from `ITestSetup` (e.g., during ROI definition). Validates DAS assignments, logs phantom assignments, sorts channels, and calls `ProcessChannels()` → `Filter()` → `ResetDataView()`.
|
||||
|
||||
- `void Filter(string term)` / `void SetFilter(PossibleFilters bridgeFilter)` / `void Filter(object tag, string term)`
|
||||
Set search term, filter type, or field-specific filter term, then invoke `Filter()`.
|
||||
|
||||
- `void ClearAllFilters()`
|
||||
Clears `_filterByField` dictionary.
|
||||
|
||||
- `void Sort(object o, bool bColumnClick)`
|
||||
Sorts `ChannelList` by field (e.g., `GroupName`, `SerialNumber`, or `ROIIncludes[i]`). Supports toggling sort direction on repeated clicks.
|
||||
|
||||
- `void SelectAll(int roiIndex, bool selection)`
|
||||
Sets `Checked` state of all `ROIIncludes[roiIndex]` checkboxes to `selection`.
|
||||
|
||||
- `void Activated()` / `void Cleanup()` / `Task CleanupAsync()` / `void Initialize()` / `Task InitializeAsync()`
|
||||
Lifecycle stubs (no-op in source).
|
||||
|
||||
- `bool Validate(ref List<string> errors)`
|
||||
Currently always returns `true`.
|
||||
|
||||
- `void OnPropertyChanged(string propertyName)`
|
||||
Raises `PropertyChanged` event.
|
||||
|
||||
---
|
||||
|
||||
### 3. **Invariants**
|
||||
|
||||
- **Channel scrubbing on ROI assignment change**: When `RegionsOfInterest` is set, stale channel references (channels no longer present in `AllChannelsUnfiltered`) are removed from `roi.ChannelNames` and `roi.ChannelIds`. This is done by comparing against `AllChannelHash` (computed from `AllChannelsUnfiltered` with hardware/serial number normalization) and `allChannelIdHash`.
|
||||
|
||||
- **Channel ID parsing**: Older `.dts` files may have composite `ChannelId` strings (e.g., `"H3-3ch_0_2"`). `ParseChannelId()` extracts the trailing numeric ID; if parsing fails, returns `-1`.
|
||||
|
||||
- **Digital outputs excluded**: In `BuildChannelListFromGroupChannels()`, channels with `sd.IsDigitalOutput()` or `sd.IsTestSpecificDigitalOutput()` are skipped.
|
||||
|
||||
- **Filtering semantics**:
|
||||
- `ChannelFilter()` uses *AND* logic across multiple filter fields (a channel must match *all* active filters).
|
||||
- `ChannelSearch()` uses *OR* logic (a channel matches if *any* field contains the search term).
|
||||
- `ROIIncludes` is excluded from search (commented as "not meaningful").
|
||||
|
||||
- **Hardware/serial normalization**: Channel descriptors use `RegionOfInterest.RemoveParentDASName()` and `RegionOfInterest.RemoveAssignedByIDFromHardwareString()` to normalize hardware strings.
|
||||
|
||||
- **Calibration caching**: `BuildChannelListFromGroupChannels()` caches sensor calibrations (`cals`) to avoid repeated DB lookups.
|
||||
|
||||
---
|
||||
|
||||
### 4. **Dependencies**
|
||||
|
||||
#### **Imports/Usings (External Dependencies)**
|
||||
- **Prism**: `IRegionManager`, `IEventAggregator`, `InteractionRequest<T>`, `PopupWindowAction` (via `NotificationRequest`).
|
||||
- **Unity**: `IUnityContainer`.
|
||||
- **DTS Common Libraries**:
|
||||
- `DTS.Common.Classes.Groups`, `DTS.Common.Enums`, `DTS.Common.Events.*`, `DTS.Common.Interface.*`, `DTS.Common.Utils`, `DTS.Common.Utilities.Logging.APILogger`.
|
||||
- `DTS.SensorDB` (for `SensorsCollection`, `SensorCalibrationList`).
|
||||
- `DTS.Serialization.SliceRaw` (for `PersistentChannel.GetIsoCode`).
|
||||
- **System**: `BindingList<T>`, `ObservableCollection<T>`, `INotifyPropertyChanged`, `IComparer<T>`.
|
||||
|
||||
#### **Internal Dependencies**
|
||||
- **Interfaces**:
|
||||
- `IRegionOfInterestChannelsView`, `IRegionOfInterest`, `IGroupChannel`, `ITestChannel`, `IDASHardware`, `IGroup`, `ITestSetup`, `ITestSummary`.
|
||||
- **Helper Types**:
|
||||
- `ChannelSerialNumber`, `HardwareConstants`, `RegionOfInterest`, `DTS.Common.Constants.CURRENT_SUFFIX`.
|
||||
- `ChannelEnabler`, `State`, `ColumnDescriptor`, `ChannelEnablerComparer` (defined in same file).
|
||||
- **Events**:
|
||||
- `RaiseNotification`, `BusyIndicatorChangeNotification`, `RegionOfInterestChannelsSelectedEvent`, `PageErrorEvent`.
|
||||
|
||||
#### **Depended Upon By**
|
||||
- UI layer (`IRegionOfInterestChannelsView` implementations).
|
||||
- Event subscribers (e.g., `RegionOfInterestChannelsSelectedEvent` listeners).
|
||||
- Export/download workflows (`SetTest()`, `SetGroups()`).
|
||||
|
||||
---
|
||||
|
||||
### 5. **Gotchas**
|
||||
|
||||
- **`AllChannels` null handling**: `AllChannelSSNs` and `BuildChannelListFromSummary()` guard against `AllChannels` being `null` (e.g., when loaded via export tile), but `BuildChannelListFromGroupChannels()` does *not* check `AllChannels` for `null` before iteration (relies on `?? new ObservableCollection<>()` in `SetGroups()`).
|
||||
|
||||
- **`ROIIncludes` index mismatch**: In `CheckboxesOnListChanged`, `LastIndexChanged` is used to infer the ROI index, but this relies on the `ListChanged` event’s `NewIndex` matching the ROI index *at the time of change*. If `ChannelList` is reordered, this index may become stale.
|
||||
|
||||
- **`ParseChannelId` edge case**: If `parsedChannelId` is non-numeric (e.g., `"f9f0bfe8-afc4-4730-8045-8f1e45340573_0_8533"` → `"8533"` is numeric, but `"f9f0bfe8-afc4-4730-8045-8f1e45340573"` is not), `long.TryParse` fails and returns `-1`. This may cause ROI channel assignment to silently drop channels.
|
||||
|
||||
- **`UpdateChannelList` heuristic**: This method replaces a channel in `channelList` if its serial number matches *any* channel in `AllChannelsUnfiltered`. This may incorrectly overwrite channels if multiple channels share the same serial number (e.g., same sensor type on different hardware).
|
||||
|
||||
- **`IsDirty` unused**: The property is defined but never set to `true`, making it unreliable for change tracking.
|
||||
|
||||
- **Calibration fallback**: In `BuildChannelListFromGroupChannels()`, if `sd.Calibration?.Records?.Records?[0]?.EngineeringUnits` is `null`, `"N/A"` is used. However, `ChannelFilter()` for `DisplayUnits` uses a *separate* `GetLatestCalibrationBySerialNumber(sd)` call, which may yield different results if calibrations change mid-session.
|
||||
|
||||
- **`ChannelSearch` redundancy**: `ChannelSearch(GroupChannel)` and `ChannelSearch(ITestChannel)` have overlapping logic, but `ChannelSearch(GroupChannel)` skips `Fields.DisplayName` (commented as "redundant"), while `ChannelSearch(ITestChannel)` does not.
|
||||
|
||||
- **`ChannelEnablerComparer` sort stability**: Sorting is case-insensitive but not culture-aware (`StringComparison.InvariantCultureIgnoreCase`). May produce unexpected orderings for non-ASCII characters.
|
||||
|
||||
- **`SetTest()` assumes single test**: `SetTest()` loads `tsl[0]` unconditionally; if `tsl` is empty, this will throw `IndexOutOfRangeException`.
|
||||
|
||||
- **`ROIChannelEnabler` unused**: A `ROIChannelEnabler` class is defined but never instantiated or used in the source.
|
||||
|
||||
- **`IsBusy` thread affinity**: `OnBusyIndicatorNotification` subscribes with `ThreadOption.PublisherThread`, but `IsBusy` setter calls `OnPropertyChanged("IsBusy")`, which may raise `PropertyChanged` on the publisher thread (UI thread). If `IsBusy` is set from a background thread elsewhere, this could cause cross-thread exceptions (though not evident in current usage).
|
||||
|
||||
- **`AllChannelSSNs` for CSV export**: The comment `//13477 Crash when clicking + to add second ROI for CSV export` implies a known race condition or null-state issue when dynamically adding ROIs during export. The guard (`null != AllChannels && AllChannels.Any()`) mitigates but does not eliminate all failure modes.
|
||||
|
||||
- **No validation of ROI channel count**: `SelectAll(int roiIndex, bool selection)` checks bounds on `roiIndex`, but no validation ensures `roiIndex` corresponds to a valid ROI in `_regionsOfInterest` *after* `RegionsOfInterest` is reassigned.
|
||||
|
||||
- **`ChannelEnabler.LastIndexChanged` reset**: `LastIndexChanged` is set on `ListChanged`, but never reset. If multiple changes occur rapidly, it may retain an outdated index.
|
||||
|
||||
- **`ChannelFilter` for `SampleRate`**: Uses `ch.TestSampleRate.ToString()` (no format specifier), while `ChannelSearch` uses both `ToString()` and `ToString("N")`. Inconsistent formatting may cause filter mismatches.
|
||||
|
||||
- **`ChannelFilter` for `DisplayUnits`**: Uses `GetLatestCalibrationBySerialNumber(sd)` *without* caching, potentially causing repeated DB lookups in `Filter()` loops.
|
||||
|
||||
- **`ChannelFilter` for `DASSerialNumber`**: Uses `ch.HardwareChannel?.GetParentDAS().SerialNumber ?? "N/A"`, but `ChannelFilter(ITestChannel)` uses `ch.HardwareChannelName` directly (no `GetParentDAS()`), leading to inconsistent behavior.
|
||||
|
||||
- **`ChannelEnabler.Descriptor` format**: `Descriptor = chHardware + "\\" + serialNumber` assumes `chHardware` and `serialNumber` are non-null; if either is `null`, this may produce `"\\serial"` or `"hardware\\"`.
|
||||
|
||||
- **`RegionOfInterestChannelsSelectedEvent` publish**: Published in `CheckboxesOnListChanged` *after* updating `_regionsOfInterest[roiChangedIndex]`, but the event args use `_regionsOfInterest[roiChangedIndex]` *after* the list has been modified. If `roiChangedIndex` is stale (see *index mismatch*), this may publish stale ROI data.
|
||||
|
||||
- **`SetGroups()` phantom DAS assignment**: Logs an error for `ch.DASChannelIndex < 0`, but continues processing (does not skip the channel). May lead to inconsistent channel sample rates.
|
||||
|
||||
- **`BuildChannelListFromGroupChannels()` sensor lookup**: If `sd` is `null` after lookup, logs and skips the channel. However, `ch.SensorData` is *not* assigned in this case, so subsequent calls may repeat the lookup.
|
||||
|
||||
- **`ChannelEnabler.GetChannelName()`**: Returns `SerialNumber` as fallback for missing `UserChannelName`/`ISOChannelName`, but `AllChannelSSNs` uses `Hardware + "\\" + serialNumber`. Inconsistent naming may cause mismatches in ROI channel matching.
|
||||
|
||||
- **`ChannelEnablerComparer` null safety**: Compares `aValue?.ToString()` and `bValue?.ToString()`, but if both are `null`, returns `0` (equal). If only one is `null`, `string.Compare(null, "x")` returns `-1`, which may not be intuitive.
|
||||
|
||||
- **`ChannelEnabler.ROIIncludes` event wiring**: `ROIIncludes.ListChanged` is wired in constructor, but `ChannelEnabler` instances are recreated on every `ChannelList = ...` assignment. The old `BindingList<State>` may not be disposed, potentially causing memory leaks if event handlers are not properly detached (though `ListChanged` is not explicitly unhooked in setter).
|
||||
|
||||
- **`ChannelEnabler.LastIndexChanged` property**: Used only to trigger `OnPropertyChanged("LastIndexChanged")`, but no UI binding or logic consumes this property. Likely tech debt.
|
||||
|
||||
- **`RegionOfInterestChannelsViewModel` `Parent` field**: Stored as `object Parent` (not typed), and passed in `RegionOfInterestChannelsSelectedEventArgs`. No validation ensures `Parent` is of expected type.
|
||||
|
||||
- **`ChannelFilter` for `ROIIncludes` field**: The `Fields.ROIIncludes` case in `ChannelFilter` throws `ArgumentOutOfRangeException`. This field is *not* filterable (as intended), but the enum value is included in `Enum.GetValues`, so the switch must handle it (currently via `default: throw`).
|
||||
|
||||
- **`ChannelSearch` for `SampleRate`**: Uses `ch.SampleRateHz.ToString()` and `ToString("N")`, but `ToString("N")` includes thousand separators (e.g., `"1,000"`), which may not match user input like `"1000"`.
|
||||
|
||||
- **`ChannelEnabler` `GuidString`**: Generated via `Guid.NewGuid()` on every `ChannelList` assignment. Not persisted across sessions or used for cross-reference (only in `CheckboxesOnListChanged` for lookup). Likely a temporary ID with no semantic meaning.
|
||||
|
||||
- **`ChannelEnabler` `ChannelId`**: Set to `Convert.ToInt64(ch.Id)` (from `IGroupChannel.Id`), but `ch.Id` is a `string`. If `ch.Id` is non-numeric, `Convert.ToInt64` throws `FormatException`. *This is a critical risk.* (Note: `ParseChannelId` is used only for `ITestChannel.ChannelId`.)
|
||||
|
||||
- **`ChannelEnabler` `ChannelId` for `ITestChannel`**: Uses `ParseChannelId(ch.ChannelId)`, which may return `-1` for invalid IDs. No validation ensures `-1` is not a valid channel ID elsewhere.
|
||||
|
||||
- **`ChannelEnabler` `ChannelId` for `IGroupChannel`**: Uses `Convert.ToInt64(ch.Id)`, which may throw `FormatException` if `ch.Id` is non-numeric (e.g., GUID-based IDs). *This is inconsistent with `ITestChannel` handling and a likely source of crashes.*
|
||||
|
||||
- **`ChannelEnabler` `ChannelId` for ROI matching**: In `CheckboxesOnListChanged`, `ChannelId` is used to build `checkedIds`, but ROI `ChannelIds` may contain IDs from `ParseChannelId` (which may be `-1`), while `IGroupChannel.Id` may throw. This mismatch could cause ROI channel assignments to fail silently.
|
||||
|
||||
- **`ChannelEnabler` `ChannelId` for `AllChannelSSNs`**: Uses `ch.Id` (from `GroupChannel`), but `AllChannelSSNs` does *not* use `ChannelId`—it uses `Hardware + "\\" + serialNumber`. Thus, ROI channel matching relies on `ChannelId`, but export relies on string-based descriptors. Inconsistency may cause mismatches.
|
||||
|
||||
- **`ChannelEnabler` `ChannelId` for `ParseChannelId`**: The `ParseChannelId` method is *only* used for `ITestChannel.ChannelId`, but `ChannelEnabler.ChannelId` is set from `IGroupChannel.Id` via `Convert.ToInt64(ch.Id)`. This means ROI channel IDs may be inconsistent between test metadata (`ParseChannelId`) and group channels (`Convert.ToInt64`).
|
||||
|
||||
- **`ChannelEnabler` `ChannelId` for `ChannelFilter`**: The `Channel
|
||||
Reference in New Issue
Block a user