@@ -15,14 +15,14 @@ use crate::{
15
15
hardware:: { Hardware , HardwareCommand , HardwareEvent , HardwareSubscribeCmd , HardwareWriteCmd } ,
16
16
protocol:: { ProtocolHandler , ProtocolIdentifier , ProtocolInitializer } ,
17
17
} ,
18
- util:: sleep,
18
+ util:: { sleep, async_manager }
19
19
} ;
20
20
use async_trait:: async_trait;
21
21
use futures:: { future:: BoxFuture , FutureExt } ;
22
22
use regex:: Regex ;
23
23
use std:: {
24
24
sync:: {
25
- atomic:: { AtomicBool , Ordering } ,
25
+ atomic:: { AtomicBool , AtomicU32 , AtomicU8 , Ordering } ,
26
26
Arc ,
27
27
} ,
28
28
time:: Duration ,
@@ -141,13 +141,12 @@ impl LovenseInitializer {
141
141
impl ProtocolInitializer for LovenseInitializer {
142
142
async fn initialize (
143
143
& mut self ,
144
- _ : Arc < Hardware > ,
144
+ hardware : Arc < Hardware > ,
145
145
device_definition : & UserDeviceDefinition ,
146
146
) -> Result < Arc < dyn ProtocolHandler > , ButtplugDeviceError > {
147
- let mut protocol = Lovense :: default ( ) ;
148
- protocol. device_type = self . device_type . clone ( ) ;
147
+ let device_type = self . device_type . clone ( ) ;
149
148
150
- protocol . vibrator_count = device_definition
149
+ let vibrator_count = device_definition
151
150
. features ( )
152
151
. iter ( )
153
152
. filter ( |x| [ FeatureType :: Vibrate , FeatureType :: Oscillate ] . contains ( x. feature_type ( ) ) )
@@ -159,31 +158,49 @@ impl ProtocolInitializer for LovenseInitializer {
159
158
. filter ( |x| x. actuator ( ) . is_some ( ) )
160
159
. count ( ) ;
161
160
161
+
162
162
// This might need better tuning if other complex Lovenses are released
163
163
// Currently this only applies to the Flexer/Lapis/Solace
164
- if ( protocol. vibrator_count == 2 && actuator_count > 2 )
165
- || protocol. vibrator_count > 2
166
- || protocol. device_type == "H"
167
- {
168
- protocol. use_mply = true ;
169
- }
164
+ let use_mply = ( vibrator_count == 2 && actuator_count > 2 )
165
+ || vibrator_count > 2
166
+ || device_type == "H" ;
170
167
171
168
debug ! (
172
169
"Device type {} initialized with {} vibrators {} using Mply" ,
173
- protocol . device_type,
174
- protocol . vibrator_count,
175
- if protocol . use_mply { "" } else { "not " }
170
+ device_type,
171
+ vibrator_count,
172
+ if use_mply { "" } else { "not " }
176
173
) ;
177
- Ok ( Arc :: new ( protocol) )
174
+
175
+ Ok ( Arc :: new ( Lovense :: new ( hardware, & device_type, vibrator_count, use_mply) ) )
178
176
}
179
177
}
180
178
181
- #[ derive( Default ) ]
182
179
pub struct Lovense {
183
180
rotation_direction : Arc < AtomicBool > ,
184
181
vibrator_count : usize ,
185
182
use_mply : bool ,
186
183
device_type : String ,
184
+ // Pairing of position: u8, duration: u32
185
+ linear_info : Arc < ( AtomicU8 , AtomicU32 ) > ,
186
+ }
187
+
188
+ impl Lovense {
189
+ pub fn new ( hardware : Arc < Hardware > , device_type : & str , vibrator_count : usize , use_mply : bool ) -> Self {
190
+
191
+ let linear_info = Arc :: new ( ( AtomicU8 :: new ( 0 ) , AtomicU32 :: new ( 0 ) ) ) ;
192
+ if device_type == "BA" {
193
+ async_manager:: spawn ( update_linear_movement ( hardware. clone ( ) , linear_info. clone ( ) ) ) ;
194
+ }
195
+
196
+ Self {
197
+ rotation_direction : Arc :: new ( AtomicBool :: new ( false ) ) ,
198
+ vibrator_count,
199
+ use_mply,
200
+ device_type : device_type. to_owned ( ) ,
201
+ linear_info,
202
+ }
203
+ }
187
204
}
188
205
189
206
impl ProtocolHandler for Lovense {
@@ -385,4 +402,59 @@ impl ProtocolHandler for Lovense {
385
402
}
386
403
. boxed ( )
387
404
}
405
+
406
+ fn handle_linear_cmd (
407
+ & self ,
408
+ message : message:: LinearCmdV4 ,
409
+ ) -> Result < Vec < HardwareCommand > , ButtplugDeviceError > {
410
+ let vector = message. vectors ( ) . first ( ) . expect ( "Already checked for vector subcommand" ) ;
411
+ self . linear_info . 0 . store ( ( vector. position ( ) * 100f64 ) as u8 , Ordering :: SeqCst ) ;
412
+ self . linear_info . 1 . store ( vector. duration ( ) , Ordering :: SeqCst ) ;
413
+ Ok ( vec ! ( ) )
414
+ }
388
415
}
416
+
417
+ async fn update_linear_movement ( device : Arc < Hardware > , linear_info : Arc < ( AtomicU8 , AtomicU32 ) > ) {
418
+ let mut last_goal_position = 0i32 ;
419
+ let mut current_move_amount = 0i32 ;
420
+ let mut current_position = 0i32 ;
421
+ loop {
422
+ // See if we've updated our goal position
423
+ let goal_position = linear_info. 0 . load ( Ordering :: Relaxed ) as i32 ;
424
+ // If we have and it's not the same, recalculate based on current status.
425
+ if last_goal_position != goal_position {
426
+ last_goal_position = goal_position;
427
+ // We move every 100ms, so divide the movement into that many chunks.
428
+ // If we're moving so fast it'd be under our 100ms boundary, just move in 1 step.
429
+ let move_steps = ( linear_info. 1 . load ( Ordering :: Relaxed ) / 100 ) . max ( 1 ) ;
430
+ current_move_amount = ( goal_position as i32 - current_position) as i32 / move_steps as i32 ;
431
+ }
432
+
433
+ // If we aren't going anywhere, just pause then restart
434
+ if current_position == last_goal_position {
435
+ sleep ( Duration :: from_millis ( 100 ) ) . await ;
436
+ continue ;
437
+ }
438
+
439
+ // Update our position, make sure we don't overshoot
440
+ current_position += current_move_amount;
441
+ if current_move_amount < 0 {
442
+ if current_position < last_goal_position {
443
+ current_position = last_goal_position;
444
+ }
445
+ } else {
446
+ if current_position > last_goal_position {
447
+ current_position = last_goal_position;
448
+ }
449
+ }
450
+
451
+ let lovense_cmd = format ! ( "FSetSite:{};" , current_position) ;
452
+ info ! ( "{}" , lovense_cmd) ;
453
+
454
+ let hardware_cmd = HardwareWriteCmd :: new ( Endpoint :: Tx , lovense_cmd. into_bytes ( ) , false ) ;
455
+ if device. write_value ( & hardware_cmd) . await . is_err ( ) {
456
+ return ;
457
+ }
458
+ sleep ( Duration :: from_millis ( 100 ) ) . await ;
459
+ }
460
+ }
0 commit comments