Skip to main content

futu_backend/
ping.rs

1//! Backend ping helper for delay statistics.
2//!
3//! C++ `NNBiz_SvrTime` feeds `ProtoDelay` with a one-way net delay calculated
4//! as `avg(rtt_ms) / 2` from `f3clogin::StartPingTask`.
5
6use std::time::{Duration, Instant};
7
8use futu_core::error::{FutuError, Result};
9use prost::Message;
10
11use crate::conn::BackendConn;
12use crate::proto_handler::nn_cmd;
13use crate::proto_internal::ft_conn_ping;
14
15const CONN_PING_SUCC: i32 = 0;
16
17/// Send one backend ping packet and return one-way delay.
18///
19/// Ref:
20/// - FutuOpenD/Src/NNProtoCenter/Other/NNBiz_SvrTime.cpp:60-78
21/// - FutuOpenD/Src/NNProtoCenter/Other/NNBiz_Ping.cpp:441-461
22///
23/// Rust does not currently have C++ `INNData_AppInfo::GetNetType()`, so
24/// `net_type` is intentionally omitted instead of defaulting to 0.
25pub async fn measure_one_way_net_delay(
26    backend: &BackendConn,
27    user_id: u64,
28    device_id: &str,
29) -> Result<Duration> {
30    let req = build_conn_ping_req(user_id, device_id, 0);
31    let body = req.encode_to_vec();
32    let begin = Instant::now();
33    let frame = backend.request(nn_cmd::PING, body).await?;
34    let rtt = begin.elapsed();
35
36    let rsp = ft_conn_ping::ConnPingRsp::decode(frame.body.as_ref())?;
37    let result_code = rsp.result_code.unwrap_or(-1);
38    if result_code != CONN_PING_SUCC {
39        return Err(FutuError::ServerError {
40            ret_type: result_code,
41            msg: "ConnPing failed".to_string(),
42        });
43    }
44
45    Ok(rtt / 2)
46}
47
48fn build_conn_ping_req(
49    user_id: u64,
50    device_id: &str,
51    data_len: usize,
52) -> ft_conn_ping::ConnPingReq {
53    ft_conn_ping::ConnPingReq {
54        device_id: Some(device_id.to_string()),
55        user_id: Some(user_id),
56        net_type: None,
57        data: Some(vec![0; data_len]),
58    }
59}
60
61#[cfg(test)]
62mod tests;