188 lines
11 KiB
Markdown
188 lines
11 KiB
Markdown
|
|
---
|
|||
|
|
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 Prism’s 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 Prism’s `INotifyPropertyChanged` may handle thread marshaling in some cases, this pattern is fragile and may cause cross-thread exceptions if the UI framework is strict.
|