harnice 0.3.0__py3-none-any.whl → 0.3.1__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 CHANGED
@@ -209,12 +209,13 @@ def verify_revision_structure():
209
209
  f"Harnice only renders revisions with a blank status."
210
210
  )
211
211
 
212
- print(f"Working on PN: {state.pn}, Rev: {state.rev}")
212
+ print(f"Rendering PN: {state.pn}, Rev: {state.rev}")
213
213
  rev_history.update_datemodified()
214
214
 
215
215
 
216
216
  def today():
217
- return datetime.date.today().strftime("%-m/%-d/%y")
217
+ d = datetime.date.today()
218
+ return f"{d.month}/{d.day}/{d.year % 100}"
218
219
 
219
220
 
220
221
  def get_git_hash_of_harnice_src():
@@ -5,23 +5,25 @@ from threading import Lock
5
5
  from harnice import fileio, state
6
6
 
7
7
  COLUMNS = [
8
- "net", #documentation needed
9
- "instance_name", #documentation needed
10
- "print_name", #documentation needed
11
- "bom_line_number", #documentation needed
12
- "mfg", #documentation needed
13
- "mpn", # unique part identifier (manufacturer + part number concatenated)
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", #documentation needed
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", #documentation needed
33
- "cable_container", #documentation needed
34
- "cable_identifier", #documentation needed
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", #documentation needed
44
- "lib_subpath", #documentation needed
45
- "lib_desc", #documentation needed
46
- "lib_latest_rev", #documentation needed
47
- "lib_rev_used_here", #documentation needed
48
- "lib_status", #documentation needed
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", #documentation needed
51
- "lib_datemodified", #documentation needed
52
- "lib_datereleased", #documentation needed
53
- "lib_drawnby", #documentation needed
54
- "lib_checkedby", #documentation needed
55
- "project_editable_lib_modified", #documentation needed
56
- "lib_build_notes", #documentation needed
57
- "lib_tools", #documentation needed
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", #documentation needed
62
- "this_net_from_device_channel_id", #documentation needed
63
- "this_net_from_device_connector_name", #documentation needed
64
- "this_net_to_device_refdes", #documentation needed
65
- "this_net_to_device_channel_id", #documentation needed
66
- "this_net_to_device_connector_name", #documentation needed
67
- "this_channel_from_device_refdes", # if channel, refdes of the device on one side of the channel
68
- "this_channel_from_device_channel_id", #documentation needed
69
- "this_channel_to_device_refdes", # if channel, refdes of the device on the other side of the channel
70
- "this_channel_to_device_channel_id", #documentation needed
71
- "this_channel_from_channel_type", #documentation needed
72
- "this_channel_to_channel_type", #documentation needed
73
- "signal_of_channel_type", #documentation needed
74
- "debug", #documentation needed
75
- "debug_cutoff", #documentation needed
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
 
@@ -6,27 +6,59 @@ from harnice import fileio, state
6
6
 
7
7
  # === Global Columns Definition ===
8
8
  COLUMNS = [
9
- "product", #documentation needed
10
- "mfg", #documentation needed
11
- "pn", #documentation needed
12
- "desc", #documentation needed
13
- "rev", #documentation needed
14
- "status", #documentation needed
15
- "releaseticket", #documentation needed
16
- "library_repo", #documentation needed
17
- "library_subpath", #documentation needed
18
- "datestarted", #documentation needed
19
- "datemodified", #documentation needed
20
- "datereleased", #documentation needed
21
- "git_hash_of_harnice_src", #documentation needed
22
- "drawnby", #documentation needed
23
- "checkedby", #documentation needed
24
- "revisionupdates", #documentation needed
25
- "affectedinstances", #documentation needed
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
- global product
190
- product = select_product_type()
191
- with open(fileio.path("revision history"), "w", newline="", encoding="utf-8") as f:
192
- writer = csv.DictWriter(f, fieldnames=columns, delimiter="\t")
193
- writer.writeheader()
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
- from harnice import cli
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
- global product
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
- product_name = candidate
356
+ state.set_product(candidate)
210
357
  break
211
- if not product_name:
212
- product_name = globals().get("product")
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 product_name:
362
+ if state.product:
219
363
  try:
220
- product_module = importlib.import_module(f"harnice.products.{product_name}")
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 {product_name}",
401
+ f"Enter a description of this {state.product}",
256
402
  default=default_desc,
257
403
  )
258
404
 
@@ -305,7 +451,7 @@ def append(next_rev=None):
305
451
 
306
452
  rows.append(
307
453
  {
308
- "product": product_name,
454
+ "product": state.product,
309
455
  "pn": state.pn,
310
456
  "rev": next_rev,
311
457
  "desc": desc,
@@ -323,3 +469,67 @@ def append(next_rev=None):
323
469
  writer = csv.DictWriter(f, fieldnames=columns, delimiter="\t")
324
470
  writer.writeheader()
325
471
  writer.writerows(rows)
472
+
473
+
474
+ def part_family_append(content_dict, rev_history_path):
475
+ """
476
+ Append a new revision history entry to the part family revision history file.
477
+
478
+ Intended to be called by part family scripts only.
479
+
480
+ The function will automatically update the following fields in the content dictionary:
481
+
482
+ - `datemodified`: Set to today's date
483
+ - `drawnby`: Set to the current user's name
484
+ - `git_hash_of_harnice_src`: Set to the current git hash of the harnice source code
485
+
486
+ If the revision history file does not exist, the function will create it.
487
+ If an entry with the same revision number already exists, the function will update that entry.
488
+ Otherwise, the function will append a new entry to the file.
489
+
490
+ **Arguments:**
491
+
492
+ - `content_dict` (dict): The content to append to the part family revision history file.
493
+ - Should contain keys matching the `COLUMNS` list.
494
+ - The `rev` key is used to determine if an entry already exists.
495
+ - `rev_history_path` (str): The path to the part family revision history file.
496
+
497
+ **Returns:**
498
+
499
+ - `None`
500
+
501
+ **Raises:**
502
+
503
+ - `ValueError`: If the content dictionary contains invalid keys or missing required fields.
504
+ """
505
+ actual_content_dict = content_dict
506
+
507
+ actual_content_dict["datemodified"] = fileio.today()
508
+ actual_content_dict["drawnby"] = fileio.drawnby()["name"]
509
+ actual_content_dict["git_hash_of_harnice_src"] = (
510
+ fileio.get_git_hash_of_harnice_src()
511
+ )
512
+
513
+ rev = content_dict.get("rev")
514
+
515
+ if os.path.exists(rev_history_path):
516
+ rows = fileio.read_tsv(rev_history_path)
517
+ else:
518
+ rows = []
519
+
520
+ found = False
521
+ for i, row in enumerate(rows):
522
+ if row.get("rev") == actual_content_dict.get("rev"):
523
+ rows[i] = actual_content_dict
524
+ found = True
525
+ break
526
+
527
+ if not found:
528
+ rows.append(actual_content_dict)
529
+
530
+ if not os.path.exists(rev_history_path):
531
+ new(path=rev_history_path, ignore_product=True)
532
+ with open(rev_history_path, "w", newline="", encoding="utf-8") as f:
533
+ writer = csv.DictWriter(f, fieldnames=COLUMNS, delimiter="\t")
534
+ writer.writeheader()
535
+ writer.writerows(rows)
@@ -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("instance_name")}}-special_contact"
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("connector_group")}}.bs", {{
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("connector_group")}}.bs",
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("cable_identifier")}}' of '{{instances_list.attribute_of(instance.get("cable_group"), "print_name")}}'"
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("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")}}'"
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("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")}}'"
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("connector_group")}}"
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("cable_group")}}"
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
@@ -635,6 +635,4 @@ def render():
635
635
  png_path = fileio.path("drawing png")
636
636
  img.save(png_path, dpi=(1000, 1000))
637
637
 
638
- print()
639
- print(f"Part file '{state.partnumber('pn')}' updated")
640
- print()
638
+ print(f"Part file '{state.partnumber('pn')}' updated\n")
@@ -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("kicad_pro_to_system_connector_list", "system_builder", "https://github.com/harnice/harnice", artifact_id="system-connector-list-1")
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("basic_channel_mapper", "system_builder", "https://github.com/harnice/harnice", artifact_id="channel-mapper-1")
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("disconnect_mapper", "system_builder", "https://github.com/harnice/harnice", artifact_id="disconnect-mapper-1")
47
- feature_tree_utils.ensure_requirements_met()
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
- #for instance in fileio.read_tsv("instances list"):
59
- #if instance.get("item_type") == "connector":
60
- #if instance.get("this_instance_mating_device_connector_mpn") == "XLR3M":
61
- #instances_list.modify(instance.get("instance_name"),{
62
- #"mpn":"D38999_26ZA98PN",
63
- #"lib_repo":"https://github.com/harnice/harnice"
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
@@ -11,6 +11,10 @@ def set_rev(x):
11
11
  global rev
12
12
  rev = x
13
13
 
14
+ def set_product(x):
15
+ global product
16
+ product = x
17
+
14
18
 
15
19
  def set_net(x):
16
20
  global net
@@ -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("instance_name")}-attributes.json",
106
+ f"{instance.get('instance_name')}-attributes.json",
107
107
  )
108
108
 
109
109
  try:
@@ -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("instance_name")}' to segments, location type of {instance.get('node_at_end_a')} is not a node."
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("instance_name")}' to segments, location type of {instance.get('node_at_end_b')} is not a node."
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
@@ -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("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')}",
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('net') == net:
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": (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')}"),
348
- "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')}"),
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('net_from_refdes'),
354
- "this_net_from_device_channel_id": circuit.get('net_from_channel_id'),
355
- "this_net_from_device_connector_name": circuit.get('net_from_connector_name'),
356
- "this_net_to_device_refdes": circuit.get('net_to_refdes'),
357
- "this_net_to_device_channel_id": circuit.get('net_to_channel_id'),
358
- "this_net_to_device_connector_name": circuit.get('net_to_connector_name'),
359
- "this_channel_from_device_refdes": circuit.get("from_side_device_refdes"),
360
- "this_channel_from_device_channel_id": circuit.get("from_side_device_chname"),
361
- "this_channel_to_device_refdes": circuit.get("to_side_device_refdes"),
362
- "this_channel_to_device_channel_id": circuit.get("to_side_device_chname"),
363
- "this_channel_from_channel_type": circuit.get("from_channel_type"),
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.0
3
+ Version: 0.3.1
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=HRPKH1V08vbhB0uihxZ_pehtngy354biklYWU109dW0,9491
5
- harnice/state.py,sha256=QyGseSu1wSpAn1K3tljA7g_SH4BPUmC9aAKuJ2E9pxw,1192
4
+ harnice/fileio.py,sha256=MiBPo5Z6DfEDkuFs9purekJHxNW3w1spjs_AZONAzCc,9511
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=Cz9RdnkFOisguOTR8p8lcI_periH0SaafULmIA2VfuU,11386
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=cwJ8wNPdnSFaCVSyTijlcFig_pP0wkzOVxw4bbv2W7c,10641
15
+ harnice/lists/rev_history.py,sha256=efIK8V3gAqk8gK3f2MnH1zI744JiNPub6-1E0PUM2to,18058
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=Q40t4xOGhp4AQVrJtvLdTEIvdhQggIhcra6uIYFQgvY,21155
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=DU1FmpGMW_2INSb0suQxKZ0DhT69mCRtzqqNmPBKWCg,22523
26
- harnice/products/system.py,sha256=mdbWdxg50-ODUbMTGLbvSh9Z_J2UoG5btyyawGNqQNA,5872
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=-2fwliPQmuxlSkb_ppZH-f2IXtA89uaiZf63KcusOGs,6973
31
- harnice/utils/formboard_utils.py,sha256=OALL9fHdG_C78j423HtWhjcw24G9MaAVLHthqrqDWKY,36921
30
+ harnice/utils/feature_tree_utils.py,sha256=dLcEUdvrA9p_EGWIqqBfRb5ZuxuXNp9OZcLh2W4N300,6973
31
+ harnice/utils/formboard_utils.py,sha256=f7SXn8uFMysv5_Gq-84G-jOauSrbMeL8PBPquzkq8Sw,36861
32
32
  harnice/utils/library_utils.py,sha256=KgBHisCuB3IOuhCSwXUoLNalI8Mz_8vFS_jI4j-1d0M,12564
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=SeQrCi1cfIgAeXdKrrIBcNvHG0G6MIj4kka1VBp7M8o,25081
36
- harnice-0.3.0.dist-info/licenses/LICENSE,sha256=2bm9uFabQZ3Ykb_SaSU_uUbAj2-htc6WJQmS_65qD00,1073
37
- harnice-0.3.0.dist-info/METADATA,sha256=3N8VOIvkxuUUClhjKxGv62Sr-AnTPFdrodGSGrCdGqQ,1042
38
- harnice-0.3.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
39
- harnice-0.3.0.dist-info/entry_points.txt,sha256=AsINav6n93Jl0X-cF2nEQT2Mqj0FI-BJu2ieyUqddnE,85
40
- harnice-0.3.0.dist-info/top_level.txt,sha256=HF3Q2YiwIW9HIDhXjucyrtGy7dubDEcJUMpb5Ajb5pM,8
41
- harnice-0.3.0.dist-info/RECORD,,
35
+ harnice/utils/system_utils.py,sha256=nFIEaf_5rt0kVETvB7DX-RVCNLH7Q9ZAhPBtFpio__Q,25651
36
+ harnice-0.3.1.dist-info/licenses/LICENSE,sha256=2bm9uFabQZ3Ykb_SaSU_uUbAj2-htc6WJQmS_65qD00,1073
37
+ harnice-0.3.1.dist-info/METADATA,sha256=lNIUXpgv-pLrrzm-bVi78XusXrYAzDsl5_ptKbZUej8,1072
38
+ harnice-0.3.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
39
+ harnice-0.3.1.dist-info/entry_points.txt,sha256=AsINav6n93Jl0X-cF2nEQT2Mqj0FI-BJu2ieyUqddnE,85
40
+ harnice-0.3.1.dist-info/top_level.txt,sha256=HF3Q2YiwIW9HIDhXjucyrtGy7dubDEcJUMpb5Ajb5pM,8
41
+ harnice-0.3.1.dist-info/RECORD,,