Metadata-Version: 2.4
Name: rb-photoneo
Version: 0.1.0
Summary: Robot-side client for the Photoneo Bin Picking Studio (BPS) communication protocol (TCP/IP, binary)
Author-email: hyojoonlee04 <hjb1410@hanmail.net>
License-Expression: MIT
Project-URL: Homepage, https://github.com/hyojoonlee04/Photoneo
Project-URL: Repository, https://github.com/hyojoonlee04/Photoneo
Keywords: photoneo,bin-picking,robotics,machine-vision
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Scientific/Engineering
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: PyYAML>=6.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: mypy>=1.0; extra == "dev"
Requires-Dist: Nuitka>=2.0; extra == "dev"
Requires-Dist: twine>=5.0; extra == "dev"
Requires-Dist: auditwheel>=6.0; extra == "dev"
Dynamic: license-file

# photoneo_vision

로봇 측에서 Photoneo **Bin Picking Studio(BPS) 1.11.0**의 통신 프로토콜
(TCP/IP, 바이너리)에 접속해 Bin Picking 작업(초기화/스캔/트래젝토리 조회)과
솔루션 관리(변경/시작/중지/조회)를 수행하기 위한 Python 패키지입니다.
`../MechMind/` (`mechmind_vision`)와 동일한 계층 구조 철학을 따르지만,
Photoneo BPS는 ASCII가 아닌 바이너리·다중 채널 프로토콜이라 세부 구현은
다릅니다 — 자세한 배경은 [docs/implementation_plan.md](docs/implementation_plan.md)
참고.

## 아키텍처

```
robot program
     │
     ▼
vision/        AbstractVisionClient (ABC) ─ PhotoneoClient
     │                                          │
     ▼                                          ▼
protocol/      codec.py (encode/decode, 순수 함수)   transport/  PhotoneoTcpClient (Request-Response 채널 소켓 I/O)
                messages.py (Pose, TrajectoryResponse, ...)      StateServer (옵션, State Server 채널)
                constants.py (Request/Message/Error 코드)
                rotation.py (RotationFormalism별 Pose ↔ 4x4 변환)
```

- **`protocol/`**: I/O 없는 순수 인코딩/디코딩 함수 + 공용 데이터 타입.
- **`transport/`**: `PhotoneoTcpClient`(Request-Response, 포트 11003, BPS=서버),
  `StateServer`(State Server 채널, 포트 11004, **로봇 컨트롤러가 서버** — 옵션,
  기본 비활성화).
- **`vision/`**: `AbstractVisionClient` ABC + `PhotoneoClient` 구현체.

## 설치

```bash
python3 -m venv .venv
.venv/bin/pip install -e ".[dev]"
```

## 설정

`config/default_config.yaml`을 복사해 실제 장비 IP/포트/Vision System ID에
맞게 수정하세요.

```yaml
photoneo:
  host: "192.168.1.1"
  request_port: 11003
  vision_system_id: 1
  rotation_formalism: "zyx_intrinsic"   # RainbowRobotics "rzyx"와 동일 포맷
  brand_id_prefix: "DOOSAN/1.11.0_"     # 실제 Brand ID는 Photoneo 지원팀과 확인 필요
```

**Rotation Formalism**: 이 패키지는 기본적으로 **RainbowRobotics의 "rzyx"
오일러**(수학적으로 `ZYX_INTRINSIC`과 동일 — 근거는
[protocol/rotation.py](src/photoneo_vision/protocol/rotation.py) 모듈
docstring)를 타깃으로 합니다. RainbowRobotics는 Photoneo가 공식 등재한
Brand ID 목록(Integrator Guide Appendix 2)에 없으므로, `brand_id_prefix`는
동일한 회전 포맷을 쓰는 `DOOSAN/1.11.0_`으로 placeholder 설정되어 있습니다 —
실기 연결 전 Photoneo 지원팀에 실제 인식 가능한 Brand ID 문자열을 확인하세요.
`RotationFormalism.QUATERNION`/`XYZ_EXTRINSIC`/`ROTATION_VECTOR`도 지원되므로
다른 로봇 브랜드로 전환 시 `rotation_formalism`/`brand_id_prefix`만 바꾸면
됩니다.

## 사용법

```python
from photoneo_vision import PhotoneoClient, PhotoneoConfig

config = PhotoneoConfig.from_yaml("config/local_config.yaml")
with PhotoneoClient(config) as bps:
    bps.initialize_vision_system(start_joints=[...], end_joints=[...])  # 4, 최초 1회
    bps.scan()                                                          # 1
    trajectory = bps.get_trajectory()                                  # 2
    for segment in trajectory.segments:
        for wp in segment.waypoints:
            print(wp.joints)  # 6 joint values, degrees
    for gripper in trajectory.gripper_commands:
        print(gripper.action)  # 1=Attach, 2=Detach, 3-5=User1-3
```

전체 예제:
- [examples/connection_check.py](examples/connection_check.py) — Initialize →
  Scan → Get Object Pose → Get Trajectory 순서로 호출하며 디코드 결과를 출력
  (실기 연결 시 가장 먼저 실행할 진단 스크립트).
- [examples/pick_and_place_demo.py](examples/pick_and_place_demo.py) — 전형적인
  pick 루프.
- [examples/solution_management_demo.py](examples/solution_management_demo.py) —
  솔루션 조회/변경/시작/중지.
- [examples/state_server_demo.py](examples/state_server_demo.py) — State Server
  옵션 기능 데모 (더미 관절값 콜백).

```bash
.venv/bin/python examples/connection_check.py config/local_config.yaml
```

## State Server (옵션)

BPS 6장의 State Server 채널은 캘리브레이션/시각화 목적의 보조 채널로,
**로봇 컨트롤러가 TCP 서버 역할**을 하며 관절/툴포즈를 10Hz 이상 스트리밍해야
합니다. 기본적으로 비활성화되어 있으며(`config.state_server.enabled=False`),
활성화하려면 실제 로봇의 현재 관절값/툴포즈를 반환하는 콜백을
`transport.state_server.StateServer`에 연결해야 합니다:

```python
from photoneo_vision import StateServer, Pose
from photoneo_vision.protocol.constants import pad_brand_id

def pose_provider():
    return robot.get_joint_positions_deg(), robot.get_tool_pose_mm_deg()  # -> (list[float], Pose)

server = StateServer(
    bind_host="0.0.0.0", port=11004,
    brand_id=pad_brand_id(config.brand_id_prefix),
    formalism=config.rotation_formalism,
    pose_provider=pose_provider,
)
server.start()  # 데몬 스레드
...
server.stop()
```

## 명령/프로토콜 레퍼런스

- [docs/protocol_reference.md](docs/protocol_reference.md) — Request ID 표,
  바이트 레이아웃, Rotation Formalism/Brand ID, mm↔m 단위 변환 규칙, 확인됨/
  미확인 표기.
- [docs/implementation_plan.md](docs/implementation_plan.md) — 이 패키지를
  설계할 때의 배경/범위 결정 기록.

구현 범위는 8개 요청(Initialize Vision System, Scan, Get Object Pose,
Trajectory, Change/Start/Stop Solution, Get Running Solution)으로 한정되어
있습니다 — Capture/Reuse Scan/Pick Failed/Get Status/Change Bounding Box/
Calibration 4종/Communication Check는 미구현이며, `protocol/constants.py`의
`RequestID`에 참고용으로만 값이 남아 있습니다.

## 테스트

```bash
.venv/bin/pytest
```

`test_codec.py`는 Integrator Guide의 hex 예시를 golden vector로 사용해
바이트 단위로 인코딩/디코딩을 검증합니다. `test_tcp_client.py`/
`test_photoneo_client.py`/`test_state_server.py`는 로컬 fake TCP 서버를 띄워
실제 하드웨어 없이 전체 round-trip을 검증합니다.

**주의**: `brand_id_prefix`, mm/m 단위 가정(비-EXT_DEVICE 포맷), Get Object
Pose의 payload size 불일치 등은 실장비로 검증되지 않았습니다 — 실기 연결 전
반드시 [docs/protocol_reference.md](docs/protocol_reference.md)의 "미확인"
항목을 `examples/connection_check.py`로 재확인하세요.
