#!/bin/bash # daemon_add_stop.sh — Pi の mecharm_daemon.py に「stop」の命令を 1 つ書き足して起動し直す (2026-11-05) # # tools/daemon_add_stop.sh # # なぜ: ゲームパッドの長押しを「15° 先へ 1 回送り, 離したら止める」形にするため (jog_probe 3 回の結果). # 今の daemon には動いている途中で止める命令がない. # 足す命令: {"cmd": "stop"} → mc.jog_stop() と mc.stop() を順に呼ぶ (どちらかが効かなかった試しがあるので両方). # ほかの命令は 1 文字も変えない (unknown cmd の else の直前に足すだけ). # 流れ: # 1. Pi で今動いている /home/ubuntu/mecharm_daemon.py を Mac に持ち帰り, KIOXIA の archive/ に写しを残す # 2. すでに stop があれば何もしない. なければ書き足し, 構文を確かめる # 3. Pi へ一時名で送ってから入れ替え, systemd で起動し直し, ping と stop が返事をするか確かめる # うまくいかなければ, 元に戻すコマンドを出して止まる # 流す前に: ゲームパッドなど daemon を使うものを止め, アームを HOME など安全な姿勢にしておく # (daemon を起動し直すと MyCobot の初期化が走る) # 書き足した daemon は tools/pi/mecharm_daemon.py にも置く (これからの手元の正. ルートの mecharm_daemon.py は古い版なので触らない) set -u PI=ubuntu@192.168.2.1 KEY="$HOME/.ssh/id_rsa_pi" SSH=(ssh -n -i "$KEY" -o ConnectTimeout=5 "$PI") UNIT=mecharm-daemon.service HERE="$(cd "$(dirname "$0")" && pwd)" ROOT="$(dirname "$HERE")" DATA_ROOT=/Volumes/KIOXIA/r2r2r-data [ -d "$DATA_ROOT/archive" ] || { echo "ABORT: $DATA_ROOT/archive がない (KIOXIA がマウントされていない?)"; exit 1; } STAMP=$(date +%Y%m%d_%H%M%S) BK="$DATA_ROOT/archive/mecharm_daemon_premerge_$STAMP" mkdir "$BK" || exit 1 WORK=$(mktemp -d) scp -i "$KEY" -q -o ConnectTimeout=5 "$PI:/home/ubuntu/mecharm_daemon.py" "$WORK/before.py" || { echo "ABORT: Pi から持ち帰れない"; exit 1; } cp "$WORK/before.py" "$BK/mecharm_daemon.py" || { echo "ABORT: 写しを残せない (Pi の daemon は変えていません)"; exit 1; } echo "今の daemon の写し: $BK/mecharm_daemon.py" python3 - "$WORK/before.py" "$WORK/after.py" <<'PY' import sys src = open(sys.argv[1], encoding="utf-8").read() if 'cmd == "stop"' in src: print("すでに stop があります。書き足しません") open(sys.argv[2], "w", encoding="utf-8").write(src) sys.exit(0) for need in ("_mc", "def log("): assert need in src, f"daemon に {need} がない (書き足す命令が動かない)" anchor = ''' else: return {"ok": False, "error": f"unknown cmd: {cmd}"}''' assert src.count(anchor) == 1, "書き足す場所 (unknown cmd の else) が 1 つに決まらない" add = ''' # 2026-11-05: 動いている途中で止める (ゲームパッドの長押しを離したとき・L2+R2). # jog_probe (diag/jog_probe_20261105_*) で jog_stop と stop はどちらも効かない回があったので両方呼ぶ. elif cmd == "stop": res = {} for name in ("jog_stop", "stop"): try: res[name] = getattr(_mc, name)() except Exception as e: res[name] = f"{type(e).__name__}: {e}" log(f"stop: {res}") if all(isinstance(v, str) for v in res.values()): return {"ok": False, "error": f"stop failed: {res}"} return {"ok": True, "result": res} ''' out = src.replace(anchor, add + anchor) compile(out, "mecharm_daemon.py", "exec") open(sys.argv[2], "w", encoding="utf-8").write(out) print("stop を書き足しました") PY [ $? = 0 ] || { echo "ABORT: 書き足せない (Pi の daemon は変えていません)"; exit 1; } cp "$WORK/after.py" "$BK/mecharm_daemon_with_stop.py" || exit 1 cp "$WORK/after.py" "$HERE/pi/mecharm_daemon.py" || exit 1 RESTORE="scp -i $KEY $BK/mecharm_daemon.py $PI:/home/ubuntu/mecharm_daemon.py && ssh -i $KEY $PI sudo systemctl restart $UNIT" fail() { echo "ABORT: $1"; echo "元に戻すには: $RESTORE"; exit 1; } echo "--- 差分" diff "$WORK/before.py" "$WORK/after.py" scp -i "$KEY" -q -o ConnectTimeout=5 "$WORK/after.py" "$PI:/home/ubuntu/mecharm_daemon.py.new" || { echo "ABORT: Pi へ送れない (Pi の daemon は変えていません)"; exit 1; } "${SSH[@]}" "python3 -m py_compile /home/ubuntu/mecharm_daemon.py.new && mv /home/ubuntu/mecharm_daemon.py.new /home/ubuntu/mecharm_daemon.py" \ || { echo "ABORT: Pi で入れ替えられない (Pi の daemon は変えていません)"; exit 1; } "${SSH[@]}" "sudo systemctl restart $UNIT" || fail "systemctl restart が失敗した" UP=no for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do sleep 2 "${SSH[@]}" "ss -ltn | grep -q ':5555 '" && { UP=yes; break; } done [ "$UP" = yes ] || fail "30 秒たっても 5555 が開かない" python3 - <<'PY' import json, socket s = socket.create_connection(("192.168.2.1", 5555), timeout=5) f = s.makefile("rwb") for c in ({"cmd": "ping"}, {"cmd": "stop"}): f.write((json.dumps(c) + "\n").encode()); f.flush() r = json.loads(f.readline().decode()) print(c["cmd"], "→", r) assert r.get("ok"), c["cmd"] + " が ok を返さない" s.close() PY [ $? = 0 ] || fail "ping か stop が ok を返さない" echo "できました。記録: $BK"