vuer-cli 0.0.4__py3-none-any.whl → 0.0.5__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.
- vuer_cli/add.py +66 -68
- vuer_cli/envs_publish.py +335 -309
- vuer_cli/envs_pull.py +177 -170
- vuer_cli/login.py +459 -0
- vuer_cli/main.py +7 -2
- vuer_cli/remove.py +84 -84
- vuer_cli/scripts/demcap.py +19 -15
- vuer_cli/scripts/mcap_playback.py +661 -0
- vuer_cli/scripts/minimap.py +113 -210
- vuer_cli/scripts/viz_ptc_cams.py +1 -1
- vuer_cli/scripts/viz_ptc_proxie.py +1 -1
- vuer_cli/sync.py +314 -308
- vuer_cli/upgrade.py +118 -126
- {vuer_cli-0.0.4.dist-info → vuer_cli-0.0.5.dist-info}/METADATA +36 -6
- vuer_cli-0.0.5.dist-info/RECORD +22 -0
- vuer_cli/scripts/vuer_ros_bridge.py +0 -210
- vuer_cli-0.0.4.dist-info/RECORD +0 -21
- {vuer_cli-0.0.4.dist-info → vuer_cli-0.0.5.dist-info}/WHEEL +0 -0
- {vuer_cli-0.0.4.dist-info → vuer_cli-0.0.5.dist-info}/entry_points.txt +0 -0
- {vuer_cli-0.0.4.dist-info → vuer_cli-0.0.5.dist-info}/licenses/LICENSE +0 -0
vuer_cli/upgrade.py
CHANGED
|
@@ -12,141 +12,133 @@ from .utils import is_dry_run, print_error
|
|
|
12
12
|
|
|
13
13
|
@proto
|
|
14
14
|
class Upgrade:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
Usage:
|
|
18
|
-
vuer upgrade some-environment-name
|
|
19
|
-
"""
|
|
20
|
-
|
|
21
|
-
# Required positional arg: environment name to upgrade (no version part)
|
|
22
|
-
env: str
|
|
23
|
-
|
|
24
|
-
def run(self) -> int:
|
|
25
|
-
"""Execute upgrade command."""
|
|
26
|
-
try:
|
|
27
|
-
env_name = self.env.strip()
|
|
28
|
-
|
|
29
|
-
cwd = Path.cwd()
|
|
30
|
-
env_json_path = cwd / "environment.json"
|
|
31
|
-
if not env_json_path.exists():
|
|
32
|
-
raise FileNotFoundError(
|
|
33
|
-
"environment.json not found. Cannot upgrade environments."
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
try:
|
|
37
|
-
with env_json_path.open("r", encoding="utf-8") as f:
|
|
38
|
-
data = json.load(f)
|
|
39
|
-
except json.JSONDecodeError as e:
|
|
40
|
-
raise ValueError(f"Invalid environment.json: {e}") from e
|
|
41
|
-
|
|
42
|
-
deps = data.get("dependencies") or {}
|
|
43
|
-
if not isinstance(deps, dict):
|
|
44
|
-
raise ValueError(
|
|
45
|
-
"environment.json 'dependencies' field must be an object"
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
current_version = deps.get(env_name)
|
|
49
|
-
if not isinstance(current_version, str) or not current_version:
|
|
50
|
-
raise ValueError(
|
|
51
|
-
f"Environment '{env_name}' not found in environment.json; nothing to upgrade."
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
dry_run = is_dry_run()
|
|
55
|
-
|
|
56
|
-
if dry_run:
|
|
57
|
-
print(
|
|
58
|
-
f"[INFO] (dry-run) Would check latest version for '{env_name}', "
|
|
59
|
-
f"current version is '{current_version}'."
|
|
60
|
-
)
|
|
61
|
-
print(
|
|
62
|
-
"[INFO] (dry-run) Skipping upgrade and sync (no network calls).")
|
|
63
|
-
return 0
|
|
64
|
-
|
|
65
|
-
# Real upgrade: require hub configuration
|
|
66
|
-
if not Hub.url:
|
|
67
|
-
raise RuntimeError(
|
|
68
|
-
"Missing VUER_HUB_URL. Please set the VUER_HUB_URL environment variable "
|
|
69
|
-
"or pass --hub.url on the command line."
|
|
70
|
-
)
|
|
71
|
-
if not Hub.auth_token:
|
|
72
|
-
raise RuntimeError(
|
|
73
|
-
"Missing VUER_AUTH_TOKEN. Please set the VUER_AUTH_TOKEN environment "
|
|
74
|
-
"variable or pass --hub.auth-token on the command line."
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
latest_version = _fetch_latest_version(env_name)
|
|
78
|
-
|
|
79
|
-
if latest_version == current_version:
|
|
80
|
-
print(
|
|
81
|
-
f"[INFO] Environment '{env_name}' is already at latest version "
|
|
82
|
-
f"({current_version})."
|
|
83
|
-
)
|
|
84
|
-
return 0
|
|
85
|
-
|
|
86
|
-
# Update environment.json with the new version and run sync
|
|
87
|
-
deps[env_name] = latest_version
|
|
88
|
-
data["dependencies"] = deps
|
|
89
|
-
with env_json_path.open("w", encoding="utf-8") as f:
|
|
90
|
-
json.dump(data, f, indent=2, ensure_ascii=False)
|
|
91
|
-
f.write("\n")
|
|
92
|
-
|
|
93
|
-
print(
|
|
94
|
-
f"[INFO] Upgraded {env_name} from {current_version} to {latest_version} "
|
|
95
|
-
"in environment.json. Running sync..."
|
|
96
|
-
)
|
|
97
|
-
return Sync().run()
|
|
98
|
-
|
|
99
|
-
except (FileNotFoundError, ValueError, RuntimeError) as e:
|
|
100
|
-
print_error(str(e))
|
|
101
|
-
return 1
|
|
102
|
-
except Exception as e:
|
|
103
|
-
print_error(f"Unexpected error: {e}")
|
|
104
|
-
return 1
|
|
15
|
+
"""Upgrade a single environment in environment.json to the latest version.
|
|
105
16
|
|
|
17
|
+
Usage:
|
|
18
|
+
vuer upgrade some-environment-name
|
|
19
|
+
"""
|
|
106
20
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
import requests # Lazy import
|
|
110
|
-
|
|
111
|
-
params = {"name": env_name}
|
|
112
|
-
url = f"{Hub.url.rstrip('/')}/environments/latest-by-name"
|
|
113
|
-
headers = {
|
|
114
|
-
"Authorization": f"Bearer {Hub.auth_token}"
|
|
115
|
-
} if Hub.auth_token else {}
|
|
21
|
+
# Required positional arg: environment name to upgrade (no version part)
|
|
22
|
+
env: str
|
|
116
23
|
|
|
24
|
+
def __call__(self) -> int:
|
|
25
|
+
"""Execute upgrade command."""
|
|
117
26
|
try:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
) from e
|
|
27
|
+
env_name = self.env.strip()
|
|
28
|
+
|
|
29
|
+
cwd = Path.cwd()
|
|
30
|
+
env_json_path = cwd / "environment.json"
|
|
31
|
+
if not env_json_path.exists():
|
|
32
|
+
raise FileNotFoundError(
|
|
33
|
+
"environment.json not found. Cannot upgrade environments."
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
with env_json_path.open("r", encoding="utf-8") as f:
|
|
38
|
+
data = json.load(f)
|
|
39
|
+
except json.JSONDecodeError as e:
|
|
40
|
+
raise ValueError(f"Invalid environment.json: {e}") from e
|
|
41
|
+
|
|
42
|
+
deps = data.get("dependencies") or {}
|
|
43
|
+
if not isinstance(deps, dict):
|
|
44
|
+
raise ValueError("environment.json 'dependencies' field must be an object")
|
|
45
|
+
|
|
46
|
+
current_version = deps.get(env_name)
|
|
47
|
+
if not isinstance(current_version, str) or not current_version:
|
|
48
|
+
raise ValueError(
|
|
49
|
+
f"Environment '{env_name}' not found in environment.json; nothing to upgrade."
|
|
50
|
+
)
|
|
132
51
|
|
|
133
|
-
|
|
52
|
+
dry_run = is_dry_run()
|
|
134
53
|
|
|
135
|
-
|
|
136
|
-
|
|
54
|
+
if dry_run:
|
|
55
|
+
print(
|
|
56
|
+
f"[INFO] (dry-run) Would check latest version for '{env_name}', "
|
|
57
|
+
f"current version is '{current_version}'."
|
|
58
|
+
)
|
|
59
|
+
print("[INFO] (dry-run) Skipping upgrade and sync (no network calls).")
|
|
60
|
+
return 0
|
|
61
|
+
|
|
62
|
+
# Real upgrade: require hub configuration
|
|
63
|
+
if not Hub.url:
|
|
64
|
+
raise RuntimeError(
|
|
65
|
+
"Missing VUER_HUB_URL. Please set the VUER_HUB_URL environment variable "
|
|
66
|
+
"or pass --hub.url on the command line."
|
|
67
|
+
)
|
|
68
|
+
# Try to get token from credentials file if not in environment
|
|
69
|
+
auth_token = Hub.get_auth_token()
|
|
70
|
+
if not auth_token:
|
|
137
71
|
raise RuntimeError(
|
|
138
|
-
|
|
72
|
+
"Missing VUER_AUTH_TOKEN. Please run 'vuer login' to authenticate, "
|
|
73
|
+
"or set the VUER_AUTH_TOKEN environment variable."
|
|
139
74
|
)
|
|
140
75
|
|
|
141
|
-
|
|
142
|
-
if not isinstance(payload, dict):
|
|
143
|
-
raise ValueError(
|
|
144
|
-
"Invalid response format: 'data' field must be an object")
|
|
76
|
+
latest_version = _fetch_latest_version(env_name)
|
|
145
77
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
78
|
+
if latest_version == current_version:
|
|
79
|
+
print(
|
|
80
|
+
f"[INFO] Environment '{env_name}' is already at latest version "
|
|
81
|
+
f"({current_version})."
|
|
150
82
|
)
|
|
83
|
+
return 0
|
|
151
84
|
|
|
152
|
-
|
|
85
|
+
# Update environment.json with the new version and run sync
|
|
86
|
+
deps[env_name] = latest_version
|
|
87
|
+
data["dependencies"] = deps
|
|
88
|
+
with env_json_path.open("w", encoding="utf-8") as f:
|
|
89
|
+
json.dump(data, f, indent=2, ensure_ascii=False)
|
|
90
|
+
f.write("\n")
|
|
91
|
+
|
|
92
|
+
print(
|
|
93
|
+
f"[INFO] Upgraded {env_name} from {current_version} to {latest_version} "
|
|
94
|
+
"in environment.json. Running sync..."
|
|
95
|
+
)
|
|
96
|
+
return Sync().run()
|
|
97
|
+
|
|
98
|
+
except (FileNotFoundError, ValueError, RuntimeError) as e:
|
|
99
|
+
print_error(str(e))
|
|
100
|
+
return 1
|
|
101
|
+
except Exception as e:
|
|
102
|
+
print_error(f"Unexpected error: {e}")
|
|
103
|
+
return 1
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _fetch_latest_version(env_name: str) -> str:
|
|
107
|
+
"""Call backend /environments/latest-by-name to get latest versionId for env_name."""
|
|
108
|
+
import requests # Lazy import
|
|
109
|
+
|
|
110
|
+
params = {"name": env_name}
|
|
111
|
+
url = f"{Hub.url.rstrip('/')}/environments/latest-by-name"
|
|
112
|
+
auth_token = Hub.get_auth_token()
|
|
113
|
+
headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else {}
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
response = requests.get(url, params=params, headers=headers, timeout=300)
|
|
117
|
+
response.raise_for_status()
|
|
118
|
+
except requests.exceptions.HTTPError as e:
|
|
119
|
+
resp = getattr(e, "response", None)
|
|
120
|
+
status = resp.status_code if resp is not None else "unknown"
|
|
121
|
+
detail = _extract_backend_error(resp)
|
|
122
|
+
raise RuntimeError(
|
|
123
|
+
f"Failed to fetch latest version for '{env_name}' ({status}): {detail}"
|
|
124
|
+
) from e
|
|
125
|
+
except requests.exceptions.RequestException as e:
|
|
126
|
+
raise RuntimeError(f"Failed to fetch latest version for '{env_name}': {e}") from e
|
|
127
|
+
|
|
128
|
+
data = response.json()
|
|
129
|
+
|
|
130
|
+
# Error response format: {"error": "name is required"}
|
|
131
|
+
if "error" in data:
|
|
132
|
+
raise RuntimeError(
|
|
133
|
+
f"Failed to fetch latest version for '{env_name}': {data['error']}"
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
payload = data.get("data")
|
|
137
|
+
if not isinstance(payload, dict):
|
|
138
|
+
raise ValueError("Invalid response format: 'data' field must be an object")
|
|
139
|
+
|
|
140
|
+
version_id = payload.get("versionId")
|
|
141
|
+
if not version_id:
|
|
142
|
+
raise ValueError("Invalid response format: 'data.versionId' field is required")
|
|
143
|
+
|
|
144
|
+
return str(version_id)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vuer-cli
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.5
|
|
4
4
|
Summary: A Python CLI for Vuer, a real-time 3D visualization library
|
|
5
5
|
Project-URL: Homepage, https://github.com/vuer-ai/vuer-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/vuer-ai/vuer-cli
|
|
@@ -27,7 +27,7 @@ Requires-Dist: mcap>=1.1.0; extra == 'all'
|
|
|
27
27
|
Requires-Dist: numpy>=1.24.0; extra == 'all'
|
|
28
28
|
Requires-Dist: opencv-python>=4.8.0; extra == 'all'
|
|
29
29
|
Requires-Dist: pandas>=2.0.0; extra == 'all'
|
|
30
|
-
Requires-Dist: vuer>=0.0.
|
|
30
|
+
Requires-Dist: vuer>=0.0.81; extra == 'all'
|
|
31
31
|
Provides-Extra: docs
|
|
32
32
|
Requires-Dist: furo; extra == 'docs'
|
|
33
33
|
Requires-Dist: myst-parser; extra == 'docs'
|
|
@@ -46,7 +46,7 @@ Provides-Extra: viz
|
|
|
46
46
|
Requires-Dist: numpy>=1.24.0; extra == 'viz'
|
|
47
47
|
Requires-Dist: opencv-python>=4.8.0; extra == 'viz'
|
|
48
48
|
Requires-Dist: pandas>=2.0.0; extra == 'viz'
|
|
49
|
-
Requires-Dist: vuer>=0.0.
|
|
49
|
+
Requires-Dist: vuer>=0.0.81; extra == 'viz'
|
|
50
50
|
Description-Content-Type: text/markdown
|
|
51
51
|
|
|
52
52
|
# Vuer Hub Environment Manager
|
|
@@ -80,6 +80,7 @@ uv add vuer-cli==0.0.4
|
|
|
80
80
|
|----------------|-----------------------------------------------------------------------|
|
|
81
81
|
| `vuer` | Show top-level help and list available commands |
|
|
82
82
|
| `vuer --help` | Show detailed CLI help |
|
|
83
|
+
| `login` | Authenticate with Vuer Hub using OAuth Device Flow |
|
|
83
84
|
| `sync` | Sync all environments from environment.json dependencies (like npm install) |
|
|
84
85
|
| `add` | Add an environment dependency to environment.json |
|
|
85
86
|
| `remove` | Remove an environment dependency from environment.json |
|
|
@@ -89,18 +90,47 @@ uv add vuer-cli==0.0.4
|
|
|
89
90
|
|
|
90
91
|
## Usage
|
|
91
92
|
|
|
92
|
-
|
|
93
|
+
### Authentication
|
|
93
94
|
|
|
94
|
-
|
|
95
|
+
The easiest way to authenticate is using the `login` command:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# Set the Hub URL first
|
|
99
|
+
export VUER_HUB_URL="https://hub.vuer.ai/api"
|
|
100
|
+
|
|
101
|
+
# Login with dev environment (default)
|
|
102
|
+
vuer login
|
|
103
|
+
|
|
104
|
+
# Or login with production environment
|
|
105
|
+
vuer login --env production
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**After successful authentication:**
|
|
109
|
+
- ✅ Credentials are automatically saved and configured
|
|
110
|
+
- ✅ **Vuer CLI will automatically use the saved credentials** - no manual setup needed!
|
|
111
|
+
- ✅ Works immediately in the current terminal
|
|
112
|
+
- ✅ Works in all new terminals (auto-configured)
|
|
113
|
+
- ✅ Cross-platform support (Windows, macOS, Linux)
|
|
114
|
+
|
|
115
|
+
**How it works:**
|
|
116
|
+
1. Saves credentials to `~/.vuer/credentials` (used automatically by CLI)
|
|
117
|
+
2. Creates shell script at `~/.vuer/env.sh` (for manual use if needed)
|
|
118
|
+
3. **Windows**: Sets user environment variables via `setx`
|
|
119
|
+
4. **macOS/Linux**: Automatically adds to your shell config (~/.zshrc or ~/.bashrc)
|
|
120
|
+
|
|
121
|
+
**Manual setup (optional):**
|
|
122
|
+
|
|
123
|
+
If you prefer to set environment variables manually:
|
|
95
124
|
|
|
96
125
|
```bash
|
|
97
126
|
export VUER_HUB_URL="https://hub.vuer.ai/api"
|
|
98
|
-
# Optional: token for private hubs or authenticated operations
|
|
99
127
|
export VUER_AUTH_TOKEN="eyJhbGci..."
|
|
100
128
|
# Optional: enable dry-run mode to simulate operations (no network changes)
|
|
101
129
|
export VUER_CLI_DRY_RUN="1"
|
|
102
130
|
```
|
|
103
131
|
|
|
132
|
+
### Basic Commands
|
|
133
|
+
|
|
104
134
|
```bash
|
|
105
135
|
# Sync all environments from environment.json dependencies
|
|
106
136
|
# Reads environment.json in current directory, validates dependencies,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
vuer_cli/__init__.py,sha256=IyB44__IHu_PHuviTe8EULikOAJ45k-52kWbNpF8Lo0,387
|
|
2
|
+
vuer_cli/add.py,sha256=yEjyRhZOgDCndiNEw7qo-rvfshnQNckY1dQbs1MMbkk,2298
|
|
3
|
+
vuer_cli/envs_publish.py,sha256=XyI8nz6xZcadMjggz8BpLky8d9K_2v0ZafDPPIMq0w8,12159
|
|
4
|
+
vuer_cli/envs_pull.py,sha256=9DHxMcqUc88SD6SSAZL_LMAeACmaGmpQNkgw2furSnw,6700
|
|
5
|
+
vuer_cli/login.py,sha256=TrcBoI2kMMNDJnH8bkWMJBExtzW9n4-6JqZ2mePUsyg,15230
|
|
6
|
+
vuer_cli/main.py,sha256=wdaa_VVtlPNdLSC-Rge5QYqzdWhUBgoEB1p5-T_uKUQ,2559
|
|
7
|
+
vuer_cli/mcap_extractor.py,sha256=zQEaiWhmfEDSf0Tej7J0arkAxTHjCDaW73fb_OjHwNA,35529
|
|
8
|
+
vuer_cli/remove.py,sha256=GmvX9pxNCY8fj3jRxWzACF1jWDhxs4Ec7Pf75P7Y3M0,3310
|
|
9
|
+
vuer_cli/sync.py,sha256=d9x8E9woORHL94Qm3CGzFyxZT_XEugpwIj_YHHuUvqo,12776
|
|
10
|
+
vuer_cli/upgrade.py,sha256=eDV3V-Z6M4HBJCy-ppSN2r41AIHJv1fjtEAur9whVBM,4685
|
|
11
|
+
vuer_cli/utils.py,sha256=L7r1Lm-j6n7NqVjUJm5n8id7ZKuPrkOH3x-tP6BNx74,2670
|
|
12
|
+
vuer_cli/scripts/demcap.py,sha256=eLRWXg7lRRLKfYEdg3LtoDyROq0IbQP46bljcZmvQJQ,5415
|
|
13
|
+
vuer_cli/scripts/mcap_playback.py,sha256=7xv8NZcGP3XHwBzcy8AzAKk74kZCN4CztqmKvlmez0o,22329
|
|
14
|
+
vuer_cli/scripts/minimap.py,sha256=51xur7LsWx39ar0VseiqpxuChk8KybcGmI8LWf35xTI,11100
|
|
15
|
+
vuer_cli/scripts/ptc_utils.py,sha256=T55BY3plXwxtZTvbpesThYvE6vZB6aBT_iklwWjXg2Y,15018
|
|
16
|
+
vuer_cli/scripts/viz_ptc_cams.py,sha256=H0QdYuW-sHT9qj06pdpum_jTdHJmajUw0ZDCjUjmNjo,21062
|
|
17
|
+
vuer_cli/scripts/viz_ptc_proxie.py,sha256=inp-o6U2LkgwKa35OCaEz7LpLWMTw0qgzftKnb19oIE,14405
|
|
18
|
+
vuer_cli-0.0.5.dist-info/METADATA,sha256=3V9ocrPDLPyX7qDGPZXO8pkc8Pk7YEPBt_2OvQ2Ok9g,10501
|
|
19
|
+
vuer_cli-0.0.5.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
20
|
+
vuer_cli-0.0.5.dist-info/entry_points.txt,sha256=pH846erbxBfJpIj151Ps_UzgWIYPeIjl5gxZyTZn_7E,45
|
|
21
|
+
vuer_cli-0.0.5.dist-info/licenses/LICENSE,sha256=MGF-inVBUaGe2mEjqT0g6XsHIXwoNXgNHqD7Z1MzR0k,1063
|
|
22
|
+
vuer_cli-0.0.5.dist-info/RECORD,,
|
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
|
-
import asyncio
|
|
4
|
-
import threading
|
|
5
|
-
from collections import defaultdict, deque
|
|
6
|
-
|
|
7
|
-
import rclpy
|
|
8
|
-
from geometry_msgs.msg import PoseStamped
|
|
9
|
-
from params_proto import proto
|
|
10
|
-
from rclpy.node import Node
|
|
11
|
-
from rclpy.qos import HistoryPolicy, QoSProfile, ReliabilityPolicy
|
|
12
|
-
from sensor_msgs.msg import CompressedImage
|
|
13
|
-
from vista_demo.image_util import compressed_depth_to_jpeg
|
|
14
|
-
from vuer import VuerClient
|
|
15
|
-
from vuer.events import ClientEvent
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class MapPose(ClientEvent):
|
|
19
|
-
etype = "ros.MAP_POSE"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class DepthMap(ClientEvent):
|
|
23
|
-
etype = "ros.DEPTH_MAP"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def stamp_to_ns(stamp) -> int:
|
|
27
|
-
return stamp.sec * 1_000_000_000 + stamp.nanosec
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def pose_to_tf(msg: PoseStamped) -> dict:
|
|
31
|
-
p = msg.pose.position
|
|
32
|
-
q = msg.pose.orientation
|
|
33
|
-
return {
|
|
34
|
-
"timestamp": stamp_to_ns(msg.header.stamp),
|
|
35
|
-
"frame_id": msg.header.frame_id,
|
|
36
|
-
"position": {"x": p.x, "y": p.y, "z": p.z},
|
|
37
|
-
"orientation": {"x": q.x, "y": q.y, "z": q.z, "w": q.w},
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def topic_to_camera_key(topic: str) -> str:
|
|
42
|
-
parts = topic.strip("/").split("/")
|
|
43
|
-
return parts[0] if parts else topic
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
RETRY_DELAY = 2.0
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
@proto.cli
|
|
50
|
-
def main(
|
|
51
|
-
server_uri: str = "ws://localhost:8012",
|
|
52
|
-
# topics
|
|
53
|
-
pose_topic: str = "/map/pose",
|
|
54
|
-
depth_topics: str = "", # depth (compressedDepth)
|
|
55
|
-
rgb_topics: str = "", # rgb (CompressedImage jpeg)
|
|
56
|
-
jpg_quality: int = 80,
|
|
57
|
-
):
|
|
58
|
-
"""Vuer ROS Bridge - streams map pose and depth to Vuer server."""
|
|
59
|
-
|
|
60
|
-
depth_list = depth_topics.split() if depth_topics else []
|
|
61
|
-
rgb_list = rgb_topics.split() if rgb_topics else []
|
|
62
|
-
|
|
63
|
-
print("=" * 60)
|
|
64
|
-
print("Vuer ROS Bridge")
|
|
65
|
-
print(f"Server: {server_uri}")
|
|
66
|
-
print("=" * 60)
|
|
67
|
-
|
|
68
|
-
rclpy.init()
|
|
69
|
-
node = Node("vuer_ros_bridge")
|
|
70
|
-
|
|
71
|
-
sensor_qos = QoSProfile(
|
|
72
|
-
reliability=ReliabilityPolicy.BEST_EFFORT,
|
|
73
|
-
history=HistoryPolicy.KEEP_LAST,
|
|
74
|
-
depth=1,
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
threading.Thread(target=rclpy.spin, args=(node,), daemon=True).start()
|
|
78
|
-
|
|
79
|
-
async def run():
|
|
80
|
-
loop = asyncio.get_event_loop()
|
|
81
|
-
|
|
82
|
-
pose_queue: asyncio.Queue = asyncio.Queue(maxsize=1)
|
|
83
|
-
depth_queue: asyncio.Queue = asyncio.Queue(maxsize=10)
|
|
84
|
-
|
|
85
|
-
tf_history: deque[dict] = deque(maxlen=50) # ~0.5s at 100Hz
|
|
86
|
-
tf_lock = threading.Lock()
|
|
87
|
-
frame_buffer: dict[str, dict] = defaultdict(dict)
|
|
88
|
-
buffer_lock = threading.Lock()
|
|
89
|
-
rgb_cameras = {topic_to_camera_key(t) for t in rgb_list}
|
|
90
|
-
|
|
91
|
-
def push(queue: asyncio.Queue, item):
|
|
92
|
-
try:
|
|
93
|
-
loop.call_soon_threadsafe(queue.put_nowait, item)
|
|
94
|
-
except asyncio.QueueFull:
|
|
95
|
-
pass
|
|
96
|
-
|
|
97
|
-
def find_closest_tf(target_ns: int) -> dict:
|
|
98
|
-
with tf_lock:
|
|
99
|
-
if not tf_history:
|
|
100
|
-
return {}
|
|
101
|
-
return min(tf_history, key=lambda t: abs(t["timestamp"] - target_ns))
|
|
102
|
-
|
|
103
|
-
# -- ROS Callbacks --
|
|
104
|
-
|
|
105
|
-
def on_pose(msg: PoseStamped):
|
|
106
|
-
tf = pose_to_tf(msg)
|
|
107
|
-
with tf_lock:
|
|
108
|
-
tf_history.append(tf)
|
|
109
|
-
push(pose_queue, tf)
|
|
110
|
-
|
|
111
|
-
def on_image(msg: CompressedImage, topic: str, field: str):
|
|
112
|
-
"""Handle both depth and rgb. field is 'depth' or 'rgb'."""
|
|
113
|
-
if not msg.data:
|
|
114
|
-
return
|
|
115
|
-
camera_key = topic_to_camera_key(topic)
|
|
116
|
-
data = (
|
|
117
|
-
compressed_depth_to_jpeg(bytes(msg.data), jpg_quality)
|
|
118
|
-
if field == "depth"
|
|
119
|
-
else bytes(msg.data)
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
with buffer_lock:
|
|
123
|
-
frame_buffer[camera_key][field] = data
|
|
124
|
-
frame_buffer[camera_key]["timestamp"] = stamp_to_ns(msg.header.stamp)
|
|
125
|
-
|
|
126
|
-
buf = frame_buffer[camera_key]
|
|
127
|
-
if "depth" not in buf:
|
|
128
|
-
return
|
|
129
|
-
if camera_key in rgb_cameras and "rgb" not in buf:
|
|
130
|
-
return
|
|
131
|
-
|
|
132
|
-
tf = find_closest_tf(buf["timestamp"])
|
|
133
|
-
payload = {"camera_key": camera_key, "depth": buf["depth"], "baselink/tf": tf}
|
|
134
|
-
if "rgb" in buf:
|
|
135
|
-
payload["rgb"] = buf["rgb"]
|
|
136
|
-
frame_buffer[camera_key] = {}
|
|
137
|
-
|
|
138
|
-
push(depth_queue, payload)
|
|
139
|
-
|
|
140
|
-
# -- Subscribe --
|
|
141
|
-
|
|
142
|
-
if pose_topic:
|
|
143
|
-
node.create_subscription(PoseStamped, pose_topic, on_pose, sensor_qos)
|
|
144
|
-
|
|
145
|
-
for topic in depth_list:
|
|
146
|
-
node.create_subscription(
|
|
147
|
-
CompressedImage,
|
|
148
|
-
topic,
|
|
149
|
-
lambda msg, t=topic: on_image(msg, t, "depth"),
|
|
150
|
-
sensor_qos,
|
|
151
|
-
)
|
|
152
|
-
|
|
153
|
-
for topic in rgb_list:
|
|
154
|
-
node.create_subscription(
|
|
155
|
-
CompressedImage,
|
|
156
|
-
topic,
|
|
157
|
-
lambda msg, t=topic: on_image(msg, t, "rgb"),
|
|
158
|
-
sensor_qos,
|
|
159
|
-
)
|
|
160
|
-
|
|
161
|
-
# -- Async Handlers --
|
|
162
|
-
|
|
163
|
-
async def pose_handler(client: VuerClient):
|
|
164
|
-
sent = 0
|
|
165
|
-
while True:
|
|
166
|
-
tf = await pose_queue.get()
|
|
167
|
-
while not pose_queue.empty():
|
|
168
|
-
try:
|
|
169
|
-
tf = pose_queue.get_nowait()
|
|
170
|
-
except asyncio.QueueEmpty:
|
|
171
|
-
break
|
|
172
|
-
await client.send(MapPose(value=tf))
|
|
173
|
-
sent += 1
|
|
174
|
-
if sent % 500 == 0:
|
|
175
|
-
print(f"[Pose] Sent {sent}")
|
|
176
|
-
|
|
177
|
-
async def depth_handler(client: VuerClient):
|
|
178
|
-
sent = 0
|
|
179
|
-
while True:
|
|
180
|
-
payload = await depth_queue.get()
|
|
181
|
-
await client.send(DepthMap(value=payload))
|
|
182
|
-
sent += 1
|
|
183
|
-
if sent % 50 == 0:
|
|
184
|
-
print(f"[Depth] Sent {sent} (cam: {payload.get('camera_key', '?')})")
|
|
185
|
-
|
|
186
|
-
# -- Connect with retry --
|
|
187
|
-
|
|
188
|
-
while True:
|
|
189
|
-
try:
|
|
190
|
-
print(f"\n[Vuer] Connecting to {server_uri}...")
|
|
191
|
-
async with VuerClient(uri=server_uri) as client:
|
|
192
|
-
print("[Vuer] Connected!")
|
|
193
|
-
await asyncio.gather(pose_handler(client), depth_handler(client))
|
|
194
|
-
except (ConnectionRefusedError, OSError) as e:
|
|
195
|
-
print(f"[Vuer] Connection failed: {e}, retrying in {RETRY_DELAY}s...")
|
|
196
|
-
await asyncio.sleep(RETRY_DELAY)
|
|
197
|
-
except asyncio.CancelledError:
|
|
198
|
-
break
|
|
199
|
-
|
|
200
|
-
try:
|
|
201
|
-
asyncio.run(run())
|
|
202
|
-
except KeyboardInterrupt:
|
|
203
|
-
print("\nShutting down...")
|
|
204
|
-
finally:
|
|
205
|
-
node.destroy_node()
|
|
206
|
-
rclpy.shutdown()
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
if __name__ == "__main__":
|
|
210
|
-
main()
|
vuer_cli-0.0.4.dist-info/RECORD
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
vuer_cli/__init__.py,sha256=IyB44__IHu_PHuviTe8EULikOAJ45k-52kWbNpF8Lo0,387
|
|
2
|
-
vuer_cli/add.py,sha256=oO8b4qzp-Trgo1ePWDLxI1pMf6QRwKEqpyOhHfohOic,2677
|
|
3
|
-
vuer_cli/envs_publish.py,sha256=mNqdEs1uhwtfRK_IwmrGkdTUfxhfveE54ckCQG4Pt_c,12793
|
|
4
|
-
vuer_cli/envs_pull.py,sha256=RnaZZVjazxSktq7Y_9VFqpXGqP_GeCGkJSO_fYJa17k,7209
|
|
5
|
-
vuer_cli/main.py,sha256=HqAj-fxM9IQezMjCAPCyghsz4eQtALyhjBoe3FO-Kj8,2305
|
|
6
|
-
vuer_cli/mcap_extractor.py,sha256=zQEaiWhmfEDSf0Tej7J0arkAxTHjCDaW73fb_OjHwNA,35529
|
|
7
|
-
vuer_cli/remove.py,sha256=ZxWtAbyW4FSNuD6SS8rss-Nb1zjKgHgvUrdxY2d_ugs,3795
|
|
8
|
-
vuer_cli/sync.py,sha256=5sKx9F1wwJpN-XRx_EpRvUSpAeFOw1RSOqfCvqvTtj4,13971
|
|
9
|
-
vuer_cli/upgrade.py,sha256=mRFXtDYBqAfK0LFT63XNlpCBf37bPSgOaVZ_KQXK6pM,5293
|
|
10
|
-
vuer_cli/utils.py,sha256=L7r1Lm-j6n7NqVjUJm5n8id7ZKuPrkOH3x-tP6BNx74,2670
|
|
11
|
-
vuer_cli/scripts/demcap.py,sha256=SenkpxQJlIB8DY4Q-5eoTjIW8nUqNKRMyPodSocqKGU,5386
|
|
12
|
-
vuer_cli/scripts/minimap.py,sha256=wXAQIdqL4beyCmEpKXBbfvqsN4Nq8fql8n6XUBuvReM,13518
|
|
13
|
-
vuer_cli/scripts/ptc_utils.py,sha256=T55BY3plXwxtZTvbpesThYvE6vZB6aBT_iklwWjXg2Y,15018
|
|
14
|
-
vuer_cli/scripts/viz_ptc_cams.py,sha256=N8D_szgGrsypl-_AKy53JV0XIO8vZKbPsYTHHBRTycc,21057
|
|
15
|
-
vuer_cli/scripts/viz_ptc_proxie.py,sha256=VK6b4pT4cWME-yyltVktDpMqTKxwVRBWff9QlReB9Pg,14400
|
|
16
|
-
vuer_cli/scripts/vuer_ros_bridge.py,sha256=fHSbka_Khsg5IfagDJ95S9ftgIv4EvpV-2w5XyLW_Og,5667
|
|
17
|
-
vuer_cli-0.0.4.dist-info/METADATA,sha256=QlpEfoJ0dmDDCxYLQQv1w8gu7-19IJKyv96jcBtahGk,9582
|
|
18
|
-
vuer_cli-0.0.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
19
|
-
vuer_cli-0.0.4.dist-info/entry_points.txt,sha256=pH846erbxBfJpIj151Ps_UzgWIYPeIjl5gxZyTZn_7E,45
|
|
20
|
-
vuer_cli-0.0.4.dist-info/licenses/LICENSE,sha256=MGF-inVBUaGe2mEjqT0g6XsHIXwoNXgNHqD7Z1MzR0k,1063
|
|
21
|
-
vuer_cli-0.0.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|