Duplicate components from the sheet and tree popups, with links between them
Ctrl+click picks out several boxes on the sheet, and Ctrl/Shift+click several rows in the tree. Copies are named on from their original (Add1 -> Add2), sit one step right and down, and keep the links that ran between the copied components. One undo step; on a device the originals are read back in full and the copies made there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -403,6 +403,80 @@ def recreate_subtree(parent_element, snapshot, build_config):
|
||||
return True, complete
|
||||
|
||||
|
||||
def duplicate_subtrees(plans, links, build_config):
|
||||
"""Adds copies of read_subtree trees to the device, then the links among them.
|
||||
|
||||
`plans` is a list of (parent_element, node, new_name, new_meta): the node is a
|
||||
read_subtree tree, copied under `parent_element` with its top renamed and moved to
|
||||
`new_meta` (None keeps it where it was). Children keep their names, which stay
|
||||
unique because their parent is new. `links` holds (from_id, from_slot, to_id,
|
||||
to_slot) between components in the trees, by their original ids.
|
||||
|
||||
Nothing in a node is changed; the device hands out new ids for the copies.
|
||||
Returns (created, new_ids, complete): `created` lists (parent_element, new_top_id)
|
||||
for every top the device took, `new_ids` maps each original id to its copy's, and
|
||||
when not complete, last_error says what is missing.
|
||||
"""
|
||||
ds = _session()
|
||||
if ds is None:
|
||||
return [], {}, False
|
||||
client = ds.sox_client
|
||||
new_ids = {}
|
||||
created = []
|
||||
|
||||
def create(node, under_id, name, meta):
|
||||
values = dict(node["values"])
|
||||
if meta is not None:
|
||||
values["meta"] = meta
|
||||
config = build_config(node["type_name"], device_values=values)
|
||||
comp = client.add(under_id, node["kit_id"], node["type_id"], name, config)
|
||||
new_ids[node["id"]] = comp.id
|
||||
for child in node["children"]:
|
||||
create(child, comp.id, child["name"], None)
|
||||
|
||||
complete = True
|
||||
for parent_element, node, new_name, new_meta in plans:
|
||||
parent_id = _comp_id(parent_element)
|
||||
if parent_id is None:
|
||||
_fail(f"The parent '{parent_element.get('name', '?')}' has no component id.")
|
||||
complete = False
|
||||
break
|
||||
try:
|
||||
create(node, parent_id, new_name, new_meta)
|
||||
except Exception as e:
|
||||
if node["id"] in new_ids:
|
||||
created.append((parent_element, new_ids[node["id"]]))
|
||||
_fail(f"'{new_name}' was added to the device, but not all of it: {e}")
|
||||
else:
|
||||
_fail(f"The device rejected the add of '{new_name}': {e}")
|
||||
complete = False
|
||||
break
|
||||
created.append((parent_element, new_ids[node["id"]]))
|
||||
|
||||
if complete:
|
||||
for from_id, from_slot, to_id, to_slot in links:
|
||||
try:
|
||||
client.link(new_ids[from_id], from_slot, new_ids[to_id], to_slot)
|
||||
except Exception as e:
|
||||
_fail(f"The copies are on the device, but not every link between them: {e}")
|
||||
complete = False
|
||||
break
|
||||
|
||||
top_ids = {top_id for _, top_id in created}
|
||||
for top_id in top_ids:
|
||||
ds.after_add(top_id)
|
||||
# after_add reads only the top; the children's values are read too, so the DOM
|
||||
# built from the cache carries their slots.
|
||||
children = [client.cache_get(i) for i in new_ids.values() if i not in top_ids]
|
||||
children = [c for c in children if c is not None]
|
||||
if children:
|
||||
try:
|
||||
client.update(children, CONFIG)
|
||||
except Exception:
|
||||
pass
|
||||
return created, new_ids, complete
|
||||
|
||||
|
||||
def wire_value(type_id, raw):
|
||||
"""A value as the device sent it, in the form encode_value wants for `type_id`."""
|
||||
if type_id == BUF_ID:
|
||||
|
||||
Reference in New Issue
Block a user