#[cfg(feature = "native")]
use std::collections::HashMap;
#[cfg(feature = "native")]
use std::rc::Rc;
#[cfg(feature = "native")]
use std::sync::Arc;
#[cfg(feature = "native")]
use anyhow::{anyhow, Context, Result};
#[cfg(feature = "native")]
use arora_bridge::Bridge;
#[cfg(feature = "native")]
use arora_hal::Hal;
#[cfg(all(feature = "native", not(feature = "studio-bridge")))]
use arora_simple_data_store::SimpleDataStore;
#[cfg(feature = "native")]
use arora_types::data::DataStore;
#[cfg(feature = "native")]
use log::info;
#[cfg(feature = "native")]
use crate::operator::{serve_access_requests, Frontend};
#[cfg(feature = "native")]
use crate::{Arora, BehaviorTreeInterpreter};
#[cfg(feature = "native")]
pub async fn run() -> Result<()> {
run_with_hal(Box::new(arora_hal::FakeHal::new())).await
}
#[cfg(all(feature = "native", not(feature = "studio-bridge")))]
pub async fn run_with_hal(hal: Box<dyn Hal>) -> Result<()> {
let server = Arc::new(arora_bridge_ws::AroraWSServer::new(
arora_bridge_ws::ServerConfig::default(),
));
let bridge = arora_bridge_ws::bridge::WsBridge::new(server.clone()).await;
tokio::spawn(async move {
if let Err(e) = server.run(arora_bridge_ws::CancellationToken::new()).await {
log::error!("local bridge server stopped: {e:?}");
}
});
info!("serving the local bridge on ws://127.0.0.1:9000");
run_with(hal, Box::new(bridge), Box::new(SimpleDataStore::new())).await
}
#[cfg(feature = "studio-bridge")]
pub async fn run_with_hal(hal: Box<dyn Hal>) -> Result<()> {
crate::studio::run_with_hal(hal).await
}
#[cfg(feature = "native")]
pub async fn run_with(
hal: Box<dyn Hal>,
bridge: Box<dyn Bridge>,
store: Box<dyn DataStore>,
) -> Result<()> {
run_with_frontend(hal, bridge, store, select_frontend()).await
}
#[cfg(feature = "native")]
pub async fn run_with_frontend(
hal: Box<dyn Hal>,
bridge: Box<dyn Bridge>,
store: Box<dyn DataStore>,
frontend: Frontend,
) -> Result<()> {
let Frontend { operator, on_ready } = frontend;
let info = bridge.get_device_info().await.ok().flatten();
let device_id = bridge.device_id().await;
let access_requests = bridge.access_requests().await;
let mut builder = Arora::builder().with_hal(hal).with_bridge(bridge);
if let Some(path) = std::env::args().nth(1) {
let xml = std::fs::read_to_string(&path)
.with_context(|| format!("could not read Groot file {path}"))?;
let mut interpreter = BehaviorTreeInterpreter::new(Rc::new(HashMap::new()));
interpreter
.load_groot(&xml, &*store)
.map_err(|e| anyhow!("failed to install behavior tree from {path}: {e:?}"))?;
builder = builder.with_behavior_interpreter(Box::new(interpreter));
info!("installed behavior tree from {path}");
}
let mut arora = builder
.with_data_store(store)
.build()
.context("failed to build Arora")?;
on_ready(arora.telemetry(), info, device_id);
info!("engine started; native behavior-tree control nodes ready");
tokio::spawn(serve_access_requests(access_requests, operator));
info!("running — Ctrl-C to stop");
arora
.run(Arora::DEFAULT_STEP_PERIOD)
.await
.map_err(|e| anyhow!("runtime error: {e}"))
}
#[cfg(feature = "native")]
pub(crate) fn select_frontend() -> Frontend {
#[cfg(feature = "tui")]
{
use std::io::IsTerminal;
if std::io::stdout().is_terminal() {
match crate::tui::tui_frontend() {
Ok(frontend) => return frontend,
Err(e) => eprintln!("arora: terminal UI unavailable ({e}); running headless"),
}
}
}
crate::operator::default_frontend()
}