--- 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`. #### **Properties** | Property | Type | Description | |---------|------|-------------| | `View` | `IGroupChannelListView` | Reference to the main channel list view. | | `SettingsView` | `IGroupChannelSettingsListView` | Reference to the channel settings view. | | `NotificationRequest` | `InteractionRequest` | Prism interaction request for displaying notifications. | | `ConfirmationRequest` | `InteractionRequest` | Prism interaction request for confirmation dialogs. | | `PropertyChanged` | `event PropertyChangedEventHandler` | Standard `INotifyPropertyChanged` event. | | `ChannelCount` | `int` | Derived from `AllChannels.Count`; triggers `GroupChannelsChangedEvent` on change. | | `AllChannels` | `IList` | *Internal* full list of channels (including blank trailing channel). | | `Channels` | `IList` | *Internal* list of non-blank channels (used for UI rendering). | | `SettingChannels` | `ObservableCollection` | Sorted, filtered list of non-blank channels for settings view. | | `SelectedChannelItems` | `ObservableCollection` | 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 sender’s position. | | `ParseText` | `private IEnumerable 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)`
`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 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 it’s 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 channel’s 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`, `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.*