dbckit 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
dbckit/__init__.py ADDED
@@ -0,0 +1,79 @@
1
+ """dbckit — parse, inspect, encode, decode, and transform DBC (CAN database) files."""
2
+ from __future__ import annotations
3
+
4
+ # ── codec ─────────────────────────────────────────────────────────────────────
5
+ from dbckit.codec import decode_frame, decode_signal, encode_frame, encode_signal
6
+
7
+ # ── io ────────────────────────────────────────────────────────────────────────
8
+ from dbckit.io import load, save
9
+
10
+ # ── models ────────────────────────────────────────────────────────────────────
11
+ from dbckit.model.database import (
12
+ AttributeDefinition,
13
+ AttributeKind,
14
+ Database,
15
+ Issue,
16
+ Node,
17
+ )
18
+ from dbckit.model.message import Message
19
+ from dbckit.model.signal import BitSlot, ByteOrder, Signal, SignalGroup, ValueTable
20
+
21
+ # ── operations ────────────────────────────────────────────────────────────────
22
+ from dbckit.operations import (
23
+ AscReader,
24
+ CodegenTarget,
25
+ DecodedFrame,
26
+ DiffResult,
27
+ FrameLike,
28
+ MergeStrategy,
29
+ MessageDiff,
30
+ RawFrame,
31
+ SignalDiff,
32
+ codegen,
33
+ decode_frames,
34
+ decode_log,
35
+ diff,
36
+ extract,
37
+ find_messages_by_pgn,
38
+ find_signals_by_spn,
39
+ merge,
40
+ register_reader,
41
+ search_messages,
42
+ search_signals,
43
+ )
44
+ from dbckit.parser.grammar import parse_string
45
+ from dbckit.parser.tokenizer import normalize
46
+ from dbckit.serializer import dump
47
+ from dbckit.validator import validate
48
+
49
+ # ── views ─────────────────────────────────────────────────────────────────────
50
+ from dbckit.views import MessageView, NodeView, SignalView
51
+
52
+ __all__ = [
53
+ # models
54
+ "Database", "Message", "Signal", "SignalGroup", "Node",
55
+ "ByteOrder", "ValueTable", "BitSlot",
56
+ "AttributeDefinition", "AttributeKind", "Issue",
57
+ # views
58
+ "MessageView", "SignalView", "NodeView",
59
+ # parse / io
60
+ "parse", "load", "dump", "save",
61
+ # validate
62
+ "validate",
63
+ # codec
64
+ "decode_frame", "encode_frame", "decode_signal", "encode_signal",
65
+ # operations
66
+ "diff", "DiffResult", "MessageDiff", "SignalDiff",
67
+ "merge", "MergeStrategy",
68
+ "extract",
69
+ "search_messages", "search_signals",
70
+ "find_messages_by_pgn", "find_signals_by_spn",
71
+ "codegen", "CodegenTarget",
72
+ "decode_frames", "decode_log", "DecodedFrame", "FrameLike", "RawFrame",
73
+ "AscReader", "register_reader",
74
+ ]
75
+
76
+
77
+ def parse(text: str) -> Database:
78
+ """Parse a DBC-formatted string and return a Database model."""
79
+ return parse_string(normalize(text))
dbckit/_cycle_time.py ADDED
@@ -0,0 +1,66 @@
1
+ """Shared helpers for the DBC ``GenMsgCycleTime`` convention."""
2
+ from __future__ import annotations
3
+
4
+ from decimal import Decimal, InvalidOperation
5
+ from math import isfinite
6
+ from typing import Any, Protocol
7
+
8
+ CYCLE_TIME_ATTRIBUTE = "GenMsgCycleTime"
9
+ AUTO_CYCLE_TIME_MINIMUM = 0
10
+ AUTO_CYCLE_TIME_MAXIMUM = 2_147_483_647
11
+ AUTO_CYCLE_TIME_DEFAULT = 0
12
+
13
+
14
+ class CycleTimeDefinition(Protocol):
15
+ """The range fields needed from an attribute definition."""
16
+
17
+ minimum: float | None
18
+ maximum: float | None
19
+
20
+
21
+ def coerce_cycle_time(value: Any) -> int:
22
+ """Return *value* as an integer cycle time or raise ``ValueError``."""
23
+ if isinstance(value, bool):
24
+ raise ValueError("GenMsgCycleTime must be an integer, not a boolean")
25
+ try:
26
+ decimal = Decimal(str(value))
27
+ except (InvalidOperation, ValueError, TypeError):
28
+ raise ValueError(f"GenMsgCycleTime must be an integer, got {value!r}") from None
29
+ if not decimal.is_finite() or decimal != decimal.to_integral_value():
30
+ raise ValueError(f"GenMsgCycleTime must be an integer, got {value!r}")
31
+ return int(decimal)
32
+
33
+
34
+ def validate_cycle_time(
35
+ value: Any,
36
+ definition: CycleTimeDefinition | None = None,
37
+ ) -> int:
38
+ """Coerce and validate a cycle time against an effective definition."""
39
+ cycle_time = coerce_cycle_time(value)
40
+ minimum = (
41
+ AUTO_CYCLE_TIME_MINIMUM if definition is None else definition.minimum
42
+ )
43
+ maximum = (
44
+ AUTO_CYCLE_TIME_MAXIMUM if definition is None else definition.maximum
45
+ )
46
+ if minimum is not None and (not isfinite(minimum) or cycle_time < minimum):
47
+ raise ValueError(
48
+ f"GenMsgCycleTime value {cycle_time} is outside the declared range "
49
+ f"[{_format_bound(minimum)}, {_format_bound(maximum)}]"
50
+ )
51
+ if maximum is not None and (not isfinite(maximum) or cycle_time > maximum):
52
+ raise ValueError(
53
+ f"GenMsgCycleTime value {cycle_time} is outside the declared range "
54
+ f"[{_format_bound(minimum)}, {_format_bound(maximum)}]"
55
+ )
56
+ return cycle_time
57
+
58
+
59
+ def _format_bound(value: float | None) -> str:
60
+ if value is None:
61
+ return "unbounded"
62
+ if not isfinite(value):
63
+ return str(value)
64
+ if value == int(value):
65
+ return str(int(value))
66
+ return str(value)