|
|
|
|
@ -75,11 +75,17 @@ impl SensorSource for MockSource {
|
|
|
|
|
{
|
|
|
|
|
let types = get_device_types(device);
|
|
|
|
|
let s: Pin<Box<dyn Stream<Item = Telemetry> + Send>> = if types.contains(&DeviceType::Trainer) {
|
|
|
|
|
// мощность рампой 150..300 Вт
|
|
|
|
|
// мощность 150..300 Вт + каденс 85..95 rpm (как FTMS 0x2AD2 — оба поля)
|
|
|
|
|
stream::unfold(0u16, |i| async move {
|
|
|
|
|
tokio::time::sleep(Duration::from_millis(500)).await;
|
|
|
|
|
let watts = 150 + ((i % 16) * 10) as i16;
|
|
|
|
|
Some((Telemetry::Power { watts }, i.wrapping_add(1)))
|
|
|
|
|
tokio::time::sleep(Duration::from_millis(250)).await;
|
|
|
|
|
let item = if i % 2 == 0 {
|
|
|
|
|
let watts = 150 + ((i / 2 % 16) * 10) as i16;
|
|
|
|
|
Telemetry::Power { watts }
|
|
|
|
|
} else {
|
|
|
|
|
let rpm = 85.0 + (i / 2 % 10) as f32;
|
|
|
|
|
Telemetry::Cadence { rpm }
|
|
|
|
|
};
|
|
|
|
|
Some((item, i.wrapping_add(1)))
|
|
|
|
|
})
|
|
|
|
|
.boxed()
|
|
|
|
|
} else if types.contains(&DeviceType::CadSensor) {
|
|
|
|
|
@ -142,4 +148,23 @@ mod tests {
|
|
|
|
|
let first = stream.next().await;
|
|
|
|
|
assert!(matches!(first, Some(Telemetry::HeartRate { .. })));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
|
async fn test_mock_trainer_emits_power_and_cadence() {
|
|
|
|
|
let src = MockSource::new();
|
|
|
|
|
let devices = src.scan(0).await.unwrap();
|
|
|
|
|
let trainer = devices.iter().find(|d| d.id == "mock-trainer").unwrap();
|
|
|
|
|
let mut stream = src.stream(trainer).await.unwrap();
|
|
|
|
|
let mut saw_power = false;
|
|
|
|
|
let mut saw_cadence = false;
|
|
|
|
|
for _ in 0..8 {
|
|
|
|
|
match stream.next().await {
|
|
|
|
|
Some(Telemetry::Power { .. }) => saw_power = true,
|
|
|
|
|
Some(Telemetry::Cadence { .. }) => saw_cadence = true,
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
assert!(saw_power, "тренер должен эмитить мощность");
|
|
|
|
|
assert!(saw_cadence, "тренер должен эмитить каденс");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|