Ship Windows plug-and-play host exe with USB wait and autostart.
Users no longer need Python: run.bat or install_autostart.ps1; agent reconnects when CYD is plugged in.
This commit is contained in:
@@ -354,32 +354,76 @@ def build_line() -> str:
|
||||
)
|
||||
|
||||
|
||||
def find_ports() -> List[str]:
|
||||
ports = [p.device for p in list_ports.comports()]
|
||||
if ports:
|
||||
return sorted(ports)
|
||||
# Extra Linux globs
|
||||
_USB_HINT = re.compile(
|
||||
r"CH340|CP210|CP2102|CP210x|SILABS|Silicon Labs|USB[- ]?SERIAL|"
|
||||
r"USB[- ]?UART|FT232|FTDI|wchusb|QinHeng|ESP32|Espressif|1A86|10C4",
|
||||
re.I,
|
||||
)
|
||||
|
||||
|
||||
def list_port_infos():
|
||||
infos = list(list_ports.comports())
|
||||
if infos:
|
||||
return infos
|
||||
# Extra Linux globs when pyserial enumeration is empty
|
||||
out = []
|
||||
if sys.platform.startswith("linux"):
|
||||
ports = sorted(
|
||||
for path in sorted(
|
||||
glob.glob("/dev/ttyUSB*")
|
||||
+ glob.glob("/dev/ttyACM*")
|
||||
+ glob.glob("/dev/tty.usb*")
|
||||
)
|
||||
return ports
|
||||
):
|
||||
out.append(type("P", (), {"device": path, "description": path, "hwid": ""})())
|
||||
return out
|
||||
|
||||
|
||||
def pick_port(explicit: str) -> str:
|
||||
def find_ports() -> List[str]:
|
||||
return sorted({p.device for p in list_port_infos()})
|
||||
|
||||
|
||||
def score_port(p) -> int:
|
||||
blob = f"{getattr(p, 'description', '')} {getattr(p, 'hwid', '')} {p.device}"
|
||||
score = 0
|
||||
if _USB_HINT.search(blob):
|
||||
score += 100
|
||||
if sys.platform.startswith("linux") and (
|
||||
"ttyUSB" in p.device or "ttyACM" in p.device
|
||||
):
|
||||
score += 50
|
||||
if sys.platform == "darwin" and "usb" in p.device.lower():
|
||||
score += 50
|
||||
return score
|
||||
|
||||
|
||||
def pick_port(explicit: str, wait: bool = True) -> str:
|
||||
if explicit:
|
||||
return explicit
|
||||
ports = find_ports()
|
||||
if not ports:
|
||||
raise SystemExit("No serial ports found. Plug in the CYD USB cable.")
|
||||
if len(ports) == 1:
|
||||
return ports[0]
|
||||
print("Available ports:", ", ".join(ports))
|
||||
pick = ports[-1]
|
||||
print(f"Using last port: {pick} (override with --port)")
|
||||
return pick
|
||||
while True:
|
||||
if any(p.device == explicit for p in list_port_infos()) or os.path.exists(
|
||||
explicit
|
||||
):
|
||||
return explicit
|
||||
if not wait:
|
||||
raise SystemExit(f"Port not found: {explicit}")
|
||||
print(f"Waiting for {explicit} ...", flush=True)
|
||||
time.sleep(1.0)
|
||||
|
||||
while True:
|
||||
infos = list_port_infos()
|
||||
if infos:
|
||||
ranked = sorted(infos, key=lambda p: (score_port(p), p.device))
|
||||
best = ranked[-1]
|
||||
if len(infos) > 1:
|
||||
print(
|
||||
"Available ports:",
|
||||
", ".join(f"{p.device}({getattr(p, 'description', '')})" for p in infos),
|
||||
flush=True,
|
||||
)
|
||||
print(f"Using port: {best.device}", flush=True)
|
||||
return best.device
|
||||
if not wait:
|
||||
raise SystemExit("No serial ports found. Plug in the CYD USB cable.")
|
||||
print("Waiting for CYD USB serial ...", flush=True)
|
||||
time.sleep(1.0)
|
||||
|
||||
|
||||
def open_serial(port: str, baud: int):
|
||||
@@ -395,24 +439,8 @@ def open_serial(port: str, baud: int):
|
||||
return ser
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser(description="Push host sysinfo to CYD over USB serial")
|
||||
ap.add_argument("--port", "-p", default="", help="Serial port (COMx / /dev/ttyUSB0)")
|
||||
ap.add_argument("--baud", "-b", type=int, default=460800)
|
||||
ap.add_argument("--interval-ms", "-i", type=int, default=1000)
|
||||
ap.add_argument("--once", action="store_true", help="Send one line and exit")
|
||||
args = ap.parse_args()
|
||||
|
||||
# Warm CPU percent baseline
|
||||
psutil.cpu_percent(interval=None)
|
||||
|
||||
port = pick_port(args.port)
|
||||
print(
|
||||
f"Sending host info -> {port} @ {args.baud} every {args.interval_ms}ms "
|
||||
f"({platform.system()} {platform.release()}; Ctrl+C to stop)"
|
||||
)
|
||||
|
||||
ser = open_serial(port, args.baud)
|
||||
def run_session(port: str, baud: int, interval_ms: int, once: bool) -> None:
|
||||
ser = open_serial(port, baud)
|
||||
time.sleep(0.35)
|
||||
try:
|
||||
while True:
|
||||
@@ -420,13 +448,63 @@ def main() -> int:
|
||||
ser.write((line + "\n").encode("utf-8", errors="ignore"))
|
||||
ser.flush()
|
||||
print(line, flush=True)
|
||||
if args.once:
|
||||
break
|
||||
time.sleep(max(0.2, args.interval_ms / 1000.0))
|
||||
if once:
|
||||
return
|
||||
time.sleep(max(0.2, interval_ms / 1000.0))
|
||||
finally:
|
||||
try:
|
||||
ser.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser(
|
||||
description="Push host sysinfo to CYD over USB serial (no Python needed if using .exe)"
|
||||
)
|
||||
ap.add_argument("--port", "-p", default="", help="Serial port (COMx / /dev/ttyUSB0)")
|
||||
ap.add_argument("--baud", "-b", type=int, default=460800)
|
||||
ap.add_argument("--interval-ms", "-i", type=int, default=1000)
|
||||
ap.add_argument("--once", action="store_true", help="Send one line and exit")
|
||||
ap.add_argument(
|
||||
"--no-wait",
|
||||
action="store_true",
|
||||
help="Exit immediately if port missing (default: wait / reconnect)",
|
||||
)
|
||||
args = ap.parse_args()
|
||||
|
||||
# Warm CPU percent baseline
|
||||
psutil.cpu_percent(interval=None)
|
||||
|
||||
print(
|
||||
f"CYD host agent ({platform.system()} {platform.release()}) "
|
||||
f"baud={args.baud} interval={args.interval_ms}ms Ctrl+C to stop",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
try:
|
||||
while True:
|
||||
port = pick_port(args.port, wait=not args.no_wait)
|
||||
print(
|
||||
f"Sending host info -> {port}",
|
||||
flush=True,
|
||||
)
|
||||
try:
|
||||
run_session(port, args.baud, args.interval_ms, args.once)
|
||||
if args.once:
|
||||
return 0
|
||||
except serial.SerialException as e:
|
||||
print(f"Serial lost ({e}); will retry ...", flush=True)
|
||||
if args.once or args.no_wait:
|
||||
return 2
|
||||
time.sleep(1.0)
|
||||
except OSError as e:
|
||||
print(f"Port error ({e}); will retry ...", flush=True)
|
||||
if args.once or args.no_wait:
|
||||
return 2
|
||||
time.sleep(1.0)
|
||||
except KeyboardInterrupt:
|
||||
print("\nStopped.")
|
||||
finally:
|
||||
ser.close()
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user