module Text.Kcd.Parser
(
parseKcdFile
, parseNetworkDefinition
, BasicLabelTypeValue (..)
, Bus (..)
, Consumer (..)
, Document (..)
, Endianess (..)
, Label (..)
, LabelGroup (..)
, LabelSet (..)
, Message (..)
, MessageId (..)
, MessageLength (..)
, Multiplex (..)
, MuxGroup (..)
, NetworkDefinition (..)
, Node (..)
, NodeRef (..)
, Notes (..)
, Producer (..)
, Signal (..)
, Value (..)
, ValueType (..)
, Var (..)
)
where
import Control.Applicative ((<|>))
import Control.Lens.TH (makeLenses)
import Control.Monad (join)
import Control.Monad.Catch (MonadCatch, MonadThrow)
import Control.Monad.Trans.Resource (runResourceT)
import Data.Conduit (($$), ConduitM)
import Data.Maybe (fromMaybe)
import Data.Monoid ((<>))
import Data.Text (Text, pack, unpack)
import Data.Text.Read (Reader, hexadecimal)
import Data.XML.Types (Event, Name(..))
import Text.XML.Stream.Parse
data NetworkDefinition = NetworkDefinition
{ _networkDefinitionDocument :: Document
, _networkDefinitionNodes :: [Node]
, _networkDefinitionBuses :: [Bus]
} deriving (Show, Eq)
data Document = Document
{
_documentName :: Maybe Text
, _documentVersion :: Maybe Text
, _documentAuthor :: Maybe Text
, _documentCompany :: Maybe Text
, _documentDate :: Maybe Text
, _documentContent :: Text
} deriving (Show, Eq)
data Bus = Bus
{ _busMessages :: [Message]
, _busName :: Text
, _busBaudrate :: Int
} deriving (Show, Eq)
data Node = Node
{
_nodeId :: Text
, _nodeName :: Maybe Text
, _nodeVars :: [Var]
} deriving (Show, Eq)
data Var = Var
{ _varValue :: Value
, _varNotes :: Maybe Notes
, _varName :: Text
} deriving (Show, Eq)
newtype MessageId = MessageId { _unMessageId :: Int } deriving (Eq, Show, Ord)
data MessageLength =
Auto
| LengthValue Int deriving (Show, Eq)
data Message = Message
{ _messageNotes :: Maybe Notes
, _messageProducer :: Maybe Producer
, _messageMultiplex :: Maybe Multiplex
, _messageSignals :: [Signal]
, _messageId :: MessageId
, _messageName :: Text
, _messageLength :: MessageLength
, _messageInterval :: Int
, _messageTriggered :: Bool
, _messageFormat :: Text
, _messageRemote :: Bool
} deriving (Show, Eq)
data Multiplex = Multiplex
{ _multiplexMuxGroup :: [MuxGroup]
, _multiplexNotes :: Maybe Notes
, _multiplexConsumer :: Maybe Consumer
, _multiplexValue :: Maybe Value
, _multiplexLabels :: [Label]
, _multiplexEndianess :: Endianess
, _multiplexLength :: Int
, _multiplexName :: Text
, _multiplexOffset :: Int
} deriving (Show, Eq)
data MuxGroup = MuxGroup
{ _muxGroupSignals :: [Signal]
, _muxGroupCount :: Int
} deriving (Show, Eq)
data Signal = Signal
{
_signalNotes :: Maybe Notes
, _signalConsumer :: Maybe Consumer
, _signalValue :: Maybe Value
, _signalLabelSet :: [LabelSet]
, _signalEndianess :: Endianess
, _signalLength :: Int
, _signalName :: Text
, _signalOffset :: Int
} deriving (Show, Eq)
data ValueType =
ValueTypeUnsigned
| ValueTypeSigned
| ValueTypeSingle
| ValueTypeDouble
deriving (Show, Eq)
data Value = Value
{
_valueMin :: Double
, _valueMax :: Double
, _valueSlope :: Double
, _valueIntercept :: Double
, _valueUnit :: Text
, _valueType :: ValueType
} deriving (Show, Eq)
data Consumer = Consumer
{ _consumerNodeRef :: [NodeRef]
} deriving (Show, Eq)
data Producer = Producer
{ _producerNodeRef :: [NodeRef]
} deriving (Show, Eq)
data NodeRef = NodeRef
{
_nodeRefId :: Text
} deriving (Show, Eq)
data Label = Label
{
_labelValue :: Int
, _labelName :: Text
, _labelType :: BasicLabelTypeValue
} deriving (Show, Eq)
data LabelGroup = LabelGroup
{
_labelGroupFrom :: Text
, _labelGroupTo :: Text
, _labelGroupName :: Text
, _labelGroupType :: BasicLabelTypeValue
} deriving (Show, Eq)
data LabelSet = LabelSet
{ _labelSetLabel :: [Label]
, _labelSetLabelGroup :: [LabelGroup]
} deriving (Show, Eq)
data BasicLabelTypeValue =
TypeValue
| TypeInvalid
| TypeError
deriving (Show, Eq)
data Endianess =
LittleEndian
| BigEndian
deriving (Show, Eq)
newtype Notes = Notes Text deriving (Show, Eq);
parseSettings :: ParseSettings
parseSettings = def { psDecodeEntities = decodeHtmlEntities
, psRetainNamespaces = False }
parseKcdFile :: String -> IO NetworkDefinition
parseKcdFile f = runResourceT $
parseFile parseSettings f $$
force "Missing NetworkDefinition" parseNetworkDefinition
parseNetworkDefinition :: MonadThrow m => ConduitM Event o m (Maybe NetworkDefinition)
parseNetworkDefinition = tagIgnoreAttrs (ns "NetworkDefinition") $ do
document <- force "Missing document" parseDocument
nodes <- many parseNode
buses <- many parseBus
return $ NetworkDefinition document nodes buses
parseDocument :: MonadThrow m => ConduitM Event o m (Maybe Document)
parseDocument = tagName (ns "Document") attrs $ \(name, version, author, company, date) -> do
docContent <- content
return $ Document name version author company date docContent
where attrs = do
name <- attr "name"
version <- attr "version"
author <- attr "author"
company <- attr "company"
date <- attr "date"
return $ (,,,,) name version author company date
parseVar :: MonadThrow m => ConduitM Event o m (Maybe Var)
parseVar = tagName (ns "Var") (requireAttr "name") $ \name -> do
notes <- parseNotes
value <- force "Missing Value" parseValue
return $ Var value notes name
parseBus :: MonadThrow m => ConduitM Event o m (Maybe Bus)
parseBus = tagName (ns "Bus") attrs $ \(name, baudrate)-> do
messages <- many parseMessage
return $ Bus messages name baudrate
where attrs = do
name <- requireAttr "name"
baudrate <- fromMaybe 500000 <$> attrRead "baudrate"
return (name, baudrate)
parseMessageLength :: Text -> MessageLength
parseMessageLength "auto" = Auto
parseMessageLength i = LengthValue $ read $ unpack i
parseMessage :: MonadThrow m => ConduitM Event o m (Maybe Message)
parseMessage = tagName (ns "Message") attrs $ \(id, name, length, interval, triggered, count, format, remote) -> do
notes <- parseNotes
producer <- parseProducer
multiplex <- parseMultiplex
signals <- many parseSignal
return $ Message notes producer multiplex signals id name length interval triggered format remote
where attrs = do
id <- (MessageId . readHexNumber) <$> requireAttr "id"
name <- requireAttr "name"
length <- maybe Auto parseMessageLength <$> attr "length"
interval <- fromMaybe 0 <$> attrRead "interval"
triggered <- fromMaybe False <$> attrRead "triggered"
count <- fromMaybe 0 <$> attrRead "count"
format <- fromMaybe "standard" <$> attr "format"
remote <- fromMaybe False <$> attrRead "remote"
return (id, name, length, interval, triggered, count, format, remote)
readHexNumber :: Text -> Int
readHexNumber s = let (Right (n, _)) = hexadecimal s in n
basicSignalAttrs :: AttrParser (Endianess, Int, Text, Int)
basicSignalAttrs = do
endianess <- (fromMaybe LittleEndian . join . fmap parseEndianess) <$> attr "endianess"
length <- fromMaybe 1 <$> attrRead "length"
name <- requireAttr "name"
offset <- (read . unpack) <$> requireAttr "offset"
return (endianess, length, name, offset)
parseMultiplex :: MonadThrow m => ConduitM Event o m (Maybe Multiplex)
parseMultiplex = tagName (ns "Multiplex") basicSignalAttrs $ \(endianess, length, name, offset) -> do
muxGroup <- many parseMuxGroup
notes <- parseNotes
consumer <- parseConsumer
value <- parseValue
labels <- many parseLabel
return $ Multiplex muxGroup notes consumer value labels endianess length name offset
parseMuxGroup :: MonadThrow m => ConduitM Event o m (Maybe MuxGroup)
parseMuxGroup = tagName (ns "MuxGroup") countAttr $ \count -> do
signals <- many parseSignal
return $ MuxGroup signals count
where countAttr = (read . unpack) <$> requireAttr "count"
parseSignal :: MonadThrow m => ConduitM Event o m (Maybe Signal)
parseSignal = tagName (ns "Signal") basicSignalAttrs $ \(endianess, length, name, offset) -> do
notes <- parseNotes
consumer <- parseConsumer
value <- parseValue
labelSet <- many parseLabelSet
return $ Signal notes consumer value labelSet endianess length name offset
parseValueType :: Text -> Maybe ValueType
parseValueType "unsigned" = Just ValueTypeUnsigned
parseValueType "signed" = Just ValueTypeSigned
parseValueType "single" = Just ValueTypeSingle
parseValueType "double" = Just ValueTypeDouble
parseValueType _ = Nothing
parseValue :: MonadThrow m => ConduitM Event o m (Maybe Value)
parseValue = tagName (ns "Value") attrs $ \value -> return value
where attrs = do
min <- fromMaybe 0.0 <$> attrRead "min"
max <- fromMaybe 0.0 <$> attrRead "max"
slope <- fromMaybe 0.0 <$> attrRead "slope"
intercept <- fromMaybe 0.0 <$> attrRead "intercept"
unit <- fromMaybe "1" <$> attr "unit"
type' <- (fromMaybe ValueTypeUnsigned . join . fmap parseValueType) <$> attr "type"
return $ Value min max slope intercept unit type'
parseConsumer :: MonadThrow m => ConduitM Event o m (Maybe Consumer)
parseConsumer = tagNoAttr (ns "Consumer") $ do
nodeRefs <- many parseNodeRef
return $ Consumer nodeRefs
parseProducer :: MonadThrow m => ConduitM Event o m (Maybe Producer)
parseProducer = tagNoAttr (ns "Producer") $ do
nodeRefs <- many parseNodeRef
return $ Producer nodeRefs
parseNode :: MonadThrow m => ConduitM Event o m (Maybe Node)
parseNode = tagName (ns "Node") attrs $ \(id, name) -> do
vars <- many parseVar
return $ Node id name vars
where attrs = do
id <- requireAttr "id"
name <- attr "name"
return (id, name)
parseNodeRef :: MonadThrow m => ConduitM Event o m (Maybe NodeRef)
parseNodeRef = tagName (ns "NodeRef") (requireAttr "id") $ \id -> return $ NodeRef id
parseLabel :: MonadThrow m => ConduitM Event o m (Maybe Label)
parseLabel = tagName (ns "Label") attrs $ \(value, name, type') ->
return $ Label value name type'
where attrs = do
value <- (read . unpack) <$> requireAttr "value"
name <- requireAttr "name"
type' <- (fromMaybe TypeValue . join) . fmap parseBasicLabelTypeValue <$> attr "type"
return (value, name, type')
parseLabelGroup :: MonadThrow m => ConduitM Event o m (Maybe LabelGroup)
parseLabelGroup = tagName (ns "LabelGroup") attrs $ \(from, to, name, type') -> return $ LabelGroup from to name type'
where attrs = do
from <- requireAttr "from"
to <- requireAttr "to"
name <- requireAttr "name"
type' <- (fromMaybe TypeValue . join . fmap parseBasicLabelTypeValue) <$> attr "type"
return (from, to, name, type')
parseNotes :: MonadThrow m => ConduitM Event o m (Maybe Notes)
parseNotes = tagNoAttr (ns "Notes") $ do
note <- content
return $ Notes note
parseBasicLabelTypeValue :: Text -> Maybe BasicLabelTypeValue
parseBasicLabelTypeValue "value" = Just TypeValue
parseBasicLabelTypeValue "invalid" = Just TypeInvalid
parseBasicLabelTypeValue "error" = Just TypeError
parseBasicLabelTypeValue _ = Nothing
parseEndianess :: Text -> Maybe Endianess
parseEndianess "little" = Just LittleEndian
parseEndianess "big" = Just BigEndian
parseEndianess _ = Nothing
parseLabelSet :: MonadThrow m => ConduitM Event o m (Maybe LabelSet)
parseLabelSet = tagNoAttr (ns "LabelSet") $ do
labels <- many parseLabel
labelGroups <- many parseLabelGroup
return $ LabelSet labels labelGroups
attrRead :: Read a => Name -> AttrParser (Maybe a)
attrRead name = fmap (read . unpack) <$> attr name
ns :: Text -> Name
ns n = let ns' = Just "http://kayak.2codeornot2code.org/1.0"
pfx = Nothing
in Name n ns' pfx
makeLenses ''Bus
makeLenses ''Consumer
makeLenses ''Document
makeLenses ''Label
makeLenses ''LabelGroup
makeLenses ''LabelSet
makeLenses ''Message
makeLenses ''Multiplex
makeLenses ''MuxGroup
makeLenses ''NetworkDefinition
makeLenses ''Node
makeLenses ''NodeRef
makeLenses ''Producer
makeLenses ''Signal
makeLenses ''Value
makeLenses ''Var