feat: 新增 KwsMonitor
This commit is contained in:
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
## 刷机
|
## 刷机
|
||||||
|
|
||||||
首先按照 [open-xiaoai](https://github.com/idootop/open-xiaoai) 里的教程将小爱音箱刷机, 然后 SSH 连接到小爱音箱。
|
首先按照 [open-xiaoai](https://github.com/idootop/open-xiaoai) 里的教程将小爱音箱刷机,然后 SSH 连接到小爱音箱。
|
||||||
|
|
||||||
## 安装脚本
|
## 安装脚本
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use open_xiaoai::services::audio::config::AudioConfig;
|
use open_xiaoai::services::audio::config::AudioConfig;
|
||||||
|
use open_xiaoai::services::monitor::kws::KwsMonitor;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
@@ -65,13 +66,19 @@ impl AppClient {
|
|||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
PlayingMonitor::instance()
|
PlayingMonitor::start(|event| async move {
|
||||||
.start(|event| async move {
|
MessageManager::instance()
|
||||||
MessageManager::instance()
|
.send_event("playing", Some(json!(event)))
|
||||||
.send_event("playing", Some(json!(event)))
|
.await
|
||||||
.await
|
})
|
||||||
})
|
.await;
|
||||||
.await;
|
|
||||||
|
KwsMonitor::start(|event| async move {
|
||||||
|
MessageManager::instance()
|
||||||
|
.send_event("kws", Some(json!(event)))
|
||||||
|
.await
|
||||||
|
})
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn dispose() {
|
async fn dispose() {
|
||||||
@@ -79,7 +86,8 @@ impl AppClient {
|
|||||||
let _ = AudioPlayer::instance().stop().await;
|
let _ = AudioPlayer::instance().stop().await;
|
||||||
let _ = AudioRecorder::instance().stop_recording().await;
|
let _ = AudioRecorder::instance().stop_recording().await;
|
||||||
InstructionMonitor::stop().await;
|
InstructionMonitor::stop().await;
|
||||||
PlayingMonitor::instance().stop().await;
|
PlayingMonitor::stop().await;
|
||||||
|
KwsMonitor::stop().await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ impl FileMonitor {
|
|||||||
Fut: Future<Output = Result<(), AppError>> + Send + 'static,
|
Fut: Future<Output = Result<(), AppError>> + Send + 'static,
|
||||||
{
|
{
|
||||||
while !Path::new(file_path).exists() {
|
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?;
|
let file = OpenOptions::new().read(true).open(file_path).await?;
|
||||||
@@ -79,7 +79,6 @@ impl FileMonitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut line = String::new();
|
let mut line = String::new();
|
||||||
let mut new_content_found = false;
|
|
||||||
|
|
||||||
while let Ok(bytes_read) = reader.read_line(&mut line).await {
|
while let Ok(bytes_read) = reader.read_line(&mut line).await {
|
||||||
if bytes_read == 0 {
|
if bytes_read == 0 {
|
||||||
@@ -88,7 +87,6 @@ impl FileMonitor {
|
|||||||
|
|
||||||
let trimmed_line = line.trim();
|
let trimmed_line = line.trim();
|
||||||
if !trimmed_line.is_empty() {
|
if !trimmed_line.is_empty() {
|
||||||
new_content_found = true;
|
|
||||||
let _ = on_update(FileMonitorEvent::NewLine(trimmed_line.to_string())).await;
|
let _ = on_update(FileMonitorEvent::NewLine(trimmed_line.to_string())).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,11 +94,7 @@ impl FileMonitor {
|
|||||||
line.clear();
|
line.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if !new_content_found {
|
sleep(Duration::from_millis(10)).await;
|
||||||
sleep(Duration::from_millis(100)).await;
|
|
||||||
} else {
|
|
||||||
sleep(Duration::from_millis(10)).await;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<F, Fut>(on_update: F)
|
||||||
|
where
|
||||||
|
F: Fn(KwsMonitorEvent) -> Fut + Send + Sync + 'static,
|
||||||
|
Fut: Future<Output = Result<(), AppError>> + 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::<Vec<&str>>();
|
||||||
|
let timestamp = data[0].parse::<i32>().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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
pub mod file;
|
pub mod file;
|
||||||
pub mod instruction;
|
pub mod instruction;
|
||||||
|
pub mod kws;
|
||||||
pub mod playing;
|
pub mod playing;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::sync::LazyLock;
|
|
||||||
use tokio::time::{sleep, Duration};
|
use tokio::time::{sleep, Duration};
|
||||||
|
|
||||||
use crate::base::AppError;
|
use crate::base::AppError;
|
||||||
@@ -16,18 +15,8 @@ pub enum PlayingMonitorEvent {
|
|||||||
|
|
||||||
pub struct PlayingMonitor;
|
pub struct PlayingMonitor;
|
||||||
|
|
||||||
static INSTANCE: LazyLock<PlayingMonitor> = LazyLock::new(PlayingMonitor::new);
|
|
||||||
|
|
||||||
impl PlayingMonitor {
|
impl PlayingMonitor {
|
||||||
fn new() -> Self {
|
pub async fn start<F, Fut>(on_update: F)
|
||||||
Self {}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn instance() -> &'static Self {
|
|
||||||
&INSTANCE
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn start<F, Fut>(&self, on_update: F)
|
|
||||||
where
|
where
|
||||||
F: Fn(PlayingMonitorEvent) -> Fut + Send + Sync + 'static,
|
F: Fn(PlayingMonitorEvent) -> Fut + Send + Sync + 'static,
|
||||||
Fut: Future<Output = Result<(), AppError>> + Send + 'static,
|
Fut: Future<Output = Result<(), AppError>> + Send + 'static,
|
||||||
@@ -39,7 +28,7 @@ impl PlayingMonitor {
|
|||||||
TaskManager::instance().add("PlayingMonitor", monitor).await;
|
TaskManager::instance().add("PlayingMonitor", monitor).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn stop(&self) {
|
pub async fn stop() {
|
||||||
TaskManager::instance().dispose("PlayingMonitor").await;
|
TaskManager::instance().dispose("PlayingMonitor").await;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +53,7 @@ impl PlayingMonitor {
|
|||||||
let _ = on_update(status).await;
|
let _ = on_update(status).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
sleep(Duration::from_millis(100)).await;
|
sleep(Duration::from_millis(10)).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user