Files
2026-04-17 14:55:32 -04:00

207 lines
11 KiB
Markdown

---
source_files:
- DTS Viewer/DTS.Viewer.Modules/DTS.Viewer.AddCalculatedChannel/ViewModel/AddCalculatedChannelViewModel.cs
generated_at: "2026-04-17T16:00:17.185622+00:00"
model: "zai-org/GLM-5-FP8"
schema_version: 1
sha256: "71a9c8acbea4fb64"
---
# Documentation: AddCalculatedChannelViewModel
## 1. Purpose
This module provides the ViewModel for the "Add Calculated Channel" feature in the DTS Viewer application. It manages the UI logic and business rules for creating derived data channels from existing test data, supporting multiple calculation types including mathematical operations (Integral, Derivative, Sin, Cos), aggregations (SUM, Average, Resultant), and specialized biomechanical calculations (HIC, 3D IR-Tracc variants). The ViewModel handles channel selection, validation, file I/O operations, and coordinates with the event aggregation system to notify other application components of changes.
## 2. Public Interface
### Class: `AddCalculatedChannelViewModel`
**Inherits:** `BaseViewModel<IAddCalculatedChannelViewModel>`
**Implements:** `IAddCalculatedChannelViewModel`
### Constructor
```csharp
public AddCalculatedChannelViewModel(
IAddCalculatedChannelView view,
IRegionManager regionManager,
IEventAggregator eventAggregator,
IUnityContainer unityContainer)
```
Initializes the ViewModel, sets up the View's DataContext, creates interaction requests, and registers the `AddCalculatedChannelCommand`.
### Public Properties
| Property | Type | Description |
|----------|------|-------------|
| `View` | `IBaseView` | The associated view instance. |
| `Parent` | `IBaseViewModel` | The parent ViewModel passed during initialization. |
| `ContextSearchRegion` | `object` | Context for search region (usage unclear from source). |
| `NotificationRequest` | `InteractionRequest<Notification>` | Raises notification dialogs. |
| `ConfirmationRequest` | `InteractionRequest<Confirmation>` | Raises confirmation dialogs. |
| `HeaderInfo` | `string` | Returns `"AddCalculatedChannelRegion"`. |
| `IsBusy` | `bool` | **Throws `NotImplementedException`** on get/set. |
| `IsDirty` | `bool` | **Throws `NotImplementedException`** on get. |
| `IsAddCalculatedChannelIncluded` | `bool` | Gets/sets inclusion flag. |
| `IncludeGroupNameInISOExport` | `bool` | Controls whether group names are included in ISO export. |
| `DefaultDTSEncoding` | `int` | Encoding code page for DTS file operations. |
| `ChannelName` | `string` | Name for the new calculated channel. |
| `ChannelDescription` | `string` | Auto-generated description based on selected calculation and channels. |
| `IsoCode` | `string` | ISO code for the channel (defaults to `"NONE"`). |
| `SingleChannelSelectorVisibility` | `bool` | Controls visibility for single-channel selection UI. |
| `HICChannelSelectorVisibility` | `bool` | Controls visibility for HIC channel selection UI. |
| `MultipleChannelSelectorVisibility` | `bool` | Controls visibility for multi-channel selection UI. |
| `ThreeDIRTRACCVisibility` | `bool` | Controls visibility for 3D IR-Tracc channel selection UI. |
| `CalculationList` | `CalculationHelper[]` | Lazy-initialized list of available calculations. |
| `SelectedCalculation` | `CalculationHelper` | Currently selected calculation type. |
| `ChannelList` | `ObservableCollection<ITestChannel>` | All available input channels. |
| `ChannelListObjects` | `ObservableCollection<ChannelHelper>` | Wrapped channel objects with `IsIncluded` tracking. |
| `AvailableHICChannels` | `ChannelHelper[]` | Channels with acceleration units valid for HIC calculation. |
| `HICAccelerationX` | `ChannelHelper` | Selected X-axis acceleration channel for HIC. |
| `HICAccelerationY` | `ChannelHelper` | Selected Y-axis acceleration channel for HIC. |
| `HICAccelerationZ` | `ChannelHelper` | Selected Z-axis acceleration channel for HIC. |
| `HICLength` | `int` | HIC calculation length parameter (default: 16). |
| `SourceChannel` | `ITestChannel` | Selected source channel for single-channel calculations. |
| `IRTraccChannelList` | `ObservableCollection<ChannelHelper>` | Valid IR-Tracc channels. |
| `IRTraccChannel` | `ChannelHelper` | Selected IR-Tracc channel. |
| `Pot1ChannelList` | `ObservableCollection<ChannelHelper>` | Valid potentiometer 1 channels. |
| `Pot1Channel` | `ChannelHelper` | Selected potentiometer 1 channel. |
| `Pot2ChannelList` | `ObservableCollection<ChannelHelper>` | Valid potentiometer 2 channels. |
| `Pot2Channel` | `ChannelHelper` | Selected potentiometer 2 channel. |
### Public Commands
| Command | Handler | Description |
|---------|---------|-------------|
| `AddCalculatedChannelCommand` | `AddCalculatedChannel(object obj)` | Validates input, creates calculated channel(s), writes to DTS file, and publishes refresh event. |
### Public Methods
```csharp
public void PublishChanges() // Throws NotImplementedException
public override void Initialize()
public override void Initialize(object parameter)
public override void Activated()
public override void Cleanup()
public bool Validate(ref List<string> errors, ref List<string> warnings, bool displayWindow)
```
### Public Enum: `Calculation`
| Value | Name | Description Attribute |
|-------|------|------------------------|
| 0 | `Integral` | "Integral" |
| 1 | `DoubleIntegral` | "Double Integral" |
| 2 | `Derivative` | "Derivative" |
| 3 | `Sin` | "Sin" |
| 4 | `Cos` | "Cos" |
| 5 | `ThreeDIRTracc` | "3D IR-Tracc" |
| 6 | `SUM` | "SUM" |
| 7 | `AVE` | "Average" |
| 8 | `ThreeDIRTraccAbdomen` | "3D IR-TRACC Abdomen" |
| 9 | `ThreeDIRTraccLowerThorax` | "3D IR-TRACC Lower Thorax" |
| 10 | `Resultant` | "Resultant" |
| 11 | `HIC` | "HIC" |
### Public Nested Classes
#### `ChannelHelper`
Wraps `ITestChannel` with display formatting and inclusion tracking.
| Member | Type | Description |
|--------|------|-------------|
| `MyChannel` | `ITestChannel` | The wrapped channel. |
| `DisplayName` | `string` | Formatted display name based on `IsoViewMode`. |
| `ChannelName` | `string` | Returns `ToString()` of the channel. |
| `IsIncluded` | `bool` | Tracks whether channel is included in multi-select calculations. |
#### `CalculationHelper`
Wraps `Calculation` enum with localized string representation.
| Member | Type | Description |
|--------|------|-------------|
| `MyCalculation` | `Calculation` | The wrapped calculation type. |
| `ToString()` | `string` | Returns localized name from resources or enum name. |
## 3. Invariants
1. **Channel List Ordering**: `ChannelList` is always sorted by `AbsoluteDisplayOrder` via `CompareDisplayOrders` comparison.
2. **IR-Tracc Channel Eligibility**: Channels in `IRTraccChannelList` must satisfy all of:
- `ChannelType` equals `Test.Module.AnalogInputChannel` type string
- `LinearizationFormula` is valid (via `LinearizationFormula.IsValid()`)
- `ZeroMethod` equals `ZeroMethodType.None`
- `ZeroPoint` is not zero
- `FactoryExcitationVoltage` is not zero
3. **Potentiometer Channel Eligibility**: Channels in `Pot1ChannelList` and `Pot2ChannelList` must satisfy:
- `ChannelType` equals `Test.Module.AnalogInputChannel` type string
- `Eu` (engineering units) is `"deg"` or `"deg-ang"` (case-insensitive)
- `ZeroMethod` equals `ZeroMethodType.None`
- `FactoryExcitationVoltage` is not zero
4. **HIC Channel Eligibility**: Channels in `AvailableHICChannels` must have engineering units contained in `Constants.ACCELERATION_UNITS`.
5. **Resultant Validation**: All included channels for Resultant calculation must have matching `SensitivityUnits` and `SampleRateHz`.
6. **HIC Validation**: HIC calculation requires all three acceleration channels (X, Y, Z) to be non-null.
7. **File Backup**: DTS file backup is only created if a backup file does not already exist.
## 4. Dependencies
### Direct Dependencies (Imports)
- `DTS.Common` / `DTS.Common.Base` / `DTS.Common.Utils` / `DTS.Common.Utilities.Logging`
- `DTS.Common.Classes.Viewer.Commands` (for `RelayCommand`)
- `DTS.Common.DAS.Concepts`
- `DTS.Common.Enums.Sensors` (for `ZeroMethodType`)
- `DTS.Common.Events` (for event types)
- `DTS.Common.Interactivity` (for `InteractionRequest`, `Notification`, `Confirmation`)
- `DTS.Common.Interface`
- `DTS.Slice.Control`
- `DTS.Viewer.AddCalculatedChannel.Model`
- `DTS.Serialization.Test` (aliased as `Test`)
- `Prism.Events` (for `IEventAggregator`)
- `Prism.Regions` (for `IRegionManager`)
- `Unity` (for `IUnityContainer`)
### Events Subscribed
- `RaiseNotification``OnRaiseNotification`
- `TestSummaryChangeNotification``OnTestSummaryChanged`
### Events Published
- `SetSaveButton` (via `SaveButtonUsability` payload)
- `PageErrorEvent` (via `PageErrorArg` payload)
- `RefreshTestRequestEvent` (with DTS file path)
- `RaiseNotification` (via `NotificationContentEventArgs`)
### Consumers
Unknown from source alone. The module is instantiated via Unity dependency injection and navigated to via Prism regions.
## 5. Gotchas
1. **Unimplemented Members**: `IsBusy`, `IsDirty`, and `PublishChanges()` throw `NotImplementedException`. These appear to be interface requirements that were never implemented.
2. **HIC Save Button Logic Appears Inverted**: In `UpdateSaveButtonVisibility()`, the HIC case sets `IsUsable = true` when acceleration channels are null and `IsUsable = false` when they are set. This appears to be a bug—the logic should likely be inverted.
```csharp
case Calculation.HIC:
if (null == HICAccelerationX || null == HICAccelerationY || null == HICAccelerationZ)
{
_eventAggregator.GetEvent<SetSaveButton>().Publish(new SaveButtonUsability() { IsUsable = true }); // Suspect
}
else
{
_eventAggregator.GetEvent<SetSaveButton>().Publish(new SaveButtonUsability() { IsUsable = false }); // Suspect
}
break;
```
3. **Member Hiding with `new` Keyword**: The class uses `new` to hide inherited members (`PropertyChanged`, `OnPropertyChanged`, `IsBusy`, `IsDirty`, `ConfirmationRequest`). This can lead to unexpected behavior when the ViewModel is accessed via base class references.
4. **Thread.Sleep in File Write**: A `Thread.Sleep(10)` follows the file write operation in `AddCalculatedChannel()`. This suggests a timing-related workaround for file system operations.
5. **Backup File Deletion**: After successful save, the backup file is deleted only if it was created during that save operation (`!backupExisted`). Original backups are preserved.
6. **Channel Loading Side Effect**: `OnTestSummaryChanged` triggers lazy loading of channel data via `Utils.SetChannelInfo()` when `ts.Channels.FirstOrDefault() == null`, modifying the test metadata in place.
7. **Default Encoding Fallback**: If `Encoding.GetEncoding(DefaultDTSEncoding)` fails, the code falls back to `Encoding.Default` without re-throwing, potentially causing encoding mismatches.