Add BPM tracking
This commit is contained in:
parent
e766b560c7
commit
898c11a67b
7
src-tauri/Cargo.lock
generated
7
src-tauri/Cargo.lock
generated
@ -863,6 +863,12 @@ dependencies = [
|
|||||||
"rustc_version",
|
"rustc_version",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "find_peaks"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "17ba6fe8df99019a8cb0c712414e10081b3bbdb10d8f7ab95719863ced5db982"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.0.33"
|
version = "1.0.33"
|
||||||
@ -3524,6 +3530,7 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
"find_peaks",
|
||||||
"log",
|
"log",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"serde",
|
"serde",
|
||||||
|
|||||||
@ -23,6 +23,7 @@ log = "0.4.22"
|
|||||||
env_logger = "0.11.5"
|
env_logger = "0.11.5"
|
||||||
tokio = { version = "1.41.1", features = ["full"] }
|
tokio = { version = "1.41.1", features = ["full"] }
|
||||||
thiserror = "2.0.3"
|
thiserror = "2.0.3"
|
||||||
|
find_peaks = "0.1.5"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!!
|
# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!!
|
||||||
|
|||||||
@ -87,12 +87,12 @@ fn create_data_listener(websockets_uri: String, data: Arc<Mutex<Vec<ECGData>>>)
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|data| {
|
.filter(|data| {
|
||||||
Utc::now().signed_duration_since(data.timestamp)
|
Utc::now().signed_duration_since(data.timestamp)
|
||||||
<= Duration::seconds(5)
|
<= Duration::minutes(1)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
ecg_data.push(ECGData {
|
ecg_data.push(ECGData {
|
||||||
value: voltage,
|
value: -voltage,
|
||||||
timestamp: Utc::now(),
|
timestamp: Utc::now(),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -107,9 +107,28 @@ fn create_data_listener(websockets_uri: String, data: Arc<Mutex<Vec<ECGData>>>)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
fn calculate_heartrate(data: State<'_, Arc<Mutex<Vec<ECGData>>>>) -> Result<usize, Error> {
|
||||||
|
let data = data.lock().expect("Failed to grab mutex lock").clone();
|
||||||
|
|
||||||
|
let filtered_data: Vec<f32> = data.into_iter().map(|entry| entry.value).collect();
|
||||||
|
|
||||||
|
let mut peak_finder = find_peaks::PeakFinder::new(&filtered_data);
|
||||||
|
peak_finder.with_min_prominence(1.);
|
||||||
|
//peak_finder.with_min_height(0.);
|
||||||
|
|
||||||
|
Ok(peak_finder.find_peaks().len())
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn get_ecg_data(data: State<'_, Arc<Mutex<Vec<ECGData>>>>) -> Result<Vec<ECGData>, Error> {
|
async fn get_ecg_data(data: State<'_, Arc<Mutex<Vec<ECGData>>>>) -> Result<Vec<ECGData>, Error> {
|
||||||
Ok(data.lock().expect("Failed to get lock").clone())
|
Ok(data
|
||||||
|
.lock()
|
||||||
|
.expect("Failed to get lock")
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.filter(|data| Utc::now().signed_duration_since(data.timestamp) <= Duration::seconds(5))
|
||||||
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
@ -120,11 +139,17 @@ async fn main() {
|
|||||||
let ecg_data: Arc<Mutex<Vec<ECGData>>> = Arc::new(Mutex::new(Vec::with_capacity(2 ^ 13)));
|
let ecg_data: Arc<Mutex<Vec<ECGData>>> = Arc::new(Mutex::new(Vec::with_capacity(2 ^ 13)));
|
||||||
|
|
||||||
// Create the websockets listener
|
// Create the websockets listener
|
||||||
create_data_listener("ws://192.168.128.50:81".to_string(), ecg_data.clone());
|
//create_data_listener("ws://192.168.128.50:81".to_string(), ecg_data.clone());
|
||||||
|
create_data_listener("ws://192.168.66.210:81".to_string(), ecg_data.clone());
|
||||||
|
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
.plugin(tauri_plugin_shell::init())
|
.plugin(tauri_plugin_shell::init())
|
||||||
.invoke_handler(tauri::generate_handler![greet, random_data, get_ecg_data])
|
.invoke_handler(tauri::generate_handler![
|
||||||
|
greet,
|
||||||
|
random_data,
|
||||||
|
get_ecg_data,
|
||||||
|
calculate_heartrate
|
||||||
|
])
|
||||||
.manage(ecg_data)
|
.manage(ecg_data)
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
|
|||||||
19
src/App.vue
19
src/App.vue
@ -3,21 +3,28 @@
|
|||||||
// Check out https://vuejs.org/api/sfc-script-setup.html#script-setup
|
// Check out https://vuejs.org/api/sfc-script-setup.html#script-setup
|
||||||
import Greet from "./components/Greet.vue";
|
import Greet from "./components/Greet.vue";
|
||||||
import Graph from "./components/Graph.vue";
|
import Graph from "./components/Graph.vue";
|
||||||
/*
|
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { onMounted } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
|
|
||||||
|
let bpm = ref(0);
|
||||||
|
|
||||||
|
async function refresh_bpm() {
|
||||||
|
bpm.value = await invoke("calculate_heartrate");
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await invoke("create_data_listener", {
|
await refresh_bpm();
|
||||||
websocketsUri: "ws://192.168.128.50:81",
|
|
||||||
});
|
setInterval(() => {
|
||||||
|
refresh_bpm();
|
||||||
|
}, 250);
|
||||||
});
|
});
|
||||||
*/
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<Graph />
|
<Graph />
|
||||||
|
<h3>Pulse: {{ bpm }} bpm</h3>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@ -32,7 +32,7 @@ export default {
|
|||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
// Ensure datasets is an array
|
// Ensure datasets is an array
|
||||||
label: "My Dataset", // Add a label for the dataset
|
label: "ECG Measurement", // Add a label for the dataset
|
||||||
data: [], // Initial data
|
data: [], // Initial data
|
||||||
fill: false, // Set to true if you want the area under the line to be filled
|
fill: false, // Set to true if you want the area under the line to be filled
|
||||||
borderColor: "blue", // Set the line color
|
borderColor: "blue", // Set the line color
|
||||||
@ -51,9 +51,16 @@ export default {
|
|||||||
x: {
|
x: {
|
||||||
ticks: {
|
ticks: {
|
||||||
callback: (value, index, values) => {
|
callback: (value, index, values) => {
|
||||||
|
/*
|
||||||
// Show only 5 timestamps on the x-axis
|
// Show only 5 timestamps on the x-axis
|
||||||
const step = Math.floor(values.length / 5);
|
const step = Math.floor(values.length / 5);
|
||||||
return index % step === 0 ? chartData.value.labels[index] : ""; // Show the label only for selected indices
|
return index % step === 0 ? chartData.value.labels[index] : ""; // Show the label only for selected indices
|
||||||
|
*/
|
||||||
|
const totalLabels = values.length;
|
||||||
|
const step = Math.ceil(totalLabels / 5); // Use ceil to ensure we cover all labels
|
||||||
|
return index % step === 0 && index < totalLabels
|
||||||
|
? chartData.value.labels[index]
|
||||||
|
: ""; // Show the label only for selected indices
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -75,7 +82,7 @@ export default {
|
|||||||
labels: timestamps,
|
labels: timestamps,
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
label: "My Dataset", // Add a label for the dataset
|
label: "ECG Measurement", // Add a label for the dataset
|
||||||
data: values, // Access the data property
|
data: values, // Access the data property
|
||||||
fill: false, // Set to true if you want the area under the line to be filled
|
fill: false, // Set to true if you want the area under the line to be filled
|
||||||
borderColor: "green", // Set the line color
|
borderColor: "green", // Set the line color
|
||||||
@ -103,7 +110,7 @@ export default {
|
|||||||
labels: timestamps,
|
labels: timestamps,
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
label: "My Dataset",
|
label: "ECG Measurement",
|
||||||
data: values,
|
data: values,
|
||||||
fill: false,
|
fill: false,
|
||||||
borderColor: "green",
|
borderColor: "green",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user