Oakrey.Applications.FileParsing.LDF 1.0.1

dotnet add package Oakrey.Applications.FileParsing.LDF --version 1.0.1
                    
NuGet\Install-Package Oakrey.Applications.FileParsing.LDF -Version 1.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Oakrey.Applications.FileParsing.LDF" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Oakrey.Applications.FileParsing.LDF" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="Oakrey.Applications.FileParsing.LDF" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Oakrey.Applications.FileParsing.LDF --version 1.0.1
                    
#r "nuget: Oakrey.Applications.FileParsing.LDF, 1.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Oakrey.Applications.FileParsing.LDF@1.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Oakrey.Applications.FileParsing.LDF&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=Oakrey.Applications.FileParsing.LDF&version=1.0.1
                    
Install as a Cake Tool

Oakrey.Applications.FileParsing.LDF

A specialized file parsing library for LDF (LIN Description File) files, built on the FileParsing framework. Provides automatic parsing of LDF files into LdfMatrix objects with reactive updates, file browsing integration, and extended file management capabilities for LIN (Local Interconnect Network) automotive applications.

Features

LDF File Parsing

  • Automatic Parsing: Parses LDF files using Oakrey.LDF library
  • LdfMatrix Support: Returns structured LIN database with frames, signals, and metadata
  • Extended Browsing: Integrates with FileBrowsing.Extended for file operations
  • Template Support: Includes default LDF template for creating new files

File Management Integration

  • Create New LDF Files: Create files from customizable templates
  • File Browsing: Browse and select LDF files in directories
  • Reactive Updates: Observable pattern for file change notifications
  • Auto-Load Support: Optionally load LDF files at application startup

LIN Database Access

  • Frames: Access LIN frame definitions
  • Signals: Work with signal definitions and parameters
  • Network Nodes: Retrieve ECU and node information
  • Schedules: Access LIN schedule tables
  • Attributes: Access extended attributes and metadata

Settings Integration

  • Persistent Configuration: Settings automatically saved and restored
  • Auto-Load Control: Enable/disable automatic file loading
  • Last Selected File: Remembers last opened LDF file

Built on FileParsing Framework

  • Extends FileParsingServiceBase<LdfMatrix>
  • Inherits all file parsing framework features
  • Consistent API with other file parsers (DBC, INI)

Installation

You can install the package via NuGet Package Manager, Package Manager Console or the .NET CLI.

NuGet Package Manager

  1. Open your project in Visual Studio.
  2. Navigate to Tools > NuGet Package Manager > Manage NuGet Packages for Solution....
  3. Search for Oakrey.Applications.FileParsing.LDF and click Install.

.NET CLI

Run the following command in your terminal:

dotnet add package Oakrey.Applications.FileParsing.LDF

Package Manager Console

Run the following command in your Package Manager Console:

Install-Package Oakrey.Applications.FileParsing.LDF

Prerequisites

This package requires:

  • Oakrey.Applications.FileParsing - Base file parsing framework
  • Oakrey.Applications.FileBrowsing.Extended - Extended file browsing with templates
  • Oakrey.LDF - LDF file parsing library (LdfMatrix)

Usage Examples

Basic Dependency Injection Setup

using Microsoft.Extensions.DependencyInjection;
using Oakrey.Applications.FileBrowsing.Extended;
using Oakrey.Applications.FileParsing.LDF;
using Oakrey.Applications.UserPrompts.Windows;

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // Register user prompts (required)
        services.AddUserPromptsService();
        
        // Register extended file browsing for LDF files
        services.AddSingleton<IFileExtendedBrowsingServiceSettings, 
                             LdfExtendedFileBrowsingServiceSettings>();
        services.AddSingleton<IFileBrowsingService, IExtendedFileBrowsingService,
                             ExtendedFileBrowsingService>();
        
        // Register LDF parsing service settings
        services.AddSingleton<ISettingsService<LdfServiceSettings>, 
                             SettingsService<LdfServiceSettings>>();
        services.AddSingleton<ILdfServiceSettings, LdfServiceSettings>();
        
        // Register LDF parsing service
        services.AddSingleton<ILdfService, LdfService>();
    }
}

Creating LDF File Browsing Settings

using Oakrey.Applications.FileBrowsing;
using Oakrey.Applications.FileBrowsing.Extended;
using Oakrey.Applications.Settings;

public class LdfExtendedFileBrowsingServiceSettings 
    : FileBrowsingServiceSettingsBase, IFileExtendedBrowsingServiceSettings
{
    public LdfExtendedFileBrowsingServiceSettings(
        ISettingsService<LdfExtendedFileBrowsingServiceSettings> settingsService)
        : base(settingsService)
    {
    }

    public override string FileExtension => "ldf";
    public override string SearchPattern => "*.ldf";
    
    // Template loaded from external file or defined inline
    public string NewFileTemplate
    {
        get
        {
            string templatePath = Path.Combine(
                AppDomain.CurrentDomain.BaseDirectory, 
                "ldfNew.txt");
            
            if (File.Exists(templatePath))
            {
                return File.ReadAllText(templatePath);
            }
            
            // Fallback template
            return "LIN_description_file;\nLIN_protocol_version = \"2.1\";\n";
        }
    }
}

Using the LDF Service

using Oakrey.Applications.FileParsing.LDF;
using Oakrey.LDF;

public class LinManager
{
    private readonly ILdfService _ldfService;

    public LinManager(ILdfService ldfService)
    {
        _ldfService = ldfService;
        
        // Subscribe to LDF file changes
        _ldfService.FileParsed.Subscribe(OnLdfFileParsed);
    }

    private void OnLdfFileParsed(LdfMatrix? ldfMatrix)
    {
        if (ldfMatrix != null)
        {
            Console.WriteLine($"LDF file loaded: {_ldfService.FilePath}");
            Console.WriteLine($"Frames: {ldfMatrix.Frames.Count}");
            Console.WriteLine($"Nodes: {ldfMatrix.Nodes.Count}");
            
            // Process the LIN database
            ProcessLinDatabase(ldfMatrix);
        }
        else
        {
            Console.WriteLine("No LDF file loaded");
        }
    }

    public void ProcessLinDatabase(LdfMatrix matrix)
    {
        // Access LIN frames
        foreach (var frame in matrix.Frames)
        {
            Console.WriteLine($"Frame: {frame.Name} (ID: {frame.Id})");
            
            // Access signals in the frame
            foreach (var signal in frame.Signals)
            {
                Console.WriteLine($"  Signal: {signal.Name}");
                Console.WriteLine($"    Offset: {signal.Offset}");
                Console.WriteLine($"    Length: {signal.Size} bits");
            }
        }
        
        // Access LIN nodes
        Console.WriteLine("\nNodes:");
        foreach (var node in matrix.Nodes)
        {
            Console.WriteLine($"  - {node.Name}");
        }
        
        // Access schedules
        Console.WriteLine("\nSchedules:");
        foreach (var schedule in matrix.Schedules)
        {
            Console.WriteLine($"  Schedule: {schedule.Name}");
        }
    }
}

Accessing Current LDF Matrix

public class LinDataProcessor
{
    private readonly ILdfService _ldfService;

    public LinDataProcessor(ILdfService ldfService)
    {
        _ldfService = ldfService;
    }

    public void ProcessLinFrame(byte frameId, byte[] data)
    {
        // Access current LDF matrix
        if (_ldfService.Instance == null)
        {
            Console.WriteLine("No LDF file loaded");
            return;
        }

        var ldfMatrix = _ldfService.Instance;
        
        // Find frame by ID
        var frame = ldfMatrix.Frames.FirstOrDefault(f => f.Id == frameId);
        
        if (frame == null)
        {
            Console.WriteLine($"Frame ID {frameId} not found in LDF");
            return;
        }

        Console.WriteLine($"Processing frame: {frame.Name}");
        
        // Decode signals
        foreach (var signal in frame.Signals)
        {
            double value = DecodeSignal(signal, data);
            double physicalValue = value * signal.Factor + signal.Offset;
            
            Console.WriteLine($"  {signal.Name}: {physicalValue}");
        }
    }

    private double DecodeSignal(Signal signal, byte[] data)
    {
        // Implement signal decoding logic
        ulong rawValue = 0;
        
        for (int i = 0; i < signal.Size; i++)
        {
            int bitPos = signal.Offset + i;
            int byteIndex = bitPos / 8;
            int bitIndex = bitPos % 8;
            
            if (byteIndex < data.Length)
            {
                if ((data[byteIndex] & (1 << bitIndex)) != 0)
                {
                    rawValue |= (1UL << i);
                }
            }
        }
        
        return rawValue;
    }
}

WPF ViewModel Integration

using Oakrey.Applications.FileParsing.LDF;
using System.ComponentModel;
using System.Windows.Input;
using CommunityToolkit.Mvvm.Input;

public class LdfViewModel : INotifyPropertyChanged
{
    private readonly ILdfService _ldfService;
    private LdfMatrix? _currentMatrix;

    public LdfViewModel(ILdfService ldfService)
    {
        _ldfService = ldfService;
        
        // Subscribe to LDF file changes
        _ldfService.FileParsed.Subscribe(OnLdfParsed);
        
        // Initialize with current matrix
        _currentMatrix = _ldfService.Instance;
        
        LoadFileCommand = new AsyncRelayCommand<string>(LoadFileAsync);
        RefreshCommand = new RelayCommand(Refresh);
    }

    public LdfMatrix? CurrentMatrix
    {
        get => _currentMatrix;
        set
        {
            if (_currentMatrix != value)
            {
                _currentMatrix = value;
                OnPropertyChanged(nameof(CurrentMatrix));
                OnPropertyChanged(nameof(IsFileLoaded));
                OnPropertyChanged(nameof(FrameCount));
                OnPropertyChanged(nameof(NodeCount));
            }
        }
    }

    public bool IsFileLoaded => _ldfService.IsFilePathCorrect;
    
    public string CurrentFilePath => _ldfService.FilePath;
    
    public int FrameCount => CurrentMatrix?.Frames.Count ?? 0;
    
    public int NodeCount => CurrentMatrix?.Nodes.Count ?? 0;
    
    public List<string> FrameNames => 
        CurrentMatrix?.Frames.Select(f => f.Name).ToList() ?? new List<string>();

    public IAsyncRelayCommand<string> LoadFileCommand { get; }
    public ICommand RefreshCommand { get; }

    private void OnLdfParsed(LdfMatrix? matrix)
    {
        CurrentMatrix = matrix;
        OnPropertyChanged(nameof(CurrentFilePath));
    }

    private async Task LoadFileAsync(string? filePath)
    {
        if (!string.IsNullOrEmpty(filePath))
        {
            var fileInfo = new FileInfo(filePath);
            await _ldfService.LoadFile(fileInfo);
        }
    }

    private void Refresh()
    {
        CurrentMatrix = _ldfService.Instance;
    }

    public event PropertyChangedEventHandler? PropertyChanged;
    
    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Manual File Loading

public class LdfFileLoader
{
    private readonly ILdfService _ldfService;

    public LdfFileLoader(ILdfService ldfService)
    {
        _ldfService = ldfService;
    }

    public async Task<LdfMatrix?> LoadLdfFileAsync(string filePath)
    {
        var fileInfo = new FileInfo(filePath);
        
        if (!fileInfo.Exists)
        {
            Console.WriteLine($"LDF file not found: {filePath}");
            return null;
        }

        // Manually load the file
        var ldfMatrix = await _ldfService.LoadFile(fileInfo);
        
        if (ldfMatrix != null)
        {
            Console.WriteLine($"Loaded LDF file: {filePath}");
            Console.WriteLine($"Frames: {ldfMatrix.Frames.Count}");
            Console.WriteLine($"Signals: {ldfMatrix.Frames.Sum(f => f.Signals.Count)}");
            return ldfMatrix;
        }

        return null;
    }

    public async Task<LdfMatrix?> LoadWithCancellationAsync(
        string filePath, 
        CancellationToken cancellationToken)
    {
        var fileInfo = new FileInfo(filePath);
        
        try
        {
            var ldfMatrix = await _ldfService.LoadFile(fileInfo, cancellationToken);
            
            if (ldfMatrix != null && !cancellationToken.IsCancellationRequested)
            {
                return ldfMatrix;
            }
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("Load operation cancelled");
        }

        return null;
    }
}

Pre-Loading at Application Startup

using Oakrey.Applications;

public class ApplicationStartup
{
    private readonly ILdfService _ldfService;

    public ApplicationStartup(ILdfService ldfService)
    {
        _ldfService = ldfService;
    }

    public async Task InitializeAsync(CancellationToken cancellationToken)
    {
        // Pre-load LDF file if AutoLoad is enabled
        if (_ldfService is IPreLoadable preLoadable)
        {
            await preLoadable.Preload(cancellationToken);
            
            if (_ldfService.Instance != null)
            {
                Console.WriteLine("LDF file pre-loaded successfully");
                Console.WriteLine($"File: {_ldfService.FilePath}");
                Console.WriteLine($"Frames: {_ldfService.Instance.Frames.Count}");
            }
        }
    }
}

Working with LIN Frames and Signals

public class LinFrameAnalyzer
{
    private readonly ILdfService _ldfService;

    public LinFrameAnalyzer(ILdfService ldfService)
    {
        _ldfService = ldfService;
    }

    public void AnalyzeFrame(string frameName)
    {
        if (_ldfService.Instance == null)
        {
            Console.WriteLine("No LDF file loaded");
            return;
        }

        var frame = _ldfService.Instance.Frames
            .FirstOrDefault(f => f.Name == frameName);

        if (frame == null)
        {
            Console.WriteLine($"Frame '{frameName}' not found");
            return;
        }

        Console.WriteLine($"Frame: {frame.Name}");
        Console.WriteLine($"  Frame ID: {frame.Id}");
        Console.WriteLine($"  Length: {frame.Length} bytes");
        Console.WriteLine($"  Publisher: {frame.Publisher}");
        Console.WriteLine($"  Signals: {frame.Signals.Count}");
        Console.WriteLine();

        foreach (var signal in frame.Signals)
        {
            Console.WriteLine($"  Signal: {signal.Name}");
            Console.WriteLine($"    Offset: {signal.Offset} bits");
            Console.WriteLine($"    Size: {signal.Size} bits");
            Console.WriteLine($"    Init Value: {signal.InitValue}");
            Console.WriteLine($"    Publisher: {signal.Publisher}");
            Console.WriteLine($"    Subscribers: {string.Join(", ", signal.Subscribers)}");
            Console.WriteLine();
        }
    }

    public void ListAllFrames()
    {
        if (_ldfService.Instance == null)
        {
            Console.WriteLine("No LDF file loaded");
            return;
        }

        Console.WriteLine("LIN Frames:");
        Console.WriteLine("ID  | Name                | Length | Signals | Publisher");
        Console.WriteLine("----|---------------------|--------|---------|----------");

        foreach (var frame in _ldfService.Instance.Frames.OrderBy(f => f.Id))
        {
            Console.WriteLine($"{frame.Id,3} | {frame.Name,-19} | {frame.Length,6} | {frame.Signals.Count,7} | {frame.Publisher}");
        }
    }

    public void ListAllNodes()
    {
        if (_ldfService.Instance == null)
        {
            Console.WriteLine("No LDF file loaded");
            return;
        }

        Console.WriteLine("Network Nodes:");
        foreach (var node in _ldfService.Instance.Nodes)
        {
            Console.WriteLine($"  - {node.Name}");
            Console.WriteLine($"    Protocol: {node.ProtocolVersion}");
            Console.WriteLine($"    Configured NAD: {node.ConfiguredNAD}");
        }
    }

    public void ListSchedules()
    {
        if (_ldfService.Instance == null)
        {
            Console.WriteLine("No LDF file loaded");
            return;
        }

        Console.WriteLine("LIN Schedules:");
        foreach (var schedule in _ldfService.Instance.Schedules)
        {
            Console.WriteLine($"  Schedule: {schedule.Name}");
            Console.WriteLine($"    Entries: {schedule.Entries.Count}");
            
            foreach (var entry in schedule.Entries)
            {
                Console.WriteLine($"      - {entry.FrameName} @ {entry.Delay}ms");
            }
        }
    }
}

Complete Application Example

using System.Windows;
using Microsoft.Extensions.DependencyInjection;
using Oakrey.Applications.FileBrowsing.Extended;
using Oakrey.Applications.FileParsing.LDF;
using Oakrey.Applications.UserPrompts.Windows;

public partial class App : Application
{
    private IServiceProvider _serviceProvider;

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        var services = new ServiceCollection();
        
        // Register user prompts
        services.AddUserPromptsService();
        
        // Register extended file browsing for LDF files
        services.AddSingleton<ISettingsService<LdfExtendedFileBrowsingServiceSettings>, 
                             SettingsService<LdfExtendedFileBrowsingServiceSettings>>();
        services.AddSingleton<IFileExtendedBrowsingServiceSettings, 
                             LdfExtendedFileBrowsingServiceSettings>();
        services.AddSingleton<IFileBrowsingService, IExtendedFileBrowsingService,
                             ExtendedFileBrowsingService>();
        
        // Register LDF parsing service
        services.AddSingleton<ISettingsService<LdfServiceSettings>, 
                             SettingsService<LdfServiceSettings>>();
        services.AddSingleton<ILdfServiceSettings, LdfServiceSettings>();
        services.AddSingleton<ILdfService, LdfService>();
        
        // Register application services
        services.AddTransient<LinManager>();
        services.AddTransient<MainViewModel>();
        
        _serviceProvider = services.BuildServiceProvider();
        
        // Pre-load LDF file
        var ldfService = _serviceProvider.GetRequiredService<ILdfService>();
        if (ldfService is IPreLoadable preLoadable)
        {
            Task.Run(() => preLoadable.Preload(CancellationToken.None)).Wait();
        }
        
        var mainWindow = new MainWindow
        {
            DataContext = _serviceProvider.GetRequiredService<MainViewModel>()
        };
        
        mainWindow.Show();
    }
}

API Reference

ILdfService

Extends IFileParsingService<LdfMatrix>:

Property Type Description
Instance LdfMatrix? Currently parsed LDF file
FileParsed IObservable<LdfMatrix?> Observable for file parse events
FilePath string Path of the currently selected file
IsFilePathCorrect bool True if file was parsed successfully

LdfMatrix Structure

From Oakrey.LDF:

Property Type Description
Frames List<Frame> LIN frame definitions
Nodes List<Node> Network nodes (Master/Slaves)
Schedules List<Schedule> LIN schedule tables
ProtocolVersion string LDF protocol version

Frame Structure

Property Type Description
Name string Frame name
Id byte Frame identifier (0-63)
Length int Data length (1-8 bytes)
Publisher string Publishing node
Signals List<Signal> Signal definitions

Signal Structure

Property Type Description
Name string Signal name
Offset int Bit offset in frame
Size int Signal size in bits
InitValue int Initial value
Publisher string Publishing node
Subscribers List<string> Subscribing nodes

ILdfServiceSettings

Extends IAutoLoadServiceSettings:

Property Type Description
AutoLoad bool Enable automatic loading on startup

About LIN and LDF Files

LIN (Local Interconnect Network) is a serial network protocol used for communication between components in vehicles. It's typically used for lower-speed, less critical applications compared to CAN bus.

LDF (LIN Description File) is an XML-based file format that describes:

  • Network topology and nodes
  • Frame definitions and signals
  • Signal encodings and physical values
  • Schedule tables for frame transmission
  • Diagnostic frames and services

LDF files are the LIN equivalent of DBC files for CAN networks.

Features in Detail

Automatic Parsing

  • Uses LdfMatrix from Oakrey.LDF
  • Async parsing for large files
  • Full LDF 2.x specification support

Error Handling

  • File not found errors shown to user
  • Parse errors displayed with user-friendly messages
  • Graceful handling of malformed LDF files

Reactive Updates

  • Subscribe to FileParsed observable
  • Get notified when files are parsed or selection changes
  • Always have access to current LdfMatrix

Integration

  • Works seamlessly with FileBrowsing.Extended service
  • Automatic reparsing on file selection change
  • Persistent settings for last selected file
  • Template support for creating new LDF files

When to Use

Use FileParsing.LDF when:

  • You need to parse and monitor LDF LIN database files
  • You want automatic reloading when files change
  • You need reactive updates for LIN database changes
  • You want persistent file selection across sessions
  • You need integration with file browsing and management UI
  • You're developing automotive LIN applications

Use Oakrey.LDF directly when:

  • You only need one-time LDF file reading
  • You don't need change tracking or observables
  • You're working with non-UI scenarios

Requirements

  • .NET 10 or higher
  • Oakrey.Applications.FileParsing
  • Oakrey.Applications.FileBrowsing.Extended
  • Oakrey.LDF (LdfMatrix parser)

Project Information

Contributing

Contributions are welcome! Feel free to open issues or submit pull requests to improve the package.

License

This project is licensed under the MIT License. See the LICENSE file for details.

Product Compatible and additional computed target framework versions.
.NET net10.0-windows7.0 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.1 58 3/18/2026
1.0.0 74 3/13/2026