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

188 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
source_files:
- DataPRO/Modules/Groups/GroupImport/ViewModel/GroupImportViewModel.cs
generated_at: "2026-04-16T04:45:51.906727+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "f514e88931e0b70e"
---
# GroupImportViewModel Documentation
## 1. Purpose
`GroupImportViewModel` is the central view model for the group and channel import functionality in the DTS application. It orchestrates the end-to-end workflow of importing data from `.grp` files—parsing file contents, validating sensor and field data, allowing user review and selection of groups/channels for import, and committing validated data to the database via background processing. It serves as the data context for three distinct views (`IGroupImportOptionsView`, `IGroupImportPreviewView`, `IGroupImportImportView`) and integrates with Prisms region management, event aggregation, and Unity DI container to coordinate UI state and external service calls.
## 2. Public Interface
### Constructor
```csharp
public GroupImportViewModel(
IGroupImportOptionsView optionsView,
IGroupImportPreviewView previewView,
IGroupImportImportView importView,
IRegionManager regionManager,
IEventAggregator eventAggregator,
IUnityContainer unityContainer)
```
Initializes the view model, assigns views and their `DataContext`, sets up interaction requests (`NotificationRequest`, `ConfirmationRequest`), and subscribes to `RaiseNotification` and `BusyIndicatorChangeNotification` events.
### Methods
- **`void ParseSourceFiles(string userTags)`**
Reads and parses all `.grp` files listed in `SourceFiles`. For each file:
- Extracts group name from file name (strips extension).
- Assigns `userTags` to `GroupTags` and `ImportingUserTags`.
- Parses each non-empty line using `Parse()` (supports escaped commas via parentheses).
- Validates fields: expects 5 fields per line (`SensorSerialNumber`, `DisplayName`, `ISOCode`, `Invert`, `FullScale`).
- Reports errors for missing sensors (unless empty), invalid invert/full-scale values, wrong field count, or empty files.
- Populates `Groups` and calls `ProcessChannels()`.
- **`void Import()`**
Starts the import process on a background thread via `ThreadPool.QueueUserWorkItem(ImportFunc)`.
- **`private void ImportFunc(object o)`**
Background method that:
- Fetches default channel settings via `DbOperations.GetChannelSettingDefaults()`.
- Updates UI progress state (`ImportProgressValue`, `ImportProgressBarVisibility`, `ImportProgressColor`, `DisableUI`).
- Iterates over included groups and their channels:
- Skips channels with critical errors (`FileEmpty`, `InvalidSensorInput`, `SensorNotFound`).
- For non-critical errors, uses `null` for invalid `FullScale` or `Invert`.
- Calls `CreateGroup`, `AddChannel`, and finally `CommitGroups`.
- Restores UI state on completion (though commented-out lines suggest UI reset is incomplete).
- **`void SetStatus(string message, Color color)`**
Updates `ImportProgressText`, `ImportProgressColor`, `ImportProgressBarVisibility` (collapsed), and invokes `EnableUI`.
- **`void Reset()`**
Clears all imported data: `SourceFiles`, `Channels`, `Groups`, progress state, and resets `ImportProgressValue` to 0.
- **`void CheckGroupName()`**
Validates group names:
- Flags groups with duplicate names in the import set.
- Flags groups whose name already exists in the application *and* `Overwrite` is `false`.
- Sets `GroupNameHasError = true` on invalid groups.
- **`private static string[] Parse(string line)`**
Parses a single `.grp` line, respecting parenthetical escaping (e.g., `"this(,)is"` → one field `"this(,)is"`).
- **`private void ProcessChannels()`**
Flattens all channels from `Groups` into the `Channels` array.
- **`void InvalidateChannels()`**
Raises `PropertyChanged` for `IncompleteChannels` and `CompleteChannels` computed properties.
- **`void Cleanup()` / `Task CleanupAsync()` / `void Initialize()` / `Task InitializeAsync()` / `void Activated()`**
No-op stubs; likely required by Prism interfaces (`INavigationAware`, `IInitializeAsync`, etc.) but not implemented.
### Properties
- **`IGroupImportOptionsView ImportOptionsView`**, **`IGroupImportPreviewView ImportPreviewView`**, **`IGroupImportImportView ImportView`**
References to the three associated views.
- **`string[] SourceFiles`**
Array of `.grp` file paths to parse.
- **`GroupGRPImportGroup[] Groups`**
Parsed groups with their channels and errors.
- **`GroupGRPImportChannel[] Channels`**
Flattened list of all channels from `Groups`.
- **`GroupGRPImportChannel[] IncompleteChannels`**
Channels in included groups that have *non-recoverable* errors (e.g., `SensorNotFound`, `InvalidSensorInput`). These will *not* be imported.
- **`GroupGRPImportChannel[] CompleteChannels`**
Channels in included groups that either have no errors *or* have recoverable errors (`InvalidFullScaleInput`, `InvalidInvertInput`). These *will* be imported (with `null` for invalid fields).
- **`string ImportProgressText`**, **`Color ImportProgressColor`**, **`Visibility ImportProgressBarVisibility`**, **`double ImportProgressValue`**
UI state for the import progress bar.
- **`bool IsBusy`**
Bound to busy indicator; set via `OnBusyIndicatorNotification`.
- **`InteractionRequest<Notification> NotificationRequest`**, **`InteractionRequest<Confirmation> ConfirmationRequest`**
Prism interaction requests for showing notifications and confirmation dialogs.
- **`bool IsDirty`**
Read-only; always `false` (never set to `true` in source).
- **`bool IsMenuIncluded`**, **`bool IsNavigationIncluded`**
UI state flags (bound to menu/navigation visibility); no logic sets them beyond property change notifications.
- **`string HeaderInfo`**
Returns `"MainRegion"`.
### Commands
- **`DelegateCommand ImportBrowseCommand`**
Opens a file dialog (`OpenFileDialog`) with `Multiselect=true`, `Filter` from `StringResources.ImportFileFilter`, sets `SourceFiles`, `BrowseOk=true`, and navigates to `Steps.Preview` via `SwitchNavSteps`.
### Delegates (Settable Properties)
- **`CheckGroupExistsDelegate CheckGroupExists`**
Function to check if a group exists in the application.
- **`CheckSensorExistsDelegate CheckSensorExists`**
Function to check if a sensor exists.
- **`CreateGroupDelegate CreateGroup`**
Action to create a group.
- **`AddChannelToGroupDelegate AddChannel`**
Action to add a channel to a group.
- **`CommitGroupsDelegate CommitGroups`**
Action to commit groups to the database.
- **`Disable_UIDelegate DisableUI`**, **`Enable_UIDelegate EnableUI`**
Actions to disable/enable the UI during import.
- **`SwitchNavStepsDelegate SwitchNavSteps`**
Action to navigate between import steps (e.g., `Steps.Preview`).
- **`FileUtils.LogDelegate Logger`**
Optional logging delegate; invoked on exceptions.
## 3. Invariants
- **File Parsing**: Each `.grp` line must contain 5 comma-separated fields (or be empty/whitespace). Lines with fewer or more fields are marked with `InvalidSensorInput`.
- **Sensor Validation**: A channel is flagged `SensorNotFound` if `CheckSensorExists` is non-null, the sensor serial number is non-empty, and `CheckSensorExists(sensorSerialNumber)` returns `false`. Empty sensor serial numbers are *allowed* (per comment referencing FB13753).
- **Invert Field**: Accepts `"yes"/"no"` (case-insensitive) or `bool.TryParse`-compatible strings. Invalid values trigger `InvalidInvertInput`.
- **FullScale Field**: Must parse as `double`; otherwise, `InvalidFullScaleInput`.
- **Group Name Uniqueness**: Within the import set, duplicate group names are flagged. Across the import set and application, group names must be unique *unless* `Overwrite=true`.
- **Import Progress State**: During `ImportFunc`, `ImportProgressBarVisibility` is set to `Visible`, `DisableUI` is called, and progress is tracked. Completion logic is partially commented out, so UI may not fully reset.
- **Channel Inclusion Logic**: Only channels from groups where `Included=true` are considered for import. Channels with critical errors (`FileEmpty`, `InvalidSensorInput`, `SensorNotFound`) are skipped entirely; others may be imported with `null` for invalid fields.
## 4. Dependencies
### Imports/References
- **DTS.Common**:
- `Events` (`IEventAggregator`, `RaiseNotification`, `BusyIndicatorChangeNotification`)
- `Utils` (`FileUtils.LogDelegate`)
- `Classes.Groups` (`GroupGRPImportGroup`, `GroupGRPImportChannel`, `GroupGRPImportError`)
- `Enums.Groups` (`GroupImportEnums.Steps`, `GroupGRPImportError.Errors`)
- `Interface.Groups` (`IGroupImportOptionsView`, `IGroupImportPreviewView`, `IGroupImportImportView`, `IGroupImportViewModel`)
- `Storage` (`DbOperations`)
- `Interactivity` (`InteractionRequest<T>`)
- **Prism**:
- `Regions` (`IRegionManager`)
- `Events` (`IEventAggregator`)
- `DelegateCommand`
- **Unity** (`IUnityContainer`)
- **System.Windows** (`Visibility`, `Color`)
- **System.Windows.Forms** (`OpenFileDialog`)
- **StringResources** (resource strings for UI messages)
### Dependencies on External Services
- `DbOperations.GetChannelSettingDefaults()` — for default channel settings.
- `CheckSensorExists`, `CheckGroupExists`, `CreateGroup`, `AddChannel`, `CommitGroups` — injected delegates for database operations.
- `SwitchNavSteps` — navigation delegate (likely from a parent view model or shell).
## 5. Gotchas
- **Incomplete UI Reset After Import**: `ImportFunc` sets `ImportProgressBarVisibility = Visibility.Visible` and `DisableUI`, but the corresponding reset (`Visibility.Collapsed`, `EnableUI`) is commented out. This may leave the UI in a disabled state if import completes without error.
- **Empty Sensor Serial Numbers Are Allowed**: Despite `CheckSensorExists` validation, empty serial numbers bypass the `SensorNotFound` check (FB13753).
- **Partial Error Tolerance**: Channels with `InvalidFullScaleInput` or `InvalidInvertInput` are included in `CompleteChannels` and imported, but with `null` for the invalid field. This may cause downstream issues if the database or business logic does not handle `null` gracefully.
- **No Validation of Group Name Format**: Only existence and duplication are checked; no format validation (e.g., reserved characters, length limits) is implemented.
- **`Parse()` Escaping Logic**: Parentheses are treated as literal characters *only* when nested (via `leftParenCount`). A single `(` or `)` without matching pairs is still treated as a delimiter if not balanced.
- **`IsDirty` Never Set**: The `IsDirty` property is declared but never updated, so it always returns `false`.
- **`Initialize*` and `Cleanup*` Methods Are No-ops**: These likely exist to satisfy Prism interfaces but contain no logic.
- **`Channels` Property Triggers `InvalidateChannels()`**: Setting `Channels` raises property change notifications for `IncompleteChannels` and `CompleteChannels`, but these are computed properties—modifying `Channels` directly may not reflect changes in the UI if `Groups` is not updated.
- **Thread Safety**: `ImportFunc` runs on a background thread but updates UI properties directly (e.g., `ImportProgressValue`). While Prisms `INotifyPropertyChanged` may handle thread marshaling in some cases, this pattern is fragile and may cause cross-thread exceptions if the UI framework is strict.