mesk 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mesk/__init__.py +10 -0
- mesk/__main__.py +80 -0
- mesk/__version__.py +1 -0
- mesk/py.typed +0 -0
- mesk/runner.py +224 -0
- mesk/vendor/__init__.py +0 -0
- mesk/vendor/boardctrl_phidget/README.md +244 -0
- mesk/vendor/boardctrl_phidget/__init__.py +0 -0
- mesk/vendor/boardctrl_phidget/monitor.py +232 -0
- mesk/vendor/boardctrl_phidget/phidget.py +224 -0
- mesk/vendor/boardctrl_phidget/phidget_control.sh +324 -0
- mesk/vendor/topo/Android.mk +27 -0
- mesk/vendor/topo/TCA9539.py +70 -0
- mesk/vendor/topo/__init__.py +0 -0
- mesk/vendor/topo/_targets/_PCA953x.py +134 -0
- mesk/vendor/topo/_targets/_TCA6408.py +122 -0
- mesk/vendor/topo/_targets/__init__.py +0 -0
- mesk/vendor/topo/_targets/_common.py +203 -0
- mesk/vendor/topo/_targets/_nv_base_orin_target.py +37 -0
- mesk/vendor/topo/_targets/_nv_base_thor_target.py +47 -0
- mesk/vendor/topo/_targets/_nv_base_topo_target.py +249 -0
- mesk/vendor/topo/_targets/_nv_thor_target.py +21 -0
- mesk/vendor/topo/_targets/_nv_topo_target.py +21 -0
- mesk/vendor/topo/_targets/concord.py +21 -0
- mesk/vendor/topo/_targets/horizon-eb1.py +99 -0
- mesk/vendor/topo/_targets/jane.py +44 -0
- mesk/vendor/topo/_targets/orin-jetson.py +21 -0
- mesk/vendor/topo/_targets/orin-slt.py +32 -0
- mesk/vendor/topo/_targets/t239-slt.py +28 -0
- mesk/vendor/topo/_targets/t268-slt.py +262 -0
- mesk/vendor/topo/_targets/tango.py +75 -0
- mesk/vendor/topo/_targets/thor-andor-slt.py +253 -0
- mesk/vendor/topo/_targets/thor-cassian-slt.py +21 -0
- mesk/vendor/topo/_targets/thor-drive-agx.py +21 -0
- mesk/vendor/topo/_targets/thor-drive-agx2.py +105 -0
- mesk/vendor/topo/_targets/thor-drive-pm.py +21 -0
- mesk/vendor/topo/_targets/thor-hope.py +21 -0
- mesk/vendor/topo/_targets/thor-jetson-devkit.py +21 -0
- mesk/vendor/topo/_targets/thor-jetson-module.py +21 -0
- mesk/vendor/topo/_targets/thor-l4t-pm.py +21 -0
- mesk/vendor/topo/_targets/thor-slt.py +262 -0
- mesk/vendor/topo/_targets/topo.py +21 -0
- mesk/vendor/topo/_targets/yukon.py +75 -0
- mesk/vendor/topo/bmc_interface.py +441 -0
- mesk/vendor/topo/board_control.py +74 -0
- mesk/vendor/topo/board_info.py +104 -0
- mesk/vendor/topo/boardctl +216 -0
- mesk/vendor/topo/debug_board.py +308 -0
- mesk/vendor/topo/e2379.py +130 -0
- mesk/vendor/topo/ftdi_i2c_lin.py +337 -0
- mesk/vendor/topo/ftdi_wrapper.py +103 -0
- mesk/vendor/topo/galen_power.py +40 -0
- mesk/vendor/topo/ina219.py +167 -0
- mesk/vendor/topo/ina226.py +197 -0
- mesk/vendor/topo/ina3221.py +135 -0
- mesk/vendor/topo/libnvtopo.so +0 -0
- mesk/vendor/topo/libnvtopo_wrapper.py +516 -0
- mesk/vendor/topo/lspm342.py +49 -0
- mesk/vendor/topo/measure +324 -0
- mesk/vendor/topo/measure_power +130 -0
- mesk/vendor/topo/metadata.py +158 -0
- mesk/vendor/topo/nvtopo.py +516 -0
- mesk/vendor/topo/pca954x.py +41 -0
- mesk/vendor/topo/pm342.py +122 -0
- mesk/vendor/topo/power.py +93 -0
- mesk/vendor/topo/requirements-bmc.txt +5 -0
- mesk/vendor/topo/supported_targets.py +16 -0
- mesk/vendor/topo/supported_targets_all.py +16 -0
- mesk/vendor/topo/tb500-ctl +792 -0
- mesk/vendor/topo/tckctrl +110 -0
- mesk/vendor/topo/th500-ctl +282 -0
- mesk/vendor/topo/utils.py +91 -0
- mesk/vendor/topo_yukon/README.txt +41 -0
- mesk/vendor/topo_yukon/__init__.py +6 -0
- mesk/vendor/topo_yukon/_targets/__init__.py +0 -0
- mesk/vendor/topo_yukon/_targets/_common.py +202 -0
- mesk/vendor/topo_yukon/_targets/_nv_base_topo_target.py +246 -0
- mesk/vendor/topo_yukon/_targets/yukon.py +75 -0
- mesk/vendor/topo_yukon/boardctl +216 -0
- mesk/vendor/topo_yukon/libnvtopo.dll +0 -0
- mesk/vendor/topo_yukon/libnvtopo.so +0 -0
- mesk/vendor/topo_yukon/libnvtopo_wrapper.py +513 -0
- mesk/vendor/topo_yukon/nvtopo.py +511 -0
- mesk-0.1.0.dist-info/METADATA +205 -0
- mesk-0.1.0.dist-info/RECORD +88 -0
- mesk-0.1.0.dist-info/WHEEL +5 -0
- mesk-0.1.0.dist-info/entry_points.txt +2 -0
- mesk-0.1.0.dist-info/top_level.txt +1 -0
mesk/__init__.py
ADDED
mesk/__main__.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""CLI entry point for mesk."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
from mesk.runner import _PLATFORMS, ACTIONS, run
|
|
9
|
+
|
|
10
|
+
_SUDO_PLATFORMS = frozenset({"concord"})
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _needs_sudo(platform: str) -> bool:
|
|
14
|
+
if platform not in _SUDO_PLATFORMS:
|
|
15
|
+
return False
|
|
16
|
+
if os.environ.get("MESK_NO_SUDO"):
|
|
17
|
+
return False
|
|
18
|
+
try:
|
|
19
|
+
return os.geteuid() != 0
|
|
20
|
+
except AttributeError:
|
|
21
|
+
return False
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _reexec_with_sudo() -> None:
|
|
25
|
+
os.execvp("sudo", ["sudo", sys.executable, "-m", "mesk", *sys.argv[1:]])
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _phidget_lib_present() -> bool:
|
|
29
|
+
from ctypes.util import find_library
|
|
30
|
+
|
|
31
|
+
return find_library("phidget22") is not None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _ensure_libphidget22() -> None:
|
|
35
|
+
if _phidget_lib_present():
|
|
36
|
+
return
|
|
37
|
+
if os.environ.get("MESK_NO_SUDO"):
|
|
38
|
+
raise RuntimeError("libphidget22 is missing; install it or unset MESK_NO_SUDO")
|
|
39
|
+
import subprocess
|
|
40
|
+
|
|
41
|
+
print("Installing libphidget22 (may prompt for sudo)...")
|
|
42
|
+
apt = subprocess.run(["sudo", "apt-get", "install", "-y", "libphidget22"])
|
|
43
|
+
if apt.returncode == 0 and _phidget_lib_present():
|
|
44
|
+
return
|
|
45
|
+
print("Adding Phidget apt repository...")
|
|
46
|
+
setup = subprocess.run(
|
|
47
|
+
["curl", "-fsSL", "https://www.phidgets.com/downloads/setup_linux"],
|
|
48
|
+
check=True,
|
|
49
|
+
stdout=subprocess.PIPE,
|
|
50
|
+
)
|
|
51
|
+
subprocess.run(["sudo", "bash"], input=setup.stdout, check=True)
|
|
52
|
+
subprocess.check_call(["sudo", "apt-get", "install", "-y", "libphidget22"])
|
|
53
|
+
if not _phidget_lib_present():
|
|
54
|
+
raise RuntimeError("libphidget22 is still missing after install")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _ensure_phidget22() -> None:
|
|
58
|
+
import importlib.util
|
|
59
|
+
import subprocess
|
|
60
|
+
|
|
61
|
+
if importlib.util.find_spec("Phidget22") is None:
|
|
62
|
+
print("Installing Phidget22 into this environment...")
|
|
63
|
+
subprocess.check_call([sys.executable, "-m", "pip", "install", "Phidget22"])
|
|
64
|
+
_ensure_libphidget22()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def main() -> None:
|
|
68
|
+
platforms = list(_PLATFORMS)
|
|
69
|
+
if len(sys.argv) != 3 or sys.argv[1] not in platforms or sys.argv[2] not in ACTIONS:
|
|
70
|
+
print(f"Usage: mesk <{'|'.join(platforms)}> <{'|'.join(ACTIONS)}>")
|
|
71
|
+
sys.exit(1)
|
|
72
|
+
if sys.argv[1] == "n1-auto":
|
|
73
|
+
_ensure_phidget22()
|
|
74
|
+
if _needs_sudo(sys.argv[1]):
|
|
75
|
+
_reexec_with_sudo()
|
|
76
|
+
sys.exit(run(sys.argv[1], sys.argv[2]))
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
if __name__ == "__main__":
|
|
80
|
+
main()
|
mesk/__version__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
mesk/py.typed
ADDED
|
File without changes
|
mesk/runner.py
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"""Execute platform power/reset actions via in-process vendor libraries."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import contextlib
|
|
6
|
+
import io
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
import time
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from importlib import import_module
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from types import ModuleType
|
|
14
|
+
from typing import Any, Callable
|
|
15
|
+
|
|
16
|
+
_VENDOR_ROOT = Path(__file__).resolve().parent / "vendor"
|
|
17
|
+
_log_dir_env = os.environ.get("MESK_LOG_DIR")
|
|
18
|
+
_LOG_DIR = Path(_log_dir_env) if _log_dir_env else Path.cwd() / "logs"
|
|
19
|
+
|
|
20
|
+
# platform -> (vendor subdirectory, nv_topo target name or None for phidget)
|
|
21
|
+
_PLATFORMS: dict[str, tuple[str, str | None]] = {
|
|
22
|
+
"concord": ("topo", "topo"),
|
|
23
|
+
"n1-auto": ("boardctrl_phidget", None),
|
|
24
|
+
"yukon": ("topo_yukon", "yukon"),
|
|
25
|
+
"tango": ("topo_yukon", "yukon"),
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
ACTIONS = ("reset", "recovery", "power_on", "power_off")
|
|
29
|
+
|
|
30
|
+
_N1_RESET_RELAY = 1
|
|
31
|
+
_N1_RECOVERY_RELAY = 0
|
|
32
|
+
_N1_POWER_RELAY = 2
|
|
33
|
+
|
|
34
|
+
_NVIDIA_VID = 0x0955
|
|
35
|
+
_CONCORD_APX_PID = 0x7023
|
|
36
|
+
|
|
37
|
+
_TOPO_MODULE_PREFIXES = (
|
|
38
|
+
"nvtopo",
|
|
39
|
+
"libnvtopo_wrapper",
|
|
40
|
+
"board_info",
|
|
41
|
+
"_targets",
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _module_file(mod: object) -> str | None:
|
|
46
|
+
path = getattr(mod, "__file__", None)
|
|
47
|
+
return str(Path(path).resolve()) if path else None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _evict_modules_from(vendor_dir: Path) -> None:
|
|
51
|
+
prefix = str(vendor_dir.resolve())
|
|
52
|
+
doomed = []
|
|
53
|
+
for name, mod in sys.modules.items():
|
|
54
|
+
file = _module_file(mod)
|
|
55
|
+
if file is not None and file.startswith(prefix + os.sep):
|
|
56
|
+
doomed.append(name)
|
|
57
|
+
elif name in _TOPO_MODULE_PREFIXES or name.startswith("_targets."):
|
|
58
|
+
doomed.append(name)
|
|
59
|
+
for name in doomed:
|
|
60
|
+
sys.modules.pop(name, None)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _activate_vendor_path(vendor_dir: Path) -> None:
|
|
64
|
+
path = str(vendor_dir)
|
|
65
|
+
sys.path = [p for p in sys.path if p != path]
|
|
66
|
+
sys.path.insert(0, path)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _load_vendor_module(vendor_subdir: str, module_name: str) -> ModuleType:
|
|
70
|
+
vendor_dir = _VENDOR_ROOT / vendor_subdir
|
|
71
|
+
_evict_modules_from(vendor_dir)
|
|
72
|
+
_activate_vendor_path(vendor_dir)
|
|
73
|
+
return import_module(module_name)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _load_nv_topo(vendor_subdir: str) -> Callable[..., Any]:
|
|
77
|
+
nvtopo = _load_vendor_module(vendor_subdir, "nvtopo")
|
|
78
|
+
return nvtopo.nv_topo
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _load_monitor() -> ModuleType:
|
|
82
|
+
return _load_vendor_module("boardctrl_phidget", "monitor")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _run_topo(vendor_subdir: str, target: str, action: str) -> None:
|
|
86
|
+
nv_topo = _load_nv_topo(vendor_subdir)
|
|
87
|
+
board = nv_topo(target=target)
|
|
88
|
+
if action == "reset":
|
|
89
|
+
board.target_reset(None)
|
|
90
|
+
elif action == "recovery":
|
|
91
|
+
board.target_recovery_mode(None)
|
|
92
|
+
elif action == "power_on":
|
|
93
|
+
board.target_power_on(None)
|
|
94
|
+
else:
|
|
95
|
+
board.target_power_off()
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _run_n1_auto(action: str) -> None:
|
|
99
|
+
monitor = _load_monitor()
|
|
100
|
+
lock = Path("/tmp/phidget.lock")
|
|
101
|
+
|
|
102
|
+
if action in ("reset", "recovery"):
|
|
103
|
+
print("Cleaning up any existing Phidget processes...")
|
|
104
|
+
print("Removing any stale lock files...")
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
lock.unlink()
|
|
108
|
+
except OSError:
|
|
109
|
+
pass
|
|
110
|
+
|
|
111
|
+
if action == "reset":
|
|
112
|
+
if not monitor.ResetControl(_N1_RESET_RELAY).execute_reset():
|
|
113
|
+
raise RuntimeError("n1-auto reset failed")
|
|
114
|
+
print("Reset sequence completed successfully")
|
|
115
|
+
print("Soft RESET performed")
|
|
116
|
+
elif action == "recovery":
|
|
117
|
+
if not monitor.RecoveryControl(
|
|
118
|
+
_N1_RESET_RELAY, _N1_RECOVERY_RELAY
|
|
119
|
+
).execute_recovery():
|
|
120
|
+
raise RuntimeError("n1-auto recovery failed")
|
|
121
|
+
print("Recovery sequence completed successfully")
|
|
122
|
+
print("Board is now in RECOVERY")
|
|
123
|
+
else:
|
|
124
|
+
desired_closed = action == "power_off"
|
|
125
|
+
ctl = monitor.PowerMonitor(_N1_POWER_RELAY, desired_closed)
|
|
126
|
+
if not ctl.initialize():
|
|
127
|
+
raise RuntimeError("n1-auto power control failed to initialize")
|
|
128
|
+
try:
|
|
129
|
+
print(
|
|
130
|
+
f"Controlling power relay ({_N1_POWER_RELAY})"
|
|
131
|
+
" - Normally Open configuration"
|
|
132
|
+
)
|
|
133
|
+
if desired_closed:
|
|
134
|
+
print("Closing circuit (turning power OFF)...")
|
|
135
|
+
ctl.phidget.relay_on(_N1_POWER_RELAY)
|
|
136
|
+
else:
|
|
137
|
+
print("Opening circuit (turning power ON)...")
|
|
138
|
+
ctl.phidget.relay_off(_N1_POWER_RELAY)
|
|
139
|
+
time.sleep(0.1)
|
|
140
|
+
if desired_closed:
|
|
141
|
+
print("Power circuit is CLOSED (OFF)")
|
|
142
|
+
print("SOC power OFF")
|
|
143
|
+
else:
|
|
144
|
+
print("Power circuit is OPEN (ON)")
|
|
145
|
+
print("SOC power ON")
|
|
146
|
+
finally:
|
|
147
|
+
ctl.cleanup()
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _nvidia_usb_devices() -> list[Any]:
|
|
151
|
+
import usb.core
|
|
152
|
+
|
|
153
|
+
found = usb.core.find(find_all=True, idVendor=_NVIDIA_VID)
|
|
154
|
+
return list(found) if found else []
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _verify_concord_recovery() -> None:
|
|
158
|
+
"""Confirm APX (0955:7023) after Concord recovery; boardctl status is unreliable."""
|
|
159
|
+
devices: list[Any] = []
|
|
160
|
+
for _ in range(5):
|
|
161
|
+
devices = _nvidia_usb_devices()
|
|
162
|
+
if any(int(dev.idProduct) == _CONCORD_APX_PID for dev in devices):
|
|
163
|
+
break
|
|
164
|
+
time.sleep(1)
|
|
165
|
+
print("lsusb check (NVIDIA 0955:*):")
|
|
166
|
+
if devices:
|
|
167
|
+
for dev in devices:
|
|
168
|
+
pid = int(dev.idProduct)
|
|
169
|
+
label = " NVIDIA Corp. APX" if pid == _CONCORD_APX_PID else ""
|
|
170
|
+
print(f" 0955:{pid:04x}{label}")
|
|
171
|
+
else:
|
|
172
|
+
print(" (none)")
|
|
173
|
+
if not any(int(dev.idProduct) == _CONCORD_APX_PID for dev in devices):
|
|
174
|
+
raise RuntimeError(
|
|
175
|
+
"Concord recovery check failed: 0955:7023 (APX) not found in lsusb"
|
|
176
|
+
)
|
|
177
|
+
print("enter into recovery mode pass")
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _dispatch(platform: str, action: str) -> None:
|
|
181
|
+
vendor_subdir, target = _PLATFORMS[platform]
|
|
182
|
+
if target is None:
|
|
183
|
+
_run_n1_auto(action)
|
|
184
|
+
return
|
|
185
|
+
_run_topo(vendor_subdir, target, action)
|
|
186
|
+
if platform == "concord" and action == "recovery":
|
|
187
|
+
_verify_concord_recovery()
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _exit_code(exc: SystemExit) -> int:
|
|
191
|
+
if exc.code is None:
|
|
192
|
+
return 0
|
|
193
|
+
if isinstance(exc.code, int):
|
|
194
|
+
return exc.code
|
|
195
|
+
return 1
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def run(platform: str, action: str) -> int:
|
|
199
|
+
"""Execute an action on the given platform. Returns 0 on success."""
|
|
200
|
+
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
201
|
+
buf = io.StringIO()
|
|
202
|
+
code = 0
|
|
203
|
+
try:
|
|
204
|
+
with contextlib.redirect_stdout(buf), contextlib.redirect_stderr(buf):
|
|
205
|
+
_dispatch(platform, action)
|
|
206
|
+
except SystemExit as exc:
|
|
207
|
+
if exc.code not in (None, 0):
|
|
208
|
+
buf.write(f"{exc.code}\n")
|
|
209
|
+
code = _exit_code(exc)
|
|
210
|
+
except Exception as exc:
|
|
211
|
+
buf.write(f"{type(exc).__name__}: {exc}\n")
|
|
212
|
+
code = 1
|
|
213
|
+
|
|
214
|
+
output = buf.getvalue()
|
|
215
|
+
print(output, end="")
|
|
216
|
+
|
|
217
|
+
_LOG_DIR.mkdir(parents=True, exist_ok=True)
|
|
218
|
+
log_file = _LOG_DIR / (timestamp[:10] + ".log")
|
|
219
|
+
with open(log_file, "a", encoding="utf-8") as f:
|
|
220
|
+
f.write(f"[{timestamp}] platform={platform} action={action} exit_code={code}\n")
|
|
221
|
+
f.write(output)
|
|
222
|
+
f.write("\n")
|
|
223
|
+
|
|
224
|
+
return code
|
mesk/vendor/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# Phidget Relay Control Suite
|
|
2
|
+
|
|
3
|
+
This project provides a robust, scriptable interface for controlling Phidget relay devices, supporting operations such as reset, recovery, and power cycling. It is designed for safe, automated hardware control, with both Python and shell script interfaces.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Overview](#overview)
|
|
10
|
+
- [File Structure](#file-structure)
|
|
11
|
+
- [Setup](#setup)
|
|
12
|
+
- [Usage](#usage)
|
|
13
|
+
- [phidget_control.sh](#phidget_controlsh)
|
|
14
|
+
- [monitor.py](#monitorpy)
|
|
15
|
+
- [phidget.py](#phidgetpy)
|
|
16
|
+
- [Detailed Code Reference](#detailed-code-reference)
|
|
17
|
+
- [monitor.py](#monitorpy-1)
|
|
18
|
+
- [phidget.py](#phidgetpy-1)
|
|
19
|
+
- [phidget_control.sh](#phidget_controlsh-1)
|
|
20
|
+
- [Extending and Maintaining](#extending-and-maintaining)
|
|
21
|
+
- [Troubleshooting](#troubleshooting)
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Overview
|
|
26
|
+
|
|
27
|
+
This suite is intended for environments where Phidget relay boards are used to control hardware (e.g., for automated testing, power cycling, or recovery operations). It provides:
|
|
28
|
+
|
|
29
|
+
- Safe, exclusive access to the hardware (via lock files)
|
|
30
|
+
- High-level operations (reset, recovery, power control)
|
|
31
|
+
- Low-level relay control and detection
|
|
32
|
+
- Easy command-line interface with environment setup
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## File Structure
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
monitor.py # High-level control and monitoring logic
|
|
40
|
+
phidget.py # Low-level Phidget relay interface
|
|
41
|
+
phidget_control.sh # User-facing shell script for all operations
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Setup
|
|
47
|
+
|
|
48
|
+
1. **Install Python 3** (if not already installed).
|
|
49
|
+
2. **Run the shell script**:
|
|
50
|
+
```bash
|
|
51
|
+
./phidget_control.sh [command] [options]
|
|
52
|
+
```
|
|
53
|
+
The script will automatically create a virtual environment and install the required Python package (`Phidget22`).
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Usage
|
|
58
|
+
|
|
59
|
+
### phidget_control.sh
|
|
60
|
+
|
|
61
|
+
This is the main entry point for users. It supports the following commands:
|
|
62
|
+
|
|
63
|
+
- `detect` — Detect connected Phidget relays.
|
|
64
|
+
- `relay [on|off|toggle] <relay_num>` — Control a specific relay.
|
|
65
|
+
- `sequence "<relay:action,...>"` — Execute a sequence of relay actions.
|
|
66
|
+
- `reset` — Perform a reset sequence (Relay 1).
|
|
67
|
+
- `recovery` — Perform a recovery sequence (Relays 0 & 1).
|
|
68
|
+
- `poweron` / `poweroff` — Control the power relay (Relay 2).
|
|
69
|
+
|
|
70
|
+
**Examples:**
|
|
71
|
+
```bash
|
|
72
|
+
./phidget_control.sh detect
|
|
73
|
+
./phidget_control.sh relay on 1
|
|
74
|
+
./phidget_control.sh sequence "1:on,2:off"
|
|
75
|
+
./phidget_control.sh reset
|
|
76
|
+
./phidget_control.sh poweroff
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### monitor.py
|
|
80
|
+
|
|
81
|
+
This script provides high-level operations and should not be run directly except for advanced use. It supports:
|
|
82
|
+
|
|
83
|
+
- `reset <relay_num>`
|
|
84
|
+
- `recovery <reset_relay> <recovery_relay>`
|
|
85
|
+
- `power <relay_num> <true|false>`
|
|
86
|
+
|
|
87
|
+
**Example:**
|
|
88
|
+
```bash
|
|
89
|
+
python3 monitor.py reset 1
|
|
90
|
+
python3 monitor.py recovery 1 0
|
|
91
|
+
python3 monitor.py power 2 true
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### phidget.py
|
|
95
|
+
|
|
96
|
+
This is a library for direct relay control. It can also be run directly for testing relay sequences.
|
|
97
|
+
|
|
98
|
+
**Example:**
|
|
99
|
+
```bash
|
|
100
|
+
python3 phidget.py -seq "1:on,2:off"
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Detailed Code Reference
|
|
106
|
+
|
|
107
|
+
### monitor.py
|
|
108
|
+
|
|
109
|
+
#### Classes
|
|
110
|
+
|
|
111
|
+
- **BaseMonitor**
|
|
112
|
+
- Handles device locking (`/tmp/phidget.lock`) to prevent concurrent access.
|
|
113
|
+
- Manages Phidget device initialization and cleanup.
|
|
114
|
+
- Methods:
|
|
115
|
+
- `acquire_lock()`: Ensures exclusive access to the device.
|
|
116
|
+
- `release_lock()`: Releases the lock and cleans up.
|
|
117
|
+
- `cleanup()`: Closes the Phidget device and releases the lock.
|
|
118
|
+
- `initialize()`: Acquires lock and initializes the Phidget device.
|
|
119
|
+
|
|
120
|
+
- **ResetControl(BaseMonitor)**
|
|
121
|
+
- For performing a reset sequence on a specified relay.
|
|
122
|
+
- Methods:
|
|
123
|
+
- `execute_reset()`: Closes and then opens the relay, simulating a reset pulse.
|
|
124
|
+
|
|
125
|
+
- **RecoveryControl(BaseMonitor)**
|
|
126
|
+
- For performing a recovery sequence involving two relays.
|
|
127
|
+
- Methods:
|
|
128
|
+
- `execute_reset()`: (Internal) Performs a reset while recovery relay is held.
|
|
129
|
+
- `execute_recovery()`: Closes recovery relay, performs reset, then opens recovery relay.
|
|
130
|
+
|
|
131
|
+
- **PowerMonitor(BaseMonitor)**
|
|
132
|
+
- Maintains a relay in a desired state (on/off) in a loop.
|
|
133
|
+
- Methods:
|
|
134
|
+
- `maintain_state()`: Continuously checks and restores the relay state.
|
|
135
|
+
|
|
136
|
+
#### Main Script Logic
|
|
137
|
+
|
|
138
|
+
- Parses command-line arguments to select the operation (`reset`, `recovery`, `power`).
|
|
139
|
+
- Instantiates the appropriate control class and executes the requested operation.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
### phidget.py
|
|
144
|
+
|
|
145
|
+
#### Classes and Functions
|
|
146
|
+
|
|
147
|
+
- **PhidgetDevice**
|
|
148
|
+
- Represents a Phidget device (not heavily used in the rest of the code).
|
|
149
|
+
- Attributes: `name`, `serial_number`, `channels`.
|
|
150
|
+
|
|
151
|
+
- **Phidget**
|
|
152
|
+
- Main interface to the Phidget relay hardware.
|
|
153
|
+
- Attributes:
|
|
154
|
+
- `relays`: Dictionary of relay objects (0-3).
|
|
155
|
+
- Methods:
|
|
156
|
+
- `initialize_relays()`: Initializes up to 4 relays.
|
|
157
|
+
- `relay_on(relay_num)`: Turns a relay ON (closes circuit).
|
|
158
|
+
- `relay_off(relay_num)`: Turns a relay OFF (opens circuit).
|
|
159
|
+
- `relay_toggle(relay_num)`: Toggles relay state.
|
|
160
|
+
- `execute_sequence(sequence)`: Executes a comma-separated sequence of relay actions.
|
|
161
|
+
- `close()`: Closes all relay connections.
|
|
162
|
+
- `detect_all()`: Static method to detect which relays are connected.
|
|
163
|
+
|
|
164
|
+
- **between(s, opat, cpat, last_closing)**
|
|
165
|
+
- Utility function to extract a substring between two delimiters (not used elsewhere).
|
|
166
|
+
|
|
167
|
+
#### Main Script Logic
|
|
168
|
+
|
|
169
|
+
- Supports command-line arguments for serial number and relay sequence.
|
|
170
|
+
- Can be used to test relay sequences directly.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
### phidget_control.sh
|
|
175
|
+
|
|
176
|
+
#### Key Features
|
|
177
|
+
|
|
178
|
+
- **Environment Setup**
|
|
179
|
+
- Creates a Python virtual environment if not present.
|
|
180
|
+
- Installs the `Phidget22` library if missing.
|
|
181
|
+
|
|
182
|
+
- **Command Functions**
|
|
183
|
+
- `detect_devices()`: Uses `phidget.py` to list connected relays.
|
|
184
|
+
- `control_relay(action, relay)`: Turns a relay on, off, or toggles it.
|
|
185
|
+
- `execute_sequence(sequence)`: Runs a sequence of relay actions.
|
|
186
|
+
- `execute_reset()`: Runs a reset sequence using `monitor.py`.
|
|
187
|
+
- `execute_recovery()`: Runs a recovery sequence using `monitor.py`.
|
|
188
|
+
- `control_power(action)`: Maintains the power relay in the desired state using `monitor.py` in the background.
|
|
189
|
+
|
|
190
|
+
- **Process and Lock Management**
|
|
191
|
+
- Kills any running `monitor.py` processes before starting new operations.
|
|
192
|
+
- Removes stale lock files to prevent deadlocks.
|
|
193
|
+
|
|
194
|
+
- **User Guidance**
|
|
195
|
+
- Provides colored output and usage instructions for ease of use.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Extending and Maintaining
|
|
200
|
+
|
|
201
|
+
- **Adding New Operations:**
|
|
202
|
+
Add new methods to `monitor.py` (inheriting from `BaseMonitor` if needed), and expose them via `phidget_control.sh`.
|
|
203
|
+
|
|
204
|
+
- **Relay Mapping:**
|
|
205
|
+
Update relay numbers in `phidget_control.sh` as needed for your hardware.
|
|
206
|
+
|
|
207
|
+
- **Error Handling:**
|
|
208
|
+
Both Python and shell scripts print detailed error messages. For more robust error handling, consider logging errors to a file.
|
|
209
|
+
|
|
210
|
+
- **Dependencies:**
|
|
211
|
+
The only Python dependency is `Phidget22`. If you add more, update the install section in the shell script.
|
|
212
|
+
|
|
213
|
+
- **Testing:**
|
|
214
|
+
You can test new relay sequences directly using `phidget.py` before integrating them into higher-level scripts.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Troubleshooting
|
|
219
|
+
|
|
220
|
+
- **Device Busy/Locked:**
|
|
221
|
+
If you see errors about the device being in use, ensure no other process is running and remove `/tmp/phidget.lock` if necessary.
|
|
222
|
+
|
|
223
|
+
- **Phidget22 Not Installed:**
|
|
224
|
+
The shell script should handle this, but you can manually install with `pip install Phidget22` in the `venv`.
|
|
225
|
+
|
|
226
|
+
- **Relay Not Responding:**
|
|
227
|
+
Use the `detect` command to check relay connectivity.
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Contact
|
|
232
|
+
|
|
233
|
+
For further information, refer to the [Phidget Python API documentation](https://www.phidgets.com/docs/Language_-_Python).
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
**For any issues, contact:**
|
|
238
|
+
|
|
239
|
+
**Ekjot Singh**
|
|
240
|
+
eksingh@nvidia.com
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
This README should provide a comprehensive reference for using, maintaining, and extending your Phidget relay control suite. If you need a more visual diagram or have specific questions about enhancements, let me know!
|
|
File without changes
|