{-# LANGUAGE OverloadedStrings #-}
module Network.SakuraIO.Platform.Message (
ModuleID
,OutGoingMessage (..)
,ChannelID
,OGChannel (..)
,Latitude, Longitude, RangeM
,Coordinate (..)
,InComingMessage (..)
,ICChannel (..)
,ChannelValue (..)
) where
import Data.Time.Clock(UTCTime(..))
import Data.Int(Int32(..),Int64(..))
import Data.Word(Word8(..),Word32(..),Word64(..))
import qualified Data.ByteString as BS
import Control.Applicative(empty)
import Data.HashMap.Strict ((!))
import Data.Aeson.Types (Value(..))
import Data.Aeson.Types (FromJSON,parseJSON,(.:),Parser(..))
import Data.Aeson.Types (ToJSON,toJSON,object,(.=))
import Data.Monoid ((<>))
type ModuleID = String
data OutGoingMessage = OGChannels ModuleID UTCTime [OGChannel]
| OGConnection ModuleID UTCTime Bool
| OGLocation ModuleID UTCTime (Maybe Coordinate)
| OGKeepalive UTCTime
deriving (Eq,Show)
type ChannelID = Word8
data OGChannel = OGChannel ChannelID UTCTime ChannelValue deriving (Eq,Show)
type Latitude = Float
type Longitude = Float
type RangeM = Int32
data Coordinate = Coordinate Latitude Longitude RangeM deriving (Eq,Show)
data InComingMessage = ICChannels ModuleID [ICChannel] deriving (Eq,Show)
data ICChannel = ICChannel ChannelID ChannelValue deriving (Eq,Show)
data ChannelValue = CVInt32 Int32
| CVWord32 Word32
| CVInt64 Int64
| CVWord64 Word64
| CVFloat Float
| CVDouble Double
| CVBytes BS.ByteString
deriving (Eq,Show)
instance FromJSON OutGoingMessage where
parseJSON (Object v ) = do
dt <- v .: "datetime"
t <- (v .: "type" :: Parser String)
case t of
"channels" -> case (v ! "payload") of
Object pl -> do
md <- v .: "module"
cs <- pl .: "channels"
return $ OGChannels md dt cs
_ -> fail "playload must be Object"
"connection" -> case (v ! "payload") of
Object pl -> do
md <- v .: "module"
ol <- pl .: "is_online"
return $ OGConnection md dt ol
_ -> fail "playload must be Object"
"location" -> case (v ! "payload") of
Object pl -> do
md <- v .: "module"
cd <- pl .: "coordinate"
return $ OGLocation md dt cd
_ -> fail "playload must be Object"
"keepalive"-> return $ OGKeepalive dt
parseJSON _ = empty
instance FromJSON OGChannel where
parseJSON (Object v) = do
ch <- v .: "channel"
dt <- v .: "datetime"
t <- v .: "type"
v <- case t of
'i' -> CVInt32 <$> v .: "value"
'I' -> CVWord32 <$> v .: "value"
'l' -> CVInt64 <$> v .: "value"
'L' -> CVWord64 <$> v .: "value"
'f' -> CVFloat <$> v .: "value"
'd' -> CVDouble <$> v .: "value"
'b' -> CVBytes . parseHex <$> v .: "value"
return $ OGChannel ch dt v
parseJSON _ = empty
instance FromJSON Coordinate where
parseJSON (Object v) =
Coordinate <$> v .: "latitude" <*> v .: "longitude" <*> v .: "range_m"
parseJSON _ = empty
instance ToJSON InComingMessage where
toJSON (ICChannels md xs) =
object ["type" .= ("channels"::String), "module" .= md,
"payload" .= object ["channels" .= xs]
]
instance ToJSON ICChannel where
toJSON (ICChannel ch (CVInt32 i)) =
object ["channel" .= ch, "type" .= ("i"::String), "value" .= i]
toJSON (ICChannel ch (CVWord32 i)) =
object ["channel" .= ch, "type" .= ("I"::String), "value" .= i]
toJSON (ICChannel ch (CVInt64 l)) =
object ["channel" .= ch, "type" .= ("l"::String), "value" .= l]
toJSON (ICChannel ch (CVWord64 l)) =
object ["channel" .= ch, "type" .= ("L"::String), "value" .= l]
toJSON (ICChannel ch (CVFloat f)) =
object ["channel" .= ch, "type" .= ("f"::String), "value" .= f]
toJSON (ICChannel ch (CVDouble d)) =
object ["channel" .= ch, "type" .= ("d"::String), "value" .= d]
toJSON (ICChannel ch (CVBytes b)) =
object ["channel" .= ch, "type" .= ("b"::String), "value" .= toHex b]
parseHex :: [Char] -> BS.ByteString
parseHex = BS.pack . f . splitAt 2
where
f :: ([Char],[Char]) -> [Word8]
f ([], _) = []
f (xs, ys) = (g xs) : (f $ splitAt 2 ys)
g :: [Char] -> Word8
g (h:l:[]) = (d h) * 16 + (d l)
where
d :: Char -> Word8
d '0' = 0x0
d '1' = 0x1
d '2' = 0x2
d '3' = 0x3
d '4' = 0x4
d '5' = 0x5
d '6' = 0x6
d '7' = 0x7
d '8' = 0x8
d '9' = 0x9
d 'a' = 0xa
d 'b' = 0xb
d 'c' = 0xc
d 'd' = 0xd
d 'e' = 0xe
d 'f' = 0xf
d 'A' = 0xa
d 'B' = 0xb
d 'C' = 0xc
d 'D' = 0xd
d 'E' = 0xe
d 'F' = 0xf
toHex :: BS.ByteString -> [Char]
toHex bs = foldr (\b->(<>)(g b)) [] $ BS.unpack bs
where
g :: Word8 -> [Char]
g w = [d $ w `div` 16, d $ w `mod` 16]
where
d :: Word8 -> Char
d 0x0 = '0'
d 0x1 = '1'
d 0x2 = '2'
d 0x3 = '3'
d 0x4 = '4'
d 0x5 = '5'
d 0x6 = '6'
d 0x7 = '7'
d 0x8 = '8'
d 0x9 = '9'
d 0xa = 'a'
d 0xb = 'b'
d 0xc = 'c'
d 0xd = 'd'
d 0xe = 'e'
d 0xf = 'f'