ros-tviewer 0.1.0__tar.gz

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.
@@ -0,0 +1,9 @@
1
+ ROS_TVIEWER_APP__DEFAULT_TARGET=world
2
+ ROS_TVIEWER_LOGGING__LEVEL=WARNING
3
+ ROS_TVIEWER_CAMERA__TOPIC=/camera/image_raw
4
+ ROS_TVIEWER_CAMERA__FPS=30
5
+ ROS_TVIEWER_CAMERA__ROTATE=0
6
+ ROS_TVIEWER_CAMERA__STRETCH=false
7
+ ROS_TVIEWER_CAMERA__COMPRESSED=false
8
+ ROS_TVIEWER_CAMERA__TIMEOUT=5.0
9
+ ROS_TVIEWER_CAMERA__FRAMES=0
@@ -0,0 +1,42 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ check:
9
+ runs-on: ubuntu-latest
10
+ permissions:
11
+ contents: read
12
+ steps:
13
+ - name: Checkout
14
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
15
+ with:
16
+ persist-credentials: false
17
+
18
+ - name: Install uv
19
+ uses: astral-sh/setup-uv@8d55fbecc275b1c35dbe060458839f8d30439ccf # v3
20
+
21
+ - name: Install system dependencies
22
+ # tcamviewer sdist 빌드에 cmake + FFmpeg dev 라이브러리 필요
23
+ run: |
24
+ sudo apt-get update
25
+ sudo apt-get install -y --no-install-recommends cmake libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev
26
+
27
+ - name: Install dependencies
28
+ run: |
29
+ if [ -f uv.lock ]; then
30
+ uv sync --locked --group dev
31
+ else
32
+ uv sync --group dev
33
+ fi
34
+
35
+ - name: Lint
36
+ run: uv run ruff check .
37
+
38
+ - name: Format
39
+ run: uv run ruff format --check .
40
+
41
+ - name: Test
42
+ run: uv run pytest
@@ -0,0 +1,38 @@
1
+ name: publish
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch: # 수동 재배포 (릴리스 재실행은 원본 워크플로우 SHA를 재사용함)
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+ permissions:
12
+ contents: read
13
+ # PyPI Trusted Publishing (OIDC) — 비밀 토큰 불필요
14
+ id-token: write
15
+ steps:
16
+ - name: Checkout
17
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
18
+ with:
19
+ persist-credentials: false
20
+
21
+ - name: Install uv
22
+ uses: astral-sh/setup-uv@8d55fbecc275b1c35dbe060458839f8d30439ccf # v3
23
+
24
+ - name: Install system dependencies
25
+ # tcamviewer sdist 빌드에 cmake + FFmpeg dev 라이브러리 필요
26
+ run: |
27
+ sudo apt-get update
28
+ sudo apt-get install -y cmake libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev
29
+
30
+ - name: Build
31
+ run: uv build
32
+ env:
33
+ UV_NO_CACHE: "1" # 캐시 독립 빌드 (cache poisoning 공격 표면 제거)
34
+
35
+ - name: Publish to PyPI
36
+ # v1.14.2 — 이전 버전(v1.12.x)의 twine은 hatchling이 생성하는
37
+ # Metadata-Version 2.5를 거부하므로 신규 버전 유지 필수
38
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
@@ -0,0 +1,116 @@
1
+ # --- OS Specific ---
2
+ # macOS
3
+ .DS_Store
4
+ .AppleDouble
5
+ .LSOverride
6
+ Icon
7
+ ._*
8
+ .DocumentRevisions-V100
9
+ .fseventsd
10
+ .Spotlight-V100
11
+ .TemporaryItems
12
+ .Trashes
13
+ .VolumeIcon.icns
14
+ .com.apple.timemachine.donotpresent
15
+
16
+ # Windows
17
+ Thumbs.db
18
+ Thumbs.db:encryptable
19
+ ehthumbs.db
20
+ ehthumbs_vista.db
21
+ *.stackdump
22
+ [Dd]esktop.ini
23
+ $RECYCLE.BIN/
24
+ *.cab
25
+ *.msi
26
+ *.msix
27
+ *.msm
28
+ *.msp
29
+ *.lnk
30
+
31
+ # Linux
32
+ *~
33
+ .fuse_hidden*
34
+ .directory
35
+ .Trash-*
36
+ .nfs*
37
+
38
+ # --- Python General ---
39
+ __pycache__/
40
+ *.py[cod]
41
+ *$py.class
42
+ *.so
43
+ .Python
44
+ build/
45
+ develop-eggs/
46
+ dist/
47
+ downloads/
48
+ eggs/
49
+ .eggs/
50
+ lib/
51
+ lib64/
52
+ parts/
53
+ sdist/
54
+ var/
55
+ wheels/
56
+ share/python-wheels/
57
+ *.egg-info/
58
+ .installed.cfg
59
+ *.egg
60
+ MANIFEST
61
+
62
+ # --- Environments & Secrets ---
63
+ .env
64
+ .venv
65
+ env/
66
+ venv/
67
+ ENV/
68
+ env.bak/
69
+ venv.bak/
70
+ # Keep .env.example for documentation
71
+
72
+ # --- Tooling & Linting ---
73
+ # Ruff
74
+ .ruff_cache/
75
+
76
+ # Mypy / Pyright
77
+ .mypy_cache/
78
+ .dmypy.json
79
+ dmypy.json
80
+ # pyrightconfig.json is usually kept if shared, but can be ignored if local-only
81
+ # pyrightconfig.json
82
+
83
+ # Pytest / Coverage
84
+ .pytest_cache/
85
+ .coverage
86
+ .coverage.*
87
+ htmlcov/
88
+ .tox/
89
+ .nox/
90
+ nosetests.xml
91
+ coverage.xml
92
+ *.cover
93
+ .hypothesis/
94
+ cover/
95
+
96
+ # --- Package Managers (uv / Poetry / PDM) ---
97
+ # uv: uv.lock should be committed for reproducibility
98
+ .pdm.toml
99
+ __pypackages__/
100
+ poetry.toml
101
+
102
+ # --- IDEs ---
103
+ # Visual Studio Code
104
+ .vscode/*
105
+ !.vscode/settings.json
106
+ !.vscode/tasks.json
107
+ !.vscode/launch.json
108
+ !.vscode/extensions.json
109
+ !.vscode/*.code-snippets
110
+ .history/
111
+
112
+ # PyCharm / JetBrains
113
+ .idea/
114
+
115
+ # --- Project Specific ---
116
+ *.log
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,153 @@
1
+ # AGENTS.md — ros-tviewer 개발 가이드
2
+
3
+ ROS 2 카메라 토픽을 터미널에서 재생하는 CLI 뷰어. 이 문서는 에이전트(및 개발자)가
4
+ **일관성 있게** 개발하기 위한 아키텍처 명세와 TDD 규칙이다. 코드 변경 전 반드시 읽는다.
5
+
6
+ ---
7
+
8
+ ## 1. 프로젝트 개요
9
+
10
+ | 항목 | 내용 |
11
+ | --- | --- |
12
+ | 목적 | ROS 2 `sensor_msgs/msg/Image`, `CompressedImage` 토픽을 터미널(ANSI TrueColor Half-Block)로 실시간 재생 |
13
+ | 렌더링 | [tcamviewer](https://pypi.org/project/tcamviewer/) `>=0.2.3` (C-ABI 코어 + ctypes 바인딩, PyPI 배포됨) |
14
+ | CLI | `wpycli` (cobra 스타일 Command), 설정 `wpyconf`, 로깅 `wpylog` |
15
+ | 실행 | `uv run ros-tviewer play /camera/image_raw` 또는 ROS env source 후 `uvx ros-tviewer ...` |
16
+ | Python | 3.12 (ROS 2 Jazzy ABI 매칭 — `requires-python = ">=3.12"`) |
17
+ | 테스트 | `pytest` (단위), E2E는 ROS 환경에서 `bash tests/e2e.sh` |
18
+
19
+ ## 2. 아키텍처
20
+
21
+ ```text
22
+ ┌──────────────────────────────┐
23
+ │ main.py (wpycli Command) │ play / topics / config
24
+ └──────┬───────────────────────┘
25
+ │ 1) ros_env.ensure_rclpy() ← 반드시 가장 먼저
26
+ ▼
27
+ ┌──────────────────────────────┐
28
+ │ ros_env.py │ rclpy 부트스트랩
29
+ │ (시스템 ROS 탐지 + re-exec) │ (pip 설치 불가 → 시스템 전용)
30
+ └──────┬───────────────────────┘
31
+ ▼
32
+ ┌──────────────────────────────┐
33
+ │ node.py (CameraViewer) │ rclpy 구독 + 스로틀 + 렌더 루프
34
+ │ - 최신 프레임만 유지(Drop) │ QoS: sensor_data (depth 1)
35
+ └──────┬───────────────────────┘
36
+ ▼
37
+ ┌────────────────────┴────────────────────┐
38
+ ▼ ▼
39
+ ┌──────────────────┐ ┌──────────────────────┐
40
+ │ convert.py │ │ tcamviewer (PyPI) │
41
+ │ Image/Compressed│ ── RGB24 numpy ──▶│ TerminalRenderer │
42
+ │ → RGB24 (H,W,3) │ │ render_rgb() │
43
+ └──────────────────┘ └──────────────────────┘
44
+ numpy + opencv만 사용. cv_bridge 사용 금지.
45
+ ```
46
+
47
+ ### 모듈 계약 (변경 금지 원칙)
48
+
49
+ | 모듈 | 책임 | 금지 사항 |
50
+ | --- | --- | --- |
51
+ | `main.py` | wpycli 커맨드 정의, ctx → 파라미터 변환 | ROS/rclpy 직접 임포트 금지, 렌더 로직 금지 |
52
+ | `ros_env.py` | rclpy 가용성 확인, sys.path 주입, 환경변수 구성 후 `os.execve` 재실행 | rclpy를 모듈 레벨에서 import 금지(항상 함수 내 지연 임포트) |
53
+ | `node.py` | rclpy 노드/구독/스핀 루프, 프레임 슬롯·레이트리미터 | 픽셀 변환 로직 금지(convert에 위임), 렌더러 생성만 |
54
+ | `convert.py` | 메시지 → RGB24 numpy. 순수 함수. rclpy 무의존 (테스트 가능) | rclpy/노드 상태 금지. cv2 임포트는 함수 내부 지연. 채널 재배치는 cv2.cvtColor 사용 |
55
+ | `tests/` | 단위 테스트는 rclpy 없이 실행 가능해야 함 | ROS 미설치 환경에서 fail 금지 → `skipif` 사용 |
56
+
57
+ ### 데이터 규약
58
+
59
+ - **내부 프레임 표준**: `numpy.ndarray`, `dtype=uint8`, `shape=(H, W, 3)`, **RGB24**, C-contiguous.
60
+ - 렌더러가 BGR을 원하면 `render_bgr`을 쓰지 말고 convert에서 RGB로 통일한다.
61
+ - `convert.image_to_rgb(data, encoding, width, height, step)`는 순수 함수 — 부작용 없음.
62
+ - 지원 encoding: `rgb8`, `bgr8`, `rgba8`, `bgra8`, `mono8`, `8UC1/3/4`. 추가 시
63
+ `_SUPPORTED_ENCODINGS` 테이블 + 테스트를 **함께** 추가한다 (TDD).
64
+ - 지원하지 않는 encoding은 `UnsupportedEncodingError`, 데이터 손상은 `FrameDecodeError`.
65
+
66
+ ### rclpy 다루기 (중요)
67
+
68
+ - **rclpy는 pip 설치 불가** — 시스템 ROS 2(`/opt/ros/<distro>`)에만 존재.
69
+ - 규칙:
70
+ 1. rclpy import는 **함수 내부 지연 임포트**만 허용 (`ros_env.ensure_rclpy()` 이후).
71
+ 2. CLI 런 핸들러 첫 줄에서 `ensure_rclpy()` 호출. 미설치 환경에서는
72
+ 명확한 `_HELP_MESSAGE`와 함께 `RuntimeError`.
73
+ 3. `ensure_rclpy()`는 find_spec 기반 확인 → sys.path 주입 → 실패 시 환경변수 구성 후
74
+ **re-exec**(`os.execve`, 마커 `ROS_TVIEWER_REEXEC`로 무한루프 방지) 순서를 유지.
75
+ 4. ruff/pyright: `import rclpy` 라인에는 `# type: ignore[import-not-found]` 허용.
76
+
77
+ ## 3. TDD 규칙 (Red → Green → Refactor)
78
+
79
+ 1. **새 기능 = 실패하는 테스트부터**. 구현 전 테스트가 반드시 `pytest`에서 fail/red를
80
+ 확인한 뒤 구현한다.
81
+ 2. 테스트 분류:
82
+ - `tests/test_convert.py` — 변환 순수함수. rclpy 불필요, 항상 실행.
83
+ - `tests/test_main.py` — CLI 커맨드/플래그 구조. rclpy 불필요, 항상 실행.
84
+ - `tests/test_ros_env.py` — 부트스트랩. `os.execve`는 **반드시 monkeypatch**로 가로챔
85
+ (테스트 프로세스 교체 방지).
86
+ - `tests/test_ros2_e2e.py` — 실제 rclpy 구독/렌더. `pytest.mark.skipif`:
87
+ rclpy 미탑재 환경에서는 skip. 서브프로세스로 `source /opt/ros/*/setup.bash` 후 실행.
88
+ 3. Fake 메시지: `sensor_msgs`를 임포트하지 말고 `types.SimpleNamespace`로
89
+ `data/encoding/width/height/step` 필드를 만든다 (`tests/fakes.py` 헬퍼 재사용).
90
+ 4. 커버리지 목표: convert 100%, ros_env 분기 커버, main 플래그 파싱 스냅샷.
91
+ 5. 커밋 단위: Red(테스트 추가) → Green(최소 구현) → Refactor → `uv run ruff check .` → 반복.
92
+
93
+ ## 4. 명령어
94
+
95
+ ```bash
96
+ uv sync # 의존성 설치 (tcamviewer는 sdist → 시스템에 cmake+FFmpeg dev 필요)
97
+ uv run pytest # 단위 테스트
98
+ uv run pytest tests/test_ros2_e2e.py -v # ROS E2E (source 없이도 skip만 됨)
99
+ uv run ruff check . && uv run ruff format --check . # 린트/포맷
100
+ bash tests/e2e.sh # ROS 환경 E2E (더미 퍼블리셔 → play 60프레임)
101
+ uv run ros-tviewer play /camera/image_raw # 실행
102
+ ```
103
+
104
+ ## 5. 설정 참조 (wpyconf)
105
+
106
+ `config.toml` / `.env` / CLI 플래그 우선순위: **CLI > env > file > defaults**.
107
+
108
+ | 키 (config) | env | CLI 플래그 | 기본값 |
109
+ | --- | --- | --- | --- |
110
+ | `camera.topic` | `ROS_TVIEWER_CAMERA__TOPIC` | (위치인자 `[topic]`) | `/camera/image_raw` |
111
+ | `camera.fps` | `ROS_TVIEWER_CAMERA__FPS` | `--fps/-f` | 30 |
112
+ | `camera.rotate` | `ROS_TVIEWER_CAMERA__ROTATE` | `--rotate/-r` | 0 |
113
+ | `camera.stretch` | `ROS_TVIEWER_CAMERA__STRETCH` | `--stretch/-s` | false |
114
+ | `camera.compressed` | `ROS_TVIEWER_CAMERA__COMPRESSED` | `--compressed/-z` | false(→None, 자동감지) |
115
+ | `camera.timeout` | `ROS_TVIEWER_CAMERA__TIMEOUT` | `--timeout/-t` | 5.0 |
116
+ | `camera.frames` | `ROS_TVIEWER_CAMERA__FRAMES` | `--frames/-n` | 0(무제한) |
117
+ | `logging.level` | `ROS_TVIEWER_LOGGING__LEVEL` | `--log-level` | WARNING |
118
+
119
+ - `compressed`는 트라이스테이트다: 플래그/설정 true → 강제, 미지정 → **None(자동 감지)**.
120
+ bool로 강제하면 CompressedImage 토픽에 Image로 구독하는 미스매치로 무한 대기한다 (E2E 회귀 1회).
121
+ - 로그는 터미널 렌더를 오염시키므로 **기본 WARNING 이상** 유지. 렌더 루프 안에서 로그 금지.
122
+ - 새 플래그 추가 시: main.py 플래그 + defaults + config.toml + .env.example + AGENTS.md 표 — 5곳 동시 갱신.
123
+
124
+ ## 6. 실행 모델 & 성능
125
+
126
+ - 구독 QoS: `qos_profile_sensor_data` (best effort, depth 1) — 프레임 밀림 방지.
127
+ - 콜백은 **변환 없이 원본 메시지만** `FrameSlot`에 저장(drop)한다. 픽셀 변환은 렌더 직전에만
128
+ 수행 — 수신률이 렌더율보다 높으면 수신 프레임 상당수가 렌더 없이 버려지므로, 변환 비용을
129
+ 렌더량에 비례시킨다.
130
+ - 손상 프레임(`FrameDecodeError`/`UnsupportedEncodingError`)은 렌더 루프에서 스킵하고
131
+ 종료 시 `bad_frames` 카운트만 로그에 남긴다.
132
+ - FPS 스로틀은 `RateLimiter`(모노토닉 클럭)로 구현 — `time.sleep` 사용 금지.
133
+ - 변환 성능(1080p, ms/frame): rgb8 0.2 / bgr8 0.3 / rgba·bgra 0.8 / mono 0.25 /
134
+ CompressedImage(jpeg) ~20(디코딩 자체가 지배). 채널 재배치에 numpy 음수 스트라이드 복사
135
+ (예: `[..., ::-1]`)을 쓰지 않는다 — `cv2.cvtColor`(SIMD)가 수십 배 빠르다.
136
+ - 터미널 리사이즈: 매 프레임 `get_terminal_size()` 비교, 변경 시 `renderer.resize()`.
137
+ - 렌더러는 `use_diff=True, alt_screen=True, hide_cursor=True` 고정.
138
+ 종료 시(Signal 포함) `renderer.close()` 보장 — 터미널 복구 필수.
139
+
140
+ ## 7. 자주 하는 실수 (Do NOT)
141
+
142
+ - ❌ 모듈 레벨 `import rclpy` → ROS 미설치 머신의 단위 테스트 전부 붕괴.
143
+ - ❌ `cv_bridge` 사용 → ROS 전용 의존성, uvx 실행이 깨짐. convert.py (numpy/cv2)만 사용.
144
+ - ❌ BGR 프레임을 `render_rgb`로 전달 → 색 반전. RGB24로 정규화 후 전달.
145
+ - ❌ 렌더 콜백 안에서 `print`/`logging.info` → 화면 깨짐.
146
+ - ❌ `except Exception: pass` 같은 무시 처리 → 에러는 명시적 예외 타입 + 안내 메시지.
147
+ - ❌ `time.sleep` 기반 프레임 페이싱 → 지연 누적. 모노토닉 기반 RateLimiter 사용.
148
+
149
+ ## 8. 배포
150
+
151
+ - `uv build` → `uv publish` (PyPI, 패키지명 `ros_tviewer` / 실행명 `ros-tviewer`).
152
+ - 배포 전 체크리스트: `uv run pytest`, `uv run ruff check .`, `bash tests/e2e.sh`, README 실행 예시 최신화.
153
+ - tcamviewer 하위호환: 구버전(≤0.2.2) 휠의 `.so` 이중 중첩 버그가 있으므로 하한 `>=0.2.3` 고정.
@@ -0,0 +1,28 @@
1
+ # Changelog
2
+
3
+ 이 프로젝트의 모든 주목할 만한 변경 사항이 이 파일에 기록됩니다.
4
+
5
+ 형식은 [Keep a Changelog](https://keepachangelog.com/ko/1.1.0/)을 따르며,
6
+ 버전 관리는 [Semantic Versioning](https://semver.org/lang/ko/)을 따릅니다.
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+
12
+ - `play` 커맨드: ROS 2 카메라 토픽(Image/CompressedImage)을 tcamviewer
13
+ Half-Block TrueColor 렌더러로 터미널에 실시간 재생 — `--fps`, `--rotate`,
14
+ `--stretch`, `--compressed`, `--timeout`, `--frames` 플래그
15
+ - `topics` 커맨드: 광고 중인 sensor_msgs 카메라 토픽 나열
16
+ - `config` / `version` 커맨드
17
+ - 최신 프레임 우선(drop) 구독 + 모노토닉 FPS 스로틀, 렌더 직전 변환
18
+ - 시스템 ROS 2 자동 부트스트랩 (sys.path 주입 → re-exec)
19
+ - 단위 테스트(ROS 불필요), ROS E2E 테스트, CI (ruff lint/format + pytest)
20
+
21
+ ## [0.1.0] - 2025-09-09
22
+
23
+ ### Added
24
+
25
+ - 최초 릴리스 (위 Unreleased 항목 포함)
26
+
27
+ [Unreleased]: https://github.com/wkqco33/ros_tviewer/compare/v0.1.0...HEAD
28
+ [0.1.0]: https://github.com/wkqco33/ros_tviewer/releases/tag/v0.1.0
@@ -0,0 +1,33 @@
1
+ # 기여 가이드 (Contributing)
2
+
3
+ ros-tviewer에 관심을 가져주셔서 감사합니다. 아래 규칙을 따라주시면 리뷰가 빨라집니다.
4
+
5
+ ## 개발 환경
6
+
7
+ - Python 3.12, [uv](https://docs.astral.sh/uv/), ROS 2 (Jazzy/Humble, 테스트 실행 시)
8
+ - 빌드 도구: `cmake`, FFmpeg dev 라이브러리 (`libavcodec-dev` 등) — tcamviewer sdist 빌드용
9
+
10
+ ```bash
11
+ uv sync # 의존성 설치
12
+ uv run pytest # 단위 테스트 (ROS 미설치 환경에서도 실행 가능)
13
+ uv run ruff check . # 린트
14
+ uv run ruff format --check . # 포맷 검사
15
+ uvx pyright --project . # 타입 체크
16
+ bash tests/e2e.sh # ROS 환경 E2E (더미 퍼블리셔 → play 60프레임)
17
+ ```
18
+
19
+ ## 규칙
20
+
21
+ - **[AGENTS.md](AGENTS.md)를 먼저 읽어주세요** — 아키텍처·모듈 계약·TDD 규칙이 정리되어 있습니다.
22
+ - 새 기능은 **실패하는 테스트부터** 추가합니다 (Red → Green → Refactor).
23
+ - 단위 테스트는 rclpy/sensor_msgs 없이 실행 가능해야 합니다 (`tests/fakes.py` 헬퍼 사용).
24
+ rclpy가 필요한 경로는 `pytest.mark.skipif` 또는 서브프로세스 E2E로 검증합니다.
25
+ - 모듈 레벨 `import rclpy` 금지 (함수 내부 지연 임포트만 허용).
26
+ - 렌더 루프 안에서 `print`/로깅 금지 (터미널 오염).
27
+ - 커밋 전에 린트·포맷·테스트를 모두 통과해야 합니다.
28
+
29
+ ## PR 프로세스
30
+
31
+ 1. 이슈를 먼저 생성해 방향을 맞추는 것을 권장합니다.
32
+ 2. 작은 단위의 PR을 선호합니다 (한 PR = 한 목적).
33
+ 3. PR 설명에 변경 요약과 검증 방법(`pytest`, E2E 등)을 적어주세요.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 seo.youngchae
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,124 @@
1
+ Metadata-Version: 2.5
2
+ Name: ros_tviewer
3
+ Version: 0.1.0
4
+ Summary: ROS 2 카메라 토픽을 터미널에서 재생하는 뷰어
5
+ Project-URL: Homepage, https://github.com/wkqco33/ros_tviewer
6
+ Project-URL: Repository, https://github.com/wkqco33/ros_tviewer
7
+ Project-URL: Issues, https://github.com/wkqco33/ros_tviewer/issues
8
+ Project-URL: Changelog, https://github.com/wkqco33/ros_tviewer/blob/main/CHANGELOG.md
9
+ Author: seo.youngchae
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: camera,rclpy,ros2,terminal,tui,viewer
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Operating System :: POSIX :: Linux
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Multimedia :: Video :: Capture
19
+ Classifier: Topic :: Terminals
20
+ Requires-Python: >=3.12
21
+ Requires-Dist: numpy
22
+ Requires-Dist: opencv-python-headless
23
+ Requires-Dist: tcamviewer>=0.2.3
24
+ Requires-Dist: wpycli
25
+ Requires-Dist: wpyconf
26
+ Requires-Dist: wpylog
27
+ Description-Content-Type: text/markdown
28
+
29
+ # ros-tviewer
30
+
31
+ [![CI](https://github.com/wkqco33/ros_tviewer/actions/workflows/ci.yml/badge.svg)](https://github.com/wkqco33/ros_tviewer/actions/workflows/ci.yml)
32
+ [![PyPI](https://img.shields.io/pypi/v/ros-tviewer)](https://pypi.org/project/ros-tviewer/)
33
+ [![Python](https://img.shields.io/badge/python-3.12%2B-blue)](https://www.python.org/)
34
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
35
+
36
+ ROS 2 카메라 토픽을 터미널에서 재생하는 CLI 뷰어.
37
+
38
+ [sensor_msgs/msg/Image](https://docs.ros.org/) 및 `sensor_msgs/msg/CompressedImage` 토픽을
39
+ [tcamviewer](https://pypi.org/project/tcamviewer/)의 Half-Block TrueColor(24-bit ANSI) 렌더러로
40
+ 터미널에 실시간(30~60+ FPS) 재생한다.
41
+
42
+ ```text
43
+ uv run ros-tviewer play /camera/image_raw
44
+ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
45
+ ```
46
+
47
+ ## 요구 사항
48
+
49
+ | 항목 | 설명 |
50
+ | --- | --- |
51
+ | OS | Linux |
52
+ | Python | 3.12 (ROS 2 Jazzy ABI 매칭) |
53
+ | ROS 2 | Jazzy/Humble (`rclpy`는 시스템 ROS에서 로드 — pip 설치 불가) |
54
+ | 빌드 도구 | `cmake`, FFmpeg dev 라이브러리 (`libavcodec-dev` 등) — tcamviewer sdist 빌드용 |
55
+ | 터미널 | TrueColor 지원 (`echo $COLORTERM` → `truecolor`/`24bit`) |
56
+
57
+ ## 시작하기
58
+
59
+ ### 설치
60
+
61
+ ```bash
62
+ # uv tool (권장)
63
+ uv tool install ros-tviewer
64
+
65
+ # 또는 pipx / pip
66
+ pipx install ros-tviewer
67
+ pip install ros-tviewer
68
+ ```
69
+
70
+ PyPI 배포 전 개발 트리에서는 저장소 클론 후 실행한다:
71
+
72
+ ### 개발 트리에서 실행
73
+
74
+ ```bash
75
+ uv sync
76
+
77
+ # 1) 광고 중인 카메라 토픽 확인
78
+ uv run ros-tviewer topics
79
+
80
+ # 2) 재생 (ROS 환경 미 source 시 자동 감지·재실행)
81
+ source /opt/ros/jazzy/setup.bash # 권장 — 미실행 시 자동 부트스트랩이 시도됨
82
+ uv run ros-tviewer play /camera/image_raw
83
+
84
+ # 옵션 예시
85
+ uv run ros-tviewer play /camera/image_raw/compressed --fps 30 --rotate 90 --stretch
86
+ uv run ros-tviewer play /camera/image_raw --frames 100 # 100 프레임 렌더 후 종료 (테스트/데모)
87
+ uv run ros-tviewer config # 현재 설정 출력
88
+ uv run ros-tviewer version # 앱·의존성·런타임 리포트
89
+ ```
90
+
91
+ - `q`/`Ctrl+C`로 종료 (터미널 상태 자동 복구).
92
+ - `--compressed` 미지정 시 토픽 타입을 자동 감지한다.
93
+ - 설정 우선순위: **CLI 플래그 > `.env` > `config.toml` > 기본값**.
94
+
95
+ ## E2E 테스트
96
+
97
+ ```bash
98
+ uv run pytest # 단위 테스트 (ROS 미설치 환경에서도 실행 가능, ROS 테스트는 skip)
99
+ bash tests/e2e.sh # 더미 퍼블리셔 → play 60프레임 데모
100
+ uv run pytest tests/test_ros2_e2e.py -v # 실제 rclpy 구독 + 렌더 E2E
101
+ ```
102
+
103
+ ## 구성
104
+
105
+ | 모듈 | 책임 |
106
+ | --- | --- |
107
+ | `main.py` | wpycli 커맨드 (`play`/`topics`/`config`), 플래그·설정 파싱 |
108
+ | `ros_env.py` | rclpy 부트스트랩 (시스템 ROS 탐지 → sys.path 주입 → re-exec) |
109
+ | `node.py` | 구독(QoS sensor_data), 최신 메시지 유지(drop), FPS 스로틀, 렌더 직전 변환 루프 |
110
+ | `convert.py` | Image/CompressedImage → RGB24 numpy (numpy/opencv만 사용, cv_bridge 없이) |
111
+
112
+ ## 개발
113
+
114
+ - 아키텍처·TDD 규칙·컨벤션: **[AGENTS.md](AGENTS.md)** — 코드 변경 전 필독.
115
+ - 린트: `uv run ruff check .`
116
+ - 타입 체크: `uvx pyright --project .`
117
+ - 테스트: `uv run pytest`
118
+
119
+ ## 기여 및 보안
120
+
121
+ - 기여 방법: [CONTRIBUTING.md](CONTRIBUTING.md)
122
+ - 취약점 보고: [SECURITY.md](SECURITY.md) — 공개 이슈로 보고하지 마세요.
123
+ - 변경 이력: [CHANGELOG.md](CHANGELOG.md)
124
+ - 라이선스: [MIT](LICENSE)
@@ -0,0 +1,96 @@
1
+ # ros-tviewer
2
+
3
+ [![CI](https://github.com/wkqco33/ros_tviewer/actions/workflows/ci.yml/badge.svg)](https://github.com/wkqco33/ros_tviewer/actions/workflows/ci.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/ros-tviewer)](https://pypi.org/project/ros-tviewer/)
5
+ [![Python](https://img.shields.io/badge/python-3.12%2B-blue)](https://www.python.org/)
6
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7
+
8
+ ROS 2 카메라 토픽을 터미널에서 재생하는 CLI 뷰어.
9
+
10
+ [sensor_msgs/msg/Image](https://docs.ros.org/) 및 `sensor_msgs/msg/CompressedImage` 토픽을
11
+ [tcamviewer](https://pypi.org/project/tcamviewer/)의 Half-Block TrueColor(24-bit ANSI) 렌더러로
12
+ 터미널에 실시간(30~60+ FPS) 재생한다.
13
+
14
+ ```text
15
+ uv run ros-tviewer play /camera/image_raw
16
+ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
17
+ ```
18
+
19
+ ## 요구 사항
20
+
21
+ | 항목 | 설명 |
22
+ | --- | --- |
23
+ | OS | Linux |
24
+ | Python | 3.12 (ROS 2 Jazzy ABI 매칭) |
25
+ | ROS 2 | Jazzy/Humble (`rclpy`는 시스템 ROS에서 로드 — pip 설치 불가) |
26
+ | 빌드 도구 | `cmake`, FFmpeg dev 라이브러리 (`libavcodec-dev` 등) — tcamviewer sdist 빌드용 |
27
+ | 터미널 | TrueColor 지원 (`echo $COLORTERM` → `truecolor`/`24bit`) |
28
+
29
+ ## 시작하기
30
+
31
+ ### 설치
32
+
33
+ ```bash
34
+ # uv tool (권장)
35
+ uv tool install ros-tviewer
36
+
37
+ # 또는 pipx / pip
38
+ pipx install ros-tviewer
39
+ pip install ros-tviewer
40
+ ```
41
+
42
+ PyPI 배포 전 개발 트리에서는 저장소 클론 후 실행한다:
43
+
44
+ ### 개발 트리에서 실행
45
+
46
+ ```bash
47
+ uv sync
48
+
49
+ # 1) 광고 중인 카메라 토픽 확인
50
+ uv run ros-tviewer topics
51
+
52
+ # 2) 재생 (ROS 환경 미 source 시 자동 감지·재실행)
53
+ source /opt/ros/jazzy/setup.bash # 권장 — 미실행 시 자동 부트스트랩이 시도됨
54
+ uv run ros-tviewer play /camera/image_raw
55
+
56
+ # 옵션 예시
57
+ uv run ros-tviewer play /camera/image_raw/compressed --fps 30 --rotate 90 --stretch
58
+ uv run ros-tviewer play /camera/image_raw --frames 100 # 100 프레임 렌더 후 종료 (테스트/데모)
59
+ uv run ros-tviewer config # 현재 설정 출력
60
+ uv run ros-tviewer version # 앱·의존성·런타임 리포트
61
+ ```
62
+
63
+ - `q`/`Ctrl+C`로 종료 (터미널 상태 자동 복구).
64
+ - `--compressed` 미지정 시 토픽 타입을 자동 감지한다.
65
+ - 설정 우선순위: **CLI 플래그 > `.env` > `config.toml` > 기본값**.
66
+
67
+ ## E2E 테스트
68
+
69
+ ```bash
70
+ uv run pytest # 단위 테스트 (ROS 미설치 환경에서도 실행 가능, ROS 테스트는 skip)
71
+ bash tests/e2e.sh # 더미 퍼블리셔 → play 60프레임 데모
72
+ uv run pytest tests/test_ros2_e2e.py -v # 실제 rclpy 구독 + 렌더 E2E
73
+ ```
74
+
75
+ ## 구성
76
+
77
+ | 모듈 | 책임 |
78
+ | --- | --- |
79
+ | `main.py` | wpycli 커맨드 (`play`/`topics`/`config`), 플래그·설정 파싱 |
80
+ | `ros_env.py` | rclpy 부트스트랩 (시스템 ROS 탐지 → sys.path 주입 → re-exec) |
81
+ | `node.py` | 구독(QoS sensor_data), 최신 메시지 유지(drop), FPS 스로틀, 렌더 직전 변환 루프 |
82
+ | `convert.py` | Image/CompressedImage → RGB24 numpy (numpy/opencv만 사용, cv_bridge 없이) |
83
+
84
+ ## 개발
85
+
86
+ - 아키텍처·TDD 규칙·컨벤션: **[AGENTS.md](AGENTS.md)** — 코드 변경 전 필독.
87
+ - 린트: `uv run ruff check .`
88
+ - 타입 체크: `uvx pyright --project .`
89
+ - 테스트: `uv run pytest`
90
+
91
+ ## 기여 및 보안
92
+
93
+ - 기여 방법: [CONTRIBUTING.md](CONTRIBUTING.md)
94
+ - 취약점 보고: [SECURITY.md](SECURITY.md) — 공개 이슈로 보고하지 마세요.
95
+ - 변경 이력: [CHANGELOG.md](CHANGELOG.md)
96
+ - 라이선스: [MIT](LICENSE)