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,209 @@
---
source_files:
- DataPRO/Modules/Groups/GroupChannelList/ViewModel/GroupChannelListViewModel.cs
generated_at: "2026-04-16T04:46:34.697931+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "7af2406ccff6580f"
---
# ViewModel
**Documentation Page: `GroupChannelListViewModel`**
---
### **1. Purpose**
The `GroupChannelListViewModel` class serves as the core view model for managing channel assignments within the *GroupChannelList* UI module. It orchestrates the display, editing, and synchronization of channel data—linking logical channel definitions (e.g., ISO/user codes, sensor assignments, hardware mappings) to physical test hardware and sensor metadata. It supports operations such as bulk import via text paste, drag-and-drop assignment of sensors and hardware channels, range synchronization across channels on the same DAS, and real-time filtering/sorting. It acts as the intermediary between the view (`IGroupChannelListView`, `IGroupChannelSettingsListView`) and domain models (`IGroup`, `ITestSetup`, `ISensorData`, `IHardwareChannel`), publishing events for UI updates and system-wide state changes.
---
### **2. Public Interface**
#### **Constructor**
```csharp
public GroupChannelListViewModel(
IGroupChannelListView view,
IGroupChannelSettingsListView settingsView,
IRegionManager regionManager,
IEventAggregator eventAggregator,
IUnityContainer unityContainer)
```
- Initializes the view model, wires up views, sets up interaction requests (`NotificationRequest`, `ConfirmationRequest`), and subscribes to events (`RaiseNotification`, `BusyIndicatorChangeNotification`, `TextPastedEvent`).
- Initializes `SelectedChannelItems` as an `ObservableCollection<IGroupChannel>`.
#### **Properties**
| Property | Type | Description |
|---------|------|-------------|
| `View` | `IGroupChannelListView` | Reference to the main channel list view. |
| `SettingsView` | `IGroupChannelSettingsListView` | Reference to the channel settings view. |
| `NotificationRequest` | `InteractionRequest<Notification>` | Prism interaction request for displaying notifications. |
| `ConfirmationRequest` | `InteractionRequest<Confirmation>` | Prism interaction request for confirmation dialogs. |
| `PropertyChanged` | `event PropertyChangedEventHandler` | Standard `INotifyPropertyChanged` event. |
| `ChannelCount` | `int` | Derived from `AllChannels.Count`; triggers `GroupChannelsChangedEvent` on change. |
| `AllChannels` | `IList<IGroupChannel>` | *Internal* full list of channels (including blank trailing channel). |
| `Channels` | `IList<IGroupChannel>` | *Internal* list of non-blank channels (used for UI rendering). |
| `SettingChannels` | `ObservableCollection<IGroupChannel>` | Sorted, filtered list of non-blank channels for settings view. |
| `SelectedChannelItems` | `ObservableCollection<IGroupChannel>` | Currently selected channels (for multi-select operations). |
| `SearchTerm` | `string` | Current search filter term. |
| `BridgeFilter` | `PossibleFilters` | Bridge-type filter (e.g., `Analog`, `Squib`, `DigitalIn`). |
| `UseTestSetupOrder` | `bool` | Whether channels are ordered by `TestSetupOrder` (vs `GroupChannelOrder`). |
| `ISOViewMode` | `bool` | Controls display of ISO/user fields. |
| `ShowSensorChannelUserValues` | `bool` | Controls visibility of sensor/user-specific fields. |
| `AllowSensorPushAndPull` | `bool` | Enables sensor parameter comparison and push/pull UI. |
| `UserIsAdmin` | `bool` | Admin status for permission checks. |
| `AllowChannelDeletionByNonAdminUser` | `bool` | Whether non-admin users can delete channels. |
| `AllowChannelDeletionFromFixedGroup` | `bool` | Whether channels in fixed groups can be deleted. |
| `IsBusy` | `bool` | Bound to busy indicator via `BusyIndicatorChangeNotification`. |
| `Group` | `IGroup` | *Set externally*; the group currently being edited. |
| `TestSetup` | `ITestSetup` | *Set externally*; the test setup currently being edited. |
| `Page` | `object` | *Set externally*; the page context (e.g., `GroupsPage`, `TestSetupsPage`). |
#### **Key Methods**
| Method | Signature | Description |
|--------|-----------|-------------|
| `UpdateRangeLowG` | `public void UpdateRangeLowG(IGroupChannel channelChanged)` | Propagates `RangeLowG` to all other channels on the same DAS that have `RangeModifiableSensorLowG = true`. |
| `UpdateRangeARS` | `public void UpdateRangeARS(IGroupChannel channelChanged)` | Propagates `Range` to all other channels on the same DAS that have `RangeModifiableSensorARS = true`. |
| `UpdateACCouplingEnabled` | `public void UpdateACCouplingEnabled(IGroupChannel channelChanged)` | Propagates `ACCouplingEnabled` to other channels on the same DAS (casts to `GroupChannel`). |
| `DoSensorAssignment` | `public void DoSensorAssignment(IGroupChannel groupChannel, IDragAndDropItem[] sensors)` | Assigns sensors to consecutive channels starting at `groupChannel`. Skips assignment to TSR-AIR channels (except StreamOut/UART). Adds new channels if needed. |
| `DoHardwareAssignment` | `public void DoHardwareAssignment(IGroupChannel groupChannel, IHardwareChannel[] hardwareChannels)` | Assigns hardware channels to consecutive channels. Skips assignment to non-blank/non-StreamOut/UART channels if source is TSR-AIR. |
| `OnTextPasted` | `public void OnTextPasted(ITextPastedEventArgs args)` | Handles paste events (ID = `GroupChannel.PASTE_ID`). Parses tab/semicolon/comma-delimited text into channels, inserting/updating starting at the senders position. |
| `ParseText` | `private IEnumerable<IGroupChannel> ParseText(string text, object tag, out bool oneColumn)` | Parses raw text into `IGroupChannel` objects. Supports column mapping via `tag` (e.g., `"GroupName"`, `"ISOCode"`). Handles sensor/hardware lookup via dictionaries. |
| `GetSensorSerialNumber` | `private string GetSensorSerialNumber(string channelSensor)` | Extracts serial number from sensor string (e.g., `"strain gauge 1 (SG1)"``"SG1"`). |
| `CreateGroupIfNeeded` | `public IGroup CreateGroupIfNeeded(ITestSetup testSetup, string groupName)`<br>`private IGroup CreateGroupIfNeeded(string groupName)` | Creates a new group if none exists for `groupName` under `TestSetup`. |
| `OnSetActive` | `public void OnSetActive()` | Called when view becomes active. Updates channel group names, compares sensor parameters (`CompareAndMarkChannelParameters`), refreshes view settings/columns, and resets sort/filter state. |
| `CompareAndMarkChannelParameters` | `public bool CompareAndMarkChannelParameters(IGroupChannel ch)` | Compares channel parameters (e.g., `Range`, `FilterClass`, `SquibFireMode`) against sensor DB defaults. Marks channel as *different* if mismatched. Returns `true` if any change detected. |
| `ResetSettingChannels` | `private void ResetSettingChannels()` | Rebuilds `SettingChannels` from `Channels`, applying sort (via `GroupChannelComparer`), filtering blanks, and setting `DigitalOutDurationMax` for TOM hardware. |
| `Unset` | `public void Unset()` | Clears all internal state: `AllChannels`, `Channels`, sensor/hardware lookup dictionaries, filters. |
| `ClearAllFilters` | `public void ClearAllFilters()` | Resets `SearchTerm`, `BridgeFilter`, `_filterByField`, `_dontFilterList`, and publishes `ListViewStatusEvent.Unloaded`. |
| `PopulateChannels` | `public IDictionary<IGroup, IGroupChannel[]> PopulateChannels(...)` | **Core initialization method.** Loads channels from `Group` or `TestSetup`, populates lookup dictionaries (`_idToSensorDictionary`, `_displayToHardwareChannel`), sets up channel metadata (e.g., `RemoveSensorVisibility`, `DeleteShouldBeEnabled`), and returns `ChannelsForGroup` mapping. |
| `MarkModified` | `public void MarkModified(IGroupChannel channel, bool bNotifyChanged = true)` | Handles adding a new blank channel after `channel` (if its the last non-blank), updates ordering, and ensures group assignment. |
| `Remove` | `public void Remove(IGroupChannel channel, bool notifyChanged = true)` | Removes `channel`, adds a new blank channel if needed, updates group/channel mappings, and triggers `NotifyChannelsChanged`. |
| `NotifyChannelsChanged` | `public void NotifyChannelsChanged()` | Publishes `PageModifiedEvent` and `GroupUpdatedEvent`, and updates `ChannelCount`/`AssignedPhysicalChannelCount`. |
| `GroupNameChanged` | `public void GroupNameChanged(IGroupChannel channel)` | Updates `TestSetup.ChannelsForGroup` when a channels group name changes (removes from old group, adds to new). |
| `ReportErrors` | `public void ReportErrors(string[] errors)` | Publishes `PageErrorEvent` with error messages. |
| `Clear` | `public void Clear(IGroupChannel channel)` | Calls `channel.Clear()` (resets channel fields to defaults). |
| `OnBusyIndicatorNotification` | `private void OnBusyIndicatorNotification(bool eventArg)` | Event handler for `BusyIndicatorChangeNotification`; sets `IsBusy`. |
| `OnRaiseNotification` | `private void OnRaiseNotification(NotificationContentEventArgs eventArgsWithTitle)` | Event handler for `RaiseNotification`; wraps message and title into `Notification` for UI. |
#### **Event Handlers (Private)**
- `OnTextPasted`: Handles paste events.
- `OnBusyIndicatorNotification`: Updates `IsBusy`.
- `OnRaiseNotification`: Triggers `NotificationRequest`.
#### **Event Properties (Public)**
- `PropertyChanged`: Standard property change notification.
---
### **3. Invariants**
- **Channel Ordering**:
- `AllChannels` always ends with a blank `GroupChannel` (used for adding new channels).
- `Channels` is a subset of `AllChannels` containing only non-blank channels.
- Channel ordering is maintained via `TestSetupOrder` (if `UseTestSetupOrder`) or `GroupChannelOrder`.
- `DetermineButtonState()` enforces `CanMoveUp`/`CanMoveDown` constraints (first channel cannot move up; last non-blank cannot move down).
- **Group/Hardware Consistency**:
- Channels are associated with a `Group` (from `Group` or `TestSetup`) and `HardwareChannel`.
- `GroupChannel.GroupName` must match `Group.DisplayName`.
- Hardware assignments to TSR-AIR channels are restricted (only to blank, StreamOut, or UART channels).
- **Sensor/Hardware Lookup**:
- `_idToSensorDictionary`, `_serialNumberToSensorDictionary`, and `_displayToHardwareChannel` must be populated before `PopulateChannels` completes.
- Sensor serial numbers are extracted from strings (e.g., `"name (SN)"``"SN"`) using `GetSensorSerialNumber`.
- **Filtering**:
- `_dontFilterList` preserves channels during filtering (e.g., after drag/drop or paste).
- `Filter()` respects `BridgeFilter`, `SearchTerm`, and per-field filters (`_filterByField`).
- **Notification & Events**:
- `OnPropertyChanged("ChannelCount")` triggers `GroupChannelsChangedEvent`.
- `MarkModified`, `Remove`, `NotifyChannelsChanged` publish `PageModifiedEvent` and `GroupUpdatedEvent`.
---
### **4. Dependencies**
#### **External Dependencies (Imports/Usings)**
- **Prism Framework**: `IEventAggregator`, `IRegionManager`, `UnityContainer`, `InteractionRequest<T>`, `DelegateCommand`.
- **DTS Common Libraries**:
- `DTS.Common.Classes.Groups`
- `DTS.Common.Enums` (e.g., `PossibleFilters`, `Fields`, `HardwareTypes`)
- `DTS.Common.Interface.*` (e.g., `IGroupChannel`, `ISensorData`, `IHardwareChannel`)
- `DTS.Common.Events.*` (e.g., `TextPastedEvent`, `PageErrorEvent`)
- `DTS.Common.Storage`, `DTS.Common.Converters`, `DTS.Common.Interactivity`
#### **Key Dependencies**
- **Views**: `IGroupChannelListView`, `IGroupChannelSettingsListView`.
- **Services**: `IEventAggregator`, `IUnityContainer`, `IRegionManager`.
- **Data Sources**: `ITestSetup`, `IGroup`, `ISensorData`, `IDASHardware`, `IChannelSetting`.
- **Events Published**:
- `GroupChannelsChangedEvent`
- `PageModifiedEvent`
- `GroupUpdatedEvent`
- `PageErrorEvent`
- `ListViewStatusEvent`
- `AppStatusEvent`
- `RaiseNotification`
#### **Dependents**
- `GroupChannelList` view (binds to `View`, `SettingsView`).
- Other modules via `GroupChannelsChangedEvent`, `PageModifiedEvent`, `GroupUpdatedEvent`.
---
### **5. Gotchas**
- **TSR-AIR Channel Restrictions**:
- Sensor/hardware assignments are blocked for non-StreamOut/UART TSR-AIR channels (see `DoSensorAssignment`, `DoHardwareAssignment`).
- Embedded sensors in TSR-AIR units are only added if the unit is not already in the group.
- **Blank Channel Handling**:
- `AllChannels` always ends with a blank channel; `Channels` excludes it.
- `MarkModified` and `Remove` manage blank channel insertion/removal to maintain this invariant.
- **Paste Parsing Ambiguity**:
- `ParseText` splits on `,`, `\t`, or `;` (in order) if tokens < 2. Column mapping depends on `tag` (e.g., `"GroupName"`).
- `oneColumn` output indicates if only one column was present (used for single-field pastes).
- **Sensor Serial Number Parsing**:
- `GetSensorSerialNumber` assumes serial numbers are enclosed in parentheses (e.g., `"name (SN)"`). If no parentheses, the entire string is used.
- **Group Name Changes**:
- `GroupNameChanged` modifies `TestSetup.ChannelsForGroup` and may remove empty groups. Does *not* publish `PageModifiedEvent` (commented out).
- **Filtering Behavior**:
- `_dontFilterList` is cleared on user-initiated filter changes (`initiatedByUser = true`).
- Per-field filters (`_filterByField`) are applied *after* `BridgeFilter` and `SearchTerm`.
- **Hardware Channel Lookup**:
- `_displayToHardwareChannel` uses `channel.ToString(hardware)` as the key (implementation-dependent string representation).
- **Sensor Constants**:
- `CompareAndMarkChannelParameters` skips comparison for test-specific sensors (e.g., `TEST_SPECIFIC_ANALOG_SERIAL`).
- **Settings View Loading**:
- `SettingsViewLoaded` controls whether `SettingChannels` triggers `OnPropertyChanged`.
- `SettingChannelsLoaded` publishes `AppStatusEvent` (Busy → Available) during UI initialization.
- **No-Op Initialization Methods**:
- `Initialize`, `InitializeAsync`, `Activated`, `Cleanup`, `CleanupAsync` are stubbed (no implementation).
- **Range Propagation Scope**:
- `UpdateRangeLowG`/`UpdateRangeARS` only propagate within the same `DASId`.
- `UpdateACCouplingEnabled` requires `GroupChannel` cast (runtime risk if non-`GroupChannel` instances exist).
- **Channel Ordering After Paste**:
- After paste, `AllChannels[i].TestSetupOrder`/`GroupChannelOrder` is reset to `1 + i` for all non-blank channels.
- **Resource Strings**:
- Uses `Resources.StringResources.TestChannelsGroupName` for default group names in `TestSetup` mode.
- **Missing Validation**:
- `ParseText` does not validate sensor/hardware references beyond existence in dictionaries (e.g., invalid IDs are logged but do not halt processing).
---
*Note: All behaviors are derived strictly from the provided source. No external documentation or runtime behavior was assumed.*