55// </copyright>
66
77using System ;
8+ using System . Collections . Generic ;
89using System . Threading ;
910using System . Threading . Tasks ;
1011using Buttplug . Core . Logging ;
@@ -21,18 +22,33 @@ private enum DeviceType
2122 {
2223 CycloneOrUnknown = 1 ,
2324 UFO = 2 ,
25+ Piston = 3 ,
2426 Bach = 6 ,
2527 }
2628
2729 public enum CommandType
2830 {
2931 Rotate = 1 ,
3032 Vibrate = 3 ,
33+ Linear = 10 ,
3134 }
3235
36+ public struct VorzePistonCommand
37+ {
38+ public uint parentMsgId ;
39+ public LinearCmd . VectorSubcommand cmd ;
40+ public CancellationToken aToken ;
41+ }
42+ private DateTime _currentTime ;
43+ private DateTime _nextDispatchTime ;
44+ private Queue < VorzePistonCommand > _linearCmdQueue ;
45+ private byte _beforeLinearCmdPosition ;
46+
3347 private DeviceType _deviceType = DeviceType . CycloneOrUnknown ;
3448 private CommandType _commandType = CommandType . Rotate ;
3549
50+ private bool isEnable = false ;
51+
3652 public VorzeSAProtocol ( IButtplugLogManager aLogManager ,
3753 IButtplugDeviceImpl aInterface )
3854 : base ( aLogManager ,
@@ -53,6 +69,11 @@ public VorzeSAProtocol(IButtplugLogManager aLogManager,
5369 Name = "Vorze UFO SA" ;
5470 break ;
5571
72+ case "VorzePiston" :
73+ _deviceType = DeviceType . Piston ;
74+ _commandType = CommandType . Linear ;
75+ Name = "Vorze A10 Piston SA" ;
76+ break ;
5677 case "Bach smart" :
5778 _deviceType = DeviceType . Bach ;
5879 _commandType = CommandType . Vibrate ;
@@ -76,6 +97,10 @@ public VorzeSAProtocol(IButtplugLogManager aLogManager,
7697 AddMessageHandler < SingleMotorVibrateCmd > ( HandleSingleMotorVibrateCmd ) ;
7798 AddMessageHandler < VibrateCmd > ( HandleVibrateCmd , new MessageAttributes ( ) { FeatureCount = 1 } ) ;
7899 break ;
100+ case CommandType . Linear :
101+ AddMessageHandler < LinearCmd > ( HandleLinearCmd , new MessageAttributes ( ) { FeatureCount = 1 } ) ;
102+ _linearCmdQueue = new Queue < VorzePistonCommand > ( ) ;
103+ break ;
79104
80105 default :
81106 BpLogger . Error ( "Unhandled command type." ) ;
@@ -85,12 +110,38 @@ public VorzeSAProtocol(IButtplugLogManager aLogManager,
85110 AddMessageHandler < StopDeviceCmd > ( HandleStopDeviceCmd ) ;
86111 }
87112
113+
114+ private async void ObserveLinearCmdQueue ( )
115+ {
116+ while ( isEnable )
117+ {
118+ _currentTime = DateTime . Now ;
119+ if ( this . _linearCmdQueue . Count > 0 && _currentTime >= _nextDispatchTime )
120+ {
121+ var cmd = _linearCmdQueue . Dequeue ( ) ;
122+ await DispatchLinearCmd ( cmd . cmd , cmd . aToken , cmd . parentMsgId ) ;
123+ _nextDispatchTime = _currentTime . AddMilliseconds ( cmd . cmd . Duration ) ;
124+ }
125+ await Task . Delay ( TimeSpan . FromMilliseconds ( 10 ) ) ;
126+ }
127+ }
128+
129+
88130 private async Task < ButtplugMessage > HandleStopDeviceCmd ( ButtplugDeviceMessage aMsg , CancellationToken aToken )
89131 {
90132 BpLogger . Debug ( "Stopping Device " + Name ) ;
91133
92- await Interface . WriteValueAsync ( new byte [ ] { ( byte ) _deviceType , ( byte ) _commandType , 0 } ,
93- aToken ) . ConfigureAwait ( false ) ;
134+ if ( _deviceType == DeviceType . Piston )
135+ {
136+ // Forced transition to the front
137+ await Interface . WriteValueAsync ( new byte [ ] { ( byte ) _deviceType , 0 , 60 } , aToken ) . ConfigureAwait ( false ) ;
138+ }
139+ else
140+ {
141+ await Interface . WriteValueAsync ( new byte [ ] { ( byte ) _deviceType , ( byte ) _commandType , 0 } , aToken ) . ConfigureAwait ( false ) ;
142+ }
143+ isEnable = false ;
144+
94145 return new Ok ( aMsg . Id ) ;
95146 }
96147
@@ -158,5 +209,79 @@ await Interface.WriteValueAsync(new[] { (byte)_deviceType, (byte)_commandType, (
158209 aToken ) . ConfigureAwait ( false ) ;
159210 return new Ok ( aMsg . Id ) ;
160211 }
212+
213+
214+ private async Task < ButtplugMessage > HandleLinearCmd ( ButtplugDeviceMessage aMsg , CancellationToken aToken )
215+ {
216+ if ( ! isEnable && _commandType == CommandType . Linear )
217+ {
218+ _linearCmdQueue . Clear ( ) ;
219+ }
220+
221+ var cmdMsg = CheckMessageHandler < LinearCmd > ( aMsg ) ;
222+ foreach ( var cmd in cmdMsg . Vectors )
223+ {
224+ VorzePistonCommand cmdWrapper ;
225+ cmdWrapper . parentMsgId = aMsg . Id ;
226+ cmdWrapper . cmd = cmd ;
227+ cmdWrapper . aToken = aToken ;
228+ _linearCmdQueue . Enqueue ( cmdWrapper ) ;
229+ }
230+
231+ if ( ! isEnable && _commandType == CommandType . Linear )
232+ {
233+ _currentTime = DateTime . Now ;
234+ _nextDispatchTime = _currentTime ;
235+ isEnable = true ;
236+ await Task . Run ( async ( ) => { ObserveLinearCmdQueue ( ) ; } ) ;
237+ }
238+
239+ return new Ok ( aMsg . Id ) ;
240+ }
241+ private async Task < ButtplugMessage > DispatchLinearCmd ( LinearCmd . VectorSubcommand cmd , CancellationToken aToken , uint parentMsgId )
242+ {
243+ //byte position = (byte)Math.Ceiling((1d / 200d) * (cmd.Position * 100) * 100);
244+ byte position = ( byte ) Math . Ceiling ( cmd . Position * 200 ) ;
245+ byte direction = ( byte ) ( _beforeLinearCmdPosition > position ? 0 : 1 ) ;
246+ byte speed = ResolveLinearCmdSpeed ( cmd . Duration , direction ) ;
247+
248+ await Interface . WriteValueAsync ( new [ ] { ( byte ) _deviceType , position , speed } , aToken ) . ConfigureAwait ( false ) ;
249+ _beforeLinearCmdPosition = position ;
250+
251+ return new Ok ( parentMsgId ) ;
252+ }
253+
254+ public byte ResolveLinearCmdSpeed ( uint duration , byte direction )
255+ {
256+ // Convert back to stroke speed from the interval.
257+ //
258+ // Front and back stroke speed is asymmetric.
259+ // (From back to front is slower than front to back.)
260+ //
261+ byte speed = 10 ; // defaults. minimum.
262+
263+ if ( duration <= 200 )
264+ {
265+ speed = 60 ;
266+ }
267+ else if ( duration <= 300 )
268+ {
269+ speed = direction > 0 ? ( byte ) 20 : ( byte ) 30 ;
270+ }
271+ else if ( duration <= 550 )
272+ {
273+ speed = direction > 0 ? ( byte ) 15 : ( byte ) 20 ;
274+ }
275+ else if ( duration <= 700 )
276+ {
277+ speed = direction > 0 ? ( byte ) 13 : ( byte ) 20 ;
278+ }
279+ else
280+ {
281+ speed = 10 ;
282+ }
283+
284+ return speed ;
285+ }
161286 }
162287}
0 commit comments