packages feed

myo-0.1.0.0: src/Myo/WebSockets/Types.hs

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
{-# OPTIONS_GHC -fno-warn-type-defaults #-}
module Myo.WebSockets.Types where

import Data.Aeson.TH
import Data.Int
import Data.Scientific
import Data.Aeson.Types
import Data.Char
import Control.Monad
import Control.Applicative
import Lens.Family2.TH
import qualified Data.Vector as V
import qualified Data.Text as T

-------------------------------------------------------------------------------
data MyoEventType =
    EVT_Paired
  | EVT_Battery_Level
  | EVT_Locked
  | EVT_Unlocked
  | EVT_Warmup_Completed
  | EVT_Connected
  | EVT_Disconnected
  | EVT_Arm_Synced
  | EVT_Arm_Unsynced
  | EVT_Orientation
  | EVT_Pose
  | EVT_RSSI
  | EVT_EMG
  deriving (Show, Eq)

-------------------------------------------------------------------------------
type MyoID = Integer

-------------------------------------------------------------------------------
data MyoVersion = MyoVersion {
    _myv_major    :: !Integer
  , _myv_minor    :: !Integer
  , _myv_patch    :: !Integer
  , _myv_hardware :: !Integer
  } deriving (Show, Eq)

-------------------------------------------------------------------------------
-- It's an 8 bit integer
data EMG  = EMG Int8 deriving (Show, Eq)

-------------------------------------------------------------------------------
data MyoPose =
     Rest
   | Fist
   | Wave_In
   | Wave_Out
   | Fingers_Spread
   | Double_Tap
   | Unknown
   deriving (Show, Eq)

data Orientation = Orientation {
     _ori_x :: !Double
   , _ori_y :: !Double
   , _ori_z :: !Double
   , _ori_w :: !Double
   } deriving (Show, Eq)

data Accelerometer = Accelerometer {
     _acc_x :: !Double
   , _acc_y :: !Double
   , _acc_z :: !Double
   } deriving (Show, Eq)

data Gyroscope = Gyroscope {
     _gyr_x :: !Double
   , _gyr_y :: !Double
   , _gyr_z :: !Double
   } deriving (Show, Eq)

-------------------------------------------------------------------------------
data MyoArm = Arm_Left | Arm_Right deriving (Show, Eq)

-------------------------------------------------------------------------------
data MyoDirection = Toward_wrist | Toward_elbow deriving (Show, Eq)

-------------------------------------------------------------------------------
data MyoFrame = Event MyoEvent
              | Command MyoCommand
              deriving (Show, Eq)

instance FromJSON MyoFrame where
 parseJSON (Array v) = case V.toList v of
   [String "event", o@(Object _)] -> Event <$> parseJSON o
   [String "command", o@(Object _)] -> Command <$> parseJSON o
   _ -> mzero
 parseJSON v = typeMismatch "MyoFrame: Expecting an Array of frames." v

-------------------------------------------------------------------------------
data MyoEvent = MyoEvent {
    _mye_type :: !MyoEventType
  , _mye_timestamp :: !T.Text
  , _mye_myo :: !MyoID
  , _mye_arm :: !(Maybe MyoArm)
  , _mye_x_direction :: !(Maybe MyoDirection)
  , _mye_version :: !(Maybe MyoVersion)
  , _mye_warmup_result :: !(Maybe MyoResult)
  , _mye_rssi :: !(Maybe Int)
  , _mye_pose :: !(Maybe MyoPose)
  , _mye_emg :: !(Maybe EMG)
  , _mye_orientation :: !(Maybe Orientation)
  , _mye_accelerometer :: !(Maybe Accelerometer)
  , _mye_gyroscope :: !(Maybe Gyroscope)
  } deriving (Show, Eq)


data MyoResult = Success | Fail deriving (Show, Eq)

data MyoCommandType =
    COM_vibrate
  | COM_request_rssi
  | COM_set_stream_emg
  | COM_set_locking_policy
  | COM_unlock
  | COM_lock
  | COM_notify_user_action
  deriving (Show, Eq)

-------------------------------------------------------------------------------
data MyoCommand = MyoCommand {
    _myc_command :: !MyoCommandType
  , _myc_timestamp :: !T.Text
  , _myc_myo :: !MyoID
  , _myc_type :: !T.Text -- Use an ADT
  } deriving (Show, Eq)

-------------------------------------------------------------------------------
instance FromJSON MyoVersion where
 parseJSON (Array v) = do
  let lst = V.toList v
  case liftM2 (,) (Just $ length lst) (mapM toNumber lst) of
   Just (4, x) -> case mapM floatingOrInteger x of
      Right [ma, mi, pa, ha] -> return $ MyoVersion ma mi pa ha
      _ -> mzero
   _ -> mzero
 parseJSON v = typeMismatch "MyoVersion: Expecting an Array like [major, minor, patch, hardware]" v


toNumber :: Value -> Maybe Scientific
toNumber (Number v) = Just v
toNumber _ = Nothing

-- TODO: Create an Int8 in a better way than this one!
instance FromJSON EMG where
 parseJSON (Array v) = do
  let lst = V.toList v
  case liftM2 (,) (Just $ length lst) (mapM toNumber lst) of
   Just (8, x) -> case mapM floatingOrInteger x of
      Right res -> return . EMG . read $ concatMap show res
      _ -> mzero
   _ -> mzero
 parseJSON v = typeMismatch "EMG: Expecting an Array of size 8." v

instance FromJSON Gyroscope where
 parseJSON (Array v) = case V.toList v of
   [Number x, Number y, Number z] -> return $ Gyroscope (toRealFloat x) (toRealFloat y) (toRealFloat z)
   _ -> mzero
 parseJSON v = typeMismatch "Gyroscope: Expecting an Array of Double like [x,y,z]" v

instance FromJSON Accelerometer where
 parseJSON (Array v) = case V.toList v of
   [Number x, Number y, Number z] -> return $ Accelerometer (toRealFloat x) (toRealFloat y) (toRealFloat z)
   _ -> mzero
 parseJSON v = typeMismatch "Accelerometer: Expecting an Array of Double like [x,y,z]" v

-------------------------------------------------------------------------------
-- JSON
deriveFromJSON defaultOptions { fieldLabelModifier = drop 5 } ''MyoEvent
deriveFromJSON defaultOptions { fieldLabelModifier = drop 5 } ''MyoCommand
deriveFromJSON defaultOptions { constructorTagModifier = map toLower . drop 4 } ''MyoCommandType
deriveFromJSON defaultOptions { constructorTagModifier = map toLower . drop 4 } ''MyoResult
deriveFromJSON defaultOptions { fieldLabelModifier = drop 5 } ''Orientation
deriveFromJSON defaultOptions { constructorTagModifier = map toLower . drop 4 } ''MyoEventType
deriveFromJSON defaultOptions { constructorTagModifier = map toLower } ''MyoPose
deriveFromJSON defaultOptions { constructorTagModifier = map toLower } ''MyoDirection
deriveFromJSON defaultOptions { constructorTagModifier = map toLower . drop 4 } ''MyoArm

-------------------------------------------------------------------------------
-- Lenses
makeLenses ''MyoEvent
makeLenses ''MyoCommand
makeLenses ''MyoVersion