Buttplug sex toy control library

chore: Remove FeatureType, fix more Sensor/Actuator naming

FeatureType is just too ambiguous and hard to deal with.

Fixes #774

+355 -573
+6 -5
crates/buttplug_client/src/device/device.rs
··· 15 15 ButtplugClientMessageSender, 16 16 ButtplugClientResultFuture, 17 17 }; 18 + use buttplug_core::message::InputType; 18 19 use buttplug_core::{ 19 20 errors::ButtplugDeviceError, 20 21 message::{ 21 - ButtplugServerMessageV4, DeviceFeature, DeviceMessageInfoV4, FeatureType, OutputType, StopDeviceCmdV0 22 + ButtplugServerMessageV4, DeviceFeature, DeviceMessageInfoV4, OutputType, StopDeviceCmdV0 22 23 }, 23 24 util::stream::convert_broadcast_receiver_to_stream, 24 25 }; ··· 211 212 self 212 213 .device_features 213 214 .iter() 214 - .any(|x| x.1.feature().feature_type() == FeatureType::Battery) 215 + .any(|x| x.1.feature().input().as_ref().is_some_and(|x| x.contains_key(&InputType::Battery))) 215 216 } 216 217 217 218 pub fn battery_level(&self) -> ButtplugClientResultFuture<u32> { 218 219 if let Some(battery) = self 219 220 .device_features 220 221 .iter() 221 - .find(|x| x.1.feature().feature_type() == FeatureType::Battery) 222 + .find(|x| x.1.feature().input().as_ref().is_some_and(|x| x.contains_key(&InputType::Battery))) 222 223 { 223 224 battery.1.battery_level() 224 225 } else { ··· 235 236 self 236 237 .device_features 237 238 .iter() 238 - .any(|x| x.1.feature().feature_type() == FeatureType::Rssi) 239 + .any(|x| x.1.feature().input().as_ref().is_some_and(|x| x.contains_key(&InputType::Rssi))) 239 240 } 240 241 241 242 pub fn rssi_level(&self) -> ButtplugClientResultFuture<i8> { 242 243 if let Some(rssi) = self 243 244 .device_features 244 245 .iter() 245 - .find(|x| x.1.feature().feature_type() == FeatureType::Rssi) 246 + .find(|x| x.1.feature().input().as_ref().is_some_and(|x| x.contains_key(&InputType::Rssi))) 246 247 { 247 248 rssi.1.rssi_level() 248 249 } else {
+6 -7
crates/buttplug_core/src/errors.rs
··· 12 12 serializer::ButtplugSerializerError, 13 13 ButtplugMessageSpecVersion, 14 14 ErrorCode, 15 - FeatureType, 16 15 InputType, 17 16 OutputType, 18 17 }; ··· 172 171 UntypedDeserializedError(String), 173 172 /// Device Configuration Error: {0} 174 173 DeviceConfigurationError(String), 175 - /// Actuator Type Mismatch: Index {0} got command for {1}, but expects {2} 176 - DeviceActuatorTypeMismatch(u32, OutputType, FeatureType), 177 - /// Sensor Type Mismatch: Index {0} got command for {1}, but expects {2} 178 - DeviceSensorTypeMismatch(u32, InputType, FeatureType), 174 + /// Output Type Mismatch: Index {0} got command for {1}, which is not valid 175 + DeviceOutputTypeMismatch(u32, OutputType, OutputType), 176 + /// Input Type Mismatch: Index {0} got command for {1}, which is not valid 177 + DeviceInputTypeMismatch(u32, InputType), 179 178 /// Protocol does not have an implementation available for Sensor Type {0} 180 - ProtocolSensorNotSupported(InputType), 179 + ProtocolInputNotSupported(InputType), 181 180 /// Device does not support {0} 182 - ActuatorNotSupported(OutputType), 181 + OutputNotSupported(OutputType), 183 182 } 184 183 185 184 /// Unknown errors occur in exceptional circumstances where no other error type
-108
crates/buttplug_core/src/message/device_feature.rs
··· 13 13 ops::RangeInclusive, 14 14 }; 15 15 16 - #[derive(Debug, Default, Display, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, EnumIter)] 17 - pub enum FeatureType { 18 - #[default] 19 - // Used for when types are added that we do not know how to handle 20 - Unknown, 21 - // Level/ValueCmd types 22 - Vibrate, 23 - // Single Direction Rotation Speed 24 - Rotate, 25 - Oscillate, 26 - Constrict, 27 - Spray, 28 - Heater, 29 - Led, 30 - // For instances where we specify a position to move to ASAP. Usually servos, probably for the 31 - // OSR-2/SR-6. 32 - Position, 33 - // ValueWithParameterCmd types 34 - // Two Direction Rotation Speed 35 - RotateWithDirection, 36 - PositionWithDuration, 37 - // Might be useful but dunno if we need it yet, or how to convey "speed" units 38 - // PositionWithSpeed 39 - // Sensor Types 40 - Battery, 41 - Rssi, 42 - Button, 43 - Pressure, 44 - // Currently unused but possible sensor features: 45 - // Temperature, 46 - // Accelerometer, 47 - // Gyro, 48 - // 49 - } 50 - 51 16 #[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash, EnumIter)] 52 17 pub enum OutputType { 53 18 Unknown, ··· 80 45 // Inflate, 81 46 } 82 47 83 - impl TryFrom<FeatureType> for OutputType { 84 - type Error = String; 85 - fn try_from(value: FeatureType) -> Result<Self, Self::Error> { 86 - match value { 87 - FeatureType::Unknown => Ok(OutputType::Unknown), 88 - FeatureType::Vibrate => Ok(OutputType::Vibrate), 89 - FeatureType::Rotate => Ok(OutputType::Rotate), 90 - FeatureType::Heater => Ok(OutputType::Heater), 91 - FeatureType::Led => Ok(OutputType::Led), 92 - FeatureType::RotateWithDirection => Ok(OutputType::RotateWithDirection), 93 - FeatureType::PositionWithDuration => Ok(OutputType::PositionWithDuration), 94 - FeatureType::Oscillate => Ok(OutputType::Oscillate), 95 - FeatureType::Constrict => Ok(OutputType::Constrict), 96 - FeatureType::Spray => Ok(OutputType::Spray), 97 - FeatureType::Position => Ok(OutputType::Position), 98 - _ => Err(format!( 99 - "Feature type {value} not valid for OutputType conversion" 100 - )), 101 - } 102 - } 103 - } 104 - 105 48 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Display, Hash, EnumIter)] 106 49 pub enum InputType { 107 50 Unknown, ··· 118 61 // Gyro, 119 62 } 120 63 121 - impl TryFrom<FeatureType> for InputType { 122 - type Error = String; 123 - fn try_from(value: FeatureType) -> Result<Self, Self::Error> { 124 - match value { 125 - FeatureType::Unknown => Ok(InputType::Unknown), 126 - FeatureType::Battery => Ok(InputType::Battery), 127 - FeatureType::Rssi => Ok(InputType::Rssi), 128 - FeatureType::Button => Ok(InputType::Button), 129 - FeatureType::Pressure => Ok(InputType::Pressure), 130 - _ => Err(format!( 131 - "Feature type {value} not valid for SensorType conversion" 132 - )), 133 - } 134 - } 135 - } 136 - 137 - impl From<OutputType> for FeatureType { 138 - fn from(value: OutputType) -> Self { 139 - match value { 140 - OutputType::Unknown => FeatureType::Unknown, 141 - OutputType::Vibrate => FeatureType::Vibrate, 142 - OutputType::Rotate => FeatureType::Rotate, 143 - OutputType::Heater => FeatureType::Heater, 144 - OutputType::Led => FeatureType::Led, 145 - OutputType::RotateWithDirection => FeatureType::RotateWithDirection, 146 - OutputType::PositionWithDuration => FeatureType::PositionWithDuration, 147 - OutputType::Oscillate => FeatureType::Oscillate, 148 - OutputType::Constrict => FeatureType::Constrict, 149 - OutputType::Spray => FeatureType::Spray, 150 - OutputType::Position => FeatureType::Position, 151 - } 152 - } 153 - } 154 - 155 - impl From<InputType> for FeatureType { 156 - fn from(value: InputType) -> Self { 157 - match value { 158 - InputType::Unknown => FeatureType::Unknown, 159 - InputType::Battery => FeatureType::Battery, 160 - InputType::Rssi => FeatureType::Rssi, 161 - InputType::Button => FeatureType::Button, 162 - InputType::Pressure => FeatureType::Pressure, 163 - } 164 - } 165 - } 166 - 167 64 // This will look almost exactly like ServerDeviceFeature. However, it will only contain 168 65 // information we want the client to know, i.e. step counts versus specific step ranges. This is 169 66 // what will be sent to the client as part of DeviceAdded/DeviceList messages. It should not be used ··· 186 83 #[serde(default)] 187 84 #[serde(rename = "FeatureDescription")] 188 85 description: String, 189 - #[getset(get_copy = "pub")] 190 - #[serde(rename = "FeatureType")] 191 - feature_type: FeatureType, 192 86 #[getset(get = "pub")] 193 87 #[serde(skip_serializing_if = "Option::is_none")] 194 88 #[serde(rename = "Output")] ··· 203 97 pub fn new( 204 98 index: u32, 205 99 description: &str, 206 - feature_type: FeatureType, 207 100 output: &Option<HashMap<OutputType, DeviceFeatureOutput>>, 208 101 input: &Option<HashMap<InputType, DeviceFeatureInput>>, 209 102 ) -> Self { 210 103 Self { 211 104 feature_index: index, 212 105 description: description.to_owned(), 213 - feature_type, 214 106 output: output.clone(), 215 107 input: input.clone(), 216 108 }
+1 -1
crates/buttplug_core/src/message/v4/output_cmd.rs
··· 137 137 OutputType::Rotate => Ok(Self::Rotate(OutputValue::new(value))), 138 138 OutputType::Vibrate => Ok(Self::Vibrate(OutputValue::new(value))), 139 139 x => Err(ButtplugError::ButtplugDeviceError( 140 - ButtplugDeviceError::ActuatorNotSupported(x), 140 + ButtplugDeviceError::OutputNotSupported(x), 141 141 )), 142 142 } 143 143 }
+3 -4
crates/buttplug_server/src/device/protocol_impl/hismith_mini.rs
··· 11 11 }; 12 12 use async_trait::async_trait; 13 13 use buttplug_core::{ 14 - errors::ButtplugDeviceError, 15 - message::FeatureType, 14 + errors::ButtplugDeviceError, message::OutputType, 16 15 }; 17 16 use buttplug_server_device_config::{ 18 17 Endpoint, ··· 88 87 dual_vibe: device_definition 89 88 .features() 90 89 .iter() 91 - .filter(|x| x.feature_type() == FeatureType::Vibrate) 90 + .filter(|x| x.output().as_ref().is_some_and(|x| x.contains_key(&OutputType::Vibrate))) 92 91 .count() 93 92 >= 2, 94 93 second_constrict: device_definition 95 94 .features() 96 95 .iter() 97 - .position(|x| x.feature_type() == FeatureType::Constrict) 96 + .position(|x| x.output().as_ref().is_some_and(|x| x.contains_key(&OutputType::Constrict))) 98 97 .unwrap_or(0) 99 98 == 1, 100 99 }))
+6 -6
crates/buttplug_server/src/device/protocol_impl/lovense/mod.rs
··· 29 29 use async_trait::async_trait; 30 30 use buttplug_core::{ 31 31 errors::ButtplugDeviceError, 32 - message::{self, FeatureType, InputData, InputReadingV4, InputTypeData}, 32 + message::{self, InputData, InputReadingV4, InputTypeData, OutputType}, 33 33 util::sleep, 34 34 }; 35 35 use buttplug_server_device_config::{ ··· 175 175 let vibrator_count = device_definition 176 176 .features() 177 177 .iter() 178 - .filter(|x| [FeatureType::Vibrate, FeatureType::Oscillate].contains(&x.feature_type())) 178 + .filter(|x| x.output().as_ref().is_some_and(|x| x.contains_key(&OutputType::Vibrate) || x.contains_key(&OutputType::Oscillate))) 179 179 .count(); 180 180 181 181 let output_count = device_definition ··· 188 188 && device_definition 189 189 .features() 190 190 .iter() 191 - .filter(|x| x.feature_type() == FeatureType::Vibrate) 191 + .filter(|x| x.output().as_ref().is_some_and(|x| x.contains_key(&OutputType::Vibrate))) 192 192 .count() 193 193 == 1 194 194 && device_definition 195 195 .features() 196 196 .iter() 197 - .filter(|x| x.feature_type() == FeatureType::RotateWithDirection) 197 + .filter(|x| x.output().as_ref().is_some_and(|x| x.contains_key(&OutputType::RotateWithDirection))) 198 198 .count() 199 199 == 1; 200 200 ··· 202 202 && device_definition 203 203 .features() 204 204 .iter() 205 - .filter(|x| x.feature_type() == FeatureType::Vibrate) 205 + .filter(|x| x.output().as_ref().is_some_and(|x| x.contains_key(&OutputType::Vibrate))) 206 206 .count() 207 207 == 1 208 208 && device_definition 209 209 .features() 210 210 .iter() 211 - .filter(|x| x.feature_type() == FeatureType::Constrict) 211 + .filter(|x| x.output().as_ref().is_some_and(|x| x.contains_key(&OutputType::Constrict))) 212 212 .count() 213 213 == 1; 214 214
+4 -2
crates/buttplug_server/src/device/protocol_impl/metaxsire.rs
··· 41 41 ) -> Result<Arc<dyn ProtocolHandler>, ButtplugDeviceError> { 42 42 let mut commands = vec![]; 43 43 def.features().iter().for_each(|x| { 44 - if x.output().is_some() { 45 - commands.push((x.feature_type().try_into().unwrap(), AtomicU8::default())) 44 + if let Some(m) = x.output() { 45 + for output in m.keys() { 46 + commands.push((*output, AtomicU8::default())) 47 + } 46 48 } 47 49 }); 48 50 Ok(Arc::new(MetaXSire::new(commands)))
-9
crates/buttplug_server_device_config/src/device_feature.rs
··· 12 12 DeviceFeature, 13 13 DeviceFeatureInput, 14 14 DeviceFeatureOutput, 15 - FeatureType, 16 15 InputCommandType, 17 16 InputType, 18 17 OutputType, ··· 68 67 #[getset(get = "pub", get_mut = "pub(super)")] 69 68 #[serde(default)] 70 69 description: String, 71 - #[getset(get_copy = "pub")] 72 - #[serde(rename = "feature-type")] 73 - feature_type: FeatureType, 74 70 #[getset(get = "pub")] 75 71 #[serde(skip_serializing_if = "Option::is_none")] 76 72 #[serde(rename = "output")] ··· 244 240 self.base_feature.description() 245 241 } 246 242 247 - pub fn feature_type(&self) -> FeatureType { 248 - self.base_feature.feature_type 249 - } 250 - 251 243 pub fn id(&self) -> Uuid { 252 244 self.user_feature.id() 253 245 } ··· 277 269 DeviceFeature::new( 278 270 index, 279 271 self.description(), 280 - self.feature_type(), 281 272 &self.output.clone().map(|x| { 282 273 x.iter() 283 274 .filter(|(_, a)| !a.user_feature().disabled().as_ref().unwrap_or(&false))
+1 -1
crates/buttplug_tests/tests/test_client_device.rs
··· 9 9 use buttplug_client::{ButtplugClientDeviceEvent, ButtplugClientError, ButtplugClientEvent}; 10 10 use buttplug_core::{ 11 11 errors::ButtplugError, 12 - message::{OutputType, FeatureType}, 12 + message::{OutputType,}, 13 13 util::async_manager 14 14 }; 15 15 use buttplug_server_device_config::{load_protocol_configs, UserDeviceCustomization, DeviceDefinition, UserDeviceIdentifier, ServerDeviceFeature, ServerDeviceFeatureOutput, Endpoint};
+1 -1
crates/buttplug_tests/tests/util/device_test/client/client_v3/device.rs
··· 589 589 .collect(); 590 590 if sensor_indexes.len() != 1 { 591 591 return create_boxed_future_client_error( 592 - ButtplugDeviceError::ProtocolSensorNotSupported(*sensor_type).into(), 592 + ButtplugDeviceError::ProtocolInputNotSupported(*sensor_type).into(), 593 593 ); 594 594 } 595 595 let msg = SensorReadCmdV3::new(self.index, sensor_indexes[0], *sensor_type).into();
+3 -3
crates/buttplug_tests/tests/util/device_test/client/client_v4/mod.rs
··· 11 11 }; 12 12 use buttplug_client_in_process::ButtplugInProcessClientConnectorBuilder; 13 13 use buttplug_server_device_config::load_protocol_configs; 14 - use buttplug_core::{message::{OutputType, FeatureType}, util::{async_manager,}}; 14 + use buttplug_core::{message::OutputType, util::async_manager}; 15 15 use buttplug_server::{device::ServerDeviceManagerBuilder, ButtplugServer, ButtplugServerBuilder}; 16 16 use tokio::sync::Notify; 17 17 ··· 59 59 let vibe_features: Vec<&ClientDeviceFeature> = device 60 60 .device_features() 61 61 .iter() 62 - .filter(|f| f.1.feature().feature_type() == FeatureType::Vibrate) 62 + .filter(|f| f.1.feature().output().as_ref().is_some_and(|x| x.contains_key(&OutputType::Vibrate))) 63 63 .map(|(_, x)| x) 64 64 .collect(); 65 65 let f = vibe_features[cmd.index() as usize].clone(); ··· 78 78 let rotate_features: Vec<&ClientDeviceFeature> = device 79 79 .device_features() 80 80 .iter() 81 - .filter(|f| f.1.feature().feature_type() == FeatureType::RotateWithDirection) 81 + .filter(|f| f.1.feature().output().as_ref().is_some_and(|x| x.contains_key(&OutputType::RotateWithDirection))) 82 82 .map(|(_, x)| x) 83 83 .collect(); 84 84 let f = rotate_features[cmd.index() as usize].clone();
+324 -426
crates/examples/src/bin/device_tester.rs
··· 5 5 // Licensed under the BSD 3-Clause license. See LICENSE file in the project root 6 6 // for full license information. 7 7 8 + use buttplug_client::device::{ClientDeviceFeature, ClientDeviceOutputCommand}; 8 9 use buttplug_client::{ButtplugClient, ButtplugClientDevice, ButtplugClientEvent}; 9 10 use buttplug_client_in_process::ButtplugInProcessClientConnectorBuilder; 10 - use buttplug_core::message::{DeviceFeature, DeviceFeatureOutput, OutputCommand, OutputRotateWithDirection, OutputType, OutputValue}; 11 + use buttplug_core::message::ButtplugDeviceMessageNameV4::OutputCmd; 12 + use buttplug_core::message::{ 13 + DeviceFeature, 14 + DeviceFeatureOutput, 15 + OutputCommand, 16 + OutputRotateWithDirection, 17 + OutputType, 18 + OutputValue, 19 + }; 11 20 use buttplug_server::ButtplugServerBuilder; 12 21 use buttplug_server::device::ServerDeviceManagerBuilder; 13 22 use buttplug_server_device_config::load_protocol_configs; 14 23 use buttplug_server_hwmgr_btleplug::BtlePlugCommunicationManagerBuilder; 15 24 use futures::StreamExt; 25 + use futures::future::try_join; 16 26 use log::error; 17 27 use std::collections::{HashMap, HashSet}; 18 28 use std::{fs, sync::Arc, time::Duration}; 19 - use futures::future::try_join; 20 29 use tokio::time::sleep; 21 30 use tracing::Level; 22 - use buttplug_client::device::{ClientDeviceFeature, ClientDeviceOutputCommand}; 23 - use buttplug_core::message::ButtplugDeviceMessageNameV4::OutputCmd; 24 31 25 - 26 - async fn set_level_and_wait(dev: &ButtplugClientDevice, feature: &ClientDeviceFeature, output: &DeviceFeatureOutput, output_type: &OutputType, level: f64) { 27 - let cmd = match (output_type) { 28 - OutputType::Vibrate => Ok(ClientDeviceOutputCommand::VibrateFloat(level)), 29 - OutputType::Rotate => Ok(ClientDeviceOutputCommand::RotateFloat(level)), 30 - OutputType::Oscillate => Ok(ClientDeviceOutputCommand::OscillateFloat(level)), 31 - OutputType::Constrict => Ok(ClientDeviceOutputCommand::ConstrictFloat(level)), 32 - OutputType::Heater => Ok(ClientDeviceOutputCommand::HeaterFloat(level)), 33 - OutputType::Led => Ok(ClientDeviceOutputCommand::LedFloat(level)), 34 - OutputType::Position => Ok(ClientDeviceOutputCommand::PositionFloat(level)), 35 - OutputType::Spray => Ok(ClientDeviceOutputCommand::SprayFloat(level)), 36 - _ => Err(format!("Unknown output type {:?}", output_type)), 37 - }.unwrap(); 38 - feature.send_command(&cmd).await.unwrap(); 39 - println!( 40 - "{} ({}) Testing feature {}: {} ({}), output {:?} - {}%", 41 - dev.name(), 42 - dev.index(), 43 - feature.feature().feature_index(), 44 - feature.feature().feature_type(), 45 - feature.feature().description(), 46 - output_type, 47 - (level * 100.0) as u8); 48 - sleep(Duration::from_secs(1)).await; 32 + async fn set_level_and_wait( 33 + dev: &ButtplugClientDevice, 34 + feature: &ClientDeviceFeature, 35 + output: &DeviceFeatureOutput, 36 + output_type: &OutputType, 37 + level: f64, 38 + ) { 39 + let cmd = match (output_type) { 40 + OutputType::Vibrate => Ok(ClientDeviceOutputCommand::VibrateFloat(level)), 41 + OutputType::Rotate => Ok(ClientDeviceOutputCommand::RotateFloat(level)), 42 + OutputType::Oscillate => Ok(ClientDeviceOutputCommand::OscillateFloat(level)), 43 + OutputType::Constrict => Ok(ClientDeviceOutputCommand::ConstrictFloat(level)), 44 + OutputType::Heater => Ok(ClientDeviceOutputCommand::HeaterFloat(level)), 45 + OutputType::Led => Ok(ClientDeviceOutputCommand::LedFloat(level)), 46 + OutputType::Position => Ok(ClientDeviceOutputCommand::PositionFloat(level)), 47 + OutputType::Spray => Ok(ClientDeviceOutputCommand::SprayFloat(level)), 48 + _ => Err(format!("Unknown output type {:?}", output_type)), 49 + } 50 + .unwrap(); 51 + feature.send_command(&cmd).await.unwrap(); 52 + println!( 53 + "{} ({}) Testing feature {}: {}, output {:?} - {}%", 54 + dev.name(), 55 + dev.index(), 56 + feature.feature().feature_index(), 57 + feature.feature().description(), 58 + output_type, 59 + (level * 100.0) as u8 60 + ); 61 + sleep(Duration::from_secs(1)).await; 49 62 } 50 63 51 64 async fn device_tester() { 52 - let mut dc = None; 53 - let mut uc = None; 65 + let mut dc = None; 66 + let mut uc = None; 54 67 55 - dc = None; //Some(fs::read_to_string("C:\\Users\\NickPoole\\AppData\\Roaming\\com.nonpolynomial\\intiface_central\\config\\buttplug-device-config-v3.json").expect("Should have been able to read dc")); 56 - uc = None; //Some(fs::read_to_string("C:\\Users\\NickPoole\\AppData\\Roaming\\com.nonpolynomial\\intiface_central\\config\\buttplug-user-device-config-v3.json").expect("Should have been able to read uc")); 68 + dc = None; //Some(fs::read_to_string("C:\\Users\\NickPoole\\AppData\\Roaming\\com.nonpolynomial\\intiface_central\\config\\buttplug-device-config-v3.json").expect("Should have been able to read dc")); 69 + uc = None; //Some(fs::read_to_string("C:\\Users\\NickPoole\\AppData\\Roaming\\com.nonpolynomial\\intiface_central\\config\\buttplug-user-device-config-v3.json").expect("Should have been able to read uc")); 57 70 58 - let dcm = load_protocol_configs(&dc, &uc, false) 59 - .unwrap() 60 - .finish() 61 - .unwrap(); 71 + let dcm = load_protocol_configs(&dc, &uc, false) 72 + .unwrap() 73 + .finish() 74 + .unwrap(); 62 75 63 - let mut server_builder = ServerDeviceManagerBuilder::new(dcm); 64 - server_builder.comm_manager(BtlePlugCommunicationManagerBuilder::default()); 65 - //server_builder.comm_manager(LovenseConnectServiceCommunicationManagerBuilder::default()); 66 - //server_builder.comm_manager(LovenseHIDDongleCommunicationManagerBuilder::default()); 67 - //server_builder.comm_manager(LovenseSerialDongleCommunicationManagerBuilder::default()); 68 - //server_builder.comm_manager(WebsocketServerDeviceCommunicationManagerBuilder::default()); 69 - //server_builder.comm_manager(HidCommunicationManagerBuilder::default()); 70 - //server_builder.comm_manager(SerialPortCommunicationManagerBuilder::default()); 76 + let mut server_builder = ServerDeviceManagerBuilder::new(dcm); 77 + server_builder.comm_manager(BtlePlugCommunicationManagerBuilder::default()); 78 + //server_builder.comm_manager(LovenseConnectServiceCommunicationManagerBuilder::default()); 79 + //server_builder.comm_manager(LovenseHIDDongleCommunicationManagerBuilder::default()); 80 + //server_builder.comm_manager(LovenseSerialDongleCommunicationManagerBuilder::default()); 81 + //server_builder.comm_manager(WebsocketServerDeviceCommunicationManagerBuilder::default()); 82 + //server_builder.comm_manager(HidCommunicationManagerBuilder::default()); 83 + //server_builder.comm_manager(SerialPortCommunicationManagerBuilder::default()); 71 84 72 - let sb = ButtplugServerBuilder::new(server_builder.finish().unwrap()); 73 - let server = sb.finish().unwrap(); 74 - let connector = ButtplugInProcessClientConnectorBuilder::default() 75 - .server(server) 76 - .finish(); 77 - let client = ButtplugClient::new("device-tester"); 78 - client.connect(connector).await.unwrap(); 85 + let sb = ButtplugServerBuilder::new(server_builder.finish().unwrap()); 86 + let server = sb.finish().unwrap(); 87 + let connector = ButtplugInProcessClientConnectorBuilder::default() 88 + .server(server) 89 + .finish(); 90 + let client = ButtplugClient::new("device-tester"); 91 + client.connect(connector).await.unwrap(); 79 92 80 - let mut event_stream = client.event_stream(); 93 + let mut event_stream = client.event_stream(); 81 94 82 - // We'll mostly be doing the same thing we did in example #3, up until we get 83 - // a device. 84 - if let Err(err) = client.start_scanning().await { 85 - println!("Client errored when starting scan! {}", err); 86 - return; 87 - } 95 + // We'll mostly be doing the same thing we did in example #3, up until we get 96 + // a device. 97 + if let Err(err) = client.start_scanning().await { 98 + println!("Client errored when starting scan! {}", err); 99 + return; 100 + } 88 101 89 - let exercise_device = |dev: ButtplugClientDevice| { 90 - async move { 91 - let mut cmds = vec![]; 92 - dev.device_features().iter().for_each(|(_, feature)| { 93 - let outs = feature.feature().output().clone().unwrap_or_default(); 94 - if let Some(out) = outs.get(&OutputType::Vibrate) { 95 - cmds.push(feature.vibrate(out.step_count())); 96 - println!( 97 - "{} ({}) should start vibrating on feature {}!", 98 - dev.name(), 99 - dev.index(), 100 - feature.feature_index() 101 - ); 102 - } else if let Some(out) = outs.get(&OutputType::Rotate) { 103 - cmds.push(feature.rotate(out.step_count())); 104 - println!( 105 - "{} ({}) should start rotating on feature {}!", 106 - dev.name(), 107 - dev.index(), 108 - feature.feature_index() 109 - ); 110 - } else if let Some(out) = outs.get(&OutputType::Oscillate) { 111 - cmds.push(feature.oscillate(out.step_count())); 112 - println!( 113 - "{} ({}) should start oscillating on feature {}!", 114 - dev.name(), 115 - dev.index(), 116 - feature.feature_index() 117 - ); 118 - } else if let Some(out) = outs.get(&OutputType::Constrict) { 119 - cmds.push(feature.constrict(out.step_count())); 120 - println!( 121 - "{} ({}) should start constricting on feature {}!", 122 - dev.name(), 123 - dev.index(), 124 - feature.feature_index() 125 - ); 126 - } else if let Some(out) = outs.get(&OutputType::Heater) { 127 - cmds.push(feature.send_command(&ClientDeviceOutputCommand::Heater(out.step_count()))); 128 - println!( 129 - "{} ({}) should start heating on feature {}!", 130 - dev.name(), 131 - dev.index(), 132 - feature.feature_index() 133 - ); 134 - } else if let Some(out) = outs.get(&OutputType::Position) { 135 - cmds.push(feature.position(out.step_count())); 136 - println!( 137 - "{} ({}) should start moving to position {} on feature {}!", 138 - dev.name(), 139 - dev.index(), 140 - out.step_count(), 141 - feature.feature_index() 142 - ); 143 - } 144 - }); 145 - futures::future::join_all(cmds) 146 - .await 147 - .iter() 148 - .for_each(|cmd| { 149 - if let Err(err) = cmd { 150 - error!("{:?}", err); 151 - } 152 - }); 102 + let exercise_device = |dev: ButtplugClientDevice| async move { 103 + let mut cmds = vec![]; 104 + dev.device_features().iter().for_each(|(_, feature)| { 105 + let outs = feature.feature().output().clone().unwrap_or_default(); 106 + if let Some(out) = outs.get(&OutputType::Vibrate) { 107 + cmds.push(feature.vibrate(out.step_count())); 108 + println!( 109 + "{} ({}) should start vibrating on feature {}!", 110 + dev.name(), 111 + dev.index(), 112 + feature.feature_index() 113 + ); 114 + } else if let Some(out) = outs.get(&OutputType::Rotate) { 115 + cmds.push(feature.rotate(out.step_count())); 116 + println!( 117 + "{} ({}) should start rotating on feature {}!", 118 + dev.name(), 119 + dev.index(), 120 + feature.feature_index() 121 + ); 122 + } else if let Some(out) = outs.get(&OutputType::Oscillate) { 123 + cmds.push(feature.oscillate(out.step_count())); 124 + println!( 125 + "{} ({}) should start oscillating on feature {}!", 126 + dev.name(), 127 + dev.index(), 128 + feature.feature_index() 129 + ); 130 + } else if let Some(out) = outs.get(&OutputType::Constrict) { 131 + cmds.push(feature.constrict(out.step_count())); 132 + println!( 133 + "{} ({}) should start constricting on feature {}!", 134 + dev.name(), 135 + dev.index(), 136 + feature.feature_index() 137 + ); 138 + } else if let Some(out) = outs.get(&OutputType::Heater) { 139 + cmds.push(feature.send_command(&ClientDeviceOutputCommand::Heater(out.step_count()))); 140 + println!( 141 + "{} ({}) should start heating on feature {}!", 142 + dev.name(), 143 + dev.index(), 144 + feature.feature_index() 145 + ); 146 + } else if let Some(out) = outs.get(&OutputType::Position) { 147 + cmds.push(feature.position(out.step_count())); 148 + println!( 149 + "{} ({}) should start moving to position {} on feature {}!", 150 + dev.name(), 151 + dev.index(), 152 + out.step_count(), 153 + feature.feature_index() 154 + ); 155 + } 156 + }); 157 + futures::future::join_all(cmds) 158 + .await 159 + .iter() 160 + .for_each(|cmd| { 161 + if let Err(err) = cmd { 162 + error!("{:?}", err); 163 + } 164 + }); 153 165 154 - sleep(Duration::from_secs(5)).await; 166 + sleep(Duration::from_secs(5)).await; 155 167 156 - let mut cmds = vec![]; 157 - dev.device_features().iter().for_each(|(_, feature)| { 158 - let outs = feature.feature().output().clone().unwrap_or_default(); 159 - if let Some(out) = outs.get(&OutputType::Vibrate) { 160 - cmds.push(feature.vibrate(0)); 161 - println!( 162 - "{} ({}) should stop vibrating on feature {}!", 163 - dev.name(), 164 - dev.index(), 165 - feature.feature_index() 166 - ); 167 - } else if let Some(out) = outs.get(&OutputType::Rotate) { 168 - cmds.push(feature.rotate(0)); 169 - println!( 170 - "{} ({}) should stop rotating on feature {}!", 171 - dev.name(), 172 - dev.index(), 173 - feature.feature_index() 174 - ); 175 - } else if let Some(out) = outs.get(&OutputType::Oscillate) { 176 - cmds.push(feature.oscillate(0)); 177 - println!( 178 - "{} ({}) should stop oscillating on feature {}!", 179 - dev.name(), 180 - dev.index(), 181 - feature.feature_index() 182 - ); 183 - } else if let Some(out) = outs.get(&OutputType::Constrict) { 184 - cmds.push(feature.constrict(0)); 185 - println!( 186 - "{} ({}) should stop constricting on feature {}!", 187 - dev.name(), 188 - dev.index(), 189 - feature.feature_index() 190 - ); 191 - } else if let Some(out) = outs.get(&OutputType::Heater) { 192 - cmds.push(feature.send_command(&ClientDeviceOutputCommand::Heater(0))); 193 - println!( 194 - "{} ({}) should stop heating on feature {}!", 195 - dev.name(), 196 - dev.index(), 197 - feature.feature_index() 198 - ); 199 - } else if let Some(out) = outs.get(&OutputType::Position) { 200 - cmds.push(feature.position(0)); 201 - println!( 202 - "{} ({}) should start moving to position 0 on feature {}!", 203 - dev.name(), 204 - dev.index(), 205 - feature.feature_index() 206 - ); 207 - } 208 - }); 209 - 210 - futures::future::join_all(cmds) 211 - .await 212 - .iter() 213 - .for_each(|cmd| { 214 - if let Err(err) = cmd { 215 - error!("{:?}", err); 216 - } 217 - }); 218 - 219 - sleep(Duration::from_secs(2)).await; 220 - 221 - for (_, feature) in dev.device_features() { 222 - for outputs in feature.feature().output() { 223 - for otype in outputs.keys() { 224 - let output = outputs.get(otype).unwrap(); 225 - match otype { 226 - OutputType::Vibrate | OutputType::Rotate | OutputType::Constrict | OutputType::Oscillate | OutputType::Heater | OutputType::Spray | OutputType::Led | OutputType::Position => { 227 - set_level_and_wait(&dev, feature, &output, otype, 0.25).await; 228 - set_level_and_wait(&dev, feature, &output, otype, 0.5).await; 229 - set_level_and_wait(&dev, feature, &output, otype, 0.75).await; 230 - set_level_and_wait(&dev, feature, &output, otype, 1.0).await; 231 - set_level_and_wait(&dev, feature, &output, otype, 0.0).await; 232 - }, 233 - OutputType::Unknown => { 234 - error!( 235 - "{} ({}) Can't test unknown feature {}: {} ({}), output {:?}", 236 - dev.name(), 237 - dev.index(), 238 - feature.feature().feature_index(), 239 - feature.feature().feature_type(), 240 - feature.feature().description(), 241 - otype); 242 - }, 243 - OutputType::RotateWithDirection => { 244 - feature.send_command(&ClientDeviceOutputCommand::RotateWithDirection(output.step_count() / 4, true)).await; 245 - println!( 246 - "{} ({}) Testing feature {}: {} ({}), output {:?} - 25% clockwise", 247 - dev.name(), 248 - dev.index(), 249 - feature.feature().feature_index(), 250 - feature.feature().feature_type(), 251 - feature.feature().description(), 252 - otype); 253 - sleep(Duration::from_secs(1)).await; 254 - feature.send_command(&ClientDeviceOutputCommand::RotateWithDirection(output.step_count() / 4, false)).await; 255 - println!( 256 - "{} ({}) Testing feature {}: {} ({}), output {:?} - 25% anti-clockwise", 257 - dev.name(), 258 - dev.index(), 259 - feature.feature().feature_index(), 260 - feature.feature().feature_type(), 261 - feature.feature().description(), 262 - otype); 263 - sleep(Duration::from_secs(1)).await; 264 - feature.send_command(&ClientDeviceOutputCommand::RotateWithDirection(output.step_count() / 2, true)).await; 265 - println!( 266 - "{} ({}) Testing feature {}: {} ({}), output {:?} - 50% clockwise", 267 - dev.name(), 268 - dev.index(), 269 - feature.feature().feature_index(), 270 - feature.feature().feature_type(), 271 - feature.feature().description(), 272 - otype); 273 - sleep(Duration::from_secs(1)).await; 274 - feature.send_command(&ClientDeviceOutputCommand::RotateWithDirection(output.step_count() / 2, false)).await; 275 - println!( 276 - "{} ({}) Testing feature {}: {} ({}), output {:?} - 50% anti-clockwise", 277 - dev.name(), 278 - dev.index(), 279 - feature.feature().feature_index(), 280 - feature.feature().feature_type(), 281 - feature.feature().description(), 282 - otype); 283 - sleep(Duration::from_secs(1)).await; 284 - feature.send_command(&ClientDeviceOutputCommand::RotateWithDirection((output.step_count() / 4) * 3, true)).await; 285 - println!( 286 - "{} ({}) Testing feature {}: {} ({}), output {:?} - 75% clockwise", 287 - dev.name(), 288 - dev.index(), 289 - feature.feature().feature_index(), 290 - feature.feature().feature_type(), 291 - feature.feature().description(), 292 - otype); 293 - sleep(Duration::from_secs(1)).await; 294 - feature.send_command(&ClientDeviceOutputCommand::RotateWithDirection((output.step_count() / 4) * 3, false)).await; 295 - println!( 296 - "{} ({}) Testing feature {}: {} ({}), output {:?} - 75% anti-clockwise", 297 - dev.name(), 298 - dev.index(), 299 - feature.feature().feature_index(), 300 - feature.feature().feature_type(), 301 - feature.feature().description(), 302 - otype); 303 - sleep(Duration::from_secs(1)).await; 304 - feature.send_command(&ClientDeviceOutputCommand::RotateWithDirection(output.step_count(), true)).await; 305 - println!( 306 - "{} ({}) Testing feature {}: {} ({}), output {:?} - 100% clockwise", 307 - dev.name(), 308 - dev.index(), 309 - feature.feature().feature_index(), 310 - feature.feature().feature_type(), 311 - feature.feature().description(), 312 - otype); 313 - sleep(Duration::from_secs(1)).await; 314 - feature.send_command(&ClientDeviceOutputCommand::RotateWithDirection(output.step_count(), false)).await; 315 - println!( 316 - "{} ({}) Testing feature {}: {} ({}), output {:?} - 100% anti-clockwise", 317 - dev.name(), 318 - dev.index(), 319 - feature.feature().feature_index(), 320 - feature.feature().feature_type(), 321 - feature.feature().description(), 322 - otype); 323 - sleep(Duration::from_secs(1)).await; 324 - feature.send_command(&ClientDeviceOutputCommand::RotateWithDirection(0, true)).await; 325 - println!( 326 - "{} ({}) Testing feature {}: {} ({}), output {:?} - 0%", 327 - dev.name(), 328 - dev.index(), 329 - feature.feature().feature_index(), 330 - feature.feature().feature_type(), 331 - feature.feature().description(), 332 - otype); 333 - sleep(Duration::from_secs(1)).await; 168 + let mut cmds = vec![]; 169 + dev.device_features().iter().for_each(|(_, feature)| { 170 + let outs = feature.feature().output().clone().unwrap_or_default(); 171 + if let Some(out) = outs.get(&OutputType::Vibrate) { 172 + cmds.push(feature.vibrate(0)); 173 + println!( 174 + "{} ({}) should stop vibrating on feature {}!", 175 + dev.name(), 176 + dev.index(), 177 + feature.feature_index() 178 + ); 179 + } else if let Some(out) = outs.get(&OutputType::Rotate) { 180 + cmds.push(feature.rotate(0)); 181 + println!( 182 + "{} ({}) should stop rotating on feature {}!", 183 + dev.name(), 184 + dev.index(), 185 + feature.feature_index() 186 + ); 187 + } else if let Some(out) = outs.get(&OutputType::Oscillate) { 188 + cmds.push(feature.oscillate(0)); 189 + println!( 190 + "{} ({}) should stop oscillating on feature {}!", 191 + dev.name(), 192 + dev.index(), 193 + feature.feature_index() 194 + ); 195 + } else if let Some(out) = outs.get(&OutputType::Constrict) { 196 + cmds.push(feature.constrict(0)); 197 + println!( 198 + "{} ({}) should stop constricting on feature {}!", 199 + dev.name(), 200 + dev.index(), 201 + feature.feature_index() 202 + ); 203 + } else if let Some(out) = outs.get(&OutputType::Heater) { 204 + cmds.push(feature.send_command(&ClientDeviceOutputCommand::Heater(0))); 205 + println!( 206 + "{} ({}) should stop heating on feature {}!", 207 + dev.name(), 208 + dev.index(), 209 + feature.feature_index() 210 + ); 211 + } else if let Some(out) = outs.get(&OutputType::Position) { 212 + cmds.push(feature.position(0)); 213 + println!( 214 + "{} ({}) should start moving to position 0 on feature {}!", 215 + dev.name(), 216 + dev.index(), 217 + feature.feature_index() 218 + ); 219 + } 220 + }); 334 221 335 - feature.send_command(&ClientDeviceOutputCommand::RotateWithDirection(output.step_count() / 4, false)).await; 336 - println!( 337 - "{} ({}) Testing feature {}: {} ({}), output {:?} - 25% anti-clockwise", 338 - dev.name(), 339 - dev.index(), 340 - feature.feature().feature_index(), 341 - feature.feature().feature_type(), 342 - feature.feature().description(), 343 - otype); 344 - sleep(Duration::from_secs(1)).await; 345 - feature.send_command(&ClientDeviceOutputCommand::RotateWithDirection(output.step_count() / 2, false)).await; 346 - println!( 347 - "{} ({}) Testing feature {}: {} ({}), output {:?} - 50% anti-clockwise", 348 - dev.name(), 349 - dev.index(), 350 - feature.feature().feature_index(), 351 - feature.feature().feature_type(), 352 - feature.feature().description(), 353 - otype); 354 - sleep(Duration::from_secs(1)).await; 355 - feature.send_command(&ClientDeviceOutputCommand::RotateWithDirection((output.step_count() / 4) * 3, false)).await; 356 - println!( 357 - "{} ({}) Testing feature {}: {} ({}), output {:?} - 75% anti-clockwise", 358 - dev.name(), 359 - dev.index(), 360 - feature.feature().feature_index(), 361 - feature.feature().feature_type(), 362 - feature.feature().description(), 363 - otype); 364 - sleep(Duration::from_secs(1)).await; 365 - feature.send_command(&ClientDeviceOutputCommand::RotateWithDirection(output.step_count(), false)).await; 366 - println!( 367 - "{} ({}) Testing feature {}: {} ({}), output {:?} - 100% anti-clockwise", 368 - dev.name(), 369 - dev.index(), 370 - feature.feature().feature_index(), 371 - feature.feature().feature_type(), 372 - feature.feature().description(), 373 - otype); 374 - sleep(Duration::from_secs(1)).await; 375 - feature.send_command(&ClientDeviceOutputCommand::RotateWithDirection(output.step_count() / 4, true)).await; 376 - println!( 377 - "{} ({}) Testing feature {}: {} ({}), output {:?} - 25% clockwise", 378 - dev.name(), 379 - dev.index(), 380 - feature.feature().feature_index(), 381 - feature.feature().feature_type(), 382 - feature.feature().description(), 383 - otype); 384 - sleep(Duration::from_secs(1)).await; 385 - feature.send_command(&ClientDeviceOutputCommand::RotateWithDirection(output.step_count() / 2, true)).await; 386 - println!( 387 - "{} ({}) Testing feature {}: {} ({}), output {:?} - 50% clockwise", 388 - dev.name(), 389 - dev.index(), 390 - feature.feature().feature_index(), 391 - feature.feature().feature_type(), 392 - feature.feature().description(), 393 - otype); 394 - sleep(Duration::from_secs(1)).await; 395 - feature.send_command(&ClientDeviceOutputCommand::RotateWithDirection((output.step_count() / 4) * 3, true)).await; 396 - println!( 397 - "{} ({}) Testing feature {}: {} ({}), output {:?} - 75% clockwise", 398 - dev.name(), 399 - dev.index(), 400 - feature.feature().feature_index(), 401 - feature.feature().feature_type(), 402 - feature.feature().description(), 403 - otype); 404 - sleep(Duration::from_secs(1)).await; 405 - feature.send_command(&ClientDeviceOutputCommand::RotateWithDirection(output.step_count(), true)).await; 406 - println!( 407 - "{} ({}) Testing feature {}: {} ({}), output {:?} - 100% clockwise", 408 - dev.name(), 409 - dev.index(), 410 - feature.feature().feature_index(), 411 - feature.feature().feature_type(), 412 - feature.feature().description(), 413 - otype); 414 - sleep(Duration::from_secs(1)).await; 415 - feature.send_command(&ClientDeviceOutputCommand::RotateWithDirection(0, true)).await; 416 - println!( 417 - "{} ({}) Testing feature {}: {} ({}), output {:?} - 0%", 418 - dev.name(), 419 - dev.index(), 420 - feature.feature().feature_index(), 421 - feature.feature().feature_type(), 422 - feature.feature().description(), 423 - otype); 424 - sleep(Duration::from_secs(1)).await; 425 - }, 426 - OutputType::PositionWithDuration => {} 427 - } 428 - } 429 - } 430 - } 222 + futures::future::join_all(cmds) 223 + .await 224 + .iter() 225 + .for_each(|cmd| { 226 + if let Err(err) = cmd { 227 + error!("{:?}", err); 431 228 } 432 - }; 229 + }); 433 230 434 - loop { 435 - match event_stream 436 - .next() 437 - .await 438 - .expect("We own the client so the event stream shouldn't die.") 439 - { 440 - ButtplugClientEvent::DeviceAdded(dev) => { 441 - println!("We got a device: {}", dev.name()); 442 - let fut = exercise_device(dev); 443 - tokio::spawn(async move { 444 - fut.await; 445 - }); 446 - // break; 231 + sleep(Duration::from_secs(2)).await; 232 + 233 + for (_, feature) in dev.device_features() { 234 + for outputs in feature.feature().output() { 235 + for otype in outputs.keys() { 236 + let output = outputs.get(otype).unwrap(); 237 + let test_feature = async |command, output_str| { 238 + feature.send_command(&command).await; 239 + println!( 240 + "{} ({}) Testing feature {} ({}), output {:?} - {}", 241 + dev.name(), 242 + dev.index(), 243 + feature.feature().feature_index(), 244 + feature.feature().description(), 245 + otype, 246 + output_str 247 + ); 248 + sleep(Duration::from_secs(1)).await; 249 + }; 250 + match otype { 251 + OutputType::Vibrate 252 + | OutputType::Rotate 253 + | OutputType::Constrict 254 + | OutputType::Oscillate 255 + | OutputType::Heater 256 + | OutputType::Spray 257 + | OutputType::Led 258 + | OutputType::Position => { 259 + set_level_and_wait(&dev, feature, &output, otype, 0.25).await; 260 + set_level_and_wait(&dev, feature, &output, otype, 0.5).await; 261 + set_level_and_wait(&dev, feature, &output, otype, 0.75).await; 262 + set_level_and_wait(&dev, feature, &output, otype, 1.0).await; 263 + set_level_and_wait(&dev, feature, &output, otype, 0.0).await; 447 264 } 448 - ButtplugClientEvent::ServerDisconnect => { 449 - // The server disconnected, which means we're done here, so just 450 - // break up to the top level. 451 - println!("Server disconnected!"); 452 - break; 265 + OutputType::Unknown => { 266 + error!( 267 + "{} ({}) Can't test unknown feature {} ({}), output {:?}", 268 + dev.name(), 269 + dev.index(), 270 + feature.feature().feature_index(), 271 + feature.feature().description(), 272 + otype 273 + ); 453 274 } 454 - _ => { 455 - // Something else happened, like scanning finishing, devices 456 - // getting removed, etc... Might as well say something about it. 457 - println!("Got some other kind of event we don't care about"); 275 + OutputType::RotateWithDirection => { 276 + test_feature( 277 + ClientDeviceOutputCommand::RotateWithDirection(output.step_count() / 4, true), 278 + "25% clockwise", 279 + ) 280 + .await; 281 + test_feature( 282 + ClientDeviceOutputCommand::RotateWithDirection(output.step_count() / 4, false), 283 + "25% anti-clockwise", 284 + ) 285 + .await; 286 + test_feature( 287 + ClientDeviceOutputCommand::RotateWithDirection(output.step_count() / 2, true), 288 + "50% clockwise", 289 + ) 290 + .await; 291 + test_feature( 292 + ClientDeviceOutputCommand::RotateWithDirection(output.step_count() / 2, false), 293 + "50% anti-clockwise", 294 + ) 295 + .await; 296 + test_feature( 297 + ClientDeviceOutputCommand::RotateWithDirection((output.step_count() / 4) * 3, true), 298 + "75% clockwise", 299 + ) 300 + .await; 301 + test_feature( 302 + ClientDeviceOutputCommand::RotateWithDirection( 303 + (output.step_count() / 4) * 3, 304 + false, 305 + ), 306 + "75% anti-clockwise", 307 + ) 308 + .await; 309 + test_feature( 310 + ClientDeviceOutputCommand::RotateWithDirection(output.step_count(), true), 311 + "100% clockwise", 312 + ) 313 + .await; 314 + test_feature( 315 + ClientDeviceOutputCommand::RotateWithDirection(output.step_count(), false), 316 + "100% anti-clockwise", 317 + ) 318 + .await; 319 + test_feature( 320 + ClientDeviceOutputCommand::RotateWithDirection(0, false), 321 + "stop", 322 + ) 323 + .await; 458 324 } 325 + OutputType::PositionWithDuration => {} 326 + } 459 327 } 328 + } 460 329 } 330 + }; 461 331 462 - // And now we're done! 463 - println!("Exiting example"); 332 + loop { 333 + match event_stream 334 + .next() 335 + .await 336 + .expect("We own the client so the event stream shouldn't die.") 337 + { 338 + ButtplugClientEvent::DeviceAdded(dev) => { 339 + println!("We got a device: {}", dev.name()); 340 + let fut = exercise_device(dev); 341 + tokio::spawn(async move { 342 + fut.await; 343 + }); 344 + // break; 345 + } 346 + ButtplugClientEvent::ServerDisconnect => { 347 + // The server disconnected, which means we're done here, so just 348 + // break up to the top level. 349 + println!("Server disconnected!"); 350 + break; 351 + } 352 + _ => { 353 + // Something else happened, like scanning finishing, devices 354 + // getting removed, etc... Might as well say something about it. 355 + println!("Got some other kind of event we don't care about"); 356 + } 357 + } 358 + } 359 + 360 + // And now we're done! 361 + println!("Exiting example"); 464 362 } 465 363 466 364 #[tokio::main(flavor = "current_thread")] 467 365 async fn main() { 468 - tracing_subscriber::fmt() 469 - .with_max_level(Level::DEBUG) 470 - .init(); 471 - device_tester().await; 366 + tracing_subscriber::fmt() 367 + .with_max_level(Level::DEBUG) 368 + .init(); 369 + device_tester().await; 472 370 }