Files
DP44/enriched-partialglm/Common/DTS.Common.Import/Persist.md
2026-04-17 14:55:32 -04:00

14 KiB

source_files, generated_at, model, schema_version, sha256
source_files generated_at model schema_version sha256
Common/DTS.Common.Import/Persist/SaveServer.cs
Common/DTS.Common.Import/Persist/SaveGroupTemplates.cs
Common/DTS.Common.Import/Persist/SaveVariantBase.cs
Common/DTS.Common.Import/Persist/SaveGlobalSettings.cs
Common/DTS.Common.Import/Persist/PersistCalculator.cs
Common/DTS.Common.Import/Persist/SaveSensorModels.cs
Common/DTS.Common.Import/Persist/SaveTestEngineerDetails.cs
Common/DTS.Common.Import/Persist/SaveLabDetails.cs
Common/DTS.Common.Import/Persist/SaveUsers.cs
Common/DTS.Common.Import/Persist/SaveCustomerDetails.cs
Common/DTS.Common.Import/Persist/SaveHardware.cs
Common/DTS.Common.Import/Persist/SaveTestSetupHelper.cs
Common/DTS.Common.Import/Persist/SaveGroups.cs
Common/DTS.Common.Import/Persist/SaveCustomChannels.cs
Common/DTS.Common.Import/Persist/SaveTestSetup.cs
Common/DTS.Common.Import/Persist/SaveCheckoutTestSetup.cs
2026-04-16T11:46:11.806575+00:00 zai-org/GLM-5-FP8 1 695fcda61b684601

DTS.Common.Import.Persist Module Documentation

1. Purpose

This module implements the persistence layer for an import system, responsible for saving various imported data entities (hardware, users, test setups, groups, sensors, etc.) to a database. It follows the Strategy pattern where each SaveVariantBase subclass handles a specific entity type, providing a unified interface (IPersistImport) for the import pipeline while supporting progress tracking, cancellation, and error reporting.


2. Public Interface

SaveVariantBase (Abstract Base Class)

public abstract class SaveVariantBase : IPersistImport
{
    protected ImportObject _importObject;
    protected readonly IPersistCalculator _persistCalculator;
    protected readonly IImportNotification _importNotification;
    protected readonly Func<bool> IsCancelled;

    protected SaveVariantBase(ImportObject importObject, IPersistCalculator persistCalculator, 
        IImportNotification importNotification, Func<bool> isCancelled = null)
    
    public abstract void Save();
}

Base class for all save variants. Provides shared infrastructure for progress reporting, cancellation checking, and access to the import data object.

IPersistCalculator / PersistCalculator

public interface IPersistCalculator
{
    void AddDone();
    void AddDone(double value);
    double ProgressValue { get; }
    void AddToTotal(double value);
}

public class PersistCalculator : IPersistCalculator
{
    public double ProgressValue { get; }  // Returns _done / _total
    public void AddDone();
    public void AddDone(double value);
    public void AddToTotal(double value);  // Throws ArgumentOutOfRangeException if value < 0
}

Tracks progress as a ratio of completed work (_done) to total work (_total).

SaveServer

public class SaveServer : SaveVariantBase
{
    public SaveServer(ImportObject importObject, IPersistCalculator persistCalculator, 
        IImportNotification importNotification, Func<bool> isCancelled = null)
    
    public override void Save();  // Throws NotImplementedException
}

Stub implementation — currently unimplemented.

SaveGroupTemplates

public class SaveGroupTemplates : SaveVariantBase
{
    public SaveGroupTemplates(ImportObject importObject, IPersistCalculator persistCalculator, 
        IImportNotification importNotification, Func<bool> isCancelled = null)
    
    public override void Save();  // Iterates _importObject.GroupTemplates(), updates progress
}

Iterates group templates without persisting them (appears to be a placeholder that only updates progress).

SaveGlobalSettings

public class SaveGlobalSettings : SaveVariantBase
{
    public override void Save();  // Calls SettingsDB.SetGlobalValue(g.Key, g.Value) for each setting
}

Persists global settings via SettingsDB.SetGlobalValue().

SaveSensorModels

public class SaveSensorModels : SaveVariantBase
{
    public User CurrentUser { get; set; }
    
    public override void Save();  // Commits sensor models via SensorModelCollection.SensorModelList.Commit()
}

Persists sensor models. Requires CurrentUser to be set for the commit operation.

SaveTestEngineerDetails

public class SaveTestEngineerDetails : SaveVariantBase
{
    public override void Save();  // Adds test engineers via TestEngineerDetailsList.TestEngineerList.AddTestEngineer()
}

Persists test engineer details. Sets status to ImportExtraStatus.ReadingEngineerDetails.

SaveLabDetails

public class SaveLabDetails : SaveVariantBase
{
    public override void Save();  // Adds labs via LabratoryDetailsList.AddLab()
}

Persists lab details. Skips invalid/blank entries (calls l.IsInvalidBlank()). Sets status to ImportExtraStatus.ReadingLabDetails.

SaveUsers

public class SaveUsers : SaveVariantBase
{
    public IUIItems[] UIItems { get; set; }
    public User CurrentUser { get; set; }
    
    public override void Save();  // Commits users via UserCollection.UsersList.Commit()
}

Persists users. Asserts that CurrentUser.IsAdmin is true (will fail in debug builds if non-admin attempts import). Sets status to ImportExtraStatus.ReadingUsers.

SaveCustomerDetails

public class SaveCustomerDetails : SaveVariantBase
{
    public override void Save();  // Adds customers via CustomerDetailsList.AddCustomer()
}

Persists customer details. Skips invalid/blank entries. Note: Sets status to ImportExtraStatus.ReadingLabDetails (appears to be a copy-paste error).

SaveHardware

public class SaveHardware : SaveVariantBase
{
    public Dictionary<int, int> OldDASIdToNewDASId { get; set; } = new Dictionary<int, int>();
    public Dictionary<int, List<IISOHardware>> TestIdToHardware { get; set; } = new Dictionary<int, List<IISOHardware>>();
    
    public override void Save();  // Commits hardware via DASHardwareList.GetList().Commit()
}

Persists hardware configurations. Tracks DAS ID remapping (OldDASIdToNewDASId) and hardware-to-test relationships (TestIdToHardware). Sorts hardware before processing. Progress includes both hardware items and their channels.

SaveGroups

public class SaveGroups : SaveVariantBase
{
    public bool CanCurrentUserCommitChannelCodes { get; set; } = true;
    public IChannelSetting DefaultZeroMethod { get; set; }
    public IChannelSetting DefaultZeroStart { get; set; }
    public IChannelSetting DefaultZeroEnd { get; set; }
    public IChannelSetting DefaultInitialOffset { get; set; }
    public Dictionary<int?, int> OldGroupIdToNewGroupId { get; set; } = new Dictionary<int?, int>();
    
    public SaveGroups(ImportObject importObject, IPersistCalculator persistCalculator, 
        IImportNotification importNotification, SaveHardware saveHardware, Func<bool> isCancelled = null)
    
    public override void Save();
}

Persists static groups. Requires SaveHardware dependency for DAS ID remapping. Fixes missing zero method parameters and initial offsets. Maps old group IDs to new IDs. Reports warnings for groups with channels assigned to unknown DAS.

SaveCustomChannels

public class SaveCustomChannels : SaveVariantBase
{
    public ISO13499FileDb IsoDb { get; set; }
    public Dictionary<string, string> CustomChannelTextIdToOldChannelId { get; set; } = new Dictionary<string, string>();
    public Dictionary<string, TestObjectChannel> CustomChannelOldChannelIdToChannel { get; set; } = new Dictionary<string, TestObjectChannel>();
    
    public override void Save();
}

Persists custom channels via CustomChannelList.List.Commit(). Handles ID remapping for existing channels. Updates group templates and groups that reference custom channels. Calls CustomChannelList.List.UpdateAll() at the end.

SaveTestSetup

public class SaveTestSetup : SaveVariantBase
{
    public User CurrentUser { get; set; }
    public string TestSetupName { get; set; }
    
    public SaveTestSetup(ImportObject importObject, IPersistCalculator persistCalculator, 
        IImportNotification importNotification, SaveCustomChannels saveCustomChannels, 
        SaveHardware saveHardware, SaveGroups saveGroups, Func<bool> isCancelled = null)
    
    public override void Save();
}

Persists test setups via TestTemplateList.TestTemplatesList.Commit(). Requires dependencies on SaveCustomChannels, SaveHardware, and SaveGroups. Handles special case for ImportFileFormat.SingleTestSetup with custom naming. Deletes existing test setups before import. Updates hardware TestId references after commit.

SaveCheckoutTestSetup

public class SaveCheckoutTestSetup : SaveVariantBase
{
    public User CurrentUser { get; set; }
    
    public SaveCheckoutTestSetup(ImportObject importObject, IPersistCalculator persistCalculator, 
        IImportNotification importNotification, SaveCustomChannels saveCustomChannels, 
        SaveHardware saveHardware, SaveGroups saveGroups, 
        Func<bool> isCancelled = null, string setupName = null)
    
    public override void Save();
}

Persists checkout test setups with specific configuration (checkout mode enabled, missing sensors allowed, etc.). Creates new groups from ChannelsForGroup configuration.

SaveTestSetupHelper (Static Class)

public static class SaveTestSetupHelper
{
    public static void AddHardwareFromEmbeddedGroups(TestTemplate t, SaveGroups saveGroups, 
        List<int> hardwareRemoved, List<int> hardwareIncluded);
    
    public static void FixDasAff(Dictionary<string, DASHardware> hardwareLookup, TestTemplate t);
    
    public static Dictionary<string, DASHardware> PopulateHarwareLookup();
    
    public static void DeleteExistingTestSetups(IEnumerable<TestTemplate> testSetups, string userName);
    
    public static void UpdateLevelTriggers(ref Dictionary<string, string> oldIdToNewIdLookup, 
        SaveCustomChannels saveCustomChannels, IEnumerable<TestTemplate> testSetups);
}

Helper methods extracted for reuse between SaveTestSetup and SaveCheckoutTestSetup.


3. Invariants

  1. Cancellation Check Pattern: All Save() implementations check IsCancelled() at the start of each iteration loop and return immediately if true.

  2. Progress Reporting Pattern: After each item is processed, implementations call _persistCalculator.AddDone() followed by _importNotification.SetProgress(_persistCalculator.ProgressValue).

  3. Constructor Signature: All SaveVariantBase subclasses accept the same base parameters: ImportObject, IPersistCalculator, IImportNotification, and optional Func<bool> isCancelled.

  4. Null Cancellation Handler: If isCancelled is null, SaveVariantBase assigns a lambda returning false.

  5. ProgressValue Calculation: ProgressValue is calculated as _done / _total. Division by zero is possible if AddToTotal() is never called (undefined behavior in source).

  6. AddToTotal Validation: PersistCalculator.AddToTotal() throws ArgumentOutOfRangeException if value < 0.

  7. Hardware ID Mapping: SaveHardware always populates OldDASIdToNewDASId when DAS IDs change during commit.

  8. Group ID Mapping: SaveGroups always populates OldGroupIdToNewGroupId for non-embedded static groups.


4. Dependencies

This module depends on:

  • DTS.Common.Import.Interfaces - IPersistImport interface (inferred)
  • DTS.Common.Settings - SettingsDB for global settings persistence
  • DTS.SensorDB - SensorModelCollection, SensorsCollection for sensor data
  • DTS.Slice.Users - User, UserCollection, IUIItems for user management
  • DataPROWin7.DataModel - Core data models (TestTemplate, DASHardware, CustomerDetails, LabratoryDetails, TestEngineerDetails, CustomChannel, etc.)
  • DTS.Common.Import.Enums - ImportStatus, ImportExtraStatus, PossibleStatus, ImportFileFormat, ImportSeverityError
  • DTS.Common.Interface.Channels - Channel interfaces
  • DTS.Common.Interface.Groups.GroupList - IGroup, IGroupChannel, GroupHelper
  • DTS.Common.SharedResource.Strings - StringResources for localized error messages
  • DTS.Common.Storage - DbOperations for database queries
  • DTS.Common.ISO - ISO13499FileDb, IsoCodeStatics, MMEPossibleChannels
  • DTS.Common.Classes.Groups.ChannelSettings - ChannelSettingBase, IChannelSetting
  • DTS.Common.Interface.DASFactory.Diagnostics - Diagnostics interfaces
  • DTS.Common.Enums - SupportedExportFormatBitFlags

What depends on this module:

  • Cannot be determined from source alone (no consumers shown).

5. Gotchas

  1. SaveServer is unimplemented: Calling Save() on SaveServer throws NotImplementedException.

  2. SaveCustomerDetails status typo: Sets ImportExtraStatus.ReadingLabDetails instead of a customer-specific status (likely copy-paste error from SaveLabDetails).

  3. SaveUsers admin assertion: Uses Trace.Assert to verify admin status — this only triggers in debug builds; production builds will silently proceed with potentially unauthorized operations.

  4. SaveGroupTemplates does nothing: Iterates group templates but performs no persistence — only updates progress.

  5. Division by zero risk: PersistCalculator.ProgressValue returns _done / _total without checking if _total is zero.

  6. SaveHardware sorts in-place: Calls _importObject.Hardware().ToList().Sort() but then iterates the original _importObject.Hardware() — unclear if this has any effect since ToList() creates a copy.

  7. SaveCustomChannels memory concern: Contains commented-out GC.Collect() with a code smell note referencing issue #11287 (out of memory exceptions on large CSV imports).

  8. Interdependent save order: SaveGroups requires SaveHardware to be executed first (needs OldDASIdToNewDASId). SaveTestSetup requires SaveCustomChannels, SaveHardware, and SaveGroups. Order of execution matters.

  9. SaveCheckoutTestSetup clears groups: Explicitly clears t.Groups and t.ChannelsForGroup before repopulating — this modifies the import object state.

  10. SaveTestSetup and SaveCheckoutTestSetup delete existing setups: Both call DeleteExistingTestSetups() which removes existing test setups by name before importing.