ttn 0.1.0.0 → 0.2.0.0
raw patch · 2 files changed
+107/−70 lines, 2 files
Files
- src/Data/TTN/Types.hs +105/−69
- ttn.cabal +2/−1
src/Data/TTN/Types.hs view
@@ -1,29 +1,40 @@ {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleInstances #-}+{-# OPTIONS_GHC -Wno-orphans #-} module Data.TTN.Types where -import System.Exit (exitFailure, exitSuccess)-import System.IO (stderr, hPutStrLn)-import qualified Data.ByteString.Char8 as B-import System.Environment (getArgs)-import Control.Monad (forM_, mzero, join)-import Control.Applicative-import Data.Aeson(decodeStrict, eitherDecodeStrict,- Value(..), FromJSON(..), ToJSON(..),- pairs, withText, (.:), (.:?), (.=), object)-import Data.Monoid-import Data.Text (Text, unpack)+import Control.Monad (forM_, mzero)++import Data.Aeson ( decodeStrict+ , eitherDecodeStrict+ , Value(..)+ , FromJSON(..)+ , ToJSON(..)+ , pairs+ , withText+ , (.:)+ , (.:?)+ , (.=)+ , object)++import Data.ByteString.Char8 (ByteString)+import Data.Text (Text)+ import GHC.Generics import Data.Time.RFC3339 import Data.Time.LocalTime --- | Workaround for https://github.com/bos/aeson/issues/287.-o .:?? val = fmap join (o .:? val)+import qualified Data.ByteString.Char8+import qualified Data.Text+import qualified System.Exit+import qualified System.IO -data TTNZonedTime = TTNZonedTime { unwrap :: ZonedTime } deriving (Show, Generic)+data TTNZonedTime = TTNZonedTime { unwrap :: ZonedTime }+ deriving (Eq, Show, Generic) instance ToJSON TTNZonedTime where toJSON = toJSON . (formatTimeRFC3339 :: ZonedTime -> Text) . unwrap@@ -35,25 +46,27 @@ Nothing -> fail $ "Parsing RFC3339 value failed, got:" ++ show x Just t -> return $ TTNZonedTime t +instance Eq ZonedTime where+ ZonedTime t1 tz1 == ZonedTime t2 tz2 = t1 == t2 && tz1 == tz2 -- https://github.com/TheThingsNetwork/ttn/blob/develop/core/types/event.go#L65 data Config = Config { configFrequency :: Maybe Double , configDataRate :: Maybe Text- , configCounter :: Maybe Double+ , configCounter :: Maybe Integer , configAirtime :: Maybe Double , configPower :: Maybe Double , configModulation :: Maybe Text- } deriving (Show, Generic)+ } deriving (Eq, Show, Generic) instance FromJSON Config where parseJSON (Object v) = Config- <$> v .:?? "frequency"- <*> v .:?? "data_rate"- <*> v .:?? "counter"- <*> v .:?? "airtime"- <*> v .:?? "power"- <*> v .:?? "modulation"+ <$> v .:? "frequency"+ <*> v .:? "data_rate"+ <*> v .:? "counter"+ <*> v .:? "airtime"+ <*> v .:? "power"+ <*> v .:? "modulation" parseJSON _ = mzero instance ToJSON Config where@@ -80,7 +93,7 @@ data GatewaysElt = GatewaysElt { gatewaysEltGtwId :: Maybe Text , gatewaysEltGtwTrusted :: Maybe Bool- , gatewaysEltTimestamp :: Maybe Int+ , gatewaysEltTimestamp :: Maybe Integer , gatewaysEltFineTimestamp :: Maybe Int , gatewaysEltFineTimestampEncrypted :: Maybe Text , gatewaysEltTime :: Maybe TTNZonedTime@@ -94,26 +107,26 @@ , gatewaysEltAltitude :: Maybe Int , gatewaysEltAccuracy :: Maybe Int , gatewaysEltSource :: Maybe Text- } deriving (Show, Generic)+ } deriving (Eq, Show, Generic) instance FromJSON GatewaysElt where parseJSON (Object v) = GatewaysElt- <$> v .:?? "gtw_id"- <*> v .:?? "gtw_trusted"- <*> v .:?? "timestamp"- <*> v .:?? "fine_timestamp"- <*> v .:?? "fine_timestamp_encrypted"- <*> v .:?? "time"- <*> v .:?? "antenna"+ <$> v .:? "gtw_id"+ <*> v .:? "gtw_trusted"+ <*> v .:? "timestamp"+ <*> v .:? "fine_timestamp"+ <*> v .:? "fine_timestamp_encrypted"+ <*> v .:? "time"+ <*> v .:? "antenna" <*> v .: "channel" <*> v .: "rssi" <*> v .: "snr" <*> v .: "rf_chain"- <*> v .:?? "latitude"- <*> v .:?? "longitude"- <*> v .:?? "altitude"- <*> v .:?? "accuracy"- <*> v .:?? "source"+ <*> v .:? "latitude"+ <*> v .:? "longitude"+ <*> v .:? "altitude"+ <*> v .:? "accuracy"+ <*> v .:? "source" parseJSON _ = mzero instance ToJSON GatewaysElt where@@ -165,17 +178,17 @@ , metadataAirtime :: Maybe Double , metadataCodingRate :: Maybe Text , metadataGateways :: [GatewaysElt]- } deriving (Show, Generic)+ } deriving (Eq, Show, Generic) instance FromJSON Metadata where parseJSON (Object v) = Metadata <$>- v .:?? "time"- <*> v .:?? "frequency"- <*> v .:?? "modulation"- <*> v .:?? "data_rate"- <*> v .:?? "bit_rate"- <*> v .:?? "airtime"- <*> v .:?? "coding_rate"+ v .:? "time"+ <*> v .:? "frequency"+ <*> v .:? "modulation"+ <*> v .:? "data_rate"+ <*> v .:? "bit_rate"+ <*> v .:? "airtime"+ <*> v .:? "coding_rate" <*> v .: "gateways" parseJSON _ = mzero @@ -206,7 +219,7 @@ messageDevId :: Text, messageAppId :: Text, messagePort :: Double- } deriving (Show, Generic)+ } deriving (Eq, Show, Generic) instance FromJSON Message where parseJSON (Object v) = Message@@ -234,7 +247,7 @@ , uplinkGatewayId :: Maybe Text , uplinkDevId :: Maybe Text , uplinkPayload :: Maybe Text- , uplinkCounter :: Maybe Double+ , uplinkCounter :: Maybe Integer , uplinkIsRetry :: Maybe Bool , uplinkMetadata :: Maybe Metadata , uplinkPayloadRaw :: Maybe Text@@ -243,23 +256,23 @@ , uplinkConfirmed :: Maybe Bool , uplinkHardwareSerial :: Maybe Text , uplinkPort :: Maybe Double- } deriving (Show, Generic)+ } deriving (Eq, Show, Generic) instance FromJSON Uplink where parseJSON (Object v) = Uplink- <$> v .:?? "config"- <*> v .:?? "gateway_id"- <*> v .:?? "dev_id"- <*> v .:?? "payload"- <*> v .:?? "counter"- <*> v .:?? "is_retry"- <*> v .:?? "metadata"- <*> v .:?? "payload_raw"- <*> v .:?? "message"- <*> v .:?? "app_id"- <*> v .:?? "confirmed"- <*> v .:?? "hardware_serial"- <*> v .:?? "port"+ <$> v .:? "config"+ <*> v .:? "gateway_id"+ <*> v .:? "dev_id"+ <*> v .:? "payload"+ <*> v .:? "counter"+ <*> v .:? "is_retry"+ <*> v .:? "metadata"+ <*> v .:? "payload_raw"+ <*> v .:? "message"+ <*> v .:? "app_id"+ <*> v .:? "confirmed"+ <*> v .:? "hardware_serial"+ <*> v .:? "port" parseJSON _ = mzero instance ToJSON Uplink where@@ -303,7 +316,7 @@ "replace" -> return ScheduleReplace "first" -> return ScheduleFirst "last" -> return ScheduleLast- w -> fail $ "Unknown schedule" ++ (unpack w)+ w -> fail $ "Unknown schedule" ++ (Data.Text.unpack w) instance ToJSON Schedule where toJSON ScheduleReplace = "replace"@@ -327,7 +340,7 @@ <*> v .: "port" <*> v .: "payload_raw" <*> v .: "confirmed"- <*> v .:?? "schedule"+ <*> v .:? "schedule" parseJSON _ = mzero instance ToJSON Downlink where@@ -359,26 +372,49 @@ toJSON (Error {..}) = object [ "error" .= errorMsg ] toEncoding (Error {..}) = pairs ( "error" .= errorMsg ) -parse :: B.ByteString -> Either String Uplink+-- used by ttn-client+data EventType =+ Up+ | Down+ | DownAcked+ | DownSent+ | DownScheduled+ | Activation+ | Create+ | Update+ | Delete+ | Unknown+ deriving (Eq, Ord, Show, Generic)++data Event =+ Event EventType Uplink+ | ClientError String+ deriving (Eq, Show, Generic)++instance FromJSON EventType where+instance ToJSON EventType where+instance FromJSON Event where+instance ToJSON Event where++parse :: ByteString -> Either String Uplink parse = eitherDecodeStrict -parseError :: B.ByteString -> Either String Error+parseError :: ByteString -> Either String Error parseError = eitherDecodeStrict parseFile :: FilePath -> IO Uplink parseFile filename = do- input <- B.readFile filename+ input <- Data.ByteString.Char8.readFile filename case decodeStrict input of Nothing -> fatal $ case (decodeStrict input :: Maybe Value) of Nothing -> "Invalid JSON file: " ++ filename- Just v -> "Mismatched JSON value from file: " ++ filename+ Just _v -> "Mismatched JSON value from file: " ++ filename Just r -> return (r :: Uplink) where fatal :: String -> IO a- fatal msg = do hPutStrLn stderr msg- exitFailure+ fatal msg = do System.IO.hPutStrLn System.IO.stderr msg+ System.Exit.exitFailure parseMany :: [String] -> IO () parseMany filenames = do forM_ filenames (\f -> parseFile f >>= (\p -> p `seq` putStrLn $ "Successfully parsed " ++ f))- exitSuccess
ttn.cabal view
@@ -1,5 +1,5 @@ name: ttn-version: 0.1.0.0+version: 0.2.0.0 synopsis: Things Tracker Network JSON Types description: Parse JSONs from TTN MQTT API homepage: https://github.com/sorki/data-ttn@@ -14,6 +14,7 @@ cabal-version: >=1.10 library+ ghc-options: -Wall hs-source-dirs: src exposed-modules: Data.TTN , Data.TTN.Types