{-# 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)