stdf 0.1.0.0 → 0.2.0.0
raw patch · 7 files changed
+1113/−91 lines, 7 filesdep +containersdep +data-binary-ieee754dep +timedep ~stdf
Dependencies added: containers, data-binary-ieee754, time, unix-time, zlib
Dependency ranges changed: stdf
Files
- Data/Stdf.hs +614/−67
- Data/Stdf/Types.hs +456/−0
- Data/Stdf/WaferMap.hs +7/−6
- Examples/StdfToJson.hs +5/−2
- Examples/WaferMap.hs +2/−2
- Readme.md +21/−6
- stdf.cabal +8/−8
Data/Stdf.hs view
@@ -1,29 +1,43 @@ {-# LANGUAGE NegativeLiterals #-}-{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoImplicitPrelude #-} module Data.Stdf ( parse+ , parseFile , Stdf(..) , Rec(..) ) where -import Data.Binary.Get-import Data.ByteString.Lazy.Char8 as BL hiding (show)-import Data.Word-import Data.Int-import Data.Bits (testBit)-import Foreign.C.Types+import Data.Stdf.Types+import Data.Binary.Get hiding (Fail)+import Data.ByteString.Lazy.Char8 as BL hiding (show, elem, notElem, all, concatMap, concat, zipWith, map, head)+import Data.Bits (testBit, (.&.), shiftR) import Control.Applicative-import Prelude hiding (show)+import Prelude hiding (show, Left, Right)+import qualified Prelude import Text.Show import Control.Monad-import Data.Text.Lazy import qualified Data.ByteString.Base64.Lazy as Base64 import Data.Text.Lazy.Encoding+import Data.Text.Lazy hiding (all, concatMap, concat, zipWith, map, head)+import GHC.Char+import Data.Sequence (replicateA)+import Data.Ix (range)+import Data.Binary.IEEE754+import Codec.Compression.GZip+import Control.Exception+import Data.UnixTime+-- import Data.Time.Compat+import Data.Time.Clock+import Data.Time.Clock.POSIX+import Data.Maybe -import GHC.Generics hiding (U1)-import Data.Aeson+import Debug.Trace +-- TODO: Travis+-- TODO: JSON/XML data model for STDF+ -- JSON gotcha: can't encode ByteString -- Have to convert character strings to Text-latin1 -- Encoding base64 raw binary data@@ -34,81 +48,206 @@ -- and I was like fuck that. All my testers and all my computers are x86 -- anyway. -type U1 = Word8 -- unsigned 1 byte-type U2 = Word16 -- unsigned 2 bytes-type U4 = Word32 -- unsigned 4 bytes-type I1 = Int8-type I2 = Int16-type I4 = Int32-type R4 = CFloat-type R8 = CDouble -instance ToJSON Rec-instance ToJSON PartFlg+getFar :: Get Rec+getFar = Far <$> getWord8 <*> getWord8 -data BinRec = BinRec - { header :: Header- , rec :: Rec } deriving (Generic, Show)+getAtr :: Get Rec+getAtr = Atr <$> getTime <*> mcn -data Header = Header- { len :: !Word16- , typ :: !Word8- , sub :: !Word8- } deriving (Generic, Show)+getTime :: Get (Maybe UTCTime)+getTime = do+ secs <- u4+ return $ if secs == 0 then Nothing else Just $ posixSecondsToUTCTime $ realToFrac secs -type Stdf = [Rec]+getMilliseconds :: Get (Maybe Milliseconds)+getMilliseconds = do+ milliseconds <- mu4+ return $ Milliseconds <$> milliseconds -data Rec = Raw { raw :: Text } -- base64 TODO: maybe don't bother. what's this good for?- | Far { cpuType :: !U1- , stdfVer :: !U1 }- | Prr { headNum :: !U1- , siteNum :: !U1- , partFlg :: !PartFlg- , numTest :: !U2- , hardBin :: !U2- , softBin :: Maybe U2- , xCoord :: Maybe I2- , yCoord :: Maybe I2- , testTime :: Maybe U4- , partID :: Maybe Text- , partTxt :: Maybe Text- , partFix :: Maybe Text }- deriving (Generic, Show)+getMinutes :: Get (Maybe Minutes)+getMinutes = do+ mins <- u2+ return $ if mins == 65535 then Nothing else Just $ Minutes mins -data PartFlg = PartFlg { supersedesPartId :: Bool -- 1 bit- , supersedesXY :: Bool -- 1 bit- , abnormalEnd :: Bool- , failed :: Bool- , noPassFailInfo :: Bool }- deriving (Generic, Show)+getMir :: Get Rec+getMir = Mir <$> getTime <*> getTime <*> u1 <*> mc1<*> mc1 <*> mc1 <*> getMinutes+ <*> mc1 <*> cn <*> cn <*> cn <*> cn <*> cn <*> mcn + <*> mcn <*> mcn <*> mcn <*> mcn <*> mcn <*> mcn + <*> mcn <*> mcn <*> mcn <*> mcn <*> mcn <*> mcn + <*> mcn <*> mcn <*> mcn <*> mcn <*> mcn <*> mcn+ <*> mcn <*> mcn <*> mcn <*> mcn <*> mcn <*> mcn -getFar :: Get Rec-getFar = Far <$> getWord8 <*> getWord8+getMrr :: Get Rec+getMrr = Mrr <$> getTime <*> mc1 <*> mcn <*> mcn +getPcr :: Get Rec+getPcr = Pcr <$> u1 <*> u1 <*> u4 <*> mu4 <*> mu4 <*> mu4 <*> mu4 ++getHbr :: Get Rec+getHbr = Hbr <$> u1 <*> u1 <*> u2 <*> u4 <*> getPassFailBin <*> mcn++getSbr :: Get Rec+getSbr = Sbr <$> u1 <*> u1 <*> u2 <*> u4 <*> getPassFailBin <*> mcn++getPmr :: Get Rec+getPmr = Pmr <$> u2 <*> mu2e0 <*> mcn <*> mcn <*> mcn <*> u1 <*> u1++getPgr :: Get Rec+getPgr = Pgr <$> u2 <*> mcn <*> getU2List++getPlr :: Get Rec+getPlr = do+ k <- fromIntegral <$> u2+ indecies <- replicateM k u2+ grpMode <- replicateM k u2+ let groupModes = map toGroupMode grpMode+ grpRdx <- replicateM k u1+ let radixes = map toRadix grpRdx+ pgmChaR <- replicateM k mcn+ rtnChaR <- replicateM k mcn+ pgmChaL <- replicateM k mcn+ rtnChaL <- replicateM k mcn++ -- let pgmChars = zipWith (++) pgmChaL pgmChaR+ -- let rtnChars = zipWith (++) rtnChaL rtnChaR++ return $ Plr indecies groupModes radixes pgmChaR rtnChaR pgmChaL rtnChaL++toGroupMode :: U2 -> GroupMode+toGroupMode 00 = UnknownGroupMode+toGroupMode 10 = Normal+toGroupMode 20 = SameCycleIO+toGroupMode 21 = SameCycleMidband+toGroupMode 22 = SameCycleValid+toGroupMode 23 = SameCycleWindowSustain+toGroupMode 30 = DualDrive+toGroupMode 31 = DualDriveMidband+toGroupMode 32 = DualDriveValid+toGroupMode 33 = DualDriveWindowSustain+toGroupMode x = OtherGroupMode x++toRadix :: U1 -> Radix+toRadix 0 = DefaultRadix+toRadix 2 = Binary+toRadix 8 = Octal+toRadix 10 = Decimal+toRadix 16 = Hexadecimal+toRadix 20 = Symbolic+toRadix x = OtherRadix x++getRdr :: Get Rec+getRdr = Rdr <$> getU2List++getSdr :: Get Rec+getSdr = Sdr <$> u1 <*> u1 <*> getU1List <*> mcn <*> mcn + <*> mcn <*> mcn <*> mcn <*> mcn <*> mcn <*> mcn <*> mcn+ <*> mcn <*> mcn <*> mcn <*> mcn <*> mcn <*> mcn <*> mcn+-- getSdr = Sdr <$> u1 <*> u1 <*> u1 <*> getU1List <*> replicateA 16 mcn++getWir :: Get Rec+getWir = Wir <$> u1 <*> u1 <*> getTime <*> mcn++getWrr :: Get Rec+getWrr = Wrr <$> u1 <*> u1 <*> getTime <*> u4 <*> mu4 <*> mu4 <*> mu4+ <*> mu4 <*> mcn <*> mcn <*> mcn <*> mcn <*> mcn <*> mcn++getWcr :: Get Rec+getWcr = Wcr <$> mr4 <*> mr4 <*> mr4 <*> getWaferUnits <*> getDirection + <*> mi2 <*> mi2 <*> getDirection <*> getDirection++getPassFailBin :: Get PassFailBin+getPassFailBin = do+ c <- c1+ return $ case c of+ 'P' -> PassBin+ 'F' -> FailBin+ ' ' -> UnknownBin+ _ -> OtherBin c++getDirection :: Get (Maybe Direction)+getDirection = charToDirection <$> c1+ where charToDirection 'U' = Just Up+ charToDirection 'D' = Just Down+ charToDirection 'L' = Just Left+ charToDirection 'R' = Just Right+ charToDirection ' ' = Nothing+ charToDirection x = Just $ OtherDirection x++getWaferUnits :: Get (Maybe WaferUnits)+getWaferUnits = numToUnits <$> u1+ where numToUnits 0 = Nothing+ numToUnits 1 = Just Inches+ numToUnits 2 = Just Centimeters+ numToUnits 3 = Just Millimeters+ numToUnits 4 = Just Mils+ numToUnits x = Just $ OtherUnits x++-- TODO: there's a pattern here+getU1List :: Get [U1]+getU1List = do+ cnt <- u1+ let count = fromIntegral cnt+ replicateM count u1++getU2List :: Get [U2]+getU2List = do+ cnt <- u2+ let count = fromIntegral cnt+ replicateM count u2+ u1 = getWord8 u2 = getWord16le u4 = getWord32le-cn = getVarLen +r4 :: Get R4+r4 = getFloat32le++r8 :: Get R8+r8 = getFloat64le+ i1 :: Get I1 i1 = fromIntegral <$> u1 i2 :: Get I2 i2 = fromIntegral <$> u2 -i4 :: Get I1-i4 = fromIntegral <$> u1+i4 :: Get I4+i4 = fromIntegral <$> u4 -mu2 :: Get (Maybe U2)+mu2, mu2e0 :: Get (Maybe U2) mu2 = missing (65535 :: U2) <$> u2+mu2e0 = missing (0 :: U2) <$> u2 +mu1e0 :: Get (Maybe U1)+mu1e0 = missing (0 :: U1) <$> u1+mu1e255 = missing (255 :: U1) <$> u1+ missing :: Eq a => a -> a -> Maybe a missing n m = if m == n then Nothing else Just m +c1 :: Get Char+c1 = (chr . fromIntegral) <$> u1 +mc1 :: Get (Maybe C1)+mc1 = getMaybe c1 ' '++mcn :: Get (Maybe Text)+mcn = getMaybe cn ""++mr4 :: Get (Maybe R4)+mr4 = getMaybe r4 0.0++getMaybe :: Eq a => Get a -> a -> Get (Maybe a)+getMaybe get missingVal = do+ x <- get+ return $ if x == missingVal+ then Nothing+ else Just x+ mi2 :: Get (Maybe I2) mi2 = do x <- i2@@ -120,31 +259,356 @@ mu4 = do x <- u4 return $ case x of- 0 -> Nothing+ 4294967295 -> Nothing otherwise -> Just x -getVarLen :: Get (Maybe Text)-getVarLen = do+cn :: Get Text+cn = do len <- u1 case len of- 0 -> return Nothing- otherwise -> liftM (Just . decodeUtf8) $ getLazyByteString (fromIntegral len)+ 0 -> return ""+ otherwise -> liftM decodeUtf8 $ getLazyByteString (fromIntegral len) bit :: U1 -> Int -> Bool bit = testBit -getPartFlg :: Get PartFlg-getPartFlg = do+getPartFlag :: Get PartFlag+getPartFlag = do b <- u1- return PartFlg { supersedesPartId = bit b 0+ return PartFlag { supersedesPartId = bit b 0 , supersedesXY = bit b 1 , abnormalEnd = bit b 2 , failed = bit b 3 , noPassFailInfo = bit b 4 } getPrr :: Get Rec-getPrr = Prr <$> u1 <*> u1 <*> getPartFlg <*> u2 <*> u2 <*> mu2 <*> mi2 <*> mi2 <*> mu4 <*> cn <*> cn <*> cn+getPrr = Prr <$> u1 <*> u1 <*> getPartFlag <*> u2 <*> u2 <*> mu2+ <*> mi2 <*> mi2 <*> getMilliseconds <*> mcn <*> mcn <*> mcn +getPir :: Get Rec+getPir = Pir <$> u1 <*> u1++getTsr :: Get Rec+getTsr = Tsr <$> u1 <*> u1 <*> getTestType <*> u4 <*> mu4 <*> mu4 <*> mu4 + <*> mcn <*> mcn <*> mcn -- TODO: actually get the optional data+ <*> pure Nothing+ <*> pure Nothing+ <*> pure Nothing+ <*> pure Nothing+ <*> pure Nothing+ where + getTestType = charToTestType <$> c1+ charToTestType 'P' = Just Parametric+ charToTestType 'F' = Just Functional+ charToTestType 'M' = Just MultiResultParametric+ charToTestType ' ' = Nothing+ charToTestType x = Just $ OtherTestType x++decodeTestFlags :: U1 -> [TestFlag]+decodeTestFlags fl = if fl == 0+ then [Pass]+ else mkFlagList mapTf fl+ where+ mapTf 0 = Alarm+ mapTf 1 = Invalid -- bit 1+ mapTf 2 = Unreliable -- bit 2+ mapTf 3 = Timeout -- bit 3+ mapTf 4 = NotExecuted -- bit 4+ mapTf 5 = Aborted -- bit 5+ mapTf 6 = InValid -- bit 6+ mapTf 7 = Fail -- bit 7++decodeParametricFlags :: U1 -> [ParametricFlag]+decodeParametricFlags = mkFlagList mapPf+ where+ mapPf 0 = ScaleError -- I could probably get this from Enum duh+ mapPf 1 = DriftError -- bit 1+ mapPf 2 = Oscillation -- bit 2+ mapPf 3 = FailHighLimit -- bit 3+ mapPf 4 = FailLowLimit -- bit 4+ mapPf 5 = PassAlternateLimits -- bit 5+ mapPf 6 = PassOnEqLowLimit -- bit 6+ mapPf 7 = PassOnEqHighLimit -- bit 7++mkFlagList :: (Int -> a) -> U1 -> [a]+mkFlagList func flags = [func bi | bi <- range (0, 7), testBit flags bi]++getDecodeFlags :: (U1 -> [a]) -> Get [a]+getDecodeFlags decodeF = do+ fl <- u1+ return $ decodeF fl++getTestFlags :: Get [TestFlag]+getTestFlags = getDecodeFlags decodeTestFlags++getParametricFlags :: Get [ParametricFlag]+getParametricFlags = getDecodeFlags decodeParametricFlags++-- TODO: Make sure result is Nothing if invalid bit set in testFlags+getPtr :: Get Rec+getPtr = do+ ptrTestNum <- u4+ ptrHeadNum <- u1+ ptrSiteNum <- u1+ testFlags <- getTestFlags+ parametricFlags <- getParametricFlags+ result <- r4 -- depends on testFlags and parametric flags+ testText <- mcn+ alarmId <- mcn -- part of optionalInfo now+ -- optionalInfo <- getOptionalInfo+ let optionalInfo = [] -- TODO+ let mresult = if validResult testFlags parametricFlags+ then Just result+ else Nothing++ return $ Ptr ptrTestNum+ ptrHeadNum+ ptrSiteNum+ testFlags+ parametricFlags+ mresult+ testText+ optionalInfo+ where+ validResult tf pf = all (`elem` [Pass, Fail]) tf + && all (`notElem` [ScaleError, DriftError, Oscillation]) pf+{-+ getOptionalInfo :: Get (Maybe OptionalInfo)+ getOptionalInfo = do+ -- check we're not at the end of the buffer+ noInfo <- isEmpty+ if noInfo + then return Nothing + else do+ optinfo <- getOptionalInfo'+ return $ Just optinfo++ getOptionalInfo' :: Get OptionalInfo+ getOptionalInfo' = do+ optFlag <- u1 -- getOptFlag++ let [ invalidResultExp, -- bit 0+ _, -- bit 1+ invalidLowSpecLimit, -- bit 2+ invalidHighSpecLimit, -- bit 3+ invalidLowLimit, -- bit 4+ invalidHighLimit, -- bit 5+ invalidLowTestLimit, -- bit 6+ invalidHighTestLimit ] -- bit 7+ = Prelude.map (testBit optFlag) $ range (0,7)++ let invalidLowTest = invalidLowLimit || invalidLowTestLimit+ let invalidHighTest = invalidHighLimit || invalidHighTestLimit++ OptionalInfo <$> getOnFalse invalidResultExp i1+ <*> getOnFalse invalidLowTest i1+ <*> getOnFalse invalidHighTest i1+ <*> getOnFalse invalidLowTest r4+ <*> getOnFalse invalidHighTest r4+ <*> mcn <*> mcn <*> mcn <*> mcn + <*> getOnFalse invalidLowSpecLimit r4+ <*> getOnFalse invalidHighSpecLimit r4+-}++-- TODO: So Mpr doesn't use the Invalid flag+getMpr :: Get Rec+getMpr = do+ testNum <- u4+ headNum <- u1+ siteNum <- u1+ testFlg <- getTestFlags+ parmFlg <- getParametricFlags+ -- TODO: record may end here if no more info+ j <- fromIntegral <$> u2+ -- TODO: record may end here if no more info+ k <- fromIntegral <$> u2+ rtnStat <- getNibbles j :: Get [U1]+ rtnRslt <- replicateM k r4+ testTxt <- mcn+ -- TODO: record may end here if no more info+ let info = [] -- TODO: parse OptionalInfo++ return $ Mpr testNum headNum siteNum testFlg parmFlg rtnStat rtnRslt testTxt info++-- Take a list of bytes and split into exactly n nibbles+fromNibbles :: [U1] -> Int -> [U1]+fromNibbles [byte] 0 = []+fromNibbles [byte] 1 = [0xF .&. byte]+fromNibbles [byte] 2 = [0xF .&. byte, byte `shiftR` 4]+fromNibbles (byte:bytes) n = 0xF .&. byte : byte `shiftR` 4 : fromNibbles bytes (n - 2)++getNibbles :: Int -> Get [U1]+getNibbles nnibs = do+ let nbytes = nnibs `div` 2 + if odd nnibs then 1 else 0+ bytes <- replicateM nbytes u1+ return $ fromNibbles bytes nnibs+++getOnFalse :: Bool -> Get a -> Get (Maybe a)+getOnFalse cond get = do+ x <- get+ return $ if cond+ then Nothing+ else Just x++getFtr :: Get Rec+getFtr = do+ testNum <- u4+ headNum <- u1+ siteNum <- u1+ testFlag <- getTestFlags -- doesn't use the invalid bit+ -- TODO: record may end here, before rtnIcnt or pgmIcnt -- check isEmpty+ empty <- isEmpty+ info <- if empty then return []+ else getInfo+ -- may end here+ emptyj <- isEmpty+ j0 <- if emptyj then return 0+ else fromIntegral <$> u2+ -- may end here+ emptyk <- isEmpty+ k0 <- if emptyk then return 0+ else fromIntegral <$> u2++ emptymi <- isEmpty+ moreInfo <- if emptymi then return []+ else getMoreInfo j0 k0++ let allInfo = info ++ moreInfo++ return $ Ftr testNum headNum siteNum testFlag allInfo+ -- cycleCnt relVadr reptCnt numFail + -- xFail yFail vectOff rtnIndx rtnStat pgmIndx pgmStat+ -- failPin vecNam timeSet opCode testTxt alarmId progTxt rsltTxt patgNum spinMap++ where + getMoreInfo j k = do+ rtnIndx <- replicateM j u2+ rtnStat <- getNibbles j+ pgmIndx <- replicateM k u2+ pgmStat <- getNibbles k++ failPin <- getBitField+ vecNam <- mcn+ timeSet <- mcn+ opCode <- mcn+ testTxt <- mcn+ alarmId <- mcn+ progTxt <- mcn+ rsltTxt <- mcn++ -- can it end here?+ patgNum <- mu1e255+ spinMap <- getBitField+ let patgNum = Nothing+ let spinMap = []++ let rtnIndx' = if j == 0 then Nothing else Just rtnIndx+ let rtnStat' = if j == 0 then Nothing else Just rtnStat+ let pgmIndx' = if k == 0 then Nothing else Just pgmIndx+ let pgmStat' = if k == 0 then Nothing else Just pgmStat+ let failPin' = if Prelude.null failPin then Nothing else Just failPin+ let spinMap' = if Prelude.null spinMap then Nothing else Just spinMap++ return $ catMaybes [ ReturnPinIndecies <$> rtnIndx'+ , ReturnedStates <$> rtnStat'+ , PgmStateIndecies <$> pgmIndx'+ , PgmStates <$> pgmStat'+ , FailPin <$> failPin'+ , VectorName <$> vecNam+ , TimeSet <$> timeSet+ , OpCode <$> opCode+ , Label <$> testTxt+ , AlarmId <$> alarmId+ , ProgramText <$> progTxt+ , ResultText <$> rsltTxt+ , PatternGen <$> patgNum+ , EnabledPins <$> spinMap' ]++ getInfo = do+ optFlag <- u1++ let [noCycleCnt,+ noRelVadr,+ noReptCnt,+ noNumFail,+ noXYFail,+ noVectOff] = Prelude.map (testBit optFlag) $ range (0, 5)++ -- Turns out you're supposed to get the byte+ -- even if it's invalid+ -- For a format seemingly obsessed with saving+ -- a few bytes, this is surprising+ cycleCnt <- getOnFalse noCycleCnt u4+ relVadr <- getOnFalse noRelVadr u4+ reptCnt <- getOnFalse noReptCnt u4+ numFail <- getOnFalse noNumFail u4+ xFail <- getOnFalse noXYFail i4+ yFail <- getOnFalse noXYFail i4+ vectOff <- getOnFalse noVectOff i2++ return $ catMaybes+ [CycleCount <$> cycleCnt,+ RelativeVectorAddr <$> relVadr,+ RepeatCount <$> reptCnt,+ NumFailingPins <$> numFail,+ XLogicalFailureAddr <$> xFail,+ YLogicalFailureAddr <$> yFail,+ OffsetFromVector <$> vectOff]++{-++ -}++-- TODO: BitField type like [U1] which toJson prints as hex "FF AF 12 ..."+getBitField :: Get [U1]+getBitField = do+ nbits <- u2+ let nbytes = let (mbytes, mbits) = nbits `divMod` 8+ obytes = mbytes + if mbits == 0 then 0 else 1+ in fromIntegral obytes+ replicateM nbytes u1++getByteField :: Get [U1]+getByteField = do+ nbytes <- fromIntegral <$> u1+ replicateM nbytes u1++getBps :: Get Rec+getBps = Bps <$> mcn++getEps :: Get Rec+getEps = return Eps++getGdr :: Get Rec+getGdr = do+ fieldCount <- fromIntegral <$> u2+ fields <- replicateM fieldCount getGdrField+ return $ Gdr fields++getGdrField :: Get GdrField+getGdrField = do+ fieldType <- fromIntegral <$> u1+ case fieldType of+ 0 -> return GPad+ 1 -> GU1 <$> u1+ 2 -> GU2 <$> u2+ 3 -> GU4 <$> u4+ 4 -> GI1 <$> i1+ 5 -> GI2 <$> i2+ 6 -> GI4 <$> i4+ 7 -> GFloat <$> r4+ 8 -> GDouble <$> r8+ 10 -> GStr <$> cn+ 11 -> GBytes <$> getByteField -- first 2 bytes are length in bytes+ 12 -> GData <$> getBitField -- first 2 bytes are length in bits. I think I wrote something like this already+ 13 -> GNibble <$> (liftM head . getNibbles) 1+ _ -> return GPad -- So don't crash on broken GDR++getDtr :: Get Rec+getDtr = Dtr <$> cn++---------------------------------------------------------------- getRawRec :: Integral a => a -> Get Rec getRawRec len = do bytes <- getLazyByteString (fromIntegral len)@@ -165,9 +629,77 @@ processRec :: Header -> ByteString -> Rec processRec hdr = runGet (specificGet hdr) +typStdfInfo = 0+subFar = 10+subAtr = 20+typPerLot = 1+subMir = 10+subMrr = 20+subPcr = 30+subHbr = 40+subSbr = 50+subPmr = 60+subPgr = 62+subPlr = 63+subRdr = 70+subSdr = 80+typPerWafer = 2+subWir = 10+subWrr = 20+subWcr = 30+typPerPart = 5+subPir = 10+subPrr = 20+typPerTest = 10+subTsr = 30+typPerTestExec = 15+subPtr = 10+subMpr = 15+subFtr = 20+typPerProgSeg = 20+subBps = 10+subEps = 20+typGeneric = 50+subGdr = 10+subDtr = 30++-- Dispatch table for record types specificGet :: Header -> Get Rec+-- STDF file info specificGet (Header _ 0 10) = getFar+specificGet (Header _ 0 20) = getAtr+-- Per Lot info+specificGet (Header _ 1 10) = getMir+specificGet (Header _ 1 20) = getMrr+specificGet (Header _ 1 30) = getPcr+specificGet (Header _ 1 40) = getHbr+specificGet (Header _ 1 50) = getSbr+specificGet (Header _ 1 60) = getPmr+specificGet (Header _ 1 62) = getPgr+specificGet (Header _ 1 63) = getPlr+specificGet (Header _ 1 70) = getRdr+specificGet (Header _ 1 80) = getSdr+-- Per Wafer Info+specificGet (Header _ 2 10) = getWir+specificGet (Header _ 2 20) = getWrr+specificGet (Header _ 2 30) = getWcr+-- Per-Part Info+specificGet (Header _ 5 10) = getPir specificGet (Header _ 5 20) = getPrr+-- Per-Test+specificGet (Header _ 10 30) = getTsr+-- -- Per-Test-Execution+specificGet (Header _ 15 10) = getPtr+specificGet (Header _ 15 15) = getMpr+specificGet (Header _ 15 20) = getFtr+-- -- Per-Program-Segment+specificGet (Header _ 20 10) = getBps+specificGet (Header _ 20 20) = getEps+-- -- Generic Data+specificGet (Header _ 50 10) = getGdr+specificGet (Header _ 50 30) = getDtr++-- specificGet (Header len _ _) = getRawRec len getBinRec :: Get BinRec@@ -194,5 +726,20 @@ recs <- getStdf return (record:recs) +isGzipped :: ByteString -> IO Bool+isGzipped bs = do+ let magic = runGet getWord16le bs+ return $ magic == 0x8b1f++-- | Parse an optionally-gzipped stdf file+parseFile :: String -> IO Stdf+parseFile fn = do+ bs <- BL.readFile fn+ isgz <- isGzipped bs+ let decompressed = decompress bs+ return $ parse $ if isgz then decompressed else bs++-- | Parse an Stdf from a ByteString in case you want to open your own files or+-- | parse a stream off the tester or something parse :: ByteString -> Stdf parse = runGet getStdf
+ Data/Stdf/Types.hs view
@@ -0,0 +1,456 @@++{-# LANGUAGE DeriveGeneric #-}++module Data.Stdf.Types where++import Data.Word+import Data.Int+import Foreign.C.Types+import Data.Text.Lazy+import GHC.Generics hiding (U1, C1)+import Data.Aeson+import Data.Aeson.Types+import Data.Time.Clock++-- Time is local unix time. Shouldn't they put the time zone in the file+-- in order for this to have any meaning? What if the data isn't being parsed+-- in the same time zone as it was generated? It will be as if the tester+-- was transported through time and space to wherever I am. I think I'll+-- just leave it alone so times can be Word32 representing Unix time++type U1 = Word8 -- unsigned 1 byte+type U2 = Word16 -- unsigned 2 bytes+type U4 = Word32 -- unsigned 4 bytes+type I1 = Int8+type I2 = Int16+type I4 = Int32+type R4 = Float -- CFloat+type R8 = Double -- CDouble+type C1 = Char++jsonOptions = defaultOptions {+ allNullaryToStringTag = False+ , omitNothingFields = True+ , sumEncoding = ObjectWithSingleField+ }++flagOptions = defaultOptions++instance ToJSON Milliseconds where+ toJSON = genericToJSON jsonOptions+instance ToJSON Minutes where+ toJSON = genericToJSON jsonOptions+instance ToJSON Rec where+ toJSON = genericToJSON jsonOptions+instance ToJSON PartFlag where+ toJSON = genericToJSON jsonOptions+instance ToJSON GdrField where+ toJSON = genericToJSON jsonOptions+instance ToJSON TestType where+ toJSON = genericToJSON jsonOptions+instance ToJSON WaferUnits where+ toJSON = genericToJSON jsonOptions+instance ToJSON Direction where+ toJSON = genericToJSON jsonOptions+instance ToJSON OptionalInfo where+ toJSON = genericToJSON jsonOptions+instance ToJSON GroupMode where+ toJSON = genericToJSON jsonOptions+instance ToJSON Radix where+ toJSON = genericToJSON jsonOptions+instance ToJSON TestFlag where+ toJSON = genericToJSON flagOptions+instance ToJSON PassFailBin where+ toJSON = genericToJSON jsonOptions+instance ToJSON ParametricFlag where+ toJSON = genericToJSON flagOptions++data BinRec = BinRec + { header :: Header+ , rec :: Rec } deriving (Generic, Show)++data Header = Header+ { len :: !Word16+ , typ :: !Word8+ , sub :: !Word8+ } deriving (Generic, Show)++type Stdf = [Rec]++data Milliseconds = Milliseconds { ms :: !U4 }+ deriving (Generic, Show)++data Minutes = Minutes { minutes :: !U2 }+ deriving (Generic, Show)++-- The mother of all datatypes+data Rec= Raw { raw :: Text } -- base64 TODO: URL encoding or maybe don't bother. what's this good for?+ | Far { cpuType :: !U1+ , stdfVersion :: !U1 }+ | Atr { modificationTime :: Maybe UTCTime+ , commandLine :: Maybe Text }+ | Mir { setupTime :: Maybe UTCTime+ , startTime :: Maybe UTCTime+ , station :: !U1+ , modeCode :: Maybe C1 -- TODO: MODE_COD record+ , retestCode :: Maybe C1 -- TODO: RTST_COD record+ , protectionCode :: Maybe C1 -- ' '+ , burninTime :: Maybe Minutes -- 65,535+ , commandCode :: Maybe C1 -- ' '+ , lotId :: Text+ , partType :: Text+ , nodeName :: Text+ , testerType :: Text+ , jobName :: Text+ , jobRevision :: Maybe Text+ , subLotId :: Maybe Text+ , operatorName :: Maybe Text+ , execType :: Maybe Text+ , execVersion :: Maybe Text+ , testCode :: Maybe Text+ , testTemperature :: Maybe Text+ , userText :: Maybe Text+ , auxFile :: Maybe Text+ , packageType :: Maybe Text+ , familyId :: Maybe Text+ , dateCode :: Maybe Text+ , facilityId :: Maybe Text+ , floorId :: Maybe Text+ , processId :: Maybe Text+ , operationFreq :: Maybe Text+ , specName :: Maybe Text+ , specVersion :: Maybe Text+ , flowId :: Maybe Text+ , setupId :: Maybe Text+ , designRev :: Maybe Text+ , engineeringLotId :: Maybe Text+ , romCodeId :: Maybe Text+ , testerSerialNum :: Maybe Text+ , supervisorName :: Maybe Text }+ | Mrr { finishTime :: Maybe UTCTime+ , lotDisposition :: Maybe C1+ , userDescription :: Maybe Text+ , execDescription :: Maybe Text }+ | Pcr { headId :: !U1+ , siteId :: !U1+ , partCount :: !U4+ , retestCount :: Maybe U4+ , abortCount :: Maybe U4+ , goodCount :: Maybe U4+ , functionalCount :: Maybe U4 }+ | Hbr { headId :: !U1+ , siteId :: !U1+ , bin :: !U2+ , binCount :: !U4+ , passFailBin :: PassFailBin+ , name :: Maybe Text }+ | Sbr { headId :: !U1+ , siteId :: !U1+ , bin :: !U2+ , binCount :: !U4+ , passFail :: PassFailBin+ , name :: Maybe Text }+ | Pmr { index :: !U2 -- Maybe rather than index reference by name+ , channelType :: Maybe U2+ , channelName :: Maybe Text+ , physicalName :: Maybe Text+ , logicalName :: Maybe Text+ , headId :: !U1+ , siteId :: !U1 }+ | Pgr { index :: !U2+ , name :: Maybe Text+ , pinIndecies :: [U2] } -- list of pins instead of refering to indecies+ -- Parsing: + -- Empty arrays (or empty members of arrays) can be omitted + -- if they occur at the end of the record.+ | Plr { + -- Instead of Maybe [] opt for empty [Maybe]+ indecies :: [U2]+ , groupModes :: [GroupMode]+ , groupRadixes :: [Radix]+ -- Should really just use a string for state characters+ -- instead of Left/Right but failed since I don't have example PLR+ -- , programStateChars :: [Text] -- combine CharR and CharL at parse?+ -- , returnStateChars :: [Text] }+ , programStateCharsRight :: [Maybe Text]+ , returnStateCharsRight :: [Maybe Text]+ , programStateCharsLeft :: [Maybe Text]+ , returnStateCharsLeft :: [Maybe Text] }+ | Rdr { retestBins :: [U2] }+ | Sdr { headId :: !U1+ , siteGroup :: !U1+ , sites :: [U1]+ , handlerType :: Maybe Text+ , handlerId :: Maybe Text+ , probeCardType :: Maybe Text+ , probeCardId :: Maybe Text+ , loadBoardType :: Maybe Text+ , loadBoardId :: Maybe Text+ , dibType :: Maybe Text+ , dibId :: Maybe Text+ , cableType :: Maybe Text+ , cableId :: Maybe Text+ , contactorType :: Maybe Text+ , contactorId :: Maybe Text+ , laserType :: Maybe Text+ , laserId :: Maybe Text+ , extraType :: Maybe Text+ , extraId :: Maybe Text }+ | Wir { headId :: !U1+ , siteGroup :: !U1 -- 255 -> Nothing -- feature removed+ , startTime :: Maybe UTCTime+ , waferId :: Maybe Text }+ | Wrr { headId :: !U1+ , siteGroup :: !U1 -- 255 means Nothing+ , finishTime :: Maybe UTCTime+ , partCount :: !U4+ , retestCount :: Maybe U4 -- 4,294,967,295 -> Nothing+ , abortCount :: Maybe U4 -- 4,294,967,295 -> Nothing+ , goodCount :: Maybe U4 -- 4,294,967,295 -> Nothing+ , functionalCount :: Maybe U4 -- 4,294,967,295 -> Nothing+ , waferId :: Maybe Text -- length 0 -> Nothing+ , fabWaferId :: Maybe Text -- length 0 -> Nothing+ , waferFrameId :: Maybe Text -- length 0 -> Nothing+ , waferMaskId :: Maybe Text -- length 0 -> Nothing+ , userDescription :: Maybe Text -- length 0 -> Nothing+ , execDescription :: Maybe Text }+ | Wcr { waferSize :: Maybe R4 -- 0 -> Nothing+ , dieHeight :: Maybe R4+ , dieWidth :: Maybe R4+ , waferUnits :: Maybe WaferUnits+ , waferFlat :: Maybe Direction+ , centerX :: Maybe I2+ , centerY :: Maybe I2+ , positiveXdirection :: Maybe Direction+ , positiveYdirection :: Maybe Direction }+ | Pir { headId :: !U1+ , siteId :: !U1 }+ | Prr { headId :: !U1+ , siteId :: !U1+ , partFlag :: !PartFlag+ , numTestsExecuted :: !U2+ , hardBin :: !U2+ , softBin :: Maybe U2+ , xCoord :: Maybe I2+ , yCoord :: Maybe I2+ , testTime :: Maybe Milliseconds -- TODO: type milliseconds like minutes or call this field milliseconds+ , partID :: Maybe Text+ , partTxt :: Maybe Text+ , partFix :: Maybe Text }+ | Tsr { headId :: !U1+ , siteId :: !U1+ , testType :: Maybe TestType+ , testId :: !U4+ , execCount :: Maybe U4+ , failCount :: Maybe U4+ , alarmCount :: Maybe U4+ , testName :: Maybe Text+ , sequencerName :: Maybe Text+ , testLabel :: Maybe Text+ -- , optionalFlags :: !U1 -- parsing optional if last field in record+ , testTimeAverage :: Maybe R4 -- optional fields based on optionalFlags TODO: name this field averageSeconds or make a Seconds type+ , valueMin :: Maybe R4 -- may make these another record type+ , valueMax :: Maybe R4+ , valueSum :: Maybe R4+ , valueSumOfSquares :: Maybe R4 }+ | Ptr { testId :: !U4+ , headId :: !U1+ , siteId :: !U1+ , testFlags :: [TestFlag] -- B1 bitfield further parsing bits+ , parametricFlags :: [ParametricFlag] -- B1 bitfield further parsing bits+ , result :: Maybe R4+ , testText :: Maybe Text+ -- , alarmId :: Maybe Text -> optionalInfo+ , info :: [OptionalInfo] } -- TODO: better name -- Maybe so empty ones don't print in Json+ | Mpr { testId :: !U4+ , headId :: !U1+ , siteId :: !U1+ , testFlags :: [TestFlag]+ , parametricFlags :: [ParametricFlag]+ -- j , stateCount :: U2+ -- k , resultCount :: U2+ , states :: [U1] -- Nibbles! array of states? j states+ , results :: [R4] -- k results+ , testText :: Maybe Text+ , info :: [OptionalInfo] }+ -- , alarmId :: Maybe Text+ -- -- , OPT_FLG B1 optional stuff to parse+ -- , resultExp :: Maybe I1 -- TODO: put mostly in OptionalInfo+ -- , lowLimitExp :: Maybe I1+ -- , highLimitExp :: Maybe I1+ -- , lowLimit :: Maybe R4+ -- , highLimit :: Maybe R4+ -- , startingInput :: Maybe R4+ -- , incrementInput :: Maybe R4+ -- , returnPinIndecies :: Maybe [U2] -- k indecies+ -- , units :: Maybe Text+ -- , unitsInputCondition :: Maybe Text+ -- , printfResultFmt :: Maybe Text+ -- , printfLowLimitFmt :: Maybe Text+ -- , printfHighLimitFmt :: Maybe Text+ -- , lowSpecLimit :: Maybe R4+ -- , highSpecLimit :: Maybe R4 }+ | Ftr { testId :: !U4+ , headId :: !U1+ , siteId :: !U1+ , testFlags :: [TestFlag]+ , info :: [OptionalInfo]+ -- -- , optFlg :: !U1 -- 8 bit packed binary -- record may have ended by here+ -- , cycleCount :: Maybe U4 -- To Optional Info+ -- , relativeVectorAddr :: Maybe U4+ -- , numFailingPins :: Maybe U4+ -- , xLogicalFailureAddr :: Maybe I4+ -- , yLogicalFailureAddr :: Maybe I4+ -- , offsetFromVector :: Maybe I2+ -- -- j U2+ -- -- k U2+ -- , pinIndecies :: Maybe [U2] -- j x U2+ -- , returnedStates :: Maybe [U1] -- j NIBBLES!+ -- , pgmStateIndecies :: Maybe [U2] -- k x U2+ -- , pgmStates :: Maybe [U1] -- k NIBBLES!+ -- , failPin :: Maybe [U1] -- bitfield!+ -- , vector :: Maybe Text+ -- , timeSet :: Maybe Text+ -- , opCode :: Maybe Text+ -- , label :: Maybe Text+ -- , alarmId :: Maybe Text+ -- , programText :: Maybe Text+ -- , resultText :: Maybe Text+ -- , patternGen :: Maybe U1 -- 255+ -- , enabledPins :: Maybe [U1] -- bitfield!+ } -- TODO: this is a long silly record. there's a bunch more things+ | Bps { sequencerName :: Maybe Text } -- Begin Program Secion+ | Eps -- End Program Section: no payload+ | Gdr [GdrField]+ | Dtr { textDat :: Text }+ deriving (Generic, Show)++data GroupMode = UnknownGroupMode+ | Normal+ | SameCycleIO+ | SameCycleMidband+ | SameCycleValid+ | SameCycleWindowSustain+ | DualDrive+ | DualDriveMidband+ | DualDriveValid+ | DualDriveWindowSustain+ | OtherGroupMode U2+ deriving (Generic, Show)++data Radix = DefaultRadix+ | Binary+ | Octal+ | Decimal+ | Hexadecimal+ | Symbolic+ | OtherRadix U1+ deriving (Generic, Show)++data TestFlag = Alarm -- bit 0+ | Invalid -- bit 1+ | Unreliable -- bit 2+ | Timeout -- bit 3+ | NotExecuted -- bit 4+ | Aborted -- bit 5+ | InValid -- bit 6+ | Pass -- bit 7 == 0+ | Fail -- bit 7+ deriving (Generic, Show, Eq, Enum)++data PassFailBin = PassBin | FailBin | UnknownBin | OtherBin Char+ deriving (Generic, Show)++data ParametricFlag = ScaleError -- bit 0+ | DriftError -- bit 1+ | Oscillation -- bit 2+ | FailHighLimit -- bit 3+ | FailLowLimit -- bit 4+ | PassAlternateLimits -- bit 5+ | PassOnEqLowLimit -- bit 6+ | PassOnEqHighLimit -- bit 7+ -- bits 6 & 7 seem stupid+ deriving (Generic, Show, Eq, Enum)++-- TODO: Another pass at scaling flags+-- Maybe better as sum type+data OptionalInfo = Units Text+ | LowSpecLimit Float+ | HighSpecLimit Float+ | LowSpecLimitStr Text+ | HighSpecLimitStr Text+ | AlarmId Text+ | LowLimit Float+ | HighLimit Float+ | LowLimitStr Text+ | HighLimitStr Text+ | ResultStr Text+ | StartingInput Float+ | StartingInputUnits Text+ | IncrementInput Float+ -- change to a map of pinName -> state+ | ReturnPinIndecies [U2] -- k+ | CycleCount U4+ | RepeatCount U4+ | RelativeVectorAddr U4+ | NumFailingPins U4+ | XLogicalFailureAddr I4+ | YLogicalFailureAddr I4+ | OffsetFromVector I2+ | PinIndecies [U2] -- j x U2 -- redundant with ReturnPinIndecies?+ | ReturnedStates [U1] -- j NIBBLES!+ | PgmStateIndecies [U2] -- k x U2 -> Parse pin states?+ | PgmStates [U1] -- k NIBBLES! -> String+ | FailPin [U1] -- bitfield! -> [PinName]+ | VectorName Text+ | TimeSet Text+ | OpCode Text+ | Label Text+ | ProgramText Text+ | ResultText Text+ | PatternGen U1 -- 255+ | EnabledPins [U1] -- bitfield!+ deriving (Generic, Show)++data TestType = Parametric+ | Functional+ | MultiResultParametric+ | UnknownTestType+ | OtherTestType C1+ deriving (Generic, Show)++data WaferUnits = Inches+ | Centimeters+ | Millimeters+ | Mils+ | OtherUnits U1+ deriving (Generic, Show)++data Direction = Up+ | Down+ | Left+ | Right+ | OtherDirection C1+ deriving (Generic, Show)++data GdrField = GPad -- discard+ | GU1 !U1+ | GU2 !U2+ | GU4 !U4+ | GI1 !I1+ | GI2 !I2+ | GI4 !I4+ | GFloat Float -- parse as CFloat+ | GDouble Double -- parse as CDouble+ | GStr Text+ | GBytes [U1] -- encoded ByteStr+ | GData [U1] -- 2byte length + encoded ByteStr+ | GNibble !U1 -- a nibble? are you fucking kidding me?+ deriving (Generic, Show)++data PartFlag = PartFlag { supersedesPartId :: Bool -- 1 bit+ , supersedesXY :: Bool -- 1 bit+ , abnormalEnd :: Bool+ , failed :: Bool+ , noPassFailInfo :: Bool }+ deriving (Generic, Show)
Data/Stdf/WaferMap.hs view
@@ -7,6 +7,7 @@ , xybsToGrid , gridToString , stdfToWaferMapString + , map2d ) where import Data.Stdf@@ -20,9 +21,9 @@ data XyBin = XyBin { x :: Int , y :: Int- , bin :: Int }+ , hbin :: Int } | Missing- deriving Show+ deriving (Show, Eq) stdfToXyBin :: Stdf -> [XyBin] stdfToXyBin (prr@(Prr { xCoord = Just xc@@ -30,7 +31,7 @@ , hardBin = hb }):prrs) = XyBin { x = fromIntegral xc , y = fromIntegral yc- , bin = fromIntegral hb } : stdfToXyBin prrs+ , hbin = fromIntegral hb } : stdfToXyBin prrs stdfToXyBin (_:prrs) = stdfToXyBin prrs stdfToXyBin [] = [] @@ -51,7 +52,7 @@ -- sort by y,x -- groupby y,x--- take lowest bin # from groups+-- take lowest hbin # from groups -- use min max x y to insert Missing dies xybsToGrid :: [XyBin] -> [[XyBin]] xybsToGrid xybs = mkgrid cols gridlist@@ -59,7 +60,7 @@ EQ -> compare (x a) (x b) ordy -> ordy eqXy a b = x a == x b && y a == y b- ordBin a b = compare (bin a) (bin b)+ ordBin a b = compare (hbin a) (hbin b) waferxy = [(atx, aty) | aty <- range (miny, maxy) , atx <- range (minx, maxx)] sortedxy = sortBy ordXy xybs@@ -88,7 +89,7 @@ pad w s = s ++ replicate (w - length s) ' ' binOrNothing Missing = Nothing- binOrNothing XyBin { bin = bin } = Just bin+ binOrNothing XyBin { hbin = hbin } = Just hbin bins = map2d binOrNothing xybs -- start here binstrs :: [[String]]
Examples/StdfToJson.hs view
@@ -15,13 +15,16 @@ notRaw (Raw _) = False notRaw _ = True +isFtr :: Rec -> Bool+isFtr (Ftr{}) = True+isFtr _ = False+ main = do args <- getArgs when (length args /= 1) $ do printUsage exitFailure let file = head args- raw <- BL.readFile file- let recs = parse raw+ recs <- parseFile file let goodRecs = filter notRaw recs mapM_ (BL.putStrLn . encode) goodRecs
Examples/WaferMap.hs view
@@ -17,5 +17,5 @@ exitFailure let file = head args putStrLn $ "File: " ++ file- raw <- BL.readFile file- putStrLn $ stdfToWaferMapString (parse raw)+ recs <- parseFile file+ putStrLn $ stdfToWaferMapString recs
Readme.md view
@@ -5,6 +5,7 @@ There's a couple of good, open-source libraries for parsing STDF, notably [PyStdf](https://github.com/cmars/pystdf). +Latest version: 0.2 The Data.Stdf advantage -----------------------@@ -16,8 +17,10 @@ Install ------- - cabal install stdf -- Is a TODO. Just clone and build the package+ cabal install stdf +Or for the latest revision, clone and build from here.+ Usage ----- @@ -65,10 +68,22 @@ * StdfToJson -- prints each Stdf record as a one-line JSON object for easy importing into other languages for further processing -Now the Bad News+Performance+-----------++Performance is very good but can be 50% better if you build with `-O2`.+However compiling takes forever with `-O2`.+++Not Implemented ---------------- -Data.Stdf currently only parses the PRR record because that's all I needed.-Well, the code is short and sweet so it won't be too much work to add support-for some more interesting records. I can't think of why I'd bother with all of-the rarely-used uninteresting ones. But maybe if I'm bored.+### Non-Intel floating-point and endianness++It really blows my mind that Stdf expects the parser to support any N CPU bit+encodings. I won't do it.++### Some tricky records and fields++Not yet covering all fields of all Stdf records.+Several interesting optional fields in PTR, MPR and FTR are not yet processed.
stdf.cabal view
@@ -10,7 +10,7 @@ -- PVP summary: +-+------- breaking API changes -- | | +----- non-breaking API additions -- | | | +--- code changes with no API change-version: 0.1.0.0+version: 0.2.0.0 -- A short (one-line) description of the package. synopsis: Parse Structured Test Data Format (STDF)@@ -51,7 +51,7 @@ library -- Modules exported by the library.- exposed-modules: Data.Stdf, Data.Stdf.WaferMap+ exposed-modules: Data.Stdf, Data.Stdf.WaferMap, Data.Stdf.Types -- Modules included in this library but not exported. -- other-modules: @@ -60,25 +60,25 @@ other-extensions: NegativeLiterals, DeriveGeneric, OverloadedStrings -- Other library packages from which modules are imported.- build-depends: base >=4.7 && <4.8, bytestring >=0.10 && <0.11, binary >=0.7 && <0.8, text >=1.1 && <1.2, base64-bytestring >=1.0 && <1.1, aeson >=0.8 && <0.9, split >=0.2 && <0.3+ build-depends: base >=4.7 && <4.8, bytestring >=0.10 && <0.11, binary >=0.7 && <0.8, text >=1.1 && <1.2, base64-bytestring >=1.0 && <1.1, aeson >=0.8 && <0.9, split >=0.2 && <0.3, time >= 1.4, unix-time >= 0.3, zlib >= 0.5, data-binary-ieee754 >= 0.4, containers >= 0.5 -- Directories containing source files. hs-source-dirs: . -- Base language which the package is written in. default-language: Haskell2010- GHC-Options: -O2+ -- GHC-Options: -O Executable StdfToJson main-is: StdfToJson.hs hs-source-dirs: Examples default-language: Haskell2010- GHC-Options: -O2- build-depends: base >=4.7 && <4.8, bytestring >=0.10 && <0.11, binary >=0.7 && <0.8, text >=1.1 && <1.2, base64-bytestring >=1.0 && <1.1, aeson >=0.8 && <0.9, split >=0.2 && <0.3, stdf+ -- GHC-Options: -O+ build-depends: base >=4.7 && <4.8, bytestring >=0.10 && <0.11, binary >=0.7 && <0.8, text >=1.1 && <1.2, base64-bytestring >=1.0 && <1.1, aeson >=0.8 && <0.9, split >=0.2 && <0.3, stdf >= 0.2 Executable WaferMap main-is: WaferMap.hs hs-source-dirs: Examples default-language: Haskell2010- GHC-Options: -O2- build-depends: base >=4.7 && <4.8, bytestring >=0.10 && <0.11, binary >=0.7 && <0.8, text >=1.1 && <1.2, base64-bytestring >=1.0 && <1.1, aeson >=0.8 && <0.9, split >=0.2 && <0.3, stdf+ -- GHC-Options: -O+ build-depends: base >=4.7 && <4.8, bytestring >=0.10 && <0.11, binary >=0.7 && <0.8, text >=1.1 && <1.2, base64-bytestring >=1.0 && <1.1, aeson >=0.8 && <0.9, split >=0.2 && <0.3, stdf >= 0.2