harnice 0.3.0__py3-none-any.whl → 0.3.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- harnice/fileio.py +53 -9
- harnice/lists/instances_list.py +41 -39
- harnice/lists/rev_history.py +258 -42
- harnice/products/harness.py +8 -8
- harnice/products/part.py +1 -3
- harnice/products/system.py +187 -15
- harnice/state.py +4 -0
- harnice/utils/feature_tree_utils.py +1 -1
- harnice/utils/formboard_utils.py +5 -11
- harnice/utils/library_utils.py +8 -2
- harnice/utils/system_utils.py +40 -18
- {harnice-0.3.0.dist-info → harnice-0.3.2.dist-info}/METADATA +2 -1
- {harnice-0.3.0.dist-info → harnice-0.3.2.dist-info}/RECORD +17 -17
- {harnice-0.3.0.dist-info → harnice-0.3.2.dist-info}/WHEEL +0 -0
- {harnice-0.3.0.dist-info → harnice-0.3.2.dist-info}/entry_points.txt +0 -0
- {harnice-0.3.0.dist-info → harnice-0.3.2.dist-info}/licenses/LICENSE +0 -0
- {harnice-0.3.0.dist-info → harnice-0.3.2.dist-info}/top_level.txt +0 -0
harnice/fileio.py
CHANGED
|
@@ -41,13 +41,15 @@ def path(target_value, structure_dict=None, base_directory=None):
|
|
|
41
41
|
|
|
42
42
|
# returns the filepath/filename of a filekey.
|
|
43
43
|
"""
|
|
44
|
-
|
|
44
|
+
Recursively searches for a value in a nested JSON structure and returns the path to the element containing that value.
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
It's complicated ... check out https://harnice.io/commands/fileio/ for more information.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
target_value (str): The value to search for.
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
Returns:
|
|
52
|
+
list: A list of container names leading to the element containing the target value, or None if not found.
|
|
51
53
|
"""
|
|
52
54
|
|
|
53
55
|
# FILES NOT DEPENDENT ON PRODUCT TYPE
|
|
@@ -65,7 +67,20 @@ def path(target_value, structure_dict=None, base_directory=None):
|
|
|
65
67
|
harnice_root = os.path.dirname(
|
|
66
68
|
os.path.dirname(os.path.dirname(harnice.__file__))
|
|
67
69
|
)
|
|
68
|
-
|
|
70
|
+
|
|
71
|
+
library_locations_path = os.path.join(harnice_root, "library_locations.csv")
|
|
72
|
+
|
|
73
|
+
if not os.path.exists(library_locations_path):
|
|
74
|
+
from harnice import cli
|
|
75
|
+
answer = cli.prompt(f"Library locations file not found at {library_locations_path}. Create it?", default="y")
|
|
76
|
+
if answer.lower() not in ("y", "yes", ""):
|
|
77
|
+
exit()
|
|
78
|
+
|
|
79
|
+
with open(library_locations_path, "w") as f:
|
|
80
|
+
f.write("repo_url,local_path\n")
|
|
81
|
+
f.write(f"https://github.com/harnice/harnice,{os.path.join(harnice_root, 'library_public')}\n")
|
|
82
|
+
|
|
83
|
+
return library_locations_path
|
|
69
84
|
|
|
70
85
|
if target_value == "project locations":
|
|
71
86
|
import harnice
|
|
@@ -73,6 +88,17 @@ def path(target_value, structure_dict=None, base_directory=None):
|
|
|
73
88
|
harnice_root = os.path.dirname(
|
|
74
89
|
os.path.dirname(os.path.dirname(harnice.__file__))
|
|
75
90
|
)
|
|
91
|
+
project_locations_path = os.path.join(harnice_root, "project_locations.csv")
|
|
92
|
+
if not os.path.exists(project_locations_path):
|
|
93
|
+
from harnice import cli
|
|
94
|
+
answer = cli.prompt(f"Project locations file not found at {project_locations_path}. Create it?", default="y")
|
|
95
|
+
if answer.lower() not in ("y", "yes", ""):
|
|
96
|
+
exit()
|
|
97
|
+
|
|
98
|
+
with open(project_locations_path, "w") as f:
|
|
99
|
+
f.write("traceable_key,local_path\n")
|
|
100
|
+
f.write("your project part number,local path to your project\n")
|
|
101
|
+
|
|
76
102
|
return os.path.join(harnice_root, "project_locations.csv")
|
|
77
103
|
|
|
78
104
|
if target_value == "drawnby":
|
|
@@ -81,7 +107,24 @@ def path(target_value, structure_dict=None, base_directory=None):
|
|
|
81
107
|
harnice_root = os.path.dirname(
|
|
82
108
|
os.path.dirname(os.path.dirname(harnice.__file__))
|
|
83
109
|
)
|
|
84
|
-
|
|
110
|
+
|
|
111
|
+
drawnby_path = os.path.join(harnice_root, "drawnby.json")
|
|
112
|
+
if not os.path.exists(drawnby_path):
|
|
113
|
+
from harnice import cli
|
|
114
|
+
answer = cli.prompt(f"Drawnby file not found at {drawnby_path}. Create it?", default="y")
|
|
115
|
+
if answer.lower() not in ("y", "yes", ""):
|
|
116
|
+
exit()
|
|
117
|
+
|
|
118
|
+
name = cli.prompt("Enter your name: (recommended: first inital, last name, all caps: K SHUTT)")
|
|
119
|
+
|
|
120
|
+
while not name:
|
|
121
|
+
print("Name cannot be empty. Please try again.")
|
|
122
|
+
name = cli.prompt("Enter your name")
|
|
123
|
+
|
|
124
|
+
with open(drawnby_path, "w") as f:
|
|
125
|
+
f.write(f"{name}\n")
|
|
126
|
+
|
|
127
|
+
return drawnby_path
|
|
85
128
|
|
|
86
129
|
# FILES INSIDE OF A STRUCURE DEFINED BY FILEIO
|
|
87
130
|
# look up from default structure state if not provided
|
|
@@ -209,12 +252,13 @@ def verify_revision_structure():
|
|
|
209
252
|
f"Harnice only renders revisions with a blank status."
|
|
210
253
|
)
|
|
211
254
|
|
|
212
|
-
print(f"
|
|
255
|
+
print(f"Rendering PN: {state.pn}, Rev: {state.rev}")
|
|
213
256
|
rev_history.update_datemodified()
|
|
214
257
|
|
|
215
258
|
|
|
216
259
|
def today():
|
|
217
|
-
|
|
260
|
+
d = datetime.date.today()
|
|
261
|
+
return f"{d.month}/{d.day}/{d.year % 100}"
|
|
218
262
|
|
|
219
263
|
|
|
220
264
|
def get_git_hash_of_harnice_src():
|
harnice/lists/instances_list.py
CHANGED
|
@@ -5,23 +5,25 @@ from threading import Lock
|
|
|
5
5
|
from harnice import fileio, state
|
|
6
6
|
|
|
7
7
|
COLUMNS = [
|
|
8
|
-
"net", #
|
|
9
|
-
"instance_name", #
|
|
10
|
-
"print_name", #
|
|
11
|
-
"bom_line_number", #
|
|
12
|
-
"mfg", #
|
|
13
|
-
"mpn", #
|
|
8
|
+
"net", # the physical harness (represented by a net in Kicad) that this instance is part of
|
|
9
|
+
"instance_name", # the unique name of this instance
|
|
10
|
+
"print_name", # the non-unique, human-readable name of this instance, used for printing on output documents
|
|
11
|
+
"bom_line_number", # if this instance represents a physical procurable good, it gets assigned a line number on a bill of materials
|
|
12
|
+
"mfg", # manufacturer of this instance
|
|
13
|
+
"mpn", # manufacturer part number
|
|
14
14
|
"item_type", # connector, backshell, whatever
|
|
15
15
|
"parent_instance", # general purpose reference
|
|
16
16
|
"location_type", # each instance is either better represented by one or ther other
|
|
17
17
|
"segment_group", # the group of segments that this instance is part of
|
|
18
18
|
"segment_order", # the sequential id of this item in its segment group
|
|
19
19
|
"connector_group", # a group of co-located parts (connectors, backshells, nodes)
|
|
20
|
-
"channel_group", #
|
|
20
|
+
"channel_group", # other instances associated with this one because they are part of the same channel will share this value
|
|
21
21
|
"circuit_id", # which signal this component is electrically connected to
|
|
22
22
|
"circuit_port_number", # the sequential id of this item in its signal chain
|
|
23
23
|
"node_at_end_a", # derived from formboard definition
|
|
24
24
|
"node_at_end_b", # derived from formboard definition
|
|
25
|
+
"print_name_at_end_a", # human-readable name of this instance if needed, associated with 'node_at_end_a'
|
|
26
|
+
"print_name_at_end_b", # human-readable name of this instance if needed, associated with 'node_at_end_b'
|
|
25
27
|
"parent_csys_instance_name", # the other instance upon which this instance's location is based
|
|
26
28
|
"parent_csys_outputcsys_name", # the specific output coordinate system of the parent that this instance's location is based
|
|
27
29
|
"translate_x", # derived from parent_csys and parent_csys_name
|
|
@@ -29,9 +31,9 @@ COLUMNS = [
|
|
|
29
31
|
"rotate_csys", # derived from parent_csys and parent_csys_name
|
|
30
32
|
"absolute_rotation", # manual add, not nominally used unless it's a flagnote, segment, or node
|
|
31
33
|
"csys_children", # imported csys children from library attributes file
|
|
32
|
-
"cable_group", #
|
|
33
|
-
"cable_container", #
|
|
34
|
-
"cable_identifier", #
|
|
34
|
+
"cable_group", # other instances associated with this one because they are part of the same cable will share this value
|
|
35
|
+
"cable_container", # which cable is this instance physically bundled inside of
|
|
36
|
+
"cable_identifier", # cable unique identifier
|
|
35
37
|
"length", # derived from formboard definition, the length of a segment
|
|
36
38
|
"diameter", # apparent diameter of a segment <---------- change to print_diameter
|
|
37
39
|
"appearance", # see harnice.utils.appearance for details
|
|
@@ -40,39 +42,39 @@ COLUMNS = [
|
|
|
40
42
|
"note_parent", # the instance the note applies to. typically don't use this in the instances list, just note_utils
|
|
41
43
|
"note_text", # the content of the note
|
|
42
44
|
"note_affected_instances", # list of instances that are affected by the note
|
|
43
|
-
"lib_repo", #
|
|
44
|
-
"lib_subpath", #
|
|
45
|
-
"lib_desc", #
|
|
46
|
-
"lib_latest_rev", #
|
|
47
|
-
"lib_rev_used_here", #
|
|
48
|
-
"lib_status", #
|
|
45
|
+
"lib_repo", # publically-traceable URL of the library this instance is from
|
|
46
|
+
"lib_subpath", # path to the instance within the library (directories between the product type and the part number)
|
|
47
|
+
"lib_desc", # description of the instance per the library's revision history
|
|
48
|
+
"lib_latest_rev", # the latest revision of the instance that exists in the remote library
|
|
49
|
+
"lib_rev_used_here", # the revision of the instance that is currently used in this project
|
|
50
|
+
"lib_status", # the status of the instance per the library's revision history
|
|
49
51
|
"lib_releaseticket", #documentation needed
|
|
50
|
-
"lib_datestarted", #
|
|
51
|
-
"lib_datemodified", #
|
|
52
|
-
"lib_datereleased", #
|
|
53
|
-
"lib_drawnby", #
|
|
54
|
-
"lib_checkedby", #
|
|
55
|
-
"project_editable_lib_modified", #
|
|
56
|
-
"lib_build_notes", #
|
|
57
|
-
"lib_tools", #
|
|
52
|
+
"lib_datestarted", # the date this instance was first added to the library
|
|
53
|
+
"lib_datemodified", # the date this instance was last modified in the library
|
|
54
|
+
"lib_datereleased", # the date this instance was released in the library, if applicable, per the library's revision history
|
|
55
|
+
"lib_drawnby", # the name of the person who drew the instance, per the library's revision history
|
|
56
|
+
"lib_checkedby", # the name of the person who checked the instance, per the library's revision history
|
|
57
|
+
"project_editable_lib_modified", # a flag to indicate if the imported contents do not match the library's version (it's been locally modified)
|
|
58
|
+
"lib_build_notes", # recommended build notes that come with the instance from the library
|
|
59
|
+
"lib_tools", # recommended tools that come with the instance from the library
|
|
58
60
|
"this_instance_mating_device_refdes", # if connector, refdes of the device it plugs into
|
|
59
61
|
"this_instance_mating_device_connector", # if connector, name of the connector it plugs into
|
|
60
62
|
"this_instance_mating_device_connector_mpn", # if connector, mpn of the connector it plugs into
|
|
61
|
-
"this_net_from_device_refdes", #
|
|
62
|
-
"this_net_from_device_channel_id", #
|
|
63
|
-
"this_net_from_device_connector_name", #
|
|
64
|
-
"this_net_to_device_refdes", #
|
|
65
|
-
"this_net_to_device_channel_id", #
|
|
66
|
-
"this_net_to_device_connector_name", #
|
|
67
|
-
"this_channel_from_device_refdes", # if channel, refdes of the device
|
|
68
|
-
"this_channel_from_device_channel_id", #
|
|
69
|
-
"this_channel_to_device_refdes", # if channel, refdes of the device
|
|
70
|
-
"this_channel_to_device_channel_id", #
|
|
71
|
-
"this_channel_from_channel_type", #
|
|
72
|
-
"this_channel_to_channel_type", #
|
|
73
|
-
"signal_of_channel_type", #
|
|
74
|
-
"debug", #
|
|
75
|
-
"debug_cutoff", #
|
|
63
|
+
"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
|
|
64
|
+
"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
|
|
65
|
+
"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
|
|
66
|
+
"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
|
|
67
|
+
"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
|
|
68
|
+
"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
|
|
69
|
+
"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
|
|
70
|
+
"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
|
|
71
|
+
"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
|
|
72
|
+
"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
|
|
73
|
+
"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
|
|
74
|
+
"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
|
|
75
|
+
"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
|
|
76
|
+
"debug", # the call chain of the function that last modified this instance row
|
|
77
|
+
"debug_cutoff", # blank cell to visually cut off the previous column
|
|
76
78
|
]
|
|
77
79
|
|
|
78
80
|
|
harnice/lists/rev_history.py
CHANGED
|
@@ -6,27 +6,59 @@ from harnice import fileio, state
|
|
|
6
6
|
|
|
7
7
|
# === Global Columns Definition ===
|
|
8
8
|
COLUMNS = [
|
|
9
|
-
"product",
|
|
10
|
-
"mfg",
|
|
11
|
-
"pn",
|
|
12
|
-
"desc",
|
|
13
|
-
"rev",
|
|
14
|
-
"status",
|
|
15
|
-
"releaseticket",
|
|
16
|
-
"library_repo",
|
|
17
|
-
"library_subpath",
|
|
18
|
-
"datestarted",
|
|
19
|
-
"datemodified",
|
|
20
|
-
"datereleased",
|
|
21
|
-
"git_hash_of_harnice_src",
|
|
22
|
-
"drawnby",
|
|
23
|
-
"checkedby",
|
|
24
|
-
"revisionupdates",
|
|
25
|
-
"affectedinstances",
|
|
9
|
+
"product", # the harnice product type (e.g. "harness", "connector", "device", "system", "macro", "flagnote", "tblock")
|
|
10
|
+
"mfg", # who manufactures this product (blank ok)
|
|
11
|
+
"pn", # name, part number, other identifier of this part. mfg+mpn combination must be unique within the library.
|
|
12
|
+
"desc", # a brief description of this product
|
|
13
|
+
"rev", # the revision of the part
|
|
14
|
+
"status", # "released", "obsolete", etc. Harnice will not render a revision if the status has text in this field as a form of protection.
|
|
15
|
+
"releaseticket", # many companies do this, but it's not required.
|
|
16
|
+
"library_repo", # auto-filled on render if the current working directory is discovered to be a library repository.
|
|
17
|
+
"library_subpath", # auto-filled on render if in a library repository, this is the chain of directories between the product type and the part number
|
|
18
|
+
"datestarted", # auto-filled to be the date when this part was first intialized
|
|
19
|
+
"datemodified", # updates to today's date upon rendering
|
|
20
|
+
"datereleased", # up to user to fill in as needed
|
|
21
|
+
"git_hash_of_harnice_src", # auto-filled, git hash of the harnice source code during the latest render
|
|
22
|
+
"drawnby", # auto-filled, the person who created the part
|
|
23
|
+
"checkedby", # the person who checked the part, blank ok
|
|
24
|
+
"revisionupdates", # a brief description of the changes made to this revision
|
|
25
|
+
"affectedinstances", # the instance names of the instances that were affected by this revision. can be referenced later by PDF builders and more.
|
|
26
26
|
]
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
def overwrite(content_dict):
|
|
30
|
+
"""
|
|
31
|
+
Overwrite a revision history entry.
|
|
32
|
+
|
|
33
|
+
**Arguments:**
|
|
34
|
+
|
|
35
|
+
- `content_dict` (dict): The content to overwrite the revision history entry with.
|
|
36
|
+
- This should be a dictionary with the keys and values to overwrite.
|
|
37
|
+
- The keys should be the column names, and the values should be the new values.
|
|
38
|
+
- Some keys are protected and cannot be overwritten:
|
|
39
|
+
- `"product"`
|
|
40
|
+
- `"mfg"`
|
|
41
|
+
- `"pn"`
|
|
42
|
+
- `"rev"`
|
|
43
|
+
- `"releaseticket"`
|
|
44
|
+
- `"library_repo"`
|
|
45
|
+
- `"library_subpath"`
|
|
46
|
+
- `"datestarted"`
|
|
47
|
+
|
|
48
|
+
The function will update the revision history file as referenced by the current product file structure.
|
|
49
|
+
|
|
50
|
+
**Returns:**
|
|
51
|
+
|
|
52
|
+
- `None`
|
|
53
|
+
|
|
54
|
+
**Raises:**
|
|
55
|
+
|
|
56
|
+
- `KeyError`: If a key is provided that is not in the COLUMNS list.
|
|
57
|
+
- `KeyError`: If a protected key is provided.
|
|
58
|
+
- `ValueError`: If the revision history file is not found.
|
|
59
|
+
- `ValueError`: If the revision is not found in the revision history file.
|
|
60
|
+
- `RuntimeError`: If `state.rev` is not set.
|
|
61
|
+
"""
|
|
30
62
|
PROTECTED_KEYS = [
|
|
31
63
|
"product",
|
|
32
64
|
"mfg",
|
|
@@ -84,6 +116,31 @@ def overwrite(content_dict):
|
|
|
84
116
|
|
|
85
117
|
|
|
86
118
|
def info(rev=None, path=None, field=None, all=False):
|
|
119
|
+
"""
|
|
120
|
+
Get information about a revision history entry.
|
|
121
|
+
|
|
122
|
+
**Arguments:**
|
|
123
|
+
|
|
124
|
+
- `rev` (str): The revision to get information about.
|
|
125
|
+
- `path` (str): The path to the revision history file.
|
|
126
|
+
- If not provided, the function will use the default path: `"revision history"`.
|
|
127
|
+
- `field` (str): The field to get information about.
|
|
128
|
+
- If not provided, the function will return the entire row.
|
|
129
|
+
- If provided, the function will return the value of the field.
|
|
130
|
+
- `all` (bool): If `True`, return all rows.
|
|
131
|
+
- If not provided, the function will return the first row.
|
|
132
|
+
|
|
133
|
+
**Returns:**
|
|
134
|
+
|
|
135
|
+
- `dict`: The row of the revision history entry (when `field` is not provided).
|
|
136
|
+
- `list`: A list of all rows in the revision history file (when `all=True`).
|
|
137
|
+
- `str`: The value of the field (when `field` is provided).
|
|
138
|
+
|
|
139
|
+
**Raises:**
|
|
140
|
+
|
|
141
|
+
- `FileNotFoundError`: If the revision history file is not found.
|
|
142
|
+
- `ValueError`: If the revision is not found in the revision history file.
|
|
143
|
+
"""
|
|
87
144
|
if path is None:
|
|
88
145
|
path = fileio.path("revision history")
|
|
89
146
|
|
|
@@ -104,7 +161,6 @@ def info(rev=None, path=None, field=None, all=False):
|
|
|
104
161
|
|
|
105
162
|
for row in rows:
|
|
106
163
|
if row.get("rev") == rev:
|
|
107
|
-
|
|
108
164
|
# ------------------------------------------------------
|
|
109
165
|
# Field requested
|
|
110
166
|
# ------------------------------------------------------
|
|
@@ -142,6 +198,17 @@ def info(rev=None, path=None, field=None, all=False):
|
|
|
142
198
|
|
|
143
199
|
|
|
144
200
|
def initial_release_exists():
|
|
201
|
+
"""
|
|
202
|
+
Check if an initial release exists.
|
|
203
|
+
|
|
204
|
+
**Arguments:**
|
|
205
|
+
|
|
206
|
+
None
|
|
207
|
+
|
|
208
|
+
**Returns:**
|
|
209
|
+
|
|
210
|
+
- `bool`: `True` if a revision with the text `"INITIAL RELEASE"` in the `"revisionupdates"` field exists, `False` otherwise.
|
|
211
|
+
"""
|
|
145
212
|
try:
|
|
146
213
|
for row in fileio.read_tsv("revision history"):
|
|
147
214
|
if str(row.get("revisionupdates", "")).strip() == "INITIAL RELEASE":
|
|
@@ -153,12 +220,39 @@ def initial_release_exists():
|
|
|
153
220
|
|
|
154
221
|
|
|
155
222
|
def initial_release_desc():
|
|
223
|
+
"""
|
|
224
|
+
Get the description of the initial release.
|
|
225
|
+
|
|
226
|
+
**Arguments:**
|
|
227
|
+
|
|
228
|
+
None
|
|
229
|
+
|
|
230
|
+
**Returns:**
|
|
231
|
+
|
|
232
|
+
- `str`: The description of the revision which has `revisionupdates == 'INITIAL RELEASE'`.
|
|
233
|
+
"""
|
|
156
234
|
for row in fileio.read_tsv("revision history"):
|
|
157
235
|
if row.get("revisionupdates") == "INITIAL RELEASE":
|
|
158
236
|
return row.get("desc")
|
|
159
237
|
|
|
160
238
|
|
|
161
239
|
def update_datemodified():
|
|
240
|
+
"""
|
|
241
|
+
Update the `datemodified` field of the current revision with today's date.
|
|
242
|
+
|
|
243
|
+
**Arguments:**
|
|
244
|
+
|
|
245
|
+
None
|
|
246
|
+
|
|
247
|
+
**Returns:**
|
|
248
|
+
|
|
249
|
+
- `None`
|
|
250
|
+
|
|
251
|
+
**Raises:**
|
|
252
|
+
|
|
253
|
+
- `ValueError`: If the revision history file is not found.
|
|
254
|
+
- `ValueError`: If the revision is not found in the revision history file.
|
|
255
|
+
"""
|
|
162
256
|
target_rev = state.partnumber("R")
|
|
163
257
|
|
|
164
258
|
# Read all rows
|
|
@@ -182,42 +276,94 @@ def update_datemodified():
|
|
|
182
276
|
writer.writerows(rows)
|
|
183
277
|
|
|
184
278
|
|
|
185
|
-
def new():
|
|
279
|
+
def new(ignore_product=False, path=None):
|
|
280
|
+
"""
|
|
281
|
+
Create a new revision history file.
|
|
282
|
+
|
|
283
|
+
**Arguments:**
|
|
284
|
+
|
|
285
|
+
- `ignore_product` (bool):
|
|
286
|
+
- If `True`, the function will raise an error if `state.product` is not set first.
|
|
287
|
+
- If `False`, the function will prompt the user to select a product type.
|
|
288
|
+
|
|
289
|
+
**Returns:**
|
|
290
|
+
|
|
291
|
+
- `None`
|
|
292
|
+
|
|
293
|
+
**Raises:**
|
|
294
|
+
|
|
295
|
+
- `ValueError`: If attempting to create a new revision history file without a product type when `ignore_product=True`.
|
|
296
|
+
- `ValueError`: If attempting to overwrite an existing revision history file.
|
|
297
|
+
"""
|
|
186
298
|
columns = COLUMNS
|
|
187
|
-
from harnice.cli import select_product_type
|
|
188
299
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
300
|
+
if path is None:
|
|
301
|
+
path = fileio.path("revision history")
|
|
302
|
+
|
|
303
|
+
if not ignore_product:
|
|
304
|
+
from harnice.cli import select_product_type
|
|
305
|
+
|
|
306
|
+
state.set_product(select_product_type())
|
|
307
|
+
|
|
308
|
+
if ignore_product and not state.product:
|
|
309
|
+
raise ValueError(
|
|
310
|
+
"You tried to create a new revision history file without a product type. This is not allowed."
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
if not os.path.exists(path):
|
|
314
|
+
with open(path, "w", newline="", encoding="utf-8") as f:
|
|
315
|
+
writer = csv.DictWriter(f, fieldnames=columns, delimiter="\t")
|
|
316
|
+
writer.writeheader()
|
|
317
|
+
|
|
318
|
+
else:
|
|
319
|
+
raise ValueError(
|
|
320
|
+
"You tried to overwrite a revision history file- this is not allowed."
|
|
321
|
+
)
|
|
194
322
|
|
|
195
323
|
|
|
196
324
|
def append(next_rev=None):
|
|
197
|
-
|
|
325
|
+
"""
|
|
326
|
+
Append a new revision history entry to the current revision history file.
|
|
327
|
+
|
|
328
|
+
If the revision history file does not exist, the function will create it.
|
|
329
|
+
If the revision history file exists, the function will append a new entry to the file.
|
|
330
|
+
|
|
331
|
+
It will prompt the user for the following fields:
|
|
332
|
+
|
|
333
|
+
- `product`: The product type of the part.
|
|
334
|
+
- `desc`: The description of the part.
|
|
335
|
+
- `revisionupdates`: What is the purpose of this revision?
|
|
336
|
+
|
|
337
|
+
If the previous revision has a blank status, the function will prompt the user to obsolete it with a message.
|
|
338
|
+
|
|
339
|
+
**Arguments:**
|
|
340
|
+
|
|
341
|
+
- `next_rev` The next revision number to append.
|
|
342
|
+
|
|
343
|
+
**Returns:**
|
|
198
344
|
|
|
199
|
-
|
|
345
|
+
- `None`
|
|
346
|
+
"""
|
|
347
|
+
from harnice import cli
|
|
200
348
|
|
|
201
349
|
if not os.path.exists(fileio.path("revision history")):
|
|
202
350
|
new()
|
|
203
351
|
rows = fileio.read_tsv("revision history")
|
|
204
|
-
product_name = None
|
|
205
352
|
if rows:
|
|
206
353
|
for row in reversed(rows):
|
|
207
354
|
candidate = (row.get("product") or "").strip()
|
|
208
355
|
if candidate:
|
|
209
|
-
|
|
356
|
+
state.set_product(candidate)
|
|
210
357
|
break
|
|
211
|
-
if not
|
|
212
|
-
|
|
213
|
-
if not product_name:
|
|
214
|
-
product_name = cli.select_product_type()
|
|
215
|
-
product = product_name
|
|
358
|
+
if not state.product:
|
|
359
|
+
state.set_product(cli.select_product_type())
|
|
216
360
|
|
|
217
361
|
default_desc = ""
|
|
218
|
-
if
|
|
362
|
+
if state.product:
|
|
219
363
|
try:
|
|
220
|
-
product_module = importlib.import_module(
|
|
364
|
+
product_module = importlib.import_module(
|
|
365
|
+
f"harnice.products.{state.product}"
|
|
366
|
+
)
|
|
221
367
|
except ModuleNotFoundError:
|
|
222
368
|
product_module = None
|
|
223
369
|
else:
|
|
@@ -252,7 +398,7 @@ def append(next_rev=None):
|
|
|
252
398
|
|
|
253
399
|
if desc in [None, ""]:
|
|
254
400
|
desc = cli.prompt(
|
|
255
|
-
f"Enter a description of this {
|
|
401
|
+
f"Enter a description of this {state.product}",
|
|
256
402
|
default=default_desc,
|
|
257
403
|
)
|
|
258
404
|
|
|
@@ -271,17 +417,24 @@ def append(next_rev=None):
|
|
|
271
417
|
# add lib_repo if filepath is found in library locations
|
|
272
418
|
library_repo = ""
|
|
273
419
|
library_subpath = ""
|
|
274
|
-
|
|
420
|
+
# Normalize path separators for comparison (handle both forward and backslashes)
|
|
421
|
+
cwd = str(os.getcwd()).lower().replace("\\", "/").strip("~")
|
|
275
422
|
|
|
276
423
|
for row in fileio.read_tsv("library locations", delimiter=","):
|
|
277
|
-
|
|
424
|
+
# Normalize path separators and expand user home directory if needed
|
|
425
|
+
lib_local_path_raw = str(row.get("local_path", "")).strip()
|
|
426
|
+
lib_local_path = (
|
|
427
|
+
os.path.expanduser(lib_local_path_raw).lower().replace("\\", "/").strip("~")
|
|
428
|
+
)
|
|
278
429
|
if lib_local_path in cwd:
|
|
279
430
|
library_repo = row.get("repo_url")
|
|
280
431
|
|
|
281
432
|
# keep only the portion AFTER local_path
|
|
282
433
|
idx = cwd.find(lib_local_path)
|
|
283
|
-
remainder = cwd[idx + len(lib_local_path) :]
|
|
284
|
-
|
|
434
|
+
remainder = cwd[idx + len(lib_local_path) :]
|
|
435
|
+
# Normalize path separators - handle both forward and backslashes
|
|
436
|
+
remainder = remainder.replace("\\", "/").lstrip("/")
|
|
437
|
+
parts = remainder.split("/") if remainder else []
|
|
285
438
|
|
|
286
439
|
# find the part number in the path
|
|
287
440
|
pn = str(state.partnumber("pn")).lower()
|
|
@@ -292,6 +445,7 @@ def append(next_rev=None):
|
|
|
292
445
|
core_parts = parts
|
|
293
446
|
|
|
294
447
|
# build library_subpath and product
|
|
448
|
+
# Use forward slashes for cross-platform compatibility (works in URLs and most contexts)
|
|
295
449
|
if core_parts:
|
|
296
450
|
library_subpath = (
|
|
297
451
|
"/".join(core_parts[1:]) + "/" if len(core_parts) > 1 else ""
|
|
@@ -305,7 +459,7 @@ def append(next_rev=None):
|
|
|
305
459
|
|
|
306
460
|
rows.append(
|
|
307
461
|
{
|
|
308
|
-
"product":
|
|
462
|
+
"product": state.product,
|
|
309
463
|
"pn": state.pn,
|
|
310
464
|
"rev": next_rev,
|
|
311
465
|
"desc": desc,
|
|
@@ -323,3 +477,65 @@ def append(next_rev=None):
|
|
|
323
477
|
writer = csv.DictWriter(f, fieldnames=columns, delimiter="\t")
|
|
324
478
|
writer.writeheader()
|
|
325
479
|
writer.writerows(rows)
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
def part_family_append(content_dict, rev_history_path):
|
|
483
|
+
"""
|
|
484
|
+
Append a new revision history entry to the part family revision history file.
|
|
485
|
+
|
|
486
|
+
Intended to be called by part family scripts only.
|
|
487
|
+
|
|
488
|
+
The function will automatically update the following fields in the content dictionary:
|
|
489
|
+
|
|
490
|
+
- `datemodified`: Set to today's date
|
|
491
|
+
- `drawnby`: Set to the current user's name
|
|
492
|
+
- `git_hash_of_harnice_src`: Set to the current git hash of the harnice source code
|
|
493
|
+
|
|
494
|
+
If the revision history file does not exist, the function will create it.
|
|
495
|
+
If an entry with the same revision number already exists, the function will update that entry.
|
|
496
|
+
Otherwise, the function will append a new entry to the file.
|
|
497
|
+
|
|
498
|
+
**Arguments:**
|
|
499
|
+
|
|
500
|
+
- `content_dict` (dict): The content to append to the part family revision history file.
|
|
501
|
+
- Should contain keys matching the `COLUMNS` list.
|
|
502
|
+
- The `rev` key is used to determine if an entry already exists.
|
|
503
|
+
- `rev_history_path` (str): The path to the part family revision history file.
|
|
504
|
+
|
|
505
|
+
**Returns:**
|
|
506
|
+
|
|
507
|
+
- `None`
|
|
508
|
+
|
|
509
|
+
**Raises:**
|
|
510
|
+
|
|
511
|
+
- `ValueError`: If the content dictionary contains invalid keys or missing required fields.
|
|
512
|
+
"""
|
|
513
|
+
actual_content_dict = content_dict
|
|
514
|
+
|
|
515
|
+
actual_content_dict["datemodified"] = fileio.today()
|
|
516
|
+
actual_content_dict["drawnby"] = fileio.drawnby()["name"]
|
|
517
|
+
actual_content_dict["git_hash_of_harnice_src"] = (
|
|
518
|
+
fileio.get_git_hash_of_harnice_src()
|
|
519
|
+
)
|
|
520
|
+
|
|
521
|
+
if os.path.exists(rev_history_path):
|
|
522
|
+
rows = fileio.read_tsv(rev_history_path)
|
|
523
|
+
else:
|
|
524
|
+
rows = []
|
|
525
|
+
|
|
526
|
+
found = False
|
|
527
|
+
for i, row in enumerate(rows):
|
|
528
|
+
if row.get("rev") == actual_content_dict.get("rev"):
|
|
529
|
+
rows[i] = actual_content_dict
|
|
530
|
+
found = True
|
|
531
|
+
break
|
|
532
|
+
|
|
533
|
+
if not found:
|
|
534
|
+
rows.append(actual_content_dict)
|
|
535
|
+
|
|
536
|
+
if not os.path.exists(rev_history_path):
|
|
537
|
+
new(path=rev_history_path, ignore_product=True)
|
|
538
|
+
with open(rev_history_path, "w", newline="", encoding="utf-8") as f:
|
|
539
|
+
writer = csv.DictWriter(f, fieldnames=COLUMNS, delimiter="\t")
|
|
540
|
+
writer.writeheader()
|
|
541
|
+
writer.writerows(rows)
|
harnice/products/harness.py
CHANGED
|
@@ -162,7 +162,7 @@ for instance in instances:
|
|
|
162
162
|
if instance.get("item_type") == "circuit":
|
|
163
163
|
circuit_instance = instance
|
|
164
164
|
connector_at_end_a = instances_list.attribute_of(instance.get("node_at_end_a"), "connector_group")
|
|
165
|
-
new_instance_name = f"{{circuit_instance.get(
|
|
165
|
+
new_instance_name = f"{{circuit_instance.get('instance_name')}}-special_contact"
|
|
166
166
|
circuit_id = int(circuit_instance.get("circuit_id"))
|
|
167
167
|
instances_list.new_instance(
|
|
168
168
|
new_instance_name, {{
|
|
@@ -181,7 +181,7 @@ circuit_utils.squeeze_instance_between_ports_in_circuit(
|
|
|
181
181
|
# example: add a backshell
|
|
182
182
|
for instance in instances:
|
|
183
183
|
if instance.get("instance_name") in ["X1.B.conn", "PREAMP2.in2.conn"]:
|
|
184
|
-
instances_list.new_instance(f"{{instance.get(
|
|
184
|
+
instances_list.new_instance(f"{{instance.get('connector_group')}}.bs", {{
|
|
185
185
|
"bom_line_number": True,
|
|
186
186
|
"mpn": "M85049-90_9Z03",
|
|
187
187
|
"item_type": "backshell",
|
|
@@ -193,7 +193,7 @@ for instance in instances:
|
|
|
193
193
|
"lib_repo": "https://github.com/harnice/harnice"
|
|
194
194
|
}})
|
|
195
195
|
instances_list.modify(instance.get("instance_name"), {{
|
|
196
|
-
"parent_csys_instance_name": f"{{instance.get(
|
|
196
|
+
"parent_csys_instance_name": f"{{instance.get('connector_group')}}.bs",
|
|
197
197
|
"parent_csys_outputcsys_name": "connector",
|
|
198
198
|
}})
|
|
199
199
|
|
|
@@ -270,23 +270,23 @@ for x in range(2):
|
|
|
270
270
|
|
|
271
271
|
elif instance.get("item_type") in ["conductor", "conductor-segment"]:
|
|
272
272
|
instances_list.modify(instance.get("instance_name"), {{
|
|
273
|
-
"print_name": f"'{{instance.get(
|
|
273
|
+
"print_name": f"'{{instance.get('cable_identifier')}}' of '{{instances_list.attribute_of(instance.get('cable_group'), 'print_name')}}'"
|
|
274
274
|
}})
|
|
275
275
|
|
|
276
276
|
elif instance.get("item_type") == "net-channel":
|
|
277
|
-
print_name = f"'{{instance.get(
|
|
277
|
+
print_name = f"'{{instance.get('this_channel_from_device_channel_id')}}' of '{{instance.get('this_channel_from_device_refdes')}}' to '{{instance.get('this_channel_to_device_channel_id')}}' of '{{instance.get('this_channel_to_device_refdes')}}'"
|
|
278
278
|
instances_list.modify(instance.get("instance_name"), {{"print_name": print_name}})
|
|
279
279
|
|
|
280
280
|
elif instance.get("item_type") == "net-channel-segment":
|
|
281
|
-
print_name = f"'{{instances_list.attribute_of(instance.get(
|
|
281
|
+
print_name = f"'{{instances_list.attribute_of(instance.get('parent_instance'), 'this_channel_from_device_channel_id')}}' of '{{instances_list.attribute_of(instance.get('parent_instance'), 'this_channel_from_device_refdes')}}' to '{{instances_list.attribute_of(instance.get('parent_instance'), 'this_channel_to_device_channel_id')}}' of '{{instances_list.attribute_of(instance.get('parent_instance'), 'this_channel_to_device_refdes')}}'"
|
|
282
282
|
instances_list.modify(instance.get("instance_name"), {{"print_name": print_name}})
|
|
283
283
|
|
|
284
284
|
elif instance.get("item_type") == "connector":
|
|
285
|
-
print_name = f"{{instance.get(
|
|
285
|
+
print_name = f"{{instance.get('connector_group')}}"
|
|
286
286
|
instances_list.modify(instance.get("instance_name"), {{"print_name": print_name}})
|
|
287
287
|
|
|
288
288
|
elif instance.get("item_type") == "cable-segment":
|
|
289
|
-
print_name = f"{{instance.get(
|
|
289
|
+
print_name = f"{{instance.get('cable_group')}}"
|
|
290
290
|
instances_list.modify(instance.get("instance_name"), {{"print_name": print_name}})
|
|
291
291
|
|
|
292
292
|
elif instance.get("item_type") == "contact":
|
harnice/products/part.py
CHANGED
harnice/products/system.py
CHANGED
|
@@ -12,7 +12,6 @@ from harnice.lists import instances_list, manifest, channel_map, circuits_list,
|
|
|
12
12
|
#===========================================================================
|
|
13
13
|
# KICAD PROCESSING
|
|
14
14
|
#===========================================================================
|
|
15
|
-
feature_tree_utils.run_macro("kicad_sch_to_pdf", "system_artifacts", "https://github.com/harnice/harnice", artifact_id="blockdiagram-1")
|
|
16
15
|
feature_tree_utils.run_macro("kicad_pro_to_bom", "system_builder", "https://github.com/harnice/harnice", artifact_id="bom-1")
|
|
17
16
|
|
|
18
17
|
#===========================================================================
|
|
@@ -23,7 +22,12 @@ system_utils.make_instances_from_bom()
|
|
|
23
22
|
#===========================================================================
|
|
24
23
|
# CHANNEL MAPPING
|
|
25
24
|
#===========================================================================
|
|
26
|
-
feature_tree_utils.run_macro(
|
|
25
|
+
feature_tree_utils.run_macro(
|
|
26
|
+
"kicad_pro_to_system_connector_list",
|
|
27
|
+
"system_builder",
|
|
28
|
+
"https://github.com/harnice/harnice",
|
|
29
|
+
artifact_id="system-connector-list-1"
|
|
30
|
+
)
|
|
27
31
|
manifest.new()
|
|
28
32
|
channel_map.new()
|
|
29
33
|
|
|
@@ -31,7 +35,12 @@ channel_map.new()
|
|
|
31
35
|
#channel_map.map(("MIC3", "out1"), ("PREAMP1", "in2"))
|
|
32
36
|
|
|
33
37
|
#map channels to other compatible channels by sorting alphabetically then mapping compatibles
|
|
34
|
-
feature_tree_utils.run_macro(
|
|
38
|
+
feature_tree_utils.run_macro(
|
|
39
|
+
"basic_channel_mapper",
|
|
40
|
+
"system_builder",
|
|
41
|
+
"https://github.com/harnice/harnice",
|
|
42
|
+
artifact_id="channel-mapper-1"
|
|
43
|
+
)
|
|
35
44
|
|
|
36
45
|
#if mapped channels must connect via disconnects, add the list of disconnects to the channel map
|
|
37
46
|
system_utils.add_chains_to_channel_map()
|
|
@@ -43,25 +52,131 @@ disconnect_map.new()
|
|
|
43
52
|
#disconnect_map.already_assigned_disconnects_set_append(('X1', 'ch0'))
|
|
44
53
|
|
|
45
54
|
#map channels passing through disconnects to available channels inside disconnects
|
|
46
|
-
feature_tree_utils.run_macro(
|
|
47
|
-
|
|
55
|
+
feature_tree_utils.run_macro(
|
|
56
|
+
"disconnect_mapper",
|
|
57
|
+
"system_builder",
|
|
58
|
+
"https://github.com/harnice/harnice",
|
|
59
|
+
artifact_id="disconnect-mapper-1"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# ensure channels that are required to be mapped through disconnects are in fact done so
|
|
63
|
+
disconnect_map.ensure_requirements_met()
|
|
48
64
|
|
|
49
65
|
#process channel and disconnect maps to make a list of every circuit in your system
|
|
50
66
|
circuits_list.new()
|
|
51
67
|
|
|
52
|
-
|
|
68
|
+
# ===========================================================================
|
|
53
69
|
# INSTANCES LIST
|
|
54
|
-
|
|
70
|
+
# ===========================================================================
|
|
55
71
|
system_utils.make_instances_for_connectors_cavities_nodes_channels_circuits()
|
|
56
72
|
|
|
57
|
-
#assign mating connectors
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
73
|
+
# assign mating connectors
|
|
74
|
+
for instance in fileio.read_tsv("instances list"):
|
|
75
|
+
if instance.get("item_type") == "connector":
|
|
76
|
+
if instance.get("this_instance_mating_device_connector_mpn") == "XLR3M":
|
|
77
|
+
instances_list.modify(
|
|
78
|
+
instance.get("instance_name"),
|
|
79
|
+
{
|
|
80
|
+
"mpn": "D38999_26ZA98PN",
|
|
81
|
+
"lib_repo": "https://github.com/harnice/harnice",
|
|
82
|
+
},
|
|
83
|
+
)
|
|
84
|
+
elif instance.get("this_instance_mating_device_connector_mpn") == "XLR3F":
|
|
85
|
+
instances_list.modify(
|
|
86
|
+
instance.get("instance_name"),
|
|
87
|
+
{
|
|
88
|
+
"mpn": "D38999_26ZB98PN",
|
|
89
|
+
"lib_repo": "https://github.com/harnice/harnice",
|
|
90
|
+
},
|
|
91
|
+
)
|
|
92
|
+
elif instance.get("this_instance_mating_device_connector_mpn") == "DB25M":
|
|
93
|
+
instances_list.modify(
|
|
94
|
+
instance.get("instance_name"),
|
|
95
|
+
{
|
|
96
|
+
"mpn": "D38999_26ZC35PN",
|
|
97
|
+
"lib_repo": "https://github.com/harnice/harnice",
|
|
98
|
+
},
|
|
99
|
+
)
|
|
100
|
+
elif instance.get("this_instance_mating_device_connector_mpn") == "DB25F":
|
|
101
|
+
instances_list.modify(
|
|
102
|
+
instance.get("instance_name"),
|
|
103
|
+
{
|
|
104
|
+
"mpn": "D38999_26ZE6PN",
|
|
105
|
+
"lib_repo": "https://github.com/harnice/harnice",
|
|
106
|
+
},
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# ===========================================================================
|
|
110
|
+
# ASSIGN CONDUCTORS
|
|
111
|
+
# ===========================================================================
|
|
112
|
+
|
|
113
|
+
# add one conductor per circuit
|
|
114
|
+
for instance in fileio.read_tsv("instances list"):
|
|
115
|
+
if instance.get("item_type") == "circuit":
|
|
116
|
+
circuit_id = instance.get("circuit_id")
|
|
117
|
+
conductor_name = f"conductor-{circuit_id}"
|
|
118
|
+
instances_list.new_instance(
|
|
119
|
+
conductor_name,
|
|
120
|
+
{
|
|
121
|
+
"net": instance.get("net"),
|
|
122
|
+
"item_type": "conductor",
|
|
123
|
+
"location_type": "segment",
|
|
124
|
+
"channel_group": instance.get("channel_group"),
|
|
125
|
+
"node_at_end_a": circuit_utils.instance_of_circuit_port_number(
|
|
126
|
+
circuit_id, 0 # assume the only existing ports at this point are cavities at 0 and 1
|
|
127
|
+
),
|
|
128
|
+
"node_at_end_b": circuit_utils.instance_of_circuit_port_number(
|
|
129
|
+
circuit_id, 1
|
|
130
|
+
),
|
|
131
|
+
"this_channel_from_channel_type": instance.get("this_channel_from_channel_type"),
|
|
132
|
+
"this_channel_to_channel_type": instance.get("this_channel_to_channel_type"),
|
|
133
|
+
"signal_of_channel_type": instance.get("signal_of_channel_type")
|
|
134
|
+
},
|
|
135
|
+
)
|
|
136
|
+
circuit_utils.squeeze_instance_between_ports_in_circuit(
|
|
137
|
+
conductor_name, instance.get("circuit_id"), 1
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# define the cable types we want to use here
|
|
141
|
+
audio_cable = {
|
|
142
|
+
"lib_repo": "https://github.com/harnice/harnice",
|
|
143
|
+
"mpn": "8762 0602000",
|
|
144
|
+
"lib_subpath": "belden",
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
# assign conductors to cable-id
|
|
148
|
+
cable_id_counter = 1
|
|
149
|
+
instances = fileio.read_tsv("instances list")
|
|
150
|
+
for net in instances_list.list_of_uniques("net"):
|
|
151
|
+
for chgroup in instances_list.list_of_uniques("channel_group"):
|
|
152
|
+
for instance in instances:
|
|
153
|
+
cable_name = f"cable-{cable_id_counter}"
|
|
154
|
+
|
|
155
|
+
if instance.get("net") != net:
|
|
156
|
+
continue
|
|
157
|
+
if instance.get("channel_group") != chgroup:
|
|
158
|
+
continue
|
|
159
|
+
if instance.get("item_type") != "conductor":
|
|
160
|
+
continue
|
|
161
|
+
|
|
162
|
+
if chtype.parse(instance.get("this_channel_from_channel_type")) in chtype.is_or_is_compatible_with((1, 'https://github.com/harnice/harnice')):
|
|
163
|
+
if instance.get("signal_of_channel_type") in ["pos"]:
|
|
164
|
+
circuit_utils.assign_cable_conductor(
|
|
165
|
+
cable_name,
|
|
166
|
+
("pair_1", "white"),
|
|
167
|
+
instance.get("instance_name"),
|
|
168
|
+
audio_cable,
|
|
169
|
+
instance.get("net")
|
|
170
|
+
)
|
|
171
|
+
if instance.get("signal_of_channel_type") in ["neg"]:
|
|
172
|
+
circuit_utils.assign_cable_conductor(
|
|
173
|
+
cable_name,
|
|
174
|
+
("pair_1", "black"),
|
|
175
|
+
instance.get("instance_name"),
|
|
176
|
+
audio_cable,
|
|
177
|
+
instance.get("net")
|
|
178
|
+
)
|
|
179
|
+
cable_id_counter += 1 # in this system, each channel gets its own cable.
|
|
65
180
|
|
|
66
181
|
#===========================================================================
|
|
67
182
|
# SYSTEM DESIGN CHECKS
|
|
@@ -71,6 +186,63 @@ circuits_list = fileio.read_tsv("circuits list")
|
|
|
71
186
|
|
|
72
187
|
#check for circuits with no connectors
|
|
73
188
|
system_utils.find_connector_with_no_circuit(connector_list, circuits_list)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
# ===========================================================================
|
|
192
|
+
# ADD STYLING AND PRINT NAMES TO CHANNELS
|
|
193
|
+
# ===========================================================================
|
|
194
|
+
# assign styling to channels
|
|
195
|
+
audio_channel_style = {
|
|
196
|
+
"base_color": "#D59A10",
|
|
197
|
+
}
|
|
198
|
+
shield_channel_style = {
|
|
199
|
+
"base_color": "#4039A1",
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
for instance in fileio.read_tsv("instances list"):
|
|
203
|
+
if instance.get("item_type") in ["channel", "net-channel"]:
|
|
204
|
+
if instance.get("this_channel_from_channel_type") in ["(1, 'https://github.com/harnice/harnice')", "(2, 'https://github.com/harnice/harnice')"]:
|
|
205
|
+
instances_list.modify(
|
|
206
|
+
instance.get("instance_name"),
|
|
207
|
+
{
|
|
208
|
+
"appearance": audio_channel_style
|
|
209
|
+
}
|
|
210
|
+
)
|
|
211
|
+
if instance.get("this_channel_from_channel_type") == "(5, 'https://github.com/harnice/harnice')":
|
|
212
|
+
instances_list.modify(
|
|
213
|
+
instance.get("instance_name"),
|
|
214
|
+
{
|
|
215
|
+
"appearance": shield_channel_style
|
|
216
|
+
}
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
for instance in fileio.read_tsv("instances list"):
|
|
220
|
+
if instance.get("item_type") == "net-channel":
|
|
221
|
+
instances_list.modify(instance.get("instance_name"), {
|
|
222
|
+
"print_name_at_end_a": instance.get("this_net_from_device_channel_id"),
|
|
223
|
+
"print_name_at_end_b": instance.get("this_net_to_device_channel_id")
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
# ===========================================================================
|
|
227
|
+
# SYSTEM ARTIFACT GENERATORS
|
|
228
|
+
# ===========================================================================
|
|
229
|
+
|
|
230
|
+
# prepare the channel map block diagram overlay
|
|
231
|
+
chmap_instances = []
|
|
232
|
+
for instance in fileio.read_tsv("instances list"):
|
|
233
|
+
if instance.get("item_type") == "net-channel":
|
|
234
|
+
chmap_instances.append(instance)
|
|
235
|
+
feature_tree_utils.run_macro(
|
|
236
|
+
"kicad_sch_net_overlay",
|
|
237
|
+
"system_artifacts",
|
|
238
|
+
"https://github.com/harnice/harnice",
|
|
239
|
+
artifact_id="blockdiagram-chmap-1",
|
|
240
|
+
item_type="net-channel",
|
|
241
|
+
instances=chmap_instances
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
# for convenience, move any pdf to the base directory of the harness
|
|
245
|
+
feature_tree_utils.copy_pdfs_to_cwd()
|
|
74
246
|
"""
|
|
75
247
|
|
|
76
248
|
|
harnice/state.py
CHANGED
|
@@ -103,7 +103,7 @@ def lookup_outputcsys_from_lib_used(instance, outputcsys, base_directory=None):
|
|
|
103
103
|
"instance_data",
|
|
104
104
|
instance.get("item_type"),
|
|
105
105
|
instance.get("instance_name"),
|
|
106
|
-
f"{instance.get(
|
|
106
|
+
f"{instance.get('instance_name')}-attributes.json",
|
|
107
107
|
)
|
|
108
108
|
|
|
109
109
|
try:
|
harnice/utils/formboard_utils.py
CHANGED
|
@@ -502,14 +502,14 @@ def map_instance_to_segments(instance):
|
|
|
502
502
|
!= "node"
|
|
503
503
|
):
|
|
504
504
|
raise ValueError(
|
|
505
|
-
f"While mapping '{instance.get(
|
|
505
|
+
f"While mapping '{instance.get('instance_name')}' to segments, location type of {instance.get('node_at_end_a')} is not a node."
|
|
506
506
|
)
|
|
507
507
|
if (
|
|
508
508
|
instances_list.attribute_of(instance.get("node_at_end_b"), "location_type")
|
|
509
509
|
!= "node"
|
|
510
510
|
):
|
|
511
511
|
raise ValueError(
|
|
512
|
-
f"While mapping '{instance.get(
|
|
512
|
+
f"While mapping '{instance.get('instance_name')}' to segments, location type of {instance.get('node_at_end_b')} is not a node."
|
|
513
513
|
)
|
|
514
514
|
|
|
515
515
|
# Resolve the node (item_type=="node") for each end's connector group
|
|
@@ -708,7 +708,7 @@ def calculate_location(lookup_instance, instances):
|
|
|
708
708
|
rotation angle in degrees.
|
|
709
709
|
|
|
710
710
|
**Raises:**
|
|
711
|
-
|
|
711
|
+
|
|
712
712
|
- `ValueError`: If parent coordinate system information is missing or invalid, or
|
|
713
713
|
if parent instances cannot be found in the instances list.
|
|
714
714
|
"""
|
|
@@ -789,7 +789,6 @@ def calculate_location(lookup_instance, instances):
|
|
|
789
789
|
angle = 0.0
|
|
790
790
|
|
|
791
791
|
for chainlink in chain:
|
|
792
|
-
|
|
793
792
|
# ==================================================================
|
|
794
793
|
# Resolve CHILD CSYS (the transform from parent output csys)
|
|
795
794
|
# ==================================================================
|
|
@@ -816,7 +815,6 @@ def calculate_location(lookup_instance, instances):
|
|
|
816
815
|
# Child CSYS: translation component
|
|
817
816
|
# ------------------------------------------------------------------
|
|
818
817
|
if relevant_csys_child is not None:
|
|
819
|
-
|
|
820
818
|
# x/y explicit translation
|
|
821
819
|
if relevant_csys_child.get("x") not in [
|
|
822
820
|
"",
|
|
@@ -932,7 +930,7 @@ def draw_line(
|
|
|
932
930
|
# -------------------------
|
|
933
931
|
svg_parts.append(
|
|
934
932
|
f'<line x1="{fx}" y1="{fy}" x2="{tx}" y2="{ty}" '
|
|
935
|
-
f'stroke="{stroke}" stroke-width="{thickness/scale}"/>'
|
|
933
|
+
f'stroke="{stroke}" stroke-width="{thickness / scale}"/>'
|
|
936
934
|
)
|
|
937
935
|
|
|
938
936
|
# -------------------------
|
|
@@ -951,11 +949,7 @@ def draw_line(
|
|
|
951
949
|
p2x = px - ux * arrow_len - perp_x * (arrow_wid / 2)
|
|
952
950
|
p2y = py - uy * arrow_len - perp_y * (arrow_wid / 2)
|
|
953
951
|
|
|
954
|
-
return
|
|
955
|
-
f'<polygon points="'
|
|
956
|
-
f'{px},{py} {p1x},{p1y} {p2x},{p2y}" '
|
|
957
|
-
f'fill="{stroke}"/>'
|
|
958
|
-
)
|
|
952
|
+
return f'<polygon points="{px},{py} {p1x},{p1y} {p2x},{p2y}" fill="{stroke}"/>'
|
|
959
953
|
|
|
960
954
|
# -------------------------
|
|
961
955
|
# Arrow at TO end
|
harnice/utils/library_utils.py
CHANGED
|
@@ -303,7 +303,9 @@ def get_local_path(lib_repo):
|
|
|
303
303
|
os.makedirs(os.path.dirname(csv_path), exist_ok=True)
|
|
304
304
|
|
|
305
305
|
with open(csv_path, "w", encoding="utf-8") as f:
|
|
306
|
-
|
|
306
|
+
# Normalize the default path to ensure correct separators
|
|
307
|
+
normalized_path = os.path.normpath(default_local_path)
|
|
308
|
+
f.write(f"https://github.com/harnice/harnice,{normalized_path}\n")
|
|
307
309
|
|
|
308
310
|
print(f"[harnice] Created '{csv_path}'")
|
|
309
311
|
print(f"[harnice] Default library-public location: {default_local_path}")
|
|
@@ -328,6 +330,10 @@ def get_local_path(lib_repo):
|
|
|
328
330
|
if url.lower() == lib_repo.lower().strip():
|
|
329
331
|
if not local:
|
|
330
332
|
raise ValueError(f"No local path found for '{lib_repo}'")
|
|
331
|
-
|
|
333
|
+
# Expand user directory (~) and normalize path separators for current platform
|
|
334
|
+
expanded_path = os.path.expanduser(local)
|
|
335
|
+
# Normalize separators - os.path.normpath() converts forward slashes to
|
|
336
|
+
# backslashes on Windows, ensuring proper path format
|
|
337
|
+
return os.path.normpath(expanded_path)
|
|
332
338
|
|
|
333
339
|
raise ValueError(f"'{lib_repo}' not found in library locations")
|
harnice/utils/system_utils.py
CHANGED
|
@@ -258,7 +258,7 @@ def make_instances_for_connectors_cavities_nodes_channels_circuits():
|
|
|
258
258
|
{
|
|
259
259
|
"net": circuit.get("net"),
|
|
260
260
|
"item_type": "circuit",
|
|
261
|
-
"print_name": f"{circuit.get(
|
|
261
|
+
"print_name": f"{circuit.get('signal')} of {circuit.get('from_side_device_refdes')}.{circuit.get('from_side_device_chname')} <-> {circuit.get('to_side_device_refdes')}.{circuit.get('to_side_device_chname')}",
|
|
262
262
|
"channel_group": f"channel-{circuit.get('from_side_device_refdes')}.{circuit.get('from_side_device_chname')}-{circuit.get('to_side_device_refdes')}.{circuit.get('to_side_device_chname')}",
|
|
263
263
|
"circuit_id": circuit.get("circuit_id"),
|
|
264
264
|
"node_at_end_a": from_cavity,
|
|
@@ -317,7 +317,7 @@ def make_instances_for_connectors_cavities_nodes_channels_circuits():
|
|
|
317
317
|
},
|
|
318
318
|
ignore_duplicates=True,
|
|
319
319
|
)
|
|
320
|
-
|
|
320
|
+
|
|
321
321
|
# Find the chain of nets for this channel from the channel map
|
|
322
322
|
# The chain_of_nets field contains semicolon-separated net names
|
|
323
323
|
chain_of_nets = []
|
|
@@ -334,33 +334,55 @@ def make_instances_for_connectors_cavities_nodes_channels_circuits():
|
|
|
334
334
|
):
|
|
335
335
|
chain_of_nets = (channel.get("chain_of_nets") or "").split(";")
|
|
336
336
|
break
|
|
337
|
-
|
|
337
|
+
|
|
338
338
|
# Create net-channel instances for each net in the chain
|
|
339
339
|
for net in chain_of_nets:
|
|
340
|
-
if circuit.get(
|
|
340
|
+
if circuit.get("net") == net:
|
|
341
341
|
instances_list.new_instance(
|
|
342
342
|
f"{net}:channel-{circuit.get('from_side_device_refdes')}.{circuit.get('from_side_device_chname')}-"
|
|
343
343
|
f"{circuit.get('to_side_device_refdes')}.{circuit.get('to_side_device_chname')}",
|
|
344
344
|
{
|
|
345
345
|
"net": net,
|
|
346
346
|
"item_type": "net-channel",
|
|
347
|
-
"print_name": (
|
|
348
|
-
|
|
347
|
+
"print_name": (
|
|
348
|
+
f"{circuit.get('from_side_device_refdes')}.{circuit.get('from_side_device_chname')} <-> {circuit.get('to_side_device_refdes')}.{circuit.get('to_side_device_chname')}"
|
|
349
|
+
),
|
|
350
|
+
"channel_group": (
|
|
351
|
+
f"channel-{circuit.get('from_side_device_refdes')}.{circuit.get('from_side_device_chname')}-{circuit.get('to_side_device_refdes')}.{circuit.get('to_side_device_chname')}"
|
|
352
|
+
),
|
|
349
353
|
"location_type": "segment",
|
|
350
354
|
"parent_instance": f"channel-{circuit.get('from_side_device_refdes')}.{circuit.get('from_side_device_chname')}-{circuit.get('to_side_device_refdes')}.{circuit.get('to_side_device_chname')}",
|
|
351
355
|
"node_at_end_a": from_cavity,
|
|
352
356
|
"node_at_end_b": to_cavity,
|
|
353
|
-
"this_net_from_device_refdes": circuit.get(
|
|
354
|
-
"this_net_from_device_channel_id": circuit.get(
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
"
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
"
|
|
361
|
-
"
|
|
362
|
-
|
|
363
|
-
|
|
357
|
+
"this_net_from_device_refdes": circuit.get("net_from_refdes"),
|
|
358
|
+
"this_net_from_device_channel_id": circuit.get(
|
|
359
|
+
"net_from_channel_id"
|
|
360
|
+
),
|
|
361
|
+
"this_net_from_device_connector_name": circuit.get(
|
|
362
|
+
"net_from_connector_name"
|
|
363
|
+
),
|
|
364
|
+
"this_net_to_device_refdes": circuit.get("net_to_refdes"),
|
|
365
|
+
"this_net_to_device_channel_id": circuit.get(
|
|
366
|
+
"net_to_channel_id"
|
|
367
|
+
),
|
|
368
|
+
"this_net_to_device_connector_name": circuit.get(
|
|
369
|
+
"net_to_connector_name"
|
|
370
|
+
),
|
|
371
|
+
"this_channel_from_device_refdes": circuit.get(
|
|
372
|
+
"from_side_device_refdes"
|
|
373
|
+
),
|
|
374
|
+
"this_channel_from_device_channel_id": circuit.get(
|
|
375
|
+
"from_side_device_chname"
|
|
376
|
+
),
|
|
377
|
+
"this_channel_to_device_refdes": circuit.get(
|
|
378
|
+
"to_side_device_refdes"
|
|
379
|
+
),
|
|
380
|
+
"this_channel_to_device_channel_id": circuit.get(
|
|
381
|
+
"to_side_device_chname"
|
|
382
|
+
),
|
|
383
|
+
"this_channel_from_channel_type": circuit.get(
|
|
384
|
+
"from_channel_type"
|
|
385
|
+
),
|
|
364
386
|
"this_channel_to_channel_type": circuit.get("to_channel_type"),
|
|
365
387
|
},
|
|
366
388
|
ignore_duplicates=True,
|
|
@@ -474,7 +496,7 @@ def add_chains_to_channel_map():
|
|
|
474
496
|
if net not in net_to_connectors:
|
|
475
497
|
net_to_connectors[net] = []
|
|
476
498
|
net_to_connectors[net].append((i, dev, con))
|
|
477
|
-
|
|
499
|
+
|
|
478
500
|
# Build connector chain: for each net in net_chain, add net.connector_name pairs
|
|
479
501
|
# Format: {net}.{connector_name} (e.g., "WH-1.MIC3out1" where "WH-1" is the net and "MIC3out1" is the connector)
|
|
480
502
|
for net in net_chain:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: harnice
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Electrical System CAD
|
|
5
5
|
Author-email: Kenyon Shutt <harnice.io@gmail.com>
|
|
6
6
|
Project-URL: Documentation, https://harnice.io/
|
|
@@ -18,6 +18,7 @@ Requires-Dist: Pillow
|
|
|
18
18
|
Requires-Dist: PyYAML
|
|
19
19
|
Requires-Dist: xlwt
|
|
20
20
|
Requires-Dist: webcolors
|
|
21
|
+
Requires-Dist: prompt_toolkit
|
|
21
22
|
Dynamic: license-file
|
|
22
23
|
|
|
23
24
|
Harnice is a free, open source electrical system CAD tool.
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
harnice/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
harnice/__main__.py,sha256=6Ni-cHmuKYqm9WKNd5bgzHyMkpyJ80IzoUmBqldUanw,68
|
|
3
3
|
harnice/cli.py,sha256=npYeqdywC8C4dStWzGnwy25hTapUEkCTFeNwo9aDmi8,6571
|
|
4
|
-
harnice/fileio.py,sha256=
|
|
5
|
-
harnice/state.py,sha256=
|
|
4
|
+
harnice/fileio.py,sha256=DT0eqw5ATLwtIQ5KA825CvBY-kxN11KneImJH2TvSS8,11360
|
|
5
|
+
harnice/state.py,sha256=V-HK1Jgh5sfEt9m3keYFdZGtvW2cA88gM8b_4Tx05-k,1248
|
|
6
6
|
harnice/gui/launcher.py,sha256=R0T48RJkgFOJqFhgO7CvZa05zzHOu_q5tgV9U1iwreY,12993
|
|
7
7
|
harnice/lists/channel_map.py,sha256=Q3cuBcYTQg7H25SFx8wb2Q0o4zNNCCS8WRBMPrwyGxg,6112
|
|
8
8
|
harnice/lists/circuits_list.py,sha256=kqbnZk4rCLTFipX6PVirYPCpaGkTkM7SOnG0OdCEys4,11897
|
|
9
9
|
harnice/lists/disconnect_map.py,sha256=43qY0osDzh6XtL8GuSWOUBr86R7YUNPOOqGjsP8kSfk,9332
|
|
10
10
|
harnice/lists/formboard_graph.py,sha256=M0KYuQobI7vyNl6X6mHopmP_zEfFrO4YHAgR09ldiMA,1727
|
|
11
|
-
harnice/lists/instances_list.py,sha256=
|
|
11
|
+
harnice/lists/instances_list.py,sha256=g28vHzarDpsRu3aPEPaX-gsRZ_knJW9fgWAn174qr5M,14158
|
|
12
12
|
harnice/lists/library_history.py,sha256=DhuRJWs1ziyfnhzEsmn3kVD5iaRELQ8OQ9UmDvmFjPU,1576
|
|
13
13
|
harnice/lists/manifest.py,sha256=nCwibNehQ1VYDN9_Q7vqM8DfssQXO4IQ1n39AW9Bp8Y,3039
|
|
14
14
|
harnice/lists/post_harness_instances_list.py,sha256=dxTwCLSRHX0J4YdEUx8GNMy9-UL8DK__4llGKTRrWSY,2490
|
|
15
|
-
harnice/lists/rev_history.py,sha256=
|
|
15
|
+
harnice/lists/rev_history.py,sha256=GyxirEfFwmjSLXguo4g9yzK_atnBK7L4nwH5dyygklk,18525
|
|
16
16
|
harnice/lists/signals_list.py,sha256=qqRAtxYsJeSVL0XDESFbBDrdcxhWKAq6oZAqPVVrSOE,6972
|
|
17
17
|
harnice/products/__init__.py,sha256=LlsPf7uk1kyBdmaBuLhlGwcQau3uzEUdCW_eqtTs7TQ,29
|
|
18
18
|
harnice/products/cable.py,sha256=I05__-WxJUR-teV-oYB1Qzi5ufWbbYCOfM8oqCSXWPs,5107
|
|
@@ -20,22 +20,22 @@ harnice/products/chtype.py,sha256=jGmjRAb4LuPfH6IV84avpfJMI-IUfPEqJ5lhpJ1Wg2o,21
|
|
|
20
20
|
harnice/products/device.py,sha256=MUZgbBKzkBh1kKIQlAVXr6ywIaiXn-Sg-Ib5czjIf3I,28096
|
|
21
21
|
harnice/products/disconnect.py,sha256=vahbAWB_hM5k5BGWORbNGygw35R8RNyfZGpep3n_ks0,7042
|
|
22
22
|
harnice/products/flagnote.py,sha256=yUMLFPIs9eLVWllpVl1ZHNh8qL5pk2P_Fj-IDgD4jHM,4434
|
|
23
|
-
harnice/products/harness.py,sha256=
|
|
23
|
+
harnice/products/harness.py,sha256=PWu9oTauQQYLt_ZbZXOnU98OxNshUzurG1bC31yTRXg,21155
|
|
24
24
|
harnice/products/macro.py,sha256=c8HpcCcH1qMQkc1-H0QT560ghvvDN16y0_3_Bl6ByRU,135
|
|
25
|
-
harnice/products/part.py,sha256=
|
|
26
|
-
harnice/products/system.py,sha256=
|
|
25
|
+
harnice/products/part.py,sha256=FjYTTkzb23yYh-YzStN2nAxrzbSQuzhittiSPz8Uso4,22501
|
|
26
|
+
harnice/products/system.py,sha256=sqsZRqlYtp9yPeyps0SeXgPg0eFLBnNw6ZbyDeNILaE,12278
|
|
27
27
|
harnice/products/tblock.py,sha256=y4Won5hXoHxHFuSA6mMltpiyX46kpjTmbdJIH5J_UIw,8435
|
|
28
28
|
harnice/utils/appearance.py,sha256=ps_tMLdbURL-FCIv0Z3X19bTRFVRIuP0J5N9zvugUbs,1551
|
|
29
29
|
harnice/utils/circuit_utils.py,sha256=k5CfwYaL2flvilE5r_pZPy_puY9nyC093DO-H3s0VYU,13085
|
|
30
|
-
harnice/utils/feature_tree_utils.py,sha256
|
|
31
|
-
harnice/utils/formboard_utils.py,sha256=
|
|
32
|
-
harnice/utils/library_utils.py,sha256=
|
|
30
|
+
harnice/utils/feature_tree_utils.py,sha256=dLcEUdvrA9p_EGWIqqBfRb5ZuxuXNp9OZcLh2W4N300,6973
|
|
31
|
+
harnice/utils/formboard_utils.py,sha256=f7SXn8uFMysv5_Gq-84G-jOauSrbMeL8PBPquzkq8Sw,36861
|
|
32
|
+
harnice/utils/library_utils.py,sha256=Qp6UEtmcgQvK8Jxxt88GngQX_OBvhxumV_hCqcfwpFQ,13015
|
|
33
33
|
harnice/utils/note_utils.py,sha256=flcWmaRNz03t-Xo6bbp8erF9FuuUuAG5hcW7ephx02M,15676
|
|
34
34
|
harnice/utils/svg_utils.py,sha256=HZrEdTS1dHyrHxaYM35Va644bdrkwao5OkJPG_RPX2Y,33490
|
|
35
|
-
harnice/utils/system_utils.py,sha256=
|
|
36
|
-
harnice-0.3.
|
|
37
|
-
harnice-0.3.
|
|
38
|
-
harnice-0.3.
|
|
39
|
-
harnice-0.3.
|
|
40
|
-
harnice-0.3.
|
|
41
|
-
harnice-0.3.
|
|
35
|
+
harnice/utils/system_utils.py,sha256=nFIEaf_5rt0kVETvB7DX-RVCNLH7Q9ZAhPBtFpio__Q,25651
|
|
36
|
+
harnice-0.3.2.dist-info/licenses/LICENSE,sha256=2bm9uFabQZ3Ykb_SaSU_uUbAj2-htc6WJQmS_65qD00,1073
|
|
37
|
+
harnice-0.3.2.dist-info/METADATA,sha256=s32OUCTTvnt-fwrwMaIy07SRgUiv6i6FxFzYD6ji8EE,1072
|
|
38
|
+
harnice-0.3.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
39
|
+
harnice-0.3.2.dist-info/entry_points.txt,sha256=AsINav6n93Jl0X-cF2nEQT2Mqj0FI-BJu2ieyUqddnE,85
|
|
40
|
+
harnice-0.3.2.dist-info/top_level.txt,sha256=HF3Q2YiwIW9HIDhXjucyrtGy7dubDEcJUMpb5Ajb5pM,8
|
|
41
|
+
harnice-0.3.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|