kafka-device-spacenav (empty) → 0.1.5.0
raw patch · 8 files changed
+368/−0 lines, 8 filesdep +aesondep +basedep +binarysetup-changed
Dependencies added: aeson, base, binary, bytestring, cereal, kafka-device, milena
Files
- LICENSE +20/−0
- ReadMe.md +14/−0
- Setup.hs +2/−0
- kafka-device-spacenav.cabal +50/−0
- src/Main.hs +46/−0
- src/Network/UI/Kafka/SpaceNav.hs +60/−0
- src/System/Hardware/Linux/Input.hs +64/−0
- src/System/Hardware/Linux/SpaceNav.hs +112/−0
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2016 Brian W Bush++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ReadMe.md view
@@ -0,0 +1,14 @@+Linux joystick events via a Kafka message broker+================================================++This package contains functions for passing Linux device events from a [SpaceNavigator](http://www.3dconnexion.com/products/spacemouse/spacenavigator.html) to topics on a [Kafka message broker](https://kafka.apache.org/).+++Clients+-------++The simple Kafka client that produces events from the joystick can be run, for example, as follows:++ cabal run kafka-device-spacenav -- /dev/input/spacenavigator spacenav-client localhost 9092 events spacenav++Also see https://hackage.haskell.org/package/kafka-device/.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
@@ -0,0 +1,50 @@+name: kafka-device-spacenav+version: 0.1.5.0+synopsis: Linux SpaceNavigator events via a Kafka message broker+description: This package contains functions for passing Linux device events from a SpaceNavigator \<<http://www.3dconnexion.com/products/spacemouse/spacenavigator.html>\> to topics on a Kafka message broker \<<https://kafka.apache.org/>\>. Also see \<<https://hackage.haskell.org/package/kafka-device/>\>.+license: MIT+license-file: LICENSE+author: Brian W Bush <consult@brianwbush.info>+maintainer: Brian W Bush <consult@brianwbush.info>+copyright: (c) 2016 Brian W Bush+category: Hardware+build-type: Simple+cabal-version: >= 1.10+stability: Experimental+homepage: https://bitbucket.org/functionally/kafka-device-spacenav+bug-reports: https://bwbush.atlassian.net/projects/HKAFDEV/issues/+package-url: https://bitbucket.org/functionally/kafka-device-spacenav/downloads/kafka-device-spacenav-0.1.5.0.tar.gz++extra-source-files: ReadMe.md++source-repository head+ type: git+ location: https://bitbucket.org/functionally/kafka-device-spacenav+ +library+ exposed-modules: Network.UI.Kafka.SpaceNav+ System.Hardware.Linux.Input+ System.Hardware.Linux.SpaceNav+ build-depends: base >= 4.8 && < 5+ , aeson+ , binary+ , bytestring+ , cereal+ , kafka-device+ , milena+ hs-source-dirs: src+ ghc-options: -Wall+ default-language: Haskell2010++executable kafka-device-spacenav+ main-is: Main.hs+ build-depends: base+ , aeson+ , binary+ , bytestring+ , cereal+ , kafka-device+ , milena+ hs-source-dirs: src+ ghc-options: -Wall+ default-language: Haskell2010
+ src/Main.hs view
@@ -0,0 +1,46 @@+{-|+Module : Main+Copyright : (c) 2016 Brian W Bush+License : MIT+Maintainer : Brian W Bush <consult@brianwbush.info>+Stability : Experimental+Portability : Linux++Produce events for a Kafka topic from a SpaceNavigator \<<http://www.3dconnexion.com/products/spacemouse/spacenavigator.html>\> on Linux.+-}+++module Main (+-- * Main entry+ main+) where+++import Data.String (IsString(fromString))+import Network.UI.Kafka.SpaceNav (spacenavLoop)+import System.Environment (getArgs)+++-- The main action.+main :: IO ()+main =+ do+ args <- getArgs+ case args of+ [device, client, host, port, topic, sensor] ->+ do+ putStrLn $ "Device: " ++ device+ putStrLn $ "Kafka client: " ++ client+ putStrLn $ "Kafka address: (" ++ host ++ "," ++ port ++ ")"+ putStrLn $ "Kafka topic: " ++ topic+ putStrLn $ "Sensor name: " ++ sensor+ (_, loop) <-+ spacenavLoop+ device+ (fromString client)+ (fromString host, toEnum $ read port)+ (fromString topic)+ sensor+ result <- loop+ either print return result+ _ -> putStrLn "USAGE: kafka-device-spacenav device client host port topic sensor"
@@ -0,0 +1,60 @@+{-|+Module : Network.UI.Kafka.SpaceNav+Copyright : (c) 2016 Brian W Bush+License : MIT+Maintainer : Brian W Bush <consult@brianwbush.info>+Stability : Experimental+Portability : Linux++Produce events for a Kafka topic from a SpaceNavigator \<<http://www.3dconnexion.com/products/spacemouse/spacenavigator.html>\> on Linux.+-}+++{-# LANGUAGE RecordWildCards #-}+++module Network.UI.Kafka.SpaceNav (+-- * Event handling+ spacenavLoop+) where+++import Data.ByteString.Lazy.Char8 (hGet)+import Network.Kafka (KafkaAddress, KafkaClientId)+import Network.Kafka.Protocol (TopicName)+import Network.UI.Kafka (ExitAction, LoopAction, Sensor, producerLoop)+import Network.UI.Kafka.Types (Button(..), Event(..), Toggle(..))+import System.Hardware.Linux.SpaceNav (SpaceNav(..), byteLength, interpretSpaceNav)+import System.IO (IOMode(ReadMode), hClose, openFile)+++-- | Produce events for a Kafka topic from a Linux SpaceNav.+spacenavLoop :: FilePath -- ^ The path to the spacenav device, e.g. "\/dev\/input\/spacenav0".+ -> KafkaClientId -- ^ A Kafka client identifier for the producer.+ -> KafkaAddress -- ^ The address of the Kafka broker.+ -> TopicName -- ^ The Kafka topic name.+ -> Sensor -- ^ The name of the sensor producing events.+ -> IO (ExitAction, LoopAction) -- ^ Action to create the exit and loop actions.+spacenavLoop path client address topic sensor =+ do+ spacenav <- openFile path ReadMode+ (exit, loop) <-+ producerLoop client address topic sensor+ $ translate+ . interpretSpaceNav+ <$> hGet spacenav byteLength+ return+ (+ do+ exit+ hClose spacenav+ , loop+ )+++-- | Translate a SpaceNavigator event on Linux into events for Kafka.+translate :: SpaceNav -- ^ The SpaceNavigator event.+ -> [Event] -- ^ The corresponding events for Kafka.+translate SpaceNavButton{..} = [ButtonEvent (IndexButton number, if pressed then Down else Up)]+translate SpaceNavAnalog{..} = [AnalogEvent number setting]+translate SpaceNavNull = []
+ src/System/Hardware/Linux/Input.hs view
@@ -0,0 +1,64 @@+{-|+Module : System.Hardware.Linux.Input+Copyright : (c) 2016 Brian W Bush+License : MIT+Maintainer : Brian W Bush <consult@brianwbush.info>+Stability : Experimental+Portability : Linux++Parse events from Linux /dev/input streams. See \<<https://github.com/torvalds/linux/blob/master/include/uapi/linux/input.h>\> for details.+-}+++{-# LANGUAGE RecordWildCards #-}+++module System.Hardware.Linux.Input (+-- * Types+ InputEvent(..)+, byteLength+) where+++import Data.Binary (Binary(..), encode)+import Data.Binary.Get (getWordhost, getWord16host, getWord32host)+import Data.Binary.Put (putWordhost, putWord16host, putWord32host)+import Data.Word (Word16, Word32)++import qualified Data.ByteString.Lazy as BS (length)++-- | Input events from Linux /dev/input streams. See \<<https://github.com/torvalds/linux/blob/master/include/uapi/linux/input.h>\> for details.+data InputEvent =+ InputEvent+ {+ timeval :: (Word, Word) -- ^ The time stamp in seconds and microseconds.+ , typ :: Word16 -- ^ The event type.+ , code :: Word16 -- ^ The event code.+ , value :: Word32 -- ^ The event value.+ }+ deriving (Eq, Ord, Read, Show)++instance Binary InputEvent where+ get =+ do+ timeval <- (,) <$> getWordhost <*> getWordhost+ typ <- getWord16host+ code <- getWord16host+ value <- getWord32host+ return InputEvent{..}+ put InputEvent{..} =+ do+ putWordhost $ fst timeval+ putWordhost $ snd timeval+ putWord16host typ+ putWord16host code+ putWord32host value+++-- | The number of bytes in an input event.+byteLength :: Integral a => a+byteLength =+ fromIntegral+ . BS.length+ . encode+ $ InputEvent (0, 0) 0 0 0
@@ -0,0 +1,112 @@+{-|+Module : System.Hardware.Linux.SpaceNav+Copyright : (c) 2016 Brian W Bush+License : MIT+Maintainer : Brian W Bush <consult@brianwbush.info>+Stability : Experimental+Portability : Linux++Interpret events from a SpaceNavigator \<<http://www.3dconnexion.com/products/spacemouse/spacenavigator.html>\>.+-}+++{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE RecordWildCards #-}+++module System.Hardware.Linux.SpaceNav (+-- * Types and sizes+ SpaceNav(..)+, byteLength+-- * Event handling+, interpretSpaceNav+, readSpaceNav+) where+++import Data.Aeson.Types (FromJSON, ToJSON)+import Data.Binary (Binary(..), decode)+import Data.Bits ((.&.), complement, shift)+import Data.ByteString.Lazy.Char8 as BS (ByteString, readFile, splitAt)+import Data.Serialize (Serialize)+import Data.Word (Word32)+import GHC.Generics (Generic)+import System.Hardware.Linux.Input (InputEvent(..), byteLength)+++-- | SpaceNavigator data.+data SpaceNav =+ SpaceNavButton+ {+ timestamp :: Integer -- ^ The event timestamp, in POSIX picoseconds.+ , number :: Int -- ^ The button number.+ , pressed :: Bool -- ^ Whether the button is depressed.+ }+ | SpaceNavAnalog+ {+ timestamp :: Integer -- ^ The event timestamp, in POSIX picoseconds.+ , number :: Int -- ^ The axis number.+ , setting :: Double -- ^ The data value.+ }+ | SpaceNavNull+ deriving (Eq, Generic, Ord, Read, Show)++instance FromJSON SpaceNav++instance ToJSON SpaceNav++instance Binary SpaceNav++instance Serialize SpaceNav+++-- | Interpret SpaceNavigator event bytes on a Linux input deviceThis interpretation is based on \<<https://github.com/vrpn/vrpn/blob/master/vrpn_3DConnexion.h>\> and \<<https://github.com/vrpn/vrpn/blob/master/vrpn_3DConnexion.c>\>.+interpretSpaceNav :: ByteString -- ^ The bytes from /dev/input.+ -> SpaceNav -- ^ The corresponding SpaceNavigator data.+interpretSpaceNav x =+ let+ InputEvent{..} = decode x+ (seconds, microseconds) = timeval+ seconds' = fromIntegral seconds :: Integer+ microseconds' = fromIntegral microseconds :: Integer+ timestamp = (10^(6 :: Int) * seconds' + microseconds') * 10^(6 :: Int)+ in+ case typ of+ 0x01 -> let+ number = fromIntegral $ code .&. 0x00ff+ pressed = value /= 0+ in+ SpaceNavButton{..}+ 0x02 -> let+ number = fromIntegral code+ setting = fromIntegral (twosComplement value) / 400+ in+ SpaceNavAnalog{..}+ _ -> SpaceNavNull+++-- | Decode a two's complement.+twosComplement :: Word32 -- ^ The two's complement.+ -> Int -- ^ The corresponding integer.+twosComplement x =+ fromIntegral (x' .&. complement mask) - fromIntegral (x' .&. mask)+ where+ x' = fromIntegral x :: Int+ mask = 1 `shift` 31+++-- | Read a stream of SpaceNavigator data.+readSpaceNav :: FilePath -- ^ The SpaceNavigator device, e.g., "\/dev\/input\/spacenav0".+ -> IO [SpaceNav] -- ^ Action to read the SpaceNavigator data.+readSpaceNav path =+ let+ chunks :: ByteString -> [ByteString]+ chunks x =+ let+ (y, ys) = BS.splitAt 8 x+ in+ y : chunks ys+ in+ map interpretSpaceNav+ . chunks + <$> BS.readFile path