Skip to content

Interacting with Signals Lists

Signals Lists define every signal going into or out of a device or disconnect. They are the primary way Harnice stores information about devices.

The signals list those instructions produce

Signals lists are usually repetative, long, and deterministic. Harnice helps you build signals lists efficiently using build instructuons, which are python rules that build the lists for you.

Build instructions that write a device signals list


Columns

Columns are automatically generated when signals_list.new() is called. Additional columns are ok but discouraged due to consistency. If you need more columns, use data structures in build instructions.

Column Description
channel_id the channel this row belongs to; the same id on every signal of that channel
repeat_channel_id which endpoint, when the channel is a passthrough with two ends; the two repeats must differ; blank when the channel has one endpoint
channel_type channel type string; every signal name it declares must be appended for this endpoint, and the string must parse as a channel type
signal signal name; one of the names declared by channel_type
cavity pin, socket, or stud this signal uses on its connector; required, and unique on that connector
connector_id the connector that holds this signal; every signal of one endpoint uses the same connector
device_connector_mpn manufacturer part number of the device-side connector; set this or harness_connector_mpn, not both, and use the same choice for every row of that connector
harness_connector_mpn manufacturer part number of the harness-side connector; set this or device_connector_mpn, not both

Signals List Validation Checks:

(These are automatically validated when you build the device that owns the list.)

General Signals List Rules

  • Every signal in the Signals List must be contained by a pre-defined channel type

    Channel Types

    Channel Types


    How are channels mapped?


    How to define a new channel type

    1. In a repository of your choice (or start with harnice-general-library on your own branch), navigate to channel_types.tsv at the library root
    2. If you want channel definitions to be private and are therefore working in a private repository, ensure the repo's path is listed in file repository_locations.csv (located at root of your harnice source code repo). The first column is the URL or traceable path, and the second column is your local path.
    3. If you find the channel_type you're looking for, temporarily note it as a touple in a notepad somewhere with format (ch_type_id, universal_library_repository).
    4. If you don't find it, make a new one. It's important to try and reduce the number of channel_types in here to reduce complexity, but it's also important that you adhere to strict and true rules about what is allowed to be mapped to what. Modifications and additions to this document should be taken and reviewed very seriously.

    Two optional columns set the default appearance. Both cells are Python dict literals, parsed with ast.literal_eval. Leave a cell blank to leave that default unset. Harnice reads the channel_types.tsv at the root of the library named by the channel type's lib_repo.

    • channel_default_appearance — one engineering appearance dict. A channel trace (channel or harness-channel) and the channel card in the connectors view use it. Example: {'base_color': '#D59A10'}.
    • signal_default_appearances — signal name to appearance dict. A circuit trace uses the entry for its signal_of_channel_type, and the connectors view uses that entry for the signal bar. Names match the signals column. Example: {'pos': {'base_color': '#E24B4A'}, 'neg': {'base_color': '#2E6B9F'}}.

    An instance appearance that already includes base_color is the appearance drawn for that trace. Traces are drawn when the project is built, so a change to these columns shows up on overlays after the next build. The connectors view reads the columns when the page opens. A trace whose resolved appearance has no base_color is the rainbow placeholder. A card or bar with no base_color keeps the console palette.

    The dict is the engineering appearance from the Appearance guide: base_color, and optionally outline_color, parallelstripe, perpstripe, and twisted. Single quotes match the rows already in the file. The separator is a tab, so a comma inside the literal stays in the cell.

    chtype.parse(val)

    Use this when a build has a channel type as stored text and needs the (channel_type_id, lib_repo) pair.

    Convert stored string into a tuple (chid:int, lib_repo:str). Handles both single tuples and extracts first tuple from lists.

    chtype.signals(channel_type)

    Use this when a system or device build needs the signal names declared for one channel type.

    Return the list of signal names associated with a specific channel type.

    Args

    • channel_type: Channel type identifier in standard tuple format (channel_type_id, lib_repo) or any string representation that parse can understand.

    Returns

    • list[str]: List of signal names from the signals column of channel_types.tsv for the matching channel_type_id. If the column is blank or the channel type cannot be found, returns an empty list.

    Data format

    • The signals column is expected to be a comma-separated string, for example: "CAN_H, CAN_L, SHIELD".
    chtype.channel_default_appearance(channel_type)

    Use this when a drawing needs the default appearance of one channel type's trace and card.

    Optional default appearance for this channel type's channel trace and card.

    Reads channel_default_appearance from the channel_types.tsv at the root of the library named by this channel type's lib_repo. The cell is a Python dict literal, parsed with ast.literal_eval (see appearance.parse). Example: {'base_color': '#D59A10'}. A blank cell returns None.

    A channel trace (item_type channel or harness-channel) and the channel card in the connectors view use this appearance. A circuit trace uses signal_default_appearances for its signal_of_channel_type instead.

    An instance appearance that already includes base_color is the appearance drawn for that trace. Traces are drawn when the project is built. The connectors view reads this column when the page opens. A trace whose resolved appearance has no base_color is the rainbow placeholder. A channel card with no base_color keeps the console palette.

    The dict is an engineering appearance: base_color plus any of outline_color, parallelstripe, perpstripe, and twisted.

    Args

    • channel_type: Channel type identifier in standard tuple format (channel_type_id, lib_repo) or any string representation that parse can understand.

    Returns

    • dict or None: Parsed appearance, or None when the cell is empty or the channel type cannot be found.
    chtype.signal_default_appearances(channel_type)

    Use this when a drawing needs the default appearance of each signal on one channel type.

    Optional default appearance for each signal of this channel type.

    Reads signal_default_appearances from the channel_types.tsv at the root of the library named by this channel type's lib_repo. The cell is a Python dict literal, parsed with ast.literal_eval: signal name to appearance dict. Example: {'pos': {'base_color': '#E24B4A'}, 'neg': {'base_color': '#2E6B9F'}}. A blank cell returns {}. Names that are not themselves dicts are dropped. Signal names match the signals column.

    A circuit trace uses the entry for its signal_of_channel_type. The connectors view uses the same entry for that signal's bar. A channel trace uses channel_default_appearance.

    An instance appearance that already includes base_color is the appearance drawn for that trace. Traces are drawn when the project is built. The connectors view reads this column when the page opens. A trace whose resolved appearance has no base_color is the rainbow placeholder. A signal bar with no base_color keeps the console palette.

    Each value is an engineering appearance: base_color plus any of outline_color, parallelstripe, perpstripe, and twisted.

    Args

    • channel_type: Channel type identifier in standard tuple format (channel_type_id, lib_repo) or any string representation that parse can understand.

    Returns

    • dict: Signal name to appearance dict. Empty when unset or when the channel type cannot be found.
    chtype.compatibles(channel_type)

    Use this when a system build needs the channel types declared compatible with one channel type, not including that type itself.

    Look up other channel types that are declared as compatible with the given channel type.

    Args

    • channel_type: Channel type identifier in standard tuple format (channel_type_id, lib_repo) or any string representation that parse can understand.

    Returns

    • list[tuple[int, str]]: List of (channel_type_id, lib_repo) tuples taken directly from the compatible_channel_types column of channel_types.tsv. Returns an empty list if no compatibles are defined or if the channel type cannot be found.

    Data format

    • The compatible_channel_types column must be an AST-parseable Python value:
      • Single tuple: (1, "library_repo")
      • List of tuples: [(1, "library_repo"), (2, "library_repo")]
    chtype.is_or_is_compatible_with(channel_type)

    Use this when a system build is validating or mapping channels and this channel type should count if it is the requested type or listed as compatible with it.

    Return the given channel type plus all channel types declared as compatible with it.

    Args

    • channel_type: Channel type identifier in standard tuple format (channel_type_id, lib_repo) or any string representation that parse can understand.

    Returns

    • list[tuple[int, str]]: List of (channel_type_id, lib_repo) tuples where the first entry is the parsed channel_type itself and the remaining entries are the compatibles returned by compatibles(channel_type).

    Typical use

    • Use this when validating or mapping channels and you want to treat a channel type as valid if it is either exactly the requested type or explicitly listed as compatible with it.
    chtype.attribute(channel_type, attribute)

    Use this when a system or device build needs an extra column from channel_types.tsv for one channel type.

    Read any additional column from channel_types.tsv for a given channel type.

    Args

    • channel_type: Channel type identifier in standard tuple format (channel_type_id, lib_repo) or any string representation that parse can understand.
    • attribute: Column header name in channel_types.tsv for the value you want to read (for example "description", "notes", "voltage_rating").

    Returns

    • Any: Value stored in the requested attribute column for the matching channel_type_id. Returns an empty list [] if the channel type cannot be found.

    Notes

    • Reads <library_root>/channel_types.tsv (same file as path()).
    • Use this for any per-channel-type metadata you've added as extra columns beyond the core ones like channel_type_id, signals, and compatible_channel_types.
    chtype.path(channel_type)

    Use this when a build needs the path of channel_types.tsv for one channel type's library checkout.

    Resolve the on-disk path to the channel_types.tsv file for a given channel type.

    Args

    • channel_type: Channel type identifier in standard tuple format (channel_type_id, lib_repo) or any string representation that parse can understand (for example "(5, 'https://github.com/harnice/harnice-general-library')").

    Returns

    • str: Absolute path to channel_types.tsv at the root of the library repository that owns the given channel type.

    Notes

    • This does not filter rows; it only locates the TSV file that defines all channel types for the given lib_repo.
  • Each signal in the signals list must have every other signal defined by its channel type also present in the list.

    • you can't just define 'positive' if the channel type requires 'positive' and 'negative'
  • Each signal defined in the list is contained by one or more cavities of connectors.

    • you can't "cap off" or not populate one of the signals within a channel because that changes the channel type.
  • Every combination of (channel_id, signal, repeat_channel_id) must be unique within the signals list

    • you can’t have two i.e. “ch1, pos” signals on the same endpoint
    • if you need to break one signal out onto multiple conductors, you'll need to change the channel type to one that defines multiple conductors (i.e. named "ch1, pos-1")
  • Each channel endpoint lives on exactly one connector_id

    • a passthrough is the same channel_id on two connectors, each with its own repeat_channel_id
    • signals that belong to one endpoint cannot be split across connectors
    • more than two endpoints on one channel_id is an error

Configurable Device Signals List Rules

A configuration is how a device is used, not what it is. The same part number can have different electrical behavior (balanced vs unbalanced, voltage setting, phantom on/off) without a new part number, as long as form, fit, and function of the hardware stay the same.

The signals list is not a pile of alternate rows that get filtered. Build instructions write one signals list for the answers currently in configuration (a dict). Harnice fills that dict from the defaults in fileio.path("configuration setup"), or—when the device is placed in a system—from that instance's answers on the block diagram / BOM.

  • Declare fields in fileio.path("configuration setup"), then read them in build instructions

    • Each field has an id. In the script, use configuration["that_id"] (channel type, select, boolean, number, and so on).
    • You can have as many fields as you need. One field is enough for an SM58 that is either balanced or unbalanced. A mixing console might have a field per input (mic vs line, balanced vs unbalanced) so automatic channel mapping sees the type you actually set, and the instance records how to set the hardware up.
    • Field types, validation, show_if, and a full YAML example are in the device configurations documentation.
  • The list you emit still has to obey the general signals-list rules

    • Complete endpoints, unique (channel_id, signal, repeat_channel_id), one connector per endpoint. Validation only sees the list for this configuration.
  • Do not use configuration to change the physical part

    • If the connector or the hardware build changes, that is a new part number, not a configuration.
  • Unused pins stay off the list
    • A pin is on the signals list only when a channel in this configuration uses it. Omitting a pin this configuration does not use is correct. A channel that is present still includes every signal its channel type defines.

Passthrough Signals List Rules

  • A passthrough is exactly two endpoints of the same channel_id, each with a distinct repeat_channel_id
  • The two channel types must be compatible with each other
    • this is so a patch that crosses the device still mates compatible types on both faces
  • A single-endpoint channel must leave repeat_channel_id blank; a 3-tuple in map() on that channel is an error


Commands

These functions are already available in build instructions as signals_list. Use them as written.

signals_list.read(path_to_signals_list=None)

Use this when a device or system build needs the rows of a signals list: a path, a file key, or the signals list of the revision that is open.

Return signals-list rows from a path, file key, or the current project list.

signals_list.new()

Use this when a device build is starting the signals list and should replace the file that is already there.

Create a signals TSV at fileio.path("signals list") with only the header row.

signals_list.append(**kwargs)

Use this when a device build needs one signal added to the signals list.

Append one signal row. Missing optional fields are written as empty strings.

Required: channel_id, signal, cavity, connector_id, channel_type, and exactly one of device_connector_mpn or harness_connector_mpn.

Args: - channel_id: Channel this row belongs to. Use the same id on every signal of that channel. - channel_type: Channel type string. Every signal name it declares must be appended for this endpoint, and the string must parse as a channel type. - signal: Signal name. It must be one of the names declared by channel_type. - cavity: Pin, socket, or stud this signal uses on its connector. Required, and unique on that connector. - connector_id: Connector that holds this signal. Every signal of one endpoint must use the same connector. - repeat_channel_id: Which endpoint, when the channel is a passthrough with two ends. The two repeats must differ. Leave blank when the channel has one endpoint. Optional. - device_connector_mpn: Manufacturer part number of the device-side connector. Set this or harness_connector_mpn, not both, and use the same choice for every row of that connector. - harness_connector_mpn: Manufacturer part number of the harness-side connector. Set this or device_connector_mpn, not both.

signals_list.cavity_of_signal(channel_id, signal, path_to_signals_list, repeat_channel_id='')

Use this when a build needs the cavity one signal uses on a channel endpoint.

Return the cavity a signal uses on one channel endpoint.

Args: - channel_id: Channel id of the signal whose cavity you want. - signal: Signal name on that channel, such as pos or neg. - path_to_signals_list: Path, or file key, of the signals list to read. - repeat_channel_id: Which endpoint when the channel has two. Leave blank to match a single-endpoint channel. Optional.

signals_list.connector_id_of_channel(channel_id, path_to_signals_list, repeat_channel_id='')

Use this when a build needs the connector id of one channel in a signals list.

Return connector_id for a channel endpoint.

Args: - channel_id: Channel id whose connector you want. - path_to_signals_list: Path, or file key, of the signals list to read. Raises if that file does not exist. - repeat_channel_id: Which endpoint when the channel has two. Leave blank to match a single-endpoint channel. Optional.