Controller

ここでは, Controllerに存在するAPIを紹介する.

fpga_state

FPGAの状態を取得する. これを使用する前に, ReadsFPGAStateで状態取得を有効化しておく必要がある.

use autd3::prelude::*;
#[allow(unused_variables)]
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut autd = Controller::open([AUTD3::default()], autd3::link::Nop::new())?;
autd.send(ReadsFPGAState::new(|_dev| true))?;

let info = autd.fpga_state()?;
Ok(())
}
#include<autd3.hpp>
#include<autd3/link/nop.hpp>
int main() {
using namespace autd3;
auto autd =
Controller::open({AUTD3{}}, link::Nop{});
autd.send(ReadsFPGAState([](const auto&) { return true; }));

const auto info = autd.fpga_state();
return 0; }
using AUTD3Sharp;
using AUTD3Sharp.Link;
using AUTD3Sharp.Utils;
using var autd = Controller.Open([new AUTD3()], new Nop());
autd.Send(new ReadsFPGAState(_ => true));

var info = autd.FPGAState();
from pyautd3 import Controller, AUTD3, ReadsFPGAState
from pyautd3.link.nop import Nop
autd = Controller.open([AUTD3()], Nop())
autd.send(ReadsFPGAState(lambda _: True))

info = autd.fpga_state()

ReadsFPGAStateコンストラクタの引数はFn(&Device) -> boolで, デバイス毎に状態取得を有効化するかどうかを指定する.

有効化していないデバイスに対してfpga_stateNoneを返す.

FPGAの状態としては, 現在以下の情報が取得できる.

  • is_thermal_assert: ファン制御用の温度センサがアサートされているかどうか
  • current_mod_segment: 現在のModulation Segment
  • current_stm_segment: 現在のFociSTM/GainSTM Segment
  • current_gain_segment: 現在のGain Segment
  • is_gain_mode: 現在Gainが使用されているかどうか
  • is_stm_mode: 現在FociSTM/GainSTMが使用されているかどうか

send

デバイスにデータを送信する.

データは単体か2つのみ同時に送信することができる.

group_send

group_send関数を使用すると, デバイスをグルーピングすることができる.

use autd3::prelude::*;
use autd3::gain::IntoBoxedGain;

use std::collections::HashMap;
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut autd = Controller::open(
        [AUTD3::default(), AUTD3::default()],
        autd3::link::Nop::new(),
    )?;
    let x = 0.;
    let y = 0.;
    let z = 0.;
autd.group_send(
    |dev| match dev.idx() {
        0 => Some("focus"),
        1 => Some("null"),
        _ => None,
    },
    HashMap::from([
        (
            "focus",
            Focus {
                pos: Point3::new(x, y, z),
                option: Default::default(),
            }
            .into_boxed(),
        ),
        ("null", Null {}.into_boxed()),
    ]),
)?;
    Ok(())
}
#include<chrono>
#include<autd3.hpp>
#include<autd3/link/nop.hpp>
int main() {
using namespace autd3;
auto autd =
Controller::open({AUTD3{}}, link::Nop{});
const auto x = 0.0;
const auto y = 0.0;
const auto z = 0.0;
autd.group_send(
    [](const Device& dev) -> std::optional<const char*> {
      if (dev.idx() == 0) {
        return "null";
      } else if (dev.idx() == 1) {
        return "focus";
      } else {
        return std::nullopt;
      }
    },
    std::unordered_map<const char*, std::shared_ptr<driver::Datagram>>{
        {"focus",
         std::make_shared<Focus>(Focus(Point3(x, y, z), FocusOption{}))},
        {"null", std::make_shared<Null>()}});
return 0; }
using AUTD3Sharp;
using AUTD3Sharp.Link;
using AUTD3Sharp.Gain;
using AUTD3Sharp.Modulation;
using AUTD3Sharp.Utils;
using var autd = Controller.Open([new AUTD3()], new Nop());
var x = 0.0f;
var y = 0.0f;
var z = 0.0f;
autd.GroupSend(dev =>
    {
        return dev.Idx() switch
        {
            0 => "null",
            1 => "focus",
            _ => null
        };
    },
    new GroupDictionary {
        { "null", new Null() },
        { "focus", new Focus(pos: new Point3(x, y, z), option: new FocusOption()) }
    }
);
from pyautd3 import AUTD3, Controller, Device
from pyautd3.gain import Focus, FocusOption, Null
from pyautd3.link.nop import Nop
autd = Controller.open([AUTD3()], Nop())
x = 0.0
y = 0.0
z = 0.0
def key_map(dev: Device) -> str | None:
    if dev.idx == 0:
        return "null"
    if dev.idx == 1:
        return "focus"
    return None


autd.group_send(
    key_map=key_map,
    data_map={"null": Null(), "focus": Focus(pos=[x, y, z], option=FocusOption())},
)

gain::Groupとは異なり, 通常のsendで送信できるデータなら何でも使用できる. ただし, デバイス単位でしかグルーピングできない.

NOTE: このサンプルでは, キーとして文字列を使用しているが, HashMapのキーとして使用できるものなら何でも良い.

sender

送信時の設定をsender経由で指定できる.

use std::time::Duration;
use autd3::prelude::*;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut autd = Controller::open([AUTD3::default()], autd3::link::Nop::new())?;
let mut sender = autd.sender(SenderOption {
    send_interval: Duration::from_millis(1),
    receive_interval: Duration::from_millis(1),
    timeout: None,
    parallel: ParallelMode::Auto,
    sleeper: SpinSleeper::default(),
});
let d = Null {};
sender.send(d)?;
Ok(())
}
#include<chrono>
#include<autd3.hpp>
#include<autd3/link/nop.hpp>
int main() {
using namespace autd3;
auto autd = Controller::open({AUTD3{}}, link::Nop{});
auto sender = autd.sender(SenderOption{
    .send_interval = std::chrono::milliseconds(1),
    .receive_interval = std::chrono::milliseconds(1),
    .timeout = std::nullopt,
    .parallel = ParallelMode::Auto,
    .sleeper = SpinSleeper(),
});
const Null d;
sender.send(d);
return 0; }
using AUTD3Sharp;
using AUTD3Sharp.Link;
using AUTD3Sharp.Gain;
using AUTD3Sharp.Utils;
using var autd = Controller.Open([new AUTD3()], new Nop());
var sender = autd.Sender(
    new SenderOption
    {
        SendInterval = Duration.FromMillis(1),
        ReceiveInterval = Duration.FromMillis(1),
        Timeout = null,
        Parallel = ParallelMode.Auto,
        Sleeper = new SpinSleeper()
    }
);
var d = new Null();
sender.Send(d);
from pyautd3 import AUTD3, Controller, Duration, Null, SenderOption, SpinSleeper, ParallelMode
from pyautd3.link.nop import Nop
autd = Controller.open([AUTD3()], Nop())
sender = autd.sender(
    SenderOption(
        send_interval=Duration.from_millis(1),
        receive_interval=Duration.from_millis(1),
        timeout=None,
        parallel=ParallelMode.Auto,
        sleeper=SpinSleeper(),
    )
)
d = Null()
sender.send(d)

ここで,

  • send_interval: 送信間隔
  • receive_interval: 受信間隔
  • timeout: タイムアウト時間. 詳細はタイムアウトについてを参照
  • parallel: 並列計算モード. 詳細は並列計算についてを参照
  • sleeper: 送受信間隔を調整する構造体
    • SpinSleeper: spin_sleepを使用
    • StdSleeper: std::thread::sleepを使用
    • WaitableSleeper: (Windowsのみ) Waitable Timerを使用

であり, デフォルト値は上記の通り.

なお, Controller::send, Controller::group_sendはデフォルトのSenderOptionを使用した場合と等価である.

タイムアウトについて

タイムアウトの値が

  • 0より大きい場合, 送信データがデバイスで処理されるか, 指定したタイムアウト時間が経過するまで待機する. 送信データがデバイスで処理されたのが確認できなかった場合にエラーを返す.
  • 0の場合, send関数は送信データがデバイスで処理されたかどうかのチェックを行わない.

確実にデータを送信したい場合はこれを適当な値に設定しておくことをおすすめする.

SenderOptionで指定しない場合, 以下に示す各データのデフォルト値が使用される.

タイムアウト値
Clear/GPIOOutputs/
ForceFan/PhaseCorrection/
PulseWidthEncoder/ReadsFPGAState/
SwapSegment/Silencer/
Synchronize/FociSTM/
GainSTM/Modulation
Gain

複数をまとめて送信する場合は, それぞれのデータのタイムアウト値の最大値が使用される.

並列計算について

各データの内部での計算は, デバイス単位で並列に実行することができる.

ParallelMode::Onを指定すると並列計算を有効化, ParallelMode::Offを指定すると無効化する.

ParallelMode::Autoの場合, 有効なデバイスの数が以下に示す各データの並列計算スレッショルド値を超える場合に並列計算が有効化される.

並列計算スレッショルド値
Clear/GPIOOutputs/
ForceFan/PhaseCorrection/
ReadsFPGAState/SwapSegment/
Silencer/Synchronize/
FociSTM (焦点数が4000未満)/
Modulation
18446744073709551615
PulseWidthEncoder/
FociSTM (焦点数が4000以上)/
/GainSTM/Gain
4