feat: 开放小爱音箱接入 MiGPT-Next 源代码
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
use neon::prelude::*;
|
||||
use node::NodeManager;
|
||||
use open_xiaoai::services::connect::{message::MessageManager, rpc::RPC};
|
||||
|
||||
use runtime::runtime;
|
||||
use serde_json::json;
|
||||
use server::AppServer;
|
||||
|
||||
mod node;
|
||||
mod runtime;
|
||||
mod server;
|
||||
|
||||
#[neon::export]
|
||||
async fn start() -> () {
|
||||
let _ = AppServer::run().await;
|
||||
}
|
||||
|
||||
#[neon::export]
|
||||
async fn run_shell(script: String, timeout_millis: f64) -> String {
|
||||
let res = RPC::instance()
|
||||
.call_remote(
|
||||
"run_shell",
|
||||
Some(json!(script)),
|
||||
Some(timeout_millis as u64),
|
||||
)
|
||||
.await;
|
||||
match res {
|
||||
Err(e) => format!("run_shell error: {}", e),
|
||||
Ok(res) => serde_json::to_string(&res.data.unwrap()).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
#[neon::export]
|
||||
async fn on_output_data(bytes: Vec<u8>) -> bool {
|
||||
MessageManager::instance()
|
||||
.send_stream("play", bytes, None)
|
||||
.await
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
#[neon::main]
|
||||
fn main(mut cx: ModuleContext) -> NeonResult<()> {
|
||||
let _ = neon::set_global_executor(&mut cx, runtime());
|
||||
neon::registered().export(&mut cx)?;
|
||||
NodeManager::instance().init(cx);
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
use neon::prelude::*;
|
||||
use std::sync::{Arc, LazyLock, Mutex};
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
use crate::runtime::run_async;
|
||||
|
||||
pub struct NodeManager {
|
||||
channel: Arc<Mutex<Option<Channel>>>,
|
||||
}
|
||||
|
||||
static INSTANCE: LazyLock<NodeManager> = LazyLock::new(NodeManager::new);
|
||||
|
||||
impl NodeManager {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
channel: Arc::new(Mutex::new(None)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn instance() -> &'static Self {
|
||||
&INSTANCE
|
||||
}
|
||||
|
||||
pub fn init(&self, mut cx: ModuleContext) {
|
||||
let channel = cx.channel();
|
||||
*self.channel.lock().unwrap() = Some(channel);
|
||||
}
|
||||
|
||||
pub async fn call_fn<R, F, F2>(&self, key: &str, map_arg: F, map_res: F2) -> Result<R, String>
|
||||
where
|
||||
R: Send + 'static,
|
||||
F: for<'a> Fn(&mut TaskContext<'a>) -> Handle<'a, JsValue> + Send + 'static,
|
||||
F2: Fn(&mut TaskContext<'_>, Handle<'_, JsValue>) -> Result<R, String>
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
{
|
||||
let channel = match self.channel.lock().unwrap().as_ref() {
|
||||
Some(channel) => channel.clone(),
|
||||
None => return Err("NodeManager 尚未初始化".into()),
|
||||
};
|
||||
|
||||
let key = key.to_string();
|
||||
let (tx, rx) = oneshot::channel::<Result<R, String>>();
|
||||
|
||||
channel.send(move |mut cx| {
|
||||
let Ok(callbacks) = cx.global::<JsObject>("RUST_CALLBACKS") else {
|
||||
let _ = tx.send(Err("无法获取 RUST_CALLBACKS 对象".into()));
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let Ok(callback) = callbacks.get::<JsFunction, _, _>(&mut cx, key.as_str()) else {
|
||||
let _ = tx.send(Err("找不到函数".into()));
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let arg = map_arg(&mut cx);
|
||||
let this = cx.undefined();
|
||||
|
||||
let Ok(res) = callback.call(&mut cx, this, [arg]) else {
|
||||
let _ = tx.send(Err("函数调用失败".into()));
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
if res.is_a::<JsPromise, _>(&mut cx) {
|
||||
let future = res
|
||||
.downcast::<JsPromise, _>(&mut cx)
|
||||
.unwrap()
|
||||
.to_future(&mut cx, move |mut cx, res| match res {
|
||||
Ok(res) => Ok(map_res(&mut cx, res)),
|
||||
Err(err) => Ok(map_res(&mut cx, err)),
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
run_async(async move {
|
||||
let res = future.await.unwrap();
|
||||
let _ = tx.send(res);
|
||||
});
|
||||
} else {
|
||||
let _ = tx.send(map_res(&mut cx, res));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
let res = rx.await;
|
||||
|
||||
match res {
|
||||
Ok(res) => res,
|
||||
Err(_) => Err("接收数据失败".into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
use std::future::Future;
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
static RUNTIME: OnceCell<Runtime> = OnceCell::new();
|
||||
|
||||
pub fn runtime() -> &'static Runtime {
|
||||
RUNTIME.get_or_try_init(Runtime::new).unwrap()
|
||||
}
|
||||
|
||||
pub fn run_async<F>(future: F)
|
||||
where
|
||||
F: Future + Send + 'static,
|
||||
F::Output: Send + 'static,
|
||||
{
|
||||
runtime().spawn(future);
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
use neon::prelude::Context;
|
||||
use neon::types::JsUint8Array;
|
||||
use open_xiaoai::base::{AppError, VERSION};
|
||||
use open_xiaoai::services::connect::data::{Event, Request, Response, Stream};
|
||||
use open_xiaoai::services::connect::handler::MessageHandler;
|
||||
use open_xiaoai::services::connect::message::{MessageManager, WsStream};
|
||||
use open_xiaoai::services::connect::rpc::RPC;
|
||||
use open_xiaoai::services::speaker::SpeakerManager;
|
||||
use open_xiaoai::utils::task::TaskManager;
|
||||
|
||||
use serde_json::json;
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio_tungstenite::accept_async;
|
||||
|
||||
use crate::node::NodeManager;
|
||||
|
||||
pub struct AppServer;
|
||||
|
||||
async fn test() -> Result<(), AppError> {
|
||||
SpeakerManager::play_text("已连接").await?;
|
||||
|
||||
// let _ = RPC::instance()
|
||||
// .call_remote("start_recording", None, None)
|
||||
// .await;
|
||||
|
||||
// let _ = RPC::instance().call_remote("start_play", None, None).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl AppServer {
|
||||
pub async fn connect(stream: TcpStream) -> Result<WsStream, AppError> {
|
||||
let ws_stream = accept_async(stream).await?;
|
||||
Ok(WsStream::Server(ws_stream))
|
||||
}
|
||||
|
||||
pub async fn run() {
|
||||
let addr = "0.0.0.0:4399";
|
||||
let listener = TcpListener::bind(&addr)
|
||||
.await
|
||||
.expect(format!("❌ 绑定地址失败: {}", &addr).as_str());
|
||||
println!("✅ 已启动: {:?}", addr);
|
||||
while let Ok((stream, addr)) = listener.accept().await {
|
||||
// 同一时刻只处理一个连接
|
||||
AppServer::handle_connection(stream, addr).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_connection(stream: TcpStream, addr: std::net::SocketAddr) {
|
||||
let Ok(ws_stream) = AppServer::connect(stream).await else {
|
||||
println!("❌ 连接异常: {}", addr);
|
||||
return;
|
||||
};
|
||||
println!("✅ 已连接: {:?}", addr);
|
||||
AppServer::init(ws_stream).await;
|
||||
if let Err(e) = MessageManager::instance().process_messages().await {
|
||||
println!("❌ 消息处理异常: {}", e);
|
||||
}
|
||||
AppServer::dispose().await;
|
||||
println!("❌ 已断开连接");
|
||||
}
|
||||
|
||||
async fn init(ws_stream: WsStream) {
|
||||
MessageManager::instance().init(ws_stream).await;
|
||||
MessageHandler::<Event>::instance()
|
||||
.set_handler(on_event)
|
||||
.await;
|
||||
MessageHandler::<Stream>::instance()
|
||||
.set_handler(on_stream)
|
||||
.await;
|
||||
|
||||
let rpc = RPC::instance();
|
||||
rpc.add_command("get_version", get_version).await;
|
||||
|
||||
let test = tokio::spawn(async move {
|
||||
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
let _ = test().await;
|
||||
});
|
||||
TaskManager::instance().add("test", test).await;
|
||||
}
|
||||
|
||||
async fn dispose() {
|
||||
MessageManager::instance().dispose().await;
|
||||
TaskManager::instance().dispose("test").await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_version(_: Request) -> Result<Response, AppError> {
|
||||
let data = json!(VERSION.to_string());
|
||||
Ok(Response::from_data(data))
|
||||
}
|
||||
|
||||
async fn on_stream(stream: Stream) -> Result<(), AppError> {
|
||||
let Stream { tag, bytes, .. } = stream;
|
||||
match tag.as_str() {
|
||||
"record" => {
|
||||
NodeManager::instance()
|
||||
.call_fn::<(), _, _>(
|
||||
"on_input_data",
|
||||
move |cx| JsUint8Array::from_slice(cx, &bytes).unwrap().upcast(),
|
||||
|_, _| Ok(()),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn on_event(event: Event) -> Result<(), AppError> {
|
||||
let event_json = serde_json::to_string(&event)?;
|
||||
NodeManager::instance()
|
||||
.call_fn::<(), _, _>(
|
||||
"on_event",
|
||||
move |cx| cx.string(&event_json).upcast(),
|
||||
|_, _| Ok(()),
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user