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
32 changes: 32 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: CI

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]

jobs:
test:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: "pip"

- name: Install dependencies
run: pip install -e ".[dev]"

- name: Lint (ruff check)
run: ruff check .

- name: Format check (ruff format)
run: ruff format --check .

- name: Tests
run: pytest
69 changes: 69 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Release

on:
push:
tags:
- "v*.*.*"

permissions:
contents: write

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: "pip"

- name: Install dependencies
run: pip install -e ".[dev]"

- name: Lint (ruff check)
run: ruff check .

- name: Format check (ruff format)
run: ruff format --check .

- name: Tests
run: pytest

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
body: |
## forscan-tools ${{ github.ref_name }}

Safety-first FORScan helper for Ford/Lincoln/Mercury/Mazda diagnostics and configuration.

### Commands

| Command | Description |
|---------|-------------|
| `parse-abt` | Parse `.abt` binary payloads; export to CSV / JSON / JSONL |
| `decode-dtc` | Interpret DTCs with severity and triage steps |
| `plan-change` | Build a pre-check, execution, and rollback checklist |
| `trust-report` | Confidence report with official-source citations |
| `explain` | Plain-language guide for As-Built, ABT, ECC, VID, TRID |

### Quick Start

```bash
pip install forscan-tools
python forscan_tools.py trust-report
python forscan_tools.py explain --list-topics
python forscan_tools.py decode-dtc --code P0171
python forscan_tools.py plan-change --module ABS --parameter TireSize \
--current 235/65R17 --target 245/65R17
```

### Safety Notes

- This tool does **not** write to your vehicle.
- Always backup before any FORScan write operation.
- Use stable battery power during configuration/programming sessions.

See [README](README.md) for full documentation.
32 changes: 15 additions & 17 deletions forscan_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
import re
import struct
import textwrap
from collections.abc import Iterable
from dataclasses import dataclass
from datetime import datetime
from enum import Enum
from enum import StrEnum
from pathlib import Path
from typing import Iterable


@dataclass(frozen=True)
Expand All @@ -29,7 +29,7 @@ class ParsedRecord:
interpretation: str


class SafetyLevel(str, Enum):
class SafetyLevel(StrEnum):
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
Expand Down Expand Up @@ -308,9 +308,7 @@ class TopicExplanation:
),
"trid": TopicExplanation(
topic="TCM TRID",
summary=(
"TRID is transmission characterization data and is safety/drivability sensitive."
),
summary=("TRID is transmission characterization data and is safety/drivability sensitive."),
why_it_matters=(
"Checksum/protection and format constraints can apply.",
"Bad changes can cause shifting or transmission behavior issues.",
Expand Down Expand Up @@ -416,9 +414,7 @@ def write_csv(parsed_data: Iterable[ParsedRecord], csv_file_path: Path) -> None:
writer = csv.writer(csv_file)
writer.writerow(["offset", "name", "value", "interpretation"])
for record in parsed_data:
writer.writerow(
[record.offset, record.name, record.value, record.interpretation]
)
writer.writerow([record.offset, record.name, record.value, record.interpretation])


def write_json(parsed_data: Iterable[ParsedRecord], json_file_path: Path) -> None:
Expand Down Expand Up @@ -474,9 +470,7 @@ def plan_change(
target_value: str,
) -> ChangePlan:
module_key = module.strip().lower()
safety_level = (
SafetyLevel.HIGH if module_key in SAFETY_CRITICAL_MODULES else SafetyLevel.MEDIUM
)
safety_level = SafetyLevel.HIGH if module_key in SAFETY_CRITICAL_MODULES else SafetyLevel.MEDIUM

pre_checks = (
"Connect stable battery maintainer before any write",
Expand All @@ -503,7 +497,8 @@ def plan_change(
)
if safety_level is SafetyLevel.HIGH:
warnings = warnings + (
"Safety-critical module detected: use OEM procedure and do not proceed without backup power.",
"Safety-critical module detected: use OEM procedure and do not proceed"
" without backup power.",
)

return ChangePlan(
Expand Down Expand Up @@ -644,15 +639,18 @@ def print_topic_explanation(explanation: TopicExplanation) -> None:

def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
description="FORScan helper for ABT parsing, Ford DTC interpretation, and safe change planning.",
description=(
"FORScan helper for ABT parsing, Ford DTC interpretation, and safe change planning."
),
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=textwrap.dedent(
"""
Examples:
python forscan_tools.py parse-abt --file .\\abt\\sample.abt --out output.csv --jsonl output.jsonl
python forscan_tools.py parse-abt --file .\\abt\\sample.abt --out out.csv
python forscan_tools.py decode-dtc --code P0171 --code U0121
python forscan_tools.py plan-change --module ABS --parameter TireSize --current 235/65R17 --target 245/65R17
python forscan_tools.py explain --topic asbuilt --topic ecc
python forscan_tools.py plan-change --module ABS --parameter TireSize \\
--current 235/65R17 --target 245/65R17
python forscan_tools.py explain --topic asbuilt --topic ecc
"""
),
)
Expand Down