17 KiB
source_files, generated_at, model, schema_version, sha256
| source_files | generated_at | model | schema_version | sha256 | |
|---|---|---|---|---|---|
|
2026-04-16T04:34:24.697647+00:00 | Qwen/Qwen3-Coder-Next-FP8 | 1 | 2e798c0127d02e4a |
ViewModel
Documentation: RegionOfInterestChannelsViewModel
1. Purpose
This module implements the RegionOfInterestChannelsViewModel, a core view model for managing and displaying channels associated with Regions of Interest (ROIs) in the DTS test configuration and analysis system. It serves as the data context for the ROI Channels UI, enabling users to view, filter, sort, and select channels for inclusion in one or more ROIs. The view model bridges UI interactions with underlying channel data (from IGroupChannel, ITestChannel, or test metadata), handles ROI channel assignment validation (e.g., scrubbing stale channel references), and coordinates with the event aggregator for cross-component communication (e.g., busy indicators, notifications). It is used in contexts such as ROI definition, CSV/HDF export, and data download workflows.
2. Public Interface
Constructor
RegionOfInterestChannelsViewModel(
IRegionOfInterestChannelsView view,
Prism.Regions.IRegionManager regionManager,
Prism.Events.IEventAggregator eventAggregator,
Unity.IUnityContainer unityContainer)
- Initializes the view model, sets up data binding, registers event subscriptions (
RaiseNotification,BusyIndicatorChangeNotification), and initializesRegionsOfInterestas aBindingList<IRegionOfInterest>.
Properties
-
bool IsDirty { get; private set; }
Indicates whether the underlying data has unsaved changes. Currently alwaysfalsein source. -
bool IsBusy { get; set; }
Binds to UI busy indicator state. Set viaOnBusyIndicatorNotification. -
bool IsMenuIncluded { get; set; } -
bool IsNavigationIncluded { get; set; }
UI layout flags for optional navigation elements. -
IRegionOfInterestChannelsView View { get; set; }
Reference to the associated view. -
InteractionRequest<Notification> NotificationRequest { get; } -
InteractionRequest<Confirmation> ConfirmationRequest { get; }
Prism Interactivity triggers for modal dialogs. -
BindingList<IRegionOfInterest> RegionsOfInterest { get; set; }
List of ROI definitions. On setter, performs scrubbing of stale channel references (see Gotchas), then callsResetDataView(). -
List<DTS.Common.Classes.Groups.GroupChannel> AllChannelsUnfiltered { get; set; }
Raw list of all channels (fromIGroupChannel[]) before filtering. -
ObservableCollection<GroupChannel> AllChannels { get; set; }
Filtered list ofGroupChannelinstances displayed in the UI. -
ObservableCollection<ITestChannel> AllTestChannels { get; set; }
Filtered list ofITestChannelinstances (used when loading from test summary/metadata). -
string[] AllChannelSSNs { get; }
Returns array ofHardware\SerialNumberstrings for all enabled channels (used for ROI export). Handles bothGroupChannelandITestChannelsources. -
BindingList<ChannelEnabler> ChannelList { get; set; }
List ofChannelEnablerobjects representing UI rows (one per channel), each containing ROI inclusion checkboxes (ROIIncludes). Setter wiresListChangedevent. -
ObservableCollection<ColumnDescriptor> Columns { get; set; }
Defines grid column metadata (header, display member, type) forChannelList. -
IsoViewMode ISOViewMode { get; set; }
Controls which channel name variant is used (e.g., ISO, User Code, or raw).
Methods
-
void SetParent(object o)
StoresParentobject (used in event args for ROI channel selection events). -
void SetTest(string path, IsoViewMode viewMode)
Loads test metadata from.dtsfile atpath, populates_testSummary, and triggersFilter()andResetDataView(). -
void SetGroups(ITestSetup testSetup, Dictionary<string, IDASHardware> serialNumberToHardware, IsoViewMode viewMode)
Initializes channel data fromITestSetup(e.g., during ROI definition). Validates DAS assignments, logs phantom assignments, sorts channels, and callsProcessChannels()→Filter()→ResetDataView(). -
void Filter(string term)/void SetFilter(PossibleFilters bridgeFilter)/void Filter(object tag, string term)
Set search term, filter type, or field-specific filter term, then invokeFilter(). -
void ClearAllFilters()
Clears_filterByFielddictionary. -
void Sort(object o, bool bColumnClick)
SortsChannelListby field (e.g.,GroupName,SerialNumber, orROIIncludes[i]). Supports toggling sort direction on repeated clicks. -
void SelectAll(int roiIndex, bool selection)
SetsCheckedstate of allROIIncludes[roiIndex]checkboxes toselection. -
void Activated()/void Cleanup()/Task CleanupAsync()/void Initialize()/Task InitializeAsync()
Lifecycle stubs (no-op in source). -
bool Validate(ref List<string> errors)
Currently always returnstrue. -
void OnPropertyChanged(string propertyName)
RaisesPropertyChangedevent.
3. Invariants
-
Channel scrubbing on ROI assignment change: When
RegionsOfInterestis set, stale channel references (channels no longer present inAllChannelsUnfiltered) are removed fromroi.ChannelNamesandroi.ChannelIds. This is done by comparing againstAllChannelHash(computed fromAllChannelsUnfilteredwith hardware/serial number normalization) andallChannelIdHash. -
Channel ID parsing: Older
.dtsfiles may have compositeChannelIdstrings (e.g.,"H3-3ch_0_2").ParseChannelId()extracts the trailing numeric ID; if parsing fails, returns-1. -
Digital outputs excluded: In
BuildChannelListFromGroupChannels(), channels withsd.IsDigitalOutput()orsd.IsTestSpecificDigitalOutput()are skipped. -
Filtering semantics:
ChannelFilter()uses AND logic across multiple filter fields (a channel must match all active filters).ChannelSearch()uses OR logic (a channel matches if any field contains the search term).ROIIncludesis excluded from search (commented as "not meaningful").
-
Hardware/serial normalization: Channel descriptors use
RegionOfInterest.RemoveParentDASName()andRegionOfInterest.RemoveAssignedByIDFromHardwareString()to normalize hardware strings. -
Calibration caching:
BuildChannelListFromGroupChannels()caches sensor calibrations (cals) to avoid repeated DB lookups.
4. Dependencies
Imports/Usings (External Dependencies)
- Prism:
IRegionManager,IEventAggregator,InteractionRequest<T>,PopupWindowAction(viaNotificationRequest). - Unity:
IUnityContainer. - DTS Common Libraries:
DTS.Common.Classes.Groups,DTS.Common.Enums,DTS.Common.Events.*,DTS.Common.Interface.*,DTS.Common.Utils,DTS.Common.Utilities.Logging.APILogger.DTS.SensorDB(forSensorsCollection,SensorCalibrationList).DTS.Serialization.SliceRaw(forPersistentChannel.GetIsoCode).
- System:
BindingList<T>,ObservableCollection<T>,INotifyPropertyChanged,IComparer<T>.
Internal Dependencies
- Interfaces:
IRegionOfInterestChannelsView,IRegionOfInterest,IGroupChannel,ITestChannel,IDASHardware,IGroup,ITestSetup,ITestSummary.
- Helper Types:
ChannelSerialNumber,HardwareConstants,RegionOfInterest,DTS.Common.Constants.CURRENT_SUFFIX.ChannelEnabler,State,ColumnDescriptor,ChannelEnablerComparer(defined in same file).
- Events:
RaiseNotification,BusyIndicatorChangeNotification,RegionOfInterestChannelsSelectedEvent,PageErrorEvent.
Depended Upon By
- UI layer (
IRegionOfInterestChannelsViewimplementations). - Event subscribers (e.g.,
RegionOfInterestChannelsSelectedEventlisteners). - Export/download workflows (
SetTest(),SetGroups()).
5. Gotchas
-
AllChannelsnull handling:AllChannelSSNsandBuildChannelListFromSummary()guard againstAllChannelsbeingnull(e.g., when loaded via export tile), butBuildChannelListFromGroupChannels()does not checkAllChannelsfornullbefore iteration (relies on?? new ObservableCollection<>()inSetGroups()). -
ROIIncludesindex mismatch: InCheckboxesOnListChanged,LastIndexChangedis used to infer the ROI index, but this relies on theListChangedevent’sNewIndexmatching the ROI index at the time of change. IfChannelListis reordered, this index may become stale. -
ParseChannelIdedge case: IfparsedChannelIdis non-numeric (e.g.,"f9f0bfe8-afc4-4730-8045-8f1e45340573_0_8533"→"8533"is numeric, but"f9f0bfe8-afc4-4730-8045-8f1e45340573"is not),long.TryParsefails and returns-1. This may cause ROI channel assignment to silently drop channels. -
UpdateChannelListheuristic: This method replaces a channel inchannelListif its serial number matches any channel inAllChannelsUnfiltered. This may incorrectly overwrite channels if multiple channels share the same serial number (e.g., same sensor type on different hardware). -
IsDirtyunused: The property is defined but never set totrue, making it unreliable for change tracking. -
Calibration fallback: In
BuildChannelListFromGroupChannels(), ifsd.Calibration?.Records?.Records?[0]?.EngineeringUnitsisnull,"N/A"is used. However,ChannelFilter()forDisplayUnitsuses a separateGetLatestCalibrationBySerialNumber(sd)call, which may yield different results if calibrations change mid-session. -
ChannelSearchredundancy:ChannelSearch(GroupChannel)andChannelSearch(ITestChannel)have overlapping logic, butChannelSearch(GroupChannel)skipsFields.DisplayName(commented as "redundant"), whileChannelSearch(ITestChannel)does not. -
ChannelEnablerComparersort stability: Sorting is case-insensitive but not culture-aware (StringComparison.InvariantCultureIgnoreCase). May produce unexpected orderings for non-ASCII characters. -
SetTest()assumes single test:SetTest()loadstsl[0]unconditionally; iftslis empty, this will throwIndexOutOfRangeException. -
ROIChannelEnablerunused: AROIChannelEnablerclass is defined but never instantiated or used in the source. -
IsBusythread affinity:OnBusyIndicatorNotificationsubscribes withThreadOption.PublisherThread, butIsBusysetter callsOnPropertyChanged("IsBusy"), which may raisePropertyChangedon the publisher thread (UI thread). IfIsBusyis set from a background thread elsewhere, this could cause cross-thread exceptions (though not evident in current usage). -
AllChannelSSNsfor CSV export: The comment//13477 Crash when clicking + to add second ROI for CSV exportimplies a known race condition or null-state issue when dynamically adding ROIs during export. The guard (null != AllChannels && AllChannels.Any()) mitigates but does not eliminate all failure modes. -
No validation of ROI channel count:
SelectAll(int roiIndex, bool selection)checks bounds onroiIndex, but no validation ensuresroiIndexcorresponds to a valid ROI in_regionsOfInterestafterRegionsOfInterestis reassigned. -
ChannelEnabler.LastIndexChangedreset:LastIndexChangedis set onListChanged, but never reset. If multiple changes occur rapidly, it may retain an outdated index. -
ChannelFilterforSampleRate: Usesch.TestSampleRate.ToString()(no format specifier), whileChannelSearchuses bothToString()andToString("N"). Inconsistent formatting may cause filter mismatches. -
ChannelFilterforDisplayUnits: UsesGetLatestCalibrationBySerialNumber(sd)without caching, potentially causing repeated DB lookups inFilter()loops. -
ChannelFilterforDASSerialNumber: Usesch.HardwareChannel?.GetParentDAS().SerialNumber ?? "N/A", butChannelFilter(ITestChannel)usesch.HardwareChannelNamedirectly (noGetParentDAS()), leading to inconsistent behavior. -
ChannelEnabler.Descriptorformat:Descriptor = chHardware + "\\" + serialNumberassumeschHardwareandserialNumberare non-null; if either isnull, this may produce"\\serial"or"hardware\\". -
RegionOfInterestChannelsSelectedEventpublish: Published inCheckboxesOnListChangedafter updating_regionsOfInterest[roiChangedIndex], but the event args use_regionsOfInterest[roiChangedIndex]after the list has been modified. IfroiChangedIndexis stale (see index mismatch), this may publish stale ROI data. -
SetGroups()phantom DAS assignment: Logs an error forch.DASChannelIndex < 0, but continues processing (does not skip the channel). May lead to inconsistent channel sample rates. -
BuildChannelListFromGroupChannels()sensor lookup: Ifsdisnullafter lookup, logs and skips the channel. However,ch.SensorDatais not assigned in this case, so subsequent calls may repeat the lookup. -
ChannelEnabler.GetChannelName(): ReturnsSerialNumberas fallback for missingUserChannelName/ISOChannelName, butAllChannelSSNsusesHardware + "\\" + serialNumber. Inconsistent naming may cause mismatches in ROI channel matching. -
ChannelEnablerComparernull safety: ComparesaValue?.ToString()andbValue?.ToString(), but if both arenull, returns0(equal). If only one isnull,string.Compare(null, "x")returns-1, which may not be intuitive. -
ChannelEnabler.ROIIncludesevent wiring:ROIIncludes.ListChangedis wired in constructor, butChannelEnablerinstances are recreated on everyChannelList = ...assignment. The oldBindingList<State>may not be disposed, potentially causing memory leaks if event handlers are not properly detached (thoughListChangedis not explicitly unhooked in setter). -
ChannelEnabler.LastIndexChangedproperty: Used only to triggerOnPropertyChanged("LastIndexChanged"), but no UI binding or logic consumes this property. Likely tech debt. -
RegionOfInterestChannelsViewModelParentfield: Stored asobject Parent(not typed), and passed inRegionOfInterestChannelsSelectedEventArgs. No validation ensuresParentis of expected type. -
ChannelFilterforROIIncludesfield: TheFields.ROIIncludescase inChannelFilterthrowsArgumentOutOfRangeException. This field is not filterable (as intended), but the enum value is included inEnum.GetValues, so the switch must handle it (currently viadefault: throw). -
ChannelSearchforSampleRate: Usesch.SampleRateHz.ToString()andToString("N"), butToString("N")includes thousand separators (e.g.,"1,000"), which may not match user input like"1000". -
ChannelEnablerGuidString: Generated viaGuid.NewGuid()on everyChannelListassignment. Not persisted across sessions or used for cross-reference (only inCheckboxesOnListChangedfor lookup). Likely a temporary ID with no semantic meaning. -
ChannelEnablerChannelId: Set toConvert.ToInt64(ch.Id)(fromIGroupChannel.Id), butch.Idis astring. Ifch.Idis non-numeric,Convert.ToInt64throwsFormatException. This is a critical risk. (Note:ParseChannelIdis used only forITestChannel.ChannelId.) -
ChannelEnablerChannelIdforITestChannel: UsesParseChannelId(ch.ChannelId), which may return-1for invalid IDs. No validation ensures-1is not a valid channel ID elsewhere. -
ChannelEnablerChannelIdforIGroupChannel: UsesConvert.ToInt64(ch.Id), which may throwFormatExceptionifch.Idis non-numeric (e.g., GUID-based IDs). This is inconsistent withITestChannelhandling and a likely source of crashes. -
ChannelEnablerChannelIdfor ROI matching: InCheckboxesOnListChanged,ChannelIdis used to buildcheckedIds, but ROIChannelIdsmay contain IDs fromParseChannelId(which may be-1), whileIGroupChannel.Idmay throw. This mismatch could cause ROI channel assignments to fail silently. -
ChannelEnablerChannelIdforAllChannelSSNs: Usesch.Id(fromGroupChannel), butAllChannelSSNsdoes not useChannelId—it usesHardware + "\\" + serialNumber. Thus, ROI channel matching relies onChannelId, but export relies on string-based descriptors. Inconsistency may cause mismatches. -
ChannelEnablerChannelIdforParseChannelId: TheParseChannelIdmethod is only used forITestChannel.ChannelId, butChannelEnabler.ChannelIdis set fromIGroupChannel.IdviaConvert.ToInt64(ch.Id). This means ROI channel IDs may be inconsistent between test metadata (ParseChannelId) and group channels (Convert.ToInt64). -
ChannelEnablerChannelIdforChannelFilter: The `Channel