packages feed

ecu-0.0.6: src/CANDBParser.hs

module Main (main) where

import Data.Bits
import Data.Char
import Data.List
import Data.Word
import Text.Printf
import System.Environment

import CANData

main :: IO ()
main = do
  args <- getArgs
  case args of
    ["-h"] -> help
    ["--help"] -> help
    [ file ] -> do
      dbc <- readFile file
      let candb = mkCANDB dbc
      writeCANDB candb
    _ -> help

help :: IO ()
help = putStrLn $ unlines
  [ ""
  , "NAME"
  , "  parsedbc - create CANDB.hs from dbc file"
  , ""
  , "SYNOPSIS"
  , "  parsedbc FILE.dbc"
  , ""
  ]

writeCANDB :: CANDB -> IO ()
writeCANDB candb = writeFile "CANDB.hs" $ concat
  [ "{- Generated file. Do not modify -}\n"
  , "module CANDB\n"
  , "  ( canDB\n"
  , "  ) where\n"
  , "\n"
  , "import CANData\n"
  , "\n"
  , "canDB :: CANDB\n"
  , "canDB =  CANDB\n"
  , showCANDB candb
  , "\n"
  ]

mkCANDB :: String -> CANDB
mkCANDB s =  mkCANDB' nodes groups types attrs (CANDB name nodes [])
  where
  canLines = filter notBlank (lines s)
  name   = getName canLines
  groups = getGroups canLines
  nodes = getNodes canLines
  types = getTypes canLines
  attrs = getMsgAttrs canLines

mkCANDB' :: [String] -> [[String]] -> [(Word32,String,CANSignalType)] -> [(Word32,CANMsgAttr)] -> CANDB -> CANDB
mkCANDB' _ [] _ _ canDb = canDb
mkCANDB' nodes (group0:groups) types attrs canDb = mkCANDB' nodes groups types attrs (fillDB group0 types attrs canDb)

fillDB :: [String] -> [(Word32,String,CANSignalType)] -> [(Word32,CANMsgAttr)] -> CANDB -> CANDB
fillDB group0 types attrs canDB = getSignals types (tail group0) (getMessage (head group0) attrs canDB)

getMessage :: String -> [(Word32,CANMsgAttr)] -> CANDB -> CANDB
getMessage s attrs (CANDB dbname nodes msgs) = CANDB dbname nodes newMessages
  where
  newMessages = (msg:msgs)
  msg = CANMsg 
    { canMsgId      = id0
    , canMsgName    = init c
    , canMsgDlc     = read d
    , canMsgTxNode  = e
    , canMsgAttrs   = map (\ (_,a) -> a) (filter (\ (i,_) -> i == id0) attrs)
    , canMsgSignals = []
    }
  id0 = readId b
  (_:b:c:d:e:_) = words s

getSignals :: [(Word32,String,CANSignalType)] -> [String] -> CANDB -> CANDB
--getSignals _ [] (CANDB dbname nodes (msg:msgs)) = CANDB dbname nodes ((sortByStartBit msg):msgs)
getSignals _ [] canDb = canDb
getSignals types (l:ls) canDb = sortByStartBit (getSignal types l (getSignals types ls canDb))

sortByStartBit :: CANDB -> CANDB
sortByStartBit (CANDB dbname nodes []) = CANDB dbname nodes []
sortByStartBit (CANDB dbname nodes (msg:msgs)) = CANDB dbname nodes (newMsg:msgs)
  where
  newMsg = msg { canMsgSignals = sortBy comparingStartBit (canMsgSignals msg) }
  comparingStartBit :: CANSignal -> CANSignal -> Ordering
  comparingStartBit a b = compare (canSignalStartBit a) (canSignalStartBit b)

getSignal :: [(Word32,String,CANSignalType)] -> String -> CANDB -> CANDB
getSignal _ [] canDb = canDb
getSignal _ _ (CANDB _ _ []) = error "CANDBParser.getSignal"
getSignal types l (CANDB dbname nodes (msg:msgs)) = CANDB dbname nodes (newMsg:msgs) 
  where
  msgId = canMsgId msg
  newMsg = msg { canMsgSignals = newSignal:(canMsgSignals msg) }
  newSignal = CANSignal
    { canSignalName      = name
    , canSignalStartBit  = (read startBitStr)
    , canSignalBitLength = (read bitLengthStr)
    , canSignalEndian    = if bigEndianStr == '0' then CANBigEndian else CANLittleEndian
    , canSignalSign      = if signedStr == '-' then CANSigned else CANUnsigned
    , canSignalFactor    = (read (drop 1 (takeWhile (/= ',') e)))
    , canSignalOffset    = (read (takeWhile (/= ')') (drop 1 (dropWhile (/= ',') e))))
    , canSignalMin       = (read (drop 1 (takeWhile (/= '|') f)))
    , canSignalMax       = (read (takeWhile (/= ']') (drop 1 (dropWhile (/= '|') f))))
    , canSignalUnit      = unit
    , canSignalRxNodes   = rx
    , canSignalType      = matchTypes msgId name types
    , canSignalMux       = mux
    }
  startBitStr = takeWhile (/= '|') d
  bitLengthStr = drop 1 (dropWhile (/= '|') (takeWhile (/= '@') d))
  bigEndianStr = head (drop 1 (dropWhile (/= '@') d)) 
  signedStr    = last d
  (_:name:maybeMux) = takeWhile (/= ":") (words l)
  mux = case maybeMux of
    []             -> CANSignalMuxNone
    ["M"]          -> CANSignalMuxer
    [('m':muxVal)] -> CANSignalMuxed (read muxVal)
    _              -> error "CANDBParser.getSignal: error parsing \" SG_ ...\" line"
  (_:d:e:f:_) = dropWhile (/= ":") (words l)
  unitAndRx = dropWhile (/= '"') l
  unit = (takeWhile (/= '"') (drop 1 unitAndRx))
  rxlist = head (words (drop 1 (dropWhile (/= '"') (drop 1 unitAndRx))))
  rx = wordsBy ',' rxlist
  matchTypes :: Word32 -> String -> [(Word32,String,CANSignalType)] -> CANSignalType
  matchTypes _ _ [] = CANSignalTypeNormal
  matchTypes msgId0 sigName ((id0,name0,typ):types0) = 
    if (msgId0 == id0 && sigName == name0)
      then typ
      else matchTypes msgId0 sigName types0
  wordsBy :: Char -> [Char] -> [String]
  wordsBy sep s = case dropWhile (== sep) s of
    "" -> []
    s' -> w : wordsBy ',' s''
      where
      (w, s'') = break (== sep) s'

getName :: [String] -> String
getName [] = []
getName (l:ls) = 
  if isPrefixOf "BA_ \"DBName\" " l
    then (takeWhile (/= '"') (drop 1 c))
    else getName ls
  where
  (_:_:c:_) = words l

getNodes :: [String] -> [String]
getNodes (l:ls) = 
  if isPrefixOf "BU_: " l
    then (words (drop 5 l))
    else getNodes ls
getNodes [] = []

getTypes :: [String] -> [(Word32,String,CANSignalType)]
getTypes ls =  getTypes' ls []

getTypes' :: [String] -> [(Word32,String,CANSignalType)] -> [(Word32,String,CANSignalType)]
getTypes' [] types = types
getTypes' (l:ls) types = 
  if isPrefixOf "SIG_VALTYPE_ " l
    then getTypes' ls ((id0,name,typ):types)
    else getTypes' ls types
  where
  typ = if typChar == '1' then CANSignalTypeFloat else CANSignalTypeDouble
  typChar = head e
  id0 = read b
  name = c
  (_:b:c:_:e:_) =  words l

getMsgAttrs :: [String] -> [(Word32,CANMsgAttr)]
getMsgAttrs ls =  getMsgAttrs' ls []

getMsgAttrs' :: [String] -> [(Word32,CANMsgAttr)] -> [(Word32,CANMsgAttr)]
getMsgAttrs' [] attrs = attrs
getMsgAttrs' (l:ls) attrs = 
  if isPrefixOf "BA_ " l && length (words l) == 5
    then 
      if c == "BO_"
        then getMsgAttrs' ls ((id0,attr):attrs)
        else getMsgAttrs' ls attrs
    else getMsgAttrs' ls attrs
  where
  id0 = readId d
  attr = (tail (init b), init e)
  (_:b:c:d:e:_) = words l

getGroups :: [String] -> [[String]]
getGroups ls = groupMessages ls [[]]

groupMessages :: [String] -> [[String]] -> [[String]]
groupMessages [] [] = [[]]
groupMessages [] sofar = map reverse sofar
groupMessages (l:ls) ([]:[])  = case take 4 l of
  "BO_ " -> groupMessages ls [[l]]
  _      -> groupMessages ls [[]]
groupMessages (l:ls) ((s:ss):gs) = case (take 4 l) of
  "BO_ " -> groupMessages ls ([l]:(s:ss):gs)
  " SG_" -> groupMessages ls ((l:s:ss):gs)
  _      -> reverse (map reverse ((s:ss):gs))
groupMessages _ [] = error "CANDBParser.groupMessages"
groupMessages _ ([]:_) = error "CANDBParser.groupMessages"

notBlank :: String -> Bool
notBlank a = not (and (map isSpace a))

readId :: String -> Word32
readId str = (read str) .&. 0x7FFFFFFF

showCANDB :: CANDB -> String
showCANDB candb = concat
  [ "  { canDbName = " ++ show (canDbName candb) ++ "\n"
  , "  , canDbNodes =\n"
  , "      [" ++ (drop 7 (concatMap showNode (canDbNodes candb)))
  , "      ]\n"
  , "  , canDbMsgs =\n"
  , "      [" ++ (drop 7 (concatMap showMsg (canDbMsgs candb)))
  , "      ]\n"
  , "  }\n"
  ]

showNode :: String -> String
showNode x = "      , " ++ show x ++ "\n"

showMsg :: CANMsg -> String
showMsg msg = concat
  [ "      , CANMsg\n"
  , "          { canMsgId      = " ++ (printf "0x%08X" (canMsgId msg)) ++ "\n"
  , "          , canMsgName    = " ++ show (canMsgName msg) ++ "\n"
  , "          , canMsgDlc     = " ++ show (canMsgDlc msg) ++ "\n"
  , "          , canMsgTxNode  = " ++ show (canMsgTxNode msg) ++ "\n"
  , "          , canMsgAttrs   =\n"
  , "              [" ++ showMsgAttrs (canMsgAttrs msg)
  , "              ]\n"
  , "          , canMsgSignals =\n"
  , "              [" ++ showMsgSignals (canMsgSignals msg) ++ "\n"
  , "              ]\n"
  , "          }\n"
  ]

showMsgAttrs :: [CANMsgAttr] -> String
showMsgAttrs attrs = case attrs of
  [] -> ""
  x  -> drop 15 (concatMap showMsgAttr x)

showMsgAttr :: CANMsgAttr -> String
showMsgAttr (a , b)  = "              , (" ++ show a ++ ", " ++ show b ++ ")\n"

showMsgSignals :: [CANSignal] -> String
showMsgSignals signals = case signals of
  [] -> ""
  x  -> drop 15 (concatMap showMsgSignal x)

showMsgSignal :: CANSignal -> String
showMsgSignal signal = concat
  [ "              , CANSignal\n"
  , "                  { canSignalName      = " ++ show (canSignalName      signal) ++ "\n"
  , "                  , canSignalStartBit  = " ++ show (canSignalStartBit  signal) ++ "\n"
  , "                  , canSignalBitLength = " ++ show (canSignalBitLength signal) ++ "\n"
  , "                  , canSignalEndian    = " ++ show (canSignalEndian    signal) ++ "\n"
  , "                  , canSignalSign      = " ++ show (canSignalSign      signal) ++ "\n"
  , "                  , canSignalFactor    = " ++ show (canSignalFactor    signal) ++ "\n"
  , "                  , canSignalOffset    = " ++ show (canSignalOffset    signal) ++ "\n"
  , "                  , canSignalMin       = " ++ show (canSignalMin       signal) ++ "\n"
  , "                  , canSignalMax       = " ++ show (canSignalMax       signal) ++ "\n"
  , "                  , canSignalUnit      = " ++ show (canSignalUnit      signal) ++ "\n"
  , "                  , canSignalRxNodes   =\n"
  , "                      [" ++ showCANSignalRxNodes (canSignalRxNodes     signal)
  , "                      ]\n"
  , "                  , canSignalType      = " ++ show (canSignalType      signal) ++ "\n"
  , "                  , canSignalMux       = " ++ show (canSignalMux       signal) ++ "\n"
  , "                  }\n"
  ]

showCANSignalRxNodes :: [String] -> String
showCANSignalRxNodes nodes = case nodes of
  [] -> ""
  x  -> drop 23 (concatMap showCANSignalRxNode x)

showCANSignalRxNode :: String -> String
showCANSignalRxNode x  = "                      , " ++ show x ++ "\n"