kafka-device-joystick (empty) → 0.1.5.0
raw patch · 7 files changed
+351/−0 lines, 7 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-joystick.cabal +49/−0
- src/Main.hs +46/−0
- src/Network/UI/Kafka/Joystick.hs +71/−0
- src/System/Hardware/Linux/Joystick.hs +149/−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 joystick](https://www.kernel.org/doc/Documentation/input/joystick-api.txt) events 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-joystick -- /dev/input/js0 joystick-client localhost 9092 events joystick++Also see https://hackage.haskell.org/package/kafka-device/.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ kafka-device-joystick.cabal view
@@ -0,0 +1,49 @@+name: kafka-device-joystick+version: 0.1.5.0+synopsis: Linux joystick events via a Kafka message broker+description: This package contains functions for passing Linux joystick events to topics on a Kafka message broker \<<https://kafka.apache.org/>\>. The joystick's driver must conform to the Linux Joystick API \<<https://www.kernel.org/doc/Documentation/input/joystick-api.txt>\>. 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-joystick+bug-reports: https://bwbush.atlassian.net/projects/HKAFDEV/issues/+package-url: https://bitbucket.org/functionally/kafka-device-joystick/downloads/kafka-device-joystick-0.1.5.0.tar.gz++extra-source-files: ReadMe.md++source-repository head+ type: git+ location: https://bitbucket.org/functionally/kafka-device-joystick+ +library+ exposed-modules: Network.UI.Kafka.Joystick+ System.Hardware.Linux.Joystick+ 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-joystick+ 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 Linux joystick.+-}+++module Main (+-- * Main entry+ main+) where+++import Data.String (IsString(fromString))+import Network.UI.Kafka.Joystick (joystickLoop)+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) <-+ joystickLoop+ device+ (fromString client)+ (fromString host, toEnum $ read port)+ (fromString topic)+ sensor+ result <- loop+ either print return result+ _ -> putStrLn "USAGE: kafka-device-joystick device client host port topic sensor"
+ src/Network/UI/Kafka/Joystick.hs view
@@ -0,0 +1,71 @@+{-|+Module : Network.UI.Kafka.Joystick+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 Linux joystick, which must conform to the Linux Joystick API \<<https://www.kernel.org/doc/Documentation/input/joystick-api.txt>\>.+-}+++{-# LANGUAGE RecordWildCards #-}+++module Network.UI.Kafka.Joystick (+-- * Event handling+ joystickLoop+) where+++import Control.Monad (guard)+import Data.ByteString.Lazy.Char8 (hGet)+import Data.Maybe (catMaybes)+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.Joystick (Joystick(..), byteLength, interpretJoystick, maxValue)+import System.IO (IOMode(ReadMode), hClose, openFile)+++-- | Produce events for a Kafka topic from a Linux Joystick.+joystickLoop :: FilePath -- ^ The path to the joystick device, e.g. "\/dev\/input\/js0".+ -> 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.+joystickLoop path client address topic sensor =+ do+ joystick <- openFile path ReadMode+ (exit, loop) <-+ producerLoop client address topic sensor+ $ translate+ . interpretJoystick+ <$> hGet joystick byteLength+ return+ (+ do+ exit+ hClose joystick+ , loop+ )+++-- | Translate a Linux Joystick event into events for Kafka.+translate :: Joystick -- ^ The joystick event.+ -> [Event] -- ^ The corresponding events for Kafka.+translate Joystick{..} =+ catMaybes+ [+ do+ guard button+ return+ $ ButtonEvent (IndexButton number, if value == 0 then Up else Down)+ , do+ guard axis+ return+ $ AnalogEvent number (fromIntegral value / fromIntegral maxValue)+ ]
+ src/System/Hardware/Linux/Joystick.hs view
@@ -0,0 +1,149 @@+{-|+Module : System.Hardware.Linux.Joystick+Copyright : (c) 2016 Brian W Bush+License : MIT+Maintainer : Brian W Bush <consult@brianwbush.info>+Stability : Experimental+Portability : Linux++Interpret events from a Linux joystick, which must conform to the Linux Joystick API \<<https://www.kernel.org/doc/Documentation/input/joystick-api.txt>\>.+-}+++{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE RecordWildCards #-}+++module System.Hardware.Linux.Joystick (+-- * Types and values+ Joystick(..)+, minValue+, maxValue+, byteLength+-- * Event handling+, interpretJoystick+, readJoystick+) where+++import Data.Aeson.Types (FromJSON, ToJSON)+import Data.Binary (Binary(..), decode, encode)+import Data.Binary.Get (getWord16host, getWord32host)+import Data.Binary.Put (putWord16host, putWord32host)+import Data.Bits ((.&.), complement, shift)+import Data.ByteString.Lazy.Char8 as BS (ByteString, length, readFile, splitAt)+import Data.Serialize (Serialize)+import Data.Word (Word8, Word16, Word32)+import GHC.Generics (Generic)+++-- | The minimum for 'value'.+minValue :: Int+minValue = - maxValue+++-- | The maximum for 'value'.+maxValue :: Int+maxValue = 1 `shift` 15 - 1+++-- | The number of bytes in a joystick event.+byteLength :: Integral a => a+byteLength =+ fromIntegral+ . BS.length+ . encode+ $ RawJoystick 0 0 0 0++++data RawJoystick =+ RawJoystick+ {+ rawTime :: Word32+ , rawValue :: Word16+ , rawType :: Word8+ , rawNumber :: Word8+ }+ deriving (Eq, Ord, Read, Show)++instance Binary RawJoystick where+ get =+ do+ rawTime <- getWord32host+ rawValue <- getWord16host+ rawType <- get+ rawNumber <- get+ return RawJoystick{..}+ put RawJoystick{..} =+ do+ putWord32host rawTime+ putWord16host rawValue+ put rawType+ put rawNumber+++-- | Joystick data. See \<<https://www.kernel.org/doc/Documentation/input/joystick-api.txt>\> for details.+data Joystick =+ Joystick+ {+ timestamp :: Int -- ^ The event timestamp in milliseconds.+ , value :: Int -- ^ The data value.+ , number :: Int -- ^ The button or axis number.+ , button :: Bool -- ^ Whether the button is being reported.+ , axis :: Bool -- ^ Whether the axis is being reported.+ , initial :: Bool -- ^ Whether the initial value is being reported.+ }+ deriving (Eq, Generic, Ord, Read, Show)++instance FromJSON Joystick++instance ToJSON Joystick++instance Binary Joystick++instance Serialize Joystick+++-- | Interpret Linux Joystick bytes accoding to \<<https://www.kernel.org/doc/Documentation/input/joystick-api.txt>\>.+interpretJoystick :: ByteString -- ^ The eight bytes.+ -> Joystick -- ^ The corresponding joystick data.+interpretJoystick x =+ let+ RawJoystick{..} = decode x+ timestamp = fromIntegral rawTime+ value = twosComplement rawValue+ number = fromIntegral rawNumber+ typ = fromIntegral rawType :: Int+ button = 0x01 .&. typ /= 0+ axis = 0x02 .&. typ /= 0+ initial = 0x80 .&. typ /= 0+ in+ Joystick{..}+++-- | Decode a two's complement.+twosComplement :: Word16 -- ^ 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` 15+++-- | Read a stream of joystick data.+readJoystick :: FilePath -- ^ The joystick device, e.g., "\/dev\/input\/js0".+ -> IO [Joystick] -- ^ Action to read the joystick data.+readJoystick path =+ let+ chunks :: ByteString -> [ByteString]+ chunks x =+ let+ (y, ys) = BS.splitAt 8 x+ in+ y : chunks ys+ in+ map interpretJoystick+ . chunks + <$> BS.readFile path