initialize
This commit is contained in:
98
LVX6048/bin/lvx-resolve-links
Executable file
98
LVX6048/bin/lvx-resolve-links
Executable file
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env python3
|
||||
"""lvx-resolve-links — create /dev/lvx6048-N symlinks keyed to PI18 serial.
|
||||
|
||||
Globs /dev/hidraw*, sends PI18 `ID` to each, and creates
|
||||
/dev/lvx6048-1 -> /dev/hidrawX where X's serial matches SERIAL_UNIT_1
|
||||
/dev/lvx6048-2 -> /dev/hidrawX where X's serial matches SERIAL_UNIT_2
|
||||
|
||||
Must run as root. Intended as a systemd oneshot before powermon*.service.
|
||||
|
||||
Runs a single discovery pass with exclusive access — unlike powermon's native
|
||||
resolve_path which each service performs independently at startup, causing
|
||||
collisions when a sibling service is already holding the target hidraw.
|
||||
|
||||
Edit SERIAL_UNIT_1 / SERIAL_UNIT_2 when a unit is replaced.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import glob
|
||||
import os
|
||||
import sys
|
||||
|
||||
SERIAL_UNIT_1 = "1496142109100037000000"
|
||||
SERIAL_UNIT_2 = "1496142408100255000000"
|
||||
|
||||
LINK_FOR_SERIAL = {
|
||||
SERIAL_UNIT_1: "/dev/lvx6048-1",
|
||||
SERIAL_UNIT_2: "/dev/lvx6048-2",
|
||||
}
|
||||
|
||||
sys.path.insert(0, "/home/noise/.local/share/uv/tools/powermon/lib/python3.11/site-packages")
|
||||
from powermon.protocols import get_protocol_definition # noqa: E402
|
||||
from powermon.ports.usbport import USBPort # noqa: E402
|
||||
|
||||
|
||||
async def probe_serial(path: str) -> str | None:
|
||||
proto = get_protocol_definition(protocol="PI18")
|
||||
port = USBPort(path=path, protocol=proto)
|
||||
port.path = path
|
||||
try:
|
||||
await port.connect()
|
||||
if not port.is_connected():
|
||||
return None
|
||||
cmd = proto.get_id_command()
|
||||
res = await port.send_and_receive(command=cmd)
|
||||
await port.disconnect()
|
||||
except Exception as e:
|
||||
print(f" probe {path}: {e.__class__.__name__}: {e}", file=sys.stderr)
|
||||
return None
|
||||
if res is None or not getattr(res, "is_valid", False) or not res.readings:
|
||||
return None
|
||||
return str(res.readings[0].data_value)
|
||||
|
||||
|
||||
def _relink(link: str, target: str) -> None:
|
||||
target_basename = os.path.basename(target)
|
||||
try:
|
||||
if os.path.islink(link) or os.path.exists(link):
|
||||
os.unlink(link)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
os.symlink(target_basename, link)
|
||||
|
||||
|
||||
async def main() -> int:
|
||||
candidates = sorted(glob.glob("/dev/hidraw*"))
|
||||
if not candidates:
|
||||
print("no /dev/hidraw* devices present", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
sn_to_path: dict[str, str] = {}
|
||||
for p in candidates:
|
||||
sn = await probe_serial(p)
|
||||
if sn:
|
||||
print(f"{p}: serial {sn}")
|
||||
sn_to_path[sn] = p
|
||||
else:
|
||||
print(f"{p}: no PI18 response (probably not an LVX6048)")
|
||||
|
||||
missing = []
|
||||
for sn, link in LINK_FOR_SERIAL.items():
|
||||
if sn in sn_to_path:
|
||||
_relink(link, sn_to_path[sn])
|
||||
print(f"symlink {link} -> {os.path.basename(sn_to_path[sn])}")
|
||||
else:
|
||||
missing.append((link, sn))
|
||||
try:
|
||||
if os.path.islink(link):
|
||||
os.unlink(link)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
print(f"WARNING: {link} serial {sn} not found on any /dev/hidraw*")
|
||||
|
||||
return 0 if not missing else 2
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(asyncio.run(main()))
|
||||
Reference in New Issue
Block a user