Architecture¶
schoolbus starts with concrete protocol objects and grows abstractions only when they remove real repetition.
The learning ladder is:
- Bits are positions and masks.
- Fields give names and meaning to ranges of bits.
- Units describe interpreted values.
- Words, frames, and sentences are the first useful protocol containers.
- Messages and transactions connect containers into a protocol act.
- Signals turn encoded values into engineering quantities.
- Observations attach time, source, and quality.
- State is what a passive observer can infer over time.
The preferred usage style is:
thing = ProtocolThing.from_raw(...)
thing.show_fields()
thing.explain()
Structural Contracts¶
Protocol objects use structural contracts rather than a required inheritance tree. Major public protocol objects should be explainable and inspectable:
explain() -> strreturns plain-English teaching context.show_fields()returns display-oriented decoded fields.
Other capabilities are optional and should be advertised by the protocol catalog when they exist:
- raw construction or export
- arbitration comparison
- framing encode/decode
- transaction grouping
- signal extraction
- observation generation
This keeps incompatible protocols from being forced into identical APIs.
Composition¶
Protocol layers should compose directly. J1939 is interpreted over a CAN identifier; it does not need to subclass a CAN frame:
from schoolbus.protocols.can import CanFrame
from schoolbus.protocols.j1939 import J1939Identifier
frame = CanFrame(identifier=0x0CF00400, data=b"", extended=True)
identifier = J1939Identifier(frame.identifier)
print(identifier.explain())
The same approach should apply to future decoders: ARINC 429 label-specific decoders can
wrap an Arinc429Word, MIL-STD-1553 transactions can group command, status, and data
words, and serial protocols can sit on top of UART framing.
Protocol Catalog¶
The protocol catalog is a static registry of implemented protocol support and synthetic tutorial examples. It is intentionally not a plugin framework, auto-detection engine, or universal decoder.
Use it for iteration and discoverability:
from schoolbus.protocols import iter_protocols, make_example
for descriptor in iter_protocols():
print(descriptor.slug, descriptor.capabilities, descriptor.binder_path)
thing = make_example("can")
print(thing.explain())
The first implementation deliberately avoids a universal protocol factory. CAN frames, J1939 identifiers, ARINC 429 words, MIL-STD-1553 words, and UART framing each get direct models first.