48 lines
2.5 KiB
Python
48 lines
2.5 KiB
Python
"""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.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", "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.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")
|