score-back/openwiki/rpc-protocol.md
Artur Mukhamadiev 162c210a7d feat(cloud_point): stereo rectification, point cloud pipeline, and CloudPointClient facade
- StereoRectifier wrapping cv::stereoRectify + cv::initUndistortRectifyMap (CV_16SC2 maps)
- PointCloudBuilder with cv::reprojectImageTo3D and depth/NaN filtering
- CloudPointClient high-level facade: connect(), compute_cloud() -> std::expected<PointCloud, Error>
- write_ply() ASCII PLY export helper
- CLI options 4 (compute-cloud) and 5 (compute-cloud + save PLY)
- Fix TcpServer to loop over multiple requests per connection
- Expose num_disparities parameter through CloudPointClient and StereoMatcherFactory
- Unity C# integration design spec
- Sync README, AGENTS, openwiki with stereo pipeline (C++23)
- E2E synthetic-scene test with constant-disparity stereo pair

TG-9 #ready-for-test
TG-4 #ready-for-test
TG-2 #in-progress
2026-07-12 22:28:21 +03:00

216 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# RPC Protocol
## JSON-RPC 2.0
The server implements **JSON-RPC 2.0** over TCP with length-prefixed framing (see [Architecture → Wire framing](architecture.md#wire-framing)).
Batch requests are not supported — only single request objects are processed.
### Request format
```json
{
"jsonrpc": "2.0",
"method": "<method_name>",
"params": {},
"id": <integer|string>
}
```
The `params` field is accepted but currently ignored by all implemented methods. The `id` field is required; requests without it receive a `-32600 Invalid Request` error.
### Success response
```json
{
"jsonrpc": "2.0",
"result": <method_specific_result>,
"id": <matching_request_id>
}
```
### Error response
```json
{
"jsonrpc": "2.0",
"error": {
"code": <integer>,
"message": "<string>"
},
"id": <matching_request_id>
}
```
### Error codes
| Code | Meaning | When |
|---|---|---|
| `-32700` | Parse error | Request is not valid JSON |
| `-32600` | Invalid Request | Missing `jsonrpc`, `method`, or `id` fields, or `jsonrpc != "2.0"` |
| `-32601` | Method not found | No handler registered for the requested method name |
| `-32000` | Server error | Handler threw an exception |
> **Known issue**: Non-object JSON (arrays, strings, numbers, null) causes a `nlohmann::json::type_error` instead of a clean `-32600` response. See `tests/test_rpc_edge_cases.cpp`.
Source: `src/rpc_server.cpp``RpcServer::process()` and `create_error()`.
## Methods
### `get-stereo-calibration`
Returns full stereo rig calibration: intrinsics for both cameras, stereo rotation and translation, and sensor resolution. Called once by `CloudPointClient::connect()`.
**Request:**
```json
{"jsonrpc": "2.0", "method": "get-stereo-calibration", "id": 1}
```
**Response:**
```json
{
"left": { "camera_matrix": [9 doubles], "dist_coeffs": [5 doubles] },
"right": { "camera_matrix": [9 doubles], "dist_coeffs": [5 doubles] },
"rotation": [9 doubles],
"translation": [3 doubles],
"image_size": { "width": 640, "height": 480 }
}
```
All matrices are row-major. `camera_matrix` is the 3×3 intrinsic matrix; `rotation` is the 3×3 left-to-right rotation (OpenCV convention); `translation` is in metres. Calibration arrays are **plain JSON doubles — not Base64**.
Test mock defaults: fx=fy=800, cx=320, cy=240, zero distortion, R=I, T=[-0.06, 0, 0], 640×480.
---
### `get-image-pair`
Returns a synchronised stereo frame as two base64-encoded images. Called on each `CloudPointClient::compute_cloud()`.
**Request:**
```json
{"jsonrpc": "2.0", "method": "get-image-pair", "id": 2}
```
**Response:**
```json
{
"frame": 42,
"left": { "width": 640, "height": 480, "type": "BGR", "data": "<base64>" },
"right": { "width": 640, "height": 480, "type": "BGR", "data": "<base64>" }
}
```
`data` is Base64-encoded raw pixel bytes (standard alphabet, no line breaks). `type` is `"BGR"` (3 ch), `"RGBA"` (4 ch), or `"DEPTH"` (1 ch float32). Unity must flip GPU readback vertically before encoding (wire format is top-left-origin row-major).
DTOs: `StereoCalibrationDto` and `ImagePairDto` in `include/cloud_point_rpc/rpc_dto.hpp`. Decoded client-side via `Base64RPCCoder`.
---
### `get-intrinsic-params` *(legacy)*
Retrieves left-camera intrinsic parameters as a flat 3×3 matrix (row-major, 9 doubles). Not used by `CloudPointClient`; kept for backward compatibility.
**Request:**
```json
{"jsonrpc": "2.0", "method": "get-intrinsic-params", "id": 3}
```
**Response:**
```json
{"jsonrpc": "2.0", "result": [fx, 0, cx, 0, fy, cy, 0, 0, 1], "id": 3}
```
Result: 9 plain JSON doubles. **Not Base64.**
### `get-extrinsic-params` *(legacy)*
Retrieves left-camera extrinsic matrix as a flat 4×4 matrix (row-major, 16 doubles). Not used by `CloudPointClient`; kept for backward compatibility.
**Request:**
```json
{"jsonrpc": "2.0", "method": "get-extrinsic-params", "id": 4}
```
**Response:**
```json
{"jsonrpc": "2.0", "result": [16 doubles], "id": 4}
```
Result: 16 plain JSON doubles. **Not Base64.**
### `get-cloud-point`
Retrieves the current field-of-view point cloud.
**Request:**
```json
{"jsonrpc": "2.0", "method": "get-cloud-point", "id": 3}
```
**Response:**
```json
{"jsonrpc": "2.0", "result": {"width": int, "height": int, "data": <base64-encoded-array>}, "id": 3}
```
Result type: `matrix WxH` (list of `[x, y, z]` points), Base64-encoded.
> **Note**: The standalone mock server (`server_main.cpp`) returns these as raw JSON arrays (not Base64-encoded) via `Service`. The Base64 encoding is expected to be implemented on the Unity side per `API.md`.
## Handler registration
Handlers are registered with `RpcServer::register_method()`. Two forms exist:
### C++ handler
```cpp
rpc_server.register_method("get-intrinsic-params", [&](const json& params) {
return service.get_intrinsic_params(); // returns vector<double>
});
```
The handler returns `std::variant<nlohmann::json, std::string>`. If a `json` is returned, it is placed directly in the `result` field. If a `std::string` is returned, it is placed as-is.
### C callback handler
```cpp
rpc_string* my_callback(rpc_string* params_json) {
// params_json->s contains the JSON params as a string
// return a result string (JSON or raw)
return crpc_str_create("result_data", 11);
}
rpc_server.register_method("my-method", my_callback);
```
The C callback receives the JSON `params` as a string in `rpc_string->s`. The return value's string is parsed as JSON if possible; otherwise it is used as a raw string in the `result` field.
Source: `src/rpc_server.cpp``register_method(name, callback_t)` overload.
## Base64 encoding
`include/cloud_point_rpc/rpc_coder.hpp`, `src/rpc_coder.cpp`
- `Base64RPCCoder` implements the `IRPCCoder` interface with `encode()` and `decode()` methods.
- Uses the [aklomp/base64](https://github.com/aklomp/base64) library (Meson wrap `subprojects/aklomp-base64.wrap`).
- `encode(vector<char>) → string` and `decode(string) → vector<char>`.
- Includes overflow protection: throws `std::length_error` if input exceeds safe size limits.
> Per `API.md`: **only image pixel data** (`get-image-pair` `data` fields) uses Base64. Calibration arrays (`camera_matrix`, `dist_coeffs`, `rotation`, `translation`) and legacy params are plain JSON doubles — never Base64. Unity encodes images before sending; the C++ client decodes them via `Base64RPCCoder`.
## Client-side usage
`include/cloud_point_rpc/rpc_client.hpp`
`RpcClient` wraps `jsonrpccxx::JsonRpcClient` and provides typed access:
```cpp
score::TCPConnector connector(ip, port);
score::RpcClient client(connector);
auto intrinsic = client.get_intrinsic_params(); // vector<double>
auto extrinsic = client.get_extrinsic_params(); // vector<double>
auto cloud = client.get_cloud_point(); // vector<vector<double>>
```
The interactive CLI (`src/cli.cpp`) and `minimal_client` (`src/minimal_client.cpp`) demonstrate client usage.