Skip to content

feat: Import SD card logging sessions into application#375

Open
tylerkron wants to merge 8 commits intomainfrom
feature/sd-card-import
Open

feat: Import SD card logging sessions into application#375
tylerkron wants to merge 8 commits intomainfrom
feature/sd-card-import

Conversation

@tylerkron
Copy link
Contributor

@tylerkron tylerkron commented Feb 6, 2026

User description

Summary

  • Add SdCardSessionImporter service that parses SD card .bin log files (via core library's SdCardFileParser) and bulk-inserts them into the desktop app's SQLite database as LoggingSession + DataSample entities
  • Support three import paths: local file (no device needed), stream, and USB device download
  • Add per-file "Import" button and "Import All" button in the Device Logs tab for USB-connected devices
  • Add "Import .bin File" button in the Application Logs tab for importing local files without a device
  • Add DownloadSdCardFileAsync and DeleteSdCardFileAsync to the desktop IStreamingDevice interface
  • Add conditional local core project reference support via DaqifiCoreProjectPath MSBuild property (needed until core fix: updated menu name with correct capitalizaiton #110/chore: Update build workflow to build MSI installer #111 are published to NuGet)

Test plan

  • Import a local .bin file via "Import .bin File" button → session appears in session list → viewable in plot → exportable to CSV
  • Connect USB device → Device Logs tab → click "Import" on a file → progress shown → session appears in Application Logs
  • "Import All" imports all listed files sequentially with progress
  • Import large file → progress reporting works, memory stays bounded
  • Cancel/error during import shows appropriate dialog, no partial session left
  • Build with local core: dotnet build -p:DaqifiCoreProjectPath="path/to/Daqifi.Core.csproj"
  • Build without local core still uses NuGet package (once core 0.15+ is published)

Prerequisites

Closes #374

🤖 Generated with Claude Code


PR Type

Enhancement


Description

  • Add SdCardSessionImporter service to parse and bulk-import SD card .bin log files into SQLite database

  • Implement three import paths: local file, stream, and USB device download with progress tracking

  • Add "Import .bin File" button in Application Logs tab for local file imports

  • Add per-file "Import" and "Import All" buttons in Device Logs tab for USB device imports

  • Extend IStreamingDevice interface with DownloadSdCardFileAsync and DeleteSdCardFileAsync methods

  • Support conditional local core project reference via DaqifiCoreProjectPath MSBuild property


Diagram Walkthrough

flowchart LR
  A["SD Card .bin File"] -->|ImportFromFileAsync| B["SdCardSessionImporter"]
  C["USB Device"] -->|ImportFromDeviceAsync| B
  D["Stream"] -->|ImportFromStreamAsync| B
  B -->|Parse| E["SdCardFileParser"]
  E -->|Map to DataSample| F["SQLite Database"]
  F -->|Create LoggingSession| G["Application Logs"]
  B -->|Bulk Insert| F
Loading

File Walkthrough

Relevant files
Enhancement
SdCardSessionImporter.cs
Core SD card import service with batch processing               

Daqifi.Desktop/Loggers/SdCardSessionImporter.cs

  • New service class implementing three import entry points:
    ImportFromFileAsync, ImportFromStreamAsync, and ImportFromDeviceAsync
  • Maps core SdCardLogEntry objects to desktop DataSample entities with
    per-channel color assignment
  • Implements batch bulk-insert logic with configurable batch size (1000
    samples) for performance
  • Supports ImportOptions for session name override, overwrite behavior,
    and post-import device file deletion
  • Provides ImportProgress class for progress reporting during import
    operations
+284/-0 
IStreamingDevice.cs
Extend interface with SD card operations                                 

Daqifi.Desktop/Device/IStreamingDevice.cs

  • Add DownloadSdCardFileAsync method to download files from device SD
    card over USB with progress tracking
  • Add DeleteSdCardFileAsync method to delete files from device SD card
  • Both methods include cancellation token support
+14/-0   
AbstractStreamingDevice.cs
Implement SD card operations in device class                         

Daqifi.Desktop/Device/AbstractStreamingDevice.cs

  • Add using statement for Daqifi.Core.Device.SdCard namespace
  • Implement DownloadSdCardFileAsync method delegating to core device
  • Implement DeleteSdCardFileAsync method delegating to core device
  • Both methods use GetCoreDeviceForSd() helper for validation
+16/-0   
DaqifiViewModel.cs
Add local file import command to main view model                 

Daqifi.Desktop/ViewModels/DaqifiViewModel.cs

  • Add ImportSdCardLogFile relay command for importing local .bin files
    via file dialog
  • Show progress overlay with sample count during import operation
  • Handle import completion by adding session to LoggingManager and
    showing success dialog
  • Implement error handling with user-friendly error messages and
    cancellation support
+54/-0   
DeviceLogsViewModel.cs
Add device file import commands with batch support             

Daqifi.Desktop/ViewModels/DeviceLogsViewModel.cs

  • Add ImportFile relay command for importing individual files from
    connected USB device
  • Add ImportAllFiles relay command for batch importing all device files
    with per-file error handling
  • Both commands show progress overlay with file name and sample count
  • Implement success/failure dialogs and add imported sessions to
    LoggingManager
  • Add logger instance for error tracking
+123/-1 
MainWindow.xaml
Add import button to application logs UI                                 

Daqifi.Desktop/MainWindow.xaml

  • Add "Import .bin File" button in Application Logs tab header with
    import icon and label
  • Button positioned on left side of toolbar alongside existing
    export/delete buttons
  • Uses ImportSdCardLogFileCommand from view model
+9/-0     
DeviceLogsView.xaml
Add import buttons to device logs UI                                         

Daqifi.Desktop/View/DeviceLogsView.xaml

  • Add "Import All" button in Device Logs header with import icon,
    positioned right of refresh button
  • Add per-file "Import" button in file list grid view as new column
  • Adjust column widths to accommodate new import button column
  • Add grid column definition for new button column
  • Minor formatting cleanup of whitespace
+46/-17 
Configuration changes
Daqifi.Desktop.csproj
Support conditional local core project reference                 

Daqifi.Desktop/Daqifi.Desktop.csproj

  • Make Daqifi.Core NuGet package reference conditional on
    DaqifiCoreProjectPath being empty
  • Add conditional ProjectReference to local core project when
    DaqifiCoreProjectPath MSBuild property is set
  • Enables development workflow with local core library without modifying
    project file
+5/-1     

Add the ability to import SD card .bin log files into the desktop app's
SQLite database for viewing, analysis, and CSV export. This implements
the desktop side of #126, wiring up the core library's SdCardFileParser
and ISdCardOperations to the desktop data model.

New SdCardSessionImporter service with three entry points:
- ImportFromFileAsync: import a local .bin file (no device needed)
- ImportFromStreamAsync: import from a stream
- ImportFromDeviceAsync: download from USB device then import

Maps core SdCardLogEntry to desktop DataSample entities (one per analog/
digital channel per sample), creates LoggingSession in SQLite, and uses
batch bulk inserts for performance.

UI additions:
- "Import .bin File" button in APPLICATION LOGS tab for local file import
- Per-file "Import" button in DEVICE LOGS file list for USB device import
- "Import All" button in DEVICE LOGS header to batch import all files
- Progress overlay during download and import operations

Also adds DownloadSdCardFileAsync and DeleteSdCardFileAsync to the
desktop IStreamingDevice interface, and conditional local core project
reference support via DaqifiCoreProjectPath MSBuild property.

Closes #374

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Feb 6, 2026

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Arbitrary file deletion

Description: The code deletes downloadResult.FilePath on cleanup without validating that the path is a
trusted temp location, so if an attacker can influence SdCardDownloadResult.FilePath
(e.g., via device/core layer), it could enable arbitrary file deletion on the host.
SdCardSessionImporter.cs [66-92]

Referred Code
// Download to temp file
var downloadResult = await device.DownloadSdCardFileAsync(fileName, null, ct);

try
{
    // Parse and import
    var parser = new SdCardFileParser();
    var logSession = await parser.ParseFileAsync(downloadResult.FilePath!, null, ct);
    var session = await ImportSessionAsync(logSession, options, progress, ct);

    // Optionally delete from device after successful import
    if (options.DeleteFromDeviceAfterImport)
    {
        await device.DeleteSdCardFileAsync(fileName, ct);
    }

    return session;
}
finally
{
    // Clean up temp file


 ... (clipped 6 lines)
Ticket Compliance
🟡
🎫 #112
🔴 Fix the device connection screen so DAQiFi devices still appear when the host PC has
multiple network interfaces (some disconnected or with no devices).
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Swallowed cleanup errors: Temp-file deletion errors are silently swallowed and downloadResult.FilePath! is
dereferenced without validation, which can hide failures and cause unhandled null-path
issues.

Referred Code
    var logSession = await parser.ParseFileAsync(downloadResult.FilePath!, null, ct);
    var session = await ImportSessionAsync(logSession, options, progress, ct);

    // Optionally delete from device after successful import
    if (options.DeleteFromDeviceAfterImport)
    {
        await device.DeleteSdCardFileAsync(fileName, ct);
    }

    return session;
}
finally
{
    // Clean up temp file
    if (downloadResult.FilePath != null && File.Exists(downloadResult.FilePath))
    {
        try { File.Delete(downloadResult.FilePath); }
        catch { /* best effort cleanup */ }
    }
}

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status:
Leaks exception message: User-facing dialogs display raw ex.Message, which may expose internal details (e.g., file
paths, parser/database specifics) to end users.

Referred Code
catch (Exception ex)
{
    _appLogger.Error(ex, "Error importing SD card log file");
    await ShowMessage("Import Failed",
        $"Failed to import file: {ex.Message}",
        MessageDialogStyle.Affirmative);

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Missing action audit: Critical actions (device file download/import and optional device deletion) are not
consistently audit-logged with actor/user context and outcome, so event reconstruction may
be incomplete.

Referred Code
public async Task<LoggingSession> ImportFromDeviceAsync(
    IStreamingDevice device,
    string fileName,
    ImportOptions? options = null,
    IProgress<ImportProgress>? progress = null,
    CancellationToken ct = default)
{
    options ??= new ImportOptions();

    // Download to temp file
    var downloadResult = await device.DownloadSdCardFileAsync(fileName, null, ct);

    try
    {
        // Parse and import
        var parser = new SdCardFileParser();
        var logSession = await parser.ParseFileAsync(downloadResult.FilePath!, null, ct);
        var session = await ImportSessionAsync(logSession, options, progress, ct);

        // Optionally delete from device after successful import
        if (options.DeleteFromDeviceAfterImport)


 ... (clipped 15 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status:
Exception logging review: Exceptions are logged via _logger.Error(ex, ...) and may include sensitive environment
details (e.g., local paths/device identifiers) depending on underlying exception content,
which needs verification against logging policy.

Referred Code
catch (Exception ex)
{
    _logger.Error(ex, $"Error importing {file.FileName}");
    await ShowMessage("Import Failed",
        $"Failed to import {file.FileName}: {ex.Message}",
        MessageDialogStyle.Affirmative);

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Unvalidated file name: SD-card fileName inputs are passed to device download/delete operations without visible
validation/normalization, so it requires verification that the core/device layer prevents
path traversal or invalid name injection.

Referred Code
/// <summary>
/// Downloads a file from the device's SD card over USB to a temporary file.
/// </summary>
Task<SdCardDownloadResult> DownloadSdCardFileAsync(
    string fileName,
    IProgress<SdCardTransferProgress>? progress = null,
    CancellationToken ct = default);

/// <summary>
/// Deletes a file from the device's SD card.
/// </summary>
Task DeleteSdCardFileAsync(string fileName, CancellationToken ct = default);

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Feb 6, 2026

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Prevent orphaned data on session overwrite

When overwriting an existing session in CreateSession, first delete all
DataSample records associated with the old session before removing the
LoggingSession entity to prevent orphaned data.

Daqifi.Desktop/Loggers/SdCardSessionImporter.cs [200-209]

 // Check for existing session with same name
 if (options.OverwriteExistingSession)
 {
     var existing = context.Sessions.FirstOrDefault(s => s.Name == sessionName);
     if (existing != null)
     {
+        // First, delete all data samples associated with the old session
+        context.Set<DataSample>().Where(ds => ds.LoggingSessionID == existing.ID).ExecuteDelete();
+
         context.Sessions.Remove(existing);
         context.SaveChanges();
     }
 }
  • Apply / Chat
Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a critical data integrity issue where overwriting a session would leave orphaned data, and provides an efficient solution to delete associated records before removing the session.

High
Avoid race conditions in ID generation

In CreateSession, remove the manual session ID generation (max(ID) + 1) and let
the database auto-generate the primary key to prevent race conditions during
concurrent import operations.

Daqifi.Desktop/Loggers/SdCardSessionImporter.cs [211-221]

-// Generate new session ID (same pattern as LoggingManager.OnActiveChanged)
-var ids = context.Sessions.AsNoTracking().Select(s => s.ID).ToList();
-var newId = ids.Count > 0 ? ids.Max() + 1 : 0;
-
-var session = new LoggingSession(newId, sessionName)
+// Let the database generate the new session ID to avoid race conditions
+var session = new LoggingSession(0, sessionName) // Pass a temporary ID
 {
     SessionStart = logSession.FileCreatedDate ?? DateTime.Now
 };
 
 context.Sessions.Add(session);
-context.SaveChanges();
+context.SaveChanges(); // After this, session.ID will be populated with the DB-generated value
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a potential race condition in manual primary key generation and proposes a robust solution by letting the database manage auto-incrementing keys, which prevents data corruption.

Medium
Remove Task.Run wrapping

In ImportFile, remove the unnecessary Task.Run wrapper and directly await the
importer.ImportFromDeviceAsync call to simplify the code and avoid redundant
thread pool usage.

Daqifi.Desktop/ViewModels/DeviceLogsViewModel.cs [199-200]

-var session = await Task.Run(() =>
-    importer.ImportFromDeviceAsync(SelectedDevice, file.FileName, null, progress, CancellationToken.None));
+var session = await importer.ImportFromDeviceAsync(SelectedDevice, file.FileName, null, progress, CancellationToken.None);
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies the redundant use of Task.Run on an already asynchronous method and recommends a cleaner, more efficient implementation by directly awaiting the async call.

Low
High-level
Inject importer service into ViewModels

Instead of creating SdCardSessionImporter instances directly in ViewModels,
define an ISdCardSessionImporter interface and use dependency injection. This
will decouple the ViewModels from the import logic, improving testability.

Examples:

Daqifi.Desktop/ViewModels/DaqifiViewModel.cs [952]
            var importer = new SdCardSessionImporter(loggingContext);
Daqifi.Desktop/ViewModels/DeviceLogsViewModel.cs [192]
            var importer = new SdCardSessionImporter(loggingContext);

Solution Walkthrough:

Before:

// In DaqifiViewModel.cs
public partial class DaqifiViewModel : ObservableObject
{
    [RelayCommand]
    private async Task ImportSdCardLogFile()
    {
        // ...
        var loggingContext = App.ServiceProvider.GetRequiredService<IDbContextFactory<LoggingContext>>();
        var importer = new SdCardSessionImporter(loggingContext);
        var session = await importer.ImportFromFileAsync(dialog.FileName, ...);
        // ...
    }
}

// In SdCardSessionImporter.cs
public class SdCardSessionImporter { /* ... implementation ... */ }

After:

// In ISdCardSessionImporter.cs (new file)
public interface ISdCardSessionImporter 
{
    Task<LoggingSession> ImportFromFileAsync(...);
    // ... other import methods
}

// In SdCardSessionImporter.cs
public class SdCardSessionImporter : ISdCardSessionImporter { /* ... */ }

// In DaqifiViewModel.cs
public partial class DaqifiViewModel : ObservableObject
{
    private readonly ISdCardSessionImporter _importer;

    public DaqifiViewModel(ISdCardSessionImporter importer, ...) 
    {
        _importer = importer;
    }

    [RelayCommand]
    private async Task ImportSdCardLogFile()
    {
        // ...
        var session = await _importer.ImportFromFileAsync(dialog.FileName, ...);
        // ...
    }
}
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies tight coupling where SdCardSessionImporter is directly instantiated in ViewModels, and proposing dependency injection via an interface is a significant architectural improvement that enhances testability and maintainability.

Medium
General
Use async bulk insert and transaction

In FlushBatchAsync, replace the synchronous database calls (BeginTransaction,
BulkInsert, Commit) with their asynchronous counterparts (BeginTransactionAsync,
BulkInsertAsync, CommitAsync) to ensure the operation is non-blocking.

Daqifi.Desktop/Loggers/SdCardSessionImporter.cs [226-234]

 private async Task FlushBatchAsync(List<DataSample> batch, CancellationToken ct)
 {
     ct.ThrowIfCancellationRequested();
 
     using var context = _loggingContext.CreateDbContext();
-    using var transaction = context.Database.BeginTransaction();
-    context.BulkInsert(batch);
-    transaction.Commit();
+    await using var transaction = await context.Database.BeginTransactionAsync(ct);
+    await context.BulkInsertAsync(batch, cancellationToken: ct);
+    await transaction.CommitAsync(ct);
 }
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that a method declared as async is performing synchronous, blocking database operations, and proposes using the proper asynchronous equivalents to improve performance and avoid thread blocking.

Medium
Avoid wrapping async methods in Task.Run

In ImportSdCardLogFile, remove the unnecessary Task.Run wrapper and directly
await the importer.ImportFromFileAsync call to avoid redundant thread pool
usage.

Daqifi.Desktop/ViewModels/DaqifiViewModel.cs [959-965]

-var session = await Task.Run(() =>
-    importer.ImportFromFileAsync(dialog.FileName, null, progress, CancellationToken.None));
+var session = await importer.ImportFromFileAsync(dialog.FileName, null, progress, CancellationToken.None);
 
 Application.Current.Dispatcher.Invoke(() =>
 {
     LoggingManager.Instance.LoggingSessions.Add(session);
 });
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies the redundant use of Task.Run on an already asynchronous method and recommends a cleaner, more efficient implementation by directly awaiting the async call.

Low
Learned
best practice
Validate inputs at boundaries

Trim and validate fileName (reject null/whitespace) before calling core APIs to
avoid hard-to-diagnose failures and inconsistent behavior.

Daqifi.Desktop/Device/AbstractStreamingDevice.cs [502-515]

 public async Task<SdCardDownloadResult> DownloadSdCardFileAsync(
     string fileName,
     IProgress<SdCardTransferProgress>? progress = null,
     CancellationToken ct = default)
 {
+    var normalizedFileName = fileName?.Trim();
+    if (string.IsNullOrWhiteSpace(normalizedFileName))
+        throw new ArgumentException("File name is required.", nameof(fileName));
+
     var coreDevice = GetCoreDeviceForSd();
-    return await coreDevice.DownloadSdCardFileAsync(fileName, progress, ct);
+    return await coreDevice.DownloadSdCardFileAsync(normalizedFileName, progress, ct);
 }
 
 public async Task DeleteSdCardFileAsync(string fileName, CancellationToken ct = default)
 {
+    var normalizedFileName = fileName?.Trim();
+    if (string.IsNullOrWhiteSpace(normalizedFileName))
+        throw new ArgumentException("File name is required.", nameof(fileName));
+
     var coreDevice = GetCoreDeviceForSd();
-    await coreDevice.DeleteSdCardFileAsync(fileName, ct);
+    await coreDevice.DeleteSdCardFileAsync(normalizedFileName, ct);
 }
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why:
Relevant best practice - Normalize and validate external/user-provided inputs (trim and reject whitespace-only values) at method boundaries.

Low
Log cleanup exceptions with context
Suggestion Impact:The cleanup File.Delete catch block was changed from silent swallowing to logging a warning when temp file deletion fails (though it logs only the message string rather than the exception object and full suggested context).

code diff:

         finally
         {
             // Clean up temp file
-            if (downloadResult.FilePath != null && File.Exists(downloadResult.FilePath))
-            {
-                try { File.Delete(downloadResult.FilePath); }
-                catch { /* best effort cleanup */ }
+            if (File.Exists(downloadResult.FilePath))
+            {
+                try
+                {
+                    File.Delete(downloadResult.FilePath);
+                }
+                catch (Exception ex)
+                {
+                    _logger.Warning($"Failed to clean up temp file '{downloadResult.FilePath}': {ex.Message}");
+                }
             }

Avoid swallowing cleanup exceptions silently; log the caught exception with
context (e.g., temp file path and SD fileName) to make cleanup failures
diagnosable.

Daqifi.Desktop/Loggers/SdCardSessionImporter.cs [84-92]

 finally
 {
     // Clean up temp file
     if (downloadResult.FilePath != null && File.Exists(downloadResult.FilePath))
     {
-        try { File.Delete(downloadResult.FilePath); }
-        catch { /* best effort cleanup */ }
+        try
+        {
+            File.Delete(downloadResult.FilePath);
+        }
+        catch (Exception ex)
+        {
+            _logger.Error(ex, "Failed to delete temp SD import file (TempPath={TempPath}, SdFileName={SdFileName})",
+                downloadResult.FilePath, fileName);
+        }
     }
 }

[Suggestion processed]

Suggestion importance[1-10]: 5

__

Why:
Relevant best practice - Log exceptions by passing the exception object (not only message) and include concrete runtime context to aid diagnosis.

Low
  • Update

- Update Daqifi.Core NuGet from 0.14.0 to 0.15.0 across all projects
  to include SD card parsing/download types needed for import feature
- Add null safety check for downloadResult.FilePath before use
- Validate downloaded file is in expected temp directory
- Log cleanup errors instead of silently swallowing them
- Sanitize user-facing error messages to avoid leaking internal details
- Add audit logging for import, download, and delete operations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@tylerkron tylerkron requested a review from a team as a code owner February 7, 2026 04:55
tylerkron and others added 4 commits February 6, 2026 22:16
- Fix StartSdCardLogging sending individual channel enable commands
  instead of a combined bitmask. Each EnableAdcChannels SCPI command
  replaces the previous setting, so sending one-channel-at-a-time
  resulted in only the last channel being logged. Now builds a single
  bitmask for all active analog channels before sending.
- Suppress streaming data from appearing on the Live Graph when in
  Log to Device mode by checking Mode in OnStreamMessageReceived.
- Add diagnostic logging to SdCardSessionImporter for device config,
  first sample details, and channel discovery to aid debugging.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…stics

- Changed ImportFromDeviceAsync to use ParseAsync with the original device
  filename instead of ParseFileAsync with the temp path. This fixes:
  1. Session names showing temp GUIDs instead of the original log filename
  2. Date extraction from log filenames (e.g., log_20260206_101851.bin)
- Added download result diagnostics (reported size, disk size, temp path)
- Added empty file detection with descriptive error message
- Added warning log when 0 samples are imported for debugging

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Log first two samples (instead of just first) to verify timestamps
  are properly spaced after the core parser fix
- Add warning when TimestampFrequency is 0, indicating firmware may
  not include TimestampFreq in logged messages
- Include sample index in diagnostic log for easier debugging

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Device firmware does not include TimestampFreq in SD card log data,
causing all imported samples to have identical timestamps.

Changes:
- Store TimestampFrequency on AbstractStreamingDevice from status messages
- Expose TimestampFrequency on IStreamingDevice interface
- Pass device's TimestampFrequency as FallbackTimestampFrequency when
  parsing SD card files via ImportFromDeviceAsync
- Add diagnostic logging for first two samples to verify timestamp spacing
- Add warning when no TimestampFrequency is available

Requires daqifi-core PR #117 (FallbackTimestampFrequency support)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link

github-actions bot commented Feb 8, 2026

📊 Code Coverage Report

Summary

Summary
Generated on: 2/8/2026 - 5:50:00 AM
Coverage date: 2/8/2026 - 5:49:48 AM - 2/8/2026 - 5:49:57 AM
Parser: MultiReport (5x Cobertura)
Assemblies: 5
Classes: 117
Files: 148
Line coverage: 10.6% (627 of 5861)
Covered lines: 627
Uncovered lines: 5234
Coverable lines: 5861
Total lines: 18186
Branch coverage: 10.6% (224 of 2107)
Covered branches: 224
Total branches: 2107
Method coverage: Feature is only available for sponsors

Coverage

DAQiFi - 9.6%
Name Line Branch
DAQiFi 9.6% 9.9%
Daqifi.Desktop.App 3% 0%
Daqifi.Desktop.Channel.AbstractChannel 22.7% 25%
Daqifi.Desktop.Channel.AnalogChannel 42.5% 25%
Daqifi.Desktop.Channel.Channel 11.5% 0%
Daqifi.Desktop.Channel.ChannelColorManager 100% 100%
Daqifi.Desktop.Channel.DataSample 90.4%
Daqifi.Desktop.Channel.DigitalChannel 0% 0%
Daqifi.Desktop.Commands.CompositeCommand 0% 0%
Daqifi.Desktop.Commands.HostCommands 0%
Daqifi.Desktop.Commands.WeakEventHandlerManager 0% 0%
Daqifi.Desktop.Configuration.FirewallConfiguration 90.6% 66.6%
Daqifi.Desktop.Configuration.WindowsFirewallWrapper 64% 68.4%
Daqifi.Desktop.ConnectionManager 39.6% 50%
Daqifi.Desktop.Converters.BoolToActiveStatusConverter 0% 0%
Daqifi.Desktop.Converters.BoolToConnectionStatusConverter 0% 0%
Daqifi.Desktop.Converters.BoolToStatusColorConverter 0% 0%
Daqifi.Desktop.Converters.ConnectionTypeToColorConverter 0% 0%
Daqifi.Desktop.Converters.ConnectionTypeToUsbConverter 0% 0%
Daqifi.Desktop.Converters.InvertedBoolToVisibilityConverter 0% 0%
Daqifi.Desktop.Converters.ListToStringConverter 0% 0%
Daqifi.Desktop.Converters.NotNullToVisibilityConverter 0% 0%
Daqifi.Desktop.Converters.OxyColorToBrushConverter 0% 0%
Daqifi.Desktop.Converters.StringRightConverter 0% 0%
Daqifi.Desktop.Device.AbstractStreamingDevice 6.1% 3.5%
Daqifi.Desktop.Device.DeviceInfoConverter 0% 0%
Daqifi.Desktop.Device.DeviceMessage 0%
Daqifi.Desktop.Device.HidDevice.HidFirmwareDevice 0%
Daqifi.Desktop.Device.NativeMethods 0%
Daqifi.Desktop.Device.SerialDevice.SerialDeviceHelper 0% 0%
Daqifi.Desktop.Device.SerialDevice.SerialStreamingDevice 7.8% 7.8%
Daqifi.Desktop.Device.SerialDevice.UsbDevice 0% 0%
Daqifi.Desktop.Device.WiFiDevice.DaqifiStreamingDevice 16.4% 0%
Daqifi.Desktop.DialogService.DialogService 0% 0%
Daqifi.Desktop.DialogService.ServiceLocator 0% 0%
Daqifi.Desktop.DuplicateDeviceCheckResult 100%
Daqifi.Desktop.Exporter.OptimizedLoggingSessionExporter 30.9% 32.9%
Daqifi.Desktop.Exporter.SampleData 0%
Daqifi.Desktop.Helpers.BooleanConverter`1 0% 0%
Daqifi.Desktop.Helpers.BooleanToInverseBoolConverter 0% 0%
Daqifi.Desktop.Helpers.BooleanToVisibilityConverter 0%
Daqifi.Desktop.Helpers.EnumDescriptionConverter 100% 100%
Daqifi.Desktop.Helpers.IntToVisibilityConverter 0% 0%
Daqifi.Desktop.Helpers.MyMultiValueConverter 0%
Daqifi.Desktop.Helpers.NaturalSortHelper 100% 100%
Daqifi.Desktop.Helpers.VersionHelper 98.2% 66.2%
Daqifi.Desktop.Logger.DatabaseLogger 0% 0%
Daqifi.Desktop.Logger.LoggedSeriesLegendItem 0% 0%
Daqifi.Desktop.Logger.LoggingContext 0%
Daqifi.Desktop.Logger.LoggingManager 0% 0%
Daqifi.Desktop.Logger.LoggingSession 26.6% 0%
Daqifi.Desktop.Logger.PlotLogger 0% 0%
Daqifi.Desktop.Logger.SummaryLogger 0% 0%
Daqifi.Desktop.Loggers.FirmwareUpdatationManager 5.8% 0%
Daqifi.Desktop.Loggers.ImportOptions 0%
Daqifi.Desktop.Loggers.ImportProgress 0% 0%
Daqifi.Desktop.Loggers.SdCardSessionImporter 0% 0%
Daqifi.Desktop.MainWindow 0% 0%
Daqifi.Desktop.Migrations.InitialSQLiteMigration 0%
Daqifi.Desktop.Migrations.LoggingContextModelSnapshot 0%
Daqifi.Desktop.Models.AddProfileModel 0%
Daqifi.Desktop.Models.DaqifiSettings 86.3% 100%
Daqifi.Desktop.Models.DebugDataCollection 0% 0%
Daqifi.Desktop.Models.DebugDataModel 0% 0%
Daqifi.Desktop.Models.Notifications 0%
Daqifi.Desktop.Models.SdCardFile 0% 0%
Daqifi.Desktop.Services.WindowsPrincipalAdminChecker 0%
Daqifi.Desktop.Services.WpfMessageBoxService 0%
Daqifi.Desktop.UpdateVersion.VersionNotification 0% 0%
Daqifi.Desktop.View.AddChannelDialog 0% 0%
Daqifi.Desktop.View.AddProfileConfirmationDialog 0% 0%
Daqifi.Desktop.View.AddprofileDialog 0% 0%
Daqifi.Desktop.View.ConnectionDialog 0% 0%
Daqifi.Desktop.View.DebugWindow 0% 0%
Daqifi.Desktop.View.DeviceLogsView 0% 0%
Daqifi.Desktop.View.DuplicateDeviceDialog 0% 0%
Daqifi.Desktop.View.ErrorDialog 0% 0%
Daqifi.Desktop.View.ExportDialog 0% 0%
Daqifi.Desktop.View.FirmwareDialog 0% 0%
Daqifi.Desktop.View.Flyouts.ChannelsFlyout 0% 0%
Daqifi.Desktop.View.Flyouts.DevicesFlyout 0% 0%
Daqifi.Desktop.View.Flyouts.FirmwareFlyout 0% 0%
Daqifi.Desktop.View.Flyouts.LiveGraphFlyout 0% 0%
Daqifi.Desktop.View.Flyouts.LoggedSessionFlyout 0% 0%
Daqifi.Desktop.View.Flyouts.NotificationsFlyout 0% 0%
Daqifi.Desktop.View.Flyouts.SummaryFlyout 0% 0%
Daqifi.Desktop.View.Flyouts.UpdateProfileFlyout 0% 0%
Daqifi.Desktop.View.SelectColorDialog 0% 0%
Daqifi.Desktop.View.SettingsDialog 0% 0%
Daqifi.Desktop.View.SuccessDialog 0% 0%
Daqifi.Desktop.ViewModels.AddChannelDialogViewModel 0% 0%
Daqifi.Desktop.ViewModels.AddProfileConfirmationDialogViewModel 0% 0%
Daqifi.Desktop.ViewModels.AddProfileDialogViewModel 0% 0%
Daqifi.Desktop.ViewModels.ConnectionDialogViewModel 0% 0%
Daqifi.Desktop.ViewModels.DaqifiViewModel 0% 0%
Daqifi.Desktop.ViewModels.DeviceLogsViewModel 0% 0%
Daqifi.Desktop.ViewModels.DeviceSettingsViewModel 0% 0%
Daqifi.Desktop.ViewModels.DuplicateDeviceDialogViewModel 0%
Daqifi.Desktop.ViewModels.ErrorDialogViewModel 0%
Daqifi.Desktop.ViewModels.ExportDialogViewModel 0% 0%
Daqifi.Desktop.ViewModels.FirmwareDialogViewModel 0% 0%
Daqifi.Desktop.ViewModels.SelectColorDialogViewModel 0% 0%
Daqifi.Desktop.ViewModels.SettingsViewModel 0%
Daqifi.Desktop.ViewModels.SuccessDialogViewModel 0%
Daqifi.Desktop.WindowViewModelMapping.IWindowViewModelMappingsContract 0%
Daqifi.Desktop.WindowViewModelMapping.WindowViewModelMappings 0%
Daqifi.Desktop.Bootloader - 19.6%
Name Line Branch
Daqifi.Desktop.Bootloader 19.6% 16.9%
Daqifi.Desktop.Bootloader.Crc16 100% 100%
Daqifi.Desktop.Bootloader.Exceptions.FirmwareUpdateException 0%
Daqifi.Desktop.Bootloader.FirmwareDownloader 82% 66.6%
Daqifi.Desktop.Bootloader.Pic32Bootloader 0% 0%
Daqifi.Desktop.Bootloader.Pic32BootloaderMessageConsumer 0% 0%
Daqifi.Desktop.Bootloader.Pic32BootloaderMessageProducer 80.9% 100%
Daqifi.Desktop.Bootloader.WifiFirmwareDownloader 0% 0%
Daqifi.Desktop.Bootloader.WifiModuleUpdater 0% 0%
Daqifi.Desktop.Common - 45.9%
Name Line Branch
Daqifi.Desktop.Common 45.9% 33.3%
Daqifi.Desktop.Common.Loggers.AppLogger 42.1% 33.3%
Daqifi.Desktop.Common.Loggers.NoOpLogger 100%
Daqifi.Desktop.DataModel - 100%
Name Line Branch
Daqifi.Desktop.DataModel 100% ****
Daqifi.Desktop.DataModel.Device.DeviceInfo 100%
Daqifi.Desktop.IO - 0%
Name Line Branch
Daqifi.Desktop.IO 0% ****
Daqifi.Desktop.IO.Messages.MessageEventArgs`1 0%

Coverage report generated by ReportGeneratorView full report in build artifacts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Import SD card logging sessions into application

1 participant