Skip to content

Refactor hardware data classes #47

@joelspadin

Description

@joelspadin

In the existing hardware data classes, Keyboard is more accurately MaybeKeyboard, because it is the base class for Board and Shield, either of which could be the main PCB for a keyboard or just one component of one.

To better model the function of each piece of hardware, we should refactor the classes to something like this:

classDiagram
    Hardware <|-- Interconnect
    Hardware <|-- KeyboardComponent
    KeyboardComponent <|-- Board
    KeyboardComponent <|-- Shield

    Keyboard *-- Board
    Keyboard *-- Shield

    class Hardware {
        +str type
        +str id
        +str name
        +str|None file_format
        +str|None url
        +str|None description
        +str|None manufacturer
        +str|None version
        $from_dict(data)
    }

    class KeyboardComponent {
        +list[str] siblings
        +list[str] exposes
        +list[Feature] features
        +list[Variant] variants
        +bool has_keys()
    }

    class Board {
        +str|None arch
        +list[Output] outputs
        +list[str] revisions
        +str|None default_revision
    }

    class Shield {
        +list[str] requires
    }

    class Interconnect {
        +dict[str, str] node_labels
        +str|None design_guideline
    }

    note for Keyboard "get_build_items() returns
    one item per combination 
    of board/shield siblings,
    with revision+qualifiers, e.g. 
    - board=foo
    - shield=bar,baz
    - board_revision=2
    - board_qualifiers=a,b
    with bar having siblings
    returns:
    - 'foo@2/a/b', 'bar_left baz'
    - 'foo@2/a/b', 'bar_right baz'"

    note for Keyboard "is_complete is true when 
    board is set and at least 
    one item in boards/shields 
    has the 'keys' feature."

    class Keyboard {
        +Board|None board
        +list[Shield] shields
        +Board|Shield|None keys_component
        +str|None board_revision
        +list[str] board_qualifiers
        +bool is_complete
        +list[str] exposes
        +list[str] requires
        +bool requirements_satisfied
        +list[Revision] revisions
        +Revision|None default_revision
        +list[BuildItem] get_build_items()
    }

    class Revision {
        +constructor(value)
        +str str()
    }

    class BuildItem {
        +str board
        +str|None shield
        +str|None snippet
        +str|None cmake_args
        +str|None artifact_name
    }

    Keyboard -- BuildItem
    Keyboard -- Revision
Loading

Hardware remains the base class for all hardware descriptions, and Interconnect remains a class that describes a hardware interface but not a specific component of a keyboard. The base class of Board and Shield is renamed to KeyboardComponent, which gets a new has_keys() helper function to differentiate between keyboard and controller boards and between keyboard and peripheral shields.

A new Keyboard class then represents the combination of a Board with zero or more Shields to form a keyboard. The zmk keyboard add command can then create a Keyboard instance and progressively populate it instead of having local variables for each of keyboard, controller, and revision.

The following properties on Keyboard would be computed from the board and shields fields:

  • keys_component - The first item out of [board, *shields] which has the "keys" feature, or None
  • is_complete - True if the keyboard has a board and a keys_component
  • exposes - Set of all exposes values in [board, *shields]
  • requires - Set of all requires values in shields
  • requirements_satisfied - True if all values in requires are present in exposes (some interconnects may support different numbers of peripherals attached to them, but that would be very difficult to model properly, and it probably wouldn't be that useful information anyways)
  • revisions - Pass through to board.revisions with values wrapped in Revision class. Empty list if board not set.
  • default_revision - Pass through to board.default_revision with value wrapped in Revision class. None if board not set or board has no default revision.

Keyboard.get_build_items() will return a separate BuildItem for every combination of siblings defined for each of the boards and shields. This will replace _get_build_items() from add.py.

A new Revision class can be added to simplify comparing revisions with different formats. For example, Revision("2") should compare equal to Revision("2.0.0"). This would allow for things like revision in keyboard.revisions without needing to manually normalize all the revision strings first.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions