1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
//! Myo Armband

extern crate libc;

mod ffi;
pub use ffi::{
    ResultCode, LockingPolicy, VibrationType, UnlockType, VersionComponent, EventType,
    Arm, XDirection, WarmupState, WarmupResult, Pose, OrientationIndex, HandlerResult,
    HardwareRevision
};
use std::ffi::CStr;

/// Operation Result
pub type Result<T> = std::result::Result<T, ErrorDetails>;

/// Owned Myo String
pub struct MyoString(ffi::libmyo_string_t);
impl MyoString
{
    pub fn c_str(&self) -> &CStr
    {
        unsafe { CStr::from_ptr(ffi::libmyo_string_c_str(self.0)) }
    }
}
impl Drop for MyoString
{
    fn drop(&mut self)
    {
        unsafe { ffi::libmyo_string_free(self.0) };
    }
}
impl std::fmt::Debug for MyoString
{
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result
    {
        self.c_str().fmt(fmt)
    }
}

/// Owned Error Details
pub struct ErrorDetails(ffi::libmyo_error_details_t);
impl ErrorDetails
{
    pub fn message(&self) -> &CStr
    {
        unsafe { CStr::from_ptr(ffi::libmyo_error_cstring(self.0)) }
    }
    pub fn kind(&self) -> ResultCode
    {
        unsafe { ffi::libmyo_error_kind(self.0) }
    }
}
impl Drop for ErrorDetails
{
    fn drop(&mut self)
    {
        unsafe { ffi::libmyo_free_error_details(self.0) };
    }
}
impl std::fmt::Debug for ErrorDetails
{
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result
    {
        write!(fmt, "libmyo Error Details({:?}: {:?})", self.kind(), self.message())
    }
}
impl std::fmt::Display for ErrorDetails
{
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result
    {
        std::fmt::Debug::fmt(self, fmt)
    }
}
impl std::error::Error for ErrorDetails
{
    fn description(&self) -> &str { "libmyo Error" }
}

/// MAC Address
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct MACAddress(u64);
impl std::fmt::Display for MACAddress
{
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result
    {
        write!(fmt, "{}", MyoString(unsafe { ffi::libmyo_mac_address_to_string(self.0) }).c_str().to_str().unwrap())
    }
}

/// Wrapper Macros
macro_rules! DefWrapperFunc
{
    (pub fn $ename: ident ( $($narg: ident : $targ: ty),* ) = $fname: ident ( $($farg: expr),* )) =>
    {
        pub fn $ename(&self $(, $narg: $targ)*) -> Result<()>
        {
            let mut e = std::ptr::null_mut();
            let r = unsafe { ffi::$fname(self.0 $(, $farg)*, &mut e) };
            if r == ResultCode::Success { Ok(()) } else { Err(ErrorDetails(e)) }
        }
    };
    (pub fn $ename: ident ( $($narg: ident : $targ: ty),* ) -> $rtype: ty = $fname: ident ( $($farg: expr),* )) =>
    {
        pub fn $ename(&self $(, $narg: $targ)*) -> $rtype
        {
            unsafe { ffi::$fname(self.0 $(, $farg)*) as _ }
        }
    }
}

/// Event Listener
pub trait EventListener
{
    /// Called when successfully paired with a Myo.
    fn on_paired(&mut self, _: PairedEvent) -> HandlerResult { HandlerResult::Continue }
    /// Called when successfully unpaired from a Myo.
    fn on_unpaired(&mut self, _: UnpairedEvent) -> HandlerResult { HandlerResult::Continue }
    /// Called when a Myo has successfully connected.
    fn on_connected(&mut self, _: ConnectedEvent) -> HandlerResult { HandlerResult::Continue }
    /// Called when a Myo has been disconnected.
    fn on_disconnected(&mut self, _: DisconnectedEvent) -> HandlerResult { HandlerResult::Continue }
    /// Called when a Myo has recognized that the sync gesture has been successfully performed.
    fn on_arm_synced(&mut self, _: ArmSyncedEvent) -> HandlerResult { HandlerResult::Continue }
    /// Called when a Myo has been moved or removed from the arm.
    fn on_arm_unsynced(&mut self, _: ArmUnsyncedEvent) -> HandlerResult { HandlerResult::Continue }
    /// Called when orientation data has been received.
    fn on_orientation_data(&mut self, _: OrientationEvent) -> HandlerResult { HandlerResult::Continue }
    /// Called when a change in pose has been detected.
    fn on_pose(&mut self, _: PoseEvent) -> HandlerResult { HandlerResult::Continue }
    /// Called when an RSSI value has been received.
    fn on_rssi_value(&mut self, _: RSSIEvent) -> HandlerResult { HandlerResult::Continue }
    /// Called when a Myo has become unlocked.
    fn on_unlocked(&mut self, _: UnlockedEvent) -> HandlerResult { HandlerResult::Continue }
    /// Called when a Myo has become locked.
    fn on_locked(&mut self, _: LockedEvent) -> HandlerResult { HandlerResult::Continue }
    /// Called when EMG data has been received.
    fn on_emg_data(&mut self, _: EMGEvent) -> HandlerResult { HandlerResult::Continue }
    /// Called when a battery level value has been received.
    fn on_battery_level(&mut self, _: BatteryLevelEvent) -> HandlerResult { HandlerResult::Continue }
    /// Called when the warmup period has completed.
    fn on_warmup_completed(&mut self, _: WarmupCompletedEvent) -> HandlerResult { HandlerResult::Continue }
}

/// Store for Trait Object(stable passing for std::raw::TraitObject)
struct TraitObjectStore<'a, T: 'a + ?Sized>(&'a mut T);

/// Hub
pub struct Hub(ffi::libmyo_hub_t);
impl Hub
{
    /// Initialize
    pub fn init<AppID: Into<Vec<u8>>>(application_identifier: AppID) -> Result<Self>
    {
        let (mut h, mut e) = (std::ptr::null_mut(), std::ptr::null_mut());
        let appid = std::ffi::CString::new(application_identifier).unwrap();
        let r = unsafe { ffi::libmyo_init_hub(&mut h, appid.as_ptr(), &mut e) };
        if r == ResultCode::Success { Ok(Hub(h)) } else { Err(ErrorDetails(e)) }
    }

    DefWrapperFunc!(pub fn set_locking_policy(locking_policy: LockingPolicy) = libmyo_set_locking_policy(locking_policy));
    /// Process Events and call the provided callback as they occur
    pub fn run(&self, duration_ms: u32, listener: &mut EventListener) -> Result<()>
    {
        let mut to = TraitObjectStore(listener);
        let mut e = std::ptr::null_mut();
        let r = unsafe { ffi::libmyo_run(self.0, duration_ms as _, Self::run_internal_handler, &mut to as *mut TraitObjectStore<_> as *mut libc::c_void, &mut e) };
        if r == ResultCode::Success { Ok(()) } else { Err(ErrorDetails(e)) }
    }

    extern "system" fn run_internal_handler(elptr: *mut libc::c_void, event: ffi::libmyo_event_t) -> HandlerResult
    {
        let el: &mut EventListener = unsafe { std::mem::transmute::<_, &mut TraitObjectStore<'static, EventListener>>(&mut *elptr).0 };
        match unsafe { std::mem::transmute(ffi::libmyo_event_get_type(event)) }
        {
            EventType::Paired => el.on_paired(PairedEvent(event)),
            EventType::Unpaired => el.on_unpaired(UnpairedEvent(event)),
            EventType::Connected => el.on_connected(ConnectedEvent(event)),
            EventType::Disconnected => el.on_disconnected(DisconnectedEvent(event)),
            EventType::ArmSynced => el.on_arm_synced(ArmSyncedEvent(event)),
            EventType::ArmUnsynced => el.on_arm_unsynced(ArmUnsyncedEvent(event)),
            EventType::Orientation => el.on_orientation_data(OrientationEvent(event)),
            EventType::Pose => el.on_pose(PoseEvent(event)),
            EventType::RSSI => el.on_rssi_value(RSSIEvent(event)),
            EventType::Unlocked => el.on_unlocked(UnlockedEvent(event)),
            EventType::Locked => el.on_locked(LockedEvent(event)),
            EventType::EMG => el.on_emg_data(EMGEvent(event)),
            EventType::BatteryLevel => el.on_battery_level(BatteryLevelEvent(event)),
            EventType::WarmupCompleted => el.on_warmup_completed(WarmupCompletedEvent(event))
        }
    }
}
impl Drop for Hub
{
    /// Shutdown
    fn drop(&mut self)
    {
        let mut e = std::ptr::null_mut();
        let r = unsafe { ffi::libmyo_shutdown_hub(self.0, &mut e) };
        if r != ResultCode::Success
        {
            panic!("Error on drop: {:?}", ErrorDetails(e).message());
        }
    }
}

/// Myo Armband
#[derive(Debug)]
pub struct Armband(ffi::libmyo_myo_t);
impl Armband
{
    /// Raw ID
    pub fn raw_id(&self) -> usize { unsafe { std::mem::transmute(self.0) } }
    /// Vibrate
    DefWrapperFunc!(pub fn vibrate(vtype: VibrationType) = libmyo_vibrate(vtype));
    /// Asynchronous Request: RSSI
    DefWrapperFunc!(pub fn request_rssi() = libmyo_request_rssi());
    /// Asynchronous Request: Battery Level
    DefWrapperFunc!(pub fn request_battery_level() = libmyo_request_battery_level());
    /// Stream EMG Data
    DefWrapperFunc!(pub fn set_stream_emg(stream_emg_data: bool) =
        libmyo_set_stream_emg(if stream_emg_data { ffi::EMGStreamingMode::Enabled } else { ffi::EMGStreamingMode::Disabled }));

    // Locking Mechanism
    /// Lock Armband
    DefWrapperFunc!(pub fn lock() = libmyo_myo_lock());
    /// Unlock Armband
    DefWrapperFunc!(pub fn unlock(unlock_type: UnlockType) = libmyo_myo_unlock(unlock_type));
    /// Notify Myo device that a user action was recognized.
    /// Device will vibrate.
    DefWrapperFunc!(pub fn notify_user_action() = libmyo_myo_notify_user_action(ffi::UserActionType::Single));
}

/// Event Object
pub trait Event
{
    /// Retrieve Event Handle
    fn handle(&self) -> ffi::libmyo_event_t;
    
    /// Event Type
    fn event_type(&self) -> EventType { unsafe { std::mem::transmute(ffi::libmyo_event_get_type(self.handle())) } }
    /// Timestamp
    fn timestamp(&self) -> u64 { unsafe { ffi::libmyo_event_get_timestamp(self.handle()) } }
    /// Myo Device
    fn device(&self) -> Armband { Armband(unsafe { ffi::libmyo_event_get_myo(self.handle()) }) }
    /// MAC Address
    fn mac_address(&self) -> MACAddress { MACAddress(unsafe { ffi::libmyo_event_get_mac_address(self.handle()) }) }
    /// Myo Name
    fn device_name(&self) -> MyoString { MyoString(unsafe { ffi::libmyo_event_get_myo_name(self.handle()) }) }
}
macro_rules! DefEvent
{
    ($t: ident) =>
    {
        pub struct $t(ffi::libmyo_event_t);
        impl Event for $t { fn handle(&self) -> ffi::libmyo_event_t { self.0 } }
    }
}
macro_rules! DefEventParamWrapper
{
    (pub property<$obj: ty> $pn: ident [$($an: ident: $at: ty),*]: $pt: ty = $f: ident ($($fan: expr),*)) =>
    {
        impl $obj
        {
            pub fn $pn(&self $(, $an: $at)*) -> $pt
            {
                unsafe { ffi::$f(self.0 $(, $fan)*) as _ }
            }
        }
    };
    (pub property<$obj: ty> $pn: ident: $pt: ty = $f: ident ($($fan: expr),*)) =>
    {
        impl $obj
        {
            pub fn $pn(&self) -> $pt
            {
                unsafe { ffi::$f(self.0 $(, $fan)*) as _ }
            }
        }
    };
}

/// Successfully paired with a Myo.
DefEvent!(PairedEvent);
/// Successfully unpaired from a Myo.
DefEvent!(UnpairedEvent);
/// A Myo has successfully connected.
DefEvent!(ConnectedEvent);
/// A Myo has been disconnected.
DefEvent!(DisconnectedEvent);
/// A Myo has recognized that the sync gesture has been successfully performed.
DefEvent!(ArmSyncedEvent);
/// A Myo has been moved or removed from the arm.
DefEvent!(ArmUnsyncedEvent);
/// Orientation data has been received.
DefEvent!(OrientationEvent);
/// A change in pose has been detected.
DefEvent!(PoseEvent);
/// An RSSI value has been received.
DefEvent!(RSSIEvent);
/// A Myo has become unlocked.
DefEvent!(UnlockedEvent);
/// A Myo has become locked.
DefEvent!(LockedEvent);
/// EMG data has been received.
DefEvent!(EMGEvent);
/// A battery level value has been received.
DefEvent!(BatteryLevelEvent);
/// The warmup period has completed.
DefEvent!(WarmupCompletedEvent);

impl PairedEvent
{
    /// Firmware Version
    DefWrapperFunc!(pub fn firmware_version(component: VersionComponent) -> u32 = libmyo_event_get_firmware_version(component));

    /// Firmware Version Set
    pub fn firmware_versions(&self) -> (u32, u32, u32, HardwareRevision)
    {
        (self.firmware_version(VersionComponent::Major),
        self.firmware_version(VersionComponent::Minor),
        self.firmware_version(VersionComponent::Patch),
        unsafe { std::mem::transmute(self.firmware_version(VersionComponent::HardwareRevision)) })
    }
}
impl ConnectedEvent
{
    /// Firmware Version
    DefWrapperFunc!(pub fn firmware_version(component: VersionComponent) -> u32 = libmyo_event_get_firmware_version(component));

    /// Firmware Version Set
    pub fn firmware_versions(&self) -> (u32, u32, u32, HardwareRevision)
    {
        (self.firmware_version(VersionComponent::Major),
        self.firmware_version(VersionComponent::Minor),
        self.firmware_version(VersionComponent::Patch),
        unsafe { std::mem::transmute(self.firmware_version(VersionComponent::HardwareRevision)) })
    }
}

impl ArmSyncedEvent
{
    /// Arm Side
    DefWrapperFunc!(pub fn arm() -> Arm = libmyo_event_get_arm());
    /// +x Direction
    DefWrapperFunc!(pub fn xdirection() -> XDirection = libmyo_event_get_x_direction());
    /// Warming up state
    DefWrapperFunc!(pub fn warmup_state() -> WarmupState = libmyo_event_get_warmup_state());
    /// Eastimated Rotation of Myo on the user's arm
    DefWrapperFunc!(pub fn rotation_on_arm() -> f32 = libmyo_event_get_rotation_on_arm());
}
DefEventParamWrapper!(pub property<WarmupCompletedEvent> result: WarmupResult = libmyo_event_get_warmup_result());

/// Accelerometer/Gyroscope Index Value
#[repr(u8)] pub enum CoordinateIndex
{
    X = 0, Y = 1, Z = 2
}

impl OrientationEvent
{
    /// Orientation data
    DefWrapperFunc!(pub fn orientation(index: OrientationIndex) -> f32 = libmyo_event_get_orientation(index));
    /// Accelerometer data
    DefWrapperFunc!(pub fn accelerometer(index: CoordinateIndex) -> f32 = libmyo_event_get_accelerometer(index as _));
    /// Gyroscope data
    DefWrapperFunc!(pub fn gyroscope(index: CoordinateIndex) -> f32 = libmyo_event_get_gyroscope(index as _));

    // Support Funcs
    /// Orientation Data
    pub fn q_orientation(&self) -> (f32, f32, f32, f32)
    {
        (self.orientation(OrientationIndex::X), self.orientation(OrientationIndex::Y),
            self.orientation(OrientationIndex::Z), self.orientation(OrientationIndex::W))
    }
    /// Accelerometer Data
    pub fn v_accelerometer(&self) -> (f32, f32, f32)
    {
        (self.accelerometer(CoordinateIndex::X), self.accelerometer(CoordinateIndex::Y), self.accelerometer(CoordinateIndex::Z))
    }
    /// Gyroscope Data
    pub fn v_gyroscope(&self) -> (f32, f32, f32)
    {
        (self.gyroscope(CoordinateIndex::X), self.gyroscope(CoordinateIndex::Y), self.gyroscope(CoordinateIndex::Z))
    }
}
DefEventParamWrapper!(pub property<PoseEvent> pose: Pose = libmyo_event_get_pose());
DefEventParamWrapper!(pub property<RSSIEvent> rssi: i8 = libmyo_event_get_rssi());
DefEventParamWrapper!(pub property<BatteryLevelEvent> battery_level: u8 = libmyo_event_get_battery_level());
impl EMGEvent
{
    /// EMG Data
    DefWrapperFunc!(pub fn emg(sensor: u8) -> i8 = libmyo_event_get_emg(sensor as _));

    /// EMGs
    pub fn emgs(&self) -> [i8; 8]
    {
        let mut a = [0; 8]; for n in 0 .. 8 { a[n] = self.emg(n as _); } a
    }
}