{"id":"76be479a-2db3-4c92-aae1-f2b9eaa44067","slug":"canforge-dbckit","kind":"skill","name":"dbckit","summary":"Parse, edit, validate, diff, and encode/decode DBC (CAN database) files in Python","owner":"canforge","repoUrl":"https://github.com/canforge/dbckit","license":"MIT","licenseTier":"permissive","trust":"community","stars":0,"match":null,"fewStars":true,"format":"skill","createdAt":"2026-04-20T23:20:00.000Z","addedAt":"2026-10-14T05:16:08.499Z","logoUrl":"https://avatars.githubusercontent.com/u/278158619?v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["databases","files-documents"],"tags":["automotive","can-bus","dbc","j1939","python"],"runtime":"python","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-14T22:54:19.266Z","sample":false,"url":"https://conduits.dev/tools/canforge-dbckit","language":"Python","transports":["stdio"],"install":{"pypi":"dbckit"},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# dbckit\n\n[![PyPI](https://img.shields.io/pypi/v/dbckit)](https://pypi.org/project/dbckit/)\n[![CI](https://github.com/canforge/dbckit/actions/workflows/ci.yml/badge.svg)](https://github.com/canforge/dbckit/actions/workflows/ci.yml)\n[![Python versions](https://img.shields.io/pypi/pyversions/dbckit)](https://pypi.org/project/dbckit/)\n[![Downloads](https://static.pepy.tech/badge/dbckit)](https://pepy.tech/project/dbckit)\n[![License: MIT](https://img.shields.io/pypi/l/dbckit)](LICENSE)\n\n`dbckit` is a Python library for working with **DBC (CAN database) files**.\n\nUse it to:\n\n- load and inspect DBC files\n- decode CAN payloads into physical signal values — integers, floats, and doubles\n- encode signal values back into CAN payloads\n- validate DBC content\n- apply deterministic edits and write the result back out\n- diff, merge, extract, and search databases\n- decode CAN log files (`.asc` built in, extensible readers) and in-memory frame streams\n\n<p align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/canforge/dbckit/main/docs/assets/signal-layout.svg\" alt=\"dbckit signal layout rendering a colored bit grid of the VehicleSpeed message in the terminal\" width=\"620\">\n</p>\n<p align=\"center\"><em>Output of <code>dbckit signal layout</code> — per-signal colored bit grid of a message.</em></p>\n\n## Install\n\n```bash\npip install dbckit\npip install \"dbckit[cli]\"   # adds the dbckit command-line tool\n```\n\nRequires Python `>=3.11`.\n\n## Design\n\n`dbckit` is built around typed data models with database-bound Views:\n\n- `Database` is for navigation, top-level creation, cross-database operations,\n  and persistence.\n- `MessageView`, `SignalView`, and `NodeView` (returned by `db.message()`,\n  `msg.signal()`, `db.node()`) are the normal way to modify or delete existing\n  entities.\n- Edit operations are copy-on-write: they return a new `Database` and never\n  mutate the original.\n\n## Quick Start\n\n```python\nimport dbckit\n\ndb = dbckit.load(\"vehicle.dbc\")\n\n# inspect\nprint(db.version, len(db.messages))\n\nmsg = db.message(0x1F4)\nsig = msg.signal(\"EngineSpeed\")\nprint(sig.start_bit, sig.length, sig.factor, sig.unit)\n\n# decode / encode a frame payload\nvalues = msg.decode(bytes([0xE8, 0x03, 0, 0, 0, 0, 0, 0]))\nraw = msg.encode({\"EngineSpeed\": 825.0, \"EngineTemp\": 90.0})\n\n# validate\nfor issue in dbckit.validate(db):\n    print(issue.severity, issue.code, issue.location)\n\n# edits return a new Database\ndb2 = sig.update(factor=0.5)\ndb3 = db2.message(0x1F4).rename(\"MotorData\")\ndb4 = db3.node(\"ECU1\").rename(\"EngineECU\")\n\ndb4.save(\"vehicle.updated.dbc\")\n```\n\n## Features\n\n### Parse and I/O\n\n`dbckit.load()` / `dbckit.save()` for files, `dbckit.parse()` / `dbckit.dump()`\nfor strings. Loading tries strict UTF-8 first and falls back to cp1252 (common\nin Vector-exported files); pass `encoding=` to either function to force one:\n\n```python\ndb = dbckit.load(\"vehicle.dbc\", encoding=\"latin-1\")\ndbckit.save(db, \"copy.dbc\", encoding=\"utf-8\")\n```\n\nFor bounded unsupported constructs, `on_unsupported=\"skip\"` returns the\nparseable model with structured diagnostics and decode-safety rollups; see the\n[API reference](docs/api-reference.md#lenient-parsing-and-decode-safety).\n\n### Codec\n\nMessage-level `decode_frame()` / `encode_frame()` and signal-level\n`decode_signal()` / `encode_signal()`. Both integer signals and IEEE-754\nfloat/double signals (`SIG_VALTYPE_`) are supported, in Intel and Motorola\nbyte order. Encoding clamps out-of-range values by default; pass `strict=True`\nto raise `ValueError` instead. Unspecified signals are zero-filled.\n\nSimple DBC multiplexing (one `M` selector, `mX` variants) is fully supported\nfor decode, encode, and validation. Extended/nested multiplexing (`m0M`) is\nunsupported and rejected during parsing with a clear error.\n\n### Editing\n\nViews cover renames, field updates, signal add/delete, sender and receiver\nedits, arbitration-ID changes, value-table choices, and attribute values.\n`Database` covers top-level creation and signal groups:\n\n```python\nfrom dbckit import Message, Signal, Node, SignalGroup\n\ndb2 = db.add_node(Node(name=\"Gateway\"))\ndb3 = db2.add_message(Message(arbitration_id=0x400, name=\"BrakeData\", length=8))\ndb4 = db3.message(0x400).add_signal(Signal(name=\"BrakePressure\", start_bit=0, length=16))\ndb5 = db4.message(0x400).set_attribute(\"GenMsgCycleTime\", 20)\ndb6 = db5.add_signal_group(SignalGroup(name=\"BrakeGroup\", message_id=0x400, repetitions=1))\ndb7 = db6.add_signal_to_group(0x400, \"BrakeGroup\", \"BrakePressure\")\ndb7.save(\"vehicle.updated.dbc\")\n```\n\n### Validation\n\n`dbckit.validate(db)` returns structured issues (severity, code, location,\nmessage) covering duplicate/invalid IDs, signal overlap and overflow,\nmultiplexing problems, missing senders/receivers, and attribute violations.\nThe full issue-code list is in the [API reference](docs/api-reference.md).\n\n### Operations\n\n```python\nresult = dbckit.diff(db_a, db_b)\nmerged = dbckit.merge(db_a, db_b, strategy=\"ours\")   # raise | ours | theirs\n\nsub = dbckit.extract(db, [0x100, 0x200])\nsub = dbckit.extract(db, message_names=[\"EngineData\"], node_names=[\"Gateway\"])\n\nmessages = dbckit.search_messages(db, \"engine\")\npairs = dbckit.search_signals(db, \"speed\")\n```\n\nJ1939 lookup helpers continue to use explicit `PGN`/`SPN` attribute values.\nFor frame resolution, `pgn_from_arbitration_id()` derives the PGN directly from\na 29-bit ID with PDU1/PDU2 handling:\n\n```python\nmatches = dbckit.find_messages_by_pgn(db, 61444)\nowner, sig = db.signal_by_spn(177)\npgn = dbckit.pgn_from_arbitration_id(0x18F00401)  # 61444\n```\n\n### Log and frame decoding\n\n`decode_log()` streams decoded frames from a log file. Vector CANalyzer `.asc`\nis built in (including extended 29-bit IDs); other formats plug in through\n`register_reader()` or the `dbckit.readers` entry-point group, and\n`format=` overrides extension-based detection for oddly named files:\n\n```python\nfor frame in dbckit.decode_log(db, \"trace.asc\"):\n    print(frame.timestamp, hex(frame.arbitration_id), frame.signals)\n\nframes = dbckit.decode_log(db, \"capture.txt\", format=\"asc\")\n```\n\n`decode_frames()` does the same for any iterable of frame objects — no file\nI/O required. Anything with `timestamp`, `arbitration_id`, and `data`\nattributes satisfies the `FrameLike` protocol, so frames from `python-can` or\nyour own tooling decode directly:\n\n```python\ndecoded = dbckit.decode_frames(db, my_frames)   # Iterator[DecodedFrame]\n\n# Resolve J1939 frames by derived PGN; ambiguous matches are returned explicitly.\ndecoded = dbckit.decode_frames(db, my_frames, match=\"j1939\")\n\n# Prefer exact IDs, then allow PGN fallback only for a J1939-marked DBC.\ndecoded = dbckit.decode_frames(db, my_frames, match=\"auto\")\n```\n\n### Code generation\n\n```python\nheader = dbckit.codegen(db, \"c\")          # experimental\nmodule = dbckit.codegen(db, \"python\")     # dataclasses with decode()/encode()\ndoc = dbckit.codegen(db, \"markdown\")\nschema = dbckit.codegen(db, \"json-schema\")\n```\n\n## CLI\n\nThe `cli` extra installs a `dbckit` command with `db`, `message`, `signal`,\n`node`, `attribute`, `decode`, `encode`, and `codegen` groups. Output formats\nare `table`, `json`, and `csv`.\n\n```bash\ndbckit db info --db vehicle.dbc\ndbckit db validate --db vehicle.dbc\ndbckit db diff base.dbc changed.dbc\ndbckit message list --db vehicle.dbc\ndbckit signal layout --db vehicle.dbc 0x1F4\ndbckit decode frame --db vehicle.dbc 0x1F4 \"E8 03 00 00 00 00 00 00\"\ndbckit decode log --db vehicle.dbc trace.asc --match auto\ndbckit codegen markdown --db vehicle.dbc --out docs.md\n```\n\nSee the [CLI reference](docs/cli.md) for every command and option.\n\n## Scope and Caveats\n\n- Classic CAN DBC workflows are the supported surface; CAN FD is untested and\n  FD-specific flags such as `VFrameFormat` are not interpreted.\n- `.sym`, `.kcd`, and ARXML database formats are out of scope.\n- J1939 lookup helpers use explicit `PGN`/`SPN` attribute values. Frame-stream\n  matching can derive PGNs from 29-bit IDs with `match=\"j1939\"`; `match=\"auto\"`\n  requires a valid message `PGN` or J1939 `ProtocolType` attribute marker.\n- Frame encoding zero-fills unspecified signals and ignores `GenSigStartValue`.\n- Mutation helpers are pure at the `Database` level, but the underlying\n  Pydantic models are not frozen objects.\n\n## Documentation\n\n- [Recipes](docs/recipes.md) — short, runnable patterns for common dbckit tasks\n- [API reference](docs/api-reference.md) — the detailed public API contract,\n  including codec overflow/error behavior and validation issue codes\n- [DBC support matrix](docs/dbc-support.md) — which DBC sections and\n  constructs are fully, partially, or not supported\n- [CLI reference](docs/cli.md) — every command and option\n\n## Development\n\n```bash\npython -m venv .venv\nsource .venv/bin/activate\npip install -e \".[dev]\"\npytest\n```\n\n## License\n\nMIT\n"}