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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "find_peaks"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17ba6fe8df99019a8cb0c712414e10081b3bbdb10d8f7ab95719863ced5db982"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.33"
|
||||
@ -3524,6 +3530,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
"env_logger",
|
||||
"find_peaks",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
|
||||
@ -23,6 +23,7 @@ log = "0.4.22"
|
||||
env_logger = "0.11.5"
|
||||
tokio = { version = "1.41.1", features = ["full"] }
|
||||
thiserror = "2.0.3"
|
||||
find_peaks = "0.1.5"
|
||||
|
||||
[features]
|
||||
# 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()
|
||||
.filter(|data| {
|
||||
Utc::now().signed_duration_since(data.timestamp)
|
||||
<= Duration::seconds(5)
|
||||
<= Duration::minutes(1)
|
||||
})
|
||||
.collect();
|
||||
|
||||
ecg_data.push(ECGData {
|
||||
value: voltage,
|
||||
value: -voltage,
|
||||
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]
|
||||
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]
|
||||
@ -120,11 +139,17 @@ async fn main() {
|
||||
let ecg_data: Arc<Mutex<Vec<ECGData>>> = Arc::new(Mutex::new(Vec::with_capacity(2 ^ 13)));
|
||||
|
||||
// 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()
|
||||
.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)
|
||||
.run(tauri::generate_context!())
|
||||
.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
|
||||
import Greet from "./components/Greet.vue";
|
||||
import Graph from "./components/Graph.vue";
|
||||
/*
|
||||
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 () => {
|
||||
await invoke("create_data_listener", {
|
||||
websocketsUri: "ws://192.168.128.50:81",
|
||||
});
|
||||
await refresh_bpm();
|
||||
|
||||
setInterval(() => {
|
||||
refresh_bpm();
|
||||
}, 250);
|
||||
});
|
||||
*/
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container">
|
||||
<Graph />
|
||||
<h3>Pulse: {{ bpm }} bpm</h3>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@ -32,7 +32,7 @@ export default {
|
||||
datasets: [
|
||||
{
|
||||
// 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
|
||||
fill: false, // Set to true if you want the area under the line to be filled
|
||||
borderColor: "blue", // Set the line color
|
||||
@ -51,9 +51,16 @@ export default {
|
||||
x: {
|
||||
ticks: {
|
||||
callback: (value, index, values) => {
|
||||
/*
|
||||
// Show only 5 timestamps on the x-axis
|
||||
const step = Math.floor(values.length / 5);
|
||||
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,
|
||||
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
|
||||
fill: false, // Set to true if you want the area under the line to be filled
|
||||
borderColor: "green", // Set the line color
|
||||
@ -103,7 +110,7 @@ export default {
|
||||
labels: timestamps,
|
||||
datasets: [
|
||||
{
|
||||
label: "My Dataset",
|
||||
label: "ECG Measurement",
|
||||
data: values,
|
||||
fill: false,
|
||||
borderColor: "green",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user