From 62c7c88eabc58c178bb6cf8612213df5be3c042d Mon Sep 17 00:00:00 2001 From: Del Wang Date: Wed, 7 May 2025 23:08:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20KwsMonitor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/kws/README.md | 2 +- packages/client-rust/src/bin/client.rs | 24 +++++--- .../client-rust/src/services/monitor/file.rs | 10 +--- .../client-rust/src/services/monitor/kws.rs | 58 +++++++++++++++++++ .../client-rust/src/services/monitor/mod.rs | 1 + .../src/services/monitor/playing.rs | 17 +----- 6 files changed, 81 insertions(+), 31 deletions(-) create mode 100644 packages/client-rust/src/services/monitor/kws.rs diff --git a/examples/kws/README.md b/examples/kws/README.md index 9da887f..31c9dba 100644 --- a/examples/kws/README.md +++ b/examples/kws/README.md @@ -10,7 +10,7 @@ ## 刷机 -首先按照 [open-xiaoai](https://github.com/idootop/open-xiaoai) 里的教程将小爱音箱刷机, 然后 SSH 连接到小爱音箱。 +首先按照 [open-xiaoai](https://github.com/idootop/open-xiaoai) 里的教程将小爱音箱刷机,然后 SSH 连接到小爱音箱。 ## 安装脚本 diff --git a/packages/client-rust/src/bin/client.rs b/packages/client-rust/src/bin/client.rs index 2f7bb34..38b4b90 100644 --- a/packages/client-rust/src/bin/client.rs +++ b/packages/client-rust/src/bin/client.rs @@ -1,4 +1,5 @@ use open_xiaoai::services::audio::config::AudioConfig; +use open_xiaoai::services::monitor::kws::KwsMonitor; use serde_json::json; use std::time::Duration; use tokio::time::sleep; @@ -65,13 +66,19 @@ impl AppClient { }) .await; - PlayingMonitor::instance() - .start(|event| async move { - MessageManager::instance() - .send_event("playing", Some(json!(event))) - .await - }) - .await; + PlayingMonitor::start(|event| async move { + MessageManager::instance() + .send_event("playing", Some(json!(event))) + .await + }) + .await; + + KwsMonitor::start(|event| async move { + MessageManager::instance() + .send_event("kws", Some(json!(event))) + .await + }) + .await; } async fn dispose() { @@ -79,7 +86,8 @@ impl AppClient { let _ = AudioPlayer::instance().stop().await; let _ = AudioRecorder::instance().stop_recording().await; InstructionMonitor::stop().await; - PlayingMonitor::instance().stop().await; + PlayingMonitor::stop().await; + KwsMonitor::stop().await; } } diff --git a/packages/client-rust/src/services/monitor/file.rs b/packages/client-rust/src/services/monitor/file.rs index e57897c..3c3bb90 100644 --- a/packages/client-rust/src/services/monitor/file.rs +++ b/packages/client-rust/src/services/monitor/file.rs @@ -56,7 +56,7 @@ impl FileMonitor { Fut: Future> + Send + 'static, { while !Path::new(file_path).exists() { - sleep(Duration::from_millis(100)).await; + sleep(Duration::from_millis(10)).await; } let file = OpenOptions::new().read(true).open(file_path).await?; @@ -79,7 +79,6 @@ impl FileMonitor { } let mut line = String::new(); - let mut new_content_found = false; while let Ok(bytes_read) = reader.read_line(&mut line).await { if bytes_read == 0 { @@ -88,7 +87,6 @@ impl FileMonitor { let trimmed_line = line.trim(); if !trimmed_line.is_empty() { - new_content_found = true; let _ = on_update(FileMonitorEvent::NewLine(trimmed_line.to_string())).await; } @@ -96,11 +94,7 @@ impl FileMonitor { line.clear(); } - if !new_content_found { - sleep(Duration::from_millis(100)).await; - } else { - sleep(Duration::from_millis(10)).await; - } + sleep(Duration::from_millis(10)).await; } } } diff --git a/packages/client-rust/src/services/monitor/kws.rs b/packages/client-rust/src/services/monitor/kws.rs new file mode 100644 index 0000000..b81c463 --- /dev/null +++ b/packages/client-rust/src/services/monitor/kws.rs @@ -0,0 +1,58 @@ +use std::future::Future; +use std::sync::atomic::{AtomicI32, Ordering}; +use std::sync::Arc; + +use serde::{Deserialize, Serialize}; + +use crate::base::AppError; + +use super::file::{FileMonitor, FileMonitorEvent}; + +#[derive(Debug, Serialize, Deserialize)] +pub enum KwsMonitorEvent { + Started, + Keyword(String), +} + +pub struct KwsMonitor; + +static KWS_FILE_PATH: &str = "/tmp/open-xiaoai/kws.log"; + +static LAST_TIMESTAMP: AtomicI32 = AtomicI32::new(0); + +impl KwsMonitor { + pub async fn start(on_update: F) + where + F: Fn(KwsMonitorEvent) -> Fut + Send + Sync + 'static, + Fut: Future> + Send + 'static, + { + let on_update = Arc::new(on_update); + FileMonitor::instance() + .start(KWS_FILE_PATH, move |event| { + let on_update = Arc::clone(&on_update); + async move { + if let FileMonitorEvent::NewLine(content) = event { + let data = content.split(' ').collect::>(); + let timestamp = data[0].parse::().unwrap(); + let keyword = data[1].to_string(); + let last_timestamp = LAST_TIMESTAMP.load(Ordering::Relaxed); + if timestamp != last_timestamp { + LAST_TIMESTAMP.store(timestamp, Ordering::Relaxed); + let kws_event = if keyword == "__STARTED__" { + KwsMonitorEvent::Started + } else { + KwsMonitorEvent::Keyword(keyword) + }; + let _ = on_update(kws_event).await; + } + } + Ok(()) + } + }) + .await; + } + + pub async fn stop() { + FileMonitor::instance().stop(KWS_FILE_PATH).await; + } +} diff --git a/packages/client-rust/src/services/monitor/mod.rs b/packages/client-rust/src/services/monitor/mod.rs index 8ab06ae..994c843 100644 --- a/packages/client-rust/src/services/monitor/mod.rs +++ b/packages/client-rust/src/services/monitor/mod.rs @@ -1,3 +1,4 @@ pub mod file; pub mod instruction; +pub mod kws; pub mod playing; diff --git a/packages/client-rust/src/services/monitor/playing.rs b/packages/client-rust/src/services/monitor/playing.rs index 69123c5..e5e293c 100644 --- a/packages/client-rust/src/services/monitor/playing.rs +++ b/packages/client-rust/src/services/monitor/playing.rs @@ -1,6 +1,5 @@ use serde::{Deserialize, Serialize}; use std::future::Future; -use std::sync::LazyLock; use tokio::time::{sleep, Duration}; use crate::base::AppError; @@ -16,18 +15,8 @@ pub enum PlayingMonitorEvent { pub struct PlayingMonitor; -static INSTANCE: LazyLock = LazyLock::new(PlayingMonitor::new); - impl PlayingMonitor { - fn new() -> Self { - Self {} - } - - pub fn instance() -> &'static Self { - &INSTANCE - } - - pub async fn start(&self, on_update: F) + pub async fn start(on_update: F) where F: Fn(PlayingMonitorEvent) -> Fut + Send + Sync + 'static, Fut: Future> + Send + 'static, @@ -39,7 +28,7 @@ impl PlayingMonitor { TaskManager::instance().add("PlayingMonitor", monitor).await; } - pub async fn stop(&self) { + pub async fn stop() { TaskManager::instance().dispose("PlayingMonitor").await; } @@ -64,7 +53,7 @@ impl PlayingMonitor { let _ = on_update(status).await; } - sleep(Duration::from_millis(100)).await; + sleep(Duration::from_millis(10)).await; } } }