fuyu-gpio-0.0.9.0: src/Fuyu/GPIO/EdgeEvent.hs
{-# LANGUAGE PatternSynonyms #-}
-- |
-- Module : Fuyu.GPIO.EdgeEvent
-- Description : High-level edge event waiting, reading, and buffer management.
-- Maintainer : BassGT
-- Stability : experimental
-- Portability : POSIX (Linux gpiod v2)
--
-- This module provides managed resource brackets ('withBuffer') and functions for waiting
-- on edge events ('waitEvents') and reading them ('readEvents') securely using the
-- 'ReadyRequest' capability token.
module Fuyu.GPIO.EdgeEvent
( -- * Security Token & Wait Result
WaitResult(..)
, ReadyRequest(..)
, readyToRequest
-- * Buffer & Event Types
, Buffer
, Capacity
, userBufferCapacity
, capacity
, Event
, Timeout
, pattern Nanoseconds
, pattern Immediate
, pattern Infinite
, Timestamp
, EdgeEventType
, pattern Rising
, pattern Falling
-- * Event Data Type & Parser
, NonEmpty(..)
, EdgeEvent(..)
, parseEvent
-- * Event Buffer Operations (Managed)
, withBuffer
, bufferCapacity
, bufferNumEvents
, bufferEvent
-- * Waiting & Reading Events
, waitEvents
, readEvents
, withRawEvents
-- * RawEdgeEvent Metadata Accessors
, eventType
, timestampNs
, lineOffset
, globalSeqNo
, lineSeqNo
, copyEvent
) where
import Control.Exception (bracket)
import Control.Monad (forM)
import Data.List.NonEmpty (NonEmpty(..))
import qualified Data.List.NonEmpty as NE
import Data.Word (Word64)
import qualified Fuyu.GPIO.Direct as D
import Fuyu.GPIO.EdgeEvent.Unsafe (newEventBuffer, freeEventBuffer, readEventsRaw)
import Fuyu.GPIO.Exception
import Fuyu.GPIO.Types hiding (eventType)
-- | Allocate an edge event buffer of the specified capacity and free it automatically afterwards.
withBuffer :: Capacity -> (Buffer -> IO a) -> IO a
withBuffer capacity' = bracket (newEventBuffer capacity') freeEventBuffer
-- | Get the capacity of an event buffer.
bufferCapacity :: Buffer -> IO Capacity
bufferCapacity buf = userBufferCapacity <$> D.eventBufferCapacity buf
-- | Get the number of events currently stored in an event buffer.
bufferNumEvents :: Buffer -> IO Word
bufferNumEvents = D.eventBufferNumEvents
-- | Get a specific edge event from the buffer by index.
bufferEvent :: Buffer -> Word -> IO Event
bufferEvent buf idx = unwrapOrThrow ReadEdgeEventsFailed (D.eventBufferGetEvent buf idx)
-- | Wait for edge events to occur on requested lines until the specified timeout.
-- Throws 'WaitEdgeEventsFailed' on error.
waitEvents :: Request -> Timeout -> IO (WaitResult ReadyRequest)
waitEvents req timeout = do
res <- unwrapOrThrow WaitEdgeEventsFailed (D.lineRequestWaitEdgeEvents req timeout)
pure $ case res of
D.EventReady -> EventReady (ReadyRequest req)
D.Timeout -> TimeoutResult
-- | Parse a raw edge event pointer into a pure Haskell 'EdgeEvent' structure.
parseEvent :: Event -> IO EdgeEvent
parseEvent ev = EdgeEvent
<$> D.rawEdgeEventLineOffset ev
<*> D.rawEdgeEventType ev
<*> D.rawEdgeEventTimestampNs ev
-- | Read buffered edge events once 'waitEvents' indicates they are ready,
-- parsing them into a non-empty list of pure 'EdgeEvent' structures.
readEvents :: ReadyRequest -> Buffer -> IO (NonEmpty EdgeEvent)
readEvents readyReq buf = withRawEvents readyReq buf parseEvent
-- | Process raw edge events directly in the buffer using a callback without intermediate allocations,
-- returning a non-empty list of results.
withRawEvents :: ReadyRequest -> Buffer -> (Event -> IO a) -> IO (NonEmpty a)
withRawEvents readyReq buf action = do
count <- readEventsRaw readyReq buf
results <- forM [0 .. count - 1] $ \idx -> do
ev <- bufferEvent buf (fromIntegral idx)
action ev
case NE.nonEmpty results of
Just ne -> pure ne
Nothing -> ioError (userError "readEvents: expected at least one event from ReadyRequest but got none")
--------------------------------------------------------------------------------
-- RawEdgeEvent Metadata Accessors
--------------------------------------------------------------------------------
-- | Get the type of event ('Rising' or 'Falling').
eventType :: Event -> IO EdgeEventType
eventType = D.rawEdgeEventType
-- | Get the event timestamp in nanoseconds.
timestampNs :: Event -> IO Timestamp
timestampNs = D.rawEdgeEventTimestampNs
-- | Get the offset of the line that triggered the event.
lineOffset :: Event -> IO Offset
lineOffset = D.rawEdgeEventLineOffset
-- | Get the global sequence number of the event.
globalSeqNo :: Event -> IO Word64
globalSeqNo = D.rawEdgeEventGlobalSeqNo
-- | Get the line-specific sequence number of the event.
lineSeqNo :: Event -> IO Offset
lineSeqNo = D.rawEdgeEventLineSeqNo
-- | Make a copy of a raw edge event object.
copyEvent :: Event -> IO Event
copyEvent ev = unwrapOrThrow RawEdgeEventCopyFailed (D.rawEdgeEventCopy ev)