コンテンツにスキップ

送信モード

DatagramBuilder::build で得た Frames は複数のFrameに分かれうる. Frameの送信方法は 2 つあり, 確実性とスループットがトレードオフになる.

以下では, 焦点を円周上で動かす例で 2 つの送信方法を比較する. あらかじめ移動先となる焦点位置を targets として用意しておく.

// Prepare 1000 focus points along a circle 150 mm above the array center.
let center = geometry.center() + offset(0.0 * mm, 0.0 * mm, 150.0 * mm);
let targets: Vec<Point3<f32>> = (0..NUM_POINTS)
.map(|i| {
let theta = 2.0 * PI * i as f32 / NUM_POINTS as f32;
center
+ offset(
RADIUS_MM * theta.cos() * mm,
RADIUS_MM * theta.sin() * mm,
0.0 * mm,
)
})
.collect();

1 フレームごとに応答を待ってから次を送る方式. send_checked を使う. 各更新が確実に反映されたことを確認してから次へ進むため確実だが, 毎フレームで往復のレイテンシがかかるためスループットは低い.

let mut patterns = geometry.pattern_buffer();
for &target in targets {
autd3_rs_pattern::focus(
geometry,
target,
wavelength,
&autd3_rs_pattern::FocusOption::default(),
&mut patterns,
);
let mut builder = client.datagram_builder();
builder.push(Pattern::new(&patterns));
for frame in &builder.build()? {
client.send_checked(frame).await?;
}
}

応答を待たずに次々と送り, 後で応答をまとめて回収する方式. send はフレームをキューに積んだ時点で ResponseFuture を返し, その ResponseFuture を await するとデバイスの応答 (Response) が得られる. Responsecheck でデバイスエラーの有無を検証できる. 送信キューの最大サイズは MAX_INFLIGHT (127). ResponseFuture を FIFO で保持し, キューが埋まったら最古の応答を回収する. 1 フレームごとに応答を待たないため, スループットが高い.

let mut patterns = geometry.pattern_buffer();
let mut pending: VecDeque<ResponseFuture> = VecDeque::with_capacity(MAX_INFLIGHT);
for &target in targets {
autd3_rs_pattern::focus(
geometry,
target,
wavelength,
&autd3_rs_pattern::FocusOption::default(),
&mut patterns,
);
let mut builder = client.datagram_builder();
builder.push(Pattern::new(&patterns));
for frame in &builder.build()? {
if pending.len() >= MAX_INFLIGHT {
pending.pop_front().expect("non-empty").await?.check()?;
}
pending.push_back(client.send(frame).await?);
}
}
// Drain the remaining responses.
while let Some(fut) = pending.pop_front() {
fut.await?.check()?;
}

初期化や設定など, 確実に 1 つずつ反映させたいコマンドは stop-and-wait. 焦点を高頻度で更新するなど, スループットが重要な場面では streaming を使う. send / send_checked の詳細は Client を参照.