-
Notifications
You must be signed in to change notification settings - Fork 4.9k
add new Rest api wrapper #13965
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: development
Are you sure you want to change the base?
add new Rest api wrapper #13965
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# Ignore vscode configuration files | ||
.vscode/ | ||
.pytest_cache/ | ||
# Ignore virtual environment folder | ||
.venv/ | ||
venv/ | ||
|
||
# Ignore Python bytecode files | ||
*.pyc | ||
__pycache__/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
# LibRS Rest API server | ||
## Overview | ||
|
||
This Python library provides a convenient wrapper to interact with the [RealSense REST API], a FastAPI-based service that exposes the functionality of the Intel RealSense SDK (librealsense) over a network. | ||
|
||
It simplifies remote control and data streaming from RealSense devices by handling communication protocols for: | ||
|
||
1. **RESTful Operations:** For device discovery, configuration, and control. | ||
2. **WebRTC:** For efficient, browser-compatible live video streaming (RGB, Depth). | ||
3. **Socket.IO:** For real-time streaming of frame metadata and point cloud data. | ||
* In order to decode point cloud data in the client, follow those steps: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we can add a python or other language you already did it on code snippet? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I dont have something official I can provide. it just a standard and languege agnostic socketio usage. |
||
* Decode Base64 string to Uint8Array | ||
* 'bytes' now holds the raw byte data that originated from the NumPy array | ||
* Interpret bytes as Float32Array. | ||
IMPORTANT ASSUMPTION: We assume the original NumPy array used float32. | ||
If it used float64 (Python's default float), use Float64Array here. | ||
The ArrayBuffer gives access to the raw binary data buffer of the Uint8Array. | ||
The Float32Array constructor creates a *view* over that buffer, interpreting | ||
groups of 4 bytes as 32-bit floats without copying data. | ||
* Now you can use the 'vertices' Float32Array | ||
* Motion raw data is also propogated using this socket.io channel | ||
|
||
## Support Features | ||
|
||
* **Device Management:** | ||
* List connected RealSense devices. | ||
* Get detailed information about specific devices (name, serial, firmware, etc.). | ||
* Perform hardware resets remotely. | ||
* **Sensor & Option Control:** | ||
* List available sensors on a device. | ||
* Get detailed sensor information and supported stream profiles. | ||
* List and retrieve current values for sensor options (e.g., exposure, gain). | ||
* Update writable sensor options. | ||
* **REST-based Stream Control:** | ||
* Start/Stop streams with specific configurations (resolution, format, FPS). | ||
* Check the streaming status of a device. | ||
* **WebRTC Video Streaming:** | ||
* Initiate WebRTC sessions with the API to receive live video feeds. | ||
* Handles the offer/answer and ICE candidate exchange mechanisms required for WebRTC setup (via REST endpoints). | ||
* **Socket.IO Data Streaming:** | ||
* Establish a Socket.IO connection to the API. | ||
* Receive real-time events containing: | ||
* Frame metadata (timestamps, frame numbers, etc.). | ||
* Point cloud data streams. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be helpful to mention that all of the endpoints and the supported features are available on the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was pretty sure I added it, probably missed that. will add. |
||
|
||
## Missing Features | ||
* Not all SDK capabilities are yet supported(e.g. Texture Mapping, Post processing filters,...) | ||
|
||
## Installation | ||
|
||
|
||
1. **Create a virtual environment:** | ||
|
||
```bash | ||
python3 -m venv venv | ||
source venv/bin/activate | ||
``` | ||
|
||
2. **Install dependencies:** | ||
|
||
```bash | ||
pip3 install -r requirements.txt | ||
``` | ||
|
||
3. **Run API server:** | ||
|
||
```bash | ||
run start_server.sh | ||
``` | ||
|
||
4. **Run tests(inside tests folder):** | ||
|
||
```bash | ||
pytest test_api_service.py | ||
``` | ||
|
||
|
||
## Testing | ||
|
||
1. **To enable testing add the following to the requirements.txt** | ||
```bash | ||
pytest==8.3.5 | ||
pytest-asyncio==0.26.0 | ||
pyyaml==6.0.2 | ||
lark==1.2.2 | ||
httpx==0.28.1 | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
from fastapi import Depends | ||
from fastapi.security import OAuth2PasswordBearer | ||
from typing import Generator | ||
|
||
from app.core.auth import get_current_user | ||
from app.services.rs_manager import RealSenseManager | ||
from app.services.webrtc_manager import WebRTCManager | ||
from app.services.socketio import sio | ||
|
||
# OAuth2 setup | ||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") | ||
|
||
# Singleton instances | ||
_realsense_manager = None | ||
_webrtc_manager = None | ||
|
||
def get_realsense_manager() -> RealSenseManager: | ||
global _realsense_manager | ||
if _realsense_manager is None: | ||
_realsense_manager = RealSenseManager(sio) | ||
return _realsense_manager | ||
|
||
def get_webrtc_manager() -> WebRTCManager: | ||
global _webrtc_manager | ||
if _webrtc_manager is None: | ||
_webrtc_manager = WebRTCManager(get_realsense_manager()) | ||
return _webrtc_manager |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
from fastapi import APIRouter, Depends, HTTPException | ||
from typing import List | ||
|
||
from app.core.auth import get_current_user | ||
from app.models.device import DeviceInfo | ||
from app.services.rs_manager import RealSenseManager | ||
from app.api.dependencies import get_realsense_manager | ||
|
||
router = APIRouter() | ||
|
||
@router.get("/", response_model=List[DeviceInfo]) | ||
async def get_devices( | ||
rs_manager: RealSenseManager = Depends(get_realsense_manager), | ||
#user: dict = Depends(get_current_user) -> enable this if security is needed | ||
): | ||
""" | ||
Get a list of all connected RealSense devices. | ||
""" | ||
return rs_manager.get_devices() | ||
|
||
@router.get("/{device_id}", response_model=DeviceInfo) | ||
async def get_device( | ||
device_id: str, | ||
rs_manager: RealSenseManager = Depends(get_realsense_manager), | ||
#user: dict = Depends(get_current_user) -> enable this if security is needed | ||
): | ||
""" | ||
Get details of a specific RealSense device. | ||
""" | ||
try: | ||
return rs_manager.get_device(device_id) | ||
except Exception as e: | ||
raise HTTPException(status_code=404, detail=str(e)) | ||
|
||
@router.post("/{device_id}/hw_reset", response_model=bool) | ||
async def hw_reset_device( | ||
device_id: str, | ||
rs_manager: RealSenseManager = Depends(get_realsense_manager), | ||
#user: dict = Depends(get_current_user) -> enable this if security is needed | ||
): | ||
""" | ||
Perform a hardware reset on a specific RealSense device. | ||
""" | ||
try: | ||
return rs_manager.reset_device(device_id) | ||
except Exception as e: | ||
raise HTTPException(status_code=400, detail=str(e)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
from fastapi import APIRouter, Depends, HTTPException | ||
from typing import List, Any | ||
|
||
from app.core.auth import get_current_user | ||
from app.models.option import OptionInfo, OptionUpdate | ||
from app.services.rs_manager import RealSenseManager | ||
from app.api.dependencies import get_realsense_manager | ||
|
||
router = APIRouter() | ||
|
||
@router.get("/", response_model=List[OptionInfo]) | ||
async def get_options( | ||
device_id: str, | ||
sensor_id: str, | ||
rs_manager: RealSenseManager = Depends(get_realsense_manager), | ||
): | ||
""" | ||
Get a list of all options for a specific sensor. | ||
""" | ||
try: | ||
return rs_manager.get_sensor_options(device_id, sensor_id) | ||
except Exception as e: | ||
raise HTTPException(status_code=404, detail=str(e)) | ||
|
||
@router.get("/{option_id}", response_model=OptionInfo) | ||
async def get_option( | ||
device_id: str, | ||
sensor_id: str, | ||
option_id: str, | ||
rs_manager: RealSenseManager = Depends(get_realsense_manager), | ||
): | ||
""" | ||
Get details of a specific option for a sensor. | ||
""" | ||
try: | ||
return rs_manager.get_sensor_option(device_id, sensor_id, option_id) | ||
except Exception as e: | ||
raise HTTPException(status_code=404, detail=str(e)) | ||
|
||
@router.put("/{option_id}", response_model=dict) | ||
async def update_option( | ||
device_id: str, | ||
sensor_id: str, | ||
option_id: str, | ||
option_update: OptionUpdate, | ||
rs_manager: RealSenseManager = Depends(get_realsense_manager), | ||
): | ||
""" | ||
Update the value of a specific option for a sensor. | ||
""" | ||
try: | ||
result = rs_manager.set_sensor_option(device_id, sensor_id, option_id, option_update.value) | ||
return {"success": result} | ||
except Exception as e: | ||
raise HTTPException(status_code=400, detail=str(e)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
from fastapi import APIRouter, Depends, HTTPException | ||
from typing import List, Optional | ||
|
||
from app.core.auth import get_current_user | ||
from app.models.stream import PointCloudStatus, StreamStatus | ||
from app.services.rs_manager import RealSenseManager | ||
from app.api.dependencies import get_realsense_manager | ||
|
||
router = APIRouter() | ||
|
||
@router.post("/activate", response_model=PointCloudStatus) | ||
async def activate_point_cloud( | ||
device_id: str, | ||
rs_manager: RealSenseManager = Depends(get_realsense_manager), | ||
): | ||
try: | ||
return rs_manager.activate_point_cloud(device_id, True) | ||
except Exception as e: | ||
raise HTTPException(status_code=400, detail=str(e)) | ||
|
||
@router.post("/deactivate", response_model=PointCloudStatus) | ||
async def deactivate_point_cloud( | ||
device_id: str, | ||
rs_manager: RealSenseManager = Depends(get_realsense_manager), | ||
): | ||
try: | ||
return rs_manager.activate_point_cloud(device_id, False) | ||
except Exception as e: | ||
raise HTTPException(status_code=400, detail=str(e)) | ||
|
||
@router.get("/status", response_model=PointCloudStatus) | ||
async def get_stream_status( | ||
device_id: str, | ||
rs_manager: RealSenseManager = Depends(get_realsense_manager) | ||
): | ||
try: | ||
return rs_manager.get_point_cloud_status(device_id) | ||
except Exception as e: | ||
raise HTTPException(status_code=404, detail=str(e)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
from fastapi import APIRouter, Depends, HTTPException | ||
from typing import List | ||
|
||
from app.core.auth import get_current_user | ||
from app.models.sensor import SensorInfo | ||
from app.services.rs_manager import RealSenseManager | ||
from app.api.dependencies import get_realsense_manager | ||
|
||
router = APIRouter() | ||
|
||
@router.get("/", response_model=List[SensorInfo]) | ||
async def get_sensors( | ||
device_id: str, | ||
rs_manager: RealSenseManager = Depends(get_realsense_manager), | ||
): | ||
""" | ||
Get a list of all sensors for a specific RealSense device. | ||
""" | ||
try: | ||
return rs_manager.get_sensors(device_id) | ||
except Exception as e: | ||
raise HTTPException(status_code=404, detail=str(e)) | ||
|
||
@router.get("/{sensor_id}", response_model=SensorInfo) | ||
async def get_sensor( | ||
device_id: str, | ||
sensor_id: str, | ||
rs_manager: RealSenseManager = Depends(get_realsense_manager), | ||
): | ||
""" | ||
Get details of a specific sensor for a RealSense device. | ||
""" | ||
try: | ||
return rs_manager.get_sensor(device_id, sensor_id) | ||
except Exception as e: | ||
raise HTTPException(status_code=404, detail=str(e)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
from fastapi import APIRouter, Depends, HTTPException | ||
from typing import List, Optional | ||
|
||
from app.core.auth import get_current_user | ||
from app.models.stream import StreamStatus, StreamStart | ||
from app.services.rs_manager import RealSenseManager | ||
from app.api.dependencies import get_realsense_manager | ||
|
||
router = APIRouter() | ||
|
||
@router.post("/start", response_model=StreamStatus) | ||
async def start_stream( | ||
device_id: str, | ||
stream_config: StreamStart, | ||
rs_manager: RealSenseManager = Depends(get_realsense_manager), | ||
): | ||
""" | ||
Start streaming from a RealSense device with the specified configuration. | ||
""" | ||
try: | ||
return rs_manager.start_stream(device_id, stream_config.configs, stream_config.align_to) | ||
except Exception as e: | ||
raise HTTPException(status_code=400, detail=str(e)) | ||
|
||
@router.post("/stop", response_model=StreamStatus) | ||
async def stop_stream( | ||
device_id: str, | ||
rs_manager: RealSenseManager = Depends(get_realsense_manager), | ||
): | ||
""" | ||
Stop streaming from a RealSense device. | ||
""" | ||
try: | ||
return rs_manager.stop_stream(device_id) | ||
except Exception as e: | ||
raise HTTPException(status_code=400, detail=str(e)) | ||
|
||
@router.get("/status", response_model=StreamStatus) | ||
async def get_stream_status( | ||
device_id: str, | ||
rs_manager: RealSenseManager = Depends(get_realsense_manager) | ||
): | ||
""" | ||
Get the streaming status for a RealSense device. | ||
""" | ||
try: | ||
return rs_manager.get_stream_status(device_id) | ||
except Exception as e: | ||
raise HTTPException(status_code=404, detail=str(e)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was the meaning here or missing some symbol?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just wanted to emphasize it