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 product 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 |
|---|---|
net |
the physical harness (represented by a net in Kicad) 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 |
parent_instance |
general purpose reference |
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 |
translate_x |
derived from parent_csys and parent_csys_name |
translate_y |
derived from parent_csys and parent_csys_name |
rotate_csys |
derived from parent_csys and parent_csys_name |
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_container |
which cable is this instance physically bundled inside of |
cable_identifier |
cable unique identifier |
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 |
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 product 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 |
this_instance_mating_device_refdes |
if connector, refdes of the device it plugs into |
this_instance_mating_device_connector |
if connector, name of the connector it plugs into |
this_instance_mating_device_connector_mpn |
if connector, mpn of the connector it plugs into |
this_net_from_device_refdes |
if this instance is a channel, circuit, conductor, etc, the refdes of the device it interfaces with, just within this net |
this_net_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 net |
this_net_from_device_connector_name |
if this instance is a channel, circuit, conductor, etc, the name of the connector it interfaces with, just within this net |
this_net_to_device_refdes |
if this instance is a channel, circuit, conductor, etc, the refdes of the device it plugs into just within this net |
this_net_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 net |
this_net_to_device_connector_name |
if this instance is a channel, circuit, conductor, etc, the name of the connector it plugs into, just within this net |
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: 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 includeinstance_name; if present it must match theinstance_nameargument or the code will fail.ignore_duplicates: If True, does nothing when an instance with the sameinstance_namealready 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) whoseinstance_namekey identifies the instance to remove. Matching is done byinstance_nameonly.
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: Theinstance_nameof the instance to look up.attribute: The column name to read (e.g."mpn","net").
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: Theconnector_groupvalue to match.item_type: Theitem_typevalue 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.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."net","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 product
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: 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 merged_nets that are either mapped to other channels or 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 |
|---|---|
merged_net |
documentation needed |
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 |
disconnect_refdes_requirement |
documentation needed |
chain_of_connectors |
documentation needed |
chain_of_nets |
documentation needed |
manual_map_channel_python_equiv |
documentation needed |
Commands:
Use the following functions by first importing the module in your script like this: 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='')
Documentation needed.
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 |
|---|---|
net |
documentation needed |
net_chain_index |
0-based index into channel map chain_of_nets for this segment |
circuit_id |
documentation needed |
signal |
documentation needed |
net_from_refdes |
documentation needed |
net_from_channel_id |
documentation needed |
net_from_connector_name |
documentation needed |
net_from_cavity |
documentation needed |
net_to_refdes |
documentation needed |
net_to_channel_id |
documentation needed |
net_to_connector_name |
documentation needed |
net_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: then use as written.
circuits_list.new()
Makes a new blank circuits list. Overwrites existing circuits list.
Args: none
Returns: none
Disconnect Map
Interacting with Disconnect Maps
Every disconnect channel in the system BOM, plus rows with device assignments after routing. Call new() to list disconnect channels, assign() for manual path requirements, then resolve() to route cross-harness channel-map rows and perform disconnect channel assignments.
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 |
|---|---|
A-side_device_refdes |
device on harness A after this disconnect channel is assigned |
A-side_device_channel_id |
channel id on that A-side device |
A-side_device_channel_type |
channel type at the A-side endpoint for this assignment |
B-side_device_refdes |
device on harness B after this disconnect channel is assigned |
B-side_device_channel_id |
channel id on that B-side device |
B-side_device_channel_type |
channel type at the B-side endpoint for this assignment |
disconnect_refdes |
disconnect symbol refdes (from BOM) |
disconnect_channel_id |
channel on that disconnect (from BOM) |
A-disconnect_port_channel_type |
disconnect port channel type on shell A |
B-disconnect_port_channel_type |
disconnect port channel type on shell B |
manual_map_channel_python_equiv |
copy-paste disconnect_map.assign(...) for this disconnect channel |
Commands:
Use the following functions by first importing the module in your script like this: then use as written.
disconnect_map.new(*, visualize=False)
Create disconnect channel rows on the disconnect map from the BOM (unassigned disconnect channels and their disconnect port channel types).
Clears disconnect requirements so the file only contains rows written by
assign() calls after this new() (removing assign() from build instructions
drops stale requirements on the next build).
When visualize is true, regenerates disconnect_networks/ PNGs via :func:visualize.
When false, removes disconnect_networks/ if present. Does not route channels.
Call assign() to record manual requirements, then resolve() to route and assign
disconnect channels.
disconnect_map.assign(assignments)
Record manual disconnect path requirements (does not assign disconnect channels on the disconnect map yet).
Writes disconnect requirements (see :func:fileio.path) in call order. new()
clears that file first; later assign() calls in the same build append after earlier
ones. Re-assigning a channel moves it to the end.
Clears disconnect_refdes_requirement on affected channel-map rows until resolve().
Cross-harness paths are checked against endpoint channel-type compatibility at assign time.
Call resolve() after new() and any assign() calls to write maps and perform
disconnect channel assignments.
Each assignment is (device_channel_key, disconnect_choice) where device_channel_key
is (device_refdes, channel_id) for either endpoint on the row, and disconnect_choice
is 'no disconnects' or a path list such as [('X1', 'ch0')].
disconnect_map.resolve()
Finish disconnect routing for channel-map rows that do not already have a solution.
Call after new() and any assign() calls. Requirements in
disconnect requirements are applied in assign() order (each call and each
list entry appends to that order). Earlier requirements assign disconnect channels
first; DFS for remaining rows prefers channels in the same order. Raises ValueError
when a requirement
or auto-route cannot be satisfied.
disconnect_map.visualize()
Regenerate disconnect_networks/ PNGs from the current channel map (no routing).
Writes one Graphviz PNG per mapped channel-map row. Disconnect edges run shell A → shell B; colors show which port channel types mate the row's from/to types (red / blue / yellow).
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: then use as written.
library_history.rebuild()
Build the 'post harness instances list' by merging instance data from: - Each harness's instances list if the harness_pn is defined and file exists - Otherwise, fall back to the system-level instances list for matching nets
Writes a clean TSV with INSTANCES_LIST_COLUMNS.
library_history.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
- 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 - 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
library_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. - 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). - 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.tsvfile for a given channel type.Args
channel_type: Channel type identifier in standard tuple format(channel_type_id, lib_repo)or any string representation thatparsecan understand (for example"(5, 'https://github.com/harnice/harnice')").
Returns
str: Absolute path tochannel_types.tsvat 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 thatparsecan understand.
Returns
list[tuple[int, str]]: List of(channel_type_id, lib_repo)tuples taken directly from thecompatible_channel_typescolumn ofchannel_types.tsv. Returns an empty list if no compatibles are defined or if the channel type cannot be found.
Data format
- The
compatible_channel_typescolumn must be an AST-parseable Python value:- Single tuple:
(1, "library_repo") - List of tuples:
[(1, "library_repo"), (2, "library_repo")]
- Single tuple:
chtype.attribute(channel_type, attribute)Read any additional column from
channel_types.tsvfor a given channel type.Args
channel_type: Channel type identifier in standard tuple format(channel_type_id, lib_repo)or any string representation thatparsecan understand.attribute: Column header name inchannel_types.tsvfor the value you want to read (for example"description","notes","voltage_rating").
Returns
Any: Value stored in the requestedattributecolumn for the matchingchannel_type_id. Returns an empty list[]if the channel type cannot be found.
Notes
- Reads
<library_root>/channel_types.tsv(same file aspath()). - Use this for any per-channel-type metadata you've added as extra columns
beyond the core ones like
channel_type_id,signals, andcompatible_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 thatparsecan understand.
Returns
list[str]: List of signal names from thesignalscolumn ofchannel_types.tsvfor the matchingchannel_type_id. If the column is blank or the channel type cannot be found, returns an empty list.
Data format
- The
signalscolumn 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 thatparsecan understand.
Returns
list[tuple[int, str]]: List of(channel_type_id, lib_repo)tuples where the first entry is the parsedchannel_typeitself and the remaining entries are the compatibles returned bycompatibles(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.
- In a repository of your choice (or start with harnice_library_public on your own branch), navigate to
-
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
It is often useful to be able to change the signals list based on how you're using the device.
-
Each possible configuration of a device must define the same number of conductors throughout the device
- Changing a configuration must not alter physical build, form, fit, or function, and thus there shall be no conductors that are added or taken away. Maybe some signals are 'unused', but they have to at least be counted for across all configurations.
-
There can be an unlimited number of configuration variables
- Sometimes just one variable is useful: an SM58 microphone can produce output signals in either balanced vs unbalanced format, depending on how you want to use it.
- Sometimes there are many variables: suppose you have a mixing console with 32 inputs, and each input can accept either mic or line level inputs depending on the configuration, and each accept either in balanced or unbalanced format signals. Because there's a channel-type defined for each of those options, this would imply 64 configuration variables of the mixing console, each mapping to a unique configuration. This allows the auto channel mapper to find compatibles, and also helps the user track how to set up their device.
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 |
config_variable |
Change header or add more headers as needed. Blank: row is true across all values of this field. Otherwise, row is only true when configuration matches the value of this field. |
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: 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.
Manifests
Interacting with System Manifests
A table that relates reference designator to part number(s), and may contain other information indexed to the reference designator
Columns
Columns are automatically generated when manifest.new() is called. Additional columns are not supported and may result in an error when parsing.
| Column | Description |
|---|---|
net |
documentation needed |
harness_pn |
documentation needed |
Commands:
Use the following functions by first importing the module in your script like this: then use as written.
manifest.new()
Synchronize the system harness manifest with the system connector list: - Remove nets that no longer exist in the connector list - Add nets that appear in the connector list but not yet in the manifest - Preserve all other column data for nets that still exist
manifest.update_upstream(path_to_system_rev, system_pn_rev, manifest_nets, harness_pn)
Documentation needed.
manifest.new()
Synchronize the system harness manifest with the system connector list: - Remove nets that no longer exist in the connector list - Add nets that appear in the connector list but not yet in the manifest - Preserve all other column data for nets that still exist
manifest.update_upstream(path_to_system_rev, system_pn_rev, manifest_nets, harness_pn)
Documentation needed.
Building a system
-
Harnice runs the build instructions if it's found in the system directory.
-
The system is validated and verified.
-
A KiCad symbol that can be used represent this system in a block diagram is generated or updated based on the system.
Channel and disconnect mapping
After Harnice exports a BOM and system connector list from your KiCad block diagram, 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 schematic connectivity into a circuits list and downstream harness instances.
Channel map file format
Interacting with Channel Maps
A list of channels on devices within merged_nets that are either mapped to other channels or 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 |
|---|---|
merged_net |
documentation needed |
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 |
disconnect_refdes_requirement |
documentation needed |
chain_of_connectors |
documentation needed |
chain_of_nets |
documentation needed |
manual_map_channel_python_equiv |
documentation needed |
Commands:
Use the following functions by first importing the module in your script like this: 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='')
Documentation needed.
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, plus rows with device assignments after routing. Call new() to list disconnect channels, assign() for manual path requirements, then resolve() to route cross-harness channel-map rows and perform disconnect channel assignments.
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 |
|---|---|
A-side_device_refdes |
device on harness A after this disconnect channel is assigned |
A-side_device_channel_id |
channel id on that A-side device |
A-side_device_channel_type |
channel type at the A-side endpoint for this assignment |
B-side_device_refdes |
device on harness B after this disconnect channel is assigned |
B-side_device_channel_id |
channel id on that B-side device |
B-side_device_channel_type |
channel type at the B-side endpoint for this assignment |
disconnect_refdes |
disconnect symbol refdes (from BOM) |
disconnect_channel_id |
channel on that disconnect (from BOM) |
A-disconnect_port_channel_type |
disconnect port channel type on shell A |
B-disconnect_port_channel_type |
disconnect port channel type on shell B |
manual_map_channel_python_equiv |
copy-paste disconnect_map.assign(...) for this disconnect channel |
Commands:
Use the following functions by first importing the module in your script like this: then use as written.
disconnect_map.new(*, visualize=False)
Create disconnect channel rows on the disconnect map from the BOM (unassigned disconnect channels and their disconnect port channel types).
Clears disconnect requirements so the file only contains rows written by
assign() calls after this new() (removing assign() from build instructions
drops stale requirements on the next build).
When visualize is true, regenerates disconnect_networks/ PNGs via :func:visualize.
When false, removes disconnect_networks/ if present. Does not route channels.
Call assign() to record manual requirements, then resolve() to route and assign
disconnect channels.
disconnect_map.assign(assignments)
Record manual disconnect path requirements (does not assign disconnect channels on the disconnect map yet).
Writes disconnect requirements (see :func:fileio.path) in call order. new()
clears that file first; later assign() calls in the same build append after earlier
ones. Re-assigning a channel moves it to the end.
Clears disconnect_refdes_requirement on affected channel-map rows until resolve().
Cross-harness paths are checked against endpoint channel-type compatibility at assign time.
Call resolve() after new() and any assign() calls to write maps and perform
disconnect channel assignments.
Each assignment is (device_channel_key, disconnect_choice) where device_channel_key
is (device_refdes, channel_id) for either endpoint on the row, and disconnect_choice
is 'no disconnects' or a path list such as [('X1', 'ch0')].
disconnect_map.resolve()
Finish disconnect routing for channel-map rows that do not already have a solution.
Call after new() and any assign() calls. Requirements in
disconnect requirements are applied in assign() order (each call and each
list entry appends to that order). Earlier requirements assign disconnect channels
first; DFS for remaining rows prefers channels in the same order. Raises ValueError
when a requirement
or auto-route cannot be satisfied.
disconnect_map.visualize()
Regenerate disconnect_networks/ PNGs from the current channel map (no routing).
Writes one Graphviz PNG per mapped channel-map row. Disconnect edges run shell A → shell B; colors show which port channel types mate the row's from/to types (red / blue / yellow).
Where mapping runs in a system build
-
Before build instructions:
channel_map.new()runs automatically duringsystem.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). -
In build instructions (typical order): map channels → create disconnect channels → assign manual disconnect requirements → resolve disconnect paths → build the circuits list.
-
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()
# disconnect_map.assign([(("MIC1", "out1"), [("X1", "ch5")])])
disconnect_map.resolve() # also fills chain_of_nets 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.
Merged net (merged_net). KiCad harness wires in your block diagram define equivalence classes of harnesses: any harnesses linked through wires on the schematic share the same merged_harness_net label on the system connector list, copied onto each channel-map row as merged_net. Automatic channel mapping only considers pairs within the same merged net—channels on different merged nets 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:
-
merged_net— from the connector'smerged_harness_net -
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):
-
Finds the row whose
from_*columns matchfrom_keyand copiesto_device_refdes,to_device_channel_id, andto_channel_typefrom the row that matchesto_key. -
Removes the separate "to-only" row for
to_key(each physical channel appears once as afrom_*endpoint; mapping is directed). -
Records both keys in the mapped channels set and writes a
manual_map_channel_python_equivcell 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 merged_net, rows are sorted alphabetically by device refdes and channel id. For each row whose from_key is not yet mapped, Harnice scans later rows on the same net 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 |
|--------|-------------------|
| disconnect_refdes_requirement | During disconnect_map.assign() / disconnect_map.resolve() |
| chain_of_connectors, chain_of_nets | During disconnect_map.resolve() (via add_chains_to_channel_map) |
| 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. Disconnect mapping answers which physical disconnect pins that signal passes through when the source and destination devices are on different harnesses linked by disconnect symbols in KiCad.
Disconnect channels: disconnect_map.new()
Reads the system BOM for every disconnect instance, loads each disconnect's signals list, and writes one disconnect channel row per unique (disconnect_refdes, disconnect_channel_id) with:
-
disconnect_refdes,disconnect_channel_id -
A-disconnect_port_channel_type,B-disconnect_port_channel_type— disconnect port channel types on shells A and B -
A/B device columns empty until that disconnect channel is assigned for a channel-map row
new() also regenerates Graphviz PNGs under maps/disconnect_networks/ for debugging (one graph per merged_net). It does not route channels; call resolve() for that.
Manual requirements: disconnect_map.assign(assignments)
assignments is a list of (device_channel_key, disconnect_choice) pairs. device_channel_key is (device_refdes, channel_id) for either endpoint on a channel-map row. disconnect_choice is either:
-
'no disconnects'— the row's endpoints are on the same harness (or you require a direct harness link with no disconnect channel assignments), or -
A path list such as
[('X1', 'ch5')]or[('X1', 'ch0'), ('X2', 'ch1')]— disconnect channel keys that must appear on the route.
assign() validates that listed disconnect channels exist on the disconnect map, checks endpoint channel-type compatibility for cross-harness rows, and appends rows to disconnect requirements (ordered by assign_order). It clears disconnect_refdes_requirement on affected channel-map rows until resolve() runs. Assign does not perform disconnect channel assignments on the disconnect map yet—assignment happens in resolve().
Re-assigning the same device channel moves its requirement to the end of the order (later requirements win precedence for disconnect channel preference).
Routing: disconnect_map.resolve()
resolve() is the main solver. It:
-
Applies manual requirements in
disconnect requirementsorder. For each requirement on a fully mapped channel-map row: -
Same harness → writes
disconnect_refdes_requirementas['no disconnects']. -
Cross-harness with
'no disconnects'→ error (a path is required). -
Cross-harness with a disconnect channel list → finds a valid path that includes every required disconnect channel, respects already-assigned disconnect channels from earlier requirements, and mates
from_channel_type/to_channel_typeat the path ends. Writes the chosen path todisconnect_refdes_requirementand assigns each disconnect channel on the disconnect map (fills A-side and B-side device columns for that channel-map link). -
Auto-routes remaining cross-harness rows that still need a solution, sorted by earliest
assign_ordertouching either endpoint (so rows related to early manual assigns are resolved first). Uses depth-first search (find_first_disconnect_path) over the disconnect network: -
The first disconnect channel in the path must mate
from_channel_typeat the source harness; the last must mateto_channel_typeat the destination harness. -
Middle harnesses are pass-through: each disconnect channel mates disconnect port channel types on the pin, not necessarily the original from/to types on every leg.
-
Each
(disconnect_refdes, disconnect_channel_id)may be used at most once per path; disconnect channels already assigned by an earlier row are skipped. If the first feasible path conflicts, the solver tries the next path. -
Search tries the row as written, then with from/to swapped if needed.
-
DFS prefers disconnect channels that appear in earlier manual requirements.
-
Backfills every disconnect channel on every resolved path with A/B device endpoints (needed for
circuits_list). -
Calls
add_chains_to_channel_map(), which setschain_of_nets(and related connector chain data) from each row'sdisconnect_refdes_requirement. -
Writes the updated channel map and disconnect map, then refreshes disconnect-network PNGs.
If no path exists, resolve() raises ValueError naming the channel-map endpoints and merged_net—add or adjust disconnect_map.assign() requirements, or change which disconnect channels are already assigned.
Assigned vs available disconnect channels
A disconnect-map row is assigned when any non-identity column is filled (A-side or B-side device refdes/channel, or manual_map_channel_python_equiv). Rows with only disconnect channel identity filled are available. Two channel-map rows cannot assign the same disconnect channel unless they share the same mapping (the solver treats assigned disconnect channels as consumed).
Disconnect-map columns after routing
| Column | Meaning after resolve() |
|--------|---------------------------|
| A-side_* / B-side_* | Device channel on each harness that uses this disconnect channel assignment for a given channel-map row |
| manual_map_channel_python_equiv | Copy-paste disconnect_map.assign(...) for that disconnect channel |
| disconnect_refdes, disconnect_channel_id, A-disconnect_port_channel_type, B-disconnect_port_channel_type | Unchanged disconnect channel identity |
Channel-map disconnect_refdes_requirement cell
After resolve(), each cross-harness mapped row stores oriented steps (list of 4-tuples: disconnect refdes, channel id, entry shell, exit shell) or ['no disconnects']. Same-harness mapped rows also get ['no disconnects']. This cell is the source of truth for chain_of_nets and for circuits_list.new() when expanding conductors through disconnects.
End-to-end example
Suppose merged net NET_AUDIO contains device MIC1 (harness H1) and PREAMP1 (harness H2), joined in KiCad by harness wires through disconnect X1:
-
channel_map.new()creates rows forMIC1/out1,PREAMP1/in2, etc., all withmerged_net=NET_AUDIO. -
channel_map.map(("MIC1", "out1"), ("PREAMP1", "in2"))pairs the microphone output to the preamp input. -
disconnect_map.new()lists every channel onX1as available disconnect channels. -
disconnect_map.assign([(("MIC1", "out1"), [("X1", "line_in")])])records that this link must use disconnect channelX1/line_in. -
disconnect_map.resolve()assigns that disconnect channel, writes the full path on the channel-map row, fillschain_of_nets(e.g.H1;H2or the expanded harness sequence), and leaves other disconnect channels onX1available for other rows. -
circuits_list.new()emits one circuit per signal in the channel type, routed through the reserved disconnect pins.
Practical tips
-
Map critical pairs manually before
map_unmapped_compatibles_alphabetically(). -
Assign disconnect paths before
resolve(); use partial disconnect channel lists when you care which pin is used but not the full trunk path—the solver can extend the path if types allow. -
Inspect
maps/disconnect_networks/*.pngafternew()orresolve()—red is channel-map from, blue is to, within eachmerged_net. -
If build fails on disconnect routing, check: both endpoints mapped, types compatible at path ends, required disconnect channels not already assigned by another row, and disconnect symbols present on the BOM with A/B sides on the correct harnesses.
File Structure
Reference the files in your product 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("library history") |-- yourpn-revX-library_import_history.tsv
fileio.path("block diagram") |-- yourpn-revX-blockdiagram.svg
fileio.dirpath("instance_data") |-- instance_data/
fileio.dirpath("build_instructions_for_relatives") |-- build_instructions_for_relatives/
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("harness manifest") | |-- yourpn-revX-harness_manifest.tsv
fileio.path("system connector list") | |-- yourpn-revX-system_connector_list.tsv
fileio.path("mapped channels set") | L-- yourpn-revX-mapped_channels_set.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 requirements") |-- yourpn-revX-disconnect_requirements.tsv
fileio.dirpath("disconnect_networks") L-- disconnect_networks/
How to define a new system
-
Make a folder for the part number of your system somewhere on your computer. Run Harnice Build (
harnice -b), which will generate an example system that you can then edit.Building a product
-
Navigate to your device folder (
cdin command line). You don't need to make a rev folder yet, just make sure your command line is in a folder you want to represent the device you're working on. -
Build it with Harnice (
harnice -borharnice --build). It should walk you through the following steps then produce an example:-
No valid Harnice file structure detected in 'your_part_number'. Create new PN here? [y]:hit enter -
Enter revision number [1]:hit enter for rev1 or type "A" or whatever you want your first rev to be called -
What product type are you working on? (harness, system, device, etc.):type "device" -
Enter a description of this device [DEVICE, FUNCTION, ATTRIBUTES, etc.]:self-explanatory -
Enter a description for this revision [INITIAL RELEASE]:hit enter, otherwise type the goal of the first revision
-
Note
It will probably fail with
FileNotFoundError: Schematic not found. Check your kicad sch exists at this name and location:. This is included with the default system build instructions. -
-
Make a new Kicad project located at the path from the above error. Make a schematic in the same directory.
-
Add Harnice devices from a validated device repo as symbols in your kicad_sch. Save and run
harnice --buildoften.
Designing your block diagram in Kicad
Device symbols can be added to your KiCad schematic.
KiCad wires can be drawn that represent entire harnesses.
KiCad is agnostic to the individual conductors, channels, or signals of a harness, just that there are certain connectors that are connected to each other via a harness.
To add disconnects in between harnesses in your system, add an official Harnice disconnect part to your project and route nets to it. Add the following info to the properties of the disconnect symbol:
-
in
MPNwrite the part number of the disconnect convention -
in
lib_repowrite the traceable path to the repo that contains the disconnect convention part number -
in
lib_subpathadd the path in between the item_type and the part number, if it exists, for your disconnect, in your library. for example, if your part number is at{fileio.get_path_to_project(traceable_key)}/disconnect/audio/tascam-db25/tascam-db25-rev1/, chooseaudio/ -
in
revadd the rev you want to use in this system. Optionally, leave it blank.