packages feed

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 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