diff --git a/haskell/main/SBP2PRETTYJSON.hs b/haskell/main/SBP2PRETTYJSON.hs new file mode 100644 index 0000000000..716fee9e39 --- /dev/null +++ b/haskell/main/SBP2PRETTYJSON.hs @@ -0,0 +1,51 @@ +{-# LANGUAGE DeriveDataTypeable #-} +{-# OPTIONS_GHC -fno-cse #-} +{-# LANGUAGE NoImplicitPrelude #-} +{-# LANGUAGE OverloadedStrings #-} + +-- | +-- Module: SBP2PRETTYJSON +-- Copyright: Copyright (C) 2019 Swift Navigation, Inc. +-- License: LGPL-3 +-- Maintainer: Pasi Miettinen +-- Stability: experimental +-- Portability: portable +-- +-- SBP to JSON tool - reads SBP binary from stdin and sends SBP JSON +-- to stdout. + +import BasicPrelude hiding (map, print) +import Control.Monad.Trans.Resource +import Data.Aeson.Encode.Pretty +import Data.ByteString.Lazy hiding (ByteString, map) +import Data.Conduit +import Data.Conduit.Binary +import Data.Conduit.List +import Data.Conduit.Serialization.Binary +import SwiftNav.SBP +import System.Console.CmdArgs +import System.IO + +data Cfg = Cfg {skiporder :: Bool + ,spaces :: Int} + deriving (Show, Data, Typeable) + +defaultCfg :: Cfg +defaultCfg = Cfg{skiporder = False + ,spaces = 0} + +-- | Encode a SBPMsg to a line of JSON. +encodeLine :: Cfg -> SBPMsg -> ByteString +encodeLine c v = toStrict + $ encodePretty' + (defConfig {confIndent = Spaces (spaces c), + confCompare = if (skiporder c) then mempty else compare}) v <> "\n" + +main :: IO () +main = do + cfg <- cmdArgs defaultCfg + runResourceT $ + sourceHandle stdin + =$= conduitDecode + =$= map (encodeLine cfg) + $$ sinkHandle stdout diff --git a/haskell/sbp.cabal b/haskell/sbp.cabal index 511278f40a..c14fd23c1b 100644 --- a/haskell/sbp.cabal +++ b/haskell/sbp.cabal @@ -82,6 +82,22 @@ executable sbp2json , sbp default-language: Haskell2010 +executable sbp2prettyjson + hs-source-dirs: main + main-is: SBP2PRETTYJSON.hs + ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall + build-depends: aeson-pretty + , base + , basic-prelude + , binary-conduit + , bytestring + , cmdargs + , conduit + , conduit-extra + , resourcet + , sbp + default-language: Haskell2010 + executable json2sbp hs-source-dirs: main main-is: JSON2SBP.hs diff --git a/python/sbp/client/loggers/json_logger.py b/python/sbp/client/loggers/json_logger.py index 0df26d2886..001e6409f8 100644 --- a/python/sbp/client/loggers/json_logger.py +++ b/python/sbp/client/loggers/json_logger.py @@ -16,13 +16,17 @@ import warnings -class JSONLogger(BaseLogger): +class JSONLoggerBase(BaseLogger): """ - JSONLogger + JSONLoggerBase - The :class:`JSONLogger` logs JSON records. + The :class:`JSONLoggerBase` base class for JSON loggers. """ + def __init__(self, handle, tags={}, dispatcher=None, sort_keys=False): + BaseLogger.__init__(self, handle, tags, dispatcher) + self._sort_keys=sort_keys + def fmt_msg(self, data, **metadata): if metadata: metadata.update(self.tags) @@ -31,10 +35,26 @@ def fmt_msg(self, data, **metadata): return data + def __call__(self, msg, **metadata): + output = self.dump(msg, **metadata) + if output: + self.handle.write(output + "\n") + + +class JSONLogger(JSONLoggerBase): + """ + JSONLogger + + The :class:`JSONLogger` logs JSON records. + """ + def dump(self, msg, **metadata): try: data = self.dispatch(msg).to_json_dict() - return json.dumps(self.fmt_msg(data, **metadata), allow_nan=False) + return json.dumps(self.fmt_msg(data, **metadata), + allow_nan=False, + sort_keys=self._sort_keys, + separators=(',',':')) except (ValueError, UnicodeDecodeError): try: warn = "Bad values in JSON encoding for msg_type %d for msg %s" \ @@ -44,27 +64,14 @@ def dump(self, msg, **metadata): except (ValueError, UnicodeDecodeError): return None - def __call__(self, msg, **metadata): - output = self.dump(msg, **metadata) - if output: - self.handle.write(output + "\n") - -class JSONBinLogger(BaseLogger): +class JSONBinLogger(JSONLoggerBase): """ JSONBinLogger The :class:`JSONLogger` logs JSON records without expanding the fields. """ - def fmt_msg(self, data, **metadata): - if metadata: - metadata.update(self.tags) - metadata['data'] = data - return metadata - - return data - def dump(self, msg, **metadata): try: data = { @@ -75,7 +82,10 @@ def dump(self, msg, **metadata): 'payload': base64.standard_b64encode(msg.payload).decode('ascii'), 'crc': msg.crc } - return json.dumps(self.fmt_msg(data, **metadata), allow_nan=False) + return json.dumps(self.fmt_msg(data, **metadata), + allow_nan=False, + sort_keys=self._sort_keys, + separators=(',',':')) except (ValueError, UnicodeDecodeError): try: warn = "Bad values in JSON encoding for msg_type %d for msg %s" \ @@ -85,11 +95,6 @@ def dump(self, msg, **metadata): except (ValueError, UnicodeDecodeError): return None - def __call__(self, msg, **metadata): - output = self.dump(msg, **metadata) - if output: - self.handle.write(output + "\n") - class JSONLogIterator(LogIterator): """ diff --git a/python/sbp/msg.py b/python/sbp/msg.py index af2335df19..c44b5b535f 100755 --- a/python/sbp/msg.py +++ b/python/sbp/msg.py @@ -156,12 +156,12 @@ def to_binary(self): ret += struct.pack("