hemokit 0.6.0 → 0.6.1
raw patch · 9 files changed
+50/−40 lines, 9 filesdep +hspecdep +textdep ~cipher-aesdep ~optparse-applicative
Dependencies added: hspec, text
Dependency ranges changed: cipher-aes, optparse-applicative
Files
- README.md +10/−5
- apps/Dump.hs +2/−7
- apps/DumpConduit.hs +2/−6
- apps/Mouse.hs +2/−2
- hemokit.cabal +9/−8
- src/Hemokit.hs +1/−1
- src/Hemokit/Internal/Utils.hs +15/−0
- src/Hemokit/Start.hs +5/−6
- test/Tests.hs +4/−5
README.md view
@@ -34,7 +34,7 @@ hemokit-dump - Examples ----------------------- -*hemokit-dump* can print EEG data, format it as JSON, serve it via Websockets, and read from real devices and dump files.+*hemokit-dump* can print EEG data, format it as JSON, serve it via TCP or Websockets, and read from real devices and dump files. * Output EEG *cumulative state* for an automatically found device:@@ -52,17 +52,22 @@ * Output only the data the device sends (no cumulative state), and format the output as JSON: ```bash- sudo hemokit-dump --mode packets --json+ sudo hemokit-dump --mode packets --format json ``` -* Instead of from a real device, read data recorded to a file, and serve it via JSON over a Websockets server on port `1234`:+ The `--format` flag allows you to change the way the output is printed.+ The output of `--mode state --format spaced` is especially easy to work with. +* Instead of from a real device, read data recorded to a file, and serve it via JSON over a TCP server on port `1234`:+ ```bash sudo cat /dev/hidraw1 > encrypted.dump # Dump data to a file- sudo hemokit-dump --from-file encrypted.dump --serial SN...GM --serve 0.0.0.0:1234 --json+ sudo hemokit-dump --from-file encrypted.dump --serial SN...GM --serve 0.0.0.0:1234 --format json ``` Here you **have** to specify the serial since HIDAPI is not used to obtain it automatically.+ + If you prefer a Websockets server over a raw TCP server, use `ws://0.0.0.0:1234` instead. * Output decrypted raw data to stdout: @@ -73,7 +78,7 @@ * Both print the data from the EEG **and** store the original data for later use: ```bash- sudo cat /dev/hidraw1 | tee >(hemokit-dump --from-file - --serial SN...GM --json) > encrypted.dump+ sudo cat /dev/hidraw1 | tee >(hemokit-dump --from-file - --serial SN...GM --format json) > encrypted.dump ``` We use `tee` and shell process substitution to duplicate the data stream, and tell *hemokit-dump* to read from `-` (stdin).
apps/Dump.hs view
@@ -10,8 +10,6 @@ import qualified Data.ByteString.Lazy.Char8 as BSL8 import qualified Data.ByteString.Lazy as BSL import qualified Data.ByteString.Lazy.Builder as Builder-import qualified Data.ByteString.Lazy.Builder.ASCII as Builder-import qualified Data.ByteString.Base64 as Base64 import Data.Function (fix) import Data.IORef import Data.List@@ -27,7 +25,7 @@ import Hemokit import Hemokit.Start -import Hemokit.Internal.Utils (withJustM)+import Hemokit.Internal.Utils (withJustM, textBase64) import SocketUtils (makeTCPServer) import WebsocketUtils (makeWSServer) @@ -79,9 +77,6 @@ <> help ("Serve output via a TCP server, e.g. 127.0.0.1:1234 " ++ "(port 1234, only localhost) or 0.0.0.0:1234 (all interfaces). " ++ "Use 'ws://' before the host to serve via websockets") )- where- -- TODO https://github.com/pcapriotti/optparse-applicative/issues/48- eitherReader str2either = reader (either fail return . str2either) -- | `DumpMode` command line parser.@@ -226,7 +221,7 @@ instance ToJSON EmotivState instance ToJSON EmotivRawData where- toJSON = toJSON . Base64.encode . emotivRawDataBytes+ toJSON = toJSON . textBase64 . emotivRawDataBytes instance ToJSON Sensor where toJSON = toJSON . show
apps/DumpConduit.hs view
@@ -11,8 +11,6 @@ import qualified Data.ByteString.Lazy as BSL import qualified Data.ByteString.Lazy.Char8 as BSL8 import qualified Data.ByteString.Lazy.Builder as Builder-import qualified Data.ByteString.Lazy.Builder.ASCII as Builder-import qualified Data.ByteString.Base64 as Base64 import Data.Conduit import qualified Data.Conduit.List as CL import Data.Function (fix)@@ -30,6 +28,7 @@ import Hemokit import Hemokit.Conduit import Hemokit.Start+import Hemokit.Internal.Utils (textBase64) -- | Arguments for the EEG dump application.@@ -79,9 +78,6 @@ <> help ("Serve output via a TCP server, e.g. 127.0.0.1:1234 " ++ "(port 1234, only localhost) or 0.0.0.0:1234 (all interfaces). " ++ "Use 'ws://' before the host to serve via websockets") )- where- -- TODO https://github.com/pcapriotti/optparse-applicative/issues/48- eitherReader str2either = reader (either fail return . str2either) -- | `DumpMode` command line parser.@@ -233,7 +229,7 @@ instance ToJSON EmotivState instance ToJSON EmotivRawData where- toJSON = toJSON . Base64.encode . emotivRawDataBytes+ toJSON = toJSON . textBase64 . emotivRawDataBytes instance ToJSON Sensor where toJSON = toJSON . show
apps/Mouse.hs view
@@ -23,8 +23,8 @@ putStrLn $ "AvailableDevices:\n" ++ ppShow devices - device <- openEmotivDevice model $ case devices of d:_ -> d- [] -> error "no Epoc devices found"+ device <- openEmotivDevice model $ case reverse devices of d:_ -> d+ [] -> error "no Epoc devices found" m'xConnection <- connect xy <- newIORef (0,0)
hemokit.cabal view
@@ -1,5 +1,5 @@ name: hemokit-version: 0.6.0+version: 0.6.1 license: MIT copyright: 2013 Niklas Hambüchen <mail@nh2.me>, Patrick Chilton <chpatrick@gmail.com> author: Niklas Hambüchen <mail@nh2.me>, Patrick Chilton <chpatrick@gmail.com>@@ -7,7 +7,7 @@ category: Bioinformatics build-type: Simple stability: experimental-tested-with: GHC==7.6.2+tested-with: GHC==7.6.3, GHC==7.8.1 cabal-version: >= 1.10 homepage: https://github.com/nh2/haskell-hemokit bug-reports: https://github.com/nh2/haskell-hemokit/issues@@ -63,15 +63,17 @@ build-depends: base >= 4 && < 5 , aeson >= 0.6.1.0+ , base64-bytestring >= 1.0.0.1 , bytestring >= 0.9.2.1- , cipher-aes >= 0.1.7+ , cipher-aes >= 0.2.0 , conduit >= 1 , deepseq >= 1.2 , deepseq-generics >= 0.1 , hidapi >= 0.1.2 , mtl >= 2.1.2 , network-simple >= 0.3.0- , optparse-applicative >= 0.5.2.1+ , optparse-applicative >= 0.7.0+ , text >= 0.11.1.1 , vector >= 0.9 , websockets >= 0.8.0.0 hs-source-dirs:@@ -111,10 +113,9 @@ base , hemokit , aeson >= 0.6.1.0- , base64-bytestring >= 1.0.0.1 , bytestring >= 0.9.2.1 , network-simple >= 0.3.0- , optparse-applicative >= 0.5.2.1+ , optparse-applicative >= 0.7.0 , pretty-show >= 1.0 , split >= 0.2.2 , time >= 1.4@@ -134,11 +135,10 @@ base , hemokit , aeson >= 0.6.1.0- , base64-bytestring >= 1.0.0.1 , bytestring >= 0.9.2.1 , conduit >= 1 , network-simple >= 0.3.0- , optparse-applicative >= 0.5.2.1+ , optparse-applicative >= 0.7.0 , pretty-show >= 1.0 , split >= 0.2.2 , time >= 1.4@@ -199,6 +199,7 @@ base , hemokit , bytestring >= 0.9.2.1+ , hspec >= 1.8.3 , HUnit >= 1.2 , vector >= 0.9 ghc-options: -Wall
src/Hemokit.hs view
@@ -112,7 +112,7 @@ sn x | x >= 0 = index num x | otherwise = sn (BS.length num + x) c = fromIntegral . ord- key = initKey . BS.pack $ start ++ middle ++ end+ key = initAES . BS.pack $ start ++ middle ++ end start = [ sn (-1), 0, sn (-2)] middle = case typ of
src/Hemokit/Internal/Utils.hs view
@@ -1,9 +1,15 @@ module Hemokit.Internal.Utils ( withJustM , untilNothing+ , textBase64 ) where +import qualified Data.ByteString as BS+import qualified Data.ByteString.Base64 as Base64+import Data.Text (Text)+import qualified Data.Text.Encoding as T + -- | If the monad retuns a Just, runs the function on its contents. -- Returns True if the action was executed. withJustM :: (Monad m) => m (Maybe a) -> (a -> m ()) -> m Bool@@ -16,3 +22,12 @@ untilNothing act f = act `withJustM` (\x -> f x >> again) where again = untilNothing act f >> return () -- void would be nicer+++-- | Base64-encodes a ByteString as Text.+--+-- This cannot fail since Base64 is ASCII.+textBase64 :: BS.ByteString -> Text+textBase64 bs = case T.decodeUtf8' (Base64.encode bs) of+ Left ex -> error $ "textBase64: BUG: base64 encoding cannot be encoded: " ++ show ex -- this cannot happen+ Right t -> t
src/Hemokit/Start.hs view
@@ -12,7 +12,6 @@ , getEmotivDeviceFromArgs ) where -import Data.List import Options.Applicative import System.IO (stdin) @@ -81,12 +80,12 @@ [] -> fail "No devices found." _ -> case serial of - -- Pick the device with the serial the user wants- Just s -> case find ((Just s ==) . deviceInfoSerial) devices of- Nothing -> fail $ "No device with serial " ++ show s- Just d -> Right <$> openEmotivDevice model d+ -- Pick the last device with the serial the user wants+ Just s -> case reverse [ d | d <- devices, deviceInfoSerial d == Just s ] of+ [] -> fail $ "No device with serial " ++ show s+ d:_ -> print d >> Right <$> openEmotivDevice model d -- TODO Do smarter auto detection, e.g. filter for Emotiv vendorIDs -- or the "EPOC BCI" product string. -- The user selected no serial, we just use the last device- _ -> Right <$> openEmotivDevice model (last devices)+ _ -> print (last devices) >> Right <$> openEmotivDevice model (last devices)
test/Tests.hs view
@@ -31,10 +31,9 @@ let encrypted = "S\a\205\165\195\182\244\DC4\NAKo\255K\\\247\146>tB\144q\165-\192\221\CANSa\150V,@\180" decrypted = decrypt _SERIAL Consumer encrypted- decrypted_expected = "P}::\199\183\221\193|!\250h\205\NUL\NUL\NUL\STX\ENQX\r\162E|\218)\ETB\155\US\224gi9"+ rawData_expected = makeEmotivRawData "P}::\199\183\221\193|!\250h\205\NUL\NUL\NUL\STX\ENQX\r\162E|\218)\ETB\155\US\224gi9" packet_expected = EmotivPacket- { packetRawData = makeEmotivRawData decrypted_expected- , packetCounter = 80+ { packetCounter = 80 , packetBattery = Nothing , packetGyroX = -1 , packetGyroY = 8@@ -42,5 +41,5 @@ , packetQuality = Just (FC6, 0) } - it "decrypts a valid packet" $ emotivRawDataBytes decrypted @?= decrypted_expected- it "parses a valid packet" $ parsePacket decrypted @?= packet_expected+ it "decrypts a valid packet" $ decrypted @?= rawData_expected+ it "parses a valid packet" $ parsePacket decrypted @?= packet_expected