packages feed

network-can-0.1.0.0: src/Network/SocketCAN.hs

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Network.SocketCAN
  ( withSocketCAN
  , sendCANMessage
  , recvCANMessage
  , Network.Socket.ifNameToIndex
  , SocketCANT
  , CANInterface
  , mkCANInterface
  , NoSuchInterface(..)
  , runSocketCAN
  ) where

import Network.CAN (CANMessage, MonadCAN(..))
import Network.Socket (Socket)
import Network.SocketCAN.Bindings (SockAddrCAN(..))

import Control.Monad.Reader (MonadReader, ask)
import Control.Monad.Trans (MonadTrans(..))
import Control.Monad.Trans.Reader (ReaderT(..))
import UnliftIO

import qualified Control.Exception
import qualified Network.Socket (ifNameToIndex)
import qualified Network.SocketCAN.LowLevel
import qualified Network.SocketCAN.Translate

withSocketCAN
  :: Int
  -> (Socket -> IO a)
  -> IO a
withSocketCAN ifaceIdx act = do
  Control.Exception.bracket
    Network.SocketCAN.LowLevel.socket
    Network.SocketCAN.LowLevel.close
    (\canSock -> do
      Network.SocketCAN.LowLevel.bind
        canSock
        $ Network.SocketCAN.Bindings.SockAddrCAN
          $ fromIntegral ifaceIdx
      act canSock
    )

sendCANMessage
  :: Socket
  -> CANMessage
  -> IO ()
sendCANMessage canSock cm =
  Network.SocketCAN.LowLevel.send
    canSock
    (Network.SocketCAN.Translate.toSocketCANFrame cm)

recvCANMessage
  :: Socket
  -> IO CANMessage
recvCANMessage canSock =
  Network.SocketCAN.LowLevel.recv canSock
  >>= pure . Network.SocketCAN.Translate.fromSocketCANFrame

newtype SocketCANT m a = SocketCANT
  { _unSocketCANT :: ReaderT Socket m a }
  deriving
    ( Functor
    , Applicative
    , Monad
    , MonadReader Socket
    , MonadIO
    , MonadUnliftIO
    )

instance MonadTrans SocketCANT where
  lift = SocketCANT . lift

-- | Run SocketCANT transformer
runSocketCANT
  :: Monad m
  => Socket
  -> SocketCANT m a
  -> m a
runSocketCANT sock =
    (`runReaderT` sock)
  . _unSocketCANT

newtype CANInterface = CANInterface
  { unCANInterface :: String }
  deriving Eq

instance Show CANInterface where
  show = unCANInterface

mkCANInterface :: String -> CANInterface
mkCANInterface = CANInterface

data NoSuchInterface = NoSuchInterface
    deriving Show

instance Exception NoSuchInterface

runSocketCAN
  :: ( MonadIO m
     , MonadUnliftIO m
     )
  => CANInterface
  -> SocketCANT m a
  -> m a
runSocketCAN interface act = do
  mIdx <-
    liftIO
    $ Network.Socket.ifNameToIndex (unCANInterface interface)

  case mIdx of
    Nothing -> throwIO NoSuchInterface
    Just idx -> withRunInIO $ \runInIO ->
      withSocketCAN idx (\s -> runInIO (runSocketCANT s act))

instance MonadIO m => MonadCAN (SocketCANT m) where
  send cm = do
    canSock <- ask
    liftIO $ sendCANMessage canSock cm
  recv = do
    canSock <- ask
    liftIO $ recvCANMessage canSock