Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
Expand All @@ -43,7 +43,7 @@ jobs:
needs: format
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
Expand Down Expand Up @@ -90,7 +90,7 @@ jobs:
needs: format
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
Expand Down Expand Up @@ -140,7 +140,7 @@ jobs:
needs: format
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
Expand Down
20 changes: 10 additions & 10 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
Expand Down Expand Up @@ -96,7 +96,7 @@ jobs:
rm ultralog-linux

- name: Upload artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: ultralog-linux${{ needs.check-tag.outputs.suffix }}
path: output/
Expand All @@ -107,7 +107,7 @@ jobs:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
Expand All @@ -126,7 +126,7 @@ jobs:
Remove-Item "output/ultralog-windows${{ needs.check-tag.outputs.suffix }}.exe"

- name: Upload artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: ultralog-windows${{ needs.check-tag.outputs.suffix }}
path: output/
Expand All @@ -146,7 +146,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
Expand Down Expand Up @@ -342,7 +342,7 @@ jobs:
rm -f "$API_KEY_PATH"

- name: Upload artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: ultralog-macos-${{ matrix.arch }}${{ needs.check-tag.outputs.suffix }}
path: output/*.dmg
Expand All @@ -361,10 +361,10 @@ jobs:
run: echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT

- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: Download all artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
path: artifacts

Expand All @@ -375,7 +375,7 @@ jobs:

- name: Create stable release
if: needs.check-tag.outputs.is_prerelease == 'false'
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v3
with:
files: |
artifacts/ultralog-macos-intel/*.dmg
Expand All @@ -391,7 +391,7 @@ jobs:

- name: Create prerelease
if: needs.check-tag.outputs.is_prerelease == 'true'
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v3
with:
files: |
artifacts/ultralog-macos-intel-beta/*.dmg
Expand Down
50 changes: 40 additions & 10 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ UltraLog is a high-performance ECU (Engine Control Unit) log viewer written in p

## Build Commands

The crate is on **edition 2024** with `rust-version = "1.95"`. The MSRV is set by
egui/eframe 0.36, not by the edition (2024 itself only needs 1.85) — so bumping
egui is what moves it. Edition 2024 means let-chains (`if a && let Some(b) = ..`)
are available and clippy's `collapsible_if` will ask for them, and that rustfmt
sorts imports with the 2024 style ordering.

```bash
# Development build
cargo build
Expand Down Expand Up @@ -266,7 +272,9 @@ The parser system uses a trait-based design for supporting multiple ECU formats:
- **`parsers/types.rs`** - Core types: `Log`, `Channel`, `Value`, `Meta`, `EcuType`, `ComputedChannelInfo`, and the `Parseable` trait
- **`parsers/haltech.rs`** - Haltech CSV parser (NSP exports)
- **`parsers/ecumaster.rs`** - ECUMaster EMU Pro CSV parser (semicolon/tab delimited)
- **`parsers/romraider.rs`** - RomRaider CSV parser with unit extraction from headers
- **`parsers/romraider.rs`** - RomRaider CSV parser with unit extraction from headers. Also the
catch-all for generic `Time,...` CSVs that no earlier parser claims — see the time-unit
contract below.
- **`parsers/speeduino.rs`** - Speeduino/rusEFI MLG binary format parser
- **`parsers/aim.rs`** - AiM XRK/DRK binary format parser for motorsport data loggers
- **`parsers/link.rs`** - Link ECU LLG binary format parser
Expand Down Expand Up @@ -314,6 +322,21 @@ Before the text detection chain runs, the content is normalized twice, so indivi
1. A leading **UTF-8 BOM** is stripped (`trim_start_matches('\u{FEFF}')`). Windows and mobile exporters commonly prepend one.
2. A leading block of **`#`-prefixed comment lines** is dropped via `parsers::strip_leading_comment_lines`, so the real CSV header row is the first line detection sees. MHD Tuning and OBD-II apps such as OBDLink write a metadata preamble there; without this the header is never found and the log loads with zero channels (issue #78). Only the *leading* contiguous block is removed — a `#` after the header row is data.

**RomRaider time-column unit contract** (`src/parsers/romraider.rs`):

`RomRaider::detect` accepts any CSV whose first column starts with `time`, which makes it the
effective catch-all for generic OBD-II exports, not just Subaru logs. The unit is therefore not
implied by the format — it must be read from the header:

- `Time (msec)` / `Time(ms)` / `Time (milliseconds)` -> milliseconds
- `Time (sec)` / `Time (s)` / `Time (seconds)` -> seconds
- a bare `Time` with no parenthesised unit -> **milliseconds**, RomRaider's own convention

Milliseconds must be tested before seconds, since `sec` is a substring of `msec`. Hardcoding the
`/1000.0` is what made a 2049-second OBDLink log render as 2.05 seconds (issue #80). Note this
interacts with the dispatch order above: `MotorsportElectronics::detect` runs first precisely
because it also leads with a `Time` column, and only matches a first column of exactly `Time`.

**Load-bearing invariant:** a parser whose own fingerprint lives inside that comment preamble must run its `detect()` against the **raw** text, before the strip. `Mhd::detect` is the current case, which is why it is dispatched ahead of `dispatch_text_content` rather than inside the chain. Anything added to the chain itself sees comment-stripped content.

**Haltech parser load-bearing behaviors** (`src/parsers/haltech.rs`, added for wall-clock-timestamped exports):
Expand Down Expand Up @@ -408,26 +431,32 @@ Handled in `UltraLogApp::handle_keyboard_shortcuts` (`src/app.rs`); ignored whil

## Key Dependencies

- **eframe/egui** (0.34) - Native GUI framework
- **egui_plot** (0.35) - Charting/plotting
- **eframe/egui** (0.36) - Native GUI framework
- **egui_plot** (0.37) - Charting/plotting
- **rfd** (0.17) - Native file dialogs
- **open** (5) - Cross-platform URL/email opening
- **strum** (0.28) - Enum string conversion for channel types
- **regex** (1.12) - Log file parsing
- **ureq** (3.3) - HTTP client for auto-updates and OpenECU Alliance API
- **regex** (1.13) - Log file parsing
- **ureq** (3.4) - HTTP client for auto-updates and OpenECU Alliance API
- **semver** (1.0) - Version comparison
- **serde_yml** (0.0.12) - YAML parsing for adapter/protocol specs (replaces deprecated `serde_yaml`)
- **printpdf** (0.9) - PDF generation
- **serde_norway** (0.9) - YAML parsing for adapter/protocol specs. Replaced `serde_yml`, which was
flagged unsound (RUSTSEC-2025-0068) and then deprecated; `serde_norway` is the maintained
`serde_yaml` fork, and unlike `serde_yaml_ng` its libyaml backend is maintained too.
Do not go back to `serde_yml` or `serde_yaml`.
- **printpdf** (0.12) - PDF generation
- **image** (0.25) - PNG export
- **memmap2** (0.9) - Memory-mapped file loading for large files
- **rayon** (1.11) - Parallel iteration for parsing
- **rayon** (1.12) - Parallel iteration for parsing
- **dirs** (6.0) - Cross-platform app data directory detection for cache and settings
- **rmcp** (0.12) - MCP server implementation (`src/mcp/`), used with `axum` as the HTTP transport
- **rmcp** (3.1) - MCP server implementation (`src/mcp/`), used with `axum` as the HTTP transport.
3.x fixes a DNS-rebinding hole in the streamable-HTTP transport; its `StreamableHttpService`
config defaults `allowed_hosts` to localhost/127.0.0.1/::1, so keep passing `Default::default()`
rather than clearing it.
- **tokio** (1) - Async runtime backing the MCP server
- **axum** (0.8) - HTTP server framework for the embedded MCP endpoint
- **schemars** (1.0) - JSON schema generation for MCP tool definitions
- **arboard** (3.6) - Clipboard support (histogram copy/paste)
- **rust-i18n** (3.1) - Internationalization / locale strings
- **rust-i18n** (4.2) - Internationalization / locale strings
- **uuid** (1.0) - Anonymous user ID generation for analytics
- **chrono** (0.4) - Date/time parsing (Locomotive, Woolich timestamps)
- **encoding_rs** (0.8) - Character encoding detection/conversion (e.g., UTF-16 BlueDriver exports)
Expand All @@ -449,6 +478,7 @@ Example log files are in `exampleLogs/` organized by ECU type:
- `exampleLogs/mhd/` - MHD Tuning CSV exports (VIN redacted)
- `exampleLogs/motorsportElectronics/` - Motorsport Electronics ME Tuner CSV exports
- `exampleLogs/bluedriver/` - BlueDriver OBD-II CSV exports
- `exampleLogs/obdlink/` - OBDLink (iOS) OBD-II CSV exports (parsed by the RomRaider chain)
- `exampleLogs/dynamicEFI/` - DynamicEFI EBL WhatsUp CSV exports
- `exampleLogs/locomotive/` - Locomotive datalogger CSV exports
- `exampleLogs/rusefi/`, `exampleLogs/speeduino/` - MLG binary logs
Expand Down
Loading