"""Owner-assigned drive slots, independent of USB addresses and motor commands.""" import json LAYOUTS = {"1x1": ("left.1", "right.1"), "2x2": ("left.1", "left.2", "right.1", "right.2")} def slot_label(layout, slot): side = "Левый" if slot.startswith("left.") else "Правый" return side + ((" передний" if slot.endswith(".1") else " задний") if layout == "2x2" else "") class DriveProfile: def __init__(self, root, atomic): self.path, self.atomic = root / "drive-profile.json", atomic self.value = json.loads(self.path.read_text()) if self.path.exists() else {"layout": None, "revision": 0, "bindings": {}} def update(self, action, params, device): current = self.value if params["revision"] != current["revision"]: raise ValueError("Профиль привода изменился. Обновите карточку.") bindings = dict(current["bindings"]) layout = current["layout"] if action == "vesc.drive.layout": layout = params["layout"] if set(bindings) - set(LAYOUTS[layout]): raise ValueError("Сначала снимите назначения задних моторов.") elif action == "vesc.drive.assign": layout, slot = params["layout"], params["slot"] if set(bindings) - set(LAYOUTS[layout]): raise ValueError("Сначала снимите назначения задних моторов.") if slot and slot in bindings and bindings[slot]["device_id"] != device.id: raise ValueError("Это место уже занято. Сначала снимите прежнее назначение.") bindings = {key: value for key, value in bindings.items() if value["device_id"] != device.id} if slot: bindings[slot] = {"device_id": device.id, "uuid": device.identity["uuid"]} else: bindings.pop(params["slot"], None) value = {"layout": layout, "revision": current["revision"] + 1, "bindings": bindings} self.atomic(self.path, value) self.value = value return value def validate(action, params): keys = {"revision", "layout"} if action == "vesc.drive.layout" else ({"revision", "layout", "slot"} if action == "vesc.drive.assign" else {"revision", "slot"}) if set(params) != keys or type(params["revision"]) is not int or params["revision"] < 0: raise ValueError("Invalid drive profile revision") if action == "vesc.drive.layout": if params["layout"] not in LAYOUTS: raise ValueError("Invalid drive layout") elif action == "vesc.drive.assign": if params["layout"] not in LAYOUTS or params["slot"] not in ("", *LAYOUTS[params["layout"]]): raise ValueError("Invalid drive layout or slot") elif params["slot"] not in LAYOUTS["2x2"]: raise ValueError("Invalid drive slot")