Skip to content

Systems

A collection of devices and harnesses that satisfies a set of functionality requirements for some external purpose.


How system data is stored

System data is stored in the following file formats.

Instances List

Interacting with Instances Lists

An instances list is a list of every physical or notional item, idea, note, part, instruction, circuit, drawing element, thing, concept literally anything that describes how to build that harness or system.

Instances lists are the single comprehensive source of truth for the project you are working on. Other inputs—especially build instructions (your Python script)—build this list, and all output documentation are derived from it.


Columns

Columns are automatically generated when instances_list.new() is called. Additional columns for this kind of list may be added by the user.

Column Description
harness the physical harness (harness refdes) that this instance is part of
instance_name the unique name of this instance
print_name the non-unique, human-readable name of this instance, used for printing on output documents
bom_line_number if this instance represents a physical procurable good, it gets assigned a line number on a bill of materials
mfg manufacturer of this instance
mpn manufacturer part number
item_type connector, backshell, whatever
location_type each instance is either better represented by one or ther other
segment_group the group of segments that this instance is part of
segment_order the sequential id of this item in its segment group
connector_group a group of co-located parts (connectors, backshells, nodes)
channel_group other instances associated with this one because they are part of the same channel will share this value
circuit_id which signal this component is electrically connected to
circuit_port_number the sequential id of this item in its signal chain
node_at_end_a derived from formboard definition
node_at_end_b derived from formboard definition
print_name_at_end_a human-readable name of this instance if needed, associated with 'node_at_end_a'
print_name_at_end_b human-readable name of this instance if needed, associated with 'node_at_end_b'
parent_csys_instance_name the other instance upon which this instance's location is based
parent_csys_outputcsys_name the specific output coordinate system of the parent that this instance's location is based
2d_translate dict {x, y, rotate} in the parent CSYS; used by flattened drawings
3d_translate 6DOF dict {x, y, z, alpha, beta, gamma}; α about X, γ about Z, used by STEP
absolute_rotation manual add, not nominally used unless it's a flagnote, segment, or node
csys_children imported csys children from library attributes file
cable_group other instances associated with this one because they are part of the same cable will share this value
cable_identifier which conductor in that cable this instance is assigned to (unique within the cable)
length derived from formboard definition, the length of a segment
length_tolerance derived from formboard definition, the tolerance on the length of a segment
diameter apparent diameter of a segment <---------- change to print_diameter
appearance see harnice.utils.appearance for details
device_configuration compact JSON of filled configuration-setup answers (from BOM)
note_type build_note, rev_note, etc
note_number if there is a counter involved (rev, bom, build_note, etc)
note_parent the instance the note applies to. typically don't use this in the instances list, just note_utils
note_text the content of the note
note_affected_instances list of instances that are affected by the note
lib_repo publically-traceable URL of the library this instance is from
lib_subpath path to the instance within the library (directories between the project type and the part number)
lib_desc description of the instance per the library's revision history
lib_latest_rev the latest revision of the instance that exists in the remote library
lib_rev_used_here the revision of the instance that is currently used in this project
lib_status the status of the instance per the library's revision history
lib_releaseticket documentation needed
lib_datestarted the date this instance was first added to the library
lib_datemodified the date this instance was last modified in the library
lib_datereleased the date this instance was released in the library, if applicable, per the library's revision history
lib_drawnby the name of the person who drew the instance, per the library's revision history
lib_checkedby the name of the person who checked the instance, per the library's revision history
project_editable_lib_modified a flag to indicate if the imported contents do not match the library's version (it's been locally modified)
lib_build_notes recommended build notes that come with the instance from the library
lib_tools recommended tools that come with the instance from the library
attributes_json if an instance is imported with an attributes json attached, it's added here
device_side_refdes if device_connector, refdes of the device it plugs into
device_side_connector_id if device_connector, name of the connector it plugs into
device_side_connector_mpn if device_connector, mpn of the connector it plugs into
device_side_from_cavity if circuit, cavity on the from side (from circuits list harness_from_cavity)
device_side_to_cavity if circuit, cavity on the to side (from circuits list harness_to_cavity)
harness_side_connector_id asserted name of the mating harness connector (from system connector list)
this_harness_from_device_refdes if this instance is a channel, circuit, conductor, etc, the refdes of the device it interfaces with, just within this harness
this_harness_from_device_channel_id if this instance is a channel, circuit, conductor, etc, the channel id in the device it interfaces with, just within this harness
this_harness_from_device_connector_name if this instance is a channel, circuit, conductor, etc, the name of the connector it interfaces with, just within this harness
this_harness_to_device_refdes if this instance is a channel, circuit, conductor, etc, the refdes of the device it plugs into just within this harness
this_harness_to_device_channel_id if this instance is a channel, circuit, conductor, etc, the channel id in the device it plugs into, just within this harness
this_harness_to_device_connector_name if this instance is a channel, circuit, conductor, etc, the name of the connector it plugs into, just within this harness
this_channel_from_device_refdes if this instance is a channel, circuit, conductor, etc, the refdes of the device it interfaces with, at the very end of the channel
this_channel_from_device_channel_id if this instance is a channel, circuit, conductor, etc, the channel id in the device it interfaces with, at the very end of the channel
this_channel_to_device_refdes if this instance is a channel, circuit, conductor, etc, the refdes of the device it plugs into, at the very end of the channel
this_channel_to_device_channel_id if this instance is a channel, circuit, conductor, etc, the channel id in the device it plugs into, at the very end of the channel
this_channel_from_channel_type if this instance is a channel, circuit, conductor, etc, the type of the channel it interfaces with, at the very end of the channel
this_channel_to_channel_type if this instance is a channel, circuit, conductor, etc, the type of the channel it plugs into, at the very end of the channel
signal_of_channel_type if this instance is a channel, circuit, conductor, etc, the signal of the channel it interfaces with, at the very end of the channel
debug the call chain of the function that last modified this instance row
debug_cutoff blank cell to visually cut off the previous column

Commands:

Use the following functions by first importing the module in your script like this:

from harnice.lists import instances_list
then use as written.

instances_list.new_instance(instance_name, instance_data, ignore_duplicates=False)

Add a new instance to the instances list.

Usage

new_instance(instance_name, instance_data, ignore_duplicates=False)

Args

  • instance_name: String; must be unique within the list.
  • instance_data: Dict of column names to values. May include instance_name; if present it must match the instance_name argument or the code will fail.
  • ignore_duplicates: If True, does nothing when an instance with the same instance_name already exists. If False (default), raises an error on duplicate.

Returns

-1 on success. Raises on invalid input or duplicate (when ignore_duplicates is False).

instances_list.modify(instance_name, instance_data)

Update columns for an existing instance by name.

Args

  • instance_name: The unique name of the instance to modify.
  • instance_data: Dict of column names to new values. Only provided keys are updated; others are unchanged.

Raises

ValueError if no instance with instance_name exists.

instances_list.remove_instance(instance_to_delete)

Remove one instance from the instances list.

Args

  • instance_to_delete: Instance row dict (or any dict) whose instance_name key identifies the instance to remove. Matching is done by instance_name only.
instances_list.new()

Create a new empty instances list file with only the standard header (COLUMNS). Overwrites existing file if present.

instances_list.assign_bom_line_numbers()

Assign sequential BOM line numbers to instances that have bom_line_number set to "True".

Groups by MPN and assigns the same line number to all instances sharing an MPN. Requires every such instance to have a non-empty mpn. Line numbers are assigned in order of first occurrence of each MPN.

Raises

ValueError if any instance marked for BOM has an empty mpn.

instances_list.attribute_of(target_instance, attribute)

Return the value of one column for a single instance.

String values that look like Python literals (list or dict, e.g. starting with `[` or `{`) are parsed with `ast.literal_eval` and the parsed value is returned; otherwise the raw string is returned.

Args

  • target_instance: The instance_name of the instance to look up.
  • attribute: The column name to read (e.g. "mpn", "harness").

Returns

The value of that column for the matching instance, or None if not found or attribute missing. List/dict-like strings are returned as list/dict.

instances_list.instance_in_connector_group_with_item_type(connector_group, item_type)

Return the single instance in a connector group with the given item type.

Args

  • connector_group: The connector_group value to match.
  • item_type: The item_type value to match (e.g. connector, backshell).

Returns

The matching instance row dict, or 0 if no match.

Raises

ValueError if connector_group or item_type is blank, or if more than one instance matches.

instances_list.seed_connector_nodes()

Creates a node instance for each connector group and parents each connector to it.

Intended for harness build instructions after connectors exist (for example after importing a harness from a system). For each harness_connector or device_connector with a non-blank connector_group:

  • Ensures {connector_group}.node exists (item_type / location_type "node")
  • If the connector has no parent_csys_instance_name, sets it to that node with parent_csys_outputcsys_name "origin"

Connectors that already have a parent coordinate system are left unchanged.

instances_list.list_of_uniques(attribute)

Return a list of unique non-empty values for one column across all instances.

Args

  • attribute: The column name to collect (e.g. "harness", "item_type").

Returns

List of unique values; blanks and None are omitted. Order follows first occurrence in the instances list.

Library Import History

Interacting with Library History

A report of what was imported during the most recent build of the current project


Columns

Columns are automatically generated when library_history.new() is called. Additional columns are not supported and may result in an error when parsing.

Column Description
instance_name documentation needed
mpn documentation needed
item_type documentation needed
lib_repo documentation needed
lib_subpath documentation needed
lib_desc documentation needed
lib_latest_rev documentation needed
lib_rev_used_here documentation needed
lib_status documentation needed
lib_releaseticket documentation needed
lib_datestarted documentation needed
lib_datemodified documentation needed
lib_datereleased documentation needed
lib_drawnby documentation needed
lib_checkedby documentation needed
project_editable_lib_modified documentation needed

Commands:

Use the following functions by first importing the module in your script like this:

from harnice.lists import library_history
then use as written.

library_history.new()

Documentation needed.

library_history.append(instance_name, instance_data)

Documentation needed.

Channel Map

Interacting with Channel Maps

A list of channels on devices within groups of connected harnesses. Each completed pairing is a mapped channel; remaining rows are unmapped.


Columns

Columns are automatically generated when channel_map.new() is called. Additional columns are not supported and may result in an error when parsing.

Column Description
group_of_connected_harnesses harnesses linked through disconnects (or a single standalone harness)
from_device_refdes documentation needed
from_device_channel_id documentation needed
from_channel_type documentation needed
to_device_refdes documentation needed
to_device_channel_id documentation needed
to_channel_type documentation needed
multi_ch_junction_id documentation needed
chain_of_connectors documentation needed
chain_of_harnesses documentation needed
manual_map_channel_python_equiv documentation needed

Commands:

Use the following functions by first importing the module in your script like this:

from harnice.lists import channel_map
then use as written.

channel_map.new()

Makes a new blank channel map. Overwrites existing channel map.

Args: none

Returns: none

channel_map.map(from_key, to_key=None, multi_ch_junction_key='')

Map a channel to another channel.

from_key and to_key are (device_refdes, channel_id) tuples. Records the mapping on the channel map and in the already-mapped set.

channel_map.already_mapped_set_append(key)

Documentation needed.

channel_map.already_mapped_set()

Documentation needed.

channel_map.already_mapped(key)

Documentation needed.

Circuits List

Interacting with Circuits Lists

A list of every individual electrical connection that must be present in your system or harness to satisfy your channel and disconnect maps.


Columns

Columns are automatically generated when circuits_list.new() is called. Additional columns are not supported and may result in an error when parsing.

Column Description
harness documentation needed
harness_chain_index 0-based index into channel map chain_of_harnesses for this segment
circuit_id documentation needed
signal documentation needed
harness_from_refdes documentation needed
harness_from_channel_id documentation needed
harness_from_connector_name documentation needed
harness_from_cavity documentation needed
harness_to_refdes documentation needed
harness_to_channel_id documentation needed
harness_to_connector_name documentation needed
harness_to_cavity documentation needed
from_side_device_refdes documentation needed
from_side_device_chname documentation needed
to_side_device_refdes documentation needed
to_side_device_chname documentation needed
from_channel_type documentation needed
to_channel_type documentation needed

Commands:

Use the following functions by first importing the module in your script like this:

from harnice.lists import circuits_list
then use as written.

circuits_list.new()

Build the circuits list from the channel map and disconnect map.

Uses chain_of_connectors / chain_of_harnesses (from disconnect_map.resolve()) and ordered satisfaction rows on the disconnect map for disconnect channel IDs. Overwrites any existing circuits list.

Disconnect Map

Interacting with Disconnect Maps

Every disconnect channel in the system BOM. Call new() to build the map and routing context, optionally assign() required hops, then resolve() to fill channel-map chains.


Columns

Columns are automatically generated when disconnect_map.new() is called. Additional columns are not supported and may result in an error when parsing.

Column Description
from_channel_type channel-map from type
from-disconnect_port_channel_type disconnect connector on the same side as from
to-disconnect_port_channel_type disconnect connector on the same side as to
to_channel_type channel-map to type
requirement user assign() step number for this disconnect-channel mapping
satisfaction resolve() assign() step number (always >= requirement when both set)
is_imaginary TRUE if this mapping was minted because no real disconnect channel was available
has_imaginary TRUE if this disconnect has any imaginary channel mappings
manual_map_disconnect_python_equiv disconnect_map.assign() that would form this requirement

Commands:

Use the following functions by first importing the module in your script like this:

from harnice.lists import disconnect_map
then use as written.

disconnect_map.new()

Create disconnect channel rows on the disconnect map from the BOM.

Rebuilds system.disconnect_routing_context as one row per unique (from_channel_type, to_channel_type) pair within each group_of_connected_harnesses, per disconnect, per directionality (1 / -1, or 0 if reversible), with an availables count of disconnect channels that obey that directionality. Does not route channels or fill channel-map chains (see resolve()).

disconnect_map.assign(to_from_channel_device_refdes, to_from_channel_id, intended_disconnect_refdes, intended_disconnect_channel_id=None, from_channel_is_close_to_disconnect_side=None, satisfaction=False)

Assign a mapped channel through a disconnect.

Looks up the mapped channel's from→to types and group_of_connected_harnesses, finds the matching routing-context row(s), picks a free disconnect channel that obeys directionality, records the device↔disconnect mapping on the disconnect map, and decrements availables on the matching context row (never below 0).

By default writes requirement (user assign). With satisfaction=True (used by resolve), writes satisfaction instead.

Returns 0 on a real assignment, -1 if an imaginary disconnect-map row was appended because availables was already 0.

disconnect_map.resolve()

Complete disconnect routing for each mapped channel (channel-map order).

Uses the routing context (filtered to group_of_connected_harnesses + from→to pair) to find the fewest disconnect channels that connect from_harness → optional ordered requirement waypoints → to_harness. Prefers paths with remaining availables; if none exist, allows availables == 0 (imaginaries).

Gap fills call assign(..., satisfaction=True). Existing requirement rows get a satisfaction stamp. Writes chain_of_connectors and chain_of_harnesses on each mapped channel-map row from the resulting path.

Stamps is_imaginary / has_imaginary on the disconnect map. Prints one warning per disconnect that needed imaginary channels to satisfy the map.

No-op when disconnect_map.new() found no harness topology (empty parent diagram / no harness-mated connectors): there is nothing to resolve.

Netlist

Interacting with Netlists

Post-Harness Instances List

Interacting with Post Harness Instances Lists

A list of every physical or notional thing, drawing element, or concept that includes instances added at the harness level, that represents a system


Commands:

Use the following functions by first importing the module in your script like this:

from harnice.lists import post_harness_instances_list
then use as written.

post_harness_instances_list.rebuild()

Build the 'post harness instances list' by merging instance data from: - Every *-instances_list.tsv under the system harnesses/ directory - The system-level instances list for harnesses not covered by those files, and for instances with no harness

Writes a clean TSV with INSTANCES_LIST_COLUMNS (plus any extra columns found).

post_harness_instances_list.push(path_to_system_rev, system_pn_rev)

Documentation needed.

Signals List

Interacting with Signals Lists

A Signals List is an exhaustive list of every signal is going into or out of a thing. Signals Lists are the primary way Harnice stores information about devices, and act as the source of truth for devices and disconnects.


Signals List Validation Checks:

(These are automatically validated when you build the device or disconnect 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_library_public on your own branch), navigate to library_repo/channel_types/channel_types.csv
    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.
    chtype.path(channel_type)

    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')").

    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.
    chtype.parse(val)

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

    chtype.compatibles(channel_type)

    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.attribute(channel_type, attribute)

    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.signals(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.is_or_is_compatible_with(channel_type)

    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.
  • 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) must be unique within the signals list

    • you can’t have two i.e. “ch1, pos” signals on the same device
    • 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")
  • You can’t put signals of the same channel on different connectors

    • this is because doing so breaks a lot of internal assumptions Harnice is making while mapping channels.

    • the following two options are recommended work-arounds:

      • Most correct but confusing: Define one channel type per signal, then manually chmap your channels or write a macro for mapping the channels to their respective destinations.

      • Janky but easiest to understand: Define a connector part number that actually represents multiple connectors, while using cavities to reference each connector.

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 device's configuration-setup.yaml defaults, or—when the device is placed in a system—from that instance's answers on the block diagram / BOM.

  • Declare fields in *-configuration-setup.yaml, 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.
  • The list you emit still has to obey the general signals-list rules

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

    • If connectors, cavities, or the build of the device change, that is a new part number, not a configuration. Unused signals that are still on the connector should stay in the list.

Disconnect Signals List Rules

  • “A” and “B” channels of the same disconnect must be compatible with each other

    • this is to ensure when you actually mate the disconnect that the channels inside will be compatible.

Columns

Columns are automatically generated when signals_list.new() is called. Additional columns are not supported and may result in an error when parsing.

Columns of Signals Lists for Devices

Column Description
channel_id Unique identifier for the channel.
signal Name of the electrical function of that signal, as it pertains to its channel type defition. i.e. "positive"
connector_name Unique identifier for the connector that this signal and channel is a part of.
cavity Identifier of the pin, socket, stud, etc, that this signal is internally electrically routed to within its connector.
connector_mpn MPN of the connector in this device (NOT the mating connector).
channel_type The channel type of this signal.
Touple (x, y) where x is the channel id within a library repo and y is the traceable name or url where that channel type library is defined

Columns of Signals Lists for Disconnects

Column Description
channel_id Unique identifier for the channel.
signal Name of the electrical function of that signal, as it pertains to its channel type defition. i.e. "positive"
A_cavity Identifier of the pin, socket, stud, etc, that this signal is internally electrically routed to within that side of the connector.
??? question "Why are A and B different here?"
Sometimes it's possible to have connectors that have cavities that may mate electrically, but have different names. For example, suppose two connectors physically mate, but are made by different manufacturers. One manufacturer used lowercase (a, b, c) to reference the cavities but the other used uppercase (A, B, C), or numbers (1, 2, 3), or colors (red, green, blue), etc.
B_cavity Identifier of the pin, socket, stud, etc, that this signal is internally electrically routed to within that side of the connector.
??? question "Why are A and B different here?"
Sometimes it's possible to have connectors that have cavities that may mate electrically, but have different names. For example, suppose two connectors physically mate, but are made by different manufacturers. One manufacturer used lowercase (a, b, c) to reference the cavities but the other used uppercase (A, B, C), or numbers (1, 2, 3), or colors (red, green, blue), etc.
A_connector_mpn MPN of the connector of the harness on this side of the disconnect
A_channel_type The channel type of this side of the discconect.
??? question "Why are A and B different here?"
It's important to keep track of which side has which channel type so that you cannot accidentally flip pins and sockets, for example, by mapping the wrong channel type to the wrong pin gender. Careful validation should be done when mapping channels through disconnects to ensure the disconnects have channels that pass through them in the correct direction.
B_connector_mpn MPN of the connector of the harness on this side of the disconnect
B_channel_type The channel type of this side of the discconect.
??? question "Why are A and B different here?"
It's important to keep track of which side has which channel type so that you cannot accidentally flip pins and sockets, for example, by mapping the wrong channel type to the wrong pin gender. Careful validation should be done when mapping channels through disconnects to ensure the disconnects have channels that pass through them in the correct direction.


Commands:

Use the following functions by first importing the module in your script like this:

from harnice.lists import signals_list
then use as written.

signals_list.set_list_type(x)

Documentation needed.

signals_list.new()

Creates a new signals TSV file at fileio.path("signals list") with only the header row. Overwrites any existing file.

signals_list.append(**kwargs)

Appends a new row to the signals TSV file. Missing optional fields will be written as empty strings. Raises ValueError if required fields are missing.

Required kwargs: For 'device': channel_id, signal, connector_name, cavity, connector_mpn, channel_type For 'disconnect': A_channel_id, A_signal, A_connector_name, A_cavity, A_connector_mpn, A_channel_type, B_channel_id, B_signal, B_connector_name, B_cavity, B_connector_mpn, B_channel_type

signals_list.cavity_of_signal(channel_id, signal, path_to_signals_list)

Documentation needed.

signals_list.connector_name_of_channel(channel_id, path_to_signals_list)

Documentation needed.



Building a system

  1. Harnice runs the build instructions if it's found in the system directory.

  2. The system is validated and verified.

  3. The external ICD is compiled from external-interface flags (after build instructions).

  4. The system block diagram SVG is updated from placed-instance symbol files under instance_data/ (so device/subsystem symbol edits are picked up), including derived channel/circuit trace overlays based on the built system.

  5. PDF drawings: call run_macro("pdf_generator", …, page_setup=…) in build instructions after other macros that write masters.

The subsystem_symbol macro runs in build instructions, so it reads the ICD from the previous build and runs device signals-list / symbol-pin verification against it. After ICD changes, build twice. That lag is expected.


Channel and disconnect mapping

After Harnice exports a BOM and system connector list from your block diagram SVG, it seeds a channel map—one row per device channel on every non-disconnect connector. Your build instructions then declare which device channels carry the same logical signal, and (when harnesses are separated by disconnects) which disconnect channel assignments each cross-harness link uses. That two-step process is what turns diagram connectivity into a circuits list and downstream harness instances.

Channel map file format

Interacting with Channel Maps

A list of channels on devices within groups of connected harnesses. Each completed pairing is a mapped channel; remaining rows are unmapped.


Columns

Columns are automatically generated when channel_map.new() is called. Additional columns are not supported and may result in an error when parsing.

Column Description
group_of_connected_harnesses harnesses linked through disconnects (or a single standalone harness)
from_device_refdes documentation needed
from_device_channel_id documentation needed
from_channel_type documentation needed
to_device_refdes documentation needed
to_device_channel_id documentation needed
to_channel_type documentation needed
multi_ch_junction_id documentation needed
chain_of_connectors documentation needed
chain_of_harnesses documentation needed
manual_map_channel_python_equiv documentation needed

Commands:

Use the following functions by first importing the module in your script like this:

from harnice.lists import channel_map
then use as written.

channel_map.new()

Makes a new blank channel map. Overwrites existing channel map.

Args: none

Returns: none

channel_map.map(from_key, to_key=None, multi_ch_junction_key='')

Map a channel to another channel.

from_key and to_key are (device_refdes, channel_id) tuples. Records the mapping on the channel map and in the already-mapped set.

channel_map.already_mapped_set_append(key)

Documentation needed.

channel_map.already_mapped_set()

Documentation needed.

channel_map.already_mapped(key)

Documentation needed.

Disconnect map file format

Interacting with Disconnect Maps

Every disconnect channel in the system BOM. Call new() to build the map and routing context, optionally assign() required hops, then resolve() to fill channel-map chains.


Columns

Columns are automatically generated when disconnect_map.new() is called. Additional columns are not supported and may result in an error when parsing.

Column Description
from_channel_type channel-map from type
from-disconnect_port_channel_type disconnect connector on the same side as from
to-disconnect_port_channel_type disconnect connector on the same side as to
to_channel_type channel-map to type
requirement user assign() step number for this disconnect-channel mapping
satisfaction resolve() assign() step number (always >= requirement when both set)
is_imaginary TRUE if this mapping was minted because no real disconnect channel was available
has_imaginary TRUE if this disconnect has any imaginary channel mappings
manual_map_disconnect_python_equiv disconnect_map.assign() that would form this requirement

Commands:

Use the following functions by first importing the module in your script like this:

from harnice.lists import disconnect_map
then use as written.

disconnect_map.new()

Create disconnect channel rows on the disconnect map from the BOM.

Rebuilds system.disconnect_routing_context as one row per unique (from_channel_type, to_channel_type) pair within each group_of_connected_harnesses, per disconnect, per directionality (1 / -1, or 0 if reversible), with an availables count of disconnect channels that obey that directionality. Does not route channels or fill channel-map chains (see resolve()).

disconnect_map.assign(to_from_channel_device_refdes, to_from_channel_id, intended_disconnect_refdes, intended_disconnect_channel_id=None, from_channel_is_close_to_disconnect_side=None, satisfaction=False)

Assign a mapped channel through a disconnect.

Looks up the mapped channel's from→to types and group_of_connected_harnesses, finds the matching routing-context row(s), picks a free disconnect channel that obeys directionality, records the device↔disconnect mapping on the disconnect map, and decrements availables on the matching context row (never below 0).

By default writes requirement (user assign). With satisfaction=True (used by resolve), writes satisfaction instead.

Returns 0 on a real assignment, -1 if an imaginary disconnect-map row was appended because availables was already 0.

disconnect_map.resolve()

Complete disconnect routing for each mapped channel (channel-map order).

Uses the routing context (filtered to group_of_connected_harnesses + from→to pair) to find the fewest disconnect channels that connect from_harness → optional ordered requirement waypoints → to_harness. Prefers paths with remaining availables; if none exist, allows availables == 0 (imaginaries).

Gap fills call assign(..., satisfaction=True). Existing requirement rows get a satisfaction stamp. Writes chain_of_connectors and chain_of_harnesses on each mapped channel-map row from the resulting path.

Stamps is_imaginary / has_imaginary on the disconnect map. Prints one warning per disconnect that needed imaginary channels to satisfy the map.

No-op when disconnect_map.new() found no harness topology (empty parent diagram / no harness-mated connectors): there is nothing to resolve.

Where mapping runs in a system build

  1. Before build instructions: channel_map.new() runs automatically during system.build(). It reads the system connector list and each device's signals list, and writes one channel-map row per (device_refdes, channel_id) on each connector (disconnect symbols are skipped—they are disconnect channels, not device endpoints).

  2. In build instructions (typical order): map channels → disconnect_map.new() → set disconnect paths on the channel map (manual) → build the circuits list.

  3. After mapping: circuits_list.new() expands every mapped channel pair (and its disconnect path) into individual conductor circuits.

The default system build instructions follow this pattern:

# Manual channel pairs: (from_device_refdes, from_channel_id) -> (to_device_refdes, to_channel_id)
# channel_map.map(("MIC3", "out1"), ("PREAMP1", "in2"))

channel_map.map_unmapped_compatibles_alphabetically()

disconnect_map.new()

# optional: disconnect_map.assign(...) to pin required disconnect hops
disconnect_map.resolve()  # fills chain_of_connectors / chain_of_harnesses on the channel map

circuits_list.new()

Concepts

Channel. A channel is one logical link on a device—audio in, power, differential pair, etc. Its channel type (from the channel-types library) defines required signals and which other types it may mate with. See channel types documentation for compatibility rules.

Mapped channel. A mapped channel is a connection from one channel on a device to another. They define the functional requirement of any harness, and form the basis of which circuits are generated and where they go. The channel map is the list of mapped channels (and any still-unmapped channels).

Group of connected harnesses (group_of_connected_harnesses). Harness wires in your block diagram define equivalence classes of harnesses: any harnesses linked through disconnects share the same group_of_connected_harnesses label on the system connector list, copied onto each channel-map row. Automatic channel mapping only considers pairs within the same group of connected harnesses—channels in different groups are never auto-mapped together.

Disconnect channel. A disconnect is a pair of mating connectors (A and B sides) with a signals list. Each channel on that disconnect is identified by (disconnect_refdes, disconnect_channel_id). A cross-harness channel-map row may need a path of one or more disconnect channel assignments through the disconnect network to reach from the source harness to the destination harness.

Channel mapping in detail

Seeding: channel_map.new()

For every row in the system connector list where device_refdes is set and disconnect is not TRUE, Harnice loads that device's signals list. For each signal whose connector_name matches the connector on that row, it appends a row with:

  • group_of_connected_harnesses — copied from the matching system connector list row

  • from_device_refdes, from_device_channel_id, from_channel_type — the device endpoint

  • to_* columns empty until mapped

It also creates an empty mapped channels set TSV used to prevent double-mapping.

Manual mapping: channel_map.map(from_key, to_key=None, multi_ch_junction_key="")

from_key and to_key are tuples (device_refdes, channel_id). Calling map(from_key, to_key):

  1. Finds the row whose from_* columns match from_key and copies to_device_refdes, to_device_channel_id, and to_channel_type from the row that matches to_key.

  2. Removes the separate "to-only" row for to_key (each physical channel appears once as a from_* endpoint; mapping is directed).

  3. Records both keys in the mapped channels set and writes a manual_map_channel_python_equiv cell you can copy back into build instructions.

If a channel participates in a multi-channel junction (one physical connector fan-out mapped as several logical channels), pass multi_ch_junction_key="some_id" instead of a to_key. Several rows can share the same junction id without pairing to a single partner.

Raises ValueError if either key was already mapped or if a required to_key row is missing.

Automatic mapping: channel_map.map_unmapped_compatibles_alphabetically()

For each group_of_connected_harnesses, rows are sorted alphabetically by device refdes and channel id. For each row whose from_key is not yet mapped, Harnice scans later rows in the same group of connected harnesses and maps to the first candidate whose channel types are mutually compatible: either to_type is in chtype.compatibles(from_type) or from_type is in chtype.compatibles(to_type). This is deterministic but not unique—use manual channel_map.map() first for anything that must not follow alphabetical pairing.

Channel-map columns filled later

| Column | When it is filled |

|--------|-------------------|

| chain_of_connectors, chain_of_harnesses | Via disconnect_map.resolve() from the disconnect map path |

| multi_ch_junction_id | When you pass multi_ch_junction_key to channel_map.map() |

A row is fully mapped when to_device_refdes and to_device_channel_id are set. Disconnect routing requires fully mapped rows whose endpoints sit on different harnesses.

Disconnect mapping in detail

Channel mapping answers which device channels are the same signal. The disconnect map lists every disconnect channel from the BOM; disconnect_map.resolve() chooses which disconnect channels carry each mapped signal and writes chain_of_connectors / chain_of_harnesses on the channel map.

disconnect_map.new()

Reads the system BOM, loads each disconnect's signals list, and writes one row per (disconnect_refdes, disconnect_channel_id) with A/B disconnect port channel types. Builds the disconnect routing context used by assign() / resolve().

disconnect_map.assign()

Pins a mapped channel through a chosen disconnect (optional channel id and A/B side). Call after new() and before resolve() for required hops.

disconnect_map.resolve()

Completes disconnect routing for each mapped channel (optional user assign() waypoints first). Writes satisfaction on the disconnect map and fills chain_of_connectors / chain_of_harnesses on the channel map. Connector tokens are refdes.connector (e.g. MIC3.out1;X1.A;X1.B;PREAMP1.in1); harness names are refdes per hop (H1;H2).

Practical tips

  • Map critical channel pairs manually before map_unmapped_compatibles_alphabetically().

  • Use assign() for required disconnect hops; let resolve() fill the rest.

File Structure

Reference the files in your project by calling fileio.path("file key") from your script. They'll automatically use this structure:

fileio.dirpath("part_directory")                 |-- yourpn/
                                                     |-- earlier revs/
fileio.path("revision history")                      |-- revhistory.csv
fileio.dirpath("rev_directory")                      L-- your rev/
fileio.path("build instructions")                        |-- yourpn-revX-build_instructions.py
fileio.path("instances list")                            |-- yourpn-revX-instances_list.tsv
fileio.path("block diagram")                             |-- yourpn-revX-blockdiagram.svg
fileio.path("icd")                                       |-- yourpn-revX-signals_list.tsv
fileio.dirpath("instance_data")                          |-- instance_data/
fileio.dirpath("harnesses")                              |-- harnesses/
fileio.dirpath("lists")                                  |-- lists/
fileio.path("bom")                                       |   |-- yourpn-revX-bom.tsv
fileio.path("circuits list")                             |   |-- yourpn-revX-circuits_list.tsv
fileio.path("post harness instances list")               |   |-- yourpn-revX-post_harness_instances_list.tsv
fileio.path("system connector list")                     |   |-- yourpn-revX-system_connector_list.tsv
fileio.path("mapped channels set")                       |   |-- yourpn-revX-mapped_channels_set.tsv
fileio.path("library history")                           |   L-- yourpn-revX-library_import_history.tsv
fileio.dirpath("maps")                                   L-- maps/
fileio.path("channel map")                                   |-- yourpn-revX-channel_map.tsv
fileio.path("disconnect map")                                |-- yourpn-revX-disconnect_map.tsv
fileio.path("disconnect routing context")                    L-- yourpn-revX-disconnect_routing_context.tsv

Instructions for child harnesses

A system can define functions such as connector_chooser and m85049_chooser in its build instructions. Child harnesses call them after they import from this revision:

build_utils.run_instruction_from_relative(
    "connector_chooser",
    system_pn,
    system_lib_repo,
    system_lib_subpath,
    system_rev,
)
build_utils.run_instruction_from_relative(
    "m85049_chooser",
    system_pn,
    system_lib_repo,
    system_lib_subpath,
    system_rev,
)

That is how system-level behavior (mating-connector policy, per-position assignments) stays on the parent instead of being copy-pasted into every harness. A missing file or function raises. It runs in the harness working directory. It is not a macro: no instance folder, no library-history row. Only the function (plus imports and literal data) is loaded from the system file — the system's own channel-mapping and macros are not re-run.

Use state.harness inside the function for one harness position. When the system revision changes, the next harness rebuild picks up the new rules.


How to define a new system

  1. Make a folder for the part number of your system somewhere on your computer. Click Build in Harnice, which will generate an example system that you can then edit.

    Building a project
    1. In Harnice, click New project…. You do not need a rev folder yet.

    2. Fill in part number, revision (1 is fine), project type, and a description, then click OK. Harnice creates the folders and an example you can edit.

    3. Click Build to generate the rest of the project files.

  2. Open that system in Harnice and edit the block diagram. Place devices and disconnects from your library, draw harness wires between connectors, then save.

  3. Click Build often as you refine the diagram and build instructions.


Designing your block diagram

Device and disconnect symbols are placed in the system block diagram SVG from the Harnice console (Place device / Place disconnect).

Harness wires represent entire harnesses between connectors. The diagram is agnostic to the individual conductors, channels, or signals of a harness—only that certain connectors are connected via a harness.

To add disconnects between harnesses, place an official Harnice disconnect part and route harness wires to it. Set library pull fields on the placed symbol (same attributes used when placing from the library):

  1. MPN — part number of the disconnect convention

  2. lib_repo — traceable path to the repo that contains the disconnect

  3. lib_subpath — path between the library root and the part number, if any (for example, if the part lives at {library_root}/disconnect/audio/tascam-db25/tascam-db25-rev1/, use disconnect/audio/)

  4. rev — revision to use in this system (optional)

An external interface flag (red triangle) marks a connector that appears on the system's external ICD (fileio.path("icd")). Place it on a harness mating connector (yellow dot) or on a device connector that has no harness mated to it. It cannot be placed on a device connector that already has a harness. On build, flags on disconnect mating connectors fill the ICD from circuits; flags on unmated device connectors copy that connector's rows from the device's signals list.

Block diagram symbols

Default system build instructions run the subsystem_symbol macro once (artifact_id="subsystem-symbol-1", scale=0.1). Each additional run_macro call of that MPN is another imported instance; open it from instance data. The editor is the same drawing tools as a device symbol, plus System diagram, which drops the current system block diagram contents (not trace overlays) as a primitive scaled by the macro's scale argument. The macro seeds a blank SVG if none exists and does not overwrite a symbol you have already drawn.

On first seed (and on later builds when pins are still missing), the macro places one connector pin per unique connector_name in the system ICD (fileio.path("icd")). It also runs device signals-list checks against that ICD (the system's signals list). Pin mismatches vs the ICD print a warning and do not fail the build; missing pins are still appended. The ICD is compiled after build instructions, so this macro always reads the ICD from the previous build. After you add, remove, or rename external-interface flags, build twice: the first build writes the ICD, and the second build updates symbol pins and re-checks from it. That one-build lag is expected. If no previous ICD exists yet, verification is skipped.