177 lines
12 KiB
Markdown
177 lines
12 KiB
Markdown
|
|
---
|
||
|
|
source_files:
|
||
|
|
- DataPRO/Modules/TestSetups/Imports/TTS/ViewModel/DigitalOutputChannelsViewModel.cs
|
||
|
|
- DataPRO/Modules/TestSetups/Imports/TTS/ViewModel/LevelTriggerViewModel.cs
|
||
|
|
- DataPRO/Modules/TestSetups/Imports/TTS/ViewModel/DigitalInputChannelsViewModel.cs
|
||
|
|
- DataPRO/Modules/TestSetups/Imports/TTS/ViewModel/TOMChannelsViewModel.cs
|
||
|
|
generated_at: "2026-04-17T15:49:35.967692+00:00"
|
||
|
|
model: "zai-org/GLM-5-FP8"
|
||
|
|
schema_version: 1
|
||
|
|
sha256: "038dac49bea7aac0"
|
||
|
|
---
|
||
|
|
|
||
|
|
# TTS Import ViewModels Documentation
|
||
|
|
|
||
|
|
## 1. Purpose
|
||
|
|
|
||
|
|
This module provides four ViewModel classes for managing channel configuration within a TTS (Test Setup) Import workflow. Each ViewModel handles a specific channel type—Digital Output, Digital Input, Level Triggers, and TOM (Squib) channels—facilitating the mapping between physical hardware channels (`IDASHardware`/`IHardwareChannel`) and logical channel records (`ITTSChannelRecord`) in test setups. The ViewModels coordinate via `IEventAggregator` to respond to hardware scan results, file imports, and channel assignment changes, providing UI binding properties and commands for user interaction.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 2. Public Interface
|
||
|
|
|
||
|
|
### DigitalOutputChannelsViewModel
|
||
|
|
|
||
|
|
**Implements:** `IDigitalOutputChannelsViewModel`
|
||
|
|
|
||
|
|
| Member | Signature | Description |
|
||
|
|
|--------|-----------|-------------|
|
||
|
|
| `View` | `public IDigitalOutputChannelsView View { get; set; }` | The associated view instance; DataContext is set to `this` in constructor. |
|
||
|
|
| `NotificationRequest` | `public InteractionRequest<Notification> NotificationRequest { get; }` | Used to raise notification dialogs. |
|
||
|
|
| `ConfirmationRequest` | `public InteractionRequest<Confirmation> ConfirmationRequest { get; }` | Used to raise confirmation dialogs. |
|
||
|
|
| `DASChannels` | `public ObservableCollection<DASChannel> DASChannels { get; set; }` | Collection of digital output channels from hardware. |
|
||
|
|
| `SelectedDASChannel` | `public DASChannel SelectedDASChannel { get; set; }` | Currently selected DAS channel in UI. |
|
||
|
|
| `EnableOrDisableText` | `public string EnableOrDisableText { get; }` | Returns localized "Enable" or "Disable" text based on `SelectedDASChannel.Channel.Disabled`. |
|
||
|
|
| `IsBusy` | `public bool IsBusy { get; set; }` | Bound to busy indicator state. |
|
||
|
|
| `IsMenuIncluded` | `public bool IsMenuIncluded { get; set; }` | Unclear purpose from source alone. |
|
||
|
|
| `IsNavigationIncluded` | `public bool IsNavigationIncluded { get; set; }` | Unclear purpose from source alone. |
|
||
|
|
| `IsDirty` | `public bool IsDirty { get; private set; }` | Indicates unsaved changes (never set to `true` in visible source). |
|
||
|
|
| `Cleanup()` | `public void Cleanup()` | Empty implementation. |
|
||
|
|
| `CleanupAsync()` | `public Task CleanupAsync()` | Returns `Task.CompletedTask`. |
|
||
|
|
| `Initialize()` | `public void Initialize()` | Empty implementation. |
|
||
|
|
| `Initialize(object)` | `public void Initialize(object parameter)` | Empty implementation. |
|
||
|
|
| `Initialize(object, object)` | `public void Initialize(object parameter, object model)` | Empty implementation. |
|
||
|
|
| `InitializeAsync()` | `public Task InitializeAsync()` | Returns `Task.CompletedTask`. |
|
||
|
|
| `InitializeAsync(object)` | `public Task InitializeAsync(object parameter)` | Returns `Task.CompletedTask`. |
|
||
|
|
| `Activated()` | `public void Activated()` | Empty implementation. |
|
||
|
|
| `OnPropertyChanged(string)` | `public void OnPropertyChanged(string propertyName)` | Raises `PropertyChanged` event. |
|
||
|
|
| `PropertyChanged` | `public event PropertyChangedEventHandler PropertyChanged` | Standard INotifyPropertyChanged event. |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### LevelTriggerViewModel
|
||
|
|
|
||
|
|
**Implements:** `ILevelTriggerViewModel`
|
||
|
|
|
||
|
|
| Member | Signature | Description |
|
||
|
|
|--------|-----------|-------------|
|
||
|
|
| `View` | `public ILevelTriggerView View { get; set; }` | The associated view instance. |
|
||
|
|
| `LevelTriggers` | `public ILevelTrigger[] LevelTriggers { get; }` | Returns `_setup?.LevelTriggers`; provides access to level trigger configurations. |
|
||
|
|
| `IsBusy`, `IsMenuIncluded`, `IsNavigationIncluded`, `IsDirty` | Same as above | Standard state properties. |
|
||
|
|
| `NotificationRequest`, `ConfirmationRequest` | Same as above | Interaction requests. |
|
||
|
|
| Lifecycle methods (`Cleanup`, `Initialize`, etc.) | Same signatures as above | All return empty or `Task.CompletedTask`. |
|
||
|
|
| `OnPropertyChanged(string)` | Same as above | Raises `PropertyChanged` event. |
|
||
|
|
| `PropertyChanged` | Same as above | INotifyPropertyChanged event. |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### DigitalInputChannelsViewModel
|
||
|
|
|
||
|
|
**Implements:** `IDigitalInputChannelsViewModel`
|
||
|
|
|
||
|
|
| Member | Signature | Description |
|
||
|
|
|--------|-----------|-------------|
|
||
|
|
| `View` | `public IDigitalInputChannelsView View { get; set; }` | The associated view instance. |
|
||
|
|
| `DASChannels` | `public ObservableCollection<DASChannel> DASChannels { get; set; }` | Physical digital input channels from hardware. |
|
||
|
|
| `RemainingChannels` | `public ObservableCollection<ITTSChannelRecord> RemainingChannels { get; set; }` | Channel records not yet assigned to hardware. |
|
||
|
|
| `SelectedDASChannel` | `public DASChannel SelectedDASChannel { get; set; }` | Currently selected hardware channel. |
|
||
|
|
| `SelectedRemainingChannel` | `public ITTSChannelRecord SelectedRemainingChannel { get; set; }` | Currently selected unassigned channel record. |
|
||
|
|
| `AssignEnabled` | `public bool AssignEnabled { get; set; }` | Enables/disables Assign button in UI. |
|
||
|
|
| `RemoveEnabled` | `public bool RemoveEnabled { get; set; }` | Enables/disables Remove button in UI. |
|
||
|
|
| `EnableOrDisableEnabled` | `public bool EnableOrDisableEnabled { get; set; }` | Enables/disables Enable/Disable button. |
|
||
|
|
| `EnableOrDisableText` | `public string EnableOrDisableText { get; }` | Localized "Enable" or "Disable" text. |
|
||
|
|
| `AssignCommand` | `public DelegateCommand AssignCommand { get; }` | Assigns `SelectedRemainingChannel` to `SelectedDASChannel`. |
|
||
|
|
| `RemoveCommand` | `public DelegateCommand RemoveCommand { get; }` | Removes channel assignment from `SelectedDASChannel`. |
|
||
|
|
| `EnableOrDisableCommand` | `public DelegateCommand EnableOrDisableCommand { get; }` | Toggles `Disabled` state on selected channel. |
|
||
|
|
| Other members | Same as above | `IsBusy`, `IsDirty`, lifecycle methods, `PropertyChanged`. |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### TOMChannelsViewModel
|
||
|
|
|
||
|
|
**Implements:** `ITOMChannelsViewModel`
|
||
|
|
|
||
|
|
| Member | Signature | Description |
|
||
|
|
|--------|-----------|-------------|
|
||
|
|
| `View` | `public ITOMChannelsView View { get; set; }` | The associated view instance. |
|
||
|
|
| `DASChannels` | `public ObservableCollection<Model.DASChannel> DASChannels { get; set; }` | Physical squib channels from hardware (every other channel, `i += 2`). |
|
||
|
|
| `RemainingChannels` | `public ObservableCollection<ITTSChannelRecord> RemainingChannels { get; set; }` | Unassigned squib channel records. |
|
||
|
|
| `SelectedDASChannel` | `public Model.DASChannel SelectedDASChannel { get; set; }` | Currently selected squib hardware channel. |
|
||
|
|
| `SelectedRemainingChannel` | `public ITTSChannelRecord SelectedRemainingChannel { get; set; }` | Currently selected unassigned squib record. |
|
||
|
|
| `AssignCommand`, `RemoveCommand`, `EnableOrDisableCommand` | Same as `DigitalInputChannelsViewModel` | Same command implementations for squib channels. |
|
||
|
|
| Other members | Same as above | Standard state properties and lifecycle methods. |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3. Invariants
|
||
|
|
|
||
|
|
1. **Singleton Lifetime**: All four ViewModels are decorated with `[PartCreationPolicy(CreationPolicy.Shared)]`, ensuring a single shared instance per container.
|
||
|
|
|
||
|
|
2. **Event Subscription Thread Affinity**: All `IEventAggregator` subscriptions use `ThreadOption.PublisherThread` with `keepSubscriberReferenceAlive = true`, meaning handlers execute on the publisher's thread and subscribers are held strongly.
|
||
|
|
|
||
|
|
3. **UI Thread Requirement**: `OnAssignedChannelsChangedEvent` in `DigitalOutputChannelsViewModel`, `DigitalInputChannelsViewModel`, and `TOMChannelsViewModel` explicitly check `Application.Current.Dispatcher.CheckAccess()` and marshal to the UI thread via `BeginInvoke` if necessary.
|
||
|
|
|
||
|
|
4. **Hardware Channel Filtering**:
|
||
|
|
- `DigitalOutputChannelsViewModel`: Filters for `ch.IsDigitalOut == true`
|
||
|
|
- `DigitalInputChannelsViewModel`: Filters for `ch.IsDigitalIn == true`
|
||
|
|
- `TOMChannelsViewModel`: Filters for `ch.IsSquib == true` and iterates by `i += 2` (squib channels are paired)
|
||
|
|
|
||
|
|
5. **Channel Record Validation**: All ViewModels skip channel records where:
|
||
|
|
- `channelRecord.IsEmptyRecord == true`
|
||
|
|
- `channelRecord.IsChannelCodeValid == false`
|
||
|
|
- `channelRecord.ChannelCode == TTSChannelRecord.NONE`
|
||
|
|
|
||
|
|
6. **EID Mapping**: `DigitalOutputChannelsViewModel` and `LevelTriggerViewModel` maintain a mapping (`_hardwareChannelIdToSensorId` / `_sensorIdToChannelId`) from `EIDMappingEvent` for sensor-to-channel resolution.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 4. Dependencies
|
||
|
|
|
||
|
|
### External Dependencies (from imports)
|
||
|
|
- **Prism Framework**: `Prism.Events`, `Prism.Regions`, `Prism.Commands` — Event aggregation, region management, delegate commands.
|
||
|
|
- **Unity**: `Unity` — Dependency injection container (`IUnityContainer`).
|
||
|
|
- **MEF**: `System.ComponentModel.Composition` — Part creation policy.
|
||
|
|
- **WPF**: `System.Windows`, `System.Windows.Data`, `System.Collections.ObjectModel` — UI collections and dispatcher.
|
||
|
|
|
||
|
|
### Internal Dependencies (DTS.* namespaces)
|
||
|
|
- `DTS.Common.Events` / `DTS.Common.Events.TTSImport` — Event types: `RaiseNotification`, `BusyIndicatorChangeNotification`, `AssignedChannelsChangedEvent`, `TTSImportHardwareScanFinishedEvent`, `EIDMappingEvent`, `TTSImportReadFileStatusEvent`, `TTSImportSavedChangesStatusEvent`, `TTSImportTestSetupChangedEvent`.
|
||
|
|
- `DTS.Common.Interface.DataRecorders` — `IDASHardware`, `IHardwareChannel`.
|
||
|
|
- `DTS.Common.Interface.TestSetups.Imports.TTS.*` — `ITTSSetup`, `ITTSChannelRecord`, `ILevelTrigger`, view interfaces.
|
||
|
|
- `DTS.Common.Interactivity` — `InteractionRequest<T>`, `Notification`, `Confirmation`, `NotificationContentEventArgs`.
|
||
|
|
- `DTS.Common.Enums` — `DigitalInputModes`, `ExcitationVoltageOptions`.
|
||
|
|
- `TTSImport.Model` — `DASChannel` model class.
|
||
|
|
- `TTSImport.Resources` — `StringResources` for localized strings.
|
||
|
|
|
||
|
|
### Consumers
|
||
|
|
- Unclear from source alone; these ViewModels are likely resolved by the DI container and bound to their respective Views (`IDigitalOutputChannelsView`, `ILevelTriggerView`, etc.).
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5. Gotchas
|
||
|
|
|
||
|
|
1. **Logic Bug in `DetermineRemoveEnableStatus`** (present in `DigitalInputChannelsViewModel` and `TOMChannelsViewModel`):
|
||
|
|
```csharp
|
||
|
|
if (!string.IsNullOrWhiteSpace(_selectedDASChannel.EID) &&
|
||
|
|
_selectedDASChannel.EID == _selectedDASChannel.Channel.SensorEID)
|
||
|
|
{
|
||
|
|
RemoveEnabled = false;
|
||
|
|
}
|
||
|
|
RemoveEnabled = true; // This always overwrites the above!
|
||
|
|
```
|
||
|
|
The `RemoveEnabled = true` assignment always executes, making the preceding conditional check ineffective.
|
||
|
|
|
||
|
|
2. **Empty Async Lifecycle Methods**: All `InitializeAsync` and `CleanupAsync` methods return `Task.CompletedTask` with no actual async work. This may indicate incomplete implementation or a placeholder pattern.
|
||
|
|
|
||
|
|
3. **`IsDirty` Never Set to `true`**: The `IsDirty` property has a private setter but is never assigned `true` anywhere in the visible source. Its purpose is unclear.
|
||
|
|
|
||
|
|
4. **Historical Bug References in Comments** (`LevelTriggerViewModel.cs`):
|
||
|
|
- Comment references "11245" and "15643" (likely defect tracking IDs) describing edge cases around EID assignment and channel code validation.
|
||
|
|
- A comment notes: *"the below condition I think was missing a not"* indicating past confusion about boolean logic.
|
||
|
|
|
||
|
|
5. **Squib Channel Pairing** (`TOMChannelsViewModel`): Hardware channels are iterated with `i += 2`, implying squib channels are paired (voltage/initiation vs. current). This is not documented elsewhere and could cause confusion if the hardware configuration changes.
|
||
|
|
|
||
|
|
6. **Task.Run for MessageBox** (`DigitalInputChannelsViewModel.Assign` and `TOMChannelsViewModel.Assign`): The code uses `Task.Run(() => MessageBox.Show(...))` followed by `Application.Current.Dispatcher.BeginInvoke` for the actual assignment work. This pattern is unusual—MessageBox is typically shown on the UI thread directly.
|
||
|
|
|
||
|
|
7. **Pre-Assigned Channels Logic** (`LevelTriggerViewModel.UpdateLevelTriggers`): The method modifies `_setup.PreAssignedSensorIdAndHwId` in-place, reassigning it to a filtered list. This mutates shared state that may be observed elsewhere.
|
||
|
|
|
||
|
|
8. **Exception Swallowing** (`LevelTriggerViewModel.VoltageIsValid`): The method contains an empty `catch { }` block that silently ignores exceptions from `GetExcitationVoltageEnumFromMagnitude`, returning `false` for invalid voltages.
|