obd 0.2.0.2 → 0.2.1.0
raw patch · 4 files changed
+62/−11 lines, 4 files
Files
- obd.cabal +1/−1
- src/System/Hardware/ELM327.hs +2/−2
- src/System/Hardware/ELM327/Connection.hs +58/−7
- src/System/Hardware/ELM327/Simulator.hs +1/−1
obd.cabal view
@@ -1,5 +1,5 @@ name: obd-version: 0.2.0.2+version: 0.2.1.0 synopsis: Communicate to OBD interfaces over ELM327 description: Haskell library to communicate with OBD-II over ELM327,
src/System/Hardware/ELM327.hs view
@@ -25,11 +25,11 @@ , stopb = One , parity = NoParity , flowControl = NoFlowControl- , timeout = 5 {- 500ms -} }+ , timeout = 50 {- 5000ms -} } port <- openSerial fp s is <- makeInputStream (produce port) os <- makeOutputStream (consume port)- initialize $ Con is os+ initialize $ Con is os (const $ return ()) where produce port = do b <- Port.recv port 8
src/System/Hardware/ELM327/Connection.hs view
@@ -1,9 +1,36 @@ {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE FlexibleContexts #-} -- | Basic interface for communication with the ELM327.-module System.Hardware.ELM327.Connection where+module System.Hardware.ELM327.Connection (+ -- * Basic types+ Con(..)+, ConT(..)+, withCon+, ConError(..) + -- * Interacting with connections+, close+, close'+, send+, sendBytes+, sendString+, recvRaw+, recv+, flushOutputStream++ -- * Sending commands+, at+, obd++ -- * Logging+, fileLog+)+where++import Prelude hiding (log)+ import Control.Lens (re, (^.), (^?))+import Control.Monad (void) import Control.Monad.Except (ExceptT, MonadError, runExceptT, throwError) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Reader (ReaderT, MonadReader, runReaderT, ask)@@ -15,6 +42,7 @@ import Data.Word (Word8) import qualified Data.ByteString.Char8 as Char8 +import System.IO (openFile, IOMode(..), hPutStrLn, hFlush, hClose) import System.IO.Streams (InputStream, OutputStream) import qualified System.IO.Streams as Streams @@ -31,8 +59,10 @@ import System.Hardware.ELM327.Utils.Monad (maybeToExcept, orThrow) -- | A connection to an ELM327 device that can be closed.-data Con = Con { conInput :: InputStream ByteString- , conOutput :: OutputStream ByteString }+data Con = Con { conInput :: InputStream ByteString -- ^ Input stream of the connection+ , conOutput :: OutputStream ByteString -- ^ Output stream of the connection+ , conLog :: Maybe ByteString -> IO () -- ^ Function that will be called with every byte sent or received, 'Nothing' indicates connection closed.+ } -- | Monad transformer for connection operations. newtype ConT m a = ConT { runConT :: ReaderT Con (ExceptT ConError m) a }@@ -51,9 +81,12 @@ close :: MonadIO m => ConT m () close = ask >>= close' + -- | Close the connection close' :: MonadIO m => Con -> m ()-close' = liftIO . Streams.write Nothing . conOutput+close' c = do+ liftIO . Streams.write Nothing . conOutput $ c+ liftIO $ conLog c Nothing -- | Send an ELM327 command. send :: MonadIO m => Command -> ConT m ()@@ -64,7 +97,7 @@ -- | Send all bytes to the ELM327. sendBytes :: MonadIO m => ByteString -> ConT m ()-sendBytes x = ask >>= liftIO . Streams.write (Just x) . conOutput+sendBytes x = ask >>= liftIO . Streams.write (Just x) . conOutput >> void (log $ Just x) -- | Send a string to the ELM327. sendString :: MonadIO m => String -> ConT m ()@@ -72,7 +105,7 @@ -- | Receive available ELM327 caracters as a byte string recvRaw :: MonadIO m => ConT m (Maybe ByteString)-recvRaw = ask >>= liftIO . Streams.read . conInput+recvRaw = ask >>= liftIO . Streams.read . conInput >>= log -- | Receive an ELM327 response as a byte string. recv :: MonadIO m => ConT m (Maybe ByteString)@@ -81,7 +114,7 @@ if Char8.null s then return Nothing else return (Just s) where recv' xs = do- bs <- ask >>= liftIO . Streams.read . conInput+ bs <- ask >>= liftIO . Streams.read . conInput >>= log case bs of Nothing -> throwError ConTimeoutError Just bs' -> handle' xs bs' handle' xs bs =@@ -118,3 +151,21 @@ removeStatusPrefixes x = foldl stripPrefix' x statusPrefixes stripPrefix' x pref = fromMaybe x (stripPrefix pref x) stripHeader = stripPrefix [0x40 + obdMode cmd, obdPID cmd]++-- | Log sent or received bytes+log :: MonadIO m => Maybe ByteString -> ConT m (Maybe ByteString)+log Nothing = return Nothing+log (Just x) = do+ log' <- conLog <$> ask+ liftIO $ log' (Just x)+ return $ Just x++-- | Create a file logger that can be used with 'conLog'.+fileLog :: FilePath -> IO (Maybe ByteString -> IO ())+fileLog fp = do+ fh <- openFile fp AppendMode+ hPutStrLn fh "Connecting..."+ return $ log' fh+ where+ log' fh Nothing = hClose fh+ log' fh (Just x) = Char8.hPut fh x >> hFlush fh
src/System/Hardware/ELM327/Simulator.hs view
@@ -83,7 +83,7 @@ is <- makeInputStream (produce c) os <- makeOutputStream (consume c) _ <- forkIO . void $ runStateT (runSimulator c) s- return $ Con is os+ return $ Con is os (const $ return ()) where produce = atomically . readTMChan . _conOutputBuffer consume c Nothing = atomically $ closeTMChan (_conInputBuffer c)