module EIBd.Client.Types (
DatapointType (..),
DPTB1,
DPTB2 (..),
DPTB1U3 (..),
DPTU8,
DPTS8,
DPTF16 (..),
) where
import Control.Monad
import Data.Word
import Data.Int
import Data.Bits
import qualified Data.ByteString as B
class DatapointType a where
fromDPT :: a -> B.ByteString
toDPT :: B.ByteString -> Maybe a
type DPTB1 = Bool
instance DatapointType Bool where
fromDPT False = "\NUL"
fromDPT True = "\SOH"
toDPT "\NUL" = Just False
toDPT "\SOH" = Just True
toDPT _ = Nothing
data DPTB2 = DPTB2 Bool Bool
deriving (Show, Eq, Ord)
instance DatapointType DPTB2 where
fromDPT (DPTB2 False False) = "\NUL"
fromDPT (DPTB2 False True) = "\SOH"
fromDPT (DPTB2 True False) = "\STX"
fromDPT (DPTB2 True True) = "\ETX"
toDPT "\NUL" = Just (DPTB2 False False)
toDPT "\SOH" = Just (DPTB2 False True)
toDPT "\STX" = Just (DPTB2 True False)
toDPT "\ETX" = Just (DPTB2 True True)
toDPT _ = Nothing
data DPTB1U3 = DPTB1U3 Bool Word8
deriving (Show, Eq, Ord)
instance DatapointType DPTB1U3 where
fromDPT (DPTB1U3 True u) = B.singleton (8 .|. (u .&. 7))
fromDPT (DPTB1U3 False u) = B.singleton (u .&. 7)
toDPT n = do
guard (B.length n == 1)
let x = B.head n
return (DPTB1U3 (testBit x 3) (x .&. 7))
type DPTU8 = Word8
instance DatapointType Word8 where
fromDPT w = B.pack [0, w]
toDPT n = do
guard (B.length n == 2)
return (B.last n)
type DPTS8 = Int8
instance DatapointType Int8 where
fromDPT w = B.pack [0, fromIntegral w]
toDPT n = do
guard (B.length n == 2)
return (fromIntegral (B.last n))
newtype DPTF16 = DPTF16 Float
deriving (Show, Eq, Ord)
instance DatapointType DPTF16 where
fromDPT (DPTF16 x) =
B.pack [0, s .|. shift e 3 .|. fromIntegral leftM, fromIntegral rightM]
where
(s, f) = if x < 0 then (bit 7, x) else (0, x)
(e, m) = findE 0 (f * 100) :: (Word8, Word16)
leftM = shift m (8) .&. 7
rightM = m .&. 255
findE t r
| t > 15 = error "DPTF16 is too large"
| r > 2047 = findE (t + 1) (r / 2)
| otherwise = (t, round r)
toDPT n = do
guard (B.length n == 3)
let [_, x, y] = B.unpack n
let sign = if testBit x 7 then (0.01) else 0.01
let mant = shiftL (fromIntegral x .&. 7) 8 .|. fromIntegral y :: Word16
let power = 2 ^ (shiftR x 3 .&. 15)
return (DPTF16 (sign * fromIntegral mant * power))